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