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(hostdata, reg) 18 #define NCR5380_write(reg, value) cumanascsi_write(hostdata, reg, value) 19 20 #define NCR5380_dma_xfer_len cumanascsi_dma_xfer_len 21 #define NCR5380_dma_recv_setup cumanascsi_pread 22 #define NCR5380_dma_send_setup cumanascsi_pwrite 23 #define NCR5380_dma_residual NCR5380_dma_residual_none 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 32 struct NCR5380_hostdata; 33 static u8 cumanascsi_read(struct NCR5380_hostdata *, unsigned int); 34 static void cumanascsi_write(struct NCR5380_hostdata *, unsigned int, u8); 35 36 #include "../NCR5380.h" 37 38 #define CTRL 0x16fc 39 #define STAT 0x2004 40 #define L(v) (((v)<<16)|((v) & 0x0000ffff)) 41 #define H(v) (((v)>>16)|((v) & 0xffff0000)) 42 43 static inline int cumanascsi_pwrite(struct NCR5380_hostdata *hostdata, 44 unsigned char *addr, int len) 45 { 46 unsigned long *laddr; 47 u8 __iomem *base = hostdata->io; 48 u8 __iomem *dma = hostdata->pdma_io + 0x2000; 49 50 if(!len) return 0; 51 52 writeb(0x02, base + CTRL); 53 laddr = (unsigned long *)addr; 54 while(len >= 32) 55 { 56 unsigned int status; 57 unsigned long v; 58 status = readb(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, base + CTRL); 78 79 while(len > 0) 80 { 81 unsigned int status; 82 status = readb(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(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(hostdata->ctrl | 0x40, base + CTRL); 104 105 if (len) 106 return -1; 107 return 0; 108 } 109 110 static inline int cumanascsi_pread(struct NCR5380_hostdata *hostdata, 111 unsigned char *addr, int len) 112 { 113 unsigned long *laddr; 114 u8 __iomem *base = hostdata->io; 115 u8 __iomem *dma = hostdata->pdma_io + 0x2000; 116 117 if(!len) return 0; 118 119 writeb(0x00, base + CTRL); 120 laddr = (unsigned long *)addr; 121 while(len >= 32) 122 { 123 unsigned int status; 124 status = readb(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, base + CTRL); 144 145 while(len > 0) 146 { 147 unsigned int status; 148 status = readb(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(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(hostdata->ctrl | 0x40, base + CTRL); 170 171 if (len) 172 return -1; 173 return 0; 174 } 175 176 static int cumanascsi_dma_xfer_len(struct NCR5380_hostdata *hostdata, 177 struct scsi_cmnd *cmd) 178 { 179 return cmd->transfersize; 180 } 181 182 static u8 cumanascsi_read(struct NCR5380_hostdata *hostdata, 183 unsigned int reg) 184 { 185 u8 __iomem *base = hostdata->io; 186 u8 val; 187 188 writeb(0, base + CTRL); 189 190 val = readb(base + 0x2100 + (reg << 2)); 191 192 hostdata->ctrl = 0x40; 193 writeb(0x40, base + CTRL); 194 195 return val; 196 } 197 198 static void cumanascsi_write(struct NCR5380_hostdata *hostdata, 199 unsigned int reg, u8 value) 200 { 201 u8 __iomem *base = hostdata->io; 202 203 writeb(0, base + CTRL); 204 205 writeb(value, base + 0x2100 + (reg << 2)); 206 207 hostdata->ctrl = 0x40; 208 writeb(0x40, base + CTRL); 209 } 210 211 #include "../NCR5380.c" 212 213 static struct scsi_host_template cumanascsi_template = { 214 .module = THIS_MODULE, 215 .name = "Cumana 16-bit SCSI", 216 .info = cumanascsi_info, 217 .queuecommand = cumanascsi_queue_command, 218 .eh_abort_handler = NCR5380_abort, 219 .eh_bus_reset_handler = NCR5380_bus_reset, 220 .can_queue = 16, 221 .this_id = 7, 222 .sg_tablesize = SG_ALL, 223 .cmd_per_lun = 2, 224 .use_clustering = DISABLE_CLUSTERING, 225 .proc_name = "CumanaSCSI-1", 226 .cmd_size = NCR5380_CMD_SIZE, 227 .max_sectors = 128, 228 }; 229 230 static int cumanascsi1_probe(struct expansion_card *ec, 231 const struct ecard_id *id) 232 { 233 struct Scsi_Host *host; 234 int ret; 235 236 ret = ecard_request_resources(ec); 237 if (ret) 238 goto out; 239 240 host = scsi_host_alloc(&cumanascsi_template, sizeof(struct NCR5380_hostdata)); 241 if (!host) { 242 ret = -ENOMEM; 243 goto out_release; 244 } 245 246 priv(host)->io = ioremap(ecard_resource_start(ec, ECARD_RES_IOCSLOW), 247 ecard_resource_len(ec, ECARD_RES_IOCSLOW)); 248 priv(host)->pdma_io = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC), 249 ecard_resource_len(ec, ECARD_RES_MEMC)); 250 if (!priv(host)->io || !priv(host)->pdma_io) { 251 ret = -ENOMEM; 252 goto out_unmap; 253 } 254 255 host->irq = ec->irq; 256 257 ret = NCR5380_init(host, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP); 258 if (ret) 259 goto out_unmap; 260 261 NCR5380_maybe_reset_bus(host); 262 263 priv(host)->ctrl = 0; 264 writeb(0, priv(host)->io + CTRL); 265 266 ret = request_irq(host->irq, cumanascsi_intr, 0, 267 "CumanaSCSI-1", host); 268 if (ret) { 269 printk("scsi%d: IRQ%d not free: %d\n", 270 host->host_no, host->irq, ret); 271 goto out_exit; 272 } 273 274 ret = scsi_add_host(host, &ec->dev); 275 if (ret) 276 goto out_free_irq; 277 278 scsi_scan_host(host); 279 goto out; 280 281 out_free_irq: 282 free_irq(host->irq, host); 283 out_exit: 284 NCR5380_exit(host); 285 out_unmap: 286 iounmap(priv(host)->io); 287 iounmap(priv(host)->pdma_io); 288 scsi_host_put(host); 289 out_release: 290 ecard_release_resources(ec); 291 out: 292 return ret; 293 } 294 295 static void cumanascsi1_remove(struct expansion_card *ec) 296 { 297 struct Scsi_Host *host = ecard_get_drvdata(ec); 298 void __iomem *base = priv(host)->io; 299 void __iomem *dma = priv(host)->pdma_io; 300 301 ecard_set_drvdata(ec, NULL); 302 303 scsi_remove_host(host); 304 free_irq(host->irq, host); 305 NCR5380_exit(host); 306 scsi_host_put(host); 307 iounmap(base); 308 iounmap(dma); 309 ecard_release_resources(ec); 310 } 311 312 static const struct ecard_id cumanascsi1_cids[] = { 313 { MANU_CUMANA, PROD_CUMANA_SCSI_1 }, 314 { 0xffff, 0xffff } 315 }; 316 317 static 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 326 static int __init cumanascsi_init(void) 327 { 328 return ecard_register_driver(&cumanascsi1_driver); 329 } 330 331 static void __exit cumanascsi_exit(void) 332 { 333 ecard_remove_driver(&cumanascsi1_driver); 334 } 335 336 module_init(cumanascsi_init); 337 module_exit(cumanascsi_exit); 338 339 MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines"); 340 MODULE_LICENSE("GPL"); 341