18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/types.h> 38c2ecf20Sopenharmony_ci#include <linux/mm.h> 48c2ecf20Sopenharmony_ci#include <linux/ioport.h> 58c2ecf20Sopenharmony_ci#include <linux/init.h> 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/page.h> 138c2ecf20Sopenharmony_ci#include <asm/amigaints.h> 148c2ecf20Sopenharmony_ci#include <asm/amigahw.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "scsi.h" 178c2ecf20Sopenharmony_ci#include "wd33c93.h" 188c2ecf20Sopenharmony_ci#include "a3000.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct a3000_hostdata { 228c2ecf20Sopenharmony_ci struct WD33C93_hostdata wh; 238c2ecf20Sopenharmony_ci struct a3000_scsiregs *regs; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic irqreturn_t a3000_intr(int irq, void *data) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct Scsi_Host *instance = data; 298c2ecf20Sopenharmony_ci struct a3000_hostdata *hdata = shost_priv(instance); 308c2ecf20Sopenharmony_ci unsigned int status = hdata->regs->ISTR; 318c2ecf20Sopenharmony_ci unsigned long flags; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (!(status & ISTR_INT_P)) 348c2ecf20Sopenharmony_ci return IRQ_NONE; 358c2ecf20Sopenharmony_ci if (status & ISTR_INTS) { 368c2ecf20Sopenharmony_ci spin_lock_irqsave(instance->host_lock, flags); 378c2ecf20Sopenharmony_ci wd33c93_intr(instance); 388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(instance->host_lock, flags); 398c2ecf20Sopenharmony_ci return IRQ_HANDLED; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci pr_warn("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status); 428c2ecf20Sopenharmony_ci return IRQ_NONE; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int dma_setup(struct scsi_cmnd *cmd, int dir_in) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct Scsi_Host *instance = cmd->device->host; 488c2ecf20Sopenharmony_ci struct a3000_hostdata *hdata = shost_priv(instance); 498c2ecf20Sopenharmony_ci struct WD33C93_hostdata *wh = &hdata->wh; 508c2ecf20Sopenharmony_ci struct a3000_scsiregs *regs = hdata->regs; 518c2ecf20Sopenharmony_ci unsigned short cntr = CNTR_PDMD | CNTR_INTEN; 528c2ecf20Sopenharmony_ci unsigned long addr = virt_to_bus(cmd->SCp.ptr); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * if the physical address has the wrong alignment, or if 568c2ecf20Sopenharmony_ci * physical address is bad, or if it is a write and at the 578c2ecf20Sopenharmony_ci * end of a physical memory chunk, then allocate a bounce 588c2ecf20Sopenharmony_ci * buffer 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci if (addr & A3000_XFER_MASK) { 618c2ecf20Sopenharmony_ci wh->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; 628c2ecf20Sopenharmony_ci wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len, 638c2ecf20Sopenharmony_ci GFP_KERNEL); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* can't allocate memory; use PIO */ 668c2ecf20Sopenharmony_ci if (!wh->dma_bounce_buffer) { 678c2ecf20Sopenharmony_ci wh->dma_bounce_len = 0; 688c2ecf20Sopenharmony_ci return 1; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!dir_in) { 728c2ecf20Sopenharmony_ci /* copy to bounce buffer for a write */ 738c2ecf20Sopenharmony_ci memcpy(wh->dma_bounce_buffer, cmd->SCp.ptr, 748c2ecf20Sopenharmony_ci cmd->SCp.this_residual); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci addr = virt_to_bus(wh->dma_bounce_buffer); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* setup dma direction */ 818c2ecf20Sopenharmony_ci if (!dir_in) 828c2ecf20Sopenharmony_ci cntr |= CNTR_DDIR; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* remember direction */ 858c2ecf20Sopenharmony_ci wh->dma_dir = dir_in; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci regs->CNTR = cntr; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* setup DMA *physical* address */ 908c2ecf20Sopenharmony_ci regs->ACR = addr; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (dir_in) { 938c2ecf20Sopenharmony_ci /* invalidate any cache */ 948c2ecf20Sopenharmony_ci cache_clear(addr, cmd->SCp.this_residual); 958c2ecf20Sopenharmony_ci } else { 968c2ecf20Sopenharmony_ci /* push any dirty cache */ 978c2ecf20Sopenharmony_ci cache_push(addr, cmd->SCp.this_residual); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* start DMA */ 1018c2ecf20Sopenharmony_ci mb(); /* make sure setup is completed */ 1028c2ecf20Sopenharmony_ci regs->ST_DMA = 1; 1038c2ecf20Sopenharmony_ci mb(); /* make sure DMA has started before next IO */ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* return success */ 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, 1108c2ecf20Sopenharmony_ci int status) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct a3000_hostdata *hdata = shost_priv(instance); 1138c2ecf20Sopenharmony_ci struct WD33C93_hostdata *wh = &hdata->wh; 1148c2ecf20Sopenharmony_ci struct a3000_scsiregs *regs = hdata->regs; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* disable SCSI interrupts */ 1178c2ecf20Sopenharmony_ci unsigned short cntr = CNTR_PDMD; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (!wh->dma_dir) 1208c2ecf20Sopenharmony_ci cntr |= CNTR_DDIR; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci regs->CNTR = cntr; 1238c2ecf20Sopenharmony_ci mb(); /* make sure CNTR is updated before next IO */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* flush if we were reading */ 1268c2ecf20Sopenharmony_ci if (wh->dma_dir) { 1278c2ecf20Sopenharmony_ci regs->FLUSH = 1; 1288c2ecf20Sopenharmony_ci mb(); /* don't allow prefetch */ 1298c2ecf20Sopenharmony_ci while (!(regs->ISTR & ISTR_FE_FLG)) 1308c2ecf20Sopenharmony_ci barrier(); 1318c2ecf20Sopenharmony_ci mb(); /* no IO until FLUSH is done */ 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* clear a possible interrupt */ 1358c2ecf20Sopenharmony_ci /* I think that this CINT is only necessary if you are 1368c2ecf20Sopenharmony_ci * using the terminal count features. HM 7 Mar 1994 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci regs->CINT = 1; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* stop DMA */ 1418c2ecf20Sopenharmony_ci regs->SP_DMA = 1; 1428c2ecf20Sopenharmony_ci mb(); /* make sure DMA is stopped before next IO */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* restore the CONTROL bits (minus the direction flag) */ 1458c2ecf20Sopenharmony_ci regs->CNTR = CNTR_PDMD | CNTR_INTEN; 1468c2ecf20Sopenharmony_ci mb(); /* make sure CNTR is updated before next IO */ 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* copy from a bounce buffer, if necessary */ 1498c2ecf20Sopenharmony_ci if (status && wh->dma_bounce_buffer) { 1508c2ecf20Sopenharmony_ci if (SCpnt) { 1518c2ecf20Sopenharmony_ci if (wh->dma_dir && SCpnt) 1528c2ecf20Sopenharmony_ci memcpy(SCpnt->SCp.ptr, wh->dma_bounce_buffer, 1538c2ecf20Sopenharmony_ci SCpnt->SCp.this_residual); 1548c2ecf20Sopenharmony_ci kfree(wh->dma_bounce_buffer); 1558c2ecf20Sopenharmony_ci wh->dma_bounce_buffer = NULL; 1568c2ecf20Sopenharmony_ci wh->dma_bounce_len = 0; 1578c2ecf20Sopenharmony_ci } else { 1588c2ecf20Sopenharmony_ci kfree(wh->dma_bounce_buffer); 1598c2ecf20Sopenharmony_ci wh->dma_bounce_buffer = NULL; 1608c2ecf20Sopenharmony_ci wh->dma_bounce_len = 0; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic struct scsi_host_template amiga_a3000_scsi_template = { 1668c2ecf20Sopenharmony_ci .module = THIS_MODULE, 1678c2ecf20Sopenharmony_ci .name = "Amiga 3000 built-in SCSI", 1688c2ecf20Sopenharmony_ci .show_info = wd33c93_show_info, 1698c2ecf20Sopenharmony_ci .write_info = wd33c93_write_info, 1708c2ecf20Sopenharmony_ci .proc_name = "A3000", 1718c2ecf20Sopenharmony_ci .queuecommand = wd33c93_queuecommand, 1728c2ecf20Sopenharmony_ci .eh_abort_handler = wd33c93_abort, 1738c2ecf20Sopenharmony_ci .eh_host_reset_handler = wd33c93_host_reset, 1748c2ecf20Sopenharmony_ci .can_queue = CAN_QUEUE, 1758c2ecf20Sopenharmony_ci .this_id = 7, 1768c2ecf20Sopenharmony_ci .sg_tablesize = SG_ALL, 1778c2ecf20Sopenharmony_ci .cmd_per_lun = CMD_PER_LUN, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int __init amiga_a3000_scsi_probe(struct platform_device *pdev) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct resource *res; 1838c2ecf20Sopenharmony_ci struct Scsi_Host *instance; 1848c2ecf20Sopenharmony_ci int error; 1858c2ecf20Sopenharmony_ci struct a3000_scsiregs *regs; 1868c2ecf20Sopenharmony_ci wd33c93_regs wdregs; 1878c2ecf20Sopenharmony_ci struct a3000_hostdata *hdata; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1908c2ecf20Sopenharmony_ci if (!res) 1918c2ecf20Sopenharmony_ci return -ENODEV; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!request_mem_region(res->start, resource_size(res), "wd33c93")) 1948c2ecf20Sopenharmony_ci return -EBUSY; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci instance = scsi_host_alloc(&amiga_a3000_scsi_template, 1978c2ecf20Sopenharmony_ci sizeof(struct a3000_hostdata)); 1988c2ecf20Sopenharmony_ci if (!instance) { 1998c2ecf20Sopenharmony_ci error = -ENOMEM; 2008c2ecf20Sopenharmony_ci goto fail_alloc; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci instance->irq = IRQ_AMIGA_PORTS; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci regs = ZTWO_VADDR(res->start); 2068c2ecf20Sopenharmony_ci regs->DAWR = DAWR_A3000; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci wdregs.SASR = ®s->SASR; 2098c2ecf20Sopenharmony_ci wdregs.SCMD = ®s->SCMD; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci hdata = shost_priv(instance); 2128c2ecf20Sopenharmony_ci hdata->wh.no_sync = 0xff; 2138c2ecf20Sopenharmony_ci hdata->wh.fast = 0; 2148c2ecf20Sopenharmony_ci hdata->wh.dma_mode = CTRL_DMA; 2158c2ecf20Sopenharmony_ci hdata->regs = regs; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci wd33c93_init(instance, wdregs, dma_setup, dma_stop, WD33C93_FS_12_15); 2188c2ecf20Sopenharmony_ci error = request_irq(IRQ_AMIGA_PORTS, a3000_intr, IRQF_SHARED, 2198c2ecf20Sopenharmony_ci "A3000 SCSI", instance); 2208c2ecf20Sopenharmony_ci if (error) 2218c2ecf20Sopenharmony_ci goto fail_irq; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci regs->CNTR = CNTR_PDMD | CNTR_INTEN; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci error = scsi_add_host(instance, NULL); 2268c2ecf20Sopenharmony_ci if (error) 2278c2ecf20Sopenharmony_ci goto fail_host; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, instance); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci scsi_scan_host(instance); 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cifail_host: 2358c2ecf20Sopenharmony_ci free_irq(IRQ_AMIGA_PORTS, instance); 2368c2ecf20Sopenharmony_cifail_irq: 2378c2ecf20Sopenharmony_ci scsi_host_put(instance); 2388c2ecf20Sopenharmony_cifail_alloc: 2398c2ecf20Sopenharmony_ci release_mem_region(res->start, resource_size(res)); 2408c2ecf20Sopenharmony_ci return error; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int __exit amiga_a3000_scsi_remove(struct platform_device *pdev) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct Scsi_Host *instance = platform_get_drvdata(pdev); 2468c2ecf20Sopenharmony_ci struct a3000_hostdata *hdata = shost_priv(instance); 2478c2ecf20Sopenharmony_ci struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci hdata->regs->CNTR = 0; 2508c2ecf20Sopenharmony_ci scsi_remove_host(instance); 2518c2ecf20Sopenharmony_ci free_irq(IRQ_AMIGA_PORTS, instance); 2528c2ecf20Sopenharmony_ci scsi_host_put(instance); 2538c2ecf20Sopenharmony_ci release_mem_region(res->start, resource_size(res)); 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic struct platform_driver amiga_a3000_scsi_driver = { 2588c2ecf20Sopenharmony_ci .remove = __exit_p(amiga_a3000_scsi_remove), 2598c2ecf20Sopenharmony_ci .driver = { 2608c2ecf20Sopenharmony_ci .name = "amiga-a3000-scsi", 2618c2ecf20Sopenharmony_ci }, 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cimodule_platform_driver_probe(amiga_a3000_scsi_driver, amiga_a3000_scsi_probe); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amiga 3000 built-in SCSI"); 2678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2688c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:amiga-a3000-scsi"); 269