1/*----------------------------------------------------------------*/
2/*
3   Qlogic linux driver - work in progress. No Warranty express or implied.
4   Use at your own risk.  Support Tort Reform so you won't have to read all
5   these silly disclaimers.
6
7   Copyright 1994, Tom Zerucha.
8   tz@execpc.com
9
10   Additional Code, and much appreciated help by
11   Michael A. Griffith
12   grif@cs.ucr.edu
13
14   Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
15   help respectively, and for suffering through my foolishness during the
16   debugging process.
17
18   Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
19   (you can reference it, but it is incomplete and inaccurate in places)
20
21   Version 0.46 1/30/97 - kernel 1.2.0+
22
23   Functions as standalone, loadable, and PCMCIA driver, the latter from
24   Dave Hinds' PCMCIA package.
25
26   Cleaned up 26/10/2002 by Alan Cox <alan@redhat.com> as part of the 2.5
27   SCSI driver cleanup and audit. This driver still needs work on the
28   following
29   	-	Non terminating hardware waits
30   	-	Some layering violations with its pcmcia stub
31
32   Redistributable under terms of the GNU General Public License
33
34   For the avoidance of doubt the "preferred form" of this code is one which
35   is in an open non patent encumbered format. Where cryptographic key signing
36   forms part of the process of creating an executable the information
37   including keys needed to generate an equivalently functional executable
38   are deemed to be part of the source code.
39
40*/
41
42#include <linux/module.h>
43#include <linux/blkdev.h>		/* to get disk capacity */
44#include <linux/kernel.h>
45#include <linux/string.h>
46#include <linux/init.h>
47#include <linux/interrupt.h>
48#include <linux/ioport.h>
49#include <linux/proc_fs.h>
50#include <linux/unistd.h>
51#include <linux/spinlock.h>
52#include <linux/stat.h>
53
54#include <asm/io.h>
55#include <asm/irq.h>
56#include <asm/dma.h>
57
58#include "scsi.h"
59#include <scsi/scsi_host.h>
60#include "qlogicfas408.h"
61
62/*----------------------------------------------------------------*/
63static int qlcfg5 = (XTALFREQ << 5);	/* 15625/512 */
64static int qlcfg6 = SYNCXFRPD;
65static int qlcfg7 = SYNCOFFST;
66static int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4);
67static int qlcfg9 = ((XTALFREQ + 4) / 5);
68static int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4);
69
70/*----------------------------------------------------------------*/
71
72/*----------------------------------------------------------------*/
73/* local functions */
74/*----------------------------------------------------------------*/
75
76/* error recovery - reset everything */
77
78static void ql_zap(struct qlogicfas408_priv *priv)
79{
80	int x;
81	int qbase = priv->qbase;
82	int int_type = priv->int_type;
83
84	x = inb(qbase + 0xd);
85	REG0;
86	outb(3, qbase + 3);	/* reset SCSI */
87	outb(2, qbase + 3);	/* reset chip */
88	if (x & 0x80)
89		REG1;
90}
91
92/*
93 *	Do a pseudo-dma tranfer
94 */
95
96static int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request, int reqlen)
97{
98	int j;
99	int qbase = priv->qbase;
100	j = 0;
101	if (phase & 1) {	/* in */
102#if QL_TURBO_PDMA
103		rtrc(4)
104		/* empty fifo in large chunks */
105		if (reqlen >= 128 && (inb(qbase + 8) & 2)) {	/* full */
106			insl(qbase + 4, request, 32);
107			reqlen -= 128;
108			request += 128;
109		}
110		while (reqlen >= 84 && !(j & 0xc0))	/* 2/3 */
111			if ((j = inb(qbase + 8)) & 4)
112			{
113				insl(qbase + 4, request, 21);
114				reqlen -= 84;
115				request += 84;
116			}
117		if (reqlen >= 44 && (inb(qbase + 8) & 8)) {	/* 1/3 */
118			insl(qbase + 4, request, 11);
119			reqlen -= 44;
120			request += 44;
121		}
122#endif
123		/* until both empty and int (or until reclen is 0) */
124		rtrc(7)
125		j = 0;
126		while (reqlen && !((j & 0x10) && (j & 0xc0)))
127		{
128			/* while bytes to receive and not empty */
129			j &= 0xc0;
130			while (reqlen && !((j = inb(qbase + 8)) & 0x10))
131			{
132				*request++ = inb(qbase + 4);
133				reqlen--;
134			}
135			if (j & 0x10)
136				j = inb(qbase + 8);
137
138		}
139	} else {		/* out */
140#if QL_TURBO_PDMA
141		rtrc(4)
142		    if (reqlen >= 128 && inb(qbase + 8) & 0x10) {	/* empty */
143			outsl(qbase + 4, request, 32);
144			reqlen -= 128;
145			request += 128;
146		}
147		while (reqlen >= 84 && !(j & 0xc0))	/* 1/3 */
148			if (!((j = inb(qbase + 8)) & 8)) {
149				outsl(qbase + 4, request, 21);
150				reqlen -= 84;
151				request += 84;
152			}
153		if (reqlen >= 40 && !(inb(qbase + 8) & 4)) {	/* 2/3 */
154			outsl(qbase + 4, request, 10);
155			reqlen -= 40;
156			request += 40;
157		}
158#endif
159		/* until full and int (or until reclen is 0) */
160		rtrc(7)
161		    j = 0;
162		while (reqlen && !((j & 2) && (j & 0xc0))) {
163			/* while bytes to send and not full */
164			while (reqlen && !((j = inb(qbase + 8)) & 2))
165			{
166				outb(*request++, qbase + 4);
167				reqlen--;
168			}
169			if (j & 2)
170				j = inb(qbase + 8);
171		}
172	}
173	/* maybe return reqlen */
174	return inb(qbase + 8) & 0xc0;
175}
176
177/*
178 *	Wait for interrupt flag (polled - not real hardware interrupt)
179 */
180
181static int ql_wai(struct qlogicfas408_priv *priv)
182{
183	int k;
184	int qbase = priv->qbase;
185	unsigned long i;
186
187	k = 0;
188	i = jiffies + WATCHDOG;
189	while (time_before(jiffies, i) && !priv->qabort &&
190					!((k = inb(qbase + 4)) & 0xe0)) {
191		barrier();
192		cpu_relax();
193	}
194	if (time_after_eq(jiffies, i))
195		return (DID_TIME_OUT);
196	if (priv->qabort)
197		return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
198	if (k & 0x60)
199		ql_zap(priv);
200	if (k & 0x20)
201		return (DID_PARITY);
202	if (k & 0x40)
203		return (DID_ERROR);
204	return 0;
205}
206
207/*
208 *	Initiate scsi command - queueing handler
209 *	caller must hold host lock
210 */
211
212static void ql_icmd(struct scsi_cmnd *cmd)
213{
214	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
215	int 	qbase = priv->qbase;
216	int	int_type = priv->int_type;
217	unsigned int i;
218
219	priv->qabort = 0;
220
221	REG0;
222	/* clearing of interrupts and the fifo is needed */
223
224	inb(qbase + 5);		/* clear interrupts */
225	if (inb(qbase + 5))	/* if still interrupting */
226		outb(2, qbase + 3);	/* reset chip */
227	else if (inb(qbase + 7) & 0x1f)
228		outb(1, qbase + 3);	/* clear fifo */
229	while (inb(qbase + 5));	/* clear ints */
230	REG1;
231	outb(1, qbase + 8);	/* set for PIO pseudo DMA */
232	outb(0, qbase + 0xb);	/* disable ints */
233	inb(qbase + 8);		/* clear int bits */
234	REG0;
235	outb(0x40, qbase + 0xb);	/* enable features */
236
237	/* configurables */
238	outb(qlcfgc, qbase + 0xc);
239	/* config: no reset interrupt, (initiator) bus id */
240	outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
241	outb(qlcfg7, qbase + 7);
242	outb(qlcfg6, qbase + 6);
243	 /**/ outb(qlcfg5, qbase + 5);	/* select timer */
244	outb(qlcfg9 & 7, qbase + 9);	/* prescaler */
245/*	outb(0x99, qbase + 5);	*/
246	outb(scmd_id(cmd), qbase + 4);
247
248	for (i = 0; i < cmd->cmd_len; i++)
249		outb(cmd->cmnd[i], qbase + 2);
250
251	priv->qlcmd = cmd;
252	outb(0x41, qbase + 3);	/* select and send command */
253}
254
255/*
256 *	Process scsi command - usually after interrupt
257 */
258
259static unsigned int ql_pcmd(struct scsi_cmnd *cmd)
260{
261	unsigned int i, j;
262	unsigned long k;
263	unsigned int result;	/* ultimate return result */
264	unsigned int status;	/* scsi returned status */
265	unsigned int message;	/* scsi returned message */
266	unsigned int phase;	/* recorded scsi phase */
267	unsigned int reqlen;	/* total length of transfer */
268	struct scatterlist *sglist;	/* scatter-gather list pointer */
269	unsigned int sgcount;	/* sg counter */
270	char *buf;
271	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
272	int qbase = priv->qbase;
273	int int_type = priv->int_type;
274
275	rtrc(1)
276	j = inb(qbase + 6);
277	i = inb(qbase + 5);
278	if (i == 0x20) {
279		return (DID_NO_CONNECT << 16);
280	}
281	i |= inb(qbase + 5);	/* the 0x10 bit can be set after the 0x08 */
282	if (i != 0x18) {
283		printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
284		ql_zap(priv);
285		return (DID_BAD_INTR << 16);
286	}
287	j &= 7;			/* j = inb( qbase + 7 ) >> 5; */
288
289	/* correct status is supposed to be step 4 */
290	/* it sometimes returns step 3 but with 0 bytes left to send */
291	/* We can try stuffing the FIFO with the max each time, but we will get a
292	   sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
293
294	if (j != 3 && j != 4) {
295		printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
296		     j, i, inb(qbase + 7) & 0x1f);
297		ql_zap(priv);
298		return (DID_ERROR << 16);
299	}
300	result = DID_OK;
301	if (inb(qbase + 7) & 0x1f)	/* if some bytes in fifo */
302		outb(1, qbase + 3);	/* clear fifo */
303	/* note that request_bufflen is the total xfer size when sg is used */
304	reqlen = cmd->request_bufflen;
305	/* note that it won't work if transfers > 16M are requested */
306	if (reqlen && !((phase = inb(qbase + 4)) & 6)) {	/* data phase */
307		rtrc(2)
308		outb(reqlen, qbase);	/* low-mid xfer cnt */
309		outb(reqlen >> 8, qbase + 1);	/* low-mid xfer cnt */
310		outb(reqlen >> 16, qbase + 0xe);	/* high xfer cnt */
311		outb(0x90, qbase + 3);	/* command do xfer */
312		/* PIO pseudo DMA to buffer or sglist */
313		REG1;
314		if (!cmd->use_sg)
315			ql_pdma(priv, phase, cmd->request_buffer,
316				cmd->request_bufflen);
317		else {
318			sgcount = cmd->use_sg;
319			sglist = cmd->request_buffer;
320			while (sgcount--) {
321				if (priv->qabort) {
322					REG0;
323					return ((priv->qabort == 1 ?
324						DID_ABORT : DID_RESET) << 16);
325				}
326				buf = page_address(sglist->page) + sglist->offset;
327				if (ql_pdma(priv, phase, buf, sglist->length))
328					break;
329				sglist++;
330			}
331		}
332		REG0;
333		rtrc(2)
334		/*
335		 *	Wait for irq (split into second state of irq handler
336		 *	if this can take time)
337		 */
338		if ((k = ql_wai(priv)))
339			return (k << 16);
340		k = inb(qbase + 5);	/* should be 0x10, bus service */
341	}
342
343	/*
344	 *	Enter Status (and Message In) Phase
345	 */
346
347	k = jiffies + WATCHDOG;
348
349	while (time_before(jiffies, k) && !priv->qabort &&
350						!(inb(qbase + 4) & 6))
351		cpu_relax();	/* wait for status phase */
352
353	if (time_after_eq(jiffies, k)) {
354		ql_zap(priv);
355		return (DID_TIME_OUT << 16);
356	}
357
358	while (inb(qbase + 5))
359		cpu_relax();	/* clear pending ints */
360
361	if (priv->qabort)
362		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
363
364	outb(0x11, qbase + 3);	/* get status and message */
365	if ((k = ql_wai(priv)))
366		return (k << 16);
367	i = inb(qbase + 5);	/* get chip irq stat */
368	j = inb(qbase + 7) & 0x1f;	/* and bytes rec'd */
369	status = inb(qbase + 2);
370	message = inb(qbase + 2);
371
372	/*
373	 *	Should get function complete int if Status and message, else
374	 *	bus serv if only status
375	 */
376	if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
377		printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
378		result = DID_ERROR;
379	}
380	outb(0x12, qbase + 3);	/* done, disconnect */
381	rtrc(1)
382	if ((k = ql_wai(priv)))
383		return (k << 16);
384
385	/*
386	 *	Should get bus service interrupt and disconnect interrupt
387	 */
388
389	i = inb(qbase + 5);	/* should be bus service */
390	while (!priv->qabort && ((i & 0x20) != 0x20)) {
391		barrier();
392		cpu_relax();
393		i |= inb(qbase + 5);
394	}
395	rtrc(0)
396
397	if (priv->qabort)
398		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
399
400	return (result << 16) | (message << 8) | (status & STATUS_MASK);
401}
402
403/*
404 *	Interrupt handler
405 */
406
407static void ql_ihandl(void *dev_id)
408{
409	struct scsi_cmnd *icmd;
410	struct Scsi_Host *host = dev_id;
411	struct qlogicfas408_priv *priv = get_priv_by_host(host);
412	int qbase = priv->qbase;
413	REG0;
414
415	if (!(inb(qbase + 4) & 0x80))	/* false alarm? */
416		return;
417
418	if (priv->qlcmd == NULL) {	/* no command to process? */
419		int i;
420		i = 16;
421		while (i-- && inb(qbase + 5));	/* maybe also ql_zap() */
422		return;
423	}
424	icmd = priv->qlcmd;
425	icmd->result = ql_pcmd(icmd);
426	priv->qlcmd = NULL;
427	/*
428	 *	If result is CHECK CONDITION done calls qcommand to request
429	 *	sense
430	 */
431	(icmd->scsi_done) (icmd);
432}
433
434irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id)
435{
436	unsigned long flags;
437	struct Scsi_Host *host = dev_id;
438
439	spin_lock_irqsave(host->host_lock, flags);
440	ql_ihandl(dev_id);
441	spin_unlock_irqrestore(host->host_lock, flags);
442	return IRQ_HANDLED;
443}
444
445/*
446 *	Queued command
447 */
448
449int qlogicfas408_queuecommand(struct scsi_cmnd *cmd,
450			      void (*done) (struct scsi_cmnd *))
451{
452	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
453	if (scmd_id(cmd) == priv->qinitid) {
454		cmd->result = DID_BAD_TARGET << 16;
455		done(cmd);
456		return 0;
457	}
458
459	cmd->scsi_done = done;
460	/* wait for the last command's interrupt to finish */
461	while (priv->qlcmd != NULL) {
462		barrier();
463		cpu_relax();
464	}
465	ql_icmd(cmd);
466	return 0;
467}
468
469/*
470 *	Return bios parameters
471 */
472
473int qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev,
474			   sector_t capacity, int ip[])
475{
476/* This should mimic the DOS Qlogic driver's behavior exactly */
477	ip[0] = 0x40;
478	ip[1] = 0x20;
479	ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
480	if (ip[2] > 1024) {
481		ip[0] = 0xff;
482		ip[1] = 0x3f;
483		ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
484	}
485	return 0;
486}
487
488/*
489 *	Abort a command in progress
490 */
491
492int qlogicfas408_abort(struct scsi_cmnd *cmd)
493{
494	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
495	priv->qabort = 1;
496	ql_zap(priv);
497	return SUCCESS;
498}
499
500
501int qlogicfas408_bus_reset(struct scsi_cmnd *cmd)
502{
503	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
504	unsigned long flags;
505
506	priv->qabort = 2;
507
508	spin_lock_irqsave(cmd->device->host->host_lock, flags);
509	ql_zap(priv);
510	spin_unlock_irqrestore(cmd->device->host->host_lock, flags);
511
512	return SUCCESS;
513}
514
515/*
516 *	Return info string
517 */
518
519const char *qlogicfas408_info(struct Scsi_Host *host)
520{
521	struct qlogicfas408_priv *priv = get_priv_by_host(host);
522	return priv->qinfo;
523}
524
525/*
526 *	Get type of chip
527 */
528
529int qlogicfas408_get_chip_type(int qbase, int int_type)
530{
531	REG1;
532	return inb(qbase + 0xe) & 0xf8;
533}
534
535/*
536 *	Perform initialization tasks
537 */
538
539void qlogicfas408_setup(int qbase, int id, int int_type)
540{
541	outb(1, qbase + 8);	/* set for PIO pseudo DMA */
542	REG0;
543	outb(0x40 | qlcfg8 | id, qbase + 8);	/* (ini) bus id, disable scsi rst */
544	outb(qlcfg5, qbase + 5);	/* select timer */
545	outb(qlcfg9, qbase + 9);	/* prescaler */
546
547#if QL_RESET_AT_START
548	outb(3, qbase + 3);
549
550	REG1;
551	while (inb(qbase + 0xf) & 4)
552		cpu_relax();
553
554	REG0;
555#endif
556}
557
558/*
559 *	Checks if this is a QLogic FAS 408
560 */
561
562int qlogicfas408_detect(int qbase, int int_type)
563{
564        REG1;
565	return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
566	       ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));
567}
568
569/*
570 *	Disable interrupts
571 */
572
573void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
574{
575	int qbase = priv->qbase;
576	int int_type = priv->int_type;
577
578	REG1;
579	outb(0, qbase + 0xb);	/* disable ints */
580}
581
582/*
583 *	Init and exit functions
584 */
585
586static int __init qlogicfas408_init(void)
587{
588	return 0;
589}
590
591static void __exit qlogicfas408_exit(void)
592{
593
594}
595
596MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
597MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
598MODULE_LICENSE("GPL");
599module_init(qlogicfas408_init);
600module_exit(qlogicfas408_exit);
601
602EXPORT_SYMBOL(qlogicfas408_info);
603EXPORT_SYMBOL(qlogicfas408_queuecommand);
604EXPORT_SYMBOL(qlogicfas408_abort);
605EXPORT_SYMBOL(qlogicfas408_bus_reset);
606EXPORT_SYMBOL(qlogicfas408_biosparam);
607EXPORT_SYMBOL(qlogicfas408_ihandl);
608EXPORT_SYMBOL(qlogicfas408_get_chip_type);
609EXPORT_SYMBOL(qlogicfas408_setup);
610EXPORT_SYMBOL(qlogicfas408_detect);
611EXPORT_SYMBOL(qlogicfas408_disable_ints);
612