File: /usr/src/linux/drivers/acorn/scsi/eesox.c
1 /*
2 * linux/drivers/acorn/scsi/eesox.c
3 *
4 * Copyright (C) 1997-2000 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 * This driver is based on experimentation. Hence, it may have made
11 * assumptions about the particular card that I have available, and
12 * may not be reliable!
13 *
14 * Changelog:
15 * 01-10-1997 RMK Created, READONLY version
16 * 15-02-1998 RMK READ/WRITE version
17 * added DMA support and hardware definitions
18 * 14-03-1998 RMK Updated DMA support
19 * Added terminator control
20 * 15-04-1998 RMK Only do PIO if FAS216 will allow it.
21 * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
22 * 02-04-2000 RMK 0.0.3 Fixed NO_IRQ/NO_DMA problem, updated for new
23 * error handling code.
24 */
25 #include <linux/module.h>
26 #include <linux/blk.h>
27 #include <linux/kernel.h>
28 #include <linux/string.h>
29 #include <linux/ioport.h>
30 #include <linux/sched.h>
31 #include <linux/proc_fs.h>
32 #include <linux/unistd.h>
33 #include <linux/stat.h>
34 #include <linux/delay.h>
35 #include <linux/pci.h>
36 #include <linux/init.h>
37
38 #include <asm/io.h>
39 #include <asm/irq.h>
40 #include <asm/dma.h>
41 #include <asm/ecard.h>
42 #include <asm/pgtable.h>
43
44 #include "../../scsi/sd.h"
45 #include "../../scsi/hosts.h"
46 #include "fas216.h"
47
48 #include <scsi/scsicam.h>
49
50 /* Configuration */
51 #define EESOX_XTALFREQ 40
52 #define EESOX_ASYNC_PERIOD 200
53 #define EESOX_SYNC_DEPTH 7
54
55 /*
56 * List of devices that the driver will recognise
57 */
58 #define EESOXSCSI_LIST { MANU_EESOX, PROD_EESOX_SCSI2 }
59
60 #define EESOX_FAS216_OFFSET 0xc00
61 #define EESOX_FAS216_SHIFT 3
62
63 #define EESOX_STATUS 0xa00
64 #define EESOX_STAT_INTR 0x01
65 #define EESOX_STAT_DMA 0x02
66
67 #define EESOX_CONTROL 0xa00
68 #define EESOX_INTR_ENABLE 0x04
69 #define EESOX_TERM_ENABLE 0x02
70 #define EESOX_RESET 0x01
71
72 #define EESOX_DMA_OFFSET 0xe00
73
74 /*
75 * Version
76 */
77 #define VER_MAJOR 0
78 #define VER_MINOR 0
79 #define VER_PATCH 3
80
81 static struct expansion_card *ecs[MAX_ECARDS];
82
83 MODULE_AUTHOR("Russell King");
84 MODULE_DESCRIPTION("EESOX SCSI driver");
85 MODULE_PARM(term, "1-8i");
86 MODULE_PARM_DESC(term, "SCSI bus termination");
87
88 /*
89 * Use term=0,1,0,0,0 to turn terminators on/off
90 */
91 static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
92
93 #define NR_SG 256
94
95 struct control {
96 unsigned int io_port;
97 unsigned int control;
98 };
99
100 typedef struct {
101 FAS216_Info info;
102
103 struct control control;
104
105 unsigned int dmaarea; /* Pseudo DMA area */
106 struct scatterlist sg[NR_SG]; /* Scatter DMA list */
107 } EESOXScsi_Info;
108
109 /* Prototype: void eesoxscsi_irqenable(ec, irqnr)
110 * Purpose : Enable interrupts on EESOX SCSI card
111 * Params : ec - expansion card structure
112 * : irqnr - interrupt number
113 */
114 static void
115 eesoxscsi_irqenable(struct expansion_card *ec, int irqnr)
116 {
117 struct control *control = (struct control *)ec->irq_data;
118
119 control->control |= EESOX_INTR_ENABLE;
120
121 outb(control->control, control->io_port);
122 }
123
124 /* Prototype: void eesoxscsi_irqdisable(ec, irqnr)
125 * Purpose : Disable interrupts on EESOX SCSI card
126 * Params : ec - expansion card structure
127 * : irqnr - interrupt number
128 */
129 static void
130 eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr)
131 {
132 struct control *control = (struct control *)ec->irq_data;
133
134 control->control &= ~EESOX_INTR_ENABLE;
135
136 outb(control->control, control->io_port);
137 }
138
139 static const expansioncard_ops_t eesoxscsi_ops = {
140 eesoxscsi_irqenable,
141 eesoxscsi_irqdisable,
142 NULL,
143 NULL,
144 NULL,
145 NULL
146 };
147
148 /* Prototype: void eesoxscsi_terminator_ctl(*host, on_off)
149 * Purpose : Turn the EESOX SCSI terminators on or off
150 * Params : host - card to turn on/off
151 * : on_off - !0 to turn on, 0 to turn off
152 */
153 static void
154 eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
155 {
156 EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
157 unsigned long flags;
158
159 save_flags_cli(flags);
160 if (on_off)
161 info->control.control |= EESOX_TERM_ENABLE;
162 else
163 info->control.control &= ~EESOX_TERM_ENABLE;
164 restore_flags(flags);
165
166 outb(info->control.control, info->control.io_port);
167 }
168
169 /* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs)
170 * Purpose : handle interrupts from EESOX SCSI card
171 * Params : irq - interrupt number
172 * dev_id - user-defined (Scsi_Host structure)
173 * regs - processor registers at interrupt
174 */
175 static void
176 eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
177 {
178 struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
179
180 fas216_intr(host);
181 }
182
183 /* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type)
184 * Purpose : initialises DMA/PIO
185 * Params : host - host
186 * SCpnt - command
187 * direction - DMA on to/off of card
188 * min_type - minimum DMA support that we must have for this transfer
189 * Returns : type of transfer to be performed
190 */
191 static fasdmatype_t
192 eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
193 fasdmadir_t direction, fasdmatype_t min_type)
194 {
195 EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
196 int dmach = host->dma_channel;
197
198 if (dmach != NO_DMA &&
199 (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
200 int bufs = SCp->buffers_residual;
201 int pci_dir, dma_dir;
202
203 if (bufs)
204 memcpy(info->sg + 1, SCp->buffer + 1,
205 sizeof(struct scatterlist) * bufs);
206 info->sg[0].address = SCp->ptr;
207 info->sg[0].length = SCp->this_residual;
208
209 if (direction == DMA_OUT)
210 pci_dir = PCI_DMA_TODEVICE,
211 dma_dir = DMA_MODE_WRITE;
212 else
213 pci_dir = PCI_DMA_FROMDEVICE,
214 dma_dir = DMA_MODE_READ;
215
216 pci_map_sg(NULL, info->sg, bufs + 1, pci_dir);
217
218 disable_dma(dmach);
219 set_dma_sg(dmach, info->sg, bufs + 1);
220 set_dma_mode(dmach, dma_dir);
221 enable_dma(dmach);
222 return fasdma_real_all;
223 }
224 /*
225 * We don't do DMA, we only do slow PIO
226 *
227 * Some day, we will do Pseudo DMA
228 */
229 return fasdma_pseudo;
230 }
231
232 static void
233 eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
234 fasdmadir_t dir, int transfer_size)
235 {
236 EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
237 unsigned int status;
238 unsigned int length = SCp->this_residual;
239 union {
240 unsigned char *c;
241 unsigned short *s;
242 unsigned long *l;
243 } buffer;
244
245 buffer.c = SCp->ptr;
246
247 status = inb(host->io_port + EESOX_STATUS);
248 if (dir == DMA_IN) {
249 while (length > 8) {
250 if (status & EESOX_STAT_DMA) {
251 unsigned long l1, l2;
252
253 l1 = inw(info->dmaarea);
254 l1 |= inw(info->dmaarea) << 16;
255 l2 = inw(info->dmaarea);
256 l2 |= inw(info->dmaarea) << 16;
257 *buffer.l++ = l1;
258 *buffer.l++ = l2;
259 length -= 8;
260 } else if (status & EESOX_STAT_INTR)
261 goto end;
262 status = inb(host->io_port + EESOX_STATUS);
263 }
264
265 while (length > 1) {
266 if (status & EESOX_STAT_DMA) {
267 *buffer.s++ = inw(info->dmaarea);
268 length -= 2;
269 } else if (status & EESOX_STAT_INTR)
270 goto end;
271 status = inb(host->io_port + EESOX_STATUS);
272 }
273
274 while (length > 0) {
275 if (status & EESOX_STAT_DMA) {
276 *buffer.c++ = inw(info->dmaarea);
277 length -= 1;
278 } else if (status & EESOX_STAT_INTR)
279 goto end;
280 status = inb(host->io_port + EESOX_STATUS);
281 }
282 } else {
283 while (length > 8) {
284 if (status & EESOX_STAT_DMA) {
285 unsigned long l1, l2;
286
287 l1 = *buffer.l++;
288 l2 = *buffer.l++;
289
290 outw(l1, info->dmaarea);
291 outw(l1 >> 16, info->dmaarea);
292 outw(l2, info->dmaarea);
293 outw(l2 >> 16, info->dmaarea);
294 length -= 8;
295 } else if (status & EESOX_STAT_INTR)
296 goto end;
297 status = inb(host->io_port + EESOX_STATUS);
298 }
299
300 while (length > 1) {
301 if (status & EESOX_STAT_DMA) {
302 outw(*buffer.s++, info->dmaarea);
303 length -= 2;
304 } else if (status & EESOX_STAT_INTR)
305 goto end;
306 status = inb(host->io_port + EESOX_STATUS);
307 }
308
309 while (length > 0) {
310 if (status & EESOX_STAT_DMA) {
311 outw(*buffer.c++, info->dmaarea);
312 length -= 1;
313 } else if (status & EESOX_STAT_INTR)
314 goto end;
315 status = inb(host->io_port + EESOX_STATUS);
316 }
317 }
318 end:
319 }
320
321 /* Prototype: int eesoxscsi_dma_stop(host, SCpnt)
322 * Purpose : stops DMA/PIO
323 * Params : host - host
324 * SCpnt - command
325 */
326 static void
327 eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
328 {
329 if (host->dma_channel != NO_DMA)
330 disable_dma(host->dma_channel);
331 }
332
333 /* Prototype: int eesoxscsi_detect(Scsi_Host_Template * tpnt)
334 * Purpose : initialises EESOX SCSI driver
335 * Params : tpnt - template for this SCSI adapter
336 * Returns : >0 if host found, 0 otherwise.
337 */
338 int
339 eesoxscsi_detect(Scsi_Host_Template *tpnt)
340 {
341 static const card_ids eesoxscsi_cids[] =
342 { EESOXSCSI_LIST, { 0xffff, 0xffff} };
343 int count = 0;
344 struct Scsi_Host *host;
345
346 tpnt->proc_name = "eesox";
347 memset(ecs, 0, sizeof (ecs));
348
349 ecard_startfind();
350
351 while(1) {
352 EESOXScsi_Info *info;
353
354 ecs[count] = ecard_find(0, eesoxscsi_cids);
355 if (!ecs[count])
356 break;
357
358 ecard_claim(ecs[count]);
359
360 host = scsi_register(tpnt, sizeof (EESOXScsi_Info));
361 if (!host) {
362 ecard_release(ecs[count]);
363 break;
364 }
365
366 host->io_port = ecard_address(ecs[count], ECARD_IOC, ECARD_FAST);
367 host->irq = ecs[count]->irq;
368 host->dma_channel = ecs[count]->dma;
369 info = (EESOXScsi_Info *)host->hostdata;
370
371 info->control.io_port = host->io_port + EESOX_CONTROL;
372 info->control.control = term[count] ? EESOX_TERM_ENABLE : 0;
373 outb(info->control.control, info->control.io_port);
374
375 ecs[count]->irqaddr = (unsigned char *)
376 ioaddr(host->io_port + EESOX_STATUS);
377 ecs[count]->irqmask = EESOX_STAT_INTR;
378 ecs[count]->irq_data = &info->control;
379 ecs[count]->ops = (expansioncard_ops_t *)&eesoxscsi_ops;
380
381 info->info.scsi.io_port = host->io_port + EESOX_FAS216_OFFSET;
382 info->info.scsi.io_shift = EESOX_FAS216_SHIFT;
383 info->info.scsi.irq = host->irq;
384 info->info.ifcfg.clockrate = EESOX_XTALFREQ;
385 info->info.ifcfg.select_timeout = 255;
386 info->info.ifcfg.asyncperiod = EESOX_ASYNC_PERIOD;
387 info->info.ifcfg.sync_max_depth = EESOX_SYNC_DEPTH;
388 info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
389 info->info.ifcfg.disconnect_ok = 1;
390 info->info.ifcfg.wide_max_size = 0;
391 info->info.dma.setup = eesoxscsi_dma_setup;
392 info->info.dma.pseudo = eesoxscsi_dma_pseudo;
393 info->info.dma.stop = eesoxscsi_dma_stop;
394 info->dmaarea = host->io_port + EESOX_DMA_OFFSET;
395
396 request_region(host->io_port + EESOX_FAS216_OFFSET,
397 16 << EESOX_FAS216_SHIFT, "eesox2-fas");
398
399 if (host->irq != NO_IRQ &&
400 request_irq(host->irq, eesoxscsi_intr,
401 SA_INTERRUPT, "eesox", host)) {
402 printk("scsi%d: IRQ%d not free, interrupts disabled\n",
403 host->host_no, host->irq);
404 host->irq = NO_IRQ;
405 }
406
407 if (host->dma_channel != NO_DMA &&
408 request_dma(host->dma_channel, "eesox")) {
409 printk("scsi%d: DMA%d not free, DMA disabled\n",
410 host->host_no, host->dma_channel);
411 host->dma_channel = NO_DMA;
412 }
413
414 fas216_init(host);
415 ++count;
416 }
417 return count;
418 }
419
420 /* Prototype: int eesoxscsi_release(struct Scsi_Host * host)
421 * Purpose : releases all resources used by this adapter
422 * Params : host - driver host structure to return info for.
423 */
424 int eesoxscsi_release(struct Scsi_Host *host)
425 {
426 int i;
427
428 fas216_release(host);
429
430 if (host->irq != NO_IRQ)
431 free_irq(host->irq, host);
432 if (host->dma_channel != NO_DMA)
433 free_dma(host->dma_channel);
434 release_region(host->io_port + EESOX_FAS216_OFFSET, 16 << EESOX_FAS216_SHIFT);
435
436 for (i = 0; i < MAX_ECARDS; i++)
437 if (ecs[i] &&
438 host->io_port == ecard_address(ecs[i], ECARD_IOC, ECARD_FAST))
439 ecard_release(ecs[i]);
440 return 0;
441 }
442
443 /* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host)
444 * Purpose : returns a descriptive string about this interface,
445 * Params : host - driver host structure to return info for.
446 * Returns : pointer to a static buffer containing null terminated string.
447 */
448 const char *eesoxscsi_info(struct Scsi_Host *host)
449 {
450 EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
451 static char string[100], *p;
452
453 p = string;
454 p += sprintf(p, "%s ", host->hostt->name);
455 p += fas216_info(&info->info, p);
456 p += sprintf(p, "v%d.%d.%d terminators o%s",
457 VER_MAJOR, VER_MINOR, VER_PATCH,
458 info->control.control & EESOX_TERM_ENABLE ? "n" : "ff");
459
460 return string;
461 }
462
463 /* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
464 * Purpose : Set a driver specific function
465 * Params : host - host to setup
466 * : buffer - buffer containing string describing operation
467 * : length - length of string
468 * Returns : -EINVAL, or 0
469 */
470 static int
471 eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
472 {
473 int ret = length;
474
475 if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) {
476 buffer += 9;
477 length -= 9;
478
479 if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
480 if (buffer[5] == '1')
481 eesoxscsi_terminator_ctl(host, 1);
482 else if (buffer[5] == '0')
483 eesoxscsi_terminator_ctl(host, 0);
484 else
485 ret = -EINVAL;
486 } else
487 ret = -EINVAL;
488 } else
489 ret = -EINVAL;
490
491 return ret;
492 }
493
494 /* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
495 * int length, int host_no, int inout)
496 * Purpose : Return information about the driver to a user process accessing
497 * the /proc filesystem.
498 * Params : buffer - a buffer to write information to
499 * start - a pointer into this buffer set by this routine to the start
500 * of the required information.
501 * offset - offset into information that we have read upto.
502 * length - length of buffer
503 * host_no - host number to return information for
504 * inout - 0 for reading, 1 for writing.
505 * Returns : length of data written to buffer.
506 */
507 int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
508 int length, int host_no, int inout)
509 {
510 int pos, begin;
511 struct Scsi_Host *host = scsi_hostlist;
512 EESOXScsi_Info *info;
513 Scsi_Device *scd;
514
515 while (host) {
516 if (host->host_no == host_no)
517 break;
518 host = host->next;
519 }
520 if (!host)
521 return 0;
522
523 if (inout == 1)
524 return eesoxscsi_set_proc_info(host, buffer, length);
525
526 info = (EESOXScsi_Info *)host->hostdata;
527
528 begin = 0;
529 pos = sprintf(buffer,
530 "EESOX SCSI driver version %d.%d.%d\n",
531 VER_MAJOR, VER_MINOR, VER_PATCH);
532 pos += fas216_print_host(&info->info, buffer + pos);
533 pos += sprintf(buffer + pos, "Term : o%s\n",
534 info->control.control & EESOX_TERM_ENABLE ? "n" : "ff");
535
536 pos += fas216_print_stats(&info->info, buffer + pos);
537
538 pos += sprintf (buffer+pos, "\nAttached devices:\n");
539
540 for (scd = host->host_queue; scd; scd = scd->next) {
541 int len;
542
543 proc_print_scsidevice (scd, buffer, &len, pos);
544 pos += len;
545 pos += sprintf (buffer+pos, "Extensions: ");
546 if (scd->tagged_supported)
547 pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
548 scd->tagged_queue ? "en" : "dis",
549 scd->current_tag);
550 pos += sprintf (buffer+pos, "\n");
551
552 if (pos + begin < offset) {
553 begin += pos;
554 pos = 0;
555 }
556 }
557 *start = buffer + (offset - begin);
558 pos -= offset - begin;
559 if (pos > length)
560 pos = length;
561
562 return pos;
563 }
564
565 static Scsi_Host_Template eesox_template = {
566 module: THIS_MODULE,
567 proc_info: eesoxscsi_proc_info,
568 name: "EESOX SCSI",
569 detect: eesoxscsi_detect,
570 release: eesoxscsi_release,
571 info: eesoxscsi_info,
572 bios_param: scsicam_bios_param,
573 can_queue: 1,
574 this_id: 7,
575 sg_tablesize: SG_ALL,
576 cmd_per_lun: 1,
577 use_clustering: DISABLE_CLUSTERING,
578 command: fas216_command,
579 queuecommand: fas216_queue_command,
580 eh_host_reset_handler: fas216_eh_host_reset,
581 eh_bus_reset_handler: fas216_eh_bus_reset,
582 eh_device_reset_handler: fas216_eh_device_reset,
583 eh_abort_handler: fas216_eh_abort,
584 use_new_eh_code: 1
585 };
586
587 static int __init eesox_init(void)
588 {
589 scsi_register_module(MODULE_SCSI_HA, &eesox_template);
590 if (eesox_template.present)
591 return 0;
592
593 scsi_unregister_module(MODULE_SCSI_HA, &eesox_template);
594 return -ENODEV;
595 }
596
597 static void __exit eesox_exit(void)
598 {
599 scsi_unregister_module(MODULE_SCSI_HA, &eesox_template);
600 }
601
602 module_init(eesox_init);
603 module_exit(eesox_exit);
604
605 EXPORT_NO_SYMBOLS;
606 MODULE_LICENSE("GPL");
607