214 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Oak Generic NCR5380 driver4 *5 * Copyright 1995-2002, Russell King6 */7 8#include <linux/module.h>9#include <linux/ioport.h>10#include <linux/blkdev.h>11#include <linux/init.h>12 13#include <asm/ecard.h>14#include <asm/io.h>15 16#include <scsi/scsi_host.h>17 18#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)19 20#define NCR5380_read(reg) readb(hostdata->io + ((reg) << 2))21#define NCR5380_write(reg, value) writeb(value, hostdata->io + ((reg) << 2))22 23#define NCR5380_dma_xfer_len NCR5380_dma_xfer_none24#define NCR5380_dma_recv_setup oakscsi_pread25#define NCR5380_dma_send_setup oakscsi_pwrite26#define NCR5380_dma_residual NCR5380_dma_residual_none27 28#define NCR5380_queue_command oakscsi_queue_command29#define NCR5380_info oakscsi_info30 31#define NCR5380_implementation_fields /* none */32 33#include "../NCR5380.h"34 35#undef START_DMA_INITIATOR_RECEIVE_REG36#define START_DMA_INITIATOR_RECEIVE_REG (128 + 7)37 38#define STAT ((128 + 16) << 2)39#define DATA ((128 + 8) << 2)40 41static inline int oakscsi_pwrite(struct NCR5380_hostdata *hostdata,42 unsigned char *addr, int len)43{44 u8 __iomem *base = hostdata->io;45 46printk("writing %p len %d\n",addr, len);47 48 while(1)49 {50 int status;51 while (((status = readw(base + STAT)) & 0x100)==0);52 }53 return 0;54}55 56static inline int oakscsi_pread(struct NCR5380_hostdata *hostdata,57 unsigned char *addr, int len)58{59 u8 __iomem *base = hostdata->io;60 61printk("reading %p len %d\n", addr, len);62 while(len > 0)63 {64 unsigned int status, timeout;65 unsigned long b;66 67 timeout = 0x01FFFFFF;68 69 while (((status = readw(base + STAT)) & 0x100)==0)70 {71 timeout--;72 if(status & 0x200 || !timeout)73 {74 printk("status = %08X\n", status);75 return -1;76 }77 }78 79 if(len >= 128)80 {81 readsw(base + DATA, addr, 128);82 addr += 128;83 len -= 128;84 }85 else86 {87 b = (unsigned long) readw(base + DATA);88 *addr ++ = b;89 len -= 1;90 if(len)91 *addr ++ = b>>8;92 len -= 1;93 }94 }95 return 0;96}97 98#undef STAT99#undef DATA100 101#include "../NCR5380.c"102 103static const struct scsi_host_template oakscsi_template = {104 .module = THIS_MODULE,105 .name = "Oak 16-bit SCSI",106 .info = oakscsi_info,107 .queuecommand = oakscsi_queue_command,108 .eh_abort_handler = NCR5380_abort,109 .eh_host_reset_handler = NCR5380_host_reset,110 .can_queue = 16,111 .this_id = 7,112 .sg_tablesize = SG_ALL,113 .cmd_per_lun = 2,114 .dma_boundary = PAGE_SIZE - 1,115 .proc_name = "oakscsi",116 .cmd_size = sizeof(struct NCR5380_cmd),117 .max_sectors = 128,118};119 120static int oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)121{122 struct Scsi_Host *host;123 int ret;124 125 ret = ecard_request_resources(ec);126 if (ret)127 goto out;128 129 host = scsi_host_alloc(&oakscsi_template, sizeof(struct NCR5380_hostdata));130 if (!host) {131 ret = -ENOMEM;132 goto release;133 }134 135 priv(host)->io = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),136 ecard_resource_len(ec, ECARD_RES_MEMC));137 if (!priv(host)->io) {138 ret = -ENOMEM;139 goto unreg;140 }141 142 host->irq = NO_IRQ;143 144 ret = NCR5380_init(host, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP);145 if (ret)146 goto out_unmap;147 148 NCR5380_maybe_reset_bus(host);149 150 ret = scsi_add_host(host, &ec->dev);151 if (ret)152 goto out_exit;153 154 scsi_scan_host(host);155 goto out;156 157 out_exit:158 NCR5380_exit(host);159 out_unmap:160 iounmap(priv(host)->io);161 unreg:162 scsi_host_put(host);163 release:164 ecard_release_resources(ec);165 out:166 return ret;167}168 169static void oakscsi_remove(struct expansion_card *ec)170{171 struct Scsi_Host *host = ecard_get_drvdata(ec);172 void __iomem *base = priv(host)->io;173 174 ecard_set_drvdata(ec, NULL);175 scsi_remove_host(host);176 177 NCR5380_exit(host);178 scsi_host_put(host);179 iounmap(base);180 ecard_release_resources(ec);181}182 183static const struct ecard_id oakscsi_cids[] = {184 { MANU_OAK, PROD_OAK_SCSI },185 { 0xffff, 0xffff }186};187 188static struct ecard_driver oakscsi_driver = {189 .probe = oakscsi_probe,190 .remove = oakscsi_remove,191 .id_table = oakscsi_cids,192 .drv = {193 .name = "oakscsi",194 },195};196 197static int __init oakscsi_init(void)198{199 return ecard_register_driver(&oakscsi_driver);200}201 202static void __exit oakscsi_exit(void)203{204 ecard_remove_driver(&oakscsi_driver);205}206 207module_init(oakscsi_init);208module_exit(oakscsi_exit);209 210MODULE_AUTHOR("Russell King");211MODULE_DESCRIPTION("Oak SCSI driver");212MODULE_LICENSE("GPL");213 214