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