added mtd driver
[linux-2.4.git] / drivers / acorn / scsi / eesox.c
1 /*
2  *  linux/drivers/acorn/scsi/eesox.c
3  *
4  *  Copyright (C) 1997-2003 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/delay.h>
33 #include <linux/pci.h>
34 #include <linux/init.h>
35
36 #include <asm/io.h>
37 #include <asm/irq.h>
38 #include <asm/dma.h>
39 #include <asm/ecard.h>
40 #include <asm/pgtable.h>
41
42 #include "../../scsi/sd.h"
43 #include "../../scsi/hosts.h"
44 #include "fas216.h"
45 #include "scsi.h"
46
47 #include <scsi/scsicam.h>
48
49 /*
50  * List of devices that the driver will recognise
51  */
52 #define EESOXSCSI_LIST  { MANU_EESOX, PROD_EESOX_SCSI2 }
53
54 #define EESOX_FAS216_OFFSET     0xc00
55 #define EESOX_FAS216_SHIFT      3
56
57 #define EESOX_DMASTAT           0xa00
58 #define EESOX_STAT_INTR         0x01
59 #define EESOX_STAT_DMA          0x02
60
61 #define EESOX_CONTROL           0xa00
62 #define EESOX_INTR_ENABLE       0x04
63 #define EESOX_TERM_ENABLE       0x02
64 #define EESOX_RESET             0x01
65
66 #define EESOX_DMADATA           0xe00
67
68 /*
69  * Version
70  */
71 #define VERSION "1.10 (22/01/2003 2.4.19-rmk5)"
72
73 /*
74  * Use term=0,1,0,0,0 to turn terminators on/off
75  */
76 static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
77
78 #define NR_SG   256
79
80 struct eesoxscsi_info {
81         FAS216_Info             info;
82         struct expansion_card   *ec;
83
84         unsigned int            ctl_port;
85         unsigned int            control;
86         struct scatterlist      sg[NR_SG];      /* Scatter DMA list     */
87 };
88
89 /* Prototype: void eesoxscsi_irqenable(ec, irqnr)
90  * Purpose  : Enable interrupts on EESOX SCSI card
91  * Params   : ec    - expansion card structure
92  *          : irqnr - interrupt number
93  */
94 static void
95 eesoxscsi_irqenable(struct expansion_card *ec, int irqnr)
96 {
97         struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data;
98
99         info->control |= EESOX_INTR_ENABLE;
100
101         outb(info->control, info->ctl_port);
102 }
103
104 /* Prototype: void eesoxscsi_irqdisable(ec, irqnr)
105  * Purpose  : Disable interrupts on EESOX SCSI card
106  * Params   : ec    - expansion card structure
107  *          : irqnr - interrupt number
108  */
109 static void
110 eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr)
111 {
112         struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data;
113
114         info->control &= ~EESOX_INTR_ENABLE;
115
116         outb(info->control, info->ctl_port);
117 }
118
119 static const expansioncard_ops_t eesoxscsi_ops = {
120         .irqenable      = eesoxscsi_irqenable,
121         .irqdisable     = eesoxscsi_irqdisable,
122 };
123
124 /* Prototype: void eesoxscsi_terminator_ctl(*host, on_off)
125  * Purpose  : Turn the EESOX SCSI terminators on or off
126  * Params   : host   - card to turn on/off
127  *          : on_off - !0 to turn on, 0 to turn off
128  */
129 static void
130 eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
131 {
132         struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
133         unsigned long flags;
134
135         save_flags_cli(flags);
136         if (on_off)
137                 info->control |= EESOX_TERM_ENABLE;
138         else
139                 info->control &= ~EESOX_TERM_ENABLE;
140
141         outb(info->control, info->ctl_port);
142         restore_flags(flags);
143 }
144
145 /* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs)
146  * Purpose  : handle interrupts from EESOX SCSI card
147  * Params   : irq    - interrupt number
148  *            dev_id - user-defined (Scsi_Host structure)
149  *            regs   - processor registers at interrupt
150  */
151 static void
152 eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
153 {
154         fas216_intr(dev_id);
155 }
156
157 /* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type)
158  * Purpose  : initialises DMA/PIO
159  * Params   : host      - host
160  *            SCpnt     - command
161  *            direction - DMA on to/off of card
162  *            min_type  - minimum DMA support that we must have for this transfer
163  * Returns  : type of transfer to be performed
164  */
165 static fasdmatype_t
166 eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
167                        fasdmadir_t direction, fasdmatype_t min_type)
168 {
169         struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
170         int dmach = host->dma_channel;
171
172         if (dmach != NO_DMA &&
173             (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
174                 int bufs, map_dir, dma_dir;
175
176                 bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
177
178                 if (direction == DMA_OUT)
179                         map_dir = PCI_DMA_TODEVICE,
180                         dma_dir = DMA_MODE_WRITE;
181                 else
182                         map_dir = PCI_DMA_FROMDEVICE,
183                         dma_dir = DMA_MODE_READ;
184
185                 pci_map_sg(NULL, info->sg, bufs, map_dir);
186
187                 disable_dma(dmach);
188                 set_dma_sg(dmach, info->sg, bufs);
189                 set_dma_mode(dmach, dma_dir);
190                 enable_dma(dmach);
191                 return fasdma_real_all;
192         }
193         /*
194          * We don't do DMA, we only do slow PIO
195          *
196          * Some day, we will do Pseudo DMA
197          */
198         return fasdma_pseudo;
199 }
200
201 static void eesoxscsi_buffer_in(void *buf, int length, unsigned long base)
202 {
203         const unsigned long reg_fas = base + EESOX_FAS216_OFFSET;
204         const unsigned long reg_dmastat = base + EESOX_DMASTAT;
205         const unsigned long reg_dmadata = base + EESOX_DMADATA;
206
207         do {
208                 unsigned int status;
209
210                 /*
211                  * Interrupt request?
212                  */
213                 status = inb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT));
214                 if (status & STAT_INT)
215                         break;
216
217                 /*
218                  * DMA request active?
219                  */
220                 status = inb(reg_dmastat);
221                 if (!(status & EESOX_STAT_DMA))
222                         continue;
223
224                 /*
225                  * Get number of bytes in FIFO
226                  */
227                 status = inb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF;
228                 if (status > 16)
229                         status = 16;
230                 if (status > length)
231                         status = length;
232
233                 /*
234                  * Align buffer.
235                  */
236                 if (((u32)buf) & 2 && status >= 2) {
237                         *((u16 *)buf)++ = inw(reg_dmadata);
238                         status -= 2;
239                         length -= 2;
240                 }
241
242                 if (status >= 8) {
243                         unsigned long l1, l2;
244
245                         l1 = inw(reg_dmadata);
246                         l1 |= inw(reg_dmadata) << 16;
247                         l2 = inw(reg_dmadata);
248                         l2 |= inw(reg_dmadata) << 16;
249                         *((u32 *)buf)++ = l1;
250                         *((u32 *)buf)++ = l2;
251                         length -= 8;
252                         continue;
253                 }
254
255                 if (status >= 4) {
256                         unsigned long l1;
257
258                         l1 = inw(reg_dmadata);
259                         l1 |= inw(reg_dmadata) << 16;
260                         *((u32 *)buf)++ = l1;
261                         length -= 4;
262                         continue;
263                 }
264
265                 if (status >= 2) {
266                         *((u16 *)buf)++ = inw(reg_dmadata);
267                         length -= 2;
268                 }
269         } while (length);
270 }
271
272 static void eesoxscsi_buffer_out(void *buf, int length, unsigned long base)
273 {
274         const unsigned long reg_fas = base + EESOX_FAS216_OFFSET;
275         const unsigned long reg_dmastat = base + EESOX_DMASTAT;
276         const unsigned long reg_dmadata = base + EESOX_DMADATA;
277
278         do {
279                 unsigned int status;
280
281                 /*
282                  * Interrupt request?
283                  */
284                 status = inb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT));
285                 if (status & STAT_INT)
286                         break;
287
288                 /*
289                  * DMA request active?
290                  */
291                 status = inb(reg_dmastat);
292                 if (!(status & EESOX_STAT_DMA))
293                         continue;
294
295                 /*
296                  * Get number of bytes in FIFO
297                  */
298                 status = inb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF;
299                 if (status > 16)
300                         status = 16;
301                 status = 16 - status;
302                 if (status > length)
303                         status = length;
304
305                 /*
306                  * Align buffer.
307                  */
308                 if (((u32)buf) & 2 && status >= 2) {
309                         outw(*((u16 *)buf)++, reg_dmadata);
310                         status -= 2;
311                         length -= 2;
312                 }
313
314                 if (status >= 8) {
315                         unsigned long l1, l2;
316
317                         l1 = *((u32 *)buf)++;
318                         l2 = *((u32 *)buf)++;
319
320                         outw(l1, reg_dmadata);
321                         outw(l1 >> 16, reg_dmadata);
322                         outw(l2, reg_dmadata);
323                         outw(l2 >> 16, reg_dmadata);
324                         status -= 8;
325                         length -= 8;
326                         continue;
327                 }
328
329                 if (status >= 4) {
330                         unsigned long l1;
331
332                         l1 = *((u32 *)buf)++;
333                         outw(l1, reg_dmadata);
334                         outw(l1 >> 16, reg_dmadata);
335                         status -= 4;
336                         length -= 4;
337                         continue;
338                 }
339
340                 if (status >= 2) {
341                         outw(*((u16 *)buf)++, reg_dmadata);
342                         length -= 2;
343                 }
344         } while (length);
345 }
346
347 static void
348 eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
349                      fasdmadir_t dir, int transfer_size)
350 {
351         unsigned int base = host->io_port;
352         if (dir == DMA_IN) {
353                 eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, base);
354         } else {
355                 eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, base);
356         }
357 }
358
359 /* Prototype: int eesoxscsi_dma_stop(host, SCpnt)
360  * Purpose  : stops DMA/PIO
361  * Params   : host  - host
362  *            SCpnt - command
363  */
364 static void
365 eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
366 {
367         if (host->dma_channel != NO_DMA)
368                 disable_dma(host->dma_channel);
369 }
370
371 /* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host)
372  * Purpose  : returns a descriptive string about this interface,
373  * Params   : host - driver host structure to return info for.
374  * Returns  : pointer to a static buffer containing null terminated string.
375  */
376 const char *eesoxscsi_info(struct Scsi_Host *host)
377 {
378         struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
379         static char string[150];
380
381         sprintf(string, "%s (%s) in slot %d v%s terminators o%s",
382                 host->hostt->name, info->info.scsi.type, info->ec->slot_no,
383                 VERSION, info->control & EESOX_TERM_ENABLE ? "n" : "ff");
384
385         return string;
386 }
387
388 /* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
389  * Purpose  : Set a driver specific function
390  * Params   : host   - host to setup
391  *          : buffer - buffer containing string describing operation
392  *          : length - length of string
393  * Returns  : -EINVAL, or 0
394  */
395 static int
396 eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
397 {
398         int ret = length;
399
400         if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) {
401                 buffer += 9;
402                 length -= 9;
403
404                 if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
405                         if (buffer[5] == '1')
406                                 eesoxscsi_terminator_ctl(host, 1);
407                         else if (buffer[5] == '0')
408                                 eesoxscsi_terminator_ctl(host, 0);
409                         else
410                                 ret = -EINVAL;
411                 } else
412                         ret = -EINVAL;
413         } else
414                 ret = -EINVAL;
415
416         return ret;
417 }
418
419 /* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
420  *                                    int length, int host_no, int inout)
421  * Purpose  : Return information about the driver to a user process accessing
422  *            the /proc filesystem.
423  * Params   : buffer - a buffer to write information to
424  *            start  - a pointer into this buffer set by this routine to the start
425  *                     of the required information.
426  *            offset - offset into information that we have read upto.
427  *            length - length of buffer
428  *            host_no - host number to return information for
429  *            inout  - 0 for reading, 1 for writing.
430  * Returns  : length of data written to buffer.
431  */
432 int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
433                             int length, int host_no, int inout)
434 {
435         int pos, begin;
436         struct Scsi_Host *host;
437         struct eesoxscsi_info *info;
438         Scsi_Device *scd;
439
440         host = scsi_host_hn_get(host_no);
441         if (!host)
442                 return 0;
443
444         if (inout == 1)
445                 return eesoxscsi_set_proc_info(host, buffer, length);
446
447         info = (struct eesoxscsi_info *)host->hostdata;
448
449         begin = 0;
450         pos = sprintf(buffer, "EESOX SCSI driver v%s\n", VERSION);
451         pos += fas216_print_host(&info->info, buffer + pos);
452         pos += sprintf(buffer + pos, "Term    : o%s\n",
453                         info->control & EESOX_TERM_ENABLE ? "n" : "ff");
454
455         pos += fas216_print_stats(&info->info, buffer + pos);
456
457         pos += sprintf(buffer+pos, "\nAttached devices:\n");
458
459         for (scd = host->host_queue; scd; scd = scd->next) {
460                 int len;
461
462                 proc_print_scsidevice(scd, buffer, &len, pos);
463                 pos += len;
464                 pos += sprintf(buffer+pos, "Extensions: ");
465                 if (scd->tagged_supported)
466                         pos += sprintf(buffer+pos, "TAG %sabled [%d] ",
467                                         scd->tagged_queue ? "en" : "dis",
468                                         scd->current_tag);
469                 pos += sprintf (buffer+pos, "\n");
470
471                 if (pos + begin < offset) {
472                         begin += pos;
473                         pos = 0;
474                 }
475         }
476         *start = buffer + (offset - begin);
477         pos -= offset - begin;
478         if (pos > length)
479                 pos = length;
480
481         return pos;
482 }
483
484 static int eesoxscsi_probe(struct expansion_card *ec);
485
486 /* Prototype: int eesoxscsi_detect(Scsi_Host_Template * tpnt)
487  * Purpose  : initialises EESOX SCSI driver
488  * Params   : tpnt - template for this SCSI adapter
489  * Returns  : >0 if host found, 0 otherwise.
490  */
491 static int eesoxscsi_detect(Scsi_Host_Template *tpnt)
492 {
493         static const card_ids eesoxscsi_cids[] =
494                         { EESOXSCSI_LIST, { 0xffff, 0xffff} };
495         struct expansion_card *ec;
496         int count = 0, ret;
497   
498         ecard_startfind();
499
500         while(1) {
501                 ec = ecard_find(0, eesoxscsi_cids);
502                 if (!ec)
503                         break;
504
505                 ecard_claim(ec);
506
507                 ret = eesoxscsi_probe(ec);
508                 if (ret) {
509                         ecard_release(ec);
510                         break;
511                 }
512                 ++count;
513         }
514         return count;
515 }
516
517 static void eesoxscsi_remove(struct Scsi_Host *host);
518
519 /* Prototype: int eesoxscsi_release(struct Scsi_Host * host)
520  * Purpose  : releases all resources used by this adapter
521  * Params   : host - driver host structure to return info for.
522  */
523 static int eesoxscsi_release(struct Scsi_Host *host)
524 {
525         eesoxscsi_remove(host);
526         return 0;
527 }
528
529 static Scsi_Host_Template eesox_template = {
530         .module                         = THIS_MODULE,
531         .proc_info                      = eesoxscsi_proc_info,
532         .name                           = "EESOX SCSI",
533         .detect                         = eesoxscsi_detect,
534         .release                        = eesoxscsi_release,
535         .info                           = eesoxscsi_info,
536         .bios_param                     = scsicam_bios_param,
537         .command                        = fas216_command,
538         .queuecommand                   = fas216_queue_command,
539         .eh_host_reset_handler          = fas216_eh_host_reset,
540         .eh_bus_reset_handler           = fas216_eh_bus_reset,
541         .eh_device_reset_handler        = fas216_eh_device_reset,
542         .eh_abort_handler               = fas216_eh_abort,
543         .use_new_eh_code                = 1,
544
545         .can_queue                      = 1,
546         .this_id                        = 7,
547         .sg_tablesize                   = SG_ALL,
548         .cmd_per_lun                    = 1,
549         .use_clustering                 = DISABLE_CLUSTERING,
550         .proc_name                      = "eesox",
551 };
552
553 static int
554 eesoxscsi_probe(struct expansion_card *ec)
555 {
556         struct Scsi_Host *host;
557         struct eesoxscsi_info *info;
558         unsigned long base;
559         int ret;
560
561         base = ecard_address(ec, ECARD_IOC, ECARD_FAST);
562
563         if (!request_region(base + EESOX_FAS216_OFFSET,
564                             16 << EESOX_FAS216_SHIFT, "eesox2-fas")) {
565                 ret = -EBUSY;
566                 goto out;
567         }
568
569         host = scsi_register(&eesox_template,
570                              sizeof(struct eesoxscsi_info));
571         if (!host) {
572                 ret = -ENOMEM;
573                 goto out_region;
574         }
575
576         host->io_port     = base;
577         host->irq         = ec->irq;
578         host->dma_channel = ec->dma;
579
580         info = (struct eesoxscsi_info *)host->hostdata;
581         info->ec        = ec;
582         info->ctl_port  = base + EESOX_CONTROL;
583         info->control   = term[ec->slot_no] ? EESOX_TERM_ENABLE : 0;
584         outb(info->control, info->ctl_port);
585
586         ec->irqaddr     = (unsigned char *)ioaddr(base + EESOX_DMASTAT);
587         ec->irqmask     = EESOX_STAT_INTR;
588         ec->irq_data    = info;
589         ec->ops         = (expansioncard_ops_t *)&eesoxscsi_ops;
590
591         info->info.scsi.io_port         = base + EESOX_FAS216_OFFSET;
592         info->info.scsi.io_shift        = EESOX_FAS216_SHIFT;
593         info->info.scsi.irq             = host->irq;
594         info->info.ifcfg.clockrate      = 40; /* MHz */
595         info->info.ifcfg.select_timeout = 255;
596         info->info.ifcfg.asyncperiod    = 200; /* ns */
597         info->info.ifcfg.sync_max_depth = 7;
598         info->info.ifcfg.cntl3          = CNTL3_FASTSCSI | CNTL3_FASTCLK;
599         info->info.ifcfg.disconnect_ok  = 1;
600         info->info.ifcfg.wide_max_size  = 0;
601         info->info.ifcfg.capabilities   = FASCAP_PSEUDODMA;
602         info->info.dma.setup            = eesoxscsi_dma_setup;
603         info->info.dma.pseudo           = eesoxscsi_dma_pseudo;
604         info->info.dma.stop             = eesoxscsi_dma_stop;
605
606         ret = fas216_init(host);
607         if (ret)
608                 goto out_free;
609
610         ret = request_irq(host->irq, eesoxscsi_intr, 0, "eesox", &info->info);
611         if (ret) {
612                 printk("scsi%d: IRQ%d not free: %d\n",
613                        host->host_no, host->irq, ret);
614                 goto out_remove;
615         }
616
617         if (host->dma_channel != NO_DMA) {
618                 if (request_dma(host->dma_channel, "eesox")) {
619                         printk("scsi%d: DMA%d not free, DMA disabled\n",
620                                host->host_no, host->dma_channel);
621                         host->dma_channel = NO_DMA;
622                 } else {
623                         set_dma_speed(host->dma_channel, 180);
624                         info->info.ifcfg.capabilities |= FASCAP_DMA;
625                         info->info.ifcfg.cntl3 |= CNTL3_BS8;
626                 }
627         }
628
629         ret = fas216_add(host);
630         if (ret == 0)
631                 goto out;
632
633         if (host->dma_channel != NO_DMA)
634                 free_dma(host->dma_channel);
635         free_irq(host->irq, host);
636
637  out_remove:
638         fas216_remove(host);
639
640  out_free:
641         scsi_unregister(host);
642
643  out_region:
644         release_region(base + EESOX_FAS216_OFFSET, 16 << EESOX_FAS216_SHIFT);
645
646  out:
647         return ret;
648 }
649
650 static void eesoxscsi_remove(struct Scsi_Host *host)
651 {
652         struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
653
654         fas216_remove(host);
655
656         if (host->dma_channel != NO_DMA)
657                 free_dma(host->dma_channel);
658         free_irq(host->irq, host);
659
660         release_region(host->io_port + EESOX_FAS216_OFFSET, 16 << EESOX_FAS216_SHIFT);
661
662         fas216_release(host);
663         ecard_release(info->ec);
664 }
665
666 static int __init eesox_init(void)
667 {
668         scsi_register_module(MODULE_SCSI_HA, &eesox_template);
669         if (eesox_template.present)
670                 return 0;
671
672         scsi_unregister_module(MODULE_SCSI_HA, &eesox_template);
673         return -ENODEV;
674 }
675
676 static void __exit eesox_exit(void)
677 {
678         scsi_unregister_module(MODULE_SCSI_HA, &eesox_template);
679 }
680
681 module_init(eesox_init);
682 module_exit(eesox_exit);
683
684 MODULE_AUTHOR("Russell King");
685 MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines");
686 MODULE_PARM(term, "1-8i");
687 MODULE_PARM_DESC(term, "SCSI bus termination");
688 MODULE_LICENSE("GPL");
689 EXPORT_NO_SYMBOLS;