162306a36Sopenharmony_ci/*----------------------------------------------------------------*/ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci Qlogic linux driver - work in progress. No Warranty express or implied. 462306a36Sopenharmony_ci Use at your own risk. Support Tort Reform so you won't have to read all 562306a36Sopenharmony_ci these silly disclaimers. 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Copyright 1994, Tom Zerucha. 862306a36Sopenharmony_ci tz@execpc.com 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci Additional Code, and much appreciated help by 1162306a36Sopenharmony_ci Michael A. Griffith 1262306a36Sopenharmony_ci grif@cs.ucr.edu 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA 1562306a36Sopenharmony_ci help respectively, and for suffering through my foolishness during the 1662306a36Sopenharmony_ci debugging process. 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994 1962306a36Sopenharmony_ci (you can reference it, but it is incomplete and inaccurate in places) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci Version 0.46 1/30/97 - kernel 1.2.0+ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci Functions as standalone, loadable, and PCMCIA driver, the latter from 2462306a36Sopenharmony_ci Dave Hinds' PCMCIA package. 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci Cleaned up 26/10/2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> as part of the 2.5 2762306a36Sopenharmony_ci SCSI driver cleanup and audit. This driver still needs work on the 2862306a36Sopenharmony_ci following 2962306a36Sopenharmony_ci - Non terminating hardware waits 3062306a36Sopenharmony_ci - Some layering violations with its pcmcia stub 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci Redistributable under terms of the GNU General Public License 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci For the avoidance of doubt the "preferred form" of this code is one which 3562306a36Sopenharmony_ci is in an open non patent encumbered format. Where cryptographic key signing 3662306a36Sopenharmony_ci forms part of the process of creating an executable the information 3762306a36Sopenharmony_ci including keys needed to generate an equivalently functional executable 3862306a36Sopenharmony_ci are deemed to be part of the source code. 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci*/ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <linux/module.h> 4362306a36Sopenharmony_ci#include <linux/blkdev.h> /* to get disk capacity */ 4462306a36Sopenharmony_ci#include <linux/kernel.h> 4562306a36Sopenharmony_ci#include <linux/string.h> 4662306a36Sopenharmony_ci#include <linux/init.h> 4762306a36Sopenharmony_ci#include <linux/interrupt.h> 4862306a36Sopenharmony_ci#include <linux/ioport.h> 4962306a36Sopenharmony_ci#include <linux/proc_fs.h> 5062306a36Sopenharmony_ci#include <linux/unistd.h> 5162306a36Sopenharmony_ci#include <linux/spinlock.h> 5262306a36Sopenharmony_ci#include <linux/stat.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include <asm/io.h> 5562306a36Sopenharmony_ci#include <asm/irq.h> 5662306a36Sopenharmony_ci#include <asm/dma.h> 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#include <scsi/scsi.h> 5962306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 6062306a36Sopenharmony_ci#include <scsi/scsi_device.h> 6162306a36Sopenharmony_ci#include <scsi/scsi_eh.h> 6262306a36Sopenharmony_ci#include <scsi/scsi_host.h> 6362306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 6462306a36Sopenharmony_ci#include "qlogicfas408.h" 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/*----------------------------------------------------------------*/ 6762306a36Sopenharmony_cistatic int qlcfg5 = (XTALFREQ << 5); /* 15625/512 */ 6862306a36Sopenharmony_cistatic int qlcfg6 = SYNCXFRPD; 6962306a36Sopenharmony_cistatic int qlcfg7 = SYNCOFFST; 7062306a36Sopenharmony_cistatic int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4); 7162306a36Sopenharmony_cistatic int qlcfg9 = ((XTALFREQ + 4) / 5); 7262306a36Sopenharmony_cistatic int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/*----------------------------------------------------------------*/ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/*----------------------------------------------------------------*/ 7762306a36Sopenharmony_ci/* local functions */ 7862306a36Sopenharmony_ci/*----------------------------------------------------------------*/ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* error recovery - reset everything */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void ql_zap(struct qlogicfas408_priv *priv) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci int x; 8562306a36Sopenharmony_ci int qbase = priv->qbase; 8662306a36Sopenharmony_ci int int_type = priv->int_type; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci x = inb(qbase + 0xd); 8962306a36Sopenharmony_ci REG0; 9062306a36Sopenharmony_ci outb(3, qbase + 3); /* reset SCSI */ 9162306a36Sopenharmony_ci outb(2, qbase + 3); /* reset chip */ 9262306a36Sopenharmony_ci if (x & 0x80) 9362306a36Sopenharmony_ci REG1; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * Do a pseudo-dma tranfer 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request, 10162306a36Sopenharmony_ci int reqlen) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int j; 10462306a36Sopenharmony_ci int qbase = priv->qbase; 10562306a36Sopenharmony_ci j = 0; 10662306a36Sopenharmony_ci if (phase & 1) { /* in */ 10762306a36Sopenharmony_ci#if QL_TURBO_PDMA 10862306a36Sopenharmony_ci rtrc(4) 10962306a36Sopenharmony_ci /* empty fifo in large chunks */ 11062306a36Sopenharmony_ci if (reqlen >= 128 && (inb(qbase + 8) & 2)) { /* full */ 11162306a36Sopenharmony_ci insl(qbase + 4, request, 32); 11262306a36Sopenharmony_ci reqlen -= 128; 11362306a36Sopenharmony_ci request += 128; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci while (reqlen >= 84 && !(j & 0xc0)) /* 2/3 */ 11662306a36Sopenharmony_ci if ((j = inb(qbase + 8)) & 4) 11762306a36Sopenharmony_ci { 11862306a36Sopenharmony_ci insl(qbase + 4, request, 21); 11962306a36Sopenharmony_ci reqlen -= 84; 12062306a36Sopenharmony_ci request += 84; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci if (reqlen >= 44 && (inb(qbase + 8) & 8)) { /* 1/3 */ 12362306a36Sopenharmony_ci insl(qbase + 4, request, 11); 12462306a36Sopenharmony_ci reqlen -= 44; 12562306a36Sopenharmony_ci request += 44; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci#endif 12862306a36Sopenharmony_ci /* until both empty and int (or until reclen is 0) */ 12962306a36Sopenharmony_ci rtrc(7) 13062306a36Sopenharmony_ci j = 0; 13162306a36Sopenharmony_ci while (reqlen && !((j & 0x10) && (j & 0xc0))) 13262306a36Sopenharmony_ci { 13362306a36Sopenharmony_ci /* while bytes to receive and not empty */ 13462306a36Sopenharmony_ci j &= 0xc0; 13562306a36Sopenharmony_ci while (reqlen && !((j = inb(qbase + 8)) & 0x10)) 13662306a36Sopenharmony_ci { 13762306a36Sopenharmony_ci *request++ = inb(qbase + 4); 13862306a36Sopenharmony_ci reqlen--; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci if (j & 0x10) 14162306a36Sopenharmony_ci j = inb(qbase + 8); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } else { /* out */ 14562306a36Sopenharmony_ci#if QL_TURBO_PDMA 14662306a36Sopenharmony_ci rtrc(4) 14762306a36Sopenharmony_ci if (reqlen >= 128 && inb(qbase + 8) & 0x10) { /* empty */ 14862306a36Sopenharmony_ci outsl(qbase + 4, request, 32); 14962306a36Sopenharmony_ci reqlen -= 128; 15062306a36Sopenharmony_ci request += 128; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci while (reqlen >= 84 && !(j & 0xc0)) /* 1/3 */ 15362306a36Sopenharmony_ci if (!((j = inb(qbase + 8)) & 8)) { 15462306a36Sopenharmony_ci outsl(qbase + 4, request, 21); 15562306a36Sopenharmony_ci reqlen -= 84; 15662306a36Sopenharmony_ci request += 84; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci if (reqlen >= 40 && !(inb(qbase + 8) & 4)) { /* 2/3 */ 15962306a36Sopenharmony_ci outsl(qbase + 4, request, 10); 16062306a36Sopenharmony_ci reqlen -= 40; 16162306a36Sopenharmony_ci request += 40; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci#endif 16462306a36Sopenharmony_ci /* until full and int (or until reclen is 0) */ 16562306a36Sopenharmony_ci rtrc(7) 16662306a36Sopenharmony_ci j = 0; 16762306a36Sopenharmony_ci while (reqlen && !((j & 2) && (j & 0xc0))) { 16862306a36Sopenharmony_ci /* while bytes to send and not full */ 16962306a36Sopenharmony_ci while (reqlen && !((j = inb(qbase + 8)) & 2)) 17062306a36Sopenharmony_ci { 17162306a36Sopenharmony_ci outb(*request++, qbase + 4); 17262306a36Sopenharmony_ci reqlen--; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci if (j & 2) 17562306a36Sopenharmony_ci j = inb(qbase + 8); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci /* maybe return reqlen */ 17962306a36Sopenharmony_ci return inb(qbase + 8) & 0xc0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * Wait for interrupt flag (polled - not real hardware interrupt) 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int ql_wai(struct qlogicfas408_priv *priv) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int k; 18962306a36Sopenharmony_ci int qbase = priv->qbase; 19062306a36Sopenharmony_ci unsigned long i; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci k = 0; 19362306a36Sopenharmony_ci i = jiffies + WATCHDOG; 19462306a36Sopenharmony_ci while (time_before(jiffies, i) && !priv->qabort && 19562306a36Sopenharmony_ci !((k = inb(qbase + 4)) & 0xe0)) { 19662306a36Sopenharmony_ci barrier(); 19762306a36Sopenharmony_ci cpu_relax(); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci if (time_after_eq(jiffies, i)) 20062306a36Sopenharmony_ci return (DID_TIME_OUT); 20162306a36Sopenharmony_ci if (priv->qabort) 20262306a36Sopenharmony_ci return (priv->qabort == 1 ? DID_ABORT : DID_RESET); 20362306a36Sopenharmony_ci if (k & 0x60) 20462306a36Sopenharmony_ci ql_zap(priv); 20562306a36Sopenharmony_ci if (k & 0x20) 20662306a36Sopenharmony_ci return (DID_PARITY); 20762306a36Sopenharmony_ci if (k & 0x40) 20862306a36Sopenharmony_ci return (DID_ERROR); 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/* 21362306a36Sopenharmony_ci * Initiate scsi command - queueing handler 21462306a36Sopenharmony_ci * caller must hold host lock 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void ql_icmd(struct scsi_cmnd *cmd) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 22062306a36Sopenharmony_ci int qbase = priv->qbase; 22162306a36Sopenharmony_ci int int_type = priv->int_type; 22262306a36Sopenharmony_ci unsigned int i; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci priv->qabort = 0; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci REG0; 22762306a36Sopenharmony_ci /* clearing of interrupts and the fifo is needed */ 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci inb(qbase + 5); /* clear interrupts */ 23062306a36Sopenharmony_ci if (inb(qbase + 5)) /* if still interrupting */ 23162306a36Sopenharmony_ci outb(2, qbase + 3); /* reset chip */ 23262306a36Sopenharmony_ci else if (inb(qbase + 7) & 0x1f) 23362306a36Sopenharmony_ci outb(1, qbase + 3); /* clear fifo */ 23462306a36Sopenharmony_ci while (inb(qbase + 5)); /* clear ints */ 23562306a36Sopenharmony_ci REG1; 23662306a36Sopenharmony_ci outb(1, qbase + 8); /* set for PIO pseudo DMA */ 23762306a36Sopenharmony_ci outb(0, qbase + 0xb); /* disable ints */ 23862306a36Sopenharmony_ci inb(qbase + 8); /* clear int bits */ 23962306a36Sopenharmony_ci REG0; 24062306a36Sopenharmony_ci outb(0x40, qbase + 0xb); /* enable features */ 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* configurables */ 24362306a36Sopenharmony_ci outb(qlcfgc, qbase + 0xc); 24462306a36Sopenharmony_ci /* config: no reset interrupt, (initiator) bus id */ 24562306a36Sopenharmony_ci outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8); 24662306a36Sopenharmony_ci outb(qlcfg7, qbase + 7); 24762306a36Sopenharmony_ci outb(qlcfg6, qbase + 6); 24862306a36Sopenharmony_ci outb(qlcfg5, qbase + 5); /* select timer */ 24962306a36Sopenharmony_ci outb(qlcfg9 & 7, qbase + 9); /* prescaler */ 25062306a36Sopenharmony_ci/* outb(0x99, qbase + 5); */ 25162306a36Sopenharmony_ci outb(scmd_id(cmd), qbase + 4); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (i = 0; i < cmd->cmd_len; i++) 25462306a36Sopenharmony_ci outb(cmd->cmnd[i], qbase + 2); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci priv->qlcmd = cmd; 25762306a36Sopenharmony_ci outb(0x41, qbase + 3); /* select and send command */ 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* 26162306a36Sopenharmony_ci * Process scsi command - usually after interrupt 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void ql_pcmd(struct scsi_cmnd *cmd) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci unsigned int i, j; 26762306a36Sopenharmony_ci unsigned long k; 26862306a36Sopenharmony_ci unsigned int status; /* scsi returned status */ 26962306a36Sopenharmony_ci unsigned int message; /* scsi returned message */ 27062306a36Sopenharmony_ci unsigned int phase; /* recorded scsi phase */ 27162306a36Sopenharmony_ci unsigned int reqlen; /* total length of transfer */ 27262306a36Sopenharmony_ci char *buf; 27362306a36Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 27462306a36Sopenharmony_ci int qbase = priv->qbase; 27562306a36Sopenharmony_ci int int_type = priv->int_type; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci rtrc(1) 27862306a36Sopenharmony_ci j = inb(qbase + 6); 27962306a36Sopenharmony_ci i = inb(qbase + 5); 28062306a36Sopenharmony_ci if (i == 0x20) { 28162306a36Sopenharmony_ci set_host_byte(cmd, DID_NO_CONNECT); 28262306a36Sopenharmony_ci return; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */ 28562306a36Sopenharmony_ci if (i != 0x18) { 28662306a36Sopenharmony_ci printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i); 28762306a36Sopenharmony_ci ql_zap(priv); 28862306a36Sopenharmony_ci set_host_byte(cmd, DID_BAD_INTR); 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci j &= 7; /* j = inb( qbase + 7 ) >> 5; */ 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* correct status is supposed to be step 4 */ 29462306a36Sopenharmony_ci /* it sometimes returns step 3 but with 0 bytes left to send */ 29562306a36Sopenharmony_ci /* We can try stuffing the FIFO with the max each time, but we will get a 29662306a36Sopenharmony_ci sequence of 3 if any bytes are left (but we do flush the FIFO anyway */ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (j != 3 && j != 4) { 29962306a36Sopenharmony_ci printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n", 30062306a36Sopenharmony_ci j, i, inb(qbase + 7) & 0x1f); 30162306a36Sopenharmony_ci ql_zap(priv); 30262306a36Sopenharmony_ci set_host_byte(cmd, DID_ERROR); 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (inb(qbase + 7) & 0x1f) /* if some bytes in fifo */ 30762306a36Sopenharmony_ci outb(1, qbase + 3); /* clear fifo */ 30862306a36Sopenharmony_ci /* note that request_bufflen is the total xfer size when sg is used */ 30962306a36Sopenharmony_ci reqlen = scsi_bufflen(cmd); 31062306a36Sopenharmony_ci /* note that it won't work if transfers > 16M are requested */ 31162306a36Sopenharmony_ci if (reqlen && !((phase = inb(qbase + 4)) & 6)) { /* data phase */ 31262306a36Sopenharmony_ci struct scatterlist *sg; 31362306a36Sopenharmony_ci rtrc(2) 31462306a36Sopenharmony_ci outb(reqlen, qbase); /* low-mid xfer cnt */ 31562306a36Sopenharmony_ci outb(reqlen >> 8, qbase + 1); /* low-mid xfer cnt */ 31662306a36Sopenharmony_ci outb(reqlen >> 16, qbase + 0xe); /* high xfer cnt */ 31762306a36Sopenharmony_ci outb(0x90, qbase + 3); /* command do xfer */ 31862306a36Sopenharmony_ci /* PIO pseudo DMA to buffer or sglist */ 31962306a36Sopenharmony_ci REG1; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) { 32262306a36Sopenharmony_ci if (priv->qabort) { 32362306a36Sopenharmony_ci REG0; 32462306a36Sopenharmony_ci set_host_byte(cmd, 32562306a36Sopenharmony_ci priv->qabort == 1 ? 32662306a36Sopenharmony_ci DID_ABORT : DID_RESET); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci buf = sg_virt(sg); 32962306a36Sopenharmony_ci if (ql_pdma(priv, phase, buf, sg->length)) 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci REG0; 33362306a36Sopenharmony_ci rtrc(2); 33462306a36Sopenharmony_ci /* 33562306a36Sopenharmony_ci * Wait for irq (split into second state of irq handler 33662306a36Sopenharmony_ci * if this can take time) 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci if ((k = ql_wai(priv))) { 33962306a36Sopenharmony_ci set_host_byte(cmd, k); 34062306a36Sopenharmony_ci return; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci k = inb(qbase + 5); /* should be 0x10, bus service */ 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * Enter Status (and Message In) Phase 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci k = jiffies + WATCHDOG; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci while (time_before(jiffies, k) && !priv->qabort && 35262306a36Sopenharmony_ci !(inb(qbase + 4) & 6)) 35362306a36Sopenharmony_ci cpu_relax(); /* wait for status phase */ 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (time_after_eq(jiffies, k)) { 35662306a36Sopenharmony_ci ql_zap(priv); 35762306a36Sopenharmony_ci set_host_byte(cmd, DID_TIME_OUT); 35862306a36Sopenharmony_ci return; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* FIXME: timeout ?? */ 36262306a36Sopenharmony_ci while (inb(qbase + 5)) 36362306a36Sopenharmony_ci cpu_relax(); /* clear pending ints */ 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (priv->qabort) { 36662306a36Sopenharmony_ci set_host_byte(cmd, 36762306a36Sopenharmony_ci priv->qabort == 1 ? DID_ABORT : DID_RESET); 36862306a36Sopenharmony_ci return; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci outb(0x11, qbase + 3); /* get status and message */ 37262306a36Sopenharmony_ci if ((k = ql_wai(priv))) { 37362306a36Sopenharmony_ci set_host_byte(cmd, k); 37462306a36Sopenharmony_ci return; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci i = inb(qbase + 5); /* get chip irq stat */ 37762306a36Sopenharmony_ci j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */ 37862306a36Sopenharmony_ci status = inb(qbase + 2); 37962306a36Sopenharmony_ci message = inb(qbase + 2); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * Should get function complete int if Status and message, else 38362306a36Sopenharmony_ci * bus serv if only status 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) { 38662306a36Sopenharmony_ci printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j); 38762306a36Sopenharmony_ci set_host_byte(cmd, DID_ERROR); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci outb(0x12, qbase + 3); /* done, disconnect */ 39062306a36Sopenharmony_ci rtrc(1); 39162306a36Sopenharmony_ci if ((k = ql_wai(priv))) { 39262306a36Sopenharmony_ci set_host_byte(cmd, k); 39362306a36Sopenharmony_ci return; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* 39762306a36Sopenharmony_ci * Should get bus service interrupt and disconnect interrupt 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci i = inb(qbase + 5); /* should be bus service */ 40162306a36Sopenharmony_ci while (!priv->qabort && ((i & 0x20) != 0x20)) { 40262306a36Sopenharmony_ci barrier(); 40362306a36Sopenharmony_ci cpu_relax(); 40462306a36Sopenharmony_ci i |= inb(qbase + 5); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci rtrc(0); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (priv->qabort) { 40962306a36Sopenharmony_ci set_host_byte(cmd, 41062306a36Sopenharmony_ci priv->qabort == 1 ? DID_ABORT : DID_RESET); 41162306a36Sopenharmony_ci return; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci set_host_byte(cmd, DID_OK); 41562306a36Sopenharmony_ci if (message != COMMAND_COMPLETE) 41662306a36Sopenharmony_ci scsi_msg_to_host_byte(cmd, message); 41762306a36Sopenharmony_ci set_status_byte(cmd, status); 41862306a36Sopenharmony_ci return; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* 42262306a36Sopenharmony_ci * Interrupt handler 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void ql_ihandl(void *dev_id) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct scsi_cmnd *icmd; 42862306a36Sopenharmony_ci struct Scsi_Host *host = dev_id; 42962306a36Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_host(host); 43062306a36Sopenharmony_ci int qbase = priv->qbase; 43162306a36Sopenharmony_ci REG0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (!(inb(qbase + 4) & 0x80)) /* false alarm? */ 43462306a36Sopenharmony_ci return; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (priv->qlcmd == NULL) { /* no command to process? */ 43762306a36Sopenharmony_ci int i; 43862306a36Sopenharmony_ci i = 16; 43962306a36Sopenharmony_ci while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */ 44062306a36Sopenharmony_ci return; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci icmd = priv->qlcmd; 44362306a36Sopenharmony_ci ql_pcmd(icmd); 44462306a36Sopenharmony_ci priv->qlcmd = NULL; 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * If result is CHECK CONDITION done calls qcommand to request 44762306a36Sopenharmony_ci * sense 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci scsi_done(icmd); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ciirqreturn_t qlogicfas408_ihandl(int irq, void *dev_id) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci unsigned long flags; 45562306a36Sopenharmony_ci struct Scsi_Host *host = dev_id; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci spin_lock_irqsave(host->host_lock, flags); 45862306a36Sopenharmony_ci ql_ihandl(dev_id); 45962306a36Sopenharmony_ci spin_unlock_irqrestore(host->host_lock, flags); 46062306a36Sopenharmony_ci return IRQ_HANDLED; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/* 46462306a36Sopenharmony_ci * Queued command 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int qlogicfas408_queuecommand_lck(struct scsi_cmnd *cmd) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci void (*done)(struct scsi_cmnd *) = scsi_done; 47062306a36Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci set_host_byte(cmd, DID_OK); 47362306a36Sopenharmony_ci set_status_byte(cmd, SAM_STAT_GOOD); 47462306a36Sopenharmony_ci if (scmd_id(cmd) == priv->qinitid) { 47562306a36Sopenharmony_ci set_host_byte(cmd, DID_BAD_TARGET); 47662306a36Sopenharmony_ci done(cmd); 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* wait for the last command's interrupt to finish */ 48162306a36Sopenharmony_ci while (priv->qlcmd != NULL) { 48262306a36Sopenharmony_ci barrier(); 48362306a36Sopenharmony_ci cpu_relax(); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci ql_icmd(cmd); 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ciDEF_SCSI_QCMD(qlogicfas408_queuecommand) 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/* 49262306a36Sopenharmony_ci * Return bios parameters 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ciint qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev, 49662306a36Sopenharmony_ci sector_t capacity, int ip[]) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci/* This should mimic the DOS Qlogic driver's behavior exactly */ 49962306a36Sopenharmony_ci ip[0] = 0x40; 50062306a36Sopenharmony_ci ip[1] = 0x20; 50162306a36Sopenharmony_ci ip[2] = (unsigned long) capacity / (ip[0] * ip[1]); 50262306a36Sopenharmony_ci if (ip[2] > 1024) { 50362306a36Sopenharmony_ci ip[0] = 0xff; 50462306a36Sopenharmony_ci ip[1] = 0x3f; 50562306a36Sopenharmony_ci ip[2] = (unsigned long) capacity / (ip[0] * ip[1]); 50662306a36Sopenharmony_ci#if 0 50762306a36Sopenharmony_ci if (ip[2] > 1023) 50862306a36Sopenharmony_ci ip[2] = 1023; 50962306a36Sopenharmony_ci#endif 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/* 51562306a36Sopenharmony_ci * Abort a command in progress 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ciint qlogicfas408_abort(struct scsi_cmnd *cmd) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 52162306a36Sopenharmony_ci priv->qabort = 1; 52262306a36Sopenharmony_ci ql_zap(priv); 52362306a36Sopenharmony_ci return SUCCESS; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/* 52762306a36Sopenharmony_ci * Reset SCSI bus 52862306a36Sopenharmony_ci * FIXME: This function is invoked with cmd = NULL directly by 52962306a36Sopenharmony_ci * the PCMCIA qlogic_stub code. This wants fixing 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ciint qlogicfas408_host_reset(struct scsi_cmnd *cmd) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 53562306a36Sopenharmony_ci unsigned long flags; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci priv->qabort = 2; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci spin_lock_irqsave(cmd->device->host->host_lock, flags); 54062306a36Sopenharmony_ci ql_zap(priv); 54162306a36Sopenharmony_ci spin_unlock_irqrestore(cmd->device->host->host_lock, flags); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return SUCCESS; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/* 54762306a36Sopenharmony_ci * Return info string 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ciconst char *qlogicfas408_info(struct Scsi_Host *host) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_host(host); 55362306a36Sopenharmony_ci return priv->qinfo; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/* 55762306a36Sopenharmony_ci * Get type of chip 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ciint qlogicfas408_get_chip_type(int qbase, int int_type) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci REG1; 56362306a36Sopenharmony_ci return inb(qbase + 0xe) & 0xf8; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci/* 56762306a36Sopenharmony_ci * Perform initialization tasks 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_civoid qlogicfas408_setup(int qbase, int id, int int_type) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci outb(1, qbase + 8); /* set for PIO pseudo DMA */ 57362306a36Sopenharmony_ci REG0; 57462306a36Sopenharmony_ci outb(0x40 | qlcfg8 | id, qbase + 8); /* (ini) bus id, disable scsi rst */ 57562306a36Sopenharmony_ci outb(qlcfg5, qbase + 5); /* select timer */ 57662306a36Sopenharmony_ci outb(qlcfg9, qbase + 9); /* prescaler */ 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci#if QL_RESET_AT_START 57962306a36Sopenharmony_ci outb(3, qbase + 3); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci REG1; 58262306a36Sopenharmony_ci /* FIXME: timeout */ 58362306a36Sopenharmony_ci while (inb(qbase + 0xf) & 4) 58462306a36Sopenharmony_ci cpu_relax(); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci REG0; 58762306a36Sopenharmony_ci#endif 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/* 59162306a36Sopenharmony_ci * Checks if this is a QLogic FAS 408 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ciint qlogicfas408_detect(int qbase, int int_type) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci REG1; 59762306a36Sopenharmony_ci return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) && 59862306a36Sopenharmony_ci ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7)); 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* 60262306a36Sopenharmony_ci * Disable interrupts 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_civoid qlogicfas408_disable_ints(struct qlogicfas408_priv *priv) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci int qbase = priv->qbase; 60862306a36Sopenharmony_ci int int_type = priv->int_type; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci REG1; 61162306a36Sopenharmony_ci outb(0, qbase + 0xb); /* disable ints */ 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci/* 61562306a36Sopenharmony_ci * Init and exit functions 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic int __init qlogicfas408_init(void) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void __exit qlogicfas408_exit(void) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ciMODULE_AUTHOR("Tom Zerucha, Michael Griffith"); 62962306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers"); 63062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 63162306a36Sopenharmony_cimodule_init(qlogicfas408_init); 63262306a36Sopenharmony_cimodule_exit(qlogicfas408_exit); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_info); 63562306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_queuecommand); 63662306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_abort); 63762306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_host_reset); 63862306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_biosparam); 63962306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_ihandl); 64062306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_get_chip_type); 64162306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_setup); 64262306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_detect); 64362306a36Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_disable_ints); 64462306a36Sopenharmony_ci 645