11da177e4SLinus Torvalds #include <linux/types.h> 21da177e4SLinus Torvalds #include <linux/mm.h> 31da177e4SLinus Torvalds #include <linux/ioport.h> 41da177e4SLinus Torvalds #include <linux/init.h> 5c2a24a4cSGeert Uytterhoeven #include <linux/slab.h> 61da177e4SLinus Torvalds #include <linux/spinlock.h> 71da177e4SLinus Torvalds #include <linux/interrupt.h> 8c2a24a4cSGeert Uytterhoeven #include <linux/platform_device.h> 9acf3368fSPaul Gortmaker #include <linux/module.h> 101da177e4SLinus Torvalds 111da177e4SLinus Torvalds #include <asm/page.h> 121da177e4SLinus Torvalds #include <asm/pgtable.h> 131da177e4SLinus Torvalds #include <asm/amigaints.h> 141da177e4SLinus Torvalds #include <asm/amigahw.h> 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds #include "scsi.h" 171da177e4SLinus Torvalds #include "wd33c93.h" 181da177e4SLinus Torvalds #include "a3000.h" 191da177e4SLinus Torvalds 209387edbeSAdrian Bunk 212b21d5e4SGeert Uytterhoeven struct a3000_hostdata { 222b21d5e4SGeert Uytterhoeven struct WD33C93_hostdata wh; 232b21d5e4SGeert Uytterhoeven struct a3000_scsiregs *regs; 242b21d5e4SGeert Uytterhoeven }; 252b21d5e4SGeert Uytterhoeven 26a8169e60SGeert Uytterhoeven static irqreturn_t a3000_intr(int irq, void *data) 271da177e4SLinus Torvalds { 28a8169e60SGeert Uytterhoeven struct Scsi_Host *instance = data; 292b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata = shost_priv(instance); 302b21d5e4SGeert Uytterhoeven unsigned int status = hdata->regs->ISTR; 311da177e4SLinus Torvalds unsigned long flags; 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds if (!(status & ISTR_INT_P)) 341da177e4SLinus Torvalds return IRQ_NONE; 3521351013SGeert Uytterhoeven if (status & ISTR_INTS) { 36a8169e60SGeert Uytterhoeven spin_lock_irqsave(instance->host_lock, flags); 37a8169e60SGeert Uytterhoeven wd33c93_intr(instance); 38a8169e60SGeert Uytterhoeven spin_unlock_irqrestore(instance->host_lock, flags); 391da177e4SLinus Torvalds return IRQ_HANDLED; 401da177e4SLinus Torvalds } 41c2a24a4cSGeert Uytterhoeven pr_warning("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status); 421da177e4SLinus Torvalds return IRQ_NONE; 431da177e4SLinus Torvalds } 441da177e4SLinus Torvalds 4565396410SHenrik Kretzschmar static int dma_setup(struct scsi_cmnd *cmd, int dir_in) 461da177e4SLinus Torvalds { 47a8169e60SGeert Uytterhoeven struct Scsi_Host *instance = cmd->device->host; 482b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata = shost_priv(instance); 492b21d5e4SGeert Uytterhoeven struct WD33C93_hostdata *wh = &hdata->wh; 502b21d5e4SGeert Uytterhoeven struct a3000_scsiregs *regs = hdata->regs; 511da177e4SLinus Torvalds unsigned short cntr = CNTR_PDMD | CNTR_INTEN; 521da177e4SLinus Torvalds unsigned long addr = virt_to_bus(cmd->SCp.ptr); 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds /* 551da177e4SLinus Torvalds * if the physical address has the wrong alignment, or if 561da177e4SLinus Torvalds * physical address is bad, or if it is a write and at the 571da177e4SLinus Torvalds * end of a physical memory chunk, then allocate a bounce 581da177e4SLinus Torvalds * buffer 591da177e4SLinus Torvalds */ 6021351013SGeert Uytterhoeven if (addr & A3000_XFER_MASK) { 612b21d5e4SGeert Uytterhoeven wh->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; 622b21d5e4SGeert Uytterhoeven wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len, 63afdbbc16SGeert Uytterhoeven GFP_KERNEL); 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds /* can't allocate memory; use PIO */ 662b21d5e4SGeert Uytterhoeven if (!wh->dma_bounce_buffer) { 672b21d5e4SGeert Uytterhoeven wh->dma_bounce_len = 0; 681da177e4SLinus Torvalds return 1; 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds if (!dir_in) { 721da177e4SLinus Torvalds /* copy to bounce buffer for a write */ 732b21d5e4SGeert Uytterhoeven memcpy(wh->dma_bounce_buffer, cmd->SCp.ptr, 74afdbbc16SGeert Uytterhoeven cmd->SCp.this_residual); 751da177e4SLinus Torvalds } 761da177e4SLinus Torvalds 772b21d5e4SGeert Uytterhoeven addr = virt_to_bus(wh->dma_bounce_buffer); 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds /* setup dma direction */ 811da177e4SLinus Torvalds if (!dir_in) 821da177e4SLinus Torvalds cntr |= CNTR_DDIR; 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds /* remember direction */ 852b21d5e4SGeert Uytterhoeven wh->dma_dir = dir_in; 861da177e4SLinus Torvalds 87d753722eSGeert Uytterhoeven regs->CNTR = cntr; 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds /* setup DMA *physical* address */ 90d753722eSGeert Uytterhoeven regs->ACR = addr; 911da177e4SLinus Torvalds 9221351013SGeert Uytterhoeven if (dir_in) { 931da177e4SLinus Torvalds /* invalidate any cache */ 941da177e4SLinus Torvalds cache_clear(addr, cmd->SCp.this_residual); 9521351013SGeert Uytterhoeven } else { 961da177e4SLinus Torvalds /* push any dirty cache */ 971da177e4SLinus Torvalds cache_push(addr, cmd->SCp.this_residual); 9821351013SGeert Uytterhoeven } 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds /* start DMA */ 1011da177e4SLinus Torvalds mb(); /* make sure setup is completed */ 102d753722eSGeert Uytterhoeven regs->ST_DMA = 1; 1031da177e4SLinus Torvalds mb(); /* make sure DMA has started before next IO */ 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds /* return success */ 1061da177e4SLinus Torvalds return 0; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 10965396410SHenrik Kretzschmar static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, 1101da177e4SLinus Torvalds int status) 1111da177e4SLinus Torvalds { 1122b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata = shost_priv(instance); 1132b21d5e4SGeert Uytterhoeven struct WD33C93_hostdata *wh = &hdata->wh; 1142b21d5e4SGeert Uytterhoeven struct a3000_scsiregs *regs = hdata->regs; 115afdbbc16SGeert Uytterhoeven 1161da177e4SLinus Torvalds /* disable SCSI interrupts */ 1171da177e4SLinus Torvalds unsigned short cntr = CNTR_PDMD; 1181da177e4SLinus Torvalds 1192b21d5e4SGeert Uytterhoeven if (!wh->dma_dir) 1201da177e4SLinus Torvalds cntr |= CNTR_DDIR; 1211da177e4SLinus Torvalds 122d753722eSGeert Uytterhoeven regs->CNTR = cntr; 1231da177e4SLinus Torvalds mb(); /* make sure CNTR is updated before next IO */ 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds /* flush if we were reading */ 1262b21d5e4SGeert Uytterhoeven if (wh->dma_dir) { 127d753722eSGeert Uytterhoeven regs->FLUSH = 1; 1281da177e4SLinus Torvalds mb(); /* don't allow prefetch */ 129d753722eSGeert Uytterhoeven while (!(regs->ISTR & ISTR_FE_FLG)) 1301da177e4SLinus Torvalds barrier(); 1311da177e4SLinus Torvalds mb(); /* no IO until FLUSH is done */ 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds /* clear a possible interrupt */ 1351da177e4SLinus Torvalds /* I think that this CINT is only necessary if you are 1361da177e4SLinus Torvalds * using the terminal count features. HM 7 Mar 1994 1371da177e4SLinus Torvalds */ 138d753722eSGeert Uytterhoeven regs->CINT = 1; 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds /* stop DMA */ 141d753722eSGeert Uytterhoeven regs->SP_DMA = 1; 1421da177e4SLinus Torvalds mb(); /* make sure DMA is stopped before next IO */ 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds /* restore the CONTROL bits (minus the direction flag) */ 145d753722eSGeert Uytterhoeven regs->CNTR = CNTR_PDMD | CNTR_INTEN; 1461da177e4SLinus Torvalds mb(); /* make sure CNTR is updated before next IO */ 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds /* copy from a bounce buffer, if necessary */ 1492b21d5e4SGeert Uytterhoeven if (status && wh->dma_bounce_buffer) { 150cc0455faSBoaz Harrosh if (SCpnt) { 1512b21d5e4SGeert Uytterhoeven if (wh->dma_dir && SCpnt) 1522b21d5e4SGeert Uytterhoeven memcpy(SCpnt->SCp.ptr, wh->dma_bounce_buffer, 1531da177e4SLinus Torvalds SCpnt->SCp.this_residual); 1542b21d5e4SGeert Uytterhoeven kfree(wh->dma_bounce_buffer); 1552b21d5e4SGeert Uytterhoeven wh->dma_bounce_buffer = NULL; 1562b21d5e4SGeert Uytterhoeven wh->dma_bounce_len = 0; 1571da177e4SLinus Torvalds } else { 1582b21d5e4SGeert Uytterhoeven kfree(wh->dma_bounce_buffer); 1592b21d5e4SGeert Uytterhoeven wh->dma_bounce_buffer = NULL; 1602b21d5e4SGeert Uytterhoeven wh->dma_bounce_len = 0; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 16565396410SHenrik Kretzschmar static int a3000_bus_reset(struct scsi_cmnd *cmd) 1661da177e4SLinus Torvalds { 167c2a24a4cSGeert Uytterhoeven struct Scsi_Host *instance = cmd->device->host; 168c2a24a4cSGeert Uytterhoeven 1691da177e4SLinus Torvalds /* FIXME perform bus-specific reset */ 170df0ae249SJeff Garzik 171df0ae249SJeff Garzik /* FIXME 2: kill this entire function, which should 172df0ae249SJeff Garzik cause mid-layer to call wd33c93_host_reset anyway? */ 17368b3aa7cSJeff Garzik 174c2a24a4cSGeert Uytterhoeven spin_lock_irq(instance->host_lock); 1751da177e4SLinus Torvalds wd33c93_host_reset(cmd); 176c2a24a4cSGeert Uytterhoeven spin_unlock_irq(instance->host_lock); 17768b3aa7cSJeff Garzik 1781da177e4SLinus Torvalds return SUCCESS; 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 181c2a24a4cSGeert Uytterhoeven static struct scsi_host_template amiga_a3000_scsi_template = { 182c2a24a4cSGeert Uytterhoeven .module = THIS_MODULE, 1831da177e4SLinus Torvalds .name = "Amiga 3000 built-in SCSI", 184*408bb25bSAl Viro .show_info = wd33c93_show_info, 185*408bb25bSAl Viro .write_info = wd33c93_write_info, 186c2a24a4cSGeert Uytterhoeven .proc_name = "A3000", 1871da177e4SLinus Torvalds .queuecommand = wd33c93_queuecommand, 1881da177e4SLinus Torvalds .eh_abort_handler = wd33c93_abort, 1891da177e4SLinus Torvalds .eh_bus_reset_handler = a3000_bus_reset, 1901da177e4SLinus Torvalds .eh_host_reset_handler = wd33c93_host_reset, 1911da177e4SLinus Torvalds .can_queue = CAN_QUEUE, 1921da177e4SLinus Torvalds .this_id = 7, 1931da177e4SLinus Torvalds .sg_tablesize = SG_ALL, 1941da177e4SLinus Torvalds .cmd_per_lun = CMD_PER_LUN, 1951da177e4SLinus Torvalds .use_clustering = ENABLE_CLUSTERING 1961da177e4SLinus Torvalds }; 1971da177e4SLinus Torvalds 198c2a24a4cSGeert Uytterhoeven static int __init amiga_a3000_scsi_probe(struct platform_device *pdev) 1991da177e4SLinus Torvalds { 200c2a24a4cSGeert Uytterhoeven struct resource *res; 201c2a24a4cSGeert Uytterhoeven struct Scsi_Host *instance; 202c2a24a4cSGeert Uytterhoeven int error; 203c2a24a4cSGeert Uytterhoeven struct a3000_scsiregs *regs; 204c2a24a4cSGeert Uytterhoeven wd33c93_regs wdregs; 2052b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata; 206d753722eSGeert Uytterhoeven 207c2a24a4cSGeert Uytterhoeven res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 208c2a24a4cSGeert Uytterhoeven if (!res) 209c2a24a4cSGeert Uytterhoeven return -ENODEV; 210c2a24a4cSGeert Uytterhoeven 211c2a24a4cSGeert Uytterhoeven if (!request_mem_region(res->start, resource_size(res), "wd33c93")) 212c2a24a4cSGeert Uytterhoeven return -EBUSY; 213c2a24a4cSGeert Uytterhoeven 214c2a24a4cSGeert Uytterhoeven instance = scsi_host_alloc(&amiga_a3000_scsi_template, 2152b21d5e4SGeert Uytterhoeven sizeof(struct a3000_hostdata)); 216c2a24a4cSGeert Uytterhoeven if (!instance) { 217c2a24a4cSGeert Uytterhoeven error = -ENOMEM; 218c2a24a4cSGeert Uytterhoeven goto fail_alloc; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 221c2a24a4cSGeert Uytterhoeven instance->irq = IRQ_AMIGA_PORTS; 222c2a24a4cSGeert Uytterhoeven 2232b21d5e4SGeert Uytterhoeven regs = (struct a3000_scsiregs *)ZTWO_VADDR(res->start); 224c2a24a4cSGeert Uytterhoeven regs->DAWR = DAWR_A3000; 225c2a24a4cSGeert Uytterhoeven 226c2a24a4cSGeert Uytterhoeven wdregs.SASR = ®s->SASR; 227c2a24a4cSGeert Uytterhoeven wdregs.SCMD = ®s->SCMD; 228c2a24a4cSGeert Uytterhoeven 229c2a24a4cSGeert Uytterhoeven hdata = shost_priv(instance); 2302b21d5e4SGeert Uytterhoeven hdata->wh.no_sync = 0xff; 2312b21d5e4SGeert Uytterhoeven hdata->wh.fast = 0; 2322b21d5e4SGeert Uytterhoeven hdata->wh.dma_mode = CTRL_DMA; 2332b21d5e4SGeert Uytterhoeven hdata->regs = regs; 234c2a24a4cSGeert Uytterhoeven 235c2a24a4cSGeert Uytterhoeven wd33c93_init(instance, wdregs, dma_setup, dma_stop, WD33C93_FS_12_15); 236c2a24a4cSGeert Uytterhoeven error = request_irq(IRQ_AMIGA_PORTS, a3000_intr, IRQF_SHARED, 237c2a24a4cSGeert Uytterhoeven "A3000 SCSI", instance); 238c2a24a4cSGeert Uytterhoeven if (error) 239c2a24a4cSGeert Uytterhoeven goto fail_irq; 240c2a24a4cSGeert Uytterhoeven 241c2a24a4cSGeert Uytterhoeven regs->CNTR = CNTR_PDMD | CNTR_INTEN; 242c2a24a4cSGeert Uytterhoeven 243c2a24a4cSGeert Uytterhoeven error = scsi_add_host(instance, NULL); 244c2a24a4cSGeert Uytterhoeven if (error) 245c2a24a4cSGeert Uytterhoeven goto fail_host; 246c2a24a4cSGeert Uytterhoeven 247c2a24a4cSGeert Uytterhoeven platform_set_drvdata(pdev, instance); 248c2a24a4cSGeert Uytterhoeven 249c2a24a4cSGeert Uytterhoeven scsi_scan_host(instance); 250c2a24a4cSGeert Uytterhoeven return 0; 251c2a24a4cSGeert Uytterhoeven 252c2a24a4cSGeert Uytterhoeven fail_host: 253c2a24a4cSGeert Uytterhoeven free_irq(IRQ_AMIGA_PORTS, instance); 254c2a24a4cSGeert Uytterhoeven fail_irq: 255c2a24a4cSGeert Uytterhoeven scsi_host_put(instance); 256c2a24a4cSGeert Uytterhoeven fail_alloc: 257c2a24a4cSGeert Uytterhoeven release_mem_region(res->start, resource_size(res)); 258c2a24a4cSGeert Uytterhoeven return error; 259c2a24a4cSGeert Uytterhoeven } 260c2a24a4cSGeert Uytterhoeven 261c2a24a4cSGeert Uytterhoeven static int __exit amiga_a3000_scsi_remove(struct platform_device *pdev) 262c2a24a4cSGeert Uytterhoeven { 263c2a24a4cSGeert Uytterhoeven struct Scsi_Host *instance = platform_get_drvdata(pdev); 2642b21d5e4SGeert Uytterhoeven struct a3000_hostdata *hdata = shost_priv(instance); 265c2a24a4cSGeert Uytterhoeven struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 266c2a24a4cSGeert Uytterhoeven 2672b21d5e4SGeert Uytterhoeven hdata->regs->CNTR = 0; 268c2a24a4cSGeert Uytterhoeven scsi_remove_host(instance); 269c2a24a4cSGeert Uytterhoeven free_irq(IRQ_AMIGA_PORTS, instance); 270c2a24a4cSGeert Uytterhoeven scsi_host_put(instance); 271c2a24a4cSGeert Uytterhoeven release_mem_region(res->start, resource_size(res)); 272c2a24a4cSGeert Uytterhoeven return 0; 273c2a24a4cSGeert Uytterhoeven } 274c2a24a4cSGeert Uytterhoeven 275c2a24a4cSGeert Uytterhoeven static struct platform_driver amiga_a3000_scsi_driver = { 276c2a24a4cSGeert Uytterhoeven .remove = __exit_p(amiga_a3000_scsi_remove), 277c2a24a4cSGeert Uytterhoeven .driver = { 278c2a24a4cSGeert Uytterhoeven .name = "amiga-a3000-scsi", 279c2a24a4cSGeert Uytterhoeven .owner = THIS_MODULE, 280c2a24a4cSGeert Uytterhoeven }, 281c2a24a4cSGeert Uytterhoeven }; 282c2a24a4cSGeert Uytterhoeven 283c2a24a4cSGeert Uytterhoeven static int __init amiga_a3000_scsi_init(void) 284c2a24a4cSGeert Uytterhoeven { 285c2a24a4cSGeert Uytterhoeven return platform_driver_probe(&amiga_a3000_scsi_driver, 286c2a24a4cSGeert Uytterhoeven amiga_a3000_scsi_probe); 287c2a24a4cSGeert Uytterhoeven } 288c2a24a4cSGeert Uytterhoeven module_init(amiga_a3000_scsi_init); 289c2a24a4cSGeert Uytterhoeven 290c2a24a4cSGeert Uytterhoeven static void __exit amiga_a3000_scsi_exit(void) 291c2a24a4cSGeert Uytterhoeven { 292c2a24a4cSGeert Uytterhoeven platform_driver_unregister(&amiga_a3000_scsi_driver); 293c2a24a4cSGeert Uytterhoeven } 294c2a24a4cSGeert Uytterhoeven module_exit(amiga_a3000_scsi_exit); 295c2a24a4cSGeert Uytterhoeven 296c2a24a4cSGeert Uytterhoeven MODULE_DESCRIPTION("Amiga 3000 built-in SCSI"); 2971da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 298c2a24a4cSGeert Uytterhoeven MODULE_ALIAS("platform:amiga-a3000-scsi"); 299