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