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