2 * linux/arch/arm/drivers/scsi/cumana_2.c
4 * Copyright (C) 1997-1998 Russell King
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.
15 #include <linux/module.h>
16 #include <linux/blk.h>
17 #include <linux/kernel.h>
18 #include <linux/string.h>
19 #include <linux/ioport.h>
20 #include <linux/sched.h>
21 #include <linux/proc_fs.h>
22 #include <linux/unistd.h>
23 #include <linux/stat.h>
24 #include <linux/delay.h>
27 #include <asm/ecard.h>
30 #include <asm/pgtable.h>
32 #include "../../scsi/sd.h"
33 #include "../../scsi/hosts.h"
37 #define CUMANASCSI2_XTALFREQ 40
38 #define CUMANASCSI2_ASYNC_PERIOD 200
39 #define CUMANASCSI2_SYNC_DEPTH 7
42 * List of devices that the driver will recognise
44 #define CUMANASCSI2_LIST { MANU_CUMANA, PROD_CUMANA_SCSI_2 }
46 #define CUMANASCSI2_STATUS (0)
47 #define STATUS_INT (1 << 0)
48 #define STATUS_DRQ (1 << 1)
49 #define STATUS_LATCHED (1 << 3)
51 #define CUMANASCSI2_ALATCH (5)
52 #define ALATCH_ENA_INT (3)
53 #define ALATCH_DIS_INT (2)
54 #define ALATCH_ENA_TERM (5)
55 #define ALATCH_DIS_TERM (4)
56 #define ALATCH_ENA_BIT32 (11)
57 #define ALATCH_DIS_BIT32 (10)
58 #define ALATCH_ENA_DMA (13)
59 #define ALATCH_DIS_DMA (12)
60 #define ALATCH_DMA_OUT (15)
61 #define ALATCH_DMA_IN (14)
63 #define CUMANASCSI2_PSEUDODMA (0x80)
65 #define CUMANASCSI2_FAS216_OFFSET (0xc0)
66 #define CUMANASCSI2_FAS216_SHIFT 0
75 static struct expansion_card
*ecs
[MAX_ECARDS
];
77 MODULE_AUTHOR("Russell King");
78 MODULE_DESCRIPTION("Cumana SCSI II driver");
79 MODULE_PARM(term
, "1-8i");
80 MODULE_PARM_DESC(term
, "SCSI bus termination");
83 * Use term=0,1,0,0,0 to turn terminators on/off
85 int term
[MAX_ECARDS
] = { 1, 1, 1, 1, 1, 1, 1, 1 };
87 static struct proc_dir_entry proc_scsi_cumanascsi_2
= {
88 PROC_SCSI_QLOGICFAS
, 6, "cumanascs2",
89 S_IFDIR
| S_IRUGO
| S_IXUGO
, 2
92 /* Prototype: void cumanascsi_2_irqenable(ec, irqnr)
93 * Purpose : Enable interrupts on Cumana SCSI 2 card
94 * Params : ec - expansion card structure
95 * : irqnr - interrupt number
98 cumanascsi_2_irqenable(struct expansion_card
*ec
, int irqnr
)
100 unsigned int port
= (unsigned int)ec
->irq_data
;
101 outb(ALATCH_ENA_INT
, port
);
104 /* Prototype: void cumanascsi_2_irqdisable(ec, irqnr)
105 * Purpose : Disable interrupts on Cumana SCSI 2 card
106 * Params : ec - expansion card structure
107 * : irqnr - interrupt number
110 cumanascsi_2_irqdisable(struct expansion_card
*ec
, int irqnr
)
112 unsigned int port
= (unsigned int)ec
->irq_data
;
113 outb(ALATCH_DIS_INT
, port
);
116 static const expansioncard_ops_t cumanascsi_2_ops
= {
117 cumanascsi_2_irqenable
,
118 cumanascsi_2_irqdisable
,
125 /* Prototype: void cumanascsi_2_terminator_ctl(host, on_off)
126 * Purpose : Turn the Cumana SCSI 2 terminators on or off
127 * Params : host - card to turn on/off
128 * : on_off - !0 to turn on, 0 to turn off
131 cumanascsi_2_terminator_ctl(struct Scsi_Host
*host
, int on_off
)
133 CumanaScsi2_Info
*info
= (CumanaScsi2_Info
*)host
->hostdata
;
137 outb (ALATCH_ENA_TERM
, info
->alatch
);
140 outb (ALATCH_DIS_TERM
, info
->alatch
);
144 /* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs)
145 * Purpose : handle interrupts from Cumana SCSI 2 card
146 * Params : irq - interrupt number
147 * dev_id - user-defined (Scsi_Host structure)
148 * regs - processor registers at interrupt
151 cumanascsi_2_intr(int irq
, void *dev_id
, struct pt_regs
*regs
)
153 struct Scsi_Host
*host
= (struct Scsi_Host
*)dev_id
;
159 cumanascsi_2_invalidate(char *addr
, long len
, fasdmadir_t direction
)
161 if (direction
== DMA_OUT
)
162 dma_cache_wback((unsigned long)addr
, (unsigned long)len
);
164 dma_cache_inv((unsigned long)addr
, (unsigned long)len
);
167 /* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type)
168 * Purpose : initialises DMA/PIO
169 * Params : host - host
171 * direction - DMA on to/off of card
172 * min_type - minimum DMA support that we must have for this transfer
173 * Returns : type of transfer to be performed
176 cumanascsi_2_dma_setup(struct Scsi_Host
*host
, Scsi_Pointer
*SCp
,
177 fasdmadir_t direction
, fasdmatype_t min_type
)
179 CumanaScsi2_Info
*info
= (CumanaScsi2_Info
*)host
->hostdata
;
180 int dmach
= host
->dma_channel
;
182 outb(ALATCH_DIS_DMA
, info
->alatch
);
184 if (dmach
!= NO_DMA
&&
185 (min_type
== fasdma_real_all
|| SCp
->this_residual
>= 512)) {
188 for (buf
= 1; buf
<= SCp
->buffers_residual
&&
189 buf
< NR_SG
; buf
++) {
190 info
->dmasg
[buf
].address
= __virt_to_bus(
191 (unsigned long)SCp
->buffer
[buf
].address
);
192 info
->dmasg
[buf
].length
= SCp
->buffer
[buf
].length
;
194 cumanascsi_2_invalidate(SCp
->buffer
[buf
].address
,
195 SCp
->buffer
[buf
].length
,
199 info
->dmasg
[0].address
= __virt_to_phys((unsigned long)SCp
->ptr
);
200 info
->dmasg
[0].length
= SCp
->this_residual
;
201 cumanascsi_2_invalidate(SCp
->ptr
,
202 SCp
->this_residual
, direction
);
205 set_dma_sg(dmach
, info
->dmasg
, buf
);
206 if (direction
== DMA_OUT
) {
207 outb(ALATCH_DMA_OUT
, info
->alatch
);
208 set_dma_mode(dmach
, DMA_MODE_WRITE
);
210 outb(ALATCH_DMA_IN
, info
->alatch
);
211 set_dma_mode(dmach
, DMA_MODE_READ
);
214 outb(ALATCH_ENA_DMA
, info
->alatch
);
215 outb(ALATCH_DIS_BIT32
, info
->alatch
);
216 return fasdma_real_all
;
220 * If we're not doing DMA,
221 * we'll do pseudo DMA
227 * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer)
228 * Purpose : handles pseudo DMA
229 * Params : host - host
231 * direction - DMA on to/off of card
232 * transfer - minimum number of bytes we expect to transfer
235 cumanascsi_2_dma_pseudo(struct Scsi_Host
*host
, Scsi_Pointer
*SCp
,
236 fasdmadir_t direction
, int transfer
)
238 CumanaScsi2_Info
*info
= (CumanaScsi2_Info
*)host
->hostdata
;
242 length
= SCp
->this_residual
;
245 if (direction
== DMA_OUT
)
249 unsigned int status
= inb(info
->status
);
251 if (status
& STATUS_INT
)
254 if (!(status
& STATUS_DRQ
))
257 word
= *addr
| *(addr
+ 1) << 8;
258 outw (info
->dmaarea
);
263 printk ("PSEUDO_OUT???\n");
266 if (transfer
&& (transfer
& 255)) {
267 while (length
>= 256) {
268 unsigned int status
= inb(info
->status
);
270 if (status
& STATUS_INT
)
273 if (!(status
& STATUS_DRQ
))
276 insw(info
->dmaarea
, addr
, 256 >> 1);
284 unsigned int status
= inb(info
->status
);
286 if (status
& STATUS_INT
)
289 if (!(status
& STATUS_DRQ
))
292 word
= inw (info
->dmaarea
);
304 /* Prototype: int cumanascsi_2_dma_stop(host, SCpnt)
305 * Purpose : stops DMA/PIO
306 * Params : host - host
310 cumanascsi_2_dma_stop(struct Scsi_Host
*host
, Scsi_Pointer
*SCp
)
312 CumanaScsi2_Info
*info
= (CumanaScsi2_Info
*)host
->hostdata
;
313 if (host
->dma_channel
!= NO_DMA
) {
314 outb(ALATCH_DIS_DMA
, info
->alatch
);
315 disable_dma(host
->dma_channel
);
319 /* Prototype: int cumanascsi_2_detect(Scsi_Host_Template * tpnt)
320 * Purpose : initialises Cumana SCSI 2 driver
321 * Params : tpnt - template for this SCSI adapter
322 * Returns : >0 if host found, 0 otherwise.
325 cumanascsi_2_detect(Scsi_Host_Template
*tpnt
)
327 static const card_ids cumanascsi_2_cids
[] =
328 { CUMANASCSI2_LIST
, { 0xffff, 0xffff} };
330 struct Scsi_Host
*host
;
332 tpnt
->proc_dir
= &proc_scsi_cumanascsi_2
;
333 memset(ecs
, 0, sizeof (ecs
));
338 CumanaScsi2_Info
*info
;
340 ecs
[count
] = ecard_find(0, cumanascsi_2_cids
);
344 ecard_claim(ecs
[count
]);
346 host
= scsi_register(tpnt
, sizeof (CumanaScsi2_Info
));
348 ecard_release(ecs
[count
]);
352 host
->io_port
= ecard_address(ecs
[count
], ECARD_MEMC
, 0);
353 host
->irq
= ecs
[count
]->irq
;
354 host
->dma_channel
= ecs
[count
]->dma
;
355 info
= (CumanaScsi2_Info
*)host
->hostdata
;
357 info
->terms
= term
[count
] ? 1 : 0;
358 cumanascsi_2_terminator_ctl(host
, info
->terms
);
360 info
->info
.scsi
.io_port
= host
->io_port
+ CUMANASCSI2_FAS216_OFFSET
;
361 info
->info
.scsi
.io_shift
= CUMANASCSI2_FAS216_SHIFT
;
362 info
->info
.scsi
.irq
= host
->irq
;
363 info
->info
.ifcfg
.clockrate
= CUMANASCSI2_XTALFREQ
;
364 info
->info
.ifcfg
.select_timeout
= 255;
365 info
->info
.ifcfg
.asyncperiod
= CUMANASCSI2_ASYNC_PERIOD
;
366 info
->info
.ifcfg
.sync_max_depth
= CUMANASCSI2_SYNC_DEPTH
;
367 info
->info
.ifcfg
.cntl3
= CNTL3_BS8
| CNTL3_FASTSCSI
| CNTL3_FASTCLK
;
368 info
->info
.ifcfg
.disconnect_ok
= 1;
369 info
->info
.ifcfg
.wide_max_size
= 0;
370 info
->info
.dma
.setup
= cumanascsi_2_dma_setup
;
371 info
->info
.dma
.pseudo
= cumanascsi_2_dma_pseudo
;
372 info
->info
.dma
.stop
= cumanascsi_2_dma_stop
;
373 info
->dmaarea
= host
->io_port
+ CUMANASCSI2_PSEUDODMA
;
374 info
->status
= host
->io_port
+ CUMANASCSI2_STATUS
;
375 info
->alatch
= host
->io_port
+ CUMANASCSI2_ALATCH
;
377 ecs
[count
]->irqaddr
= (unsigned char *)ioaddr(info
->status
);
378 ecs
[count
]->irqmask
= STATUS_INT
;
379 ecs
[count
]->irq_data
= (void *)info
->alatch
;
380 ecs
[count
]->ops
= (expansioncard_ops_t
*)&cumanascsi_2_ops
;
382 request_region(host
->io_port
+ CUMANASCSI2_FAS216_OFFSET
,
383 16 << CUMANASCSI2_FAS216_SHIFT
, "cumanascsi2-fas");
385 if (host
->irq
!= NO_IRQ
&&
386 request_irq(host
->irq
, cumanascsi_2_intr
,
387 SA_INTERRUPT
, "cumanascsi2", host
)) {
388 printk("scsi%d: IRQ%d not free, interrupts disabled\n",
389 host
->host_no
, host
->irq
);
391 info
->info
.scsi
.irq
= NO_IRQ
;
394 if (host
->dma_channel
!= NO_DMA
&&
395 request_dma(host
->dma_channel
, "cumanascsi2")) {
396 printk("scsi%d: DMA%d not free, DMA disabled\n",
397 host
->host_no
, host
->dma_channel
);
398 host
->dma_channel
= NO_DMA
;
407 /* Prototype: int cumanascsi_2_release(struct Scsi_Host * host)
408 * Purpose : releases all resources used by this adapter
409 * Params : host - driver host structure to return info for.
411 int cumanascsi_2_release(struct Scsi_Host
*host
)
415 fas216_release(host
);
417 if (host
->irq
!= NO_IRQ
)
418 free_irq(host
->irq
, host
);
419 if (host
->dma_channel
!= NO_DMA
)
420 free_dma(host
->dma_channel
);
421 release_region(host
->io_port
+ CUMANASCSI2_FAS216_OFFSET
,
422 16 << CUMANASCSI2_FAS216_SHIFT
);
424 for (i
= 0; i
< MAX_ECARDS
; i
++)
425 if (ecs
[i
] && host
->io_port
== ecard_address (ecs
[i
], ECARD_MEMC
, 0))
426 ecard_release (ecs
[i
]);
430 /* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host)
431 * Purpose : returns a descriptive string about this interface,
432 * Params : host - driver host structure to return info for.
433 * Returns : pointer to a static buffer containing null terminated string.
435 const char *cumanascsi_2_info(struct Scsi_Host
*host
)
437 CumanaScsi2_Info
*info
= (CumanaScsi2_Info
*)host
->hostdata
;
438 static char string
[100], *p
;
441 p
+= sprintf(string
, "%s at port %lX ",
442 host
->hostt
->name
, host
->io_port
);
444 if (host
->irq
!= NO_IRQ
)
445 p
+= sprintf(p
, "irq %d ", host
->irq
);
447 p
+= sprintf(p
, "NO IRQ ");
449 if (host
->dma_channel
!= NO_DMA
)
450 p
+= sprintf(p
, "dma %d ", host
->dma_channel
);
452 p
+= sprintf(p
, "NO DMA ");
454 p
+= sprintf(p
, "v%d.%d.%d scsi %s",
455 VER_MAJOR
, VER_MINOR
, VER_PATCH
,
456 info
->info
.scsi
.type
);
458 p
+= sprintf(p
, " terminators %s",
459 info
->terms
? "on" : "off");
464 /* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
465 * Purpose : Set a driver specific function
466 * Params : host - host to setup
467 * : buffer - buffer containing string describing operation
468 * : length - length of string
469 * Returns : -EINVAL, or 0
472 cumanascsi_2_set_proc_info(struct Scsi_Host
*host
, char *buffer
, int length
)
476 if (length
>= 11 && strcmp(buffer
, "CUMANASCSI2") == 0) {
480 if (length
>= 5 && strncmp(buffer
, "term=", 5) == 0) {
481 if (buffer
[5] == '1')
482 cumanascsi_2_terminator_ctl(host
, 1);
483 else if (buffer
[5] == '0')
484 cumanascsi_2_terminator_ctl(host
, 0);
495 /* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset,
496 * int length, int host_no, int inout)
497 * Purpose : Return information about the driver to a user process accessing
498 * the /proc filesystem.
499 * Params : buffer - a buffer to write information to
500 * start - a pointer into this buffer set by this routine to the start
501 * of the required information.
502 * offset - offset into information that we have read upto.
503 * length - length of buffer
504 * host_no - host number to return information for
505 * inout - 0 for reading, 1 for writing.
506 * Returns : length of data written to buffer.
508 int cumanascsi_2_proc_info (char *buffer
, char **start
, off_t offset
,
509 int length
, int host_no
, int inout
)
512 struct Scsi_Host
*host
= scsi_hostlist
;
513 CumanaScsi2_Info
*info
;
517 if (host
->host_no
== host_no
)
525 return cumanascsi_2_set_proc_info(host
, buffer
, length
);
527 info
= (CumanaScsi2_Info
*)host
->hostdata
;
530 pos
= sprintf(buffer
,
531 "Cumana SCSI II driver version %d.%d.%d\n",
532 VER_MAJOR
, VER_MINOR
, VER_PATCH
);
533 pos
+= sprintf(buffer
+ pos
,
534 "Address: %08lX IRQ : %d DMA : %d\n"
535 "FAS : %-10s TERM: %-3s\n\n"
537 host
->io_port
, host
->irq
, host
->dma_channel
,
538 info
->info
.scsi
.type
, info
->terms
? "on" : "off");
540 pos
+= sprintf(buffer
+pos
,
541 "Queued commands: %-10u Issued commands: %-10u\n"
542 "Done commands : %-10u Reads : %-10u\n"
543 "Writes : %-10u Others : %-10u\n"
544 "Disconnects : %-10u Aborts : %-10u\n"
546 info
->info
.stats
.queues
, info
->info
.stats
.removes
,
547 info
->info
.stats
.fins
, info
->info
.stats
.reads
,
548 info
->info
.stats
.writes
, info
->info
.stats
.miscs
,
549 info
->info
.stats
.disconnects
, info
->info
.stats
.aborts
,
550 info
->info
.stats
.resets
);
552 pos
+= sprintf(buffer
+pos
, "\nAttached devices:%s\n", host
->host_queue
? "" : " none");
554 for (scd
= host
->host_queue
; scd
; scd
= scd
->next
) {
557 proc_print_scsidevice(scd
, buffer
, &len
, pos
);
559 pos
+= sprintf(buffer
+pos
, "Extensions: ");
560 if (scd
->tagged_supported
)
561 pos
+= sprintf(buffer
+pos
, "TAG %sabled [%d] ",
562 scd
->tagged_queue
? "en" : "dis",
564 pos
+= sprintf(buffer
+pos
, "\n");
566 if (pos
+ begin
< offset
) {
570 if (pos
+ begin
> offset
+ length
)
574 *start
= buffer
+ (offset
- begin
);
575 pos
-= offset
- begin
;
583 Scsi_Host_Template driver_template
= CUMANASCSI_2
;
585 #include "../../scsi/scsi_module.c"