2 * linux/arch/arm/drivers/scsi/cumana_2.c
4 * Copyright (C) 1997-2000 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.
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>
28 #include <asm/ecard.h>
31 #include <asm/pgtable.h>
33 #include "../../scsi/sd.h"
34 #include "../../scsi/hosts.h"
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
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
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
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
,
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
127 cumanascsi_2_terminator_ctl(struct Scsi_Host
*host
, int on_off
)
129 CumanaScsi2_Info
*info
= (CumanaScsi2_Info
*)host
->hostdata
;
133 outb (ALATCH_ENA_TERM
, info
->alatch
);
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
147 cumanascsi_2_intr(int irq
, void *dev_id
, struct pt_regs
*regs
)
149 struct Scsi_Host
*host
= (struct Scsi_Host
*)dev_id
;
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
);
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
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
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)) {
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
,
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
);
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
);
206 outb(ALATCH_DMA_IN
, info
->alatch
);
207 set_dma_mode(dmach
, DMA_MODE_READ
);
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
223 * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer)
224 * Purpose : handles pseudo DMA
225 * Params : host - host
227 * direction - DMA on to/off of card
228 * transfer - minimum number of bytes we expect to transfer
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
;
238 length
= SCp
->this_residual
;
241 if (direction
== DMA_OUT
)
245 unsigned int status
= inb(info
->status
);
247 if (status
& STATUS_INT
)
250 if (!(status
& STATUS_DRQ
))
253 word
= *addr
| *(addr
+ 1) << 8;
254 outw (info
->dmaarea
);
259 printk ("PSEUDO_OUT???\n");
262 if (transfer
&& (transfer
& 255)) {
263 while (length
>= 256) {
264 unsigned int status
= inb(info
->status
);
266 if (status
& STATUS_INT
)
269 if (!(status
& STATUS_DRQ
))
272 insw(info
->dmaarea
, addr
, 256 >> 1);
280 unsigned int status
= inb(info
->status
);
282 if (status
& STATUS_INT
)
285 if (!(status
& STATUS_DRQ
))
288 word
= inw (info
->dmaarea
);
300 /* Prototype: int cumanascsi_2_dma_stop(host, SCpnt)
301 * Purpose : stops DMA/PIO
302 * Params : host - host
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} };
326 struct Scsi_Host
*host
;
328 tpnt
->proc_name
= "cumanascs2";
329 memset(ecs
, 0, sizeof (ecs
));
334 CumanaScsi2_Info
*info
;
336 ecs
[count
] = ecard_find(0, cumanascsi_2_cids
);
340 ecard_claim(ecs
[count
]);
342 host
= scsi_register(tpnt
, sizeof (CumanaScsi2_Info
));
344 ecard_release(ecs
[count
]);
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
);
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
;
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
)
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
]);
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
;
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");
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
454 cumanascsi_2_set_proc_info(struct Scsi_Host
*host
, char *buffer
, int length
)
458 if (length
>= 11 && strcmp(buffer
, "CUMANASCSI2") == 0) {
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);
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
)
494 struct Scsi_Host
*host
= scsi_hostlist
;
495 CumanaScsi2_Info
*info
;
499 if (host
->host_no
== host_no
)
507 return cumanascsi_2_set_proc_info(host
, buffer
, length
);
509 info
= (CumanaScsi2_Info
*)host
->hostdata
;
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
) {
527 proc_print_scsidevice(scd
, buffer
, &len
, pos
);
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",
534 pos
+= sprintf(buffer
+pos
, "\n");
536 if (pos
+ begin
< offset
) {
540 if (pos
+ begin
> offset
+ length
)
544 *start
= buffer
+ (offset
- begin
);
545 pos
-= offset
- begin
;
553 Scsi_Host_Template driver_template
= CUMANASCSI_2
;
555 #include "../../scsi/scsi_module.c"