109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Generic Generic NCR5380 driver
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright 1995-2002, Russell King
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds #include <linux/module.h>
81da177e4SLinus Torvalds #include <linux/ioport.h>
91da177e4SLinus Torvalds #include <linux/blkdev.h>
101da177e4SLinus Torvalds #include <linux/init.h>
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include <asm/ecard.h>
131da177e4SLinus Torvalds #include <asm/io.h>
141da177e4SLinus Torvalds
151da177e4SLinus Torvalds #include <scsi/scsi_host.h>
161da177e4SLinus Torvalds
178b801eadSRussell King #define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
1861e1ce58SFinn Thain #define NCR5380_read(reg) cumanascsi_read(hostdata, reg)
1961e1ce58SFinn Thain #define NCR5380_write(reg, value) cumanascsi_write(hostdata, reg, value)
20ff3d4578SFinn Thain
214a98f896SFinn Thain #define NCR5380_dma_xfer_len cumanascsi_dma_xfer_len
226c4b88caSFinn Thain #define NCR5380_dma_recv_setup cumanascsi_pread
236c4b88caSFinn Thain #define NCR5380_dma_send_setup cumanascsi_pwrite
244a98f896SFinn Thain #define NCR5380_dma_residual NCR5380_dma_residual_none
25ff3d4578SFinn Thain
261da177e4SLinus Torvalds #define NCR5380_intr cumanascsi_intr
271da177e4SLinus Torvalds #define NCR5380_queue_command cumanascsi_queue_command
288c32513bSFinn Thain #define NCR5380_info cumanascsi_info
291da177e4SLinus Torvalds
308b801eadSRussell King #define NCR5380_implementation_fields \
31820682b1SFinn Thain unsigned ctrl
328b801eadSRussell King
337c606631SFinn Thain struct NCR5380_hostdata;
347c606631SFinn Thain static u8 cumanascsi_read(struct NCR5380_hostdata *, unsigned int);
357c606631SFinn Thain static void cumanascsi_write(struct NCR5380_hostdata *, unsigned int, u8);
367c606631SFinn Thain
371da177e4SLinus Torvalds #include "../NCR5380.h"
381da177e4SLinus Torvalds
398b801eadSRussell King #define CTRL 0x16fc
408b801eadSRussell King #define STAT 0x2004
411da177e4SLinus Torvalds #define L(v) (((v)<<16)|((v) & 0x0000ffff))
421da177e4SLinus Torvalds #define H(v) (((v)>>16)|((v) & 0xffff0000))
431da177e4SLinus Torvalds
cumanascsi_pwrite(struct NCR5380_hostdata * hostdata,unsigned char * addr,int len)444a98f896SFinn Thain static inline int cumanascsi_pwrite(struct NCR5380_hostdata *hostdata,
456c4b88caSFinn Thain unsigned char *addr, int len)
461da177e4SLinus Torvalds {
471da177e4SLinus Torvalds unsigned long *laddr;
484a98f896SFinn Thain u8 __iomem *base = hostdata->io;
494a98f896SFinn Thain u8 __iomem *dma = hostdata->pdma_io + 0x2000;
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds if(!len) return 0;
521da177e4SLinus Torvalds
53820682b1SFinn Thain writeb(0x02, base + CTRL);
541da177e4SLinus Torvalds laddr = (unsigned long *)addr;
551da177e4SLinus Torvalds while(len >= 32)
561da177e4SLinus Torvalds {
578b801eadSRussell King unsigned int status;
581da177e4SLinus Torvalds unsigned long v;
59820682b1SFinn Thain status = readb(base + STAT);
601da177e4SLinus Torvalds if(status & 0x80)
611da177e4SLinus Torvalds goto end;
621da177e4SLinus Torvalds if(!(status & 0x40))
631da177e4SLinus Torvalds continue;
648b801eadSRussell King v=*laddr++; writew(L(v), dma); writew(H(v), dma);
658b801eadSRussell King v=*laddr++; writew(L(v), dma); writew(H(v), dma);
668b801eadSRussell King v=*laddr++; writew(L(v), dma); writew(H(v), dma);
678b801eadSRussell King v=*laddr++; writew(L(v), dma); writew(H(v), dma);
688b801eadSRussell King v=*laddr++; writew(L(v), dma); writew(H(v), dma);
698b801eadSRussell King v=*laddr++; writew(L(v), dma); writew(H(v), dma);
708b801eadSRussell King v=*laddr++; writew(L(v), dma); writew(H(v), dma);
718b801eadSRussell King v=*laddr++; writew(L(v), dma); writew(H(v), dma);
721da177e4SLinus Torvalds len -= 32;
731da177e4SLinus Torvalds if(len == 0)
741da177e4SLinus Torvalds break;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds
771da177e4SLinus Torvalds addr = (unsigned char *)laddr;
78820682b1SFinn Thain writeb(0x12, base + CTRL);
798b801eadSRussell King
801da177e4SLinus Torvalds while(len > 0)
811da177e4SLinus Torvalds {
828b801eadSRussell King unsigned int status;
83820682b1SFinn Thain status = readb(base + STAT);
841da177e4SLinus Torvalds if(status & 0x80)
851da177e4SLinus Torvalds goto end;
861da177e4SLinus Torvalds if(status & 0x40)
871da177e4SLinus Torvalds {
888b801eadSRussell King writeb(*addr++, dma);
891da177e4SLinus Torvalds if(--len == 0)
901da177e4SLinus Torvalds break;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds
93820682b1SFinn Thain status = readb(base + STAT);
941da177e4SLinus Torvalds if(status & 0x80)
951da177e4SLinus Torvalds goto end;
961da177e4SLinus Torvalds if(status & 0x40)
971da177e4SLinus Torvalds {
988b801eadSRussell King writeb(*addr++, dma);
991da177e4SLinus Torvalds if(--len == 0)
1001da177e4SLinus Torvalds break;
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds end:
1044a98f896SFinn Thain writeb(hostdata->ctrl | 0x40, base + CTRL);
105438af51cSFinn Thain
106438af51cSFinn Thain if (len)
107438af51cSFinn Thain return -1;
108438af51cSFinn Thain return 0;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds
cumanascsi_pread(struct NCR5380_hostdata * hostdata,unsigned char * addr,int len)1114a98f896SFinn Thain static inline int cumanascsi_pread(struct NCR5380_hostdata *hostdata,
1126c4b88caSFinn Thain unsigned char *addr, int len)
1131da177e4SLinus Torvalds {
1141da177e4SLinus Torvalds unsigned long *laddr;
1154a98f896SFinn Thain u8 __iomem *base = hostdata->io;
1164a98f896SFinn Thain u8 __iomem *dma = hostdata->pdma_io + 0x2000;
1171da177e4SLinus Torvalds
1181da177e4SLinus Torvalds if(!len) return 0;
1191da177e4SLinus Torvalds
120820682b1SFinn Thain writeb(0x00, base + CTRL);
1211da177e4SLinus Torvalds laddr = (unsigned long *)addr;
1221da177e4SLinus Torvalds while(len >= 32)
1231da177e4SLinus Torvalds {
1248b801eadSRussell King unsigned int status;
125820682b1SFinn Thain status = readb(base + STAT);
1261da177e4SLinus Torvalds if(status & 0x80)
1271da177e4SLinus Torvalds goto end;
1281da177e4SLinus Torvalds if(!(status & 0x40))
1291da177e4SLinus Torvalds continue;
1308b801eadSRussell King *laddr++ = readw(dma) | (readw(dma) << 16);
1318b801eadSRussell King *laddr++ = readw(dma) | (readw(dma) << 16);
1328b801eadSRussell King *laddr++ = readw(dma) | (readw(dma) << 16);
1338b801eadSRussell King *laddr++ = readw(dma) | (readw(dma) << 16);
1348b801eadSRussell King *laddr++ = readw(dma) | (readw(dma) << 16);
1358b801eadSRussell King *laddr++ = readw(dma) | (readw(dma) << 16);
1368b801eadSRussell King *laddr++ = readw(dma) | (readw(dma) << 16);
1378b801eadSRussell King *laddr++ = readw(dma) | (readw(dma) << 16);
1381da177e4SLinus Torvalds len -= 32;
1391da177e4SLinus Torvalds if(len == 0)
1401da177e4SLinus Torvalds break;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds
1431da177e4SLinus Torvalds addr = (unsigned char *)laddr;
144820682b1SFinn Thain writeb(0x10, base + CTRL);
1458b801eadSRussell King
1461da177e4SLinus Torvalds while(len > 0)
1471da177e4SLinus Torvalds {
1488b801eadSRussell King unsigned int status;
149820682b1SFinn Thain status = readb(base + STAT);
1501da177e4SLinus Torvalds if(status & 0x80)
1511da177e4SLinus Torvalds goto end;
1521da177e4SLinus Torvalds if(status & 0x40)
1531da177e4SLinus Torvalds {
1548b801eadSRussell King *addr++ = readb(dma);
1551da177e4SLinus Torvalds if(--len == 0)
1561da177e4SLinus Torvalds break;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds
159820682b1SFinn Thain status = readb(base + STAT);
1601da177e4SLinus Torvalds if(status & 0x80)
1611da177e4SLinus Torvalds goto end;
1621da177e4SLinus Torvalds if(status & 0x40)
1631da177e4SLinus Torvalds {
1648b801eadSRussell King *addr++ = readb(dma);
1651da177e4SLinus Torvalds if(--len == 0)
1661da177e4SLinus Torvalds break;
1671da177e4SLinus Torvalds }
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds end:
1704a98f896SFinn Thain writeb(hostdata->ctrl | 0x40, base + CTRL);
171438af51cSFinn Thain
172438af51cSFinn Thain if (len)
173438af51cSFinn Thain return -1;
174438af51cSFinn Thain return 0;
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds
cumanascsi_dma_xfer_len(struct NCR5380_hostdata * hostdata,struct scsi_cmnd * cmd)1774a98f896SFinn Thain static int cumanascsi_dma_xfer_len(struct NCR5380_hostdata *hostdata,
1784a98f896SFinn Thain struct scsi_cmnd *cmd)
1794a98f896SFinn Thain {
1804a98f896SFinn Thain return cmd->transfersize;
1814a98f896SFinn Thain }
1824a98f896SFinn Thain
cumanascsi_read(struct NCR5380_hostdata * hostdata,unsigned int reg)18361e1ce58SFinn Thain static u8 cumanascsi_read(struct NCR5380_hostdata *hostdata,
18461e1ce58SFinn Thain unsigned int reg)
1851da177e4SLinus Torvalds {
18661e1ce58SFinn Thain u8 __iomem *base = hostdata->io;
18761e1ce58SFinn Thain u8 val;
1881da177e4SLinus Torvalds
1898b801eadSRussell King writeb(0, base + CTRL);
1901da177e4SLinus Torvalds
1918b801eadSRussell King val = readb(base + 0x2100 + (reg << 2));
1928b801eadSRussell King
19361e1ce58SFinn Thain hostdata->ctrl = 0x40;
1948b801eadSRussell King writeb(0x40, base + CTRL);
1958b801eadSRussell King
1968b801eadSRussell King return val;
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds
cumanascsi_write(struct NCR5380_hostdata * hostdata,unsigned int reg,u8 value)19961e1ce58SFinn Thain static void cumanascsi_write(struct NCR5380_hostdata *hostdata,
20061e1ce58SFinn Thain unsigned int reg, u8 value)
2011da177e4SLinus Torvalds {
20261e1ce58SFinn Thain u8 __iomem *base = hostdata->io;
2031da177e4SLinus Torvalds
2048b801eadSRussell King writeb(0, base + CTRL);
2058b801eadSRussell King
2068b801eadSRussell King writeb(value, base + 0x2100 + (reg << 2));
2078b801eadSRussell King
20861e1ce58SFinn Thain hostdata->ctrl = 0x40;
2098b801eadSRussell King writeb(0x40, base + CTRL);
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds
2121da177e4SLinus Torvalds #include "../NCR5380.c"
2131da177e4SLinus Torvalds
214*202423c5SBart Van Assche static const struct scsi_host_template cumanascsi_template = {
2151da177e4SLinus Torvalds .module = THIS_MODULE,
2161da177e4SLinus Torvalds .name = "Cumana 16-bit SCSI",
2171da177e4SLinus Torvalds .info = cumanascsi_info,
2181da177e4SLinus Torvalds .queuecommand = cumanascsi_queue_command,
2191da177e4SLinus Torvalds .eh_abort_handler = NCR5380_abort,
22012e5fc66SHannes Reinecke .eh_host_reset_handler = NCR5380_host_reset,
2211da177e4SLinus Torvalds .can_queue = 16,
2221da177e4SLinus Torvalds .this_id = 7,
2231da177e4SLinus Torvalds .sg_tablesize = SG_ALL,
2241da177e4SLinus Torvalds .cmd_per_lun = 2,
2251da177e4SLinus Torvalds .proc_name = "CumanaSCSI-1",
226cd614642SBart Van Assche .cmd_size = sizeof(struct NCR5380_cmd),
2270a4e3612SFinn Thain .max_sectors = 128,
2284af14d11SChristoph Hellwig .dma_boundary = PAGE_SIZE - 1,
2291da177e4SLinus Torvalds };
2301da177e4SLinus Torvalds
cumanascsi1_probe(struct expansion_card * ec,const struct ecard_id * id)2316f039790SGreg Kroah-Hartman static int cumanascsi1_probe(struct expansion_card *ec,
2326f039790SGreg Kroah-Hartman const struct ecard_id *id)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds struct Scsi_Host *host;
2358b801eadSRussell King int ret;
2361da177e4SLinus Torvalds
2378b801eadSRussell King ret = ecard_request_resources(ec);
2388b801eadSRussell King if (ret)
2391da177e4SLinus Torvalds goto out;
2401da177e4SLinus Torvalds
2418b801eadSRussell King host = scsi_host_alloc(&cumanascsi_template, sizeof(struct NCR5380_hostdata));
2428b801eadSRussell King if (!host) {
2438b801eadSRussell King ret = -ENOMEM;
2448b801eadSRussell King goto out_release;
2458b801eadSRussell King }
2468b801eadSRussell King
247820682b1SFinn Thain priv(host)->io = ioremap(ecard_resource_start(ec, ECARD_RES_IOCSLOW),
2488b801eadSRussell King ecard_resource_len(ec, ECARD_RES_IOCSLOW));
249820682b1SFinn Thain priv(host)->pdma_io = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
2508b801eadSRussell King ecard_resource_len(ec, ECARD_RES_MEMC));
251820682b1SFinn Thain if (!priv(host)->io || !priv(host)->pdma_io) {
2528b801eadSRussell King ret = -ENOMEM;
2538b801eadSRussell King goto out_unmap;
2548b801eadSRussell King }
2558b801eadSRussell King
2561da177e4SLinus Torvalds host->irq = ec->irq;
2571da177e4SLinus Torvalds
2588053b0eeSFinn Thain ret = NCR5380_init(host, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP);
2590ad0eff9SFinn Thain if (ret)
2600ad0eff9SFinn Thain goto out_unmap;
2611da177e4SLinus Torvalds
262b6488f97SFinn Thain NCR5380_maybe_reset_bus(host);
263b6488f97SFinn Thain
2648b801eadSRussell King priv(host)->ctrl = 0;
265820682b1SFinn Thain writeb(0, priv(host)->io + CTRL);
2668b801eadSRussell King
2674909cc2bSMichael Opdenacker ret = request_irq(host->irq, cumanascsi_intr, 0,
2681da177e4SLinus Torvalds "CumanaSCSI-1", host);
2691da177e4SLinus Torvalds if (ret) {
2701da177e4SLinus Torvalds printk("scsi%d: IRQ%d not free: %d\n",
2711da177e4SLinus Torvalds host->host_no, host->irq, ret);
2720ad0eff9SFinn Thain goto out_exit;
2731da177e4SLinus Torvalds }
2741da177e4SLinus Torvalds
2751da177e4SLinus Torvalds ret = scsi_add_host(host, &ec->dev);
2761da177e4SLinus Torvalds if (ret)
2771da177e4SLinus Torvalds goto out_free_irq;
2781da177e4SLinus Torvalds
2791da177e4SLinus Torvalds scsi_scan_host(host);
2801da177e4SLinus Torvalds goto out;
2811da177e4SLinus Torvalds
2821da177e4SLinus Torvalds out_free_irq:
2831da177e4SLinus Torvalds free_irq(host->irq, host);
2840ad0eff9SFinn Thain out_exit:
2850ad0eff9SFinn Thain NCR5380_exit(host);
2868b801eadSRussell King out_unmap:
287820682b1SFinn Thain iounmap(priv(host)->io);
288820682b1SFinn Thain iounmap(priv(host)->pdma_io);
2891da177e4SLinus Torvalds scsi_host_put(host);
2908b801eadSRussell King out_release:
2918b801eadSRussell King ecard_release_resources(ec);
2921da177e4SLinus Torvalds out:
2931da177e4SLinus Torvalds return ret;
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds
cumanascsi1_remove(struct expansion_card * ec)2966f039790SGreg Kroah-Hartman static void cumanascsi1_remove(struct expansion_card *ec)
2971da177e4SLinus Torvalds {
2981da177e4SLinus Torvalds struct Scsi_Host *host = ecard_get_drvdata(ec);
299820682b1SFinn Thain void __iomem *base = priv(host)->io;
300820682b1SFinn Thain void __iomem *dma = priv(host)->pdma_io;
3011da177e4SLinus Torvalds
3021da177e4SLinus Torvalds ecard_set_drvdata(ec, NULL);
3031da177e4SLinus Torvalds
3041da177e4SLinus Torvalds scsi_remove_host(host);
3051da177e4SLinus Torvalds free_irq(host->irq, host);
3061da177e4SLinus Torvalds NCR5380_exit(host);
3071da177e4SLinus Torvalds scsi_host_put(host);
308820682b1SFinn Thain iounmap(base);
309820682b1SFinn Thain iounmap(dma);
3108b801eadSRussell King ecard_release_resources(ec);
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds
3131da177e4SLinus Torvalds static const struct ecard_id cumanascsi1_cids[] = {
3141da177e4SLinus Torvalds { MANU_CUMANA, PROD_CUMANA_SCSI_1 },
3151da177e4SLinus Torvalds { 0xffff, 0xffff }
3161da177e4SLinus Torvalds };
3171da177e4SLinus Torvalds
3181da177e4SLinus Torvalds static struct ecard_driver cumanascsi1_driver = {
3191da177e4SLinus Torvalds .probe = cumanascsi1_probe,
3206f039790SGreg Kroah-Hartman .remove = cumanascsi1_remove,
3211da177e4SLinus Torvalds .id_table = cumanascsi1_cids,
3221da177e4SLinus Torvalds .drv = {
3231da177e4SLinus Torvalds .name = "cumanascsi1",
3241da177e4SLinus Torvalds },
3251da177e4SLinus Torvalds };
3261da177e4SLinus Torvalds
cumanascsi_init(void)3271da177e4SLinus Torvalds static int __init cumanascsi_init(void)
3281da177e4SLinus Torvalds {
3291da177e4SLinus Torvalds return ecard_register_driver(&cumanascsi1_driver);
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds
cumanascsi_exit(void)3321da177e4SLinus Torvalds static void __exit cumanascsi_exit(void)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds ecard_remove_driver(&cumanascsi1_driver);
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds
3371da177e4SLinus Torvalds module_init(cumanascsi_init);
3381da177e4SLinus Torvalds module_exit(cumanascsi_exit);
3391da177e4SLinus Torvalds
3401da177e4SLinus Torvalds MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines");
3411da177e4SLinus Torvalds MODULE_LICENSE("GPL");
342