109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds #include <linux/types.h> 31da177e4SLinus Torvalds #include <linux/mm.h> 41da177e4SLinus Torvalds #include <linux/ioport.h> 51da177e4SLinus Torvalds #include <linux/init.h> 6c2a24a4cSGeert Uytterhoeven #include <linux/slab.h> 71da177e4SLinus Torvalds #include <linux/spinlock.h> 81da177e4SLinus Torvalds #include <linux/interrupt.h> 9c2a24a4cSGeert Uytterhoeven #include <linux/platform_device.h> 10e214806dSMichael Schmitz #include <linux/dma-mapping.h> 11acf3368fSPaul Gortmaker #include <linux/module.h> 121da177e4SLinus Torvalds 131da177e4SLinus Torvalds #include <asm/page.h> 141da177e4SLinus Torvalds #include <asm/amigaints.h> 151da177e4SLinus Torvalds #include <asm/amigahw.h> 161da177e4SLinus Torvalds 1753555fb7SBart Van Assche #include <scsi/scsi.h> 1853555fb7SBart Van Assche #include <scsi/scsi_cmnd.h> 1953555fb7SBart Van Assche #include <scsi/scsi_device.h> 2053555fb7SBart Van Assche #include <scsi/scsi_eh.h> 2153555fb7SBart Van Assche #include <scsi/scsi_tcq.h> 221da177e4SLinus Torvalds #include "wd33c93.h" 231da177e4SLinus Torvalds #include "a3000.h" 241da177e4SLinus Torvalds 259387edbeSAdrian Bunk 262b21d5e4SGeert Uytterhoeven struct a3000_hostdata { 272b21d5e4SGeert Uytterhoeven struct WD33C93_hostdata wh; 282b21d5e4SGeert Uytterhoeven struct a3000_scsiregs *regs; 29e214806dSMichael Schmitz struct device *dev; 302b21d5e4SGeert Uytterhoeven }; 312b21d5e4SGeert Uytterhoeven 32e214806dSMichael Schmitz #define DMA_DIR(d) ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE) 33e214806dSMichael Schmitz 34a8169e60SGeert Uytterhoeven static irqreturn_t a3000_intr(int irq, void *data) 351da177e4SLinus Torvalds { 36a8169e60SGeert Uytterhoeven struct Scsi_Host *instance = data; 372b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata = shost_priv(instance); 382b21d5e4SGeert Uytterhoeven unsigned int status = hdata->regs->ISTR; 391da177e4SLinus Torvalds unsigned long flags; 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds if (!(status & ISTR_INT_P)) 421da177e4SLinus Torvalds return IRQ_NONE; 4321351013SGeert Uytterhoeven if (status & ISTR_INTS) { 44a8169e60SGeert Uytterhoeven spin_lock_irqsave(instance->host_lock, flags); 45a8169e60SGeert Uytterhoeven wd33c93_intr(instance); 46a8169e60SGeert Uytterhoeven spin_unlock_irqrestore(instance->host_lock, flags); 471da177e4SLinus Torvalds return IRQ_HANDLED; 481da177e4SLinus Torvalds } 49a2cc701bSKefeng Wang pr_warn("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status); 501da177e4SLinus Torvalds return IRQ_NONE; 511da177e4SLinus Torvalds } 521da177e4SLinus Torvalds 5365396410SHenrik Kretzschmar static int dma_setup(struct scsi_cmnd *cmd, int dir_in) 541da177e4SLinus Torvalds { 55dbb2da55SBart Van Assche struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd); 56e214806dSMichael Schmitz unsigned long len = scsi_pointer->this_residual; 57a8169e60SGeert Uytterhoeven struct Scsi_Host *instance = cmd->device->host; 582b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata = shost_priv(instance); 592b21d5e4SGeert Uytterhoeven struct WD33C93_hostdata *wh = &hdata->wh; 602b21d5e4SGeert Uytterhoeven struct a3000_scsiregs *regs = hdata->regs; 611da177e4SLinus Torvalds unsigned short cntr = CNTR_PDMD | CNTR_INTEN; 62e214806dSMichael Schmitz dma_addr_t addr; 63e214806dSMichael Schmitz 64e214806dSMichael Schmitz addr = dma_map_single(hdata->dev, scsi_pointer->ptr, 65e214806dSMichael Schmitz len, DMA_DIR(dir_in)); 66e214806dSMichael Schmitz if (dma_mapping_error(hdata->dev, addr)) { 67e214806dSMichael Schmitz dev_warn(hdata->dev, "cannot map SCSI data block %p\n", 68e214806dSMichael Schmitz scsi_pointer->ptr); 69e214806dSMichael Schmitz return 1; 70e214806dSMichael Schmitz } 71e214806dSMichael Schmitz scsi_pointer->dma_handle = addr; 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds /* 741da177e4SLinus Torvalds * if the physical address has the wrong alignment, or if 751da177e4SLinus Torvalds * physical address is bad, or if it is a write and at the 761da177e4SLinus Torvalds * end of a physical memory chunk, then allocate a bounce 771da177e4SLinus Torvalds * buffer 78e214806dSMichael Schmitz * MSch 20220629 - only wrong alignment tested - bounce 79e214806dSMichael Schmitz * buffer returned by kmalloc is guaranteed to be aligned 801da177e4SLinus Torvalds */ 8121351013SGeert Uytterhoeven if (addr & A3000_XFER_MASK) { 82e214806dSMichael Schmitz WARN_ONCE(1, "Invalid alignment for DMA!"); 83e214806dSMichael Schmitz /* drop useless mapping */ 84e214806dSMichael Schmitz dma_unmap_single(hdata->dev, scsi_pointer->dma_handle, 85e214806dSMichael Schmitz scsi_pointer->this_residual, 86e214806dSMichael Schmitz DMA_DIR(dir_in)); 87e214806dSMichael Schmitz 88dbb2da55SBart Van Assche wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff; 892b21d5e4SGeert Uytterhoeven wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len, 90afdbbc16SGeert Uytterhoeven GFP_KERNEL); 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds /* can't allocate memory; use PIO */ 932b21d5e4SGeert Uytterhoeven if (!wh->dma_bounce_buffer) { 942b21d5e4SGeert Uytterhoeven wh->dma_bounce_len = 0; 95e214806dSMichael Schmitz scsi_pointer->dma_handle = (dma_addr_t) NULL; 961da177e4SLinus Torvalds return 1; 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds if (!dir_in) { 1001da177e4SLinus Torvalds /* copy to bounce buffer for a write */ 101dbb2da55SBart Van Assche memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr, 102dbb2da55SBart Van Assche scsi_pointer->this_residual); 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 105e214806dSMichael Schmitz addr = dma_map_single(hdata->dev, scsi_pointer->ptr, 106e214806dSMichael Schmitz len, DMA_DIR(dir_in)); 107e214806dSMichael Schmitz if (dma_mapping_error(hdata->dev, addr)) { 108e214806dSMichael Schmitz dev_warn(hdata->dev, 109e214806dSMichael Schmitz "cannot map SCSI data block %p\n", 110e214806dSMichael Schmitz scsi_pointer->ptr); 111e214806dSMichael Schmitz return 1; 112e214806dSMichael Schmitz } 113e214806dSMichael Schmitz scsi_pointer->dma_handle = addr; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds /* setup dma direction */ 1171da177e4SLinus Torvalds if (!dir_in) 1181da177e4SLinus Torvalds cntr |= CNTR_DDIR; 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds /* remember direction */ 1212b21d5e4SGeert Uytterhoeven wh->dma_dir = dir_in; 1221da177e4SLinus Torvalds 123d753722eSGeert Uytterhoeven regs->CNTR = cntr; 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds /* setup DMA *physical* address */ 126d753722eSGeert Uytterhoeven regs->ACR = addr; 1271da177e4SLinus Torvalds 128e214806dSMichael Schmitz /* no more cache flush here - dma_map_single() takes care */ 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds /* start DMA */ 1311da177e4SLinus Torvalds mb(); /* make sure setup is completed */ 132d753722eSGeert Uytterhoeven regs->ST_DMA = 1; 1331da177e4SLinus Torvalds mb(); /* make sure DMA has started before next IO */ 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds /* return success */ 1361da177e4SLinus Torvalds return 0; 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds 13965396410SHenrik Kretzschmar static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, 1401da177e4SLinus Torvalds int status) 1411da177e4SLinus Torvalds { 142dbb2da55SBart Van Assche struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(SCpnt); 1432b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata = shost_priv(instance); 1442b21d5e4SGeert Uytterhoeven struct WD33C93_hostdata *wh = &hdata->wh; 1452b21d5e4SGeert Uytterhoeven struct a3000_scsiregs *regs = hdata->regs; 146afdbbc16SGeert Uytterhoeven 1471da177e4SLinus Torvalds /* disable SCSI interrupts */ 1481da177e4SLinus Torvalds unsigned short cntr = CNTR_PDMD; 1491da177e4SLinus Torvalds 1502b21d5e4SGeert Uytterhoeven if (!wh->dma_dir) 1511da177e4SLinus Torvalds cntr |= CNTR_DDIR; 1521da177e4SLinus Torvalds 153d753722eSGeert Uytterhoeven regs->CNTR = cntr; 1541da177e4SLinus Torvalds mb(); /* make sure CNTR is updated before next IO */ 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds /* flush if we were reading */ 1572b21d5e4SGeert Uytterhoeven if (wh->dma_dir) { 158d753722eSGeert Uytterhoeven regs->FLUSH = 1; 1591da177e4SLinus Torvalds mb(); /* don't allow prefetch */ 160d753722eSGeert Uytterhoeven while (!(regs->ISTR & ISTR_FE_FLG)) 1611da177e4SLinus Torvalds barrier(); 1621da177e4SLinus Torvalds mb(); /* no IO until FLUSH is done */ 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds /* clear a possible interrupt */ 1661da177e4SLinus Torvalds /* I think that this CINT is only necessary if you are 1671da177e4SLinus Torvalds * using the terminal count features. HM 7 Mar 1994 1681da177e4SLinus Torvalds */ 169d753722eSGeert Uytterhoeven regs->CINT = 1; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* stop DMA */ 172d753722eSGeert Uytterhoeven regs->SP_DMA = 1; 1731da177e4SLinus Torvalds mb(); /* make sure DMA is stopped before next IO */ 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds /* restore the CONTROL bits (minus the direction flag) */ 176d753722eSGeert Uytterhoeven regs->CNTR = CNTR_PDMD | CNTR_INTEN; 1771da177e4SLinus Torvalds mb(); /* make sure CNTR is updated before next IO */ 1781da177e4SLinus Torvalds 179e214806dSMichael Schmitz dma_unmap_single(hdata->dev, scsi_pointer->dma_handle, 180e214806dSMichael Schmitz scsi_pointer->this_residual, 181e214806dSMichael Schmitz DMA_DIR(wh->dma_dir)); 182e214806dSMichael Schmitz 1831da177e4SLinus Torvalds /* copy from a bounce buffer, if necessary */ 1842b21d5e4SGeert Uytterhoeven if (status && wh->dma_bounce_buffer) { 185cc0455faSBoaz Harrosh if (SCpnt) { 1862b21d5e4SGeert Uytterhoeven if (wh->dma_dir && SCpnt) 187dbb2da55SBart Van Assche memcpy(scsi_pointer->ptr, wh->dma_bounce_buffer, 188dbb2da55SBart Van Assche scsi_pointer->this_residual); 1892b21d5e4SGeert Uytterhoeven kfree(wh->dma_bounce_buffer); 1902b21d5e4SGeert Uytterhoeven wh->dma_bounce_buffer = NULL; 1912b21d5e4SGeert Uytterhoeven wh->dma_bounce_len = 0; 1921da177e4SLinus Torvalds } else { 1932b21d5e4SGeert Uytterhoeven kfree(wh->dma_bounce_buffer); 1942b21d5e4SGeert Uytterhoeven wh->dma_bounce_buffer = NULL; 1952b21d5e4SGeert Uytterhoeven wh->dma_bounce_len = 0; 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 200*88530b3eSBart Van Assche static const struct scsi_host_template amiga_a3000_scsi_template = { 201c2a24a4cSGeert Uytterhoeven .module = THIS_MODULE, 2021da177e4SLinus Torvalds .name = "Amiga 3000 built-in SCSI", 203408bb25bSAl Viro .show_info = wd33c93_show_info, 204408bb25bSAl Viro .write_info = wd33c93_write_info, 205c2a24a4cSGeert Uytterhoeven .proc_name = "A3000", 2061da177e4SLinus Torvalds .queuecommand = wd33c93_queuecommand, 2071da177e4SLinus Torvalds .eh_abort_handler = wd33c93_abort, 2081da177e4SLinus Torvalds .eh_host_reset_handler = wd33c93_host_reset, 2091da177e4SLinus Torvalds .can_queue = CAN_QUEUE, 2101da177e4SLinus Torvalds .this_id = 7, 2111da177e4SLinus Torvalds .sg_tablesize = SG_ALL, 2121da177e4SLinus Torvalds .cmd_per_lun = CMD_PER_LUN, 213dbb2da55SBart Van Assche .cmd_size = sizeof(struct scsi_pointer), 2141da177e4SLinus Torvalds }; 2151da177e4SLinus Torvalds 216c2a24a4cSGeert Uytterhoeven static int __init amiga_a3000_scsi_probe(struct platform_device *pdev) 2171da177e4SLinus Torvalds { 218c2a24a4cSGeert Uytterhoeven struct resource *res; 219c2a24a4cSGeert Uytterhoeven struct Scsi_Host *instance; 220c2a24a4cSGeert Uytterhoeven int error; 221c2a24a4cSGeert Uytterhoeven struct a3000_scsiregs *regs; 222c2a24a4cSGeert Uytterhoeven wd33c93_regs wdregs; 2232b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata; 224d753722eSGeert Uytterhoeven 225e214806dSMichael Schmitz if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) { 226e214806dSMichael Schmitz dev_warn(&pdev->dev, "cannot use 32 bit DMA\n"); 227e214806dSMichael Schmitz return -ENODEV; 228e214806dSMichael Schmitz } 229e214806dSMichael Schmitz 230c2a24a4cSGeert Uytterhoeven res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 231c2a24a4cSGeert Uytterhoeven if (!res) 232c2a24a4cSGeert Uytterhoeven return -ENODEV; 233c2a24a4cSGeert Uytterhoeven 234c2a24a4cSGeert Uytterhoeven if (!request_mem_region(res->start, resource_size(res), "wd33c93")) 235c2a24a4cSGeert Uytterhoeven return -EBUSY; 236c2a24a4cSGeert Uytterhoeven 237c2a24a4cSGeert Uytterhoeven instance = scsi_host_alloc(&amiga_a3000_scsi_template, 2382b21d5e4SGeert Uytterhoeven sizeof(struct a3000_hostdata)); 239c2a24a4cSGeert Uytterhoeven if (!instance) { 240c2a24a4cSGeert Uytterhoeven error = -ENOMEM; 241c2a24a4cSGeert Uytterhoeven goto fail_alloc; 2421da177e4SLinus Torvalds } 2431da177e4SLinus Torvalds 244c2a24a4cSGeert Uytterhoeven instance->irq = IRQ_AMIGA_PORTS; 245c2a24a4cSGeert Uytterhoeven 2466112ea08SGeert Uytterhoeven regs = ZTWO_VADDR(res->start); 247c2a24a4cSGeert Uytterhoeven regs->DAWR = DAWR_A3000; 248c2a24a4cSGeert Uytterhoeven 249c2a24a4cSGeert Uytterhoeven wdregs.SASR = ®s->SASR; 250c2a24a4cSGeert Uytterhoeven wdregs.SCMD = ®s->SCMD; 251c2a24a4cSGeert Uytterhoeven 252c2a24a4cSGeert Uytterhoeven hdata = shost_priv(instance); 253e214806dSMichael Schmitz hdata->dev = &pdev->dev; 2542b21d5e4SGeert Uytterhoeven hdata->wh.no_sync = 0xff; 2552b21d5e4SGeert Uytterhoeven hdata->wh.fast = 0; 2562b21d5e4SGeert Uytterhoeven hdata->wh.dma_mode = CTRL_DMA; 2572b21d5e4SGeert Uytterhoeven hdata->regs = regs; 258c2a24a4cSGeert Uytterhoeven 259c2a24a4cSGeert Uytterhoeven wd33c93_init(instance, wdregs, dma_setup, dma_stop, WD33C93_FS_12_15); 260c2a24a4cSGeert Uytterhoeven error = request_irq(IRQ_AMIGA_PORTS, a3000_intr, IRQF_SHARED, 261c2a24a4cSGeert Uytterhoeven "A3000 SCSI", instance); 262c2a24a4cSGeert Uytterhoeven if (error) 263c2a24a4cSGeert Uytterhoeven goto fail_irq; 264c2a24a4cSGeert Uytterhoeven 265c2a24a4cSGeert Uytterhoeven regs->CNTR = CNTR_PDMD | CNTR_INTEN; 266c2a24a4cSGeert Uytterhoeven 267c2a24a4cSGeert Uytterhoeven error = scsi_add_host(instance, NULL); 268c2a24a4cSGeert Uytterhoeven if (error) 269c2a24a4cSGeert Uytterhoeven goto fail_host; 270c2a24a4cSGeert Uytterhoeven 271c2a24a4cSGeert Uytterhoeven platform_set_drvdata(pdev, instance); 272c2a24a4cSGeert Uytterhoeven 273c2a24a4cSGeert Uytterhoeven scsi_scan_host(instance); 274c2a24a4cSGeert Uytterhoeven return 0; 275c2a24a4cSGeert Uytterhoeven 276c2a24a4cSGeert Uytterhoeven fail_host: 277c2a24a4cSGeert Uytterhoeven free_irq(IRQ_AMIGA_PORTS, instance); 278c2a24a4cSGeert Uytterhoeven fail_irq: 279c2a24a4cSGeert Uytterhoeven scsi_host_put(instance); 280c2a24a4cSGeert Uytterhoeven fail_alloc: 281c2a24a4cSGeert Uytterhoeven release_mem_region(res->start, resource_size(res)); 282c2a24a4cSGeert Uytterhoeven return error; 283c2a24a4cSGeert Uytterhoeven } 284c2a24a4cSGeert Uytterhoeven 285c2a24a4cSGeert Uytterhoeven static int __exit amiga_a3000_scsi_remove(struct platform_device *pdev) 286c2a24a4cSGeert Uytterhoeven { 287c2a24a4cSGeert Uytterhoeven struct Scsi_Host *instance = platform_get_drvdata(pdev); 2882b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata = shost_priv(instance); 289c2a24a4cSGeert Uytterhoeven struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 290c2a24a4cSGeert Uytterhoeven 2912b21d5e4SGeert Uytterhoeven hdata->regs->CNTR = 0; 292c2a24a4cSGeert Uytterhoeven scsi_remove_host(instance); 293c2a24a4cSGeert Uytterhoeven free_irq(IRQ_AMIGA_PORTS, instance); 294c2a24a4cSGeert Uytterhoeven scsi_host_put(instance); 295c2a24a4cSGeert Uytterhoeven release_mem_region(res->start, resource_size(res)); 296c2a24a4cSGeert Uytterhoeven return 0; 297c2a24a4cSGeert Uytterhoeven } 298c2a24a4cSGeert Uytterhoeven 299c2a24a4cSGeert Uytterhoeven static struct platform_driver amiga_a3000_scsi_driver = { 300c2a24a4cSGeert Uytterhoeven .remove = __exit_p(amiga_a3000_scsi_remove), 301c2a24a4cSGeert Uytterhoeven .driver = { 302c2a24a4cSGeert Uytterhoeven .name = "amiga-a3000-scsi", 303c2a24a4cSGeert Uytterhoeven }, 304c2a24a4cSGeert Uytterhoeven }; 305c2a24a4cSGeert Uytterhoeven 306a915b84aSJingoo Han module_platform_driver_probe(amiga_a3000_scsi_driver, amiga_a3000_scsi_probe); 307c2a24a4cSGeert Uytterhoeven 308c2a24a4cSGeert Uytterhoeven MODULE_DESCRIPTION("Amiga 3000 built-in SCSI"); 3091da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 310c2a24a4cSGeert Uytterhoeven MODULE_ALIAS("platform:amiga-a3000-scsi"); 311