| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 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@lxorguk.ukuu.org.uk> 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 | /*----------------------------------------------------------------*/ | 
 | 63 | static int qlcfg5 = (XTALFREQ << 5);	/* 15625/512 */ | 
 | 64 | static int qlcfg6 = SYNCXFRPD; | 
 | 65 | static int qlcfg7 = SYNCOFFST; | 
 | 66 | static int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4); | 
 | 67 | static int qlcfg9 = ((XTALFREQ + 4) / 5); | 
 | 68 | static int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4); | 
 | 69 |  | 
 | 70 | /*----------------------------------------------------------------*/ | 
 | 71 |  | 
 | 72 | /*----------------------------------------------------------------*/ | 
 | 73 | /* local functions */ | 
 | 74 | /*----------------------------------------------------------------*/ | 
 | 75 |  | 
 | 76 | /* error recovery - reset everything */ | 
 | 77 |  | 
 | 78 | static 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 |   | 
 | 96 | static 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 |  | 
 | 181 | static 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 |  | 
 | 212 | static 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 |  | 
 | 259 | static 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 | 	char *buf; | 
 | 269 | 	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); | 
 | 270 | 	int qbase = priv->qbase; | 
 | 271 | 	int int_type = priv->int_type; | 
 | 272 |  | 
 | 273 | 	rtrc(1) | 
 | 274 | 	j = inb(qbase + 6); | 
 | 275 | 	i = inb(qbase + 5); | 
 | 276 | 	if (i == 0x20) { | 
 | 277 | 		return (DID_NO_CONNECT << 16); | 
 | 278 | 	} | 
 | 279 | 	i |= inb(qbase + 5);	/* the 0x10 bit can be set after the 0x08 */ | 
 | 280 | 	if (i != 0x18) { | 
 | 281 | 		printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i); | 
 | 282 | 		ql_zap(priv); | 
 | 283 | 		return (DID_BAD_INTR << 16); | 
 | 284 | 	} | 
 | 285 | 	j &= 7;			/* j = inb( qbase + 7 ) >> 5; */ | 
 | 286 |  | 
 | 287 | 	/* correct status is supposed to be step 4 */ | 
 | 288 | 	/* it sometimes returns step 3 but with 0 bytes left to send */ | 
 | 289 | 	/* We can try stuffing the FIFO with the max each time, but we will get a | 
 | 290 | 	   sequence of 3 if any bytes are left (but we do flush the FIFO anyway */ | 
 | 291 |  | 
 | 292 | 	if (j != 3 && j != 4) { | 
 | 293 | 		printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n", | 
 | 294 | 		     j, i, inb(qbase + 7) & 0x1f); | 
 | 295 | 		ql_zap(priv); | 
 | 296 | 		return (DID_ERROR << 16); | 
 | 297 | 	} | 
 | 298 | 	result = DID_OK; | 
 | 299 | 	if (inb(qbase + 7) & 0x1f)	/* if some bytes in fifo */ | 
 | 300 | 		outb(1, qbase + 3);	/* clear fifo */ | 
 | 301 | 	/* note that request_bufflen is the total xfer size when sg is used */ | 
 | 302 | 	reqlen = scsi_bufflen(cmd); | 
 | 303 | 	/* note that it won't work if transfers > 16M are requested */ | 
 | 304 | 	if (reqlen && !((phase = inb(qbase + 4)) & 6)) {	/* data phase */ | 
 | 305 | 		struct scatterlist *sg; | 
 | 306 | 		rtrc(2) | 
 | 307 | 		outb(reqlen, qbase);	/* low-mid xfer cnt */ | 
 | 308 | 		outb(reqlen >> 8, qbase + 1);	/* low-mid xfer cnt */ | 
 | 309 | 		outb(reqlen >> 16, qbase + 0xe);	/* high xfer cnt */ | 
 | 310 | 		outb(0x90, qbase + 3);	/* command do xfer */ | 
 | 311 | 		/* PIO pseudo DMA to buffer or sglist */ | 
 | 312 | 		REG1; | 
 | 313 |  | 
 | 314 | 		scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) { | 
 | 315 | 			if (priv->qabort) { | 
 | 316 | 				REG0; | 
 | 317 | 				return ((priv->qabort == 1 ? | 
 | 318 | 					 DID_ABORT : DID_RESET) << 16); | 
 | 319 | 			} | 
 | 320 | 			buf = sg_virt(sg); | 
 | 321 | 			if (ql_pdma(priv, phase, buf, sg->length)) | 
 | 322 | 				break; | 
 | 323 | 		} | 
 | 324 | 		REG0; | 
 | 325 | 		rtrc(2) | 
 | 326 | 		/* | 
 | 327 | 		 *	Wait for irq (split into second state of irq handler | 
 | 328 | 		 *	if this can take time)  | 
 | 329 | 		 */ | 
 | 330 | 		if ((k = ql_wai(priv))) | 
 | 331 | 			return (k << 16); | 
 | 332 | 		k = inb(qbase + 5);	/* should be 0x10, bus service */ | 
 | 333 | 	} | 
 | 334 |  | 
 | 335 | 	/* | 
 | 336 | 	 *	Enter Status (and Message In) Phase  | 
 | 337 | 	 */ | 
 | 338 | 	  | 
 | 339 | 	k = jiffies + WATCHDOG; | 
 | 340 |  | 
 | 341 | 	while (time_before(jiffies, k) && !priv->qabort && | 
 | 342 | 						!(inb(qbase + 4) & 6)) | 
 | 343 | 		cpu_relax();	/* wait for status phase */ | 
 | 344 |  | 
 | 345 | 	if (time_after_eq(jiffies, k)) { | 
 | 346 | 		ql_zap(priv); | 
 | 347 | 		return (DID_TIME_OUT << 16); | 
 | 348 | 	} | 
 | 349 |  | 
 | 350 | 	/* FIXME: timeout ?? */ | 
 | 351 | 	while (inb(qbase + 5)) | 
 | 352 | 		cpu_relax();	/* clear pending ints */ | 
 | 353 |  | 
 | 354 | 	if (priv->qabort) | 
 | 355 | 		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16); | 
 | 356 |  | 
 | 357 | 	outb(0x11, qbase + 3);	/* get status and message */ | 
 | 358 | 	if ((k = ql_wai(priv))) | 
 | 359 | 		return (k << 16); | 
 | 360 | 	i = inb(qbase + 5);	/* get chip irq stat */ | 
 | 361 | 	j = inb(qbase + 7) & 0x1f;	/* and bytes rec'd */ | 
 | 362 | 	status = inb(qbase + 2); | 
 | 363 | 	message = inb(qbase + 2); | 
 | 364 |  | 
 | 365 | 	/* | 
 | 366 | 	 *	Should get function complete int if Status and message, else  | 
 | 367 | 	 *	bus serv if only status  | 
 | 368 | 	 */ | 
 | 369 | 	if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) { | 
 | 370 | 		printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j); | 
 | 371 | 		result = DID_ERROR; | 
 | 372 | 	} | 
 | 373 | 	outb(0x12, qbase + 3);	/* done, disconnect */ | 
 | 374 | 	rtrc(1) | 
 | 375 | 	if ((k = ql_wai(priv))) | 
 | 376 | 		return (k << 16); | 
 | 377 |  | 
 | 378 | 	/* | 
 | 379 | 	 *	Should get bus service interrupt and disconnect interrupt  | 
 | 380 | 	 */ | 
 | 381 | 	  | 
 | 382 | 	i = inb(qbase + 5);	/* should be bus service */ | 
 | 383 | 	while (!priv->qabort && ((i & 0x20) != 0x20)) { | 
 | 384 | 		barrier(); | 
 | 385 | 		cpu_relax(); | 
 | 386 | 		i |= inb(qbase + 5); | 
 | 387 | 	} | 
 | 388 | 	rtrc(0) | 
 | 389 |  | 
 | 390 | 	if (priv->qabort) | 
 | 391 | 		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16); | 
 | 392 | 		 | 
 | 393 | 	return (result << 16) | (message << 8) | (status & STATUS_MASK); | 
 | 394 | } | 
 | 395 |  | 
 | 396 | /* | 
 | 397 |  *	Interrupt handler  | 
 | 398 |  */ | 
 | 399 |  | 
 | 400 | static void ql_ihandl(void *dev_id) | 
 | 401 | { | 
 | 402 | 	struct scsi_cmnd *icmd; | 
 | 403 | 	struct Scsi_Host *host = dev_id; | 
 | 404 | 	struct qlogicfas408_priv *priv = get_priv_by_host(host); | 
 | 405 | 	int qbase = priv->qbase; | 
 | 406 | 	REG0; | 
 | 407 |  | 
 | 408 | 	if (!(inb(qbase + 4) & 0x80))	/* false alarm? */ | 
 | 409 | 		return; | 
 | 410 |  | 
 | 411 | 	if (priv->qlcmd == NULL) {	/* no command to process? */ | 
 | 412 | 		int i; | 
 | 413 | 		i = 16; | 
 | 414 | 		while (i-- && inb(qbase + 5));	/* maybe also ql_zap() */ | 
 | 415 | 		return; | 
 | 416 | 	} | 
 | 417 | 	icmd = priv->qlcmd; | 
 | 418 | 	icmd->result = ql_pcmd(icmd); | 
 | 419 | 	priv->qlcmd = NULL; | 
 | 420 | 	/* | 
 | 421 | 	 *	If result is CHECK CONDITION done calls qcommand to request  | 
 | 422 | 	 *	sense  | 
 | 423 | 	 */ | 
 | 424 | 	(icmd->scsi_done) (icmd); | 
 | 425 | } | 
 | 426 |  | 
 | 427 | irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id) | 
 | 428 | { | 
 | 429 | 	unsigned long flags; | 
 | 430 | 	struct Scsi_Host *host = dev_id; | 
 | 431 |  | 
 | 432 | 	spin_lock_irqsave(host->host_lock, flags); | 
 | 433 | 	ql_ihandl(dev_id); | 
 | 434 | 	spin_unlock_irqrestore(host->host_lock, flags); | 
 | 435 | 	return IRQ_HANDLED; | 
 | 436 | } | 
 | 437 |  | 
 | 438 | /* | 
 | 439 |  *	Queued command | 
 | 440 |  */ | 
 | 441 |  | 
 | 442 | static int qlogicfas408_queuecommand_lck(struct scsi_cmnd *cmd, | 
 | 443 | 			      void (*done) (struct scsi_cmnd *)) | 
 | 444 | { | 
 | 445 | 	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); | 
 | 446 | 	if (scmd_id(cmd) == priv->qinitid) { | 
 | 447 | 		cmd->result = DID_BAD_TARGET << 16; | 
 | 448 | 		done(cmd); | 
 | 449 | 		return 0; | 
 | 450 | 	} | 
 | 451 |  | 
 | 452 | 	cmd->scsi_done = done; | 
 | 453 | 	/* wait for the last command's interrupt to finish */ | 
 | 454 | 	while (priv->qlcmd != NULL) { | 
 | 455 | 		barrier(); | 
 | 456 | 		cpu_relax(); | 
 | 457 | 	} | 
 | 458 | 	ql_icmd(cmd); | 
 | 459 | 	return 0; | 
 | 460 | } | 
 | 461 |  | 
 | 462 | DEF_SCSI_QCMD(qlogicfas408_queuecommand) | 
 | 463 |  | 
 | 464 | /*  | 
 | 465 |  *	Return bios parameters  | 
 | 466 |  */ | 
 | 467 |  | 
 | 468 | int qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev, | 
 | 469 | 			   sector_t capacity, int ip[]) | 
 | 470 | { | 
 | 471 | /* This should mimic the DOS Qlogic driver's behavior exactly */ | 
 | 472 | 	ip[0] = 0x40; | 
 | 473 | 	ip[1] = 0x20; | 
 | 474 | 	ip[2] = (unsigned long) capacity / (ip[0] * ip[1]); | 
 | 475 | 	if (ip[2] > 1024) { | 
 | 476 | 		ip[0] = 0xff; | 
 | 477 | 		ip[1] = 0x3f; | 
 | 478 | 		ip[2] = (unsigned long) capacity / (ip[0] * ip[1]); | 
 | 479 | #if 0 | 
 | 480 | 		if (ip[2] > 1023) | 
 | 481 | 			ip[2] = 1023; | 
 | 482 | #endif | 
 | 483 | 	} | 
 | 484 | 	return 0; | 
 | 485 | } | 
 | 486 |  | 
 | 487 | /* | 
 | 488 |  *	Abort a command in progress | 
 | 489 |  */ | 
 | 490 |   | 
 | 491 | int qlogicfas408_abort(struct scsi_cmnd *cmd) | 
 | 492 | { | 
 | 493 | 	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); | 
 | 494 | 	priv->qabort = 1; | 
 | 495 | 	ql_zap(priv); | 
 | 496 | 	return SUCCESS; | 
 | 497 | } | 
 | 498 |  | 
 | 499 | /* | 
 | 500 |  *	Reset SCSI bus | 
 | 501 |  *	FIXME: This function is invoked with cmd = NULL directly by | 
 | 502 |  *	the PCMCIA qlogic_stub code. This wants fixing | 
 | 503 |  */ | 
 | 504 |  | 
 | 505 | int qlogicfas408_host_reset(struct scsi_cmnd *cmd) | 
 | 506 | { | 
 | 507 | 	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); | 
 | 508 | 	unsigned long flags; | 
 | 509 |  | 
 | 510 | 	priv->qabort = 2; | 
 | 511 |  | 
 | 512 | 	spin_lock_irqsave(cmd->device->host->host_lock, flags); | 
 | 513 | 	ql_zap(priv); | 
 | 514 | 	spin_unlock_irqrestore(cmd->device->host->host_lock, flags); | 
 | 515 |  | 
 | 516 | 	return SUCCESS; | 
 | 517 | } | 
 | 518 |  | 
 | 519 | /* | 
 | 520 |  *	Return info string | 
 | 521 |  */ | 
 | 522 |  | 
 | 523 | const char *qlogicfas408_info(struct Scsi_Host *host) | 
 | 524 | { | 
 | 525 | 	struct qlogicfas408_priv *priv = get_priv_by_host(host); | 
 | 526 | 	return priv->qinfo; | 
 | 527 | } | 
 | 528 |  | 
 | 529 | /* | 
 | 530 |  *	Get type of chip | 
 | 531 |  */ | 
 | 532 |  | 
 | 533 | int qlogicfas408_get_chip_type(int qbase, int int_type) | 
 | 534 | { | 
 | 535 | 	REG1; | 
 | 536 | 	return inb(qbase + 0xe) & 0xf8; | 
 | 537 | } | 
 | 538 |  | 
 | 539 | /* | 
 | 540 |  *	Perform initialization tasks | 
 | 541 |  */ | 
 | 542 |  | 
 | 543 | void qlogicfas408_setup(int qbase, int id, int int_type) | 
 | 544 | { | 
 | 545 | 	outb(1, qbase + 8);	/* set for PIO pseudo DMA */ | 
 | 546 | 	REG0; | 
 | 547 | 	outb(0x40 | qlcfg8 | id, qbase + 8);	/* (ini) bus id, disable scsi rst */ | 
 | 548 | 	outb(qlcfg5, qbase + 5);	/* select timer */ | 
 | 549 | 	outb(qlcfg9, qbase + 9);	/* prescaler */ | 
 | 550 |  | 
 | 551 | #if QL_RESET_AT_START | 
 | 552 | 	outb(3, qbase + 3); | 
 | 553 |  | 
 | 554 | 	REG1; | 
 | 555 | 	/* FIXME: timeout */ | 
 | 556 | 	while (inb(qbase + 0xf) & 4) | 
 | 557 | 		cpu_relax(); | 
 | 558 |  | 
 | 559 | 	REG0; | 
 | 560 | #endif | 
 | 561 | } | 
 | 562 |  | 
 | 563 | /* | 
 | 564 |  *	Checks if this is a QLogic FAS 408 | 
 | 565 |  */ | 
 | 566 |  | 
 | 567 | int qlogicfas408_detect(int qbase, int int_type) | 
 | 568 | { | 
 | 569 |         REG1; | 
 | 570 | 	return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) && | 
 | 571 | 	       ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));		 | 
 | 572 | } | 
 | 573 |  | 
 | 574 | /* | 
 | 575 |  *	Disable interrupts | 
 | 576 |  */ | 
 | 577 |  | 
 | 578 | void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv) | 
 | 579 | { | 
 | 580 | 	int qbase = priv->qbase; | 
 | 581 | 	int int_type = priv->int_type; | 
 | 582 |  | 
 | 583 | 	REG1; | 
 | 584 | 	outb(0, qbase + 0xb);	/* disable ints */ | 
 | 585 | } | 
 | 586 |  | 
 | 587 | /* | 
 | 588 |  *	Init and exit functions | 
 | 589 |  */ | 
 | 590 |  | 
 | 591 | static int __init qlogicfas408_init(void) | 
 | 592 | { | 
 | 593 | 	return 0; | 
 | 594 | } | 
 | 595 |  | 
 | 596 | static void __exit qlogicfas408_exit(void) | 
 | 597 | { | 
 | 598 |  | 
 | 599 | } | 
 | 600 |  | 
 | 601 | MODULE_AUTHOR("Tom Zerucha, Michael Griffith"); | 
 | 602 | MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers"); | 
 | 603 | MODULE_LICENSE("GPL"); | 
 | 604 | module_init(qlogicfas408_init); | 
 | 605 | module_exit(qlogicfas408_exit); | 
 | 606 |  | 
 | 607 | EXPORT_SYMBOL(qlogicfas408_info); | 
 | 608 | EXPORT_SYMBOL(qlogicfas408_queuecommand); | 
 | 609 | EXPORT_SYMBOL(qlogicfas408_abort); | 
 | 610 | EXPORT_SYMBOL(qlogicfas408_host_reset); | 
 | 611 | EXPORT_SYMBOL(qlogicfas408_biosparam); | 
 | 612 | EXPORT_SYMBOL(qlogicfas408_ihandl); | 
 | 613 | EXPORT_SYMBOL(qlogicfas408_get_chip_type); | 
 | 614 | EXPORT_SYMBOL(qlogicfas408_setup); | 
 | 615 | EXPORT_SYMBOL(qlogicfas408_detect); | 
 | 616 | EXPORT_SYMBOL(qlogicfas408_disable_ints); | 
 | 617 |  |