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