#include "../port/led.h"
#pragma varargck type "T" int
-#define dprint(...) if(debug) iprint(__VA_ARGS__); else USED(debug)
-#define idprint(...) if(prid) print(__VA_ARGS__); else USED(prid)
+#define dprint(...) if(debug) print(__VA_ARGS__); else USED(debug)
+#define idprint(...) if(prid) print(__VA_ARGS__); else USED(prid)
#define aprint(...) if(datapi) print(__VA_ARGS__); else USED(datapi)
-#define ledprint(...) if(dled) print(__VA_ARGS__); else USED(dled)
+#define ledprint(...) if(dled) print(__VA_ARGS__); else USED(dled)
#define Pciwaddrh(a) 0
#define Tname(c) tname[(c)->type]
#define Ticks MACHP(0)->ticks
Ledport;
uchar drivechange;
+ uchar nodma;
uchar state;
uvlong sectors;
Drive rawdrive[NCtlrdrv];
Drive* drive[NCtlrdrv];
int ndrive;
+
+ uint missirq;
};
static Ctlr iactlr[NCtlr];
return -1;
}
-static void
+static Alist*
mkalist(Aportm *m, uint flags, uchar *data, int len)
{
Actab *t;
Aprdt *p;
t = m->ctab;
- l = m->list;
- l->flags = flags | 0x5;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = Pciwaddrh(t);
- if(data){
- l->flags |= 1<<16;
+ if(data && len > 0){
p = &t->prdt;
p->dba = PCIWADDR(data);
p->dbahi = Pciwaddrh(data);
p->count = 1<<31 | len - 2 | 1;
+ flags |= 1<<16;
}
+ l = m->list;
+ l->flags = flags | 0x5;
+ l->len = 0;
+ l->ctab = PCIWADDR(t);
+ l->ctabhi = Pciwaddrh(t);
+ return l;
}
static int
static void
asleep(int ms)
{
- if(up == nil)
+ if(up == nil || !islo())
delay(ms);
else
esleep(ms);
}
-static int
+static void
ahciportreset(Aportc *c, uint mode)
{
ulong *cmd, i;
break;
asleep(25);
}
+ if((*cmd & Apwr) != Apwr)
+ *cmd |= Apwr;
p->sctl = 3*Aipm | 0*Aspd | Adet;
delay(1);
p->sctl = 3*Aipm | mode*Aspd;
- return 0;
}
static int
mkalist(pc->m, Lwrite, 0, 0);
if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
- dprint("ahciflushcache fail %lux\n", pc->p->task);
+ dprint("ahciflushcache fail [task %lux]\n", pc->p->task);
// preg(pc->m->fis.r, 20);
return -1;
}
return -1;
stop1:
/* extra check */
- dprint("ahci: clo clear %lux\n", a->task);
+ dprint("ahci: clo clear [task %lux]\n", a->task);
if(a->task & ASbsy)
return -1;
*p |= Afre | Ast;
ahciwakeup(Aportc *c, uint mode)
{
ushort s;
+ Aport *p;
- s = c->p->sstatus;
+ p = c->p;
+ s = p->sstatus;
if((s & Isleepy) == 0)
return;
if((s & Smask) != Spresent){
- print("ahci: slumbering drive missing %.3ux\n", s);
+ dprint("ahci: slumbering drive missing [ss %.3ux]\n", s);
return;
}
ahciportreset(c, mode);
-// iprint("ahci: wake %.3ux -> %.3lux\n", s, c->p->sstatus);
+ dprint("ahci: wake %.3ux -> %.3lux\n", s, c->p->sstatus);
}
static int
{
Aportm *m;
Aport *p;
+ int i;
p = c->p;
m = c->m;
m->ctab = malign(sizeof *m->ctab, 128);
}
+ if(ahciidle(p) == -1){
+ dprint("ahci: port not idle\n");
+ return -1;
+ }
+
p->list = PCIWADDR(m->list);
p->listhi = Pciwaddrh(m->list);
p->fis = PCIWADDR(m->fis.base);
p->cmd |= Afre;
- if((p->sstatus & Sbist) == 0 && (p->cmd & Apwr) != Apwr)
- if((p->sstatus & Sphylink) == 0 && h->cap & Hss){
- /* staggered spin-up? */
- dprint("ahci: spin up ... [%.3lux]\n", p->sstatus);
+ if((p->cmd & Apwr) != Apwr)
p->cmd |= Apwr;
- for(int i = 0; i < 1400; i += 50){
- if(p->sstatus & (Sphylink | Sbist))
+
+ if((h->cap & Hss) != 0){
+ dprint("ahci: spin up ... [%.3lux]\n", p->sstatus);
+ for(i = 0; i < 1400; i += 50){
+ if((p->sstatus & Sbist) != 0)
+ break;
+ if((p->sstatus & Smask) == Sphylink)
break;
asleep(50);
}
}
- p->serror = SerrAll;
-
if((p->sstatus & SSmask) == (Isleepy | Spresent))
ahciwakeup(c, mode);
+
+ p->serror = SerrAll;
+ p->ie = IEM;
+
+ /* we will get called again once phylink has been established */
+ if((p->sstatus & Smask) != Sphylink)
+ return 0;
+
/* disable power managment sequence from book. */
p->sctl = 3*Aipm | mode*Aspd | 0*Adet;
p->cmd &= ~Aalpe;
- p->cmd |= Ast;
- p->ie = IEM;
+ p->cmd |= Afre | Ast;
return 0;
}
if((u & Ham) == 0)
h->ghc |= Hae;
-// print("#S/sd%c: ahci %s port %#p: sss %d ncs %d coal %d "
-// "mport %d led %d clo %d ems %d\n",
-// c->sdev->idno, Tname(c), h,
-// (u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1, u & 0x1f, (u>>25) & 1,
-// (u>>24) & 1, (u>>6) & 1);
return countbits(h->pi);
}
+static int
+ahcihandoff(Ahba *h)
+{
+ int wait;
+
+ if((h->cap2 & Boh) == 0)
+ return 0;
+ h->bios |= Oos;
+ for(wait = 0; wait < 2000; wait += 100){
+ if((h->bios & Bos) == 0)
+ return 0;
+ delay(100);
+ }
+ iprint("ahci: bios handoff timed out\n");
+ return -1;
+}
+
static int
ahcihbareset(Ahba *h)
{
h->ghc |= Hhr;
for(wait = 0; wait < 1000; wait += 100){
- if(h->ghc == 0)
+ if((h->ghc & Hhr) == 0)
return 0;
delay(100);
}
id = d->info;
s = ahciidentify(&d->portc, id, &d->secsize, dnam(d));
- if(s == -1){
- d->state = Derror;
+ if(s == -1)
return -1;
- }
osectors = d->sectors;
memmove(oserial, d->serial, sizeof d->serial);
if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
d->drivechange = 1;
+ d->nodma = 0;
u->sectors = 0;
}
return 0;
}
}
-static int
-intel(Ctlr *c)
-{
- return c->pci->vid == 0x8086;
-}
-
static int
ignoreahdrs(Drive *d)
{
if(p->ci == 0){
f |= Fdone;
pr = 0;
- }else if(cause & Adps)
+ }else if(cause & Adps){
pr = 0;
+ }else if(cause & Atfes){
+ f |= Ferror;
+ ewake = 1;
+ pr = 0;
+ }
if(cause & Ifatal){
ewake = 1;
dprint("%s: fatal\n", dnam(d));
d->state = Doffline;
break;
}
- dprint("%s: %s → %s [Apcrs] %.3lux\n", dnam(d), diskstates[s0],
- diskstates[d->state], p->sstatus);
+ dprint("%s: updatedrive: %s → %s [ss %.3lux]\n",
+ dnam(d), diskstates[s0], diskstates[d->state], p->sstatus);
if(s0 == Dready && d->state != Dready)
- idprint("%s: pulled\n", dnam(d));
+ dprint("%s: pulled\n", dnam(d));
if(d->state != Dready)
f |= Ferror;
if(d->state != Dready || p->ci)
}
static void
-pstatus(Drive *d, ulong s)
+dstatus(Drive *d, int s)
{
- /*
- * bogus code because the first interrupt is currently dropped.
- * likely my fault. serror is maybe cleared at the wrong time.
- */
- switch(s){
+ dprint("%s: dstatus: %s → %s from pc=%p\n", dnam(d),
+ diskstates[d->state], diskstates[s], getcallerpc(&d));
+
+ ilock(d);
+ d->state = s;
+ iunlock(d);
+}
+
+static void
+configdrive(Drive *d)
+{
+ if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1){
+ dstatus(d, Dportreset);
+ return;
+ }
+
+ ilock(d);
+ switch(d->port->sstatus & Smask){
default:
- print("%s: pstatus: bad status %.3lux\n", dnam(d), s);
case Smissing:
d->state = Dmissing;
break;
case Spresent:
+ if(d->state == Dnull)
+ d->state = Dportreset;
break;
case Sphylink:
+ if(d->state == Dready)
+ break;
d->wait = 0;
d->state = Dnew;
break;
d->state = Doffline;
break;
}
-}
-
-static int
-configdrive(Drive *d)
-{
- if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1)
- return -1;
- ilock(d);
- pstatus(d, d->port->sstatus & Smask);
iunlock(d);
- return 0;
+
+ dprint("%s: configdrive: %s\n", dnam(d), diskstates[d->state]);
}
static void
det = p->sctl & 7;
stat = p->sstatus & Smask;
state = (p->cmd>>28) & 0xf;
- dprint("%s: resetdisk: icc %ux det %.3ux sdet %.3ux\n", dnam(d), state, det, stat);
+ dprint("%s: resetdisk [icc %ux; det %.3ux; sdet %.3ux]\n", dnam(d), state, det, stat);
ilock(d);
- state = d->state;
- if(d->state != Dready || d->state != Dnew)
+ if(d->state != Dready && d->state != Dnew)
d->portm.flag |= Ferror;
+ if(stat != Sphylink)
+ d->state = Dportreset;
+ else
+ d->state = Dreset;
clearci(p); /* satisfy sleep condition. */
wakeup(&d->portm);
- d->state = Derror;
iunlock(d);
- if(stat != Sphylink){
- ilock(d);
- d->state = Dportreset;
- iunlock(d);
+ if(stat != Sphylink)
return;
- }
qlock(&d->portm);
- if(p->cmd&Ast && ahciswreset(&d->portc) == -1){
- ilock(d);
- d->state = Dportreset; /* get a bigger stick. */
- iunlock(d);
- }else{
- ilock(d);
- d->state = Dmissing;
- iunlock(d);
+ if(p->cmd&Ast && ahciswreset(&d->portc) == -1)
+ dstatus(d, Dportreset); /* get a bigger stick. */
+ else
configdrive(d);
- }
- dprint("%s: resetdisk: %s → %s\n", dnam(d), diskstates[state], diskstates[d->state]);
qunlock(&d->portm);
}
goto lose;
}
if(m->feat & Dpower && setfeatures(c, 0x85, 3*1000) == -1){
+ dprint("%s: can't disable apm\n", dnam(d));
m->feat &= ~Dpower;
if(ahcirecover(c) == -1)
goto lose;
}
-
- ilock(d);
- d->state = Dready;
- iunlock(d);
-
+ dstatus(d, Dready);
qunlock(c->m);
s = "";
lose:
idprint("%s: can't be initialized\n", dnam(d));
- ilock(d);
- d->state = Dnull;
- iunlock(d);
+ dstatus(d, Dnull);
qunlock(c->m);
return -1;
}
enum {
Nms = 256,
+ Mcomrwait = 1*1024/Nms - 1,
Mphywait = 2*1024/Nms - 1,
Midwait = 16*1024/Nms - 1,
- Mcomrwait = 64*1024/Nms - 1,
};
static void
hangck(Drive *d)
{
- if((d->portm.feat & Datapi) == 0 && d->active &&
- d->totick != 0 && (long)(Ticks - d->totick) > 0){
- dprint("%s: drive hung; resetting [%lux] ci %lux\n",
- dnam(d), d->port->task, d->port->ci);
+ if(d->active && d->totick != 0 && (long)(Ticks - d->totick) > 0){
+ dprint("%s: drive hung [task %lux; ci %lux; serr %lux]%s\n",
+ dnam(d), d->port->task, d->port->ci, d->port->serror,
+ d->nodma == 0 ? "; disabling dma" : "");
+ d->nodma = 1;
d->state = Dreset;
}
}
static ushort olds[NCtlr*NCtlrdrv];
-static int
+static void
doportreset(Drive *d)
{
- int i;
-
- i = -1;
qlock(&d->portm);
- if(ahciportreset(&d->portc, d->mode) == -1)
- dprint("ahci: ahciportreset fails\n");
- else
- i = 0;
+ ahciportreset(&d->portc, d->mode);
qunlock(&d->portm);
- dprint("ahci: portreset → %s [task %.4lux ss %.3lux]\n",
+
+ dprint("ahci: portreset: %s [task %lux; ss %.3lux]\n",
diskstates[d->state], d->port->task, d->port->sstatus);
- return i;
}
/* drive must be locked */
return (c->hba->cap & 0xf*Hiss)/Hiss;
}
+static void iainterrupt(Ureg*, void *);
+
static void
checkdrive(Drive *d, int i)
{
ushort s, sig;
+ if(d->ctlr->enabled == 0)
+ return;
+ if(d->driveno == 0)
+ iainterrupt(0, d->ctlr); /* check for missed irq's */
+
ilock(d);
s = d->port->sstatus;
if(s)
else
d->mode--;
if(d->mode == DMautoneg){
+ d->wait = 0;
d->state = Dportreset;
goto portreset;
}
if(d->unit == nil)
break;
if((++d->wait&Midwait) == 0){
- dprint("%s: slow reset %.3ux task=%lux; %d\n",
- dnam(d), s, d->port->task, d->wait);
+ dprint("%s: slow reset [task %lux; ss %.3ux; wait %d]\n",
+ dnam(d), d->port->task, s, d->wait);
goto reset;
}
s = (uchar)d->port->task;
case Doffline:
if(d->wait++ & Mcomrwait)
break;
- /* fallthrough */
case Derror:
case Dreset:
dprint("%s: reset [%s]: mode %d; status %.3ux\n",
break;
case Dportreset:
portreset:
- if(d->wait++ & 0xff && (s & Iactive) == 0)
+ if(d->wait++ & Mcomrwait)
break;
+ if(d->wait > Mcomrwait && (s & Iactive) == 0){
+ d->state = Dnull; /* stuck in portreset */
+ break;
+ }
dprint("%s: portreset [%s]: mode %d; status %.3ux\n",
dnam(d), diskstates[d->state], d->mode, s);
d->portm.flag |= Ferror;
clearci(d->port);
wakeup(&d->portm);
- if((s & Smask) == 0){
+ if((s & Smask) == Smissing){
d->state = Dmissing;
break;
}
{
int i;
+ while(waserror())
+ ;
for(;;){
tsleep(&up->sleep, return0, 0, Nms);
for(i = 0; i < niadrive; i++)
}
static void
-iainterrupt(Ureg*, void *a)
+iainterrupt(Ureg *u, void *a)
{
int i;
ulong cause, m;
cause &= ~m;
d = c->rawdrive + i;
ilock(d);
- if(d->port->isr && c->hba->pi & m)
+ if(d->port != nil && d->port->isr && c->hba->pi & m)
updatedrive(d);
c->hba->isr = m;
iunlock(d);
}
+ if(u == 0 && i > 0)
+ c->missirq++;
iunlock(c);
}
ahciencreset(Ctlr *c)
{
Ahba *h;
+ int i;
if(c->enctype == Eesb)
return 0;
h = c->hba;
h->emctl |= Emrst;
- while(h->emctl & Emrst)
- delay(1);
- return 0;
+ for(i = 0; i < 1000; i++){
+ if((h->emctl & Emrst) == 0)
+ return 0;
+ esleep(1);
+ }
+ print("%s: ahciencreset: hung ctlr\n", Tname(c));
+ return -1;
}
/*
return 0;
c = d->ctlr;
h = c->hba;
+
/* ensure last message has been transmitted */
- while(h->emctl & Tmsg)
- microdelay(1);
+ if(h->emctl & Tmsg)
+ return -1;
+
switch(c->enctype){
default:
- panic("%s: bad led type %d\n", dnam(d), c->enctype);
+ panic("%s: bad led type %d", dnam(d), c->enctype);
case Elmt:
memset(&msg, 0, sizeof msg);
msg.type = Mled;
memset(map, 0, sizeof map);
for(i = 0; i < niactlr; i++)
if(iactlr[i].enctype != 0){
- ahciencreset(iactlr + i);
+ if(ahciencreset(iactlr + i) == -1)
+ continue;
map[i] = 1;
j++;
}
if(j == 0)
pexit("no work", 1);
for(i = 0; i < niadrive; i++){
- iadrive[i]->nled = 3; /* hardcoded */
- if(iadrive[i]->ctlr->enctype == Eesb)
- iadrive[i]->nled = 3;
- iadrive[i]->ledbits = -1;
+ d = iadrive[i];
+ d->nled = 3; /* hardcoded */
+ if(d->ctlr->enctype == Eesb)
+ d->nled = 3;
+ d->ledbits = -1;
}
for(i = 0; ; i++){
t0 = Ticks;
for(j = 0; j < niadrive; ){
- c = iadrive[j]->ctlr;
- if(map[j] == 0)
- j += c->enctype;
- else if(c->enctype == Eesb){
+ d = iadrive[j];
+ c = d->ctlr;
+ if(map[c - iactlr] == 0)
+ j++;
+ else
+ if(c->enctype == Eesb){
blinkesb(c, i);
j += c->ndrive;
}else{
- d = iadrive[j++];
blink(d, i);
+ j++;
}
}
t1 = Ticks;
}
}
+static int
+waitready(Drive *d)
+{
+ ulong s, i, δ;
+
+ for(i = 0;; i += 250){
+ if(d->state == Dreset || d->state == Dportreset || d->state == Dnew)
+ return 1;
+ ilock(d);
+ s = d->port->sstatus;
+ if(d->state == Dready && (s & Smask) == Sphylink){
+ iunlock(d);
+ return 0;
+ }
+ δ = Ticks - d->lastseen;
+ if(d->state == Dnull || δ > 10*1000)
+ break;
+ if((s & Imask) == 0 && δ > 1500)
+ break;
+ if(i >= 15*1000){
+ d->state = Doffline;
+ iunlock(d);
+ print("%s: not responding; offline\n", dnam(d));
+ return -1;
+ }
+ iunlock(d);
+ esleep(250);
+ }
+ iunlock(d);
+ return -1;
+}
+
static int
iaverify(SDunit *u)
{
return 1;
}
+static int
+iaonline(SDunit *u)
+{
+ int r;
+ Ctlr *c;
+ Drive *d;
+
+ c = u->dev->ctlr;
+ d = c->drive[u->subno];
+
+ while(d->state != Dmissing && waitready(d) == 1)
+ esleep(1);
+
+ dprint("%s: iaonline: %s\n", dnam(d), diskstates[d->state]);
+
+ ilock(d);
+ if(d->portm.feat & Datapi){
+ r = d->drivechange;
+ d->drivechange = 0;
+ iunlock(d);
+ if(r != 0)
+ scsiverify(u);
+ return scsionline(u);
+ }
+ r = 0;
+ if(d->drivechange){
+ d->drivechange = 0;
+ r = 2;
+ }else if(d->state == Dready)
+ r = 1;
+ if(r){
+ u->sectors = d->sectors;
+ u->secsize = d->secsize;
+ }
+ iunlock(d);
+
+ return r;
+}
+
static int
iaenable(SDev *s)
{
c = s->ctlr;
ilock(c);
- if(!c->enabled){
- if(once == 0)
- kproc("iasata", satakproc, 0);
- if(c->ndrive == 0)
- panic("iaenable: zero s->ctlr->ndrive");
- pcisetbme(c->pci);
- snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
- intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
- /* supposed to squelch leftover interrupts here. */
- ahcienable(c->hba);
- c->enabled = 1;
- if(++once == niactlr)
- kproc("ialed", ledkproc, 0);
+ if(c->enabled){
+ iunlock(c);
+ return 1;
}
+ if(c->ndrive == 0)
+ panic("iaenable: zero s->ctlr->ndrive");
+ pcisetbme(c->pci);
+ snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
+ intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
+ /* supposed to squelch leftover interrupts here. */
+ ahcienable(c->hba);
+ c->enabled = 1;
iunlock(c);
+
+ if(once == 0)
+ kproc("iasata", satakproc, 0);
+ if(++once == niactlr)
+ kproc("ialed", ledkproc, 0);
+
return 1;
}
return 1;
}
-static int
-iaonline(SDunit *unit)
-{
- int r;
- Ctlr *c;
- Drive *d;
-
- c = unit->dev->ctlr;
- d = c->drive[unit->subno];
- r = 0;
-
- if(d->portm.feat & Datapi && d->drivechange){
- r = scsionline(unit);
- if(r > 0)
- d->drivechange = 0;
- return r;
- }
-
- ilock(d);
- if(d->drivechange){
- r = 2;
- d->drivechange = 0;
- /* devsd resets this after online is called; why? */
- unit->sectors = d->sectors;
- unit->secsize = d->secsize;
- }else if(d->state == Dready)
- r = 1;
- iunlock(d);
- return r;
-}
-
static Alist*
-ahcibuild(Aportm *m, int rw, void *data, uint n, vlong lba)
+ahcibuild(Drive *d, int rw, void *data, int nsect, vlong lba)
{
uchar *c;
uint flags;
- Alist *l;
+ Aportm *m;
- l = m->list;
+ m = &d->portm;
c = m->ctab->cfis;
- rwfis(m, c, rw, n, lba);
+ rwfis(m, c, rw, nsect, lba);
flags = Lpref;
if(rw == SDwrite)
flags |= Lwrite;
- mkalist(m, flags, data, 512*n);
- return l;
+ return mkalist(m, flags, data, nsect * d->secsize);
}
static Alist*
-ahcibuildpkt(Aportm *m, SDreq *r, void *data, int n)
+ahcibuildpkt(Drive *d, SDreq *r, void *data, int n)
{
uint flags;
+ Aportm *m;
uchar *c;
Actab *t;
- Alist *l;
- l = m->list;
+ m = &d->portm;
t = m->ctab;
c = t->cfis;
- atapirwfis(m, c, r->cmd, r->clen, n);
- flags = 1<<16 | Lpref | Latapi;
+
+ atapirwfis(m, c, r->cmd, r->clen, 0x2000);
+ if((n & 15) != 0 || d->nodma)
+ c[Ffeat] &= ~1; /* use pio */
+ else if(c[Ffeat] & 1 && d->info[62] & (1<<15)) /* dma direction */
+ c[Ffeat] = (c[Ffeat] & ~(1<<2)) | ((r->write == 0) << 2);
+ flags = Lpref | Latapi;
if(r->write != 0 && data)
flags |= Lwrite;
- mkalist(m, flags, data, n);
- return l;
+ return mkalist(m, flags, data, n);
}
static Alist*
-ahcibuildfis(Aportm *m, SDreq *r, void *data, uint n)
+ahcibuildfis(Drive *d, SDreq *r, void *data, uint n)
{
- uchar *c;
uint flags;
- Alist *l;
-
- l = m->list;
- c = m->ctab->cfis;
- if((r->ataproto & Pprotom) != Ppkt){
- memmove(c, r->cmd, r->clen);
- flags = Lpref;
- if(r->write || n == 0)
- flags |= Lwrite;
- mkalist(m, flags, data, n);
- }else{
- atapirwfis(m, c, r->cmd, r->clen, n);
- flags = 1<<16 | Lpref | Latapi;
- if(r->write && data)
- flags |= Lwrite;
- mkalist(m, flags, data, n);
- }
- return l;
-}
+ uchar *c;
+ Aportm *m;
-static int
-waitready(Drive *d)
-{
- ulong s, i, δ;
+ if((r->ataproto & Pprotom) == Ppkt)
+ return ahcibuildpkt(d, r, data, n);
- for(i = 0; i < 15000; i += 250){
- if(d->state == Dreset || d->state == Dportreset ||
- d->state == Dnew)
- return 1;
- δ = Ticks - d->lastseen;
- if(d->state == Dnull || δ > 10*1000)
- return -1;
- ilock(d);
- s = d->port->sstatus;
- iunlock(d);
- if((s & Imask) == 0 && δ > 1500)
- return -1;
- if(d->state == Dready && (s & Smask) == Sphylink)
- return 0;
- esleep(250);
- }
- print("%s: not responding; offline\n", dnam(d));
- ilock(d);
- d->state = Doffline;
- iunlock(d);
- return -1;
+ m = &d->portm;
+ c = m->ctab->cfis;
+ memmove(c, r->cmd, r->clen);
+ flags = Lpref;
+ if(r->write || n == 0)
+ flags |= Lwrite;
+ return mkalist(m, flags, data, n);
}
static int
static int
io(Drive *d, uint proto, int to, int interrupt)
{
- uint task, flag, rv;
+ uint task, flag, stat, rv;
Aport *p;
Asleep as;
if(interrupt){
d->active--;
d->port->ci = 0;
- if(ahcicomreset(&d->portc) == -1){
- ilock(d);
- d->state = Dreset;
- iunlock(d);
- }
+ if(ahcicomreset(&d->portc) == -1)
+ dstatus(d, Dreset);
return SDtimeout;
}
+
sleep(&d->portm, ahciclear, &as);
poperror();
d->active--;
ilock(d);
+ stat = d->state;
flag = d->portm.flag;
task = d->port->task;
iunlock(d);
rv = SDok;
- if(proto & Ppkt){
+ if(proto & Ppkt && stat == Dready){
rv = task >> 8 + 4 & 0xf;
flag &= ~Fahdrs;
flag |= Fdone;
- }else if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){
+ }else if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && stat == Dready){
d->port->ci = 0;
ahcirecover(&d->portc);
task = d->port->task;
static int
iariopkt(SDreq *r, Drive *d)
{
- int n, count, try, max;
+ int try, to;
uchar *cmd;
+ Alist *l;
cmd = r->cmd;
aprint("%s: %.2ux %.2ux %c %d %p\n", dnam(d), cmd[0], cmd[2],
"rw"[r->write], r->dlen, r->data);
+
r->rlen = 0;
- count = r->dlen;
- max = 65536;
+
+ /*
+ * prevent iaonline() to hang forever by timing out
+ * inquiry and capacity commands after 5 seconds.
+ */
+ to = 30*1000;
+ switch(cmd[0]){
+ case 0x9e: if(cmd[1] != 0x10) break;
+ case 0x25:
+ case 0x12:
+ to = 5*1000;
+ break;
+ }
for(try = 0; try < 10; try++){
- n = count;
- if(n > max)
- n = max;
qlock(&d->portm);
- ahcibuildpkt(&d->portm, r, r->data, n);
- r->status = io(d, Ppkt, 5000, 0);
- qunlock(&d->portm);
+ l = ahcibuildpkt(d, r, r->data, r->dlen);
+ r->status = io(d, Ppkt, to, 0);
switch(r->status){
case SDeio:
+ qunlock(&d->portm);
return SDeio;
case SDretry:
+ qunlock(&d->portm);
continue;
}
-// print("%.2ux :: %.2ux :: %.4ux\n", r->cmd[0], r->status, d->port->task);
- r->rlen = d->portm.list->len;
+ r->rlen = l->len;
+ qunlock(&d->portm);
return SDok;
}
print("%s: bad disk\n", dnam(d));
}
rw = write? SDwrite: SDread;
data = a;
+ dprint("%s: bio: %llud %c %lud %p\n",
+ dnam(d), lba, "rw"[rw], count, data);
+
for(try = 0; try < 10;){
n = count;
if(n > max)
n = max;
qlock(&d->portm);
- ahcibuild(&d->portm, rw, data, n, lba);
+ ahcibuild(d, rw, data, n, lba);
status = io(d, Pdma, 5000, 0);
qunlock(&d->portm);
switch(status){
}
try = 0;
count -= n;
- lba += n;
+ lba += n;
data += n * u->secsize;
if(count == 0)
return data - (uchar*)a;
uvlong lba;
Ctlr *c;
Drive *d;
- SDunit *unit;
+ SDunit *u;
- unit = r->unit;
- c = unit->dev->ctlr;
- d = c->drive[unit->subno];
+ u = r->unit;
+ c = u->dev->ctlr;
+ d = c->drive[u->subno];
if(d->portm.feat & Datapi)
return iariopkt(r, d);
cmd = r->cmd;
if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
return i;
- n = ahcibio(unit, r->lun, r->write, r->data, count, lba);
+ n = ahcibio(u, r->lun, r->write, r->data, count, lba);
if(n == -1)
return SDeio;
r->rlen = n;
static uchar bogusrfis[16] = {
[Ftype] 0x34,
[Fioport] 0x40,
-[Fstatus] 0x50,
-
+[Fstatus] 0x50,
[Fdev] 0xa0,
};
Ctlr *c;
Drive *d;
SDunit *u;
+ Alist *l;
u = r->unit;
c = u->dev->ctlr;
sdr0(d);
for(try = 0; try < 10; try++){
qlock(&d->portm);
- ahcibuildfis(&d->portm, r, r->data, r->dlen);
+ l = ahcibuildfis(d, r, r->data, r->dlen);
r->status = io(d, r->ataproto & Pprotom, -1, 1);
- qunlock(&d->portm);
switch(r->status){
case SDtimeout:
+ qunlock(&d->portm);
return sdsetsense(r, SDcheck, 11, 0, 6);
case SDeio:
+ qunlock(&d->portm);
return SDeio;
case SDretry:
+ qunlock(&d->portm);
continue;
}
- r->rlen = r->dlen;
- if((r->ataproto & Pprotom) == Ppkt)
- r->rlen = d->portm.list->len;
- return sdr(r, d, r->status);
+ r->rlen = (r->ataproto & Pprotom) == Ppkt ? l->len : r->dlen;
+ try = sdr(r, d, r->status);
+ qunlock(&d->portm);
+ return try;
}
print("%s: bad disk\n", dnam(d));
- r->status = SDeio;
- return SDeio;
-}
-
-/*
- * configure drives 0-5 as ahci sata (c.f. errata)
- */
-static int
-iaahcimode(Pcidev *p)
-{
- uint u;
-
- u = pcicfgr16(p, 0x92);
- dprint("ahci: %T: iaahcimode %.2ux %.4ux\n", p->tbdf, pcicfgr8(p, 0x91), u);
- pcicfgw16(p, 0x92, u | 0xf); /* ports 0-15 */
- return 0;
+ return r->status = SDeio;
}
enum{
static void
iasetupahci(Ctlr *c)
{
+ if(c->type != Tich)
+ return;
+
pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~Cmddec);
pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~Cmddec);
c->lmmio[Ghc] |= Ahcien;
- c->lmmio[Pi] = (1 << 6) - 1; /* 5 ports (supposedly ro pi reg) */
+ c->lmmio[Pi] = (1 << 6) - 1; /* 6 ports (supposedly ro pi reg) */
/* enable ahci mode; from ich9 datasheet */
pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5);
+
+ /* configure drives 0-5 as ahci sata (c.f. errata) */
+ pcicfgw16(c->pci, 0x92, pcicfgr16(c->pci, 0x92) | 0xf);
}
static void
default:
return -1;
case 0x8086:
+ if((p->did & 0xffff) == 0x1e02)
+ return Tich; /* c210 */
+ if((p->did & 0xffff) == 0x8c02)
+ return Tich; /* c220 */
+ if((p->did & 0xffff) == 0x24d1)
+ return Tich; /* 82801eb/er */
+ if((p->did & 0xffff) == 0x2653)
+ return Tich; /* 82801fbm */
if((p->did & 0xfffc) == 0x2680)
return Tesb;
if((p->did & 0xfffb) == 0x27c1)
return Tich; /* 82801g[bh]m */
+ if((p->did & 0xffff) == 0x2822)
+ return Tich; /* 82801 SATA RAID */
if((p->did & 0xffff) == 0x2821)
return Tich; /* 82801h[roh] */
if((p->did & 0xfffe) == 0x2824)
return Tich; /* ich8 */
if((p->did & 0xfffe) == 0x2922)
return Tich; /* ich9 */
- if((p->did & 0xffff) == 0x3a02)
+ if((p->did & 0xffff) == 0x3a02)
return Tich; /* 82801jd/do */
- if((p->did & 0xfefe) == 0x3a22)
+ if((p->did & 0xfefe) == 0x3a22)
return Tich; /* ich10, pch */
- if((p->did & 0xfff7) == 0x3b28)
+ if((p->did & 0xfff7) == 0x3b28)
return Tich; /* pchm */
if((p->did & 0xfffe) == 0x3b22)
return Tich; /* pch */
if(p->did == 0x3349)
return Tahci;
break;
+ case 0x1022:
+ /* Hudson SATA Controller [AHCI mode] */
+ if((p->did & 0xfffe) == 0x7800){
+ sbsetupahci(p);
+ return Tahci;
+ }
+ break;
case 0x10de:
case 0x1039:
case 0x1b4b:
iapnp(void)
{
int i, n, nunit, type;
- ulong io;
+ uintptr io;
Ctlr *c;
Drive *d;
Pcidev *p;
if(done)
return nil;
done = 1;
+
+ if(getconf("*noahci") != nil)
+ return nil;
+
+ if(getconf("*ahcidebug") != nil){
+ debug = 1;
+ datapi = 1;
+ }
+
memset(olds, 0xff, sizeof olds);
p = nil;
-loop:
while((p = pcimatch(p, 0, 0)) != nil){
if((type = didtype(p)) == -1)
continue;
- if(p->mem[Abar].bar == 0)
+ io = p->mem[Abar].bar;
+ if(io == 0 || (io & 1) != 0 || p->mem[Abar].size < 0x180)
continue;
+ io &= ~0xf;
if(niactlr == NCtlr){
print("iapnp: %s: too many controllers\n", tname[type]);
break;
s = sdevs + niactlr;
memset(c, 0, sizeof *c);
memset(s, 0, sizeof *s);
- io = p->mem[Abar].bar & ~0xf;
c->mmio = vmap(io, p->mem[Abar].size);
if(c->mmio == 0){
print("%s: address %#p in use did %.4ux\n",
s->ctlr = c;
c->sdev = s;
- if(intel(c) && p->did != 0x2681)
+ pcienable(p);
+ ahcihandoff((Ahba*)c->mmio);
+ if(p->vid == 0x8086)
iasetupahci(c);
-// ahcihbareset((Ahba*)c->mmio);
nunit = ahciconf(c);
- if(intel(c) && iaahcimode(p) == -1 || nunit < 1){
+ if(nunit < 1){
vunmap(c->mmio, p->mem[Abar].size);
+ pcidisable(p);
continue;
}
c->ndrive = s->nunit = nunit;
d->ctlr = c;
if((c->hba->pi & 1<<i) == 0)
continue;
- snprint(d->name, sizeof d->name, "iahci%d.%d", niactlr, i);
- d->port = (Aport*)(c->mmio + 0x80*i + 0x100);
+ io = 0x100 + 0x80*i;
+ if((io + 0x80) > p->mem[Abar].size)
+ continue;
+ d->port = (Aport*)(c->mmio + io);
d->portc.p = d->port;
d->portc.m = &d->portm;
d->driveno = n++;
+ snprint(d->name, sizeof d->name, "iahci%d.%d", niactlr, i);
c->drive[d->driveno] = d;
iadrive[niadrive + d->driveno] = d;
}
- for(i = 0; i < n; i++)
- if(ahciidle(c->drive[i]->port) == -1){
- print("%s: port %d wedged; abort\n",
- Tname(c), i);
- goto loop;
- }
for(i = 0; i < n; i++){
c->drive[i]->mode = DMautoneg;
configdrive(c->drive[i]);
p = capfmt(p, e, ctab, nelem(ctab), o->cmd);
p = seprint(p, e, "\n");
p = seprint(p, e, "mode\t%s %s\n", modes[d->mode], modes[maxmode(c)]);
- p = seprint(p, e, "geometry %llud %lud\n", d->sectors, u->secsize);
+ p = seprint(p, e, "geometry %llud %d\n", d->sectors, d->secsize);
+ p = seprint(p, e, "alignment %d %d\n",
+ d->secsize<<d->portm.physshift, d->portm.physalign);
+ p = seprint(p, e, "missirq\t%ud\n", c->missirq);
return p - op;
}
break;
if(i == nelem(diskstates))
error(Ebadctl);
- ilock(d);
- d->state = i;
-// statechange(d);
- iunlock(d);
+ dstatus(d, i);
}
static int