2 * linux/drivers/acorn/scsi/powertec.c
4 * Copyright (C) 1997-2002 Russell King
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.
10 #include <linux/module.h>
11 #include <linux/blk.h>
12 #include <linux/kernel.h>
13 #include <linux/string.h>
14 #include <linux/ioport.h>
15 #include <linux/sched.h>
16 #include <linux/proc_fs.h>
17 #include <linux/unistd.h>
18 #include <linux/stat.h>
19 #include <linux/delay.h>
20 #include <linux/pci.h>
21 #include <linux/init.h>
24 #include <asm/ecard.h>
27 #include <asm/pgtable.h>
29 #include "../../scsi/scsi.h"
30 #include "../../scsi/hosts.h"
34 #include <scsi/scsicam.h>
36 #define POWERTEC_FAS216_OFFSET 0xc00
37 #define POWERTEC_FAS216_SHIFT 4
38 #define POWERTEC_FAS216_SIZE (16 << POWERTEC_FAS216_SHIFT)
40 #define POWERTEC_INTR_STATUS 0x800
41 #define POWERTEC_INTR_BIT 0x80
43 #define POWERTEC_RESET_CONTROL 0x406
44 #define POWERTEC_RESET_BIT 1
46 #define POWERTEC_TERM_CONTROL 0x806
47 #define POWERTEC_TERM_ENABLE 1
49 #define POWERTEC_INTR_CONTROL 0x407
50 #define POWERTEC_INTR_ENABLE 1
51 #define POWERTEC_INTR_DISABLE 0
53 #define VERSION "1.00 (13/11/2002 2.5.47)"
56 * Use term=0,1,0,0,0 to turn terminators on/off.
59 static int term
[MAX_ECARDS
] = { 1, 1, 1, 1, 1, 1, 1, 1 };
63 struct powertec_info
{
65 unsigned int term_port
;
66 unsigned int term_ctl
;
67 struct scatterlist sg
[NR_SG
];
70 /* Prototype: void powertecscsi_irqenable(ec, irqnr)
71 * Purpose : Enable interrupts on Powertec SCSI card
72 * Params : ec - expansion card structure
73 * : irqnr - interrupt number
76 powertecscsi_irqenable(struct expansion_card
*ec
, int irqnr
)
78 unsigned int port
= (unsigned int)ec
->irq_data
;
79 outb(POWERTEC_INTR_ENABLE
, port
);
82 /* Prototype: void powertecscsi_irqdisable(ec, irqnr)
83 * Purpose : Disable interrupts on Powertec SCSI card
84 * Params : ec - expansion card structure
85 * : irqnr - interrupt number
88 powertecscsi_irqdisable(struct expansion_card
*ec
, int irqnr
)
90 unsigned int port
= (unsigned int)ec
->irq_data
;
91 outb(POWERTEC_INTR_DISABLE
, port
);
94 static const expansioncard_ops_t powertecscsi_ops
= {
95 .irqenable
= powertecscsi_irqenable
,
96 .irqdisable
= powertecscsi_irqdisable
,
99 /* Prototype: void powertecscsi_terminator_ctl(host, on_off)
100 * Purpose : Turn the Powertec SCSI terminators on or off
101 * Params : host - card to turn on/off
102 * : on_off - !0 to turn on, 0 to turn off
105 powertecscsi_terminator_ctl(struct Scsi_Host
*host
, int on_off
)
107 struct powertec_info
*info
= (struct powertec_info
*)host
->hostdata
;
109 info
->term_ctl
= on_off
? POWERTEC_TERM_ENABLE
: 0;
110 outb(info
->term_ctl
, info
->term_port
);
113 /* Prototype: void powertecscsi_intr(irq, *dev_id, *regs)
114 * Purpose : handle interrupts from Powertec SCSI card
115 * Params : irq - interrupt number
116 * dev_id - user-defined (Scsi_Host structure)
117 * regs - processor registers at interrupt
120 powertecscsi_intr(int irq
, void *dev_id
, struct pt_regs
*regs
)
122 struct Scsi_Host
*host
= (struct Scsi_Host
*)dev_id
;
127 /* Prototype: fasdmatype_t powertecscsi_dma_setup(host, SCpnt, direction, min_type)
128 * Purpose : initialises DMA/PIO
129 * Params : host - host
131 * direction - DMA on to/off of card
132 * min_type - minimum DMA support that we must have for this transfer
133 * Returns : type of transfer to be performed
136 powertecscsi_dma_setup(struct Scsi_Host
*host
, Scsi_Pointer
*SCp
,
137 fasdmadir_t direction
, fasdmatype_t min_type
)
139 struct powertec_info
*info
= (struct powertec_info
*)host
->hostdata
;
140 int dmach
= host
->dma_channel
;
142 if (dmach
!= NO_DMA
&&
143 (min_type
== fasdma_real_all
|| SCp
->this_residual
>= 512)) {
144 int bufs
, pci_dir
, dma_dir
;
146 bufs
= copy_SCp_to_sg(&info
->sg
[0], SCp
, NR_SG
);
148 if (direction
== DMA_OUT
)
149 pci_dir
= PCI_DMA_TODEVICE
,
150 dma_dir
= DMA_MODE_WRITE
;
152 pci_dir
= PCI_DMA_FROMDEVICE
,
153 dma_dir
= DMA_MODE_READ
;
155 pci_map_sg(NULL
, info
->sg
, bufs
+ 1, pci_dir
);
158 set_dma_sg(dmach
, info
->sg
, bufs
+ 1);
159 set_dma_mode(dmach
, dma_dir
);
161 return fasdma_real_all
;
165 * If we're not doing DMA,
171 /* Prototype: int powertecscsi_dma_stop(host, SCpnt)
172 * Purpose : stops DMA/PIO
173 * Params : host - host
177 powertecscsi_dma_stop(struct Scsi_Host
*host
, Scsi_Pointer
*SCp
)
179 if (host
->dma_channel
!= NO_DMA
)
180 disable_dma(host
->dma_channel
);
183 /* Prototype: const char *powertecscsi_info(struct Scsi_Host * host)
184 * Purpose : returns a descriptive string about this interface,
185 * Params : host - driver host structure to return info for.
186 * Returns : pointer to a static buffer containing null terminated string.
188 const char *powertecscsi_info(struct Scsi_Host
*host
)
190 struct powertec_info
*info
= (struct powertec_info
*)host
->hostdata
;
191 static char string
[100], *p
;
194 p
+= sprintf(p
, "%s ", host
->hostt
->name
);
195 p
+= fas216_info(&info
->info
, p
);
196 p
+= sprintf(p
, "v%s terminators o%s",
197 VERSION
, info
->term_ctl
? "n" : "ff");
202 /* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
203 * Purpose : Set a driver specific function
204 * Params : host - host to setup
205 * : buffer - buffer containing string describing operation
206 * : length - length of string
207 * Returns : -EINVAL, or 0
210 powertecscsi_set_proc_info(struct Scsi_Host
*host
, char *buffer
, int length
)
214 if (length
>= 12 && strncmp(buffer
, "POWERTECSCSI", 12) == 0) {
218 if (length
>= 5 && strncmp(buffer
, "term=", 5) == 0) {
219 if (buffer
[5] == '1')
220 powertecscsi_terminator_ctl(host
, 1);
221 else if (buffer
[5] == '0')
222 powertecscsi_terminator_ctl(host
, 0);
233 /* Prototype: int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
234 * int length, int host_no, int inout)
235 * Purpose : Return information about the driver to a user process accessing
236 * the /proc filesystem.
237 * Params : buffer - a buffer to write information to
238 * start - a pointer into this buffer set by this routine to the start
239 * of the required information.
240 * offset - offset into information that we have read upto.
241 * length - length of buffer
242 * host_no - host number to return information for
243 * inout - 0 for reading, 1 for writing.
244 * Returns : length of data written to buffer.
246 int powertecscsi_proc_info(char *buffer
, char **start
, off_t offset
,
247 int length
, int host_no
, int inout
)
250 struct Scsi_Host
*host
;
251 struct powertec_info
*info
;
254 host
= scsi_host_hn_get(host_no
);
259 return powertecscsi_set_proc_info(host
, buffer
, length
);
261 info
= (struct powertec_info
*)host
->hostdata
;
264 pos
= sprintf(buffer
, "PowerTec SCSI driver v%s\n", VERSION
);
266 pos
+= fas216_print_host(&info
->info
, buffer
+ pos
);
267 pos
+= sprintf(buffer
+ pos
, "Term : o%s\n",
268 info
->term_ctl
? "n" : "ff");
270 pos
+= fas216_print_stats(&info
->info
, buffer
+ pos
);
272 pos
+= sprintf(buffer
+pos
, "\nAttached devices:\n");
274 for (scd
= host
->host_queue
; scd
; scd
= scd
->next
) {
275 pos
+= fas216_print_device(&info
->info
, scd
, buffer
+ pos
);
277 if (pos
+ begin
< offset
) {
281 if (pos
+ begin
> offset
+ length
)
285 *start
= buffer
+ (offset
- begin
);
286 pos
-= offset
- begin
;
293 static Scsi_Host_Template powertecscsi_template
= {
294 .module
= THIS_MODULE
,
295 .proc_info
= powertecscsi_proc_info
,
296 .name
= "PowerTec SCSI",
297 .info
= powertecscsi_info
,
298 .command
= fas216_command
,
299 .queuecommand
= fas216_queue_command
,
300 .eh_host_reset_handler
= fas216_eh_host_reset
,
301 .eh_bus_reset_handler
= fas216_eh_bus_reset
,
302 .eh_device_reset_handler
= fas216_eh_device_reset
,
303 .eh_abort_handler
= fas216_eh_abort
,
307 .sg_tablesize
= SG_ALL
,
309 .use_clustering
= ENABLE_CLUSTERING
,
310 .proc_name
= "powertec",
314 powertecscsi_probe(struct expansion_card
*ec
, const struct ecard_id
*id
)
316 struct Scsi_Host
*host
;
317 struct powertec_info
*info
;
320 host
= scsi_register(&powertecscsi_template
,
321 sizeof (struct powertec_info
));
325 host
->io_port
= ecard_address(ec
, ECARD_IOC
, ECARD_FAST
);
327 host
->dma_channel
= ec
->dma
;
329 if (!request_region(host
->io_port
+ POWERTEC_FAS216_OFFSET
,
330 POWERTEC_FAS216_SIZE
, "powertec2-fas")) {
335 ec
->irqaddr
= (unsigned char *)
336 ioaddr(host
->io_port
+ POWERTEC_INTR_STATUS
);
337 ec
->irqmask
= POWERTEC_INTR_BIT
;
338 ec
->irq_data
= (void *)(host
->io_port
+ POWERTEC_INTR_CONTROL
);
339 ec
->ops
= (expansioncard_ops_t
*)&powertecscsi_ops
;
341 ecard_set_drvdata(ec
, host
);
343 info
= (struct powertec_info
*)host
->hostdata
;
344 info
->term_port
= host
->io_port
+ POWERTEC_TERM_CONTROL
;
345 powertecscsi_terminator_ctl(host
, term
[ec
->slot_no
]);
347 info
->info
.scsi
.io_port
= host
->io_port
+ POWERTEC_FAS216_OFFSET
;
348 info
->info
.scsi
.io_shift
= POWERTEC_FAS216_SHIFT
;
349 info
->info
.scsi
.irq
= host
->irq
;
350 info
->info
.ifcfg
.clockrate
= 40; /* MHz */
351 info
->info
.ifcfg
.select_timeout
= 255;
352 info
->info
.ifcfg
.asyncperiod
= 200; /* ns */
353 info
->info
.ifcfg
.sync_max_depth
= 7;
354 info
->info
.ifcfg
.cntl3
= CNTL3_BS8
| CNTL3_FASTSCSI
| CNTL3_FASTCLK
;
355 info
->info
.ifcfg
.disconnect_ok
= 1;
356 info
->info
.ifcfg
.wide_max_size
= 0;
357 info
->info
.dma
.setup
= powertecscsi_dma_setup
;
358 info
->info
.dma
.pseudo
= NULL
;
359 info
->info
.dma
.stop
= powertecscsi_dma_stop
;
361 ret
= request_irq(host
->irq
, powertecscsi_intr
,
362 SA_INTERRUPT
, "powertec", host
);
364 printk("scsi%d: IRQ%d not free: %d\n",
365 host
->host_no
, host
->irq
, ret
);
369 if (host
->dma_channel
!= NO_DMA
) {
370 if (request_dma(host
->dma_channel
, "powertec")) {
371 printk("scsi%d: DMA%d not free, using PIO\n",
372 host
->host_no
, host
->dma_channel
);
373 host
->dma_channel
= NO_DMA
;
375 set_dma_speed(host
->dma_channel
, 180);
381 ret
= scsi_add_host(host
);
385 fas216_release(host
);
387 if (host
->dma_channel
!= NO_DMA
)
388 free_dma(host
->dma_channel
);
389 free_irq(host
->irq
, host
);
391 release_region(host
->io_port
+ POWERTEC_FAS216_OFFSET
,
392 POWERTEC_FAS216_SIZE
);
394 scsi_unregister(host
);
400 static void __devexit
powertecscsi_remove(struct expansion_card
*ec
)
402 struct Scsi_Host
*host
= ecard_get_drvdata(ec
);
404 ecard_set_drvdata(ec
, NULL
);
405 scsi_remove_host(host
);
406 fas216_release(host
);
408 if (host
->dma_channel
!= NO_DMA
)
409 free_dma(host
->dma_channel
);
410 free_irq(host
->irq
, host
);
411 release_region(host
->io_port
+ POWERTEC_FAS216_OFFSET
,
412 POWERTEC_FAS216_SIZE
);
413 scsi_unregister(host
);
416 static const struct ecard_id powertecscsi_cids
[] = {
417 { MANU_ALSYSTEMS
, PROD_ALSYS_SCSIATAPI
},
421 static struct ecard_driver powertecscsi_driver
= {
422 .probe
= powertecscsi_probe
,
423 .remove
= __devexit_p(powertecscsi_remove
),
424 .id_table
= powertecscsi_cids
,
426 .name
= "powertecscsi",
430 static int __init
powertecscsi_init(void)
432 return ecard_register_driver(&powertecscsi_driver
);
435 static void __exit
powertecscsi_exit(void)
437 ecard_remove_driver(&powertecscsi_driver
);
440 module_init(powertecscsi_init
);
441 module_exit(powertecscsi_exit
);
443 MODULE_AUTHOR("Russell King");
444 MODULE_DESCRIPTION("Powertec SCSI driver");
445 MODULE_PARM(term
, "1-8i");
446 MODULE_PARM_DESC(term
, "SCSI bus termination");
447 MODULE_LICENSE("GPL");