Merge with Linux 2.3.99-pre4.
[linux-2.6/linux-mips.git] / drivers / acorn / scsi / cumana_2.c
blobb812ef2d0b65359f91966a6d585565b7a5af44f2
1 /*
2 * linux/arch/arm/drivers/scsi/cumana_2.c
4 * Copyright (C) 1997-2000 Russell King
6 * Changelog:
7 * 30-08-1997 RMK 0.0.0 Created, READONLY version.
8 * 22-01-1998 RMK 0.0.1 Updated to 2.1.80.
9 * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it.
10 * 02-05-1998 RMK 0.0.2 Updated & added DMA support.
11 * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
12 * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth.
13 * 02-04-2000 RMK 0.0.4 Updated for new error handling code.
16 #include <linux/module.h>
17 #include <linux/blk.h>
18 #include <linux/kernel.h>
19 #include <linux/string.h>
20 #include <linux/ioport.h>
21 #include <linux/sched.h>
22 #include <linux/proc_fs.h>
23 #include <linux/unistd.h>
24 #include <linux/stat.h>
25 #include <linux/delay.h>
27 #include <asm/dma.h>
28 #include <asm/ecard.h>
29 #include <asm/io.h>
30 #include <asm/irq.h>
31 #include <asm/pgtable.h>
33 #include "../../scsi/sd.h"
34 #include "../../scsi/hosts.h"
35 #include "cumana_2.h"
37 /* Configuration */
38 #define CUMANASCSI2_XTALFREQ 40
39 #define CUMANASCSI2_ASYNC_PERIOD 200
40 #define CUMANASCSI2_SYNC_DEPTH 7
43 * List of devices that the driver will recognise
45 #define CUMANASCSI2_LIST { MANU_CUMANA, PROD_CUMANA_SCSI_2 }
47 #define CUMANASCSI2_STATUS (0)
48 #define STATUS_INT (1 << 0)
49 #define STATUS_DRQ (1 << 1)
50 #define STATUS_LATCHED (1 << 3)
52 #define CUMANASCSI2_ALATCH (5)
53 #define ALATCH_ENA_INT (3)
54 #define ALATCH_DIS_INT (2)
55 #define ALATCH_ENA_TERM (5)
56 #define ALATCH_DIS_TERM (4)
57 #define ALATCH_ENA_BIT32 (11)
58 #define ALATCH_DIS_BIT32 (10)
59 #define ALATCH_ENA_DMA (13)
60 #define ALATCH_DIS_DMA (12)
61 #define ALATCH_DMA_OUT (15)
62 #define ALATCH_DMA_IN (14)
64 #define CUMANASCSI2_PSEUDODMA (0x80)
66 #define CUMANASCSI2_FAS216_OFFSET (0xc0)
67 #define CUMANASCSI2_FAS216_SHIFT 0
70 * Version
72 #define VER_MAJOR 0
73 #define VER_MINOR 0
74 #define VER_PATCH 4
76 static struct expansion_card *ecs[MAX_ECARDS];
78 MODULE_AUTHOR("Russell King");
79 MODULE_DESCRIPTION("Cumana SCSI II driver");
80 MODULE_PARM(term, "1-8i");
81 MODULE_PARM_DESC(term, "SCSI bus termination");
84 * Use term=0,1,0,0,0 to turn terminators on/off
86 int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
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
93 static void
94 cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr)
96 unsigned int port = (unsigned int)ec->irq_data;
97 outb(ALATCH_ENA_INT, port);
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
105 static void
106 cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr)
108 unsigned int port = (unsigned int)ec->irq_data;
109 outb(ALATCH_DIS_INT, port);
112 static const expansioncard_ops_t cumanascsi_2_ops = {
113 cumanascsi_2_irqenable,
114 cumanascsi_2_irqdisable,
115 NULL,
116 NULL,
117 NULL,
118 NULL
121 /* Prototype: void cumanascsi_2_terminator_ctl(host, on_off)
122 * Purpose : Turn the Cumana SCSI 2 terminators on or off
123 * Params : host - card to turn on/off
124 * : on_off - !0 to turn on, 0 to turn off
126 static void
127 cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off)
129 CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
131 if (on_off) {
132 info->terms = 1;
133 outb (ALATCH_ENA_TERM, info->alatch);
134 } else {
135 info->terms = 0;
136 outb (ALATCH_DIS_TERM, info->alatch);
140 /* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs)
141 * Purpose : handle interrupts from Cumana SCSI 2 card
142 * Params : irq - interrupt number
143 * dev_id - user-defined (Scsi_Host structure)
144 * regs - processor registers at interrupt
146 static void
147 cumanascsi_2_intr(int irq, void *dev_id, struct pt_regs *regs)
149 struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
151 fas216_intr(host);
154 static void
155 cumanascsi_2_invalidate(char *addr, long len, fasdmadir_t direction)
157 if (direction == DMA_OUT)
158 dma_cache_wback((unsigned long)addr, (unsigned long)len);
159 else
160 dma_cache_inv((unsigned long)addr, (unsigned long)len);
163 /* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type)
164 * Purpose : initialises DMA/PIO
165 * Params : host - host
166 * SCpnt - command
167 * direction - DMA on to/off of card
168 * min_type - minimum DMA support that we must have for this transfer
169 * Returns : type of transfer to be performed
171 static fasdmatype_t
172 cumanascsi_2_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
173 fasdmadir_t direction, fasdmatype_t min_type)
175 CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
176 int dmach = host->dma_channel;
178 outb(ALATCH_DIS_DMA, info->alatch);
180 if (dmach != NO_DMA &&
181 (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
182 int buf;
184 for (buf = 1; buf <= SCp->buffers_residual &&
185 buf < NR_SG; buf++) {
186 info->dmasg[buf].address = __virt_to_bus(
187 (unsigned long)SCp->buffer[buf].address);
188 info->dmasg[buf].length = SCp->buffer[buf].length;
190 cumanascsi_2_invalidate(SCp->buffer[buf].address,
191 SCp->buffer[buf].length,
192 direction);
195 info->dmasg[0].address = __virt_to_phys((unsigned long)SCp->ptr);
196 info->dmasg[0].length = SCp->this_residual;
197 cumanascsi_2_invalidate(SCp->ptr,
198 SCp->this_residual, direction);
200 disable_dma(dmach);
201 set_dma_sg(dmach, info->dmasg, buf);
202 if (direction == DMA_OUT) {
203 outb(ALATCH_DMA_OUT, info->alatch);
204 set_dma_mode(dmach, DMA_MODE_WRITE);
205 } else {
206 outb(ALATCH_DMA_IN, info->alatch);
207 set_dma_mode(dmach, DMA_MODE_READ);
209 enable_dma(dmach);
210 outb(ALATCH_ENA_DMA, info->alatch);
211 outb(ALATCH_DIS_BIT32, info->alatch);
212 return fasdma_real_all;
216 * If we're not doing DMA,
217 * we'll do pseudo DMA
219 return fasdma_pio;
223 * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer)
224 * Purpose : handles pseudo DMA
225 * Params : host - host
226 * SCpnt - command
227 * direction - DMA on to/off of card
228 * transfer - minimum number of bytes we expect to transfer
230 static void
231 cumanascsi_2_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
232 fasdmadir_t direction, int transfer)
234 CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
235 unsigned int length;
236 unsigned char *addr;
238 length = SCp->this_residual;
239 addr = SCp->ptr;
241 if (direction == DMA_OUT)
242 #if 0
243 while (length > 1) {
244 unsigned long word;
245 unsigned int status = inb(info->status);
247 if (status & STATUS_INT)
248 goto end;
250 if (!(status & STATUS_DRQ))
251 continue;
253 word = *addr | *(addr + 1) << 8;
254 outw (info->dmaarea);
255 addr += 2;
256 length -= 2;
258 #else
259 printk ("PSEUDO_OUT???\n");
260 #endif
261 else {
262 if (transfer && (transfer & 255)) {
263 while (length >= 256) {
264 unsigned int status = inb(info->status);
266 if (status & STATUS_INT)
267 goto end;
269 if (!(status & STATUS_DRQ))
270 continue;
272 insw(info->dmaarea, addr, 256 >> 1);
273 addr += 256;
274 length -= 256;
278 while (length > 0) {
279 unsigned long word;
280 unsigned int status = inb(info->status);
282 if (status & STATUS_INT)
283 goto end;
285 if (!(status & STATUS_DRQ))
286 continue;
288 word = inw (info->dmaarea);
289 *addr++ = word;
290 if (--length > 0) {
291 *addr++ = word >> 8;
292 length --;
297 end:
300 /* Prototype: int cumanascsi_2_dma_stop(host, SCpnt)
301 * Purpose : stops DMA/PIO
302 * Params : host - host
303 * SCpnt - command
305 static void
306 cumanascsi_2_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
308 CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
309 if (host->dma_channel != NO_DMA) {
310 outb(ALATCH_DIS_DMA, info->alatch);
311 disable_dma(host->dma_channel);
315 /* Prototype: int cumanascsi_2_detect(Scsi_Host_Template * tpnt)
316 * Purpose : initialises Cumana SCSI 2 driver
317 * Params : tpnt - template for this SCSI adapter
318 * Returns : >0 if host found, 0 otherwise.
321 cumanascsi_2_detect(Scsi_Host_Template *tpnt)
323 static const card_ids cumanascsi_2_cids[] =
324 { CUMANASCSI2_LIST, { 0xffff, 0xffff} };
325 int count = 0;
326 struct Scsi_Host *host;
328 tpnt->proc_name = "cumanascs2";
329 memset(ecs, 0, sizeof (ecs));
331 ecard_startfind();
333 while (1) {
334 CumanaScsi2_Info *info;
336 ecs[count] = ecard_find(0, cumanascsi_2_cids);
337 if (!ecs[count])
338 break;
340 ecard_claim(ecs[count]);
342 host = scsi_register(tpnt, sizeof (CumanaScsi2_Info));
343 if (!host) {
344 ecard_release(ecs[count]);
345 break;
348 host->io_port = ecard_address(ecs[count], ECARD_MEMC, 0);
349 host->irq = ecs[count]->irq;
350 host->dma_channel = ecs[count]->dma;
351 info = (CumanaScsi2_Info *)host->hostdata;
353 info->terms = term[count] ? 1 : 0;
354 cumanascsi_2_terminator_ctl(host, info->terms);
356 info->info.scsi.io_port = host->io_port + CUMANASCSI2_FAS216_OFFSET;
357 info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT;
358 info->info.scsi.irq = host->irq;
359 info->info.ifcfg.clockrate = CUMANASCSI2_XTALFREQ;
360 info->info.ifcfg.select_timeout = 255;
361 info->info.ifcfg.asyncperiod = CUMANASCSI2_ASYNC_PERIOD;
362 info->info.ifcfg.sync_max_depth = CUMANASCSI2_SYNC_DEPTH;
363 info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
364 info->info.ifcfg.disconnect_ok = 1;
365 info->info.ifcfg.wide_max_size = 0;
366 info->info.dma.setup = cumanascsi_2_dma_setup;
367 info->info.dma.pseudo = cumanascsi_2_dma_pseudo;
368 info->info.dma.stop = cumanascsi_2_dma_stop;
369 info->dmaarea = host->io_port + CUMANASCSI2_PSEUDODMA;
370 info->status = host->io_port + CUMANASCSI2_STATUS;
371 info->alatch = host->io_port + CUMANASCSI2_ALATCH;
373 ecs[count]->irqaddr = (unsigned char *)ioaddr(info->status);
374 ecs[count]->irqmask = STATUS_INT;
375 ecs[count]->irq_data = (void *)info->alatch;
376 ecs[count]->ops = (expansioncard_ops_t *)&cumanascsi_2_ops;
378 request_region(host->io_port + CUMANASCSI2_FAS216_OFFSET,
379 16 << CUMANASCSI2_FAS216_SHIFT, "cumanascsi2-fas");
381 if (host->irq != NO_IRQ &&
382 request_irq(host->irq, cumanascsi_2_intr,
383 SA_INTERRUPT, "cumanascsi2", host)) {
384 printk("scsi%d: IRQ%d not free, interrupts disabled\n",
385 host->host_no, host->irq);
386 host->irq = NO_IRQ;
387 info->info.scsi.irq = NO_IRQ;
390 if (host->dma_channel != NO_DMA &&
391 request_dma(host->dma_channel, "cumanascsi2")) {
392 printk("scsi%d: DMA%d not free, DMA disabled\n",
393 host->host_no, host->dma_channel);
394 host->dma_channel = NO_DMA;
397 fas216_init(host);
398 ++count;
400 return count;
403 /* Prototype: int cumanascsi_2_release(struct Scsi_Host * host)
404 * Purpose : releases all resources used by this adapter
405 * Params : host - driver host structure to return info for.
407 int cumanascsi_2_release(struct Scsi_Host *host)
409 int i;
411 fas216_release(host);
413 if (host->irq != NO_IRQ)
414 free_irq(host->irq, host);
415 if (host->dma_channel != NO_DMA)
416 free_dma(host->dma_channel);
417 release_region(host->io_port + CUMANASCSI2_FAS216_OFFSET,
418 16 << CUMANASCSI2_FAS216_SHIFT);
420 for (i = 0; i < MAX_ECARDS; i++)
421 if (ecs[i] && host->io_port == ecard_address (ecs[i], ECARD_MEMC, 0))
422 ecard_release (ecs[i]);
423 return 0;
426 /* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host)
427 * Purpose : returns a descriptive string about this interface,
428 * Params : host - driver host structure to return info for.
429 * Returns : pointer to a static buffer containing null terminated string.
431 const char *cumanascsi_2_info(struct Scsi_Host *host)
433 CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
434 static char string[100], *p;
436 p = string;
437 p += sprintf(p, "%s ", host->hostt->name);
438 p += fas216_info(&info->info, p);
439 p += sprintf(p, "v%d.%d.%d terminators o%s",
440 VER_MAJOR, VER_MINOR, VER_PATCH,
441 info->terms ? "n" : "ff");
443 return string;
446 /* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
447 * Purpose : Set a driver specific function
448 * Params : host - host to setup
449 * : buffer - buffer containing string describing operation
450 * : length - length of string
451 * Returns : -EINVAL, or 0
453 static int
454 cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
456 int ret = length;
458 if (length >= 11 && strcmp(buffer, "CUMANASCSI2") == 0) {
459 buffer += 11;
460 length -= 11;
462 if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
463 if (buffer[5] == '1')
464 cumanascsi_2_terminator_ctl(host, 1);
465 else if (buffer[5] == '0')
466 cumanascsi_2_terminator_ctl(host, 0);
467 else
468 ret = -EINVAL;
469 } else
470 ret = -EINVAL;
471 } else
472 ret = -EINVAL;
474 return ret;
477 /* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset,
478 * int length, int host_no, int inout)
479 * Purpose : Return information about the driver to a user process accessing
480 * the /proc filesystem.
481 * Params : buffer - a buffer to write information to
482 * start - a pointer into this buffer set by this routine to the start
483 * of the required information.
484 * offset - offset into information that we have read upto.
485 * length - length of buffer
486 * host_no - host number to return information for
487 * inout - 0 for reading, 1 for writing.
488 * Returns : length of data written to buffer.
490 int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset,
491 int length, int host_no, int inout)
493 int pos, begin;
494 struct Scsi_Host *host = scsi_hostlist;
495 CumanaScsi2_Info *info;
496 Scsi_Device *scd;
498 while (host) {
499 if (host->host_no == host_no)
500 break;
501 host = host->next;
503 if (!host)
504 return 0;
506 if (inout == 1)
507 return cumanascsi_2_set_proc_info(host, buffer, length);
509 info = (CumanaScsi2_Info *)host->hostdata;
511 begin = 0;
512 pos = sprintf(buffer,
513 "Cumana SCSI II driver version %d.%d.%d\n",
514 VER_MAJOR, VER_MINOR, VER_PATCH);
516 pos += fas216_print_host(&info->info, buffer + pos);
517 pos += sprintf(buffer + pos, "Term : o%s\n",
518 info->terms ? "n" : "ff");
520 pos += fas216_print_stats(&info->info, buffer + pos);
522 pos += sprintf(buffer+pos, "\nAttached devices:\n");
524 for (scd = host->host_queue; scd; scd = scd->next) {
525 int len;
527 proc_print_scsidevice(scd, buffer, &len, pos);
528 pos += len;
529 pos += sprintf(buffer+pos, "Extensions: ");
530 if (scd->tagged_supported)
531 pos += sprintf(buffer+pos, "TAG %sabled [%d] ",
532 scd->tagged_queue ? "en" : "dis",
533 scd->current_tag);
534 pos += sprintf(buffer+pos, "\n");
536 if (pos + begin < offset) {
537 begin += pos;
538 pos = 0;
540 if (pos + begin > offset + length)
541 break;
544 *start = buffer + (offset - begin);
545 pos -= offset - begin;
546 if (pos > length)
547 pos = length;
549 return pos;
552 #ifdef MODULE
553 Scsi_Host_Template driver_template = CUMANASCSI_2;
555 #include "../../scsi/scsi_module.c"
556 #endif