a3000.c (7009B)
1// SPDX-License-Identifier: GPL-2.0-only 2#include <linux/types.h> 3#include <linux/mm.h> 4#include <linux/ioport.h> 5#include <linux/init.h> 6#include <linux/slab.h> 7#include <linux/spinlock.h> 8#include <linux/interrupt.h> 9#include <linux/platform_device.h> 10#include <linux/module.h> 11 12#include <asm/page.h> 13#include <asm/amigaints.h> 14#include <asm/amigahw.h> 15 16#include <scsi/scsi.h> 17#include <scsi/scsi_cmnd.h> 18#include <scsi/scsi_device.h> 19#include <scsi/scsi_eh.h> 20#include <scsi/scsi_tcq.h> 21#include "wd33c93.h" 22#include "a3000.h" 23 24 25struct a3000_hostdata { 26 struct WD33C93_hostdata wh; 27 struct a3000_scsiregs *regs; 28}; 29 30static irqreturn_t a3000_intr(int irq, void *data) 31{ 32 struct Scsi_Host *instance = data; 33 struct a3000_hostdata *hdata = shost_priv(instance); 34 unsigned int status = hdata->regs->ISTR; 35 unsigned long flags; 36 37 if (!(status & ISTR_INT_P)) 38 return IRQ_NONE; 39 if (status & ISTR_INTS) { 40 spin_lock_irqsave(instance->host_lock, flags); 41 wd33c93_intr(instance); 42 spin_unlock_irqrestore(instance->host_lock, flags); 43 return IRQ_HANDLED; 44 } 45 pr_warn("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status); 46 return IRQ_NONE; 47} 48 49static int dma_setup(struct scsi_cmnd *cmd, int dir_in) 50{ 51 struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd); 52 struct Scsi_Host *instance = cmd->device->host; 53 struct a3000_hostdata *hdata = shost_priv(instance); 54 struct WD33C93_hostdata *wh = &hdata->wh; 55 struct a3000_scsiregs *regs = hdata->regs; 56 unsigned short cntr = CNTR_PDMD | CNTR_INTEN; 57 unsigned long addr = virt_to_bus(scsi_pointer->ptr); 58 59 /* 60 * if the physical address has the wrong alignment, or if 61 * physical address is bad, or if it is a write and at the 62 * end of a physical memory chunk, then allocate a bounce 63 * buffer 64 */ 65 if (addr & A3000_XFER_MASK) { 66 wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff; 67 wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len, 68 GFP_KERNEL); 69 70 /* can't allocate memory; use PIO */ 71 if (!wh->dma_bounce_buffer) { 72 wh->dma_bounce_len = 0; 73 return 1; 74 } 75 76 if (!dir_in) { 77 /* copy to bounce buffer for a write */ 78 memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr, 79 scsi_pointer->this_residual); 80 } 81 82 addr = virt_to_bus(wh->dma_bounce_buffer); 83 } 84 85 /* setup dma direction */ 86 if (!dir_in) 87 cntr |= CNTR_DDIR; 88 89 /* remember direction */ 90 wh->dma_dir = dir_in; 91 92 regs->CNTR = cntr; 93 94 /* setup DMA *physical* address */ 95 regs->ACR = addr; 96 97 if (dir_in) { 98 /* invalidate any cache */ 99 cache_clear(addr, scsi_pointer->this_residual); 100 } else { 101 /* push any dirty cache */ 102 cache_push(addr, scsi_pointer->this_residual); 103 } 104 105 /* start DMA */ 106 mb(); /* make sure setup is completed */ 107 regs->ST_DMA = 1; 108 mb(); /* make sure DMA has started before next IO */ 109 110 /* return success */ 111 return 0; 112} 113 114static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, 115 int status) 116{ 117 struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(SCpnt); 118 struct a3000_hostdata *hdata = shost_priv(instance); 119 struct WD33C93_hostdata *wh = &hdata->wh; 120 struct a3000_scsiregs *regs = hdata->regs; 121 122 /* disable SCSI interrupts */ 123 unsigned short cntr = CNTR_PDMD; 124 125 if (!wh->dma_dir) 126 cntr |= CNTR_DDIR; 127 128 regs->CNTR = cntr; 129 mb(); /* make sure CNTR is updated before next IO */ 130 131 /* flush if we were reading */ 132 if (wh->dma_dir) { 133 regs->FLUSH = 1; 134 mb(); /* don't allow prefetch */ 135 while (!(regs->ISTR & ISTR_FE_FLG)) 136 barrier(); 137 mb(); /* no IO until FLUSH is done */ 138 } 139 140 /* clear a possible interrupt */ 141 /* I think that this CINT is only necessary if you are 142 * using the terminal count features. HM 7 Mar 1994 143 */ 144 regs->CINT = 1; 145 146 /* stop DMA */ 147 regs->SP_DMA = 1; 148 mb(); /* make sure DMA is stopped before next IO */ 149 150 /* restore the CONTROL bits (minus the direction flag) */ 151 regs->CNTR = CNTR_PDMD | CNTR_INTEN; 152 mb(); /* make sure CNTR is updated before next IO */ 153 154 /* copy from a bounce buffer, if necessary */ 155 if (status && wh->dma_bounce_buffer) { 156 if (SCpnt) { 157 if (wh->dma_dir && SCpnt) 158 memcpy(scsi_pointer->ptr, wh->dma_bounce_buffer, 159 scsi_pointer->this_residual); 160 kfree(wh->dma_bounce_buffer); 161 wh->dma_bounce_buffer = NULL; 162 wh->dma_bounce_len = 0; 163 } else { 164 kfree(wh->dma_bounce_buffer); 165 wh->dma_bounce_buffer = NULL; 166 wh->dma_bounce_len = 0; 167 } 168 } 169} 170 171static struct scsi_host_template amiga_a3000_scsi_template = { 172 .module = THIS_MODULE, 173 .name = "Amiga 3000 built-in SCSI", 174 .show_info = wd33c93_show_info, 175 .write_info = wd33c93_write_info, 176 .proc_name = "A3000", 177 .queuecommand = wd33c93_queuecommand, 178 .eh_abort_handler = wd33c93_abort, 179 .eh_host_reset_handler = wd33c93_host_reset, 180 .can_queue = CAN_QUEUE, 181 .this_id = 7, 182 .sg_tablesize = SG_ALL, 183 .cmd_per_lun = CMD_PER_LUN, 184 .cmd_size = sizeof(struct scsi_pointer), 185}; 186 187static int __init amiga_a3000_scsi_probe(struct platform_device *pdev) 188{ 189 struct resource *res; 190 struct Scsi_Host *instance; 191 int error; 192 struct a3000_scsiregs *regs; 193 wd33c93_regs wdregs; 194 struct a3000_hostdata *hdata; 195 196 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 197 if (!res) 198 return -ENODEV; 199 200 if (!request_mem_region(res->start, resource_size(res), "wd33c93")) 201 return -EBUSY; 202 203 instance = scsi_host_alloc(&amiga_a3000_scsi_template, 204 sizeof(struct a3000_hostdata)); 205 if (!instance) { 206 error = -ENOMEM; 207 goto fail_alloc; 208 } 209 210 instance->irq = IRQ_AMIGA_PORTS; 211 212 regs = ZTWO_VADDR(res->start); 213 regs->DAWR = DAWR_A3000; 214 215 wdregs.SASR = ®s->SASR; 216 wdregs.SCMD = ®s->SCMD; 217 218 hdata = shost_priv(instance); 219 hdata->wh.no_sync = 0xff; 220 hdata->wh.fast = 0; 221 hdata->wh.dma_mode = CTRL_DMA; 222 hdata->regs = regs; 223 224 wd33c93_init(instance, wdregs, dma_setup, dma_stop, WD33C93_FS_12_15); 225 error = request_irq(IRQ_AMIGA_PORTS, a3000_intr, IRQF_SHARED, 226 "A3000 SCSI", instance); 227 if (error) 228 goto fail_irq; 229 230 regs->CNTR = CNTR_PDMD | CNTR_INTEN; 231 232 error = scsi_add_host(instance, NULL); 233 if (error) 234 goto fail_host; 235 236 platform_set_drvdata(pdev, instance); 237 238 scsi_scan_host(instance); 239 return 0; 240 241fail_host: 242 free_irq(IRQ_AMIGA_PORTS, instance); 243fail_irq: 244 scsi_host_put(instance); 245fail_alloc: 246 release_mem_region(res->start, resource_size(res)); 247 return error; 248} 249 250static int __exit amiga_a3000_scsi_remove(struct platform_device *pdev) 251{ 252 struct Scsi_Host *instance = platform_get_drvdata(pdev); 253 struct a3000_hostdata *hdata = shost_priv(instance); 254 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 255 256 hdata->regs->CNTR = 0; 257 scsi_remove_host(instance); 258 free_irq(IRQ_AMIGA_PORTS, instance); 259 scsi_host_put(instance); 260 release_mem_region(res->start, resource_size(res)); 261 return 0; 262} 263 264static struct platform_driver amiga_a3000_scsi_driver = { 265 .remove = __exit_p(amiga_a3000_scsi_remove), 266 .driver = { 267 .name = "amiga-a3000-scsi", 268 }, 269}; 270 271module_platform_driver_probe(amiga_a3000_scsi_driver, amiga_a3000_scsi_probe); 272 273MODULE_DESCRIPTION("Amiga 3000 built-in SCSI"); 274MODULE_LICENSE("GPL"); 275MODULE_ALIAS("platform:amiga-a3000-scsi");