1/* 2 * Generic Generic NCR5380 driver 3 * 4 * Copyright 1995-2002, Russell King 5 */ 6#include <linux/module.h> 7#include <linux/signal.h> 8#include <linux/ioport.h> 9#include <linux/delay.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.h" 17#include <scsi/scsi_host.h> 18 19#include <scsi/scsicam.h> 20 21#define AUTOSENSE 22#define PSEUDO_DMA 23 24#define CUMANASCSI_PUBLIC_RELEASE 1 25 26#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata) 27#define NCR5380_local_declare() struct Scsi_Host *_instance 28#define NCR5380_setup(instance) _instance = instance 29#define NCR5380_read(reg) cumanascsi_read(_instance, reg) 30#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value) 31#define NCR5380_intr cumanascsi_intr 32#define NCR5380_queue_command cumanascsi_queue_command 33 34#define NCR5380_implementation_fields \ 35 unsigned ctrl; \ 36 void __iomem *base; \ 37 void __iomem *dma 38 39#include "../NCR5380.h" 40 41void cumanascsi_setup(char *str, int *ints) 42{ 43} 44 45const char *cumanascsi_info(struct Scsi_Host *spnt) 46{ 47 return ""; 48} 49 50#define CTRL 0x16fc 51#define STAT 0x2004 52#define L(v) (((v)<<16)|((v) & 0x0000ffff)) 53#define H(v) (((v)>>16)|((v) & 0xffff0000)) 54 55static inline int 56NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, int len) 57{ 58 unsigned long *laddr; 59 void __iomem *dma = priv(host)->dma + 0x2000; 60 61 if(!len) return 0; 62 63 writeb(0x02, priv(host)->base + CTRL); 64 laddr = (unsigned long *)addr; 65 while(len >= 32) 66 { 67 unsigned int status; 68 unsigned long v; 69 status = readb(priv(host)->base + STAT); 70 if(status & 0x80) 71 goto end; 72 if(!(status & 0x40)) 73 continue; 74 v=*laddr++; writew(L(v), dma); writew(H(v), dma); 75 v=*laddr++; writew(L(v), dma); writew(H(v), dma); 76 v=*laddr++; writew(L(v), dma); writew(H(v), dma); 77 v=*laddr++; writew(L(v), dma); writew(H(v), dma); 78 v=*laddr++; writew(L(v), dma); writew(H(v), dma); 79 v=*laddr++; writew(L(v), dma); writew(H(v), dma); 80 v=*laddr++; writew(L(v), dma); writew(H(v), dma); 81 v=*laddr++; writew(L(v), dma); writew(H(v), dma); 82 len -= 32; 83 if(len == 0) 84 break; 85 } 86 87 addr = (unsigned char *)laddr; 88 writeb(0x12, priv(host)->base + CTRL); 89 90 while(len > 0) 91 { 92 unsigned int status; 93 status = readb(priv(host)->base + STAT); 94 if(status & 0x80) 95 goto end; 96 if(status & 0x40) 97 { 98 writeb(*addr++, dma); 99 if(--len == 0) 100 break; 101 } 102 103 status = readb(priv(host)->base + STAT); 104 if(status & 0x80) 105 goto end; 106 if(status & 0x40) 107 { 108 writeb(*addr++, dma); 109 if(--len == 0) 110 break; 111 } 112 } 113end: 114 writeb(priv(host)->ctrl | 0x40, priv(host)->base + CTRL); 115 return len; 116} 117 118static inline int 119NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, int len) 120{ 121 unsigned long *laddr; 122 void __iomem *dma = priv(host)->dma + 0x2000; 123 124 if(!len) return 0; 125 126 writeb(0x00, priv(host)->base + CTRL); 127 laddr = (unsigned long *)addr; 128 while(len >= 32) 129 { 130 unsigned int status; 131 status = readb(priv(host)->base + STAT); 132 if(status & 0x80) 133 goto end; 134 if(!(status & 0x40)) 135 continue; 136 *laddr++ = readw(dma) | (readw(dma) << 16); 137 *laddr++ = readw(dma) | (readw(dma) << 16); 138 *laddr++ = readw(dma) | (readw(dma) << 16); 139 *laddr++ = readw(dma) | (readw(dma) << 16); 140 *laddr++ = readw(dma) | (readw(dma) << 16); 141 *laddr++ = readw(dma) | (readw(dma) << 16); 142 *laddr++ = readw(dma) | (readw(dma) << 16); 143 *laddr++ = readw(dma) | (readw(dma) << 16); 144 len -= 32; 145 if(len == 0) 146 break; 147 } 148 149 addr = (unsigned char *)laddr; 150 writeb(0x10, priv(host)->base + CTRL); 151 152 while(len > 0) 153 { 154 unsigned int status; 155 status = readb(priv(host)->base + STAT); 156 if(status & 0x80) 157 goto end; 158 if(status & 0x40) 159 { 160 *addr++ = readb(dma); 161 if(--len == 0) 162 break; 163 } 164 165 status = readb(priv(host)->base + STAT); 166 if(status & 0x80) 167 goto end; 168 if(status & 0x40) 169 { 170 *addr++ = readb(dma); 171 if(--len == 0) 172 break; 173 } 174 } 175end: 176 writeb(priv(host)->ctrl | 0x40, priv(host)->base + CTRL); 177 return len; 178} 179 180static unsigned char cumanascsi_read(struct Scsi_Host *host, unsigned int reg) 181{ 182 void __iomem *base = priv(host)->base; 183 unsigned char val; 184 185 writeb(0, base + CTRL); 186 187 val = readb(base + 0x2100 + (reg << 2)); 188 189 priv(host)->ctrl = 0x40; 190 writeb(0x40, base + CTRL); 191 192 return val; 193} 194 195static void cumanascsi_write(struct Scsi_Host *host, unsigned int reg, unsigned int value) 196{ 197 void __iomem *base = priv(host)->base; 198 199 writeb(0, base + CTRL); 200 201 writeb(value, base + 0x2100 + (reg << 2)); 202 203 priv(host)->ctrl = 0x40; 204 writeb(0x40, base + CTRL); 205} 206 207#include "../NCR5380.c" 208 209static struct scsi_host_template cumanascsi_template = { 210 .module = THIS_MODULE, 211 .name = "Cumana 16-bit SCSI", 212 .info = cumanascsi_info, 213 .queuecommand = cumanascsi_queue_command, 214 .eh_abort_handler = NCR5380_abort, 215 .eh_bus_reset_handler = NCR5380_bus_reset, 216 .can_queue = 16, 217 .this_id = 7, 218 .sg_tablesize = SG_ALL, 219 .cmd_per_lun = 2, 220 .use_clustering = DISABLE_CLUSTERING, 221 .proc_name = "CumanaSCSI-1", 222}; 223 224static int cumanascsi1_probe(struct expansion_card *ec, 225 const struct ecard_id *id) 226{ 227 struct Scsi_Host *host; 228 int ret; 229 230 ret = ecard_request_resources(ec); 231 if (ret) 232 goto out; 233 234 host = scsi_host_alloc(&cumanascsi_template, sizeof(struct NCR5380_hostdata)); 235 if (!host) { 236 ret = -ENOMEM; 237 goto out_release; 238 } 239 240 priv(host)->base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCSLOW), 241 ecard_resource_len(ec, ECARD_RES_IOCSLOW)); 242 priv(host)->dma = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC), 243 ecard_resource_len(ec, ECARD_RES_MEMC)); 244 if (!priv(host)->base || !priv(host)->dma) { 245 ret = -ENOMEM; 246 goto out_unmap; 247 } 248 249 host->irq = ec->irq; 250 251 NCR5380_init(host, 0); 252 253 priv(host)->ctrl = 0; 254 writeb(0, priv(host)->base + CTRL); 255 256 host->n_io_port = 255; 257 if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) { 258 ret = -EBUSY; 259 goto out_unmap; 260 } 261 262 ret = request_irq(host->irq, cumanascsi_intr, 0, 263 "CumanaSCSI-1", host); 264 if (ret) { 265 printk("scsi%d: IRQ%d not free: %d\n", 266 host->host_no, host->irq, ret); 267 goto out_unmap; 268 } 269 270 printk("scsi%d: at port 0x%08lx irq %d", 271 host->host_no, host->io_port, host->irq); 272 printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", 273 host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE); 274 printk("\nscsi%d:", host->host_no); 275 NCR5380_print_options(host); 276 printk("\n"); 277 278 ret = scsi_add_host(host, &ec->dev); 279 if (ret) 280 goto out_free_irq; 281 282 scsi_scan_host(host); 283 goto out; 284 285 out_free_irq: 286 free_irq(host->irq, host); 287 out_unmap: 288 iounmap(priv(host)->base); 289 iounmap(priv(host)->dma); 290 scsi_host_put(host); 291 out_release: 292 ecard_release_resources(ec); 293 out: 294 return ret; 295} 296 297static void cumanascsi1_remove(struct expansion_card *ec) 298{ 299 struct Scsi_Host *host = ecard_get_drvdata(ec); 300 301 ecard_set_drvdata(ec, NULL); 302 303 scsi_remove_host(host); 304 free_irq(host->irq, host); 305 NCR5380_exit(host); 306 iounmap(priv(host)->base); 307 iounmap(priv(host)->dma); 308 scsi_host_put(host); 309 ecard_release_resources(ec); 310} 311 312static const struct ecard_id cumanascsi1_cids[] = { 313 { MANU_CUMANA, PROD_CUMANA_SCSI_1 }, 314 { 0xffff, 0xffff } 315}; 316 317static struct ecard_driver cumanascsi1_driver = { 318 .probe = cumanascsi1_probe, 319 .remove = cumanascsi1_remove, 320 .id_table = cumanascsi1_cids, 321 .drv = { 322 .name = "cumanascsi1", 323 }, 324}; 325 326static int __init cumanascsi_init(void) 327{ 328 return ecard_register_driver(&cumanascsi1_driver); 329} 330 331static void __exit cumanascsi_exit(void) 332{ 333 ecard_remove_driver(&cumanascsi1_driver); 334} 335 336module_init(cumanascsi_init); 337module_exit(cumanascsi_exit); 338 339MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines"); 340MODULE_LICENSE("GPL"); 341