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