18c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Qlogic linux driver - work in progress. No Warranty express or implied. 48c2ecf20Sopenharmony_ci Use at your own risk. Support Tort Reform so you won't have to read all 58c2ecf20Sopenharmony_ci these silly disclaimers. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci Copyright 1994, Tom Zerucha. 88c2ecf20Sopenharmony_ci tz@execpc.com 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci Additional Code, and much appreciated help by 118c2ecf20Sopenharmony_ci Michael A. Griffith 128c2ecf20Sopenharmony_ci grif@cs.ucr.edu 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA 158c2ecf20Sopenharmony_ci help respectively, and for suffering through my foolishness during the 168c2ecf20Sopenharmony_ci debugging process. 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994 198c2ecf20Sopenharmony_ci (you can reference it, but it is incomplete and inaccurate in places) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci Version 0.46 1/30/97 - kernel 1.2.0+ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci Functions as standalone, loadable, and PCMCIA driver, the latter from 248c2ecf20Sopenharmony_ci Dave Hinds' PCMCIA package. 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci Cleaned up 26/10/2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> as part of the 2.5 278c2ecf20Sopenharmony_ci SCSI driver cleanup and audit. This driver still needs work on the 288c2ecf20Sopenharmony_ci following 298c2ecf20Sopenharmony_ci - Non terminating hardware waits 308c2ecf20Sopenharmony_ci - Some layering violations with its pcmcia stub 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci Redistributable under terms of the GNU General Public License 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci For the avoidance of doubt the "preferred form" of this code is one which 358c2ecf20Sopenharmony_ci is in an open non patent encumbered format. Where cryptographic key signing 368c2ecf20Sopenharmony_ci forms part of the process of creating an executable the information 378c2ecf20Sopenharmony_ci including keys needed to generate an equivalently functional executable 388c2ecf20Sopenharmony_ci are deemed to be part of the source code. 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci*/ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <linux/module.h> 438c2ecf20Sopenharmony_ci#include <linux/blkdev.h> /* to get disk capacity */ 448c2ecf20Sopenharmony_ci#include <linux/kernel.h> 458c2ecf20Sopenharmony_ci#include <linux/string.h> 468c2ecf20Sopenharmony_ci#include <linux/init.h> 478c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 488c2ecf20Sopenharmony_ci#include <linux/ioport.h> 498c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 508c2ecf20Sopenharmony_ci#include <linux/unistd.h> 518c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 528c2ecf20Sopenharmony_ci#include <linux/stat.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include <asm/io.h> 558c2ecf20Sopenharmony_ci#include <asm/irq.h> 568c2ecf20Sopenharmony_ci#include <asm/dma.h> 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#include "scsi.h" 598c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 608c2ecf20Sopenharmony_ci#include "qlogicfas408.h" 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/ 638c2ecf20Sopenharmony_cistatic int qlcfg5 = (XTALFREQ << 5); /* 15625/512 */ 648c2ecf20Sopenharmony_cistatic int qlcfg6 = SYNCXFRPD; 658c2ecf20Sopenharmony_cistatic int qlcfg7 = SYNCOFFST; 668c2ecf20Sopenharmony_cistatic int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4); 678c2ecf20Sopenharmony_cistatic int qlcfg9 = ((XTALFREQ + 4) / 5); 688c2ecf20Sopenharmony_cistatic int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/ 738c2ecf20Sopenharmony_ci/* local functions */ 748c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* error recovery - reset everything */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void ql_zap(struct qlogicfas408_priv *priv) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int x; 818c2ecf20Sopenharmony_ci int qbase = priv->qbase; 828c2ecf20Sopenharmony_ci int int_type = priv->int_type; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci x = inb(qbase + 0xd); 858c2ecf20Sopenharmony_ci REG0; 868c2ecf20Sopenharmony_ci outb(3, qbase + 3); /* reset SCSI */ 878c2ecf20Sopenharmony_ci outb(2, qbase + 3); /* reset chip */ 888c2ecf20Sopenharmony_ci if (x & 0x80) 898c2ecf20Sopenharmony_ci REG1; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * Do a pseudo-dma tranfer 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request, int reqlen) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int j; 998c2ecf20Sopenharmony_ci int qbase = priv->qbase; 1008c2ecf20Sopenharmony_ci j = 0; 1018c2ecf20Sopenharmony_ci if (phase & 1) { /* in */ 1028c2ecf20Sopenharmony_ci#if QL_TURBO_PDMA 1038c2ecf20Sopenharmony_ci rtrc(4) 1048c2ecf20Sopenharmony_ci /* empty fifo in large chunks */ 1058c2ecf20Sopenharmony_ci if (reqlen >= 128 && (inb(qbase + 8) & 2)) { /* full */ 1068c2ecf20Sopenharmony_ci insl(qbase + 4, request, 32); 1078c2ecf20Sopenharmony_ci reqlen -= 128; 1088c2ecf20Sopenharmony_ci request += 128; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci while (reqlen >= 84 && !(j & 0xc0)) /* 2/3 */ 1118c2ecf20Sopenharmony_ci if ((j = inb(qbase + 8)) & 4) 1128c2ecf20Sopenharmony_ci { 1138c2ecf20Sopenharmony_ci insl(qbase + 4, request, 21); 1148c2ecf20Sopenharmony_ci reqlen -= 84; 1158c2ecf20Sopenharmony_ci request += 84; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci if (reqlen >= 44 && (inb(qbase + 8) & 8)) { /* 1/3 */ 1188c2ecf20Sopenharmony_ci insl(qbase + 4, request, 11); 1198c2ecf20Sopenharmony_ci reqlen -= 44; 1208c2ecf20Sopenharmony_ci request += 44; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci#endif 1238c2ecf20Sopenharmony_ci /* until both empty and int (or until reclen is 0) */ 1248c2ecf20Sopenharmony_ci rtrc(7) 1258c2ecf20Sopenharmony_ci j = 0; 1268c2ecf20Sopenharmony_ci while (reqlen && !((j & 0x10) && (j & 0xc0))) 1278c2ecf20Sopenharmony_ci { 1288c2ecf20Sopenharmony_ci /* while bytes to receive and not empty */ 1298c2ecf20Sopenharmony_ci j &= 0xc0; 1308c2ecf20Sopenharmony_ci while (reqlen && !((j = inb(qbase + 8)) & 0x10)) 1318c2ecf20Sopenharmony_ci { 1328c2ecf20Sopenharmony_ci *request++ = inb(qbase + 4); 1338c2ecf20Sopenharmony_ci reqlen--; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci if (j & 0x10) 1368c2ecf20Sopenharmony_ci j = inb(qbase + 8); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci } else { /* out */ 1408c2ecf20Sopenharmony_ci#if QL_TURBO_PDMA 1418c2ecf20Sopenharmony_ci rtrc(4) 1428c2ecf20Sopenharmony_ci if (reqlen >= 128 && inb(qbase + 8) & 0x10) { /* empty */ 1438c2ecf20Sopenharmony_ci outsl(qbase + 4, request, 32); 1448c2ecf20Sopenharmony_ci reqlen -= 128; 1458c2ecf20Sopenharmony_ci request += 128; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci while (reqlen >= 84 && !(j & 0xc0)) /* 1/3 */ 1488c2ecf20Sopenharmony_ci if (!((j = inb(qbase + 8)) & 8)) { 1498c2ecf20Sopenharmony_ci outsl(qbase + 4, request, 21); 1508c2ecf20Sopenharmony_ci reqlen -= 84; 1518c2ecf20Sopenharmony_ci request += 84; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci if (reqlen >= 40 && !(inb(qbase + 8) & 4)) { /* 2/3 */ 1548c2ecf20Sopenharmony_ci outsl(qbase + 4, request, 10); 1558c2ecf20Sopenharmony_ci reqlen -= 40; 1568c2ecf20Sopenharmony_ci request += 40; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci#endif 1598c2ecf20Sopenharmony_ci /* until full and int (or until reclen is 0) */ 1608c2ecf20Sopenharmony_ci rtrc(7) 1618c2ecf20Sopenharmony_ci j = 0; 1628c2ecf20Sopenharmony_ci while (reqlen && !((j & 2) && (j & 0xc0))) { 1638c2ecf20Sopenharmony_ci /* while bytes to send and not full */ 1648c2ecf20Sopenharmony_ci while (reqlen && !((j = inb(qbase + 8)) & 2)) 1658c2ecf20Sopenharmony_ci { 1668c2ecf20Sopenharmony_ci outb(*request++, qbase + 4); 1678c2ecf20Sopenharmony_ci reqlen--; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci if (j & 2) 1708c2ecf20Sopenharmony_ci j = inb(qbase + 8); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci /* maybe return reqlen */ 1748c2ecf20Sopenharmony_ci return inb(qbase + 8) & 0xc0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* 1788c2ecf20Sopenharmony_ci * Wait for interrupt flag (polled - not real hardware interrupt) 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int ql_wai(struct qlogicfas408_priv *priv) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci int k; 1848c2ecf20Sopenharmony_ci int qbase = priv->qbase; 1858c2ecf20Sopenharmony_ci unsigned long i; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci k = 0; 1888c2ecf20Sopenharmony_ci i = jiffies + WATCHDOG; 1898c2ecf20Sopenharmony_ci while (time_before(jiffies, i) && !priv->qabort && 1908c2ecf20Sopenharmony_ci !((k = inb(qbase + 4)) & 0xe0)) { 1918c2ecf20Sopenharmony_ci barrier(); 1928c2ecf20Sopenharmony_ci cpu_relax(); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, i)) 1958c2ecf20Sopenharmony_ci return (DID_TIME_OUT); 1968c2ecf20Sopenharmony_ci if (priv->qabort) 1978c2ecf20Sopenharmony_ci return (priv->qabort == 1 ? DID_ABORT : DID_RESET); 1988c2ecf20Sopenharmony_ci if (k & 0x60) 1998c2ecf20Sopenharmony_ci ql_zap(priv); 2008c2ecf20Sopenharmony_ci if (k & 0x20) 2018c2ecf20Sopenharmony_ci return (DID_PARITY); 2028c2ecf20Sopenharmony_ci if (k & 0x40) 2038c2ecf20Sopenharmony_ci return (DID_ERROR); 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* 2088c2ecf20Sopenharmony_ci * Initiate scsi command - queueing handler 2098c2ecf20Sopenharmony_ci * caller must hold host lock 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void ql_icmd(struct scsi_cmnd *cmd) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 2158c2ecf20Sopenharmony_ci int qbase = priv->qbase; 2168c2ecf20Sopenharmony_ci int int_type = priv->int_type; 2178c2ecf20Sopenharmony_ci unsigned int i; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci priv->qabort = 0; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci REG0; 2228c2ecf20Sopenharmony_ci /* clearing of interrupts and the fifo is needed */ 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci inb(qbase + 5); /* clear interrupts */ 2258c2ecf20Sopenharmony_ci if (inb(qbase + 5)) /* if still interrupting */ 2268c2ecf20Sopenharmony_ci outb(2, qbase + 3); /* reset chip */ 2278c2ecf20Sopenharmony_ci else if (inb(qbase + 7) & 0x1f) 2288c2ecf20Sopenharmony_ci outb(1, qbase + 3); /* clear fifo */ 2298c2ecf20Sopenharmony_ci while (inb(qbase + 5)); /* clear ints */ 2308c2ecf20Sopenharmony_ci REG1; 2318c2ecf20Sopenharmony_ci outb(1, qbase + 8); /* set for PIO pseudo DMA */ 2328c2ecf20Sopenharmony_ci outb(0, qbase + 0xb); /* disable ints */ 2338c2ecf20Sopenharmony_ci inb(qbase + 8); /* clear int bits */ 2348c2ecf20Sopenharmony_ci REG0; 2358c2ecf20Sopenharmony_ci outb(0x40, qbase + 0xb); /* enable features */ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* configurables */ 2388c2ecf20Sopenharmony_ci outb(qlcfgc, qbase + 0xc); 2398c2ecf20Sopenharmony_ci /* config: no reset interrupt, (initiator) bus id */ 2408c2ecf20Sopenharmony_ci outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8); 2418c2ecf20Sopenharmony_ci outb(qlcfg7, qbase + 7); 2428c2ecf20Sopenharmony_ci outb(qlcfg6, qbase + 6); 2438c2ecf20Sopenharmony_ci outb(qlcfg5, qbase + 5); /* select timer */ 2448c2ecf20Sopenharmony_ci outb(qlcfg9 & 7, qbase + 9); /* prescaler */ 2458c2ecf20Sopenharmony_ci/* outb(0x99, qbase + 5); */ 2468c2ecf20Sopenharmony_ci outb(scmd_id(cmd), qbase + 4); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci for (i = 0; i < cmd->cmd_len; i++) 2498c2ecf20Sopenharmony_ci outb(cmd->cmnd[i], qbase + 2); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci priv->qlcmd = cmd; 2528c2ecf20Sopenharmony_ci outb(0x41, qbase + 3); /* select and send command */ 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* 2568c2ecf20Sopenharmony_ci * Process scsi command - usually after interrupt 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic unsigned int ql_pcmd(struct scsi_cmnd *cmd) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci unsigned int i, j; 2628c2ecf20Sopenharmony_ci unsigned long k; 2638c2ecf20Sopenharmony_ci unsigned int result; /* ultimate return result */ 2648c2ecf20Sopenharmony_ci unsigned int status; /* scsi returned status */ 2658c2ecf20Sopenharmony_ci unsigned int message; /* scsi returned message */ 2668c2ecf20Sopenharmony_ci unsigned int phase; /* recorded scsi phase */ 2678c2ecf20Sopenharmony_ci unsigned int reqlen; /* total length of transfer */ 2688c2ecf20Sopenharmony_ci char *buf; 2698c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 2708c2ecf20Sopenharmony_ci int qbase = priv->qbase; 2718c2ecf20Sopenharmony_ci int int_type = priv->int_type; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci rtrc(1) 2748c2ecf20Sopenharmony_ci j = inb(qbase + 6); 2758c2ecf20Sopenharmony_ci i = inb(qbase + 5); 2768c2ecf20Sopenharmony_ci if (i == 0x20) { 2778c2ecf20Sopenharmony_ci return (DID_NO_CONNECT << 16); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */ 2808c2ecf20Sopenharmony_ci if (i != 0x18) { 2818c2ecf20Sopenharmony_ci printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i); 2828c2ecf20Sopenharmony_ci ql_zap(priv); 2838c2ecf20Sopenharmony_ci return (DID_BAD_INTR << 16); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci j &= 7; /* j = inb( qbase + 7 ) >> 5; */ 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* correct status is supposed to be step 4 */ 2888c2ecf20Sopenharmony_ci /* it sometimes returns step 3 but with 0 bytes left to send */ 2898c2ecf20Sopenharmony_ci /* We can try stuffing the FIFO with the max each time, but we will get a 2908c2ecf20Sopenharmony_ci sequence of 3 if any bytes are left (but we do flush the FIFO anyway */ 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (j != 3 && j != 4) { 2938c2ecf20Sopenharmony_ci printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n", 2948c2ecf20Sopenharmony_ci j, i, inb(qbase + 7) & 0x1f); 2958c2ecf20Sopenharmony_ci ql_zap(priv); 2968c2ecf20Sopenharmony_ci return (DID_ERROR << 16); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci result = DID_OK; 2998c2ecf20Sopenharmony_ci if (inb(qbase + 7) & 0x1f) /* if some bytes in fifo */ 3008c2ecf20Sopenharmony_ci outb(1, qbase + 3); /* clear fifo */ 3018c2ecf20Sopenharmony_ci /* note that request_bufflen is the total xfer size when sg is used */ 3028c2ecf20Sopenharmony_ci reqlen = scsi_bufflen(cmd); 3038c2ecf20Sopenharmony_ci /* note that it won't work if transfers > 16M are requested */ 3048c2ecf20Sopenharmony_ci if (reqlen && !((phase = inb(qbase + 4)) & 6)) { /* data phase */ 3058c2ecf20Sopenharmony_ci struct scatterlist *sg; 3068c2ecf20Sopenharmony_ci rtrc(2) 3078c2ecf20Sopenharmony_ci outb(reqlen, qbase); /* low-mid xfer cnt */ 3088c2ecf20Sopenharmony_ci outb(reqlen >> 8, qbase + 1); /* low-mid xfer cnt */ 3098c2ecf20Sopenharmony_ci outb(reqlen >> 16, qbase + 0xe); /* high xfer cnt */ 3108c2ecf20Sopenharmony_ci outb(0x90, qbase + 3); /* command do xfer */ 3118c2ecf20Sopenharmony_ci /* PIO pseudo DMA to buffer or sglist */ 3128c2ecf20Sopenharmony_ci REG1; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) { 3158c2ecf20Sopenharmony_ci if (priv->qabort) { 3168c2ecf20Sopenharmony_ci REG0; 3178c2ecf20Sopenharmony_ci return ((priv->qabort == 1 ? 3188c2ecf20Sopenharmony_ci DID_ABORT : DID_RESET) << 16); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci buf = sg_virt(sg); 3218c2ecf20Sopenharmony_ci if (ql_pdma(priv, phase, buf, sg->length)) 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci REG0; 3258c2ecf20Sopenharmony_ci rtrc(2) 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * Wait for irq (split into second state of irq handler 3288c2ecf20Sopenharmony_ci * if this can take time) 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_ci if ((k = ql_wai(priv))) 3318c2ecf20Sopenharmony_ci return (k << 16); 3328c2ecf20Sopenharmony_ci k = inb(qbase + 5); /* should be 0x10, bus service */ 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * Enter Status (and Message In) Phase 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci k = jiffies + WATCHDOG; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci while (time_before(jiffies, k) && !priv->qabort && 3428c2ecf20Sopenharmony_ci !(inb(qbase + 4) & 6)) 3438c2ecf20Sopenharmony_ci cpu_relax(); /* wait for status phase */ 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, k)) { 3468c2ecf20Sopenharmony_ci ql_zap(priv); 3478c2ecf20Sopenharmony_ci return (DID_TIME_OUT << 16); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* FIXME: timeout ?? */ 3518c2ecf20Sopenharmony_ci while (inb(qbase + 5)) 3528c2ecf20Sopenharmony_ci cpu_relax(); /* clear pending ints */ 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (priv->qabort) 3558c2ecf20Sopenharmony_ci return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci outb(0x11, qbase + 3); /* get status and message */ 3588c2ecf20Sopenharmony_ci if ((k = ql_wai(priv))) 3598c2ecf20Sopenharmony_ci return (k << 16); 3608c2ecf20Sopenharmony_ci i = inb(qbase + 5); /* get chip irq stat */ 3618c2ecf20Sopenharmony_ci j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */ 3628c2ecf20Sopenharmony_ci status = inb(qbase + 2); 3638c2ecf20Sopenharmony_ci message = inb(qbase + 2); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* 3668c2ecf20Sopenharmony_ci * Should get function complete int if Status and message, else 3678c2ecf20Sopenharmony_ci * bus serv if only status 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) { 3708c2ecf20Sopenharmony_ci printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j); 3718c2ecf20Sopenharmony_ci result = DID_ERROR; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci outb(0x12, qbase + 3); /* done, disconnect */ 3748c2ecf20Sopenharmony_ci rtrc(1) 3758c2ecf20Sopenharmony_ci if ((k = ql_wai(priv))) 3768c2ecf20Sopenharmony_ci return (k << 16); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * Should get bus service interrupt and disconnect interrupt 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci i = inb(qbase + 5); /* should be bus service */ 3838c2ecf20Sopenharmony_ci while (!priv->qabort && ((i & 0x20) != 0x20)) { 3848c2ecf20Sopenharmony_ci barrier(); 3858c2ecf20Sopenharmony_ci cpu_relax(); 3868c2ecf20Sopenharmony_ci i |= inb(qbase + 5); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci rtrc(0) 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (priv->qabort) 3918c2ecf20Sopenharmony_ci return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return (result << 16) | (message << 8) | (status & STATUS_MASK); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/* 3978c2ecf20Sopenharmony_ci * Interrupt handler 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void ql_ihandl(void *dev_id) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct scsi_cmnd *icmd; 4038c2ecf20Sopenharmony_ci struct Scsi_Host *host = dev_id; 4048c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_host(host); 4058c2ecf20Sopenharmony_ci int qbase = priv->qbase; 4068c2ecf20Sopenharmony_ci REG0; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (!(inb(qbase + 4) & 0x80)) /* false alarm? */ 4098c2ecf20Sopenharmony_ci return; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (priv->qlcmd == NULL) { /* no command to process? */ 4128c2ecf20Sopenharmony_ci int i; 4138c2ecf20Sopenharmony_ci i = 16; 4148c2ecf20Sopenharmony_ci while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */ 4158c2ecf20Sopenharmony_ci return; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci icmd = priv->qlcmd; 4188c2ecf20Sopenharmony_ci icmd->result = ql_pcmd(icmd); 4198c2ecf20Sopenharmony_ci priv->qlcmd = NULL; 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * If result is CHECK CONDITION done calls qcommand to request 4228c2ecf20Sopenharmony_ci * sense 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci (icmd->scsi_done) (icmd); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ciirqreturn_t qlogicfas408_ihandl(int irq, void *dev_id) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci unsigned long flags; 4308c2ecf20Sopenharmony_ci struct Scsi_Host *host = dev_id; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci spin_lock_irqsave(host->host_lock, flags); 4338c2ecf20Sopenharmony_ci ql_ihandl(dev_id); 4348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(host->host_lock, flags); 4358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* 4398c2ecf20Sopenharmony_ci * Queued command 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int qlogicfas408_queuecommand_lck(struct scsi_cmnd *cmd, 4438c2ecf20Sopenharmony_ci void (*done) (struct scsi_cmnd *)) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 4468c2ecf20Sopenharmony_ci if (scmd_id(cmd) == priv->qinitid) { 4478c2ecf20Sopenharmony_ci cmd->result = DID_BAD_TARGET << 16; 4488c2ecf20Sopenharmony_ci done(cmd); 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci cmd->scsi_done = done; 4538c2ecf20Sopenharmony_ci /* wait for the last command's interrupt to finish */ 4548c2ecf20Sopenharmony_ci while (priv->qlcmd != NULL) { 4558c2ecf20Sopenharmony_ci barrier(); 4568c2ecf20Sopenharmony_ci cpu_relax(); 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci ql_icmd(cmd); 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciDEF_SCSI_QCMD(qlogicfas408_queuecommand) 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci/* 4658c2ecf20Sopenharmony_ci * Return bios parameters 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ciint qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev, 4698c2ecf20Sopenharmony_ci sector_t capacity, int ip[]) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci/* This should mimic the DOS Qlogic driver's behavior exactly */ 4728c2ecf20Sopenharmony_ci ip[0] = 0x40; 4738c2ecf20Sopenharmony_ci ip[1] = 0x20; 4748c2ecf20Sopenharmony_ci ip[2] = (unsigned long) capacity / (ip[0] * ip[1]); 4758c2ecf20Sopenharmony_ci if (ip[2] > 1024) { 4768c2ecf20Sopenharmony_ci ip[0] = 0xff; 4778c2ecf20Sopenharmony_ci ip[1] = 0x3f; 4788c2ecf20Sopenharmony_ci ip[2] = (unsigned long) capacity / (ip[0] * ip[1]); 4798c2ecf20Sopenharmony_ci#if 0 4808c2ecf20Sopenharmony_ci if (ip[2] > 1023) 4818c2ecf20Sopenharmony_ci ip[2] = 1023; 4828c2ecf20Sopenharmony_ci#endif 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci/* 4888c2ecf20Sopenharmony_ci * Abort a command in progress 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ciint qlogicfas408_abort(struct scsi_cmnd *cmd) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 4948c2ecf20Sopenharmony_ci priv->qabort = 1; 4958c2ecf20Sopenharmony_ci ql_zap(priv); 4968c2ecf20Sopenharmony_ci return SUCCESS; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/* 5008c2ecf20Sopenharmony_ci * Reset SCSI bus 5018c2ecf20Sopenharmony_ci * FIXME: This function is invoked with cmd = NULL directly by 5028c2ecf20Sopenharmony_ci * the PCMCIA qlogic_stub code. This wants fixing 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ciint qlogicfas408_host_reset(struct scsi_cmnd *cmd) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd); 5088c2ecf20Sopenharmony_ci unsigned long flags; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci priv->qabort = 2; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci spin_lock_irqsave(cmd->device->host->host_lock, flags); 5138c2ecf20Sopenharmony_ci ql_zap(priv); 5148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(cmd->device->host->host_lock, flags); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return SUCCESS; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/* 5208c2ecf20Sopenharmony_ci * Return info string 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ciconst char *qlogicfas408_info(struct Scsi_Host *host) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct qlogicfas408_priv *priv = get_priv_by_host(host); 5268c2ecf20Sopenharmony_ci return priv->qinfo; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci/* 5308c2ecf20Sopenharmony_ci * Get type of chip 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ciint qlogicfas408_get_chip_type(int qbase, int int_type) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci REG1; 5368c2ecf20Sopenharmony_ci return inb(qbase + 0xe) & 0xf8; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci/* 5408c2ecf20Sopenharmony_ci * Perform initialization tasks 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_civoid qlogicfas408_setup(int qbase, int id, int int_type) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci outb(1, qbase + 8); /* set for PIO pseudo DMA */ 5468c2ecf20Sopenharmony_ci REG0; 5478c2ecf20Sopenharmony_ci outb(0x40 | qlcfg8 | id, qbase + 8); /* (ini) bus id, disable scsi rst */ 5488c2ecf20Sopenharmony_ci outb(qlcfg5, qbase + 5); /* select timer */ 5498c2ecf20Sopenharmony_ci outb(qlcfg9, qbase + 9); /* prescaler */ 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci#if QL_RESET_AT_START 5528c2ecf20Sopenharmony_ci outb(3, qbase + 3); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci REG1; 5558c2ecf20Sopenharmony_ci /* FIXME: timeout */ 5568c2ecf20Sopenharmony_ci while (inb(qbase + 0xf) & 4) 5578c2ecf20Sopenharmony_ci cpu_relax(); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci REG0; 5608c2ecf20Sopenharmony_ci#endif 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* 5648c2ecf20Sopenharmony_ci * Checks if this is a QLogic FAS 408 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ciint qlogicfas408_detect(int qbase, int int_type) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci REG1; 5708c2ecf20Sopenharmony_ci return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) && 5718c2ecf20Sopenharmony_ci ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7)); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/* 5758c2ecf20Sopenharmony_ci * Disable interrupts 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_civoid qlogicfas408_disable_ints(struct qlogicfas408_priv *priv) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci int qbase = priv->qbase; 5818c2ecf20Sopenharmony_ci int int_type = priv->int_type; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci REG1; 5848c2ecf20Sopenharmony_ci outb(0, qbase + 0xb); /* disable ints */ 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/* 5888c2ecf20Sopenharmony_ci * Init and exit functions 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic int __init qlogicfas408_init(void) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic void __exit qlogicfas408_exit(void) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tom Zerucha, Michael Griffith"); 6028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers"); 6038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6048c2ecf20Sopenharmony_cimodule_init(qlogicfas408_init); 6058c2ecf20Sopenharmony_cimodule_exit(qlogicfas408_exit); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_info); 6088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_queuecommand); 6098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_abort); 6108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_host_reset); 6118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_biosparam); 6128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_ihandl); 6138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_get_chip_type); 6148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_setup); 6158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_detect); 6168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qlogicfas408_disable_ints); 6178c2ecf20Sopenharmony_ci 618