Commit a5d98e0c authored by Aristeu Sergio Rozanski Filho's avatar Aristeu Sergio Rozanski Filho Committed by James Bottomley

[PATCH] qlogicfas: support multiple cards

parent 60da61f9
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
SCSI driver cleanup and audit. This driver still needs work on the SCSI driver cleanup and audit. This driver still needs work on the
following following
- Non terminating hardware waits - Non terminating hardware waits
- Support multiple cards at a time
- Some layering violations with its pcmcia stub - Some layering violations with its pcmcia stub
Redistributable under terms of the GNU General Public License Redistributable under terms of the GNU General Public License
...@@ -139,16 +138,10 @@ ...@@ -139,16 +138,10 @@
#include "scsi.h" #include "scsi.h"
#include "hosts.h" #include "hosts.h"
#include "qlogicfas.h"
/*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/
/* driver state info, local to driver */ /* driver state info, local to driver */
static int qbase; /* Port */
static int qinitid; /* initiator ID */
static int qabort; /* Flag to cause an abort */
static int qlirq = -1; /* IRQ being used */
static char qinfo[80]; /* description */
static Scsi_Cmnd *qlcmd; /* current command being processed */
static int qlcfg5 = (XTALFREQ << 5); /* 15625/512 */ static int qlcfg5 = (XTALFREQ << 5); /* 15625/512 */
static int qlcfg6 = SYNCXFRPD; static int qlcfg6 = SYNCXFRPD;
static int qlcfg7 = SYNCOFFST; static int qlcfg7 = SYNCOFFST;
...@@ -184,9 +177,10 @@ int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)); ...@@ -184,9 +177,10 @@ int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *));
/* error recovery - reset everything */ /* error recovery - reset everything */
static void ql_zap(void) static void ql_zap(qlogicfas_priv_t priv)
{ {
int x; int x;
int qbase = priv->qbase;
x = inb(qbase + 0xd); x = inb(qbase + 0xd);
REG0; REG0;
...@@ -200,9 +194,10 @@ static void ql_zap(void) ...@@ -200,9 +194,10 @@ static void ql_zap(void)
* Do a pseudo-dma tranfer * Do a pseudo-dma tranfer
*/ */
static int ql_pdma(int phase, char *request, int reqlen) static int ql_pdma(qlogicfas_priv_t priv, int phase, char *request, int reqlen)
{ {
int j; int j;
int qbase = priv->qbase;
j = 0; j = 0;
if (phase & 1) { /* in */ if (phase & 1) { /* in */
#if QL_TURBO_PDMA #if QL_TURBO_PDMA
...@@ -284,23 +279,25 @@ static int ql_pdma(int phase, char *request, int reqlen) ...@@ -284,23 +279,25 @@ static int ql_pdma(int phase, char *request, int reqlen)
* Wait for interrupt flag (polled - not real hardware interrupt) * Wait for interrupt flag (polled - not real hardware interrupt)
*/ */
static int ql_wai(void) static int ql_wai(qlogicfas_priv_t priv)
{ {
int k; int k;
int qbase = priv->qbase;
unsigned long i; unsigned long i;
k = 0; k = 0;
i = jiffies + WATCHDOG; i = jiffies + WATCHDOG;
while (time_before(jiffies, i) && !qabort && !((k = inb(qbase + 4)) & 0xe0)) { while (time_before(jiffies, i) && !priv->qabort &&
!((k = inb(qbase + 4)) & 0xe0)) {
barrier(); barrier();
cpu_relax(); cpu_relax();
} }
if (time_after_eq(jiffies, i)) if (time_after_eq(jiffies, i))
return (DID_TIME_OUT); return (DID_TIME_OUT);
if (qabort) if (priv->qabort)
return (qabort == 1 ? DID_ABORT : DID_RESET); return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
if (k & 0x60) if (k & 0x60)
ql_zap(); ql_zap(priv);
if (k & 0x20) if (k & 0x20)
return (DID_PARITY); return (DID_PARITY);
if (k & 0x40) if (k & 0x40)
...@@ -315,9 +312,11 @@ static int ql_wai(void) ...@@ -315,9 +312,11 @@ static int ql_wai(void)
static void ql_icmd(Scsi_Cmnd * cmd) static void ql_icmd(Scsi_Cmnd * cmd)
{ {
qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
int qbase = priv->qbase;
unsigned int i; unsigned int i;
qabort = 0; priv->qabort = 0;
REG0; REG0;
/* clearing of interrupts and the fifo is needed */ /* clearing of interrupts and the fifo is needed */
...@@ -338,7 +337,7 @@ static void ql_icmd(Scsi_Cmnd * cmd) ...@@ -338,7 +337,7 @@ static void ql_icmd(Scsi_Cmnd * cmd)
/* configurables */ /* configurables */
outb(qlcfgc, qbase + 0xc); outb(qlcfgc, qbase + 0xc);
/* config: no reset interrupt, (initiator) bus id */ /* config: no reset interrupt, (initiator) bus id */
outb(0x40 | qlcfg8 | qinitid, qbase + 8); outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
outb(qlcfg7, qbase + 7); outb(qlcfg7, qbase + 7);
outb(qlcfg6, qbase + 6); outb(qlcfg6, qbase + 6);
/**/ outb(qlcfg5, qbase + 5); /* select timer */ /**/ outb(qlcfg5, qbase + 5); /* select timer */
...@@ -349,7 +348,7 @@ static void ql_icmd(Scsi_Cmnd * cmd) ...@@ -349,7 +348,7 @@ static void ql_icmd(Scsi_Cmnd * cmd)
for (i = 0; i < cmd->cmd_len; i++) for (i = 0; i < cmd->cmd_len; i++)
outb(cmd->cmnd[i], qbase + 2); outb(cmd->cmnd[i], qbase + 2);
qlcmd = cmd; priv->qlcmd = cmd;
outb(0x41, qbase + 3); /* select and send command */ outb(0x41, qbase + 3); /* select and send command */
} }
...@@ -369,6 +368,8 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -369,6 +368,8 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
struct scatterlist *sglist; /* scatter-gather list pointer */ struct scatterlist *sglist; /* scatter-gather list pointer */
unsigned int sgcount; /* sg counter */ unsigned int sgcount; /* sg counter */
char *buf; char *buf;
qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
int qbase = priv->qbase;
rtrc(1) rtrc(1)
j = inb(qbase + 6); j = inb(qbase + 6);
...@@ -379,7 +380,7 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -379,7 +380,7 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */ i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */
if (i != 0x18) { if (i != 0x18) {
printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i); printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
ql_zap(); ql_zap(priv);
return (DID_BAD_INTR << 16); return (DID_BAD_INTR << 16);
} }
j &= 7; /* j = inb( qbase + 7 ) >> 5; */ j &= 7; /* j = inb( qbase + 7 ) >> 5; */
...@@ -392,7 +393,7 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -392,7 +393,7 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
if (j != 3 && j != 4) { if (j != 3 && j != 4) {
printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n", printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
j, i, inb(qbase + 7) & 0x1f); j, i, inb(qbase + 7) & 0x1f);
ql_zap(); ql_zap(priv);
return (DID_ERROR << 16); return (DID_ERROR << 16);
} }
result = DID_OK; result = DID_OK;
...@@ -410,18 +411,19 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -410,18 +411,19 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
/* PIO pseudo DMA to buffer or sglist */ /* PIO pseudo DMA to buffer or sglist */
REG1; REG1;
if (!cmd->use_sg) if (!cmd->use_sg)
ql_pdma(phase, cmd->request_buffer, ql_pdma(priv, phase, cmd->request_buffer,
cmd->request_bufflen); cmd->request_bufflen);
else { else {
sgcount = cmd->use_sg; sgcount = cmd->use_sg;
sglist = cmd->request_buffer; sglist = cmd->request_buffer;
while (sgcount--) { while (sgcount--) {
if (qabort) { if (priv->qabort) {
REG0; REG0;
return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16); return ((priv->qabort == 1 ?
DID_ABORT : DID_RESET) << 16);
} }
buf = page_address(sglist->page) + sglist->offset; buf = page_address(sglist->page) + sglist->offset;
if (ql_pdma(phase, buf, sglist->length)) if (ql_pdma(priv, phase, buf, sglist->length))
break; break;
sglist++; sglist++;
} }
...@@ -432,7 +434,7 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -432,7 +434,7 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
* Wait for irq (split into second state of irq handler * Wait for irq (split into second state of irq handler
* if this can take time) * if this can take time)
*/ */
if ((k = ql_wai())) if ((k = ql_wai(priv)))
return (k << 16); return (k << 16);
k = inb(qbase + 5); /* should be 0x10, bus service */ k = inb(qbase + 5); /* should be 0x10, bus service */
} }
...@@ -443,11 +445,12 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -443,11 +445,12 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
k = jiffies + WATCHDOG; k = jiffies + WATCHDOG;
while (time_before(jiffies, k) && !qabort && !(inb(qbase + 4) & 6)) while (time_before(jiffies, k) && !priv->qabort &&
!(inb(qbase + 4) & 6))
cpu_relax(); /* wait for status phase */ cpu_relax(); /* wait for status phase */
if (time_after_eq(jiffies, k)) { if (time_after_eq(jiffies, k)) {
ql_zap(); ql_zap(priv);
return (DID_TIME_OUT << 16); return (DID_TIME_OUT << 16);
} }
...@@ -455,11 +458,11 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -455,11 +458,11 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
while (inb(qbase + 5)) while (inb(qbase + 5))
cpu_relax(); /* clear pending ints */ cpu_relax(); /* clear pending ints */
if (qabort) if (priv->qabort)
return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16); return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
outb(0x11, qbase + 3); /* get status and message */ outb(0x11, qbase + 3); /* get status and message */
if ((k = ql_wai())) if ((k = ql_wai(priv)))
return (k << 16); return (k << 16);
i = inb(qbase + 5); /* get chip irq stat */ i = inb(qbase + 5); /* get chip irq stat */
j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */ j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */
...@@ -476,7 +479,7 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -476,7 +479,7 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
} }
outb(0x12, qbase + 3); /* done, disconnect */ outb(0x12, qbase + 3); /* done, disconnect */
rtrc(1) rtrc(1)
if ((k = ql_wai())) if ((k = ql_wai(priv)))
return (k << 16); return (k << 16);
/* /*
...@@ -484,15 +487,15 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -484,15 +487,15 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
*/ */
i = inb(qbase + 5); /* should be bus service */ i = inb(qbase + 5); /* should be bus service */
while (!qabort && ((i & 0x20) != 0x20)) { while (!priv->qabort && ((i & 0x20) != 0x20)) {
barrier(); barrier();
cpu_relax(); cpu_relax();
i |= inb(qbase + 5); i |= inb(qbase + 5);
} }
rtrc(0) rtrc(0)
if (qabort) if (priv->qabort)
return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16); return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
return (result << 16) | (message << 8) | (status & STATUS_MASK); return (result << 16) | (message << 8) | (status & STATUS_MASK);
} }
...@@ -504,20 +507,23 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd) ...@@ -504,20 +507,23 @@ static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
static void ql_ihandl(int irq, void *dev_id, struct pt_regs *regs) static void ql_ihandl(int irq, void *dev_id, struct pt_regs *regs)
{ {
Scsi_Cmnd *icmd; Scsi_Cmnd *icmd;
struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
qlogicfas_priv_t priv = (qlogicfas_priv_t)&(host->hostdata[0]);
int qbase = priv->qbase;
REG0; REG0;
if (!(inb(qbase + 4) & 0x80)) /* false alarm? */ if (!(inb(qbase + 4) & 0x80)) /* false alarm? */
return; return;
if (qlcmd == NULL) { /* no command to process? */ if (priv->qlcmd == NULL) { /* no command to process? */
int i; int i;
i = 16; i = 16;
while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */ while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
return; return;
} }
icmd = qlcmd; icmd = priv->qlcmd;
icmd->result = ql_pcmd(icmd); icmd->result = ql_pcmd(icmd);
qlcmd = NULL; priv->qlcmd = NULL;
/* /*
* If result is CHECK CONDITION done calls qcommand to request * If result is CHECK CONDITION done calls qcommand to request
* sense * sense
...@@ -542,7 +548,8 @@ static irqreturn_t do_ql_ihandl(int irq, void *dev_id, struct pt_regs *regs) ...@@ -542,7 +548,8 @@ static irqreturn_t do_ql_ihandl(int irq, void *dev_id, struct pt_regs *regs)
int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
{ {
if (cmd->device->id == qinitid) { qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
if (cmd->device->id == priv->qinitid) {
cmd->result = DID_BAD_TARGET << 16; cmd->result = DID_BAD_TARGET << 16;
done(cmd); done(cmd);
return 0; return 0;
...@@ -550,7 +557,7 @@ int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) ...@@ -550,7 +557,7 @@ int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
cmd->scsi_done = done; cmd->scsi_done = done;
/* wait for the last command's interrupt to finish */ /* wait for the last command's interrupt to finish */
while (qlcmd != NULL) { while (priv->qlcmd != NULL) {
barrier(); barrier();
cpu_relax(); cpu_relax();
} }
...@@ -558,30 +565,18 @@ int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) ...@@ -558,30 +565,18 @@ int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
return 0; return 0;
} }
#ifdef PCMCIA
/*
* Allow PCMCIA code to preset the port
* port should be 0 and irq to -1 respectively for autoprobing
*/
void qlogicfas_preset(int port, int irq)
{
qbase = port;
qlirq = irq;
}
#endif
/* /*
* Look for qlogic card and init if found * Look for qlogic card and init if found
*/ */
struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host) struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host, int qbase,
int qlirq)
{ {
int i, j; /* these are only used by IRQ detect */ int i, j; /* these are only used by IRQ detect */
int qltyp; /* type of chip */ int qltyp; /* type of chip */
int qinitid;
struct Scsi_Host *hreg; /* registered host structure */ struct Scsi_Host *hreg; /* registered host structure */
qlogicfas_priv_t priv;
/* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself /* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself
* decodes the address - I check 230 first since MIDI cards are * decodes the address - I check 230 first since MIDI cards are
...@@ -652,16 +647,20 @@ struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host) ...@@ -652,16 +647,20 @@ struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host)
} else } else
printk(KERN_INFO "Ql: Using preset IRQ %d\n", qlirq); printk(KERN_INFO "Ql: Using preset IRQ %d\n", qlirq);
hreg = scsi_host_alloc(host, 0); /* no host data */ hreg = scsi_host_alloc(host, sizeof(struct qlogicfas_priv));
if (!hreg) if (!hreg)
goto err_release_mem; goto err_release_mem;
priv = (qlogicfas_priv_t)&(hreg->hostdata[0]);
hreg->io_port = qbase; hreg->io_port = qbase;
hreg->n_io_port = 16; hreg->n_io_port = 16;
hreg->dma_channel = -1; hreg->dma_channel = -1;
if (qlirq != -1) if (qlirq != -1)
hreg->irq = qlirq; hreg->irq = qlirq;
priv->qbase = qbase;
priv->qlirq = qlirq;
priv->qinitid = qinitid;
sprintf(qinfo, sprintf(priv->qinfo,
"Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d", "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
qltyp, qbase, qlirq, QL_TURBO_PDMA); qltyp, qbase, qlirq, QL_TURBO_PDMA);
host->name = qlogicfas_name; host->name = qlogicfas_name;
...@@ -687,13 +686,35 @@ struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host) ...@@ -687,13 +686,35 @@ struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host)
return NULL; return NULL;
} }
#define MAX_QLOGICFAS 8
static int iobase[MAX_QLOGICFAS];
static int irq[MAX_QLOGICFAS] = { [0 ... MAX_QLOGICFAS-1] = -1 };
MODULE_PARM(iobase, "1-" __MODULE_STRING(MAX_QLOGICFAS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_QLOGICFAS) "i");
MODULE_PARM_DESC(iobase, "I/O address");
MODULE_PARM_DESC(irq, "IRQ");
int __devinit qlogicfas_detect(Scsi_Host_Template *sht) int __devinit qlogicfas_detect(Scsi_Host_Template *sht)
{ {
return (__qlogicfas_detect(sht) != NULL); int i,
num = 0;
for (i = 0; i < MAX_QLOGICFAS; i++) {
if (__qlogicfas_detect(sht, iobase[num], irq[num]) == NULL) {
/* no more devices */
break;
}
num++;
}
return num;
} }
static int qlogicfas_release(struct Scsi_Host *shost) static int qlogicfas_release(struct Scsi_Host *shost)
{ {
qlogicfas_priv_t priv = (qlogicfas_priv_t)&(shost->hostdata[0]);
int qbase = priv->qbase;
if (shost->irq) { if (shost->irq) {
REG1; REG1;
outb(0, qbase + 0xb); /* disable ints */ outb(0, qbase + 0xb); /* disable ints */
...@@ -740,8 +761,9 @@ int qlogicfas_biosparam(struct scsi_device * disk, ...@@ -740,8 +761,9 @@ int qlogicfas_biosparam(struct scsi_device * disk,
static int qlogicfas_abort(Scsi_Cmnd * cmd) static int qlogicfas_abort(Scsi_Cmnd * cmd)
{ {
qabort = 1; qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
ql_zap(); priv->qabort = 1;
ql_zap(priv);
return SUCCESS; return SUCCESS;
} }
...@@ -753,8 +775,9 @@ static int qlogicfas_abort(Scsi_Cmnd * cmd) ...@@ -753,8 +775,9 @@ static int qlogicfas_abort(Scsi_Cmnd * cmd)
int qlogicfas_bus_reset(Scsi_Cmnd * cmd) int qlogicfas_bus_reset(Scsi_Cmnd * cmd)
{ {
qabort = 2; qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
ql_zap(); priv->qabort = 2;
ql_zap(priv);
return SUCCESS; return SUCCESS;
} }
...@@ -782,7 +805,8 @@ static int qlogicfas_device_reset(Scsi_Cmnd * cmd) ...@@ -782,7 +805,8 @@ static int qlogicfas_device_reset(Scsi_Cmnd * cmd)
static const char *qlogicfas_info(struct Scsi_Host *host) static const char *qlogicfas_info(struct Scsi_Host *host)
{ {
return qinfo; qlogicfas_priv_t priv = (qlogicfas_priv_t)&(host->hostdata[0]);
return priv->qinfo;
} }
MODULE_AUTHOR("Tom Zerucha, Michael Griffith"); MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
......
/* to be used by qlogicfas and qlogic_cs */
#ifndef __QLOGICFAS_H
#define __QLOGICFAS_H
struct qlogicfas_priv {
int qbase; /* Port */
int qinitid; /* initiator ID */
int qabort; /* Flag to cause an abort */
int qlirq; /* IRQ being used */
char qinfo[80]; /* description */
Scsi_Cmnd *qlcmd; /* current command being processed */
};
typedef struct qlogicfas_priv *qlogicfas_priv_t;
#endif /* __QLOGICFAS_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment