OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [uclinux/] [uClinux-2.0.x/] [arch/] [armnommu/] [drivers/] [scsi/] [eesox.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 199 simons
/*
2
 * linux/arch/arm/drivers/scsi/eesox.c
3
 *
4
 * Copyright (C) 1997-1998 Russell King
5
 *
6
 * This driver is based on experimentation.  Hence, it may have made
7
 * assumptions about the particular card that I have available, and
8
 * may not be reliable!
9
 *
10
 * Changelog:
11
 *  01-10-1997  RMK     Created, READONLY version
12
 *  15-02-1998  RMK     READ/WRITE version
13
 *                      added DMA support and hardware definitions
14
 *  14-03-1998  RMK     Updated DMA support
15
 *                      Added terminator control
16
 *  15-04-1998  RMK     Only do PIO if FAS216 will allow it.
17
 */
18
 
19
#include <linux/module.h>
20
#include <linux/blk.h>
21
#include <linux/kernel.h>
22
#include <linux/string.h>
23
#include <linux/ioport.h>
24
#include <linux/sched.h>
25
#include <linux/proc_fs.h>
26
#include <linux/unistd.h>
27
#include <linux/stat.h>
28
 
29
#include <asm/delay.h>
30
#include <asm/io.h>
31
#include <asm/irq.h>
32
#include <asm/dma.h>
33
#include <asm/ecard.h>
34
#include <asm/pgtable.h>
35
 
36
#include "sd.h"
37
#include "hosts.h"
38
#include "eesox.h"
39
 
40
#define NO_IRQ  255
41
#define NO_DMA  255
42
 
43
/* Configuration */
44
#define EESOX_XTALFREQ          40
45
#define EESOX_ASYNC_PERIOD      200
46
#define EESOX_SYNC_DEPTH        7
47
 
48
/*
49
 * List of devices that the driver will recognise
50
 */
51
#define EESOXSCSI_LIST  { MANU_EESOX, PROD_EESOX_SCSI2 }
52
 
53
#define EESOX_FAS216_OFFSET     0xc00
54
#define EESOX_FAS216_SHIFT      3
55
 
56
#define EESOX_STATUS            0xa00
57
#define EESOX_STAT_INTR         0x01
58
#define EESOX_STAT_DMA          0x02
59
 
60
#define EESOX_CONTROL           0xa00
61
#define EESOX_INTR_ENABLE       0x04
62
#define EESOX_TERM_ENABLE       0x02
63
#define EESOX_RESET             0x01
64
 
65
#define EESOX_DMA_OFFSET        0xe00
66
 
67
/*
68
 * Version
69
 */
70
#define VER_MAJOR       0
71
#define VER_MINOR       0
72
#define VER_PATCH       2
73
 
74
static struct expansion_card *ecs[MAX_ECARDS];
75
 
76
/*
77
 * Use term=0,1,0,0,0 to turn terminators on/off
78
 */
79
int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
80
 
81
static struct proc_dir_entry proc_scsi_eesox = {
82
        PROC_SCSI_QLOGICISP, 5, "eesox",
83
        S_IFDIR | S_IRUGO | S_IXUGO, 2
84
};
85
 
86
/* Prototype: void eesoxscsi_irqenable(ec, irqnr)
87
 * Purpose  : Enable interrupts on EESOX SCSI card
88
 * Params   : ec    - expansion card structure
89
 *          : irqnr - interrupt number
90
 */
91
static void
92
eesoxscsi_irqenable(struct expansion_card *ec, int irqnr)
93
{
94
        struct control *control = (struct control *)ec->irq_data;
95
 
96
        control->control |= EESOX_INTR_ENABLE;
97
 
98
        outb(control->control, control->io_port);
99
}
100
 
101
/* Prototype: void eesoxscsi_irqdisable(ec, irqnr)
102
 * Purpose  : Disable interrupts on EESOX SCSI card
103
 * Params   : ec    - expansion card structure
104
 *          : irqnr - interrupt number
105
 */
106
static void
107
eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr)
108
{
109
        struct control *control = (struct control *)ec->irq_data;
110
 
111
        control->control &= ~EESOX_INTR_ENABLE;
112
 
113
        outb(control->control, control->io_port);
114
}
115
 
116
static const expansioncard_ops_t eesoxscsi_ops = {
117
        eesoxscsi_irqenable,
118
        eesoxscsi_irqdisable,
119
        NULL,
120
        NULL,
121
        NULL,
122
        NULL
123
};
124
 
125
/* Prototype: void eesoxscsi_terminator_ctl(*host, on_off)
126
 * Purpose  : Turn the EESOX SCSI terminators on or off
127
 * Params   : host   - card to turn on/off
128
 *          : on_off - !0 to turn on, 0 to turn off
129
 */
130
static void
131
eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
132
{
133
        EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
134
        unsigned long flags;
135
 
136
        save_flags_cli(flags);
137
        if (on_off)
138
                info->control.control |= EESOX_TERM_ENABLE;
139
        else
140
                info->control.control &= ~EESOX_TERM_ENABLE;
141
        restore_flags(flags);
142
 
143
        outb(info->control.control, info->control.io_port);
144
}
145
 
146
/* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs)
147
 * Purpose  : handle interrupts from EESOX SCSI card
148
 * Params   : irq    - interrupt number
149
 *            dev_id - user-defined (Scsi_Host structure)
150
 *            regs   - processor registers at interrupt
151
 */
152
static void
153
eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
154
{
155
        struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
156
 
157
        fas216_intr(host);
158
}
159
 
160
static void
161
eesoxscsi_invalidate(char *addr, long len, fasdmadir_t direction)
162
{
163
        unsigned int page;
164
 
165
        if (direction == DMA_OUT) {
166
                for(page = (unsigned int) addr; len > 0;
167
                     page += PAGE_SIZE, len -= PAGE_SIZE)
168
                        flush_page_to_ram(page);
169
        } else
170
                flush_cache_range(current->mm, (unsigned long)addr,
171
                                  (unsigned long)addr + len);
172
}
173
 
174
/* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type)
175
 * Purpose  : initialises DMA/PIO
176
 * Params   : host      - host
177
 *            SCpnt     - command
178
 *            direction - DMA on to/off of card
179
 *            min_type  - minimum DMA support that we must have for this transfer
180
 * Returns  : type of transfer to be performed
181
 */
182
static fasdmatype_t
183
eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
184
                       fasdmadir_t direction, fasdmatype_t min_type)
185
{
186
        EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
187
        int dmach = host->dma_channel;
188
 
189
        if (dmach != NO_DMA &&
190
            (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
191
                int buf;
192
 
193
                for(buf = 1; buf <= SCp->buffers_residual &&
194
                             buf < NR_SG; buf++) {
195
                        info->dmasg[buf].address = __virt_to_bus(
196
                                (unsigned long)SCp->buffer[buf].address);
197
                        info->dmasg[buf].length = SCp->buffer[buf].length;
198
 
199
                        eesoxscsi_invalidate(SCp->buffer[buf].address,
200
                                                SCp->buffer[buf].length,
201
                                                direction);
202
                }
203
 
204
                info->dmasg[0].address = __virt_to_phys((unsigned long)SCp->ptr);
205
                info->dmasg[0].length = SCp->this_residual;
206
                eesoxscsi_invalidate(SCp->ptr,
207
                                        SCp->this_residual, direction);
208
 
209
                disable_dma(dmach);
210
                set_dma_sg(dmach, info->dmasg, buf);
211
                set_dma_mode(dmach,
212
                             direction == DMA_OUT ? DMA_MODE_WRITE :
213
                                                    DMA_MODE_READ);
214
                enable_dma(dmach);
215
                return fasdma_real_all;
216
        }
217
        /*
218
         * We don't do DMA, we only do slow PIO
219
         *
220
         * Some day, we will do Pseudo DMA
221
         */
222
        return fasdma_pseudo;
223
}
224
 
225
static void
226
eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
227
                     fasdmadir_t dir, int transfer_size)
228
{
229
        EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
230
        unsigned int status;
231
        unsigned int length = SCp->this_residual;
232
        union {
233
                unsigned char *c;
234
                unsigned short *s;
235
                unsigned long *l;
236
        } buffer;
237
 
238
        buffer.c = SCp->ptr;
239
 
240
        status = inb(host->io_port + EESOX_STATUS);
241
        if (dir == DMA_IN) {
242
                while (length > 8) {
243
                        if (status & EESOX_STAT_DMA) {
244
                                unsigned long l1, l2;
245
 
246
                                l1 = inw(info->dmaarea);
247
                                l1 |= inw(info->dmaarea) << 16;
248
                                l2 = inw(info->dmaarea);
249
                                l2 |= inw(info->dmaarea) << 16;
250
                                *buffer.l++ = l1;
251
                                *buffer.l++ = l2;
252
                                length -= 8;
253
                        } else if (status & EESOX_STAT_INTR)
254
                                goto end;
255
                        status = inb(host->io_port + EESOX_STATUS);
256
                }
257
 
258
                while (length > 1) {
259
                        if (status & EESOX_STAT_DMA) {
260
                                *buffer.s++ = inw(info->dmaarea);
261
                                length -= 2;
262
                        } else if (status & EESOX_STAT_INTR)
263
                                goto end;
264
                        status = inb(host->io_port + EESOX_STATUS);
265
                }
266
 
267
                while (length > 0) {
268
                        if (status & EESOX_STAT_DMA) {
269
                                *buffer.c++ = inw(info->dmaarea);
270
                                length -= 1;
271
                        } else if (status & EESOX_STAT_INTR)
272
                                goto end;
273
                        status = inb(host->io_port + EESOX_STATUS);
274
                }
275
        } else {
276
                while (length > 8) {
277
                        if (status & EESOX_STAT_DMA) {
278
                                unsigned long l1, l2;
279
 
280
                                l1 = *buffer.l++;
281
                                l2 = *buffer.l++;
282
 
283
                                outw(l1, info->dmaarea);
284
                                outw(l1 >> 16, info->dmaarea);
285
                                outw(l2, info->dmaarea);
286
                                outw(l2 >> 16, info->dmaarea);
287
                                length -= 8;
288
                        } else if (status & EESOX_STAT_INTR)
289
                                goto end;
290
                        status = inb(host->io_port + EESOX_STATUS);
291
                }
292
 
293
                while (length > 1) {
294
                        if (status & EESOX_STAT_DMA) {
295
                                outw(*buffer.s++, info->dmaarea);
296
                                length -= 2;
297
                        } else if (status & EESOX_STAT_INTR)
298
                                goto end;
299
                        status = inb(host->io_port + EESOX_STATUS);
300
                }
301
 
302
                while (length > 0) {
303
                        if (status & EESOX_STAT_DMA) {
304
                                outw(*buffer.c++, info->dmaarea);
305
                                length -= 1;
306
                        } else if (status & EESOX_STAT_INTR)
307
                                goto end;
308
                        status = inb(host->io_port + EESOX_STATUS);
309
                }
310
        }
311
end:
312
}
313
 
314
/* Prototype: int eesoxscsi_dma_stop(host, SCpnt)
315
 * Purpose  : stops DMA/PIO
316
 * Params   : host  - host
317
 *            SCpnt - command
318
 */
319
static void
320
eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
321
{
322
        if (host->dma_channel != NO_DMA)
323
                disable_dma(host->dma_channel);
324
}
325
 
326
/* Prototype: int eesoxscsi_detect(Scsi_Host_Template * tpnt)
327
 * Purpose  : initialises EESOX SCSI driver
328
 * Params   : tpnt - template for this SCSI adapter
329
 * Returns  : >0 if host found, 0 otherwise.
330
 */
331
int
332
eesoxscsi_detect(Scsi_Host_Template *tpnt)
333
{
334
        static const card_ids eesoxscsi_cids[] =
335
                        { EESOXSCSI_LIST, { 0xffff, 0xffff} };
336
        int count = 0;
337
        struct Scsi_Host *host;
338
 
339
        tpnt->proc_dir = &proc_scsi_eesox;
340
        memset(ecs, 0, sizeof (ecs));
341
 
342
        ecard_startfind();
343
 
344
        while(1) {
345
                EESOXScsi_Info *info;
346
 
347
                ecs[count] = ecard_find(0, eesoxscsi_cids);
348
                if (!ecs[count])
349
                        break;
350
 
351
                ecard_claim(ecs[count]);
352
 
353
                host = scsi_register(tpnt, sizeof (EESOXScsi_Info));
354
                if (!host) {
355
                        ecard_release(ecs[count]);
356
                        break;
357
                }
358
 
359
                host->io_port = ecard_address(ecs[count], ECARD_IOC, ECARD_FAST);
360
                host->irq = ecs[count]->irq;
361
                host->dma_channel = ecs[count]->dma;
362
                info = (EESOXScsi_Info *)host->hostdata;
363
 
364
                info->control.io_port = host->io_port + EESOX_CONTROL;
365
                info->control.control = term[count] ? EESOX_TERM_ENABLE : 0;
366
                outb(info->control.control, info->control.io_port);
367
 
368
                ecs[count]->irqaddr = (unsigned char *)
369
                            ioaddr(host->io_port + EESOX_STATUS);
370
                ecs[count]->irqmask = EESOX_STAT_INTR;
371
                ecs[count]->irq_data = &info->control;
372
                ecs[count]->ops = (expansioncard_ops_t *)&eesoxscsi_ops;
373
 
374
                info->info.scsi.io_port         = host->io_port + EESOX_FAS216_OFFSET;
375
                info->info.scsi.io_shift        = EESOX_FAS216_SHIFT;
376
                info->info.scsi.irq             = host->irq;
377
                info->info.ifcfg.clockrate      = EESOX_XTALFREQ;
378
                info->info.ifcfg.select_timeout = 255;
379
                info->info.ifcfg.asyncperiod    = EESOX_ASYNC_PERIOD;
380
                info->info.ifcfg.sync_max_depth = EESOX_SYNC_DEPTH;
381
                info->info.ifcfg.cntl3          = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
382
                info->info.ifcfg.disconnect_ok  = 1;
383
                info->info.ifcfg.wide_max_size  = 0;
384
                info->info.dma.setup            = eesoxscsi_dma_setup;
385
                info->info.dma.pseudo           = eesoxscsi_dma_pseudo;
386
                info->info.dma.stop             = eesoxscsi_dma_stop;
387
                info->dmaarea                   = host->io_port + EESOX_DMA_OFFSET;
388
 
389
                request_region(host->io_port + EESOX_FAS216_OFFSET,
390
                                16 << EESOX_FAS216_SHIFT, "eesox2-fas");
391
 
392
                if (request_irq(host->irq, eesoxscsi_intr,
393
                                SA_INTERRUPT, "eesox", host)) {
394
                        printk("scsi%d: IRQ%d not free, interrupts disabled\n",
395
                               host->host_no, host->irq);
396
                        host->irq = NO_IRQ;
397
                }
398
 
399
                if (request_dma(host->dma_channel, "eesox")) {
400
                        printk("scsi%d: DMA%d not free, DMA disabled\n",
401
                               host->host_no, host->dma_channel);
402
                        host->dma_channel = NO_DMA;
403
                }
404
 
405
                fas216_init(host);
406
                ++count;
407
        }
408
        return count;
409
}
410
 
411
/* Prototype: int eesoxscsi_release(struct Scsi_Host * host)
412
 * Purpose  : releases all resources used by this adapter
413
 * Params   : host - driver host structure to return info for.
414
 */
415
int eesoxscsi_release(struct Scsi_Host *host)
416
{
417
        int i;
418
 
419
        fas216_release(host);
420
 
421
        if (host->irq != NO_IRQ)
422
                free_irq(host->irq, host);
423
        if (host->dma_channel != NO_DMA)
424
                free_dma(host->dma_channel);
425
        release_region(host->io_port + EESOX_FAS216_OFFSET, 16 << EESOX_FAS216_SHIFT);
426
 
427
        for (i = 0; i < MAX_ECARDS; i++)
428
                if (ecs[i] &&
429
                    host->io_port == ecard_address(ecs[i], ECARD_IOC, ECARD_FAST))
430
                        ecard_release(ecs[i]);
431
        return 0;
432
}
433
 
434
/* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host)
435
 * Purpose  : returns a descriptive string about this interface,
436
 * Params   : host - driver host structure to return info for.
437
 * Returns  : pointer to a static buffer containing null terminated string.
438
 */
439
const char *eesoxscsi_info(struct Scsi_Host *host)
440
{
441
        EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
442
        static char string[100], *p;
443
 
444
        p = string;
445
        p += sprintf(string, "%s at port %X ", host->hostt->name, host->io_port);
446
 
447
        if (host->irq != NO_IRQ)
448
                p += sprintf(p, "irq %d ", host->irq);
449
        else
450
                p += sprintf(p, "NO IRQ ");
451
 
452
        if (host->dma_channel != NO_DMA)
453
                p += sprintf(p, "dma %d ", host->dma_channel);
454
        else
455
                p += sprintf(p, "NO DMA ");
456
 
457
        p += sprintf(p, "v%d.%d.%d scsi %s", VER_MAJOR, VER_MINOR, VER_PATCH,
458
                  info->info.scsi.type);
459
 
460
        p += sprintf(p, " terminators %s",
461
                  info->control.control & EESOX_TERM_ENABLE ? "on" : "off");
462
 
463
        return string;
464
}
465
 
466
/* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
467
 * Purpose  : Set a driver specific function
468
 * Params   : host   - host to setup
469
 *          : buffer - buffer containing string describing operation
470
 *          : length - length of string
471
 * Returns  : -EINVAL, or 0
472
 */
473
static int
474
eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
475
{
476
        int ret = length;
477
 
478
        if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) {
479
                buffer += 9;
480
                length -= 9;
481
 
482
                if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
483
                        if (buffer[5] == '1')
484
                                eesoxscsi_terminator_ctl(host, 1);
485
                        else if (buffer[5] == '0')
486
                                eesoxscsi_terminator_ctl(host, 0);
487
                        else
488
                                ret = -EINVAL;
489
                } else
490
                        ret = -EINVAL;
491
        } else
492
                ret = -EINVAL;
493
 
494
        return ret;
495
}
496
 
497
/* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
498
 *                                    int length, int host_no, int inout)
499
 * Purpose  : Return information about the driver to a user process accessing
500
 *            the /proc filesystem.
501
 * Params   : buffer - a buffer to write information to
502
 *            start  - a pointer into this buffer set by this routine to the start
503
 *                     of the required information.
504
 *            offset - offset into information that we have read upto.
505
 *            length - length of buffer
506
 *            host_no - host number to return information for
507
 *            inout  - 0 for reading, 1 for writing.
508
 * Returns  : length of data written to buffer.
509
 */
510
int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
511
                            int length, int host_no, int inout)
512
{
513
        int pos, begin, first;
514
        struct Scsi_Host *host = scsi_hostlist;
515
        EESOXScsi_Info *info;
516
        Scsi_Device *scd;
517
 
518
        while (host) {
519
                if (host->host_no == host_no)
520
                        break;
521
                host = host->next;
522
        }
523
        if (!host)
524
                return 0;
525
 
526
        if (inout == 1)
527
                return eesoxscsi_set_proc_info(host, buffer, length);
528
 
529
        info = (EESOXScsi_Info *)host->hostdata;
530
 
531
        begin = 0;
532
        pos = sprintf(buffer,
533
                        "EESOX SCSI driver version %d.%d.%d\n",
534
                        VER_MAJOR, VER_MINOR, VER_PATCH);
535
        pos += sprintf(buffer + pos,
536
                        "Address: %08X    IRQ : %d     DMA : %d\n"
537
                        "FAS    : %-10s  TERM: %-3s\n\n"
538
                        "Statistics:\n",
539
                        host->io_port, host->irq, host->dma_channel,
540
                        info->info.scsi.type, info->control.control & EESOX_TERM_ENABLE ? "on" : "off");
541
 
542
        pos += sprintf(buffer+pos,
543
                        "Queued commands: %-10u   Issued commands: %-10u\n"
544
                        "Done commands  : %-10u   Reads          : %-10u\n"
545
                        "Writes         : %-10u   Others         : %-10u\n"
546
                        "Disconnects    : %-10u   Aborts         : %-10u\n"
547
                        "Resets         : %-10u\n",
548
                        info->info.stats.queues,      info->info.stats.removes,
549
                        info->info.stats.fins,        info->info.stats.reads,
550
                        info->info.stats.writes,      info->info.stats.miscs,
551
                        info->info.stats.disconnects, info->info.stats.aborts,
552
                        info->info.stats.resets);
553
 
554
        first = 1;
555
        for (scd = scsi_devices; scd; scd = scd->next) {
556
                if (scd->host == host) {
557
                        int len;
558
 
559
                        if (first) {
560
                                pos += sprintf(buffer+pos,
561
                                               "\nAttached devices:\n");
562
                                first = 0;
563
                        }
564
 
565
                        proc_print_scsidevice(scd, buffer, &len, pos);
566
                        pos += len;
567
                        pos += sprintf(buffer+pos, "Extensions: ");
568
                        if (scd->tagged_supported)
569
                                pos += sprintf(buffer+pos, "TAG %sabled [%d] ",
570
                                              scd->tagged_queue ? "en" : "dis",
571
                                              scd->current_tag);
572
                        pos += sprintf(buffer+pos, "\n");
573
 
574
                        if (pos + begin < offset) {
575
                                begin += pos;
576
                                pos = 0;
577
                        }
578
                        if (pos + begin > offset + length)
579
                                break;
580
                }
581
        }
582
 
583
        *start = buffer + (offset - begin);
584
        pos -= offset - begin;
585
        if (pos > length)
586
                pos = length;
587
 
588
        return pos;
589
}
590
 
591
#ifdef MODULE
592
Scsi_Host_Template driver_template = EESOXSCSI;
593
 
594
#include "scsi_module.c"
595
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.