## diffname pc/adaptec.c 1993/0114
## diff -e /dev/null /n/bootesdump/1993/0114/sys/src/9/pc/adaptec.c
0a
/*
* Adaptec AHA-154[02]B Intelligent Host Adapter.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "ureg.h"
/*
* Uniprocessors can't (don't need to) lock.
*/
#define lock(l)
#define unlock(l)
enum {
NCtlr = 1,
NTarget = 8, /* targets per controller */
Port = 0x330, /* factory defaults: I/O port */
CtlrID = 7, /* adapter SCSI id */
Irq = 11, /* interrupt request level */
};
enum { /* registers */
Rc = 0, /* WO: control */
Rs = 0, /* RO: status */
Rcdo = 1, /* WO: command/data out */
Rdi = 1, /* RO: data in */
Rif = 2, /* RO: interrupt flags */
};
enum { /* Rc */
Scrst = 0x10, /* SCSI bus reset */
Irst = 0x20, /* interrupt reset */
Srst = 0x40, /* soft reset */
Hrst = 0x80, /* hard reset */
};
enum { /* Rs */
Invdcmd = 0x01, /* invalid host adapter command */
Df = 0x04, /* data in port full */
Cdf = 0x08, /* command/data port full */
Idle = 0x10, /* SCSI host adapter idle */
Init = 0x20, /* mailbox initialisation required */
Diagf = 0x40, /* internal diagnostic failure */
Stst = 0x80, /* self testing in progress */
};
enum { /* Rcdo */
Cnop = 0x00, /* no operation */
Cmbinit = 0x01, /* mailbox initialisation */
Cstart = 0x02, /* start SCSI command */
Cbios = 0x03, /* start PC/AT BIOS command */
Cinquiry = 0x04, /* adapter inquiry */
Cmboie = 0x05, /* enable mailbox out available interrupt */
Cselection = 0x06, /* set selection timeout */
Cbuson = 0x07, /* set bus-on time */
Cbusoff = 0x08, /* set bus-off time */
Ctransfer = 0x09, /* set transfer speed */
Cdevices = 0x0A, /* return installed devices */
Cconfiguration = 0x0B, /* return configuration data */
Ctenable = 0x0C, /* enable target mode */
Csetup = 0x0D, /* return setup data */
Cwbuff = 0x1A, /* write adapter channel 2 buffer */
Crbuff = 0x1B, /* read adapter channel 2 buffer */
Cwfifo = 0x1C, /* write adapter FIFO buffer */
Crfifo = 0x1D, /* read adapter FIFO buffer */
Cecho = 0x1F, /* ECHO command data */
Cdiag = 0x20, /* adapter diagnostic */
Coptions = 0x21, /* set host adapter options */
};
enum { /* Rif */
Mbif = 0x01, /* mailbox in full */
Mboa = 0x02, /* mailbox out available */
Hacc = 0x04, /* host adapter command complete */
Scrd = 0x08, /* SCSI reset detected */
Ai = 0x80, /* any interrupt */
};
typedef struct {
uchar cmd; /* command */
uchar msb; /* CCB pointer MSB */
uchar mid; /* CCB pointer MID */
uchar lsb; /* CCB pointer LSB */
} Mbox;
enum { /* mailbox commands */
Mbfree = 0x00, /* mailbox is free */
Mbostart = 0x01, /* start SCSI or adapter command */
Mboabort = 0x02, /* abort SCSI or adapter command */
Mbiok = 0x01, /* CCB completed without error */
Mbiabort = 0x02, /* CCB aborted by host */
Mbinx = 0x03, /* aborted CCB not found */
Mbierror = 0x04, /* CCB completed with error */
};
typedef struct {
uchar op; /* command control block operation code */
uchar ctl; /* address and direction control */
uchar cmdlen; /* SCSI command length */
uchar reqlen; /* request sense allocation length */
uchar datalen[3]; /* data length (MSB, MID, LSB) */
uchar dataptr[3]; /* data pointer (MSB, MID, LSB) */
uchar linkptr[3]; /* link pointer (MSB, MID, LSB) */
uchar linkid; /* command linking identifier */
uchar hastat; /* host adapter status */
uchar tarstat; /* target device status */
uchar reserved[2];
uchar cs[12+0xFF]; /* SCSI command and sense bytes */
} Ccb;
enum { /* op */
OInitiator = 0x00, /* initiator CCB */
OTarget = 0x01, /* target CCB */
Osg = 0x02, /* initiator CCB with scatter/gather */
Ordl = 0x03, /* initiator CCB, residual data length returned */
Osgrdl = 0x04, /* initiator CCB, both of the above */
Obdr = 0x81, /* bus device reset */
};
enum { /* ctl */
CCBdatain = 0x08, /* inbound data transfer, length is checked */
CCBdataout = 0x10, /* outbound data transfer, length is checked */
};
enum { /* hastat */
Eok = 0x00, /* no host adapter detected error */
Etimeout = 0x11, /* selection timeout */
Elength = 0x12, /* data over/under run */
Ebusfree = 0x13, /* unexpected bus free */
Ephase = 0x14, /* target bus phase sequence error */
Eopcode = 0x16, /* invalid CCB opcode */
Elink = 0x17, /* linked CCB does not have same LUN */
Edirection = 0x18, /* invalid target direction received from host */
Eduplicate = 0x19, /* duplicate CCB received in target mode */
Esegment = 0x1A, /* invalid CCB or segment list parameter */
};
enum { /* tarstat */
Sgood = 0x00, /* good status */
Scheck = 0x02, /* check status */
Sbusy = 0x08, /* LUN busy */
};
typedef struct Target Target;
typedef struct Ctlr Ctlr;
struct Target {
QLock;
Rendez;
Ccb ccb;
ulong paddr; /* physical address of ccb */
uchar *sense; /* address of returned sense data */
int done;
Target *active; /* link on active list */
};
struct Ctlr {
Lock;
ulong port; /* I/O port */
ulong id; /* adapter SCSI id */
uchar cmd[5]; /* adapter command out */
uchar cmdlen; /* adapter command out length */
uchar data[256]; /* adapter command data in */
uchar datalen; /* adapter command data in length */
Mbox mb[NTarget+NTarget]; /* mailbox out + mailbox in */
Mbox *mbox; /* current mailbox out index into mb */
Mbox *mbix; /* current mailbox in index into mb */
Target target[NTarget];
Target *active; /* link on active list */
};
static Ctlr softctlr[NCtlr];
/*
* Issue a command to the controller. The command and its length is
* contained in ctlr->cmd and ctlr->cmdlen. If any data is to be
* returned, ctlr->datalen should be non-0, and the returned data will
* be placed in ctlr->data.
* If we see Hacc set, bail out, we'll process
* the invalid command at interrupt time.
*/
static void
issue(Ctlr *ctlr)
{
int len;
len = 0;
while(len < ctlr->cmdlen){
if((inb(ctlr->port+Rs) & Cdf) == 0){
outb(ctlr->port+Rcdo, ctlr->cmd[len]);
len++;
}
if(inb(ctlr->port+Rif) & Hacc)
return;
}
if(ctlr->datalen){
len = 0;
while(len < ctlr->datalen){
if(inb(ctlr->port+Rs) & Df){
ctlr->data[len] = inb(ctlr->port+Rdi);
len++;
}
if(inb(ctlr->port+Rif) & Hacc)
return;
}
}
}
static int
done(void *arg)
{
return ((Target*)arg)->done;
}
static int
scsiio(int bus, Scsi *p, int rw)
{
Ctlr *ctlr;
Target *tp;
ushort status;
ulong len, s;
/*
* Wait for the target to become free,
* then set it up. The Adaptec will allow us to
* queue multiple transactions per target, but
* gives no guarantee about ordering, so we just
* allow one per target.
*/
ctlr = &softctlr[bus];
tp = &ctlr->target[p->target];
qlock(tp);
if(waserror()){
qunlock(tp);
nexterror();
}
/*
* If this is a request-sense and we have valid sense data
* from the last command, return it immediately.
* A pox on these weird enum names and the WD33C93A status
* codes.
*/
if(p->cmd.base[0] == ScsiExtsens && tp->sense){
len = 8+tp->sense[7];
memmove(p->data.ptr, tp->sense, len);
p->data.ptr += len;
tp->sense = 0;
qunlock(tp);
poperror();
return 0x6000;
}
tp->sense = 0;
/*
* Fill in the ccb.
*/
tp->ccb.op = Ordl;
tp->ccb.ctl = (p->target<<5)|p->lun;
len = p->cmd.lim - p->cmd.base;
tp->ccb.cmdlen = len;
memmove(tp->ccb.cs, p->cmd.base, len);
tp->ccb.reqlen = 0xFF;
len = p->data.lim - p->data.base;
tp->ccb.datalen[0] = (len>>16) & 0xFF;
tp->ccb.datalen[1] = (len>>8) & 0xFF;
tp->ccb.datalen[2] = len;
if(len == 0)
tp->ccb.ctl |= CCBdataout|CCBdatain;
else if(rw == ScsiIn)
tp->ccb.ctl |= CCBdatain;
else
tp->ccb.ctl |= CCBdataout;
len = PADDR(p->data.base);
tp->ccb.dataptr[0] = (len>>16) & 0xFF;
tp->ccb.dataptr[1] = (len>>8) & 0xFF;
tp->ccb.dataptr[2] = len;
tp->ccb.linkptr[0] = tp->ccb.linkptr[1] = tp->ccb.linkptr[2] = 0;
tp->ccb.linkid = 0;
tp->ccb.hastat = tp->ccb.tarstat = 0;
tp->ccb.reserved[0] = tp->ccb.reserved[1] = 0;
/*
* Link the target onto the beginning of the
* ctlr active list and start the request.
* The interrupt routine has to be able to take
* requests off the queue in any order.
*/
s = splhi();
lock(ctlr);
tp->done = 0;
tp->active = ctlr->active;
ctlr->active = tp;
ctlr->mbox->msb = (tp->paddr>>16) & 0xFF;
ctlr->mbox->mid = (tp->paddr>>8) & 0xFF;
ctlr->mbox->lsb = tp->paddr & 0xFF;
ctlr->mbox->cmd = Mbostart;
ctlr->cmd[0] = Cstart;
ctlr->cmdlen = 1;
ctlr->datalen = 0;
issue(ctlr);
ctlr->mbox++;
if(ctlr->mbox >= &ctlr->mb[NTarget])
ctlr->mbox = ctlr->mb;
unlock(ctlr);
splx(s);
/*
* Wait for the request to complete
* and return the status.
*/
tsleep(tp, done, tp, 60*5*1000);
if(done(tp) == 0)
print("adaptec%d: timeout cmd=#%2.2ux\n", p->target, tp->ccb.cs[0]);
if((status = (tp->ccb.hastat<<8)) == 0)
status = 0x6000;
status |= tp->ccb.tarstat;
len = (tp->ccb.datalen[0]<<16)|(tp->ccb.datalen[1]<<8)|tp->ccb.datalen[2];
p->data.ptr = p->data.lim - len;
/*
* If the command returned sense data, keep a note
* of where it is for a subsequent request-sense command.
*/
if(tp->ccb.tarstat == Scheck && tp->ccb.hastat == Eok)
tp->sense = &tp->ccb.cs[tp->ccb.cmdlen];
qunlock(tp);
poperror();
return status;
}
int
scsiexec(Scsi *p, int rw)
{
USED(rw);
if(p->target == CtlrID)
return 0x6002;
return p->status = scsiio(0, p, rw);
}
static void
interrupt(Ureg *ur)
{
Ctlr *ctlr = &softctlr[0];
uchar rif, rs;
Target *tp, **l;
ulong paddr;
USED(ur);
lock(ctlr);
/*
* Save and clear the interrupt(s). The only
* interrupts expected are Hacc, which we ignore,
* and Mbif which means something completed.
*/
rif = inb(ctlr->port+Rif);
rs = inb(ctlr->port+Rs);
outb(ctlr->port+Rc, Irst);
if(rif & ~(Ai|Hacc|Mbif))
print("adaptec%d: interrupt #%2.2ux\n", 0, rif);
if((rif & Hacc) && (rs & Invdcmd))
print("adaptec%d: invdcmd #%2.2ux, len %d\n", 0, ctlr->cmd[0], ctlr->cmdlen);
/*
* Look for something in the mail.
* If there is, try to find the recipient from the
* ccb address, take it off the active list and
* wakeup whoever.
*/
while(ctlr->mbix->cmd){
paddr = (ctlr->mbix->msb<<16)|(ctlr->mbix->mid<<8)|ctlr->mbix->lsb;
l = &ctlr->active;
for(tp = *l; tp; tp = tp->active){
if(tp->paddr == paddr)
break;
l = &tp->active;
}
if(tp == 0)
panic("adaptec%d: no target for ccb #%lux\n", 0, paddr);
*l = tp->active;
ctlr->mbix->cmd = 0;
tp->done = 1;
wakeup(tp);
ctlr->mbix++;
if(ctlr->mbix >= &ctlr->mb[NTarget+NTarget])
ctlr->mbix = &ctlr->mb[NTarget];
}
unlock(ctlr);
}
static void
reset(Ctlr *ctlr, ulong port, ulong id)
{
ulong paddr;
int i;
/*
* Initialise the software controller and set the board
* scanning the mailboxes.
* Need code here to tidy things up if we're
* resetting after being active.
*/
memset(ctlr, 0, sizeof(Ctlr));
ctlr->port = port;
ctlr->id = id;
for(i = 0; i < NTarget; i++){
ctlr->target[i].paddr = PADDR(&ctlr->target[i].ccb);
ctlr->mbox = ctlr->mb;
ctlr->mbix = &ctlr->mb[NTarget];
}
ctlr->cmd[0] = Cmbinit;
paddr = PADDR(ctlr->mb);
ctlr->cmd[1] = NTarget;
ctlr->cmd[2] = (paddr>>16) & 0xFF;
ctlr->cmd[3] = (paddr>>8) & 0xFF;
ctlr->cmd[4] = paddr & 0xFF;
ctlr->cmdlen = 5;
ctlr->datalen = 0;
issue(ctlr);
}
void
resetscsi(void)
{
uchar rs;
ulong port;
/*
* For the moment assume the factory default settings.
*/
port = Port;
/*
* Attempt to soft-reset the board and reset
* the SCSI bus. If the board state doesn't settle to
* idle with mailbox initialisation required, either
* it isn't an Adaptec or it's broken.
*/
outb(port+Rc, Srst|Scrst);
delay(500);
if((rs = inb(port+Rs)) != (Init|Idle)){
print("adaptec%d: reset status #%2.2ux\n", 0, rs);
return;
}
setvec(Int0vec+Irq, interrupt);
reset(&softctlr[0], port, CtlrID);
}
/*
* Known to devscsi.c.
*/
int scsidebugs[8];
int scsiownid = CtlrID;
void
initscsi(void)
{
}
/*
* Quick hack. Need to do a better job of dynamic initialisation
* for machines with peculiar memory/cache restictions.
* Also, what about 16Mb address limit on the Adaptec?
*/
static ulong bufdatasize;
void
scsibufreset(ulong datasize)
{
bufdatasize = datasize;
}
Scsibuf *
scsibuf(void)
{
Scsibuf *b;
b = smalloc(sizeof(*b));
b->virt = smalloc(bufdatasize);
b->phys = (void *)(PADDR(b->virt));
return b;
}
void
scsifree(Scsibuf *b)
{
free(b->virt);
free(b);
}
/*
* Hack for devvid
*/
Scsibuf *
scsialloc(ulong n)
{
Scsibuf *b;
b = smalloc(sizeof(*b));
b->virt = smalloc(n);
b->phys = (void *)(PADDR(b->virt));
return b;
}
.
## diffname pc/adaptec.c 1993/0119
## diff -e /n/bootesdump/1993/0114/sys/src/9/pc/adaptec.c /n/bootesdump/1993/0119/sys/src/9/pc/adaptec.c
371a
if(ctlr->port == 0)
error(Enodev);
.
370c
Ctlr *ctlr = &softctlr[0];
.
## diffname pc/adaptec.c 1993/0220
## diff -e /n/bootesdump/1993/0119/sys/src/9/pc/adaptec.c /n/bootesdump/1993/0220/sys/src/9/pc/adaptec.c
488c
/*print("adaptec%d: reset status #%2.2ux\n", 0, rs);/**/
.
## diffname pc/adaptec.c 1993/0223
## diff -e /n/bootesdump/1993/0220/sys/src/9/pc/adaptec.c /n/bootesdump/1993/0223/sys/src/9/pc/adaptec.c
490d
487,488c
if(inb(port+Rs) != (Init|Idle))
.
471d
## diffname pc/adaptec.c 1993/0915
## diff -e /n/bootesdump/1993/0223/sys/src/9/pc/adaptec.c /n/fornaxdump/1993/0915/sys/src/brazil/pc/adaptec.c
493,546c
return aha1542exec;
.
489,491c
setvec(Int0vec+isa->irq, interrupt);
reset(&softctlr[0], isa->port, CtlrID);
.
486,487c
if(inb(isa->port+Rs) != (Init|Idle))
return 0;
.
484c
outb(isa->port+Rc, Hrst|Scrst);
.
479c
* Attempt to hard-reset the board and reset
.
476c
if(isaconfig("scsi", 0, &aha1542) == 0)
return 0;
.
474c
* See if we have any configuration info.
* Only one controller for now.
.
471c
ISAConf *isa = &aha1542;
.
468,469c
static ISAConf aha1542 = {
"aha1542",
Port,
Irq,
0,
0,
};
int (*
aha1542reset(void))(Scsi*, int)
.
433c
if(active.machs > 1)
unlock(ctlr);
.
390c
if(active.machs > 1)
lock(ctlr);
.
367,368c
static int
aha1542exec(Scsi *p, int rw)
.
337c
if(active.machs > 1)
unlock(ctlr);
.
317c
if(active.machs > 1)
lock(ctlr);
.
14,19d
2c
* Adaptec AHA-154[02][BC] Intelligent Host Adapter.
* Needs work:
* handle multiple controllers;
* set options from user-level;
* split out to handle ultrastor too;
.
## diffname pc/adaptec.c 1993/1113 # deleted
## diff -e /n/fornaxdump/1993/0915/sys/src/brazil/pc/adaptec.c /n/fornaxdump/1993/1113/sys/src/brazil/pc/adaptec.c
1,505d
|