109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
3f30c2269SUwe Zeisberger * linux/drivers/scsi/arm/arxescsi.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1997-2000 Russell King, Stefan Hanske
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * This driver is based on experimentation. Hence, it may have made
81da177e4SLinus Torvalds * assumptions about the particular card that I have available, and
91da177e4SLinus Torvalds * may not be reliable!
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds * Changelog:
121da177e4SLinus Torvalds * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c
131da177e4SLinus Torvalds * 22-01-1998 RMK 0.0.1 Updated to 2.1.80
141da177e4SLinus Torvalds * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it.
151da177e4SLinus Torvalds * 11-06-1998 SH 0.0.2 Changed to support ARXE 16-bit SCSI card
161da177e4SLinus Torvalds * enabled writing
171da177e4SLinus Torvalds * 01-01-2000 SH 0.1.0 Added *real* pseudo dma writing
181da177e4SLinus Torvalds * (arxescsi_pseudo_dma_write)
191da177e4SLinus Torvalds * 02-04-2000 RMK 0.1.1 Updated for new error handling code.
201da177e4SLinus Torvalds * 22-10-2000 SH Updated for new registering scheme.
211da177e4SLinus Torvalds */
221da177e4SLinus Torvalds #include <linux/module.h>
231da177e4SLinus Torvalds #include <linux/blkdev.h>
241da177e4SLinus Torvalds #include <linux/kernel.h>
251da177e4SLinus Torvalds #include <linux/string.h>
261da177e4SLinus Torvalds #include <linux/ioport.h>
271da177e4SLinus Torvalds #include <linux/proc_fs.h>
281da177e4SLinus Torvalds #include <linux/unistd.h>
291da177e4SLinus Torvalds #include <linux/stat.h>
301da177e4SLinus Torvalds #include <linux/delay.h>
311da177e4SLinus Torvalds #include <linux/init.h>
321da177e4SLinus Torvalds #include <linux/interrupt.h>
331da177e4SLinus Torvalds
341da177e4SLinus Torvalds #include <asm/dma.h>
351da177e4SLinus Torvalds #include <asm/io.h>
361da177e4SLinus Torvalds #include <asm/ecard.h>
371da177e4SLinus Torvalds
3853555fb7SBart Van Assche #include <scsi/scsi.h>
3953555fb7SBart Van Assche #include <scsi/scsi_cmnd.h>
4053555fb7SBart Van Assche #include <scsi/scsi_device.h>
4153555fb7SBart Van Assche #include <scsi/scsi_eh.h>
421da177e4SLinus Torvalds #include <scsi/scsi_host.h>
4353555fb7SBart Van Assche #include <scsi/scsi_tcq.h>
441da177e4SLinus Torvalds #include "fas216.h"
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds struct arxescsi_info {
471da177e4SLinus Torvalds FAS216_Info info;
481da177e4SLinus Torvalds struct expansion_card *ec;
491da177e4SLinus Torvalds void __iomem *base;
501da177e4SLinus Torvalds };
511da177e4SLinus Torvalds
521da177e4SLinus Torvalds #define DMADATA_OFFSET (0x200)
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds #define DMASTAT_OFFSET (0x600)
551da177e4SLinus Torvalds #define DMASTAT_DRQ (1 << 0)
561da177e4SLinus Torvalds
571da177e4SLinus Torvalds #define CSTATUS_IRQ (1 << 0)
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds #define VERSION "1.10 (23/01/2003 2.5.57)"
601da177e4SLinus Torvalds
611da177e4SLinus Torvalds /*
621da177e4SLinus Torvalds * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type)
631da177e4SLinus Torvalds * Purpose : initialises DMA/PIO
641da177e4SLinus Torvalds * Params : host - host
651da177e4SLinus Torvalds * SCpnt - command
661da177e4SLinus Torvalds * direction - DMA on to/off of card
671da177e4SLinus Torvalds * min_type - minimum DMA support that we must have for this transfer
681da177e4SLinus Torvalds * Returns : 0 if we should not set CMD_WITHDMA for transfer info command
691da177e4SLinus Torvalds */
701da177e4SLinus Torvalds static fasdmatype_t
arxescsi_dma_setup(struct Scsi_Host * host,struct scsi_pointer * SCp,fasdmadir_t direction,fasdmatype_t min_type)710a04137eSChristoph Hellwig arxescsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp,
721da177e4SLinus Torvalds fasdmadir_t direction, fasdmatype_t min_type)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds /*
751da177e4SLinus Torvalds * We don't do real DMA
761da177e4SLinus Torvalds */
771da177e4SLinus Torvalds return fasdma_pseudo;
781da177e4SLinus Torvalds }
791da177e4SLinus Torvalds
arxescsi_pseudo_dma_write(unsigned char * addr,void __iomem * base)801da177e4SLinus Torvalds static void arxescsi_pseudo_dma_write(unsigned char *addr, void __iomem *base)
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds __asm__ __volatile__(
831da177e4SLinus Torvalds " stmdb sp!, {r0-r12}\n"
841da177e4SLinus Torvalds " mov r3, %0\n"
851da177e4SLinus Torvalds " mov r1, %1\n"
861da177e4SLinus Torvalds " add r2, r1, #512\n"
871da177e4SLinus Torvalds " mov r4, #256\n"
881da177e4SLinus Torvalds ".loop_1: ldmia r3!, {r6, r8, r10, r12}\n"
891da177e4SLinus Torvalds " mov r5, r6, lsl #16\n"
901da177e4SLinus Torvalds " mov r7, r8, lsl #16\n"
911da177e4SLinus Torvalds ".loop_2: ldrb r0, [r1, #1536]\n"
921da177e4SLinus Torvalds " tst r0, #1\n"
931da177e4SLinus Torvalds " beq .loop_2\n"
941da177e4SLinus Torvalds " stmia r2, {r5-r8}\n\t"
951da177e4SLinus Torvalds " mov r9, r10, lsl #16\n"
961da177e4SLinus Torvalds " mov r11, r12, lsl #16\n"
971da177e4SLinus Torvalds ".loop_3: ldrb r0, [r1, #1536]\n"
981da177e4SLinus Torvalds " tst r0, #1\n"
991da177e4SLinus Torvalds " beq .loop_3\n"
1001da177e4SLinus Torvalds " stmia r2, {r9-r12}\n"
1011da177e4SLinus Torvalds " subs r4, r4, #16\n"
1021da177e4SLinus Torvalds " bne .loop_1\n"
1031da177e4SLinus Torvalds " ldmia sp!, {r0-r12}\n"
1041da177e4SLinus Torvalds :
1051da177e4SLinus Torvalds : "r" (addr), "r" (base));
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds
1081da177e4SLinus Torvalds /*
1091da177e4SLinus Torvalds * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer)
1101da177e4SLinus Torvalds * Purpose : handles pseudo DMA
1111da177e4SLinus Torvalds * Params : host - host
1121da177e4SLinus Torvalds * SCpnt - command
1131da177e4SLinus Torvalds * direction - DMA on to/off of card
1141da177e4SLinus Torvalds * transfer - minimum number of bytes we expect to transfer
1151da177e4SLinus Torvalds */
1161da177e4SLinus Torvalds static void
arxescsi_dma_pseudo(struct Scsi_Host * host,struct scsi_pointer * SCp,fasdmadir_t direction,int transfer)1170a04137eSChristoph Hellwig arxescsi_dma_pseudo(struct Scsi_Host *host, struct scsi_pointer *SCp,
1181da177e4SLinus Torvalds fasdmadir_t direction, int transfer)
1191da177e4SLinus Torvalds {
1201da177e4SLinus Torvalds struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;
1211da177e4SLinus Torvalds unsigned int length, error = 0;
1221da177e4SLinus Torvalds void __iomem *base = info->info.scsi.io_base;
1231da177e4SLinus Torvalds unsigned char *addr;
1241da177e4SLinus Torvalds
1251da177e4SLinus Torvalds length = SCp->this_residual;
1261da177e4SLinus Torvalds addr = SCp->ptr;
1271da177e4SLinus Torvalds
1281da177e4SLinus Torvalds if (direction == DMA_OUT) {
1291da177e4SLinus Torvalds unsigned int word;
1301da177e4SLinus Torvalds while (length > 256) {
1311da177e4SLinus Torvalds if (readb(base + 0x80) & STAT_INT) {
1321da177e4SLinus Torvalds error = 1;
1331da177e4SLinus Torvalds break;
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds arxescsi_pseudo_dma_write(addr, base);
1361da177e4SLinus Torvalds addr += 256;
1371da177e4SLinus Torvalds length -= 256;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds
1401da177e4SLinus Torvalds if (!error)
1411da177e4SLinus Torvalds while (length > 0) {
1421da177e4SLinus Torvalds if (readb(base + 0x80) & STAT_INT)
1431da177e4SLinus Torvalds break;
1441da177e4SLinus Torvalds
1451da177e4SLinus Torvalds if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))
1461da177e4SLinus Torvalds continue;
1471da177e4SLinus Torvalds
1481da177e4SLinus Torvalds word = *addr | *(addr + 1) << 8;
1491da177e4SLinus Torvalds
1501da177e4SLinus Torvalds writew(word, base + DMADATA_OFFSET);
1511da177e4SLinus Torvalds if (length > 1) {
1521da177e4SLinus Torvalds addr += 2;
1531da177e4SLinus Torvalds length -= 2;
1541da177e4SLinus Torvalds } else {
1551da177e4SLinus Torvalds addr += 1;
1561da177e4SLinus Torvalds length -= 1;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds else {
1611da177e4SLinus Torvalds if (transfer && (transfer & 255)) {
1621da177e4SLinus Torvalds while (length >= 256) {
1631da177e4SLinus Torvalds if (readb(base + 0x80) & STAT_INT) {
1641da177e4SLinus Torvalds error = 1;
1651da177e4SLinus Torvalds break;
1661da177e4SLinus Torvalds }
1671da177e4SLinus Torvalds
1681da177e4SLinus Torvalds if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))
1691da177e4SLinus Torvalds continue;
1701da177e4SLinus Torvalds
1711da177e4SLinus Torvalds readsw(base + DMADATA_OFFSET, addr, 256 >> 1);
1721da177e4SLinus Torvalds addr += 256;
1731da177e4SLinus Torvalds length -= 256;
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds
1771da177e4SLinus Torvalds if (!(error))
1781da177e4SLinus Torvalds while (length > 0) {
1791da177e4SLinus Torvalds unsigned long word;
1801da177e4SLinus Torvalds
1811da177e4SLinus Torvalds if (readb(base + 0x80) & STAT_INT)
1821da177e4SLinus Torvalds break;
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))
1851da177e4SLinus Torvalds continue;
1861da177e4SLinus Torvalds
1871da177e4SLinus Torvalds word = readw(base + DMADATA_OFFSET);
1881da177e4SLinus Torvalds *addr++ = word;
1891da177e4SLinus Torvalds if (--length > 0) {
1901da177e4SLinus Torvalds *addr++ = word >> 8;
1911da177e4SLinus Torvalds length --;
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds
1971da177e4SLinus Torvalds /*
1981da177e4SLinus Torvalds * Function: int arxescsi_dma_stop(host, SCpnt)
1991da177e4SLinus Torvalds * Purpose : stops DMA/PIO
2001da177e4SLinus Torvalds * Params : host - host
2011da177e4SLinus Torvalds * SCpnt - command
2021da177e4SLinus Torvalds */
arxescsi_dma_stop(struct Scsi_Host * host,struct scsi_pointer * SCp)2030a04137eSChristoph Hellwig static void arxescsi_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp)
2041da177e4SLinus Torvalds {
2051da177e4SLinus Torvalds /*
2061da177e4SLinus Torvalds * no DMA to stop
2071da177e4SLinus Torvalds */
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds
2101da177e4SLinus Torvalds /*
2111da177e4SLinus Torvalds * Function: const char *arxescsi_info(struct Scsi_Host * host)
2121da177e4SLinus Torvalds * Purpose : returns a descriptive string about this interface,
2131da177e4SLinus Torvalds * Params : host - driver host structure to return info for.
2141da177e4SLinus Torvalds * Returns : pointer to a static buffer containing null terminated string.
2151da177e4SLinus Torvalds */
arxescsi_info(struct Scsi_Host * host)2161da177e4SLinus Torvalds static const char *arxescsi_info(struct Scsi_Host *host)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;
2191da177e4SLinus Torvalds static char string[150];
2201da177e4SLinus Torvalds
2211da177e4SLinus Torvalds sprintf(string, "%s (%s) in slot %d v%s",
2221da177e4SLinus Torvalds host->hostt->name, info->info.scsi.type, info->ec->slot_no,
2231da177e4SLinus Torvalds VERSION);
2241da177e4SLinus Torvalds
2251da177e4SLinus Torvalds return string;
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds
2281da177e4SLinus Torvalds static int
arxescsi_show_info(struct seq_file * m,struct Scsi_Host * host)2299d4e5c54SAl Viro arxescsi_show_info(struct seq_file *m, struct Scsi_Host *host)
2301da177e4SLinus Torvalds {
2311da177e4SLinus Torvalds struct arxescsi_info *info;
2321da177e4SLinus Torvalds info = (struct arxescsi_info *)host->hostdata;
2331da177e4SLinus Torvalds
2349d4e5c54SAl Viro seq_printf(m, "ARXE 16-bit SCSI driver v%s\n", VERSION);
2359d4e5c54SAl Viro fas216_print_host(&info->info, m);
2369d4e5c54SAl Viro fas216_print_stats(&info->info, m);
2379d4e5c54SAl Viro fas216_print_devices(&info->info, m);
2389d4e5c54SAl Viro return 0;
2391da177e4SLinus Torvalds }
2401da177e4SLinus Torvalds
241*116e5de7SBart Van Assche static const struct scsi_host_template arxescsi_template = {
2429d4e5c54SAl Viro .show_info = arxescsi_show_info,
2431da177e4SLinus Torvalds .name = "ARXE SCSI card",
2441da177e4SLinus Torvalds .info = arxescsi_info,
2451da177e4SLinus Torvalds .queuecommand = fas216_noqueue_command,
2461da177e4SLinus Torvalds .eh_host_reset_handler = fas216_eh_host_reset,
2471da177e4SLinus Torvalds .eh_bus_reset_handler = fas216_eh_bus_reset,
2481da177e4SLinus Torvalds .eh_device_reset_handler = fas216_eh_device_reset,
2491da177e4SLinus Torvalds .eh_abort_handler = fas216_eh_abort,
250caffd3adSBart Van Assche .cmd_size = sizeof(struct fas216_cmd_priv),
2511da177e4SLinus Torvalds .can_queue = 0,
2521da177e4SLinus Torvalds .this_id = 7,
2531da177e4SLinus Torvalds .sg_tablesize = SG_ALL,
2544af14d11SChristoph Hellwig .dma_boundary = PAGE_SIZE - 1,
2551da177e4SLinus Torvalds .proc_name = "arxescsi",
2561da177e4SLinus Torvalds };
2571da177e4SLinus Torvalds
arxescsi_probe(struct expansion_card * ec,const struct ecard_id * id)2586f039790SGreg Kroah-Hartman static int arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)
2591da177e4SLinus Torvalds {
2601da177e4SLinus Torvalds struct Scsi_Host *host;
2611da177e4SLinus Torvalds struct arxescsi_info *info;
2621da177e4SLinus Torvalds void __iomem *base;
2631da177e4SLinus Torvalds int ret;
2641da177e4SLinus Torvalds
2651da177e4SLinus Torvalds ret = ecard_request_resources(ec);
2661da177e4SLinus Torvalds if (ret)
2671da177e4SLinus Torvalds goto out;
2681da177e4SLinus Torvalds
26910bdaaa0SRussell King base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
2701da177e4SLinus Torvalds if (!base) {
2711da177e4SLinus Torvalds ret = -ENOMEM;
2721da177e4SLinus Torvalds goto out_region;
2731da177e4SLinus Torvalds }
2741da177e4SLinus Torvalds
2751da177e4SLinus Torvalds host = scsi_host_alloc(&arxescsi_template, sizeof(struct arxescsi_info));
2761da177e4SLinus Torvalds if (!host) {
2771da177e4SLinus Torvalds ret = -ENOMEM;
27810bdaaa0SRussell King goto out_region;
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds
2811da177e4SLinus Torvalds info = (struct arxescsi_info *)host->hostdata;
2821da177e4SLinus Torvalds info->ec = ec;
2831da177e4SLinus Torvalds info->base = base;
2841da177e4SLinus Torvalds
2851da177e4SLinus Torvalds info->info.scsi.io_base = base + 0x2000;
28641569e37SRussell King info->info.scsi.irq = 0;
2871da177e4SLinus Torvalds info->info.scsi.dma = NO_DMA;
2881da177e4SLinus Torvalds info->info.scsi.io_shift = 5;
2891da177e4SLinus Torvalds info->info.ifcfg.clockrate = 24; /* MHz */
2901da177e4SLinus Torvalds info->info.ifcfg.select_timeout = 255;
2911da177e4SLinus Torvalds info->info.ifcfg.asyncperiod = 200; /* ns */
2921da177e4SLinus Torvalds info->info.ifcfg.sync_max_depth = 0;
2931da177e4SLinus Torvalds info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK;
2941da177e4SLinus Torvalds info->info.ifcfg.disconnect_ok = 0;
2951da177e4SLinus Torvalds info->info.ifcfg.wide_max_size = 0;
2961da177e4SLinus Torvalds info->info.ifcfg.capabilities = FASCAP_PSEUDODMA;
2971da177e4SLinus Torvalds info->info.dma.setup = arxescsi_dma_setup;
2981da177e4SLinus Torvalds info->info.dma.pseudo = arxescsi_dma_pseudo;
2991da177e4SLinus Torvalds info->info.dma.stop = arxescsi_dma_stop;
3001da177e4SLinus Torvalds
3011da177e4SLinus Torvalds ec->irqaddr = base;
3021da177e4SLinus Torvalds ec->irqmask = CSTATUS_IRQ;
3031da177e4SLinus Torvalds
3041da177e4SLinus Torvalds ret = fas216_init(host);
3051da177e4SLinus Torvalds if (ret)
3061da177e4SLinus Torvalds goto out_unregister;
3071da177e4SLinus Torvalds
3081da177e4SLinus Torvalds ret = fas216_add(host, &ec->dev);
3091da177e4SLinus Torvalds if (ret == 0)
3101da177e4SLinus Torvalds goto out;
3111da177e4SLinus Torvalds
3121da177e4SLinus Torvalds fas216_release(host);
3131da177e4SLinus Torvalds out_unregister:
3141da177e4SLinus Torvalds scsi_host_put(host);
3151da177e4SLinus Torvalds out_region:
3161da177e4SLinus Torvalds ecard_release_resources(ec);
3171da177e4SLinus Torvalds out:
3181da177e4SLinus Torvalds return ret;
3191da177e4SLinus Torvalds }
3201da177e4SLinus Torvalds
arxescsi_remove(struct expansion_card * ec)3216f039790SGreg Kroah-Hartman static void arxescsi_remove(struct expansion_card *ec)
3221da177e4SLinus Torvalds {
3231da177e4SLinus Torvalds struct Scsi_Host *host = ecard_get_drvdata(ec);
3241da177e4SLinus Torvalds
3251da177e4SLinus Torvalds ecard_set_drvdata(ec, NULL);
3261da177e4SLinus Torvalds fas216_remove(host);
3271da177e4SLinus Torvalds
3281da177e4SLinus Torvalds fas216_release(host);
3291da177e4SLinus Torvalds scsi_host_put(host);
3301da177e4SLinus Torvalds ecard_release_resources(ec);
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds
3331da177e4SLinus Torvalds static const struct ecard_id arxescsi_cids[] = {
3341da177e4SLinus Torvalds { MANU_ARXE, PROD_ARXE_SCSI },
3351da177e4SLinus Torvalds { 0xffff, 0xffff },
3361da177e4SLinus Torvalds };
3371da177e4SLinus Torvalds
3381da177e4SLinus Torvalds static struct ecard_driver arxescsi_driver = {
3391da177e4SLinus Torvalds .probe = arxescsi_probe,
3406f039790SGreg Kroah-Hartman .remove = arxescsi_remove,
3411da177e4SLinus Torvalds .id_table = arxescsi_cids,
3421da177e4SLinus Torvalds .drv = {
3431da177e4SLinus Torvalds .name = "arxescsi",
3441da177e4SLinus Torvalds },
3451da177e4SLinus Torvalds };
3461da177e4SLinus Torvalds
init_arxe_scsi_driver(void)3471da177e4SLinus Torvalds static int __init init_arxe_scsi_driver(void)
3481da177e4SLinus Torvalds {
3491da177e4SLinus Torvalds return ecard_register_driver(&arxescsi_driver);
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds
exit_arxe_scsi_driver(void)3521da177e4SLinus Torvalds static void __exit exit_arxe_scsi_driver(void)
3531da177e4SLinus Torvalds {
3541da177e4SLinus Torvalds ecard_remove_driver(&arxescsi_driver);
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds
3571da177e4SLinus Torvalds module_init(init_arxe_scsi_driver);
3581da177e4SLinus Torvalds module_exit(exit_arxe_scsi_driver);
3591da177e4SLinus Torvalds
3601da177e4SLinus Torvalds MODULE_AUTHOR("Stefan Hanske");
3611da177e4SLinus Torvalds MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines");
3621da177e4SLinus Torvalds MODULE_LICENSE("GPL");
3631da177e4SLinus Torvalds
364