cumana_2.c (13729B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * linux/drivers/acorn/scsi/cumana_2.c 4 * 5 * Copyright (C) 1997-2005 Russell King 6 * 7 * Changelog: 8 * 30-08-1997 RMK 0.0.0 Created, READONLY version. 9 * 22-01-1998 RMK 0.0.1 Updated to 2.1.80. 10 * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. 11 * 02-05-1998 RMK 0.0.2 Updated & added DMA support. 12 * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h 13 * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth. 14 * 02-04-2000 RMK 0.0.4 Updated for new error handling code. 15 */ 16#include <linux/module.h> 17#include <linux/blkdev.h> 18#include <linux/kernel.h> 19#include <linux/string.h> 20#include <linux/ioport.h> 21#include <linux/proc_fs.h> 22#include <linux/delay.h> 23#include <linux/interrupt.h> 24#include <linux/init.h> 25#include <linux/dma-mapping.h> 26#include <linux/pgtable.h> 27 28#include <asm/dma.h> 29#include <asm/ecard.h> 30#include <asm/io.h> 31 32#include <scsi/scsi.h> 33#include <scsi/scsi_cmnd.h> 34#include <scsi/scsi_device.h> 35#include <scsi/scsi_eh.h> 36#include <scsi/scsi_host.h> 37#include <scsi/scsi_tcq.h> 38#include "fas216.h" 39#include "arm_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 */ 73static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; 74 75#define NR_SG 256 76 77struct 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 */ 93static void 94cumanascsi_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 */ 105static void 106cumanascsi_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 112static 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 */ 122static void 123cumanascsi_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 */ 141static irqreturn_t 142cumanascsi_2_intr(int irq, void *dev_id) 143{ 144 struct cumanascsi2_info *info = dev_id; 145 146 return fas216_intr(&info->info); 147} 148 149/* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type) 150 * Purpose : initialises DMA/PIO 151 * Params : host - host 152 * SCpnt - command 153 * direction - DMA on to/off of card 154 * min_type - minimum DMA support that we must have for this transfer 155 * Returns : type of transfer to be performed 156 */ 157static fasdmatype_t 158cumanascsi_2_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp, 159 fasdmadir_t direction, fasdmatype_t min_type) 160{ 161 struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; 162 struct device *dev = scsi_get_device(host); 163 int dmach = info->info.scsi.dma; 164 165 writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH); 166 167 if (dmach != NO_DMA && 168 (min_type == fasdma_real_all || SCp->this_residual >= 512)) { 169 int bufs, map_dir, dma_dir, alatch_dir; 170 171 bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); 172 173 if (direction == DMA_OUT) { 174 map_dir = DMA_TO_DEVICE; 175 dma_dir = DMA_MODE_WRITE; 176 alatch_dir = ALATCH_DMA_OUT; 177 } else { 178 map_dir = DMA_FROM_DEVICE; 179 dma_dir = DMA_MODE_READ; 180 alatch_dir = ALATCH_DMA_IN; 181 } 182 183 dma_map_sg(dev, info->sg, bufs, map_dir); 184 185 disable_dma(dmach); 186 set_dma_sg(dmach, info->sg, bufs); 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 */ 210static void 211cumanascsi_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 */ 284static void 285cumanascsi_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 */ 299const 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 */ 318static int 319cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) 320{ 321 int ret = length; 322 323 if (length >= 11 && strncmp(buffer, "CUMANASCSI2", 11) == 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 } 337 } else { 338 ret = -EINVAL; 339 } 340 341 return ret; 342} 343 344static int cumanascsi_2_show_info(struct seq_file *m, struct Scsi_Host *host) 345{ 346 struct cumanascsi2_info *info; 347 info = (struct cumanascsi2_info *)host->hostdata; 348 349 seq_printf(m, "Cumana SCSI II driver v%s\n", VERSION); 350 fas216_print_host(&info->info, m); 351 seq_printf(m, "Term : o%s\n", 352 info->terms ? "n" : "ff"); 353 354 fas216_print_stats(&info->info, m); 355 fas216_print_devices(&info->info, m); 356 return 0; 357} 358 359static struct scsi_host_template cumanascsi2_template = { 360 .module = THIS_MODULE, 361 .show_info = cumanascsi_2_show_info, 362 .write_info = cumanascsi_2_set_proc_info, 363 .name = "Cumana SCSI II", 364 .info = cumanascsi_2_info, 365 .queuecommand = fas216_queue_command, 366 .eh_host_reset_handler = fas216_eh_host_reset, 367 .eh_bus_reset_handler = fas216_eh_bus_reset, 368 .eh_device_reset_handler = fas216_eh_device_reset, 369 .eh_abort_handler = fas216_eh_abort, 370 .cmd_size = sizeof(struct fas216_cmd_priv), 371 .can_queue = 1, 372 .this_id = 7, 373 .sg_tablesize = SG_MAX_SEGMENTS, 374 .dma_boundary = IOMD_DMA_BOUNDARY, 375 .proc_name = "cumanascsi2", 376}; 377 378static int cumanascsi2_probe(struct expansion_card *ec, 379 const struct ecard_id *id) 380{ 381 struct Scsi_Host *host; 382 struct cumanascsi2_info *info; 383 void __iomem *base; 384 int ret; 385 386 ret = ecard_request_resources(ec); 387 if (ret) 388 goto out; 389 390 base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); 391 if (!base) { 392 ret = -ENOMEM; 393 goto out_region; 394 } 395 396 host = scsi_host_alloc(&cumanascsi2_template, 397 sizeof(struct cumanascsi2_info)); 398 if (!host) { 399 ret = -ENOMEM; 400 goto out_region; 401 } 402 403 ecard_set_drvdata(ec, host); 404 405 info = (struct cumanascsi2_info *)host->hostdata; 406 info->ec = ec; 407 info->base = base; 408 409 cumanascsi_2_terminator_ctl(host, term[ec->slot_no]); 410 411 info->info.scsi.io_base = base + CUMANASCSI2_FAS216_OFFSET; 412 info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT; 413 info->info.scsi.irq = ec->irq; 414 info->info.scsi.dma = ec->dma; 415 info->info.ifcfg.clockrate = 40; /* MHz */ 416 info->info.ifcfg.select_timeout = 255; 417 info->info.ifcfg.asyncperiod = 200; /* ns */ 418 info->info.ifcfg.sync_max_depth = 7; 419 info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; 420 info->info.ifcfg.disconnect_ok = 1; 421 info->info.ifcfg.wide_max_size = 0; 422 info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; 423 info->info.dma.setup = cumanascsi_2_dma_setup; 424 info->info.dma.pseudo = cumanascsi_2_dma_pseudo; 425 info->info.dma.stop = cumanascsi_2_dma_stop; 426 427 ec->irqaddr = info->base + CUMANASCSI2_STATUS; 428 ec->irqmask = STATUS_INT; 429 430 ecard_setirq(ec, &cumanascsi_2_ops, info); 431 432 ret = fas216_init(host); 433 if (ret) 434 goto out_free; 435 436 ret = request_irq(ec->irq, cumanascsi_2_intr, 437 0, "cumanascsi2", info); 438 if (ret) { 439 printk("scsi%d: IRQ%d not free: %d\n", 440 host->host_no, ec->irq, ret); 441 goto out_release; 442 } 443 444 if (info->info.scsi.dma != NO_DMA) { 445 if (request_dma(info->info.scsi.dma, "cumanascsi2")) { 446 printk("scsi%d: DMA%d not free, using PIO\n", 447 host->host_no, info->info.scsi.dma); 448 info->info.scsi.dma = NO_DMA; 449 } else { 450 set_dma_speed(info->info.scsi.dma, 180); 451 info->info.ifcfg.capabilities |= FASCAP_DMA; 452 } 453 } 454 455 ret = fas216_add(host, &ec->dev); 456 if (ret == 0) 457 goto out; 458 459 if (info->info.scsi.dma != NO_DMA) 460 free_dma(info->info.scsi.dma); 461 free_irq(ec->irq, info); 462 463 out_release: 464 fas216_release(host); 465 466 out_free: 467 scsi_host_put(host); 468 469 out_region: 470 ecard_release_resources(ec); 471 472 out: 473 return ret; 474} 475 476static void cumanascsi2_remove(struct expansion_card *ec) 477{ 478 struct Scsi_Host *host = ecard_get_drvdata(ec); 479 struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; 480 481 ecard_set_drvdata(ec, NULL); 482 fas216_remove(host); 483 484 if (info->info.scsi.dma != NO_DMA) 485 free_dma(info->info.scsi.dma); 486 free_irq(ec->irq, info); 487 488 fas216_release(host); 489 scsi_host_put(host); 490 ecard_release_resources(ec); 491} 492 493static const struct ecard_id cumanascsi2_cids[] = { 494 { MANU_CUMANA, PROD_CUMANA_SCSI_2 }, 495 { 0xffff, 0xffff }, 496}; 497 498static struct ecard_driver cumanascsi2_driver = { 499 .probe = cumanascsi2_probe, 500 .remove = cumanascsi2_remove, 501 .id_table = cumanascsi2_cids, 502 .drv = { 503 .name = "cumanascsi2", 504 }, 505}; 506 507static int __init cumanascsi2_init(void) 508{ 509 return ecard_register_driver(&cumanascsi2_driver); 510} 511 512static void __exit cumanascsi2_exit(void) 513{ 514 ecard_remove_driver(&cumanascsi2_driver); 515} 516 517module_init(cumanascsi2_init); 518module_exit(cumanascsi2_exit); 519 520MODULE_AUTHOR("Russell King"); 521MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines"); 522module_param_array(term, int, NULL, 0); 523MODULE_PARM_DESC(term, "SCSI bus termination"); 524MODULE_LICENSE("GPL");