new: 1. attempt to support unknown chipsets with ahci support. 2. recognize more intel vids. fixes: 1. fix race caused by misplaced inc/dec of active. 2. deal with state change more uniformly. 3. fix reset → portreset transition. Notes: Wed Mar 19 15:07:22 EDT 2008 geoff Please (re)read style(6) and edit your code to conform, then resubmit this patch. Thanks. Reference: /n/sources/patch/sorry/ahciupd2 Date: Fri Mar 14 03:34:38 CET 2008 Signed-off-by: quanstro@quanstro.net Reviewed-by: geoff --- /sys/src/9/pc/sdiahci.c Fri Mar 14 03:31:05 2008 +++ /sys/src/9/pc/sdiahci.c Fri Mar 14 03:31:01 2008 @@ -1,6 +1,6 @@ /* * intel/amd ahci sata controller - * copyright © 2007 coraid, inc. + * copyright © 2007-8 coraid, inc. */ #include "u.h" @@ -15,8 +15,9 @@ #define dprint(...) if(debug) iprint(__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 aprint(...) if(datapi) print(__VA_ARGS__); else USED(datapi) #define Tname(c) tname[(c)->type] +#define Ticks MACHP(0)->ticks enum { NCtlr = 4, @@ -38,14 +39,16 @@ Tesb, Tich, Tsb600, + Tunk, }; -#define Intel(x) ((x) == Tesb || (x) == Tich) +#define Intel(x) ((x)->pci->vid == 0x8086) static char *tname[] = { "63xxesb", "ich", "sb600", + "ahci", }; enum { @@ -151,7 +154,7 @@ static Drive *iadrive[NDrive]; static int niadrive; -static int debug; +static int debug = 0; static int prid = 1; static int datapi; @@ -759,7 +762,7 @@ ushort s; s = p->sstatus; - if((s & 0xF00) != 0x600) + if((s & 0x700) != 0x600) return; if((s & 7) != 1){ /* not (device, no phy) */ iprint("ahci: slumbering drive unwakeable %ux\n", s); @@ -801,7 +804,7 @@ p->fishi = 0; p->cmd |= Afre|Ast; - if((p->sstatus & 0xF0F) == 0x601) /* drive coming up in slumbering? */ + if((p->sstatus & 0x707) == 0x601) /* drive coming up in slumbering? */ ahciwakeup(p); /* disable power managment sequence from book. */ @@ -840,20 +843,20 @@ } static int -ahciconf(Ctlr *ctlr) +ahciconf(Ctlr *c) { Ahba *h; ulong u; - h = ctlr->hba = (Ahba*)ctlr->mmio; + h = c->hba = (Ahba*)c->mmio; u = h->cap; if((u&Hsam) == 0) h->ghc |= Hae; - print("#S/sd%c: ahci: port %#p: hba sss %ld; ncs %ld; coal %ld; " - "mports %ld; led %ld; clo %ld; ems %ld\n", - ctlr->sdev->idno, h, + print("#S/sd%c: ahci %s port %#p: sss %ld ncs %ld coal %ld " + "mports %ld led %ld clo %ld ems %ld\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); @@ -925,8 +928,7 @@ u->inquiry[4] = sizeof u->inquiry - 4; memmove(u->inquiry+8, d->model, 40); - if((osectors == 0 || osectors != s) && - memcmp(oserial, d->serial, sizeof oserial) != 0){ + if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){ d->mediachange = 1; u->sectors = 0; } @@ -992,7 +994,7 @@ d->state = Dmissing; break; case 1: /* device but no phy comm. */ - if((p->sstatus & 0xF00) == 0x600) + if((p->sstatus & 0x700) == 0x600) d->state = Dnew; /* slumbering */ else d->state = Derror; @@ -1074,12 +1076,7 @@ stat = p->sstatus & 7; state = (p->cmd>>28) & 0xf; dprint("resetdisk: icc %ux det %d sdet %d\n", state, det, stat); - if(stat != 3){ /* device absent or phy not communicating? */ - ilock(d); - d->state = Dportreset; - iunlock(d); - return; - } + ilock(d); state = d->state; if(d->state != Dready || d->state != Dnew) @@ -1089,8 +1086,14 @@ d->state = Derror; iunlock(d); - qlock(&d->portm); + if(stat != 3){ /* device absent or phy not communicating? */ + ilock(d); + d->state = Dportreset; + iunlock(d); + return; + } + qlock(&d->portm); if(p->cmd&Ast && ahciswreset(&d->portc) == -1){ ilock(d); d->state = Dportreset; /* get a bigger stick. */ @@ -1171,8 +1174,8 @@ westerndigitalhung(Drive *d) { if((d->portm.feat&Datapi) == 0 && d->active && - TK2MS(MACHP(0)->ticks - d->intick) > 5000){ - dprint("%s: drive hung; resetting [%lux] ci=%lx\n", + TK2MS(Ticks-d->intick) > 5000){ + dprint("%s: drive hung; resetting [%lux] ci=%lux\n", d->unit->name, d->port->task, d->port->ci); d->state = Dreset; } @@ -1197,6 +1200,22 @@ return i; } +/* drive must be locked */ +static void +statechange(Drive *d) +{ + switch(d->state){ + case Dnull: +// case Dmissing: + case Doffline: + if(d->unit->sectors != 0){ + d->sectors = 0; + d->mediachange = 1; + } + } + d->wait = 0; +} + static void checkdrive(Drive *d, int i) { @@ -1207,7 +1226,7 @@ name = d->unit->name; s = d->port->sstatus; if(s) - d->lastseen = MACHP(0)->ticks; + d->lastseen = Ticks; if(s != olds[i]){ dprint("%s: status: %04ux -> %04ux: %s\n", name, olds[i], s, diskstates[d->state]); @@ -1293,6 +1312,7 @@ ilock(d); break; } + statechange(d); iunlock(d); } @@ -1546,7 +1566,7 @@ if(d->state == Dreset || d->state == Dportreset || d->state == Dnew) return 1; - δ = MACHP(0)->ticks - d->lastseen; + δ = Ticks-d->lastseen; if(d->state == Dnull || δ > 10*1000) return -1; ilock(d); @@ -1570,11 +1590,12 @@ { int i; +loop: qlock(&d->portm); - while ((i = waitready(d)) == 1) { + if((i = waitready(d)) == 1){ qunlock(&d->portm); esleep(1); - qlock(&d->portm); + goto loop; } return i; } @@ -1618,7 +1639,6 @@ n = count; if(n > max) n = max; - d->active++; ahcibuildpkt(&d->portm, r, data, n); switch(waitready(d)){ case -1: @@ -1637,25 +1657,26 @@ as.p = p; as.i = 1; - d->intick = MACHP(0)->ticks; + d->intick = Ticks; + d->active++; while(waserror()) ; sleep(&d->portm, ahciclear, &as); poperror(); + d->active--; ilock(d); flag = d->portm.flag; task = d->port->task; iunlock(d); if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){ - d->port->ci = 0; /* clearci? */ + d->port->ci = 0; ahcirecover(&d->portc); task = d->port->task; flag &= ~Fdone; /* either an error or do-over */ } - d->active--; qunlock(&d->portm); if(flag == 0){ if(++try == 10){ @@ -1734,7 +1755,6 @@ n = count; if(n > max) n = max; - d->active++; ahcibuild(&d->portm, cmd, data, n, lba); switch(waitready(d)){ case -1: @@ -1752,13 +1772,15 @@ as.p = p; as.i = 1; - d->intick = MACHP(0)->ticks; + d->intick = Ticks; + d->active++; while(waserror()) ; sleep(&d->portm, ahciclear, &as); poperror(); + d->active--; ilock(d); flag = d->portm.flag; task = d->port->task; @@ -1766,11 +1788,10 @@ if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){ - d->port->ci = 0; /* @? */ + d->port->ci = 0; ahcirecover(&d->portc); task = d->port->task; } - d->active--; qunlock(&d->portm); if(flag == 0){ if(++try == 10){ @@ -1816,11 +1837,13 @@ pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~(1<<15)); pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~(1<<15)); +pcicfgw16(c->pci, 0x92, (1<<6)-1); c->lmmio[0x4/4] |= 1 << 31; /* enable ahci mode (ghc register) */ c->lmmio[0xc/4] = (1 << 6) - 1; /* 5 ports. (supposedly ro pi reg.) */ /* enable ahci mode; from ich9 datasheet */ - pcicfgw8(c->pci, 0x90, 1<<6 | 1<<5); +// pcicfgw8(c->pci, 0x90, 1<<6 | 1<<5); + pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5); } static SDev* @@ -1841,23 +1864,29 @@ head = tail = nil; loop: while((p = pcimatch(p, 0, 0)) != nil){ - /* 0x27c4 is the intel 82801 in compatibility (not sata) mode */ if(p->vid == 0x8086 && (p->did & 0xfffc) == 0x2680) type = Tesb; - else if(p->vid == 0x8086 && p->did == 0x27c5) - type = Tich; /* 82801g[bh]m; compat mode fails */ + else if(p->vid == 0x8086 && (p->did & 0xfffe) == 0x27c5) + type = Tich; /* 82801g[bh]m */ + else if(p->vid == 0x8086 && (p->did & 0xfeff) == 0x2829) + type = Tich; /* ich8 */ + else if(p->vid == 0x8086 && (p->did & 0xfffe) == 0x2922) + type = Tich; /* ich9 */ else if(p->vid == 0x1002 && p->did == 0x4380) type = Tsb600; - else + else if(p->ccrb != Pcibcstore || p->ccru != 6 || p->ccrp != 1) continue; - if (p->mem[Abar].bar == 0) + else + type = Tunk; /* i'm feeling lucky */ + + if(p->mem[Abar].bar == 0) continue; if(niactlr == NCtlr){ print("%spnp: too many controllers\n", tname[type]); break; } c = iactlr + niactlr; - s = sdevs + niactlr; + s = sdevs + niactlr; memset(c, 0, sizeof *c); memset(s, 0, sizeof *s); io = p->mem[Abar].bar & ~0xf; @@ -1876,11 +1905,11 @@ s->ctlr = c; c->sdev = s; - if(Intel(c->type) && p->did != 0x2681) + if(Intel(c) && p->did != 0x2681) iasetupahci(c); nunit = ahciconf(c); // ahcihbareset((Ahba*)c->mmio); - if(Intel(c->type) && iaahcimode(p) == -1) + if(Intel(c) && iaahcimode(p) == -1) break; if(nunit < 1){ vunmap(c->mmio, p->mem[Abar].size); @@ -1994,10 +2023,10 @@ { long t0; - t0 = MACHP(0)->ticks; + t0 = Ticks; if(flushcache(d) != 0) error(Eio); - dprint("flush in %ldms\n", MACHP(0)->ticks - t0); + dprint("flush in %ldms\n", Ticks-t0); } static void @@ -2040,14 +2069,10 @@ if(strcmp(state, diskstates[i]) == 0) break; if(i == nelem(diskstates)) - i = 0; + error(Ebadctl); ilock(d); d->state = i; - if(i == Dnull){ - d->mediachange = 1; - if(d->unit) - d->unit->sectors = 0; /* force disk to disappear. */ - } +// statechange(d); iunlock(d); } @@ -2142,42 +2167,48 @@ return p; } +struct{ + ulong bit; + char *name; +}htab[] = { + Hs64a, "64a", + Hsalp, "alp", + Hsam, "am", + Hsclo, "clo", + Hcccs, "coal", + Hems, "ems", + Hsal, "led", + Hsmps, "mps", + Hsncq, "ncq", + Hssntf, "ntf", + Hspm, "pm", + Hpsc, "pslum", + Hssc, "slum", + Hsss, "ss", + Hsxs, "sxs", +}; + /* must emit exactly one line per controller (sd(3)) */ static char* -iartopctl(SDev *sdev, char *p, char *e) +iartopctl(SDev *s, char *p, char *e) { - ulong cap; char pr[25]; - Ahba *hba; - Ctlr *ctlr; - -#define has(x, str) if(cap & (x)) p = seprint(p, e, "%s ", (str)) + ulong cap, i; + Ahba *h; + Ctlr *c; - ctlr = sdev->ctlr; - hba = ctlr->hba; - p = seprint(p, e, "sd%c ahci port %#p: ", sdev->idno, hba); - cap = hba->cap; - has(Hs64a, "64a"); - has(Hsalp, "alp"); - has(Hsam, "am"); - has(Hsclo, "clo"); - has(Hcccs, "coal"); - has(Hems, "ems"); - has(Hsal, "led"); - has(Hsmps, "mps"); - has(Hsncq, "ncq"); - has(Hssntf, "ntf"); - has(Hspm, "pm"); - has(Hpsc, "pslum"); - has(Hssc, "slum"); - has(Hsss, "ss"); - has(Hsxs, "sxs"); - portr(pr, pr + sizeof pr, hba->pi); + c = s->ctlr; + h = c->hba; + p = seprint(p, e, "sd%c ahci %s port %#p: ", s->idno, Tname(c), h); + cap = h->cap; + for(i = 0; i < nelem(htab); i++) + if(cap&htab[i].bit) + p = seprint(p, e, "%s ", htab[i].name); + portr(pr, pr + sizeof pr, h->pi); return seprint(p, e, - "iss %ld ncs %ld np %ld; ghc %lux isr %lux pi %lux %s ver %lux\n", + "iss %ld ncs %ld np %ld ghc %lux isr %lux pi %lux %s ver %lux\n", (cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f), - hba->ghc, hba->isr, hba->pi, pr, hba->ver); -#undef has + h->ghc, h->isr, h->pi, pr, h->ver); } static int