1 /* 2 * linux/drivers/acorn/scsi/cumana_2.c 3 * 4 * Copyright (C) 1997-2005 Russell King 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * Changelog: 11 * 30-08-1997 RMK 0.0.0 Created, READONLY version. 12 * 22-01-1998 RMK 0.0.1 Updated to 2.1.80. 13 * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. 14 * 02-05-1998 RMK 0.0.2 Updated & added DMA support. 15 * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h 16 * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth. 17 * 02-04-2000 RMK 0.0.4 Updated for new error handling code. 18 */ 19 #include <linux/module.h> 20 #include <linux/blkdev.h> 21 #include <linux/kernel.h> 22 #include <linux/string.h> 23 #include <linux/ioport.h> 24 #include <linux/sched.h> 25 #include <linux/proc_fs.h> 26 #include <linux/delay.h> 27 #include <linux/interrupt.h> 28 #include <linux/init.h> 29 #include <linux/dma-mapping.h> 30 31 #include <asm/dma.h> 32 #include <asm/ecard.h> 33 #include <asm/io.h> 34 #include <asm/irq.h> 35 #include <asm/pgtable.h> 36 37 #include "../scsi.h" 38 #include <scsi/scsi_host.h> 39 #include "fas216.h" 40 #include "scsi.h" 41 42 #include <scsi/scsicam.h> 43 44 #define CUMANASCSI2_STATUS (0x0000) 45 #define STATUS_INT (1 << 0) 46 #define STATUS_DRQ (1 << 1) 47 #define STATUS_LATCHED (1 << 3) 48 49 #define CUMANASCSI2_ALATCH (0x0014) 50 #define ALATCH_ENA_INT (3) 51 #define ALATCH_DIS_INT (2) 52 #define ALATCH_ENA_TERM (5) 53 #define ALATCH_DIS_TERM (4) 54 #define ALATCH_ENA_BIT32 (11) 55 #define ALATCH_DIS_BIT32 (10) 56 #define ALATCH_ENA_DMA (13) 57 #define ALATCH_DIS_DMA (12) 58 #define ALATCH_DMA_OUT (15) 59 #define ALATCH_DMA_IN (14) 60 61 #define CUMANASCSI2_PSEUDODMA (0x0200) 62 63 #define CUMANASCSI2_FAS216_OFFSET (0x0300) 64 #define CUMANASCSI2_FAS216_SHIFT 2 65 66 /* 67 * Version 68 */ 69 #define VERSION "1.00 (13/11/2002 2.5.47)" 70 71 /* 72 * Use term=0,1,0,0,0 to turn terminators on/off 73 */ 74 static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; 75 76 #define NR_SG 256 77 78 struct cumanascsi2_info { 79 FAS216_Info info; 80 struct expansion_card *ec; 81 void __iomem *base; 82 unsigned int terms; /* Terminator state */ 83 struct scatterlist sg[NR_SG]; /* Scatter DMA list */ 84 }; 85 86 #define CSTATUS_IRQ (1 << 0) 87 #define CSTATUS_DRQ (1 << 1) 88 89 /* Prototype: void cumanascsi_2_irqenable(ec, irqnr) 90 * Purpose : Enable interrupts on Cumana SCSI 2 card 91 * Params : ec - expansion card structure 92 * : irqnr - interrupt number 93 */ 94 static void 95 cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr) 96 { 97 struct cumanascsi2_info *info = ec->irq_data; 98 writeb(ALATCH_ENA_INT, info->base + CUMANASCSI2_ALATCH); 99 } 100 101 /* Prototype: void cumanascsi_2_irqdisable(ec, irqnr) 102 * Purpose : Disable interrupts on Cumana SCSI 2 card 103 * Params : ec - expansion card structure 104 * : irqnr - interrupt number 105 */ 106 static void 107 cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr) 108 { 109 struct cumanascsi2_info *info = ec->irq_data; 110 writeb(ALATCH_DIS_INT, info->base + CUMANASCSI2_ALATCH); 111 } 112 113 static const expansioncard_ops_t cumanascsi_2_ops = { 114 .irqenable = cumanascsi_2_irqenable, 115 .irqdisable = cumanascsi_2_irqdisable, 116 }; 117 118 /* Prototype: void cumanascsi_2_terminator_ctl(host, on_off) 119 * Purpose : Turn the Cumana SCSI 2 terminators on or off 120 * Params : host - card to turn on/off 121 * : on_off - !0 to turn on, 0 to turn off 122 */ 123 static void 124 cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off) 125 { 126 struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; 127 128 if (on_off) { 129 info->terms = 1; 130 writeb(ALATCH_ENA_TERM, info->base + CUMANASCSI2_ALATCH); 131 } else { 132 info->terms = 0; 133 writeb(ALATCH_DIS_TERM, info->base + CUMANASCSI2_ALATCH); 134 } 135 } 136 137 /* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs) 138 * Purpose : handle interrupts from Cumana SCSI 2 card 139 * Params : irq - interrupt number 140 * dev_id - user-defined (Scsi_Host structure) 141 * regs - processor registers at interrupt 142 */ 143 static irqreturn_t 144 cumanascsi_2_intr(int irq, void *dev_id, struct pt_regs *regs) 145 { 146 struct cumanascsi2_info *info = dev_id; 147 148 return fas216_intr(&info->info); 149 } 150 151 /* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type) 152 * Purpose : initialises DMA/PIO 153 * Params : host - host 154 * SCpnt - command 155 * direction - DMA on to/off of card 156 * min_type - minimum DMA support that we must have for this transfer 157 * Returns : type of transfer to be performed 158 */ 159 static fasdmatype_t 160 cumanascsi_2_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, 161 fasdmadir_t direction, fasdmatype_t min_type) 162 { 163 struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; 164 struct device *dev = scsi_get_device(host); 165 int dmach = info->info.scsi.dma; 166 167 writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH); 168 169 if (dmach != NO_DMA && 170 (min_type == fasdma_real_all || SCp->this_residual >= 512)) { 171 int bufs, map_dir, dma_dir, alatch_dir; 172 173 bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); 174 175 if (direction == DMA_OUT) 176 map_dir = DMA_TO_DEVICE, 177 dma_dir = DMA_MODE_WRITE, 178 alatch_dir = ALATCH_DMA_OUT; 179 else 180 map_dir = DMA_FROM_DEVICE, 181 dma_dir = DMA_MODE_READ, 182 alatch_dir = ALATCH_DMA_IN; 183 184 dma_map_sg(dev, info->sg, bufs + 1, map_dir); 185 186 disable_dma(dmach); 187 set_dma_sg(dmach, info->sg, bufs + 1); 188 writeb(alatch_dir, info->base + CUMANASCSI2_ALATCH); 189 set_dma_mode(dmach, dma_dir); 190 enable_dma(dmach); 191 writeb(ALATCH_ENA_DMA, info->base + CUMANASCSI2_ALATCH); 192 writeb(ALATCH_DIS_BIT32, info->base + CUMANASCSI2_ALATCH); 193 return fasdma_real_all; 194 } 195 196 /* 197 * If we're not doing DMA, 198 * we'll do pseudo DMA 199 */ 200 return fasdma_pio; 201 } 202 203 /* 204 * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer) 205 * Purpose : handles pseudo DMA 206 * Params : host - host 207 * SCpnt - command 208 * direction - DMA on to/off of card 209 * transfer - minimum number of bytes we expect to transfer 210 */ 211 static void 212 cumanascsi_2_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, 213 fasdmadir_t direction, int transfer) 214 { 215 struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; 216 unsigned int length; 217 unsigned char *addr; 218 219 length = SCp->this_residual; 220 addr = SCp->ptr; 221 222 if (direction == DMA_OUT) 223 #if 0 224 while (length > 1) { 225 unsigned long word; 226 unsigned int status = readb(info->base + CUMANASCSI2_STATUS); 227 228 if (status & STATUS_INT) 229 goto end; 230 231 if (!(status & STATUS_DRQ)) 232 continue; 233 234 word = *addr | *(addr + 1) << 8; 235 writew(word, info->base + CUMANASCSI2_PSEUDODMA); 236 addr += 2; 237 length -= 2; 238 } 239 #else 240 printk ("PSEUDO_OUT???\n"); 241 #endif 242 else { 243 if (transfer && (transfer & 255)) { 244 while (length >= 256) { 245 unsigned int status = readb(info->base + CUMANASCSI2_STATUS); 246 247 if (status & STATUS_INT) 248 return; 249 250 if (!(status & STATUS_DRQ)) 251 continue; 252 253 readsw(info->base + CUMANASCSI2_PSEUDODMA, 254 addr, 256 >> 1); 255 addr += 256; 256 length -= 256; 257 } 258 } 259 260 while (length > 0) { 261 unsigned long word; 262 unsigned int status = readb(info->base + CUMANASCSI2_STATUS); 263 264 if (status & STATUS_INT) 265 return; 266 267 if (!(status & STATUS_DRQ)) 268 continue; 269 270 word = readw(info->base + CUMANASCSI2_PSEUDODMA); 271 *addr++ = word; 272 if (--length > 0) { 273 *addr++ = word >> 8; 274 length --; 275 } 276 } 277 } 278 } 279 280 /* Prototype: int cumanascsi_2_dma_stop(host, SCpnt) 281 * Purpose : stops DMA/PIO 282 * Params : host - host 283 * SCpnt - command 284 */ 285 static void 286 cumanascsi_2_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) 287 { 288 struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; 289 if (info->info.scsi.dma != NO_DMA) { 290 writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH); 291 disable_dma(info->info.scsi.dma); 292 } 293 } 294 295 /* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host) 296 * Purpose : returns a descriptive string about this interface, 297 * Params : host - driver host structure to return info for. 298 * Returns : pointer to a static buffer containing null terminated string. 299 */ 300 const char *cumanascsi_2_info(struct Scsi_Host *host) 301 { 302 struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; 303 static char string[150]; 304 305 sprintf(string, "%s (%s) in slot %d v%s terminators o%s", 306 host->hostt->name, info->info.scsi.type, info->ec->slot_no, 307 VERSION, info->terms ? "n" : "ff"); 308 309 return string; 310 } 311 312 /* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) 313 * Purpose : Set a driver specific function 314 * Params : host - host to setup 315 * : buffer - buffer containing string describing operation 316 * : length - length of string 317 * Returns : -EINVAL, or 0 318 */ 319 static int 320 cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) 321 { 322 int ret = length; 323 324 if (length >= 11 && strcmp(buffer, "CUMANASCSI2") == 0) { 325 buffer += 11; 326 length -= 11; 327 328 if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { 329 if (buffer[5] == '1') 330 cumanascsi_2_terminator_ctl(host, 1); 331 else if (buffer[5] == '0') 332 cumanascsi_2_terminator_ctl(host, 0); 333 else 334 ret = -EINVAL; 335 } else 336 ret = -EINVAL; 337 } else 338 ret = -EINVAL; 339 340 return ret; 341 } 342 343 /* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset, 344 * int length, int host_no, int inout) 345 * Purpose : Return information about the driver to a user process accessing 346 * the /proc filesystem. 347 * Params : buffer - a buffer to write information to 348 * start - a pointer into this buffer set by this routine to the start 349 * of the required information. 350 * offset - offset into information that we have read upto. 351 * length - length of buffer 352 * host_no - host number to return information for 353 * inout - 0 for reading, 1 for writing. 354 * Returns : length of data written to buffer. 355 */ 356 int cumanascsi_2_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t offset, 357 int length, int inout) 358 { 359 struct cumanascsi2_info *info; 360 char *p = buffer; 361 int pos; 362 363 if (inout == 1) 364 return cumanascsi_2_set_proc_info(host, buffer, length); 365 366 info = (struct cumanascsi2_info *)host->hostdata; 367 368 p += sprintf(p, "Cumana SCSI II driver v%s\n", VERSION); 369 p += fas216_print_host(&info->info, p); 370 p += sprintf(p, "Term : o%s\n", 371 info->terms ? "n" : "ff"); 372 373 p += fas216_print_stats(&info->info, p); 374 p += fas216_print_devices(&info->info, p); 375 376 *start = buffer + offset; 377 pos = p - buffer - offset; 378 if (pos > length) 379 pos = length; 380 381 return pos; 382 } 383 384 static Scsi_Host_Template cumanascsi2_template = { 385 .module = THIS_MODULE, 386 .proc_info = cumanascsi_2_proc_info, 387 .name = "Cumana SCSI II", 388 .info = cumanascsi_2_info, 389 .queuecommand = fas216_queue_command, 390 .eh_host_reset_handler = fas216_eh_host_reset, 391 .eh_bus_reset_handler = fas216_eh_bus_reset, 392 .eh_device_reset_handler = fas216_eh_device_reset, 393 .eh_abort_handler = fas216_eh_abort, 394 .can_queue = 1, 395 .this_id = 7, 396 .sg_tablesize = SG_ALL, 397 .cmd_per_lun = 1, 398 .use_clustering = DISABLE_CLUSTERING, 399 .proc_name = "cumanascsi2", 400 }; 401 402 static int __devinit 403 cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id) 404 { 405 struct Scsi_Host *host; 406 struct cumanascsi2_info *info; 407 unsigned long resbase, reslen; 408 void __iomem *base; 409 int ret; 410 411 ret = ecard_request_resources(ec); 412 if (ret) 413 goto out; 414 415 resbase = ecard_resource_start(ec, ECARD_RES_MEMC); 416 reslen = ecard_resource_len(ec, ECARD_RES_MEMC); 417 base = ioremap(resbase, reslen); 418 if (!base) { 419 ret = -ENOMEM; 420 goto out_region; 421 } 422 423 host = scsi_host_alloc(&cumanascsi2_template, 424 sizeof(struct cumanascsi2_info)); 425 if (!host) { 426 ret = -ENOMEM; 427 goto out_unmap; 428 } 429 430 ecard_set_drvdata(ec, host); 431 432 info = (struct cumanascsi2_info *)host->hostdata; 433 info->ec = ec; 434 info->base = base; 435 436 cumanascsi_2_terminator_ctl(host, term[ec->slot_no]); 437 438 info->info.scsi.io_base = base + CUMANASCSI2_FAS216_OFFSET; 439 info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT; 440 info->info.scsi.irq = ec->irq; 441 info->info.scsi.dma = ec->dma; 442 info->info.ifcfg.clockrate = 40; /* MHz */ 443 info->info.ifcfg.select_timeout = 255; 444 info->info.ifcfg.asyncperiod = 200; /* ns */ 445 info->info.ifcfg.sync_max_depth = 7; 446 info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; 447 info->info.ifcfg.disconnect_ok = 1; 448 info->info.ifcfg.wide_max_size = 0; 449 info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; 450 info->info.dma.setup = cumanascsi_2_dma_setup; 451 info->info.dma.pseudo = cumanascsi_2_dma_pseudo; 452 info->info.dma.stop = cumanascsi_2_dma_stop; 453 454 ec->irqaddr = info->base + CUMANASCSI2_STATUS; 455 ec->irqmask = STATUS_INT; 456 ec->irq_data = info; 457 ec->ops = &cumanascsi_2_ops; 458 459 ret = fas216_init(host); 460 if (ret) 461 goto out_free; 462 463 ret = request_irq(ec->irq, cumanascsi_2_intr, 464 SA_INTERRUPT, "cumanascsi2", info); 465 if (ret) { 466 printk("scsi%d: IRQ%d not free: %d\n", 467 host->host_no, ec->irq, ret); 468 goto out_release; 469 } 470 471 if (info->info.scsi.dma != NO_DMA) { 472 if (request_dma(info->info.scsi.dma, "cumanascsi2")) { 473 printk("scsi%d: DMA%d not free, using PIO\n", 474 host->host_no, info->info.scsi.dma); 475 info->info.scsi.dma = NO_DMA; 476 } else { 477 set_dma_speed(info->info.scsi.dma, 180); 478 info->info.ifcfg.capabilities |= FASCAP_DMA; 479 } 480 } 481 482 ret = fas216_add(host, &ec->dev); 483 if (ret == 0) 484 goto out; 485 486 if (info->info.scsi.dma != NO_DMA) 487 free_dma(info->info.scsi.dma); 488 free_irq(ec->irq, host); 489 490 out_release: 491 fas216_release(host); 492 493 out_free: 494 scsi_host_put(host); 495 496 out_unmap: 497 iounmap(base); 498 499 out_region: 500 ecard_release_resources(ec); 501 502 out: 503 return ret; 504 } 505 506 static void __devexit cumanascsi2_remove(struct expansion_card *ec) 507 { 508 struct Scsi_Host *host = ecard_get_drvdata(ec); 509 struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; 510 511 ecard_set_drvdata(ec, NULL); 512 fas216_remove(host); 513 514 if (info->info.scsi.dma != NO_DMA) 515 free_dma(info->info.scsi.dma); 516 free_irq(ec->irq, info); 517 518 iounmap(info->base); 519 520 fas216_release(host); 521 scsi_host_put(host); 522 ecard_release_resources(ec); 523 } 524 525 static const struct ecard_id cumanascsi2_cids[] = { 526 { MANU_CUMANA, PROD_CUMANA_SCSI_2 }, 527 { 0xffff, 0xffff }, 528 }; 529 530 static struct ecard_driver cumanascsi2_driver = { 531 .probe = cumanascsi2_probe, 532 .remove = __devexit_p(cumanascsi2_remove), 533 .id_table = cumanascsi2_cids, 534 .drv = { 535 .name = "cumanascsi2", 536 }, 537 }; 538 539 static int __init cumanascsi2_init(void) 540 { 541 return ecard_register_driver(&cumanascsi2_driver); 542 } 543 544 static void __exit cumanascsi2_exit(void) 545 { 546 ecard_remove_driver(&cumanascsi2_driver); 547 } 548 549 module_init(cumanascsi2_init); 550 module_exit(cumanascsi2_exit); 551 552 MODULE_AUTHOR("Russell King"); 553 MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines"); 554 MODULE_PARM(term, "1-8i"); 555 MODULE_PARM_DESC(term, "SCSI bus termination"); 556 MODULE_LICENSE("GPL"); 557