From d9975caeb001e83ef89c9bce8865c70bf97d6404 Mon Sep 17 00:00:00 2001 From: Giacomo Tesio Date: Fri, 11 Aug 2017 00:11:20 +0200 Subject: [PATCH] kernel: import sdahci from 9front --- sys/src/kern/amd64/sdahci.c | 1282 ++++++++++++++++++++++------------- 1 file changed, 807 insertions(+), 475 deletions(-) diff --git a/sys/src/kern/amd64/sdahci.c b/sys/src/kern/amd64/sdahci.c index 42b08d4..f951b87 100644 --- a/sys/src/kern/amd64/sdahci.c +++ b/sys/src/kern/amd64/sdahci.c @@ -17,15 +17,18 @@ #include "../port/led.h" #pragma varargck type "T" int -#define dprint(...) if(debug) iprint(__VA_ARGS__); else USED(debug) -#define idprint(...) if(prid) jehanne_print(__VA_ARGS__); else USED(prid) -#define aprint(...) if(datapi) jehanne_print(__VA_ARGS__); else USED(datapi) -#define ledprint(...) if(dled) jehanne_print(__VA_ARGS__); else USED(dled) +#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 Pciwaddrh(a) 0 +#define Tname(c) tname[(c)->type] #define Ticks sys->ticks +#define MS2TK(t) (((uint32_t)(t)*HZ)/1000) enum { NCtlr = 4, - NCtlrdrv = 32, + NCtlrdrv = 32, NDrive = NCtlr*NCtlrdrv, Fahdrs = 4, @@ -33,54 +36,42 @@ enum { Read = 0, Write, - Eesb = 1<<0, /* must have (Eesb & Emtype) == 0 */ + Eesb = 1<<0, /* must have (Eesb & Emtype) == 0 */ +}; - /* pci space configuration */ - Pmap = 0x90, - Ppcs = 0x91, - - Nms = 256, - Mphywait = 2*1024/Nms - 1, - Midwait = 16*1024/Nms - 1, - Mcomrwait = 64*1024/Nms - 1, +/* pci space configuration */ +enum { + Pmap = 0x90, + Ppcs = 0x91, + Prev = 0xa8, }; enum { Tesb, + Tich, Tsb600, Tjmicron, Tahci, - Tlast, }; -typedef struct Ctlrtype Ctlrtype; -typedef struct Ctlr Ctlr; -typedef struct Drive Drive; - -struct Ctlrtype { - uint32_t type; - uint32_t maxdmaxfr; - uint32_t flags; - char *name; -}; - -Ctlrtype cttab[Tlast] = { -[Tesb] Tesb, 8192, 0, "63xxesb", -[Tsb600] Tsb600, 256, 0, "sb600", -[Tjmicron] Tjmicron, 8192, 0, "jmicron", -[Tahci] Tahci, 8192, 0, "ahci", +static char *tname[] = { + "63xxesb", + "ich", + "sb600", + "jmicron", + "ahci", }; enum { - Dnull = 0, - Dmissing = 1<<0, - Dnew = 1<<1, - Dready = 1<<2, - Derror = 1<<3, - Dreset = 1<<4, - Doffline = 1<<5, - Dportreset = 1<<6, - Dlast = 8, + Dnull, + Dmissing, + Dnew, + Dready, + Derror, + Dreset, + Doffline, + Dportreset, + Dlast, }; static char *diskstates[Dlast] = { @@ -94,7 +85,8 @@ static char *diskstates[Dlast] = { "portreset", }; -extern SDifc sdahciifc; +extern SDifc sdiahciifc; +typedef struct Ctlr Ctlr; enum { DMautoneg, @@ -117,7 +109,7 @@ struct Htab { char *name; }; -struct Drive { +typedef struct { Lock; Ctlr *ctlr; @@ -128,11 +120,24 @@ struct Drive { Aportc portc; /* redundant ptr to port and portm. */ Ledport; + uint8_t drivechange; + uint8_t nodma; + uint8_t state; + + uint64_t sectors; + uint32_t secsize; uint32_t totick; uint32_t lastseen; uint32_t wait; uint8_t mode; - uint8_t state; + uint8_t active; + + char serial[20+1]; + char firmware[8+1]; + char model[40+1]; + uint64_t wwn; + + uint16_t info[0x200]; /* * ahci allows non-sequential ports. @@ -142,12 +147,12 @@ struct Drive { */ uint32_t driveno; uint32_t portno; -}; +} Drive; struct Ctlr { Lock; - Ctlrtype *type; + int type; int enabled; SDev *sdev; Pcidev *pci; @@ -161,13 +166,13 @@ struct Ctlr { Drive rawdrive[NCtlrdrv]; Drive* drive[NCtlrdrv]; int ndrive; - uint32_t pi; + + uint32_t missirq; }; static Ctlr iactlr[NCtlr]; static SDev sdevs[NCtlr]; static int niactlr; -static uint16_t olds[NCtlr*NCtlrdrv]; static Drive *iadrive[NDrive]; static int niadrive; @@ -198,10 +203,29 @@ serrstr(uint32_t r, char *s, char *e) *s = 0; } +static char ntab[] = "0123456789abcdef"; + +static void +preg(uint8_t *reg, int n) +{ + char buf[25*3+1], *e; + int i; + + e = buf; + for(i = 0; i < n; i++){ + *e++ = ntab[reg[i] >> 4]; + *e++ = ntab[reg[i] & 0xf]; + *e++ = ' '; + } + *e++ = '\n'; + *e = 0; + dprint(buf); +} + static void dreg(char *s, Aport *p) { - dprint("%stask=%ux; cmd=%ux; ci=%ux; is=%ux\n", + dprint("%stask=%lux; cmd=%lux; ci=%lux; is=%lux\n", s, p->task, p->cmd, p->ci, p->isr); } @@ -254,7 +278,7 @@ ahciwait(Aportc *c, int ms) return -1; } -static void +static Alist* mkalist(Aportm *m, uint32_t flags, uint8_t *data, int len) { Actab *t; @@ -262,22 +286,23 @@ mkalist(Aportm *m, uint32_t flags, uint8_t *data, int len) Aprdt *p; t = m->ctab; + 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 = Pciwaddrl(t); + l->ctab = PCIWADDR(t); l->ctabhi = Pciwaddrh(t); - if(data){ - l->flags |= 1<<16; - p = &t->prdt; - p->dba = Pciwaddrl(data); - p->dbahi = Pciwaddrh(data); - p->count = 1<<31 | len - 2 | 1; - } + return l; } static int -settxmode(Aportc *pc, uint8_t f) +nop(Aportc *pc) { uint8_t *c; @@ -291,7 +316,7 @@ settxmode(Aportc *pc, uint8_t f) static void asleep(int ms) { - if(up == nil) + if(up == nil || !islo()) delay(ms); else esleep(ms); @@ -300,8 +325,7 @@ asleep(int ms) static int ahciportreset(Aportc *c, uint32_t mode) { - int i; - uint32_t *cmd; + uint32_t *cmd, i; Aport *p; p = c->p; @@ -312,17 +336,77 @@ ahciportreset(Aportc *c, uint32_t mode) 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 +ahciflushcache(Aportc *pc) +{ + uint8_t *c; + + c = pc->m->ctab->cfis; + flushcachefis(pc->m, c); + mkalist(pc->m, Lwrite, 0, 0); + + if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){ + dprint("ahciflushcache fail [task %lux]\n", pc->p->task); +// preg(pc->m->fis.r, 20); + return -1; + } + return 0; +} + +static int +ahciidentify0(Aportc *pc, void *id) +{ + uint8_t *c; + Actab *t; + + t = pc->m->ctab; + c = t->cfis; + memset(id, 0, 0x200); + identifyfis(pc->m, c); + mkalist(pc->m, 0, id, 0x200); + return ahciwait(pc, 3*1000); +} + +static vlong +ahciidentify(Aportc *pc, uint16_t *id, uint32_t *ss, char *d) +{ + int i, n; + vlong s; + Aportm *m; + + m = pc->m; + for(i = 0;; i++){ + if(i > 5 || ahciidentify0(pc, id) != 0) + return -1; + n = idpuis(id); + if(n & Pspinup && setfeatures(pc, 7, 20*1000) == -1) + print("%s: puis spinup fail\n", d); + if(n & Pidready) + break; + print("%s: puis waiting\n", d); + } + s = idfeat(m, id); + *ss = idss(m, id); + if(s == -1 || (m->feat&Dlba) == 0){ + if((m->feat&Dlba) == 0) + dprint("%s: no lba support\n", d); + return -1; + } + return s; +} + static int ahciquiet(Aport *a) { - int i; - uint32_t *p; + uint32_t *p, i; p = &a->cmd; *p &= ~Ast; @@ -347,7 +431,7 @@ stop: return -1; stop1: /* extra check */ - dprint("ahci: clo clear %ux\n", a->task); + dprint("ahci: clo clear [task %lux]\n", a->task); if(a->task & ASbsy) return -1; *p |= Afre | Ast; @@ -389,8 +473,7 @@ ahcicomreset(Aportc *pc) static int ahciidle(Aport *port) { - int i, r; - uint32_t *p; + uint32_t *p, i, r; p = &port->cmd; if((*p & Arun) == 0) @@ -444,10 +527,20 @@ ahcirecover(Aportc *pc) return 0; } +static void* +malign(int size, int align) +{ + void *v; + + v = xspanalloc(size, align, 0); + memset(v, 0, size); + return v; +} + static void setupfis(Afis *f) { - f->base = jehanne_mallocalign(0x100, 0x100, 0, 0); + f->base = malign(0x100, 0x100); f->d = f->base + 0; f->p = f->base + 0x20; f->r = f->base + 0x40; @@ -459,16 +552,18 @@ static void ahciwakeup(Aportc *c, uint32_t mode) { uint16_t s; + Aport *p; - s = c->p->sstatus; + p = c->p; + s = p->sstatus; if((s & Isleepy) == 0) return; if((s & Smask) != Spresent){ - jehanne_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 @@ -476,66 +571,74 @@ ahciconfigdrive(Ahba *h, Aportc *c, int mode) { Aportm *m; Aport *p; + int i; p = c->p; m = c->m; if(m->list == 0){ setupfis(&m->fis); - m->list = jehanne_mallocalign(sizeof *m->list, 1024, 0, 0); - m->ctab = jehanne_mallocalign(sizeof *m->ctab, 128, 0, 0); + m->list = malign(sizeof *m->list, 1024); + m->ctab = malign(sizeof *m->ctab, 128); } - p->list = Pciwaddrl(m->list); + if(ahciidle(p) == -1){ + dprint("ahci: port not idle\n"); + return -1; + } + + p->list = PCIWADDR(m->list); p->listhi = Pciwaddrh(m->list); - p->fis = Pciwaddrl(m->fis.base); + p->fis = PCIWADDR(m->fis.base); p->fishi = Pciwaddrh(m->fis.base); p->cmd |= Afre; - if((p->sstatus & Sbist) == 0 && (p->cmd & Apwr) != Apwr) - if((p->sstatus & Sphylink) == 0 && h->cap & Hss){ - dprint("ahci: spin up ... [%.3ux]\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; } -static void -setstate(Drive *d, int state) -{ - ilock(d); - d->state = state; - iunlock(d); -} - -static void +static int ahcienable(Ahba *h) { h->ghc |= Hie; + return 0; } -static void +static int ahcidisable(Ahba *h) { h->ghc &= ~Hie; + return 0; } static int @@ -558,11 +661,30 @@ ahciconf(Ctlr *c) h = c->hba = (Ahba*)c->mmio; u = h->cap; + if((u & Ham) == 0) h->ghc |= Hae; + 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) { @@ -570,60 +692,13 @@ 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); } return -1; } -/* under development */ -static int -ahcibioshandoff(Ahba *h) -{ - int i, wait; - - if((h->cap2 & Boh) == 0) - return 0; - if((h->bios & Bos) == 0) - return 0; - - jehanne_print("ahcibioshandoff: claim\n"); - h->bios |= Oos; - - wait = 25; - for(i = 0; i < wait; i++){ - delay(1); - if((h->bios & Bos) == 0) - break; - if(i < 25 && h->bios & Bb){ - jehanne_print("ahcibioshandoff: busy\n"); - wait = 2000; - } - } - if(i == wait){ - jehanne_print("ahcibioshandoff: timeout %.1ux\n", h->bios); - h->bios = Oos; - } - return 0; -} - -static char* -dstate(uint32_t s) -{ - int i; - - for(i = 0; s; i++) - s >>= 1; - return diskstates[i]; -} - -static char* -tnam(Ctlr *c) -{ - return c->type->name; -} - static char* dnam(Drive *d) { @@ -635,6 +710,43 @@ dnam(Drive *d) return s; } +static int +identify(Drive *d) +{ + uint8_t oserial[21]; + uint16_t *id; + vlong osectors, s; + SDunit *u; + + id = d->info; + s = ahciidentify(&d->portc, id, &d->secsize, dnam(d)); + if(s == -1) + return -1; + osectors = d->sectors; + memmove(oserial, d->serial, sizeof d->serial); + + d->sectors = s; + + idmove(d->serial, id+10, 20); + idmove(d->firmware, id+23, 8); + idmove(d->model, id+27, 40); + d->wwn = idwwn(d->portc.m, id); + + u = d->unit; + memset(u->inquiry, 0, sizeof u->inquiry); + u->inquiry[2] = 2; + u->inquiry[3] = 2; + u->inquiry[4] = sizeof u->inquiry - 4; + memmove(u->inquiry+8, d->model, 40); + + if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){ + d->drivechange = 1; + d->nodma = 0; + u->sectors = 0; + } + return 0; +} + static void clearci(Aport *p) { @@ -653,7 +765,7 @@ intel(Ctlr *c) static int ignoreahdrs(Drive *d) { - return d->portm.feat & Datapi && d->ctlr->type->type == Tsb600; + return d->portm.feat & Datapi && d->ctlr->type == Tsb600; } static void @@ -668,7 +780,7 @@ updatedrive(Drive *d) f = 0; p = d->port; cause = p->isr; - if(d->ctlr->type->type == Tjmicron) + if(d->ctlr->type == Tjmicron) cause &= ~Aifs; serr = p->serror; p->isr = cause; @@ -676,8 +788,13 @@ updatedrive(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)); @@ -686,7 +803,7 @@ updatedrive(Drive *d) if(p->task & 33){ if(ignoreahdrs(d) && serr & ErrE) f |= Fahdrs; - dprint("%s: Adhrs cause %ux serr %ux task %ux\n", + dprint("%s: Adhrs cause %lux serr %lux task %lux\n", dnam(d), cause, serr, p->task); f |= Ferror; ewake = 1; @@ -694,10 +811,10 @@ updatedrive(Drive *d) pr = 0; } if(p->task & 1 && last != cause) - dprint("%s: err ca %ux serr %ux task %ux sstat %.3ux\n", + dprint("%s: err ca %lux serr %lux task %lux sstat %.3lux\n", dnam(d), cause, serr, p->task, p->sstatus); if(pr) - dprint("%s: upd %ux ta %ux\n", dnam(d), cause, p->task); + dprint("%s: upd %lux ta %lux\n", dnam(d), cause, p->task); if(cause & (Aprcs|Aifs)){ s0 = d->state; @@ -720,10 +837,10 @@ updatedrive(Drive *d) d->state = Doffline; break; } - dprint("%s: %s → %s [Apcrs] %.3ux\n", dnam(d), dstate(s0), - dstate(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) @@ -740,23 +857,37 @@ updatedrive(Drive *d) } static void -pstatus(Drive *d, uint32_t 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. - */ - if(s) - d->lastseen = Ticks; - 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: - jehanne_print("%s: pstatus: bad status %.3ux\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; @@ -764,17 +895,9 @@ pstatus(Drive *d, uint32_t s) 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 @@ -787,36 +910,34 @@ resetdisk(Drive *d) 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){ - setstate(d, Dportreset); + if(stat != Sphylink) return; - } qlock(&d->portm); if(p->cmd&Ast && ahciswreset(&d->portc) == -1) - setstate(d, Dportreset); /* get a bigger stick. */ - else{ - setstate(d, Dmissing); + dstatus(d, Dportreset); /* get a bigger stick. */ + else configdrive(d); - } - dprint("%s: resetdisk: %s → %s\n", dnam(d), dstate(state), dstate(d->state)); qunlock(&d->portm); } static int newdrive(Drive *d) { + char *s; Aportc *c; Aportm *m; @@ -825,56 +946,81 @@ newdrive(Drive *d) qlock(c->m); setfissig(m, c->p->sig); + if(identify(d) == -1){ + dprint("%s: identify failure\n", dnam(d)); + goto lose; + } + if(settxmode(c, m->udma) == -1){ + dprint("%s: can't set udma mode\n", dnam(d)); + 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; + } + dstatus(d, Dready); qunlock(c->m); - if(ataonline(d->unit, m) != 0) - goto lose; - m->atamaxxfr = 128; - if(d->portm.feat & Dllba) - m->atamaxxfr = d->ctlr->type->maxdmaxfr; - - setstate(d, Dready); - pronline(d->unit, m); + s = ""; + if(m->feat & Dllba) + s = "L"; + idprint("%s: %sLBA %,lld sectors\n", dnam(d), s, d->sectors); + idprint(" %s %s %s %s\n", d->model, d->firmware, d->serial, + d->drivechange? "[newdrive]": ""); return 0; lose: - qlock(c->m); idprint("%s: can't be initialized\n", dnam(d)); - setstate(d, Dnull); + dstatus(d, Dnull); qunlock(c->m); return -1; } -static int -doportreset(Drive *d) -{ - int i; +enum { + Nms = 256, + Mphywait = 2*1024/Nms - 1, + Midwait = 16*1024/Nms - 1, + Mcomrwait = 64*1024/Nms - 1, +}; - i = -1; - qlock(&d->portm); - if(ahciportreset(&d->portc, d->mode) == -1) - dprint("ahci: ahciportreset fails\n"); - else - i = 0; - qunlock(&d->portm); - dprint("ahci: portreset → %s [task %.4ux ss %.3ux]\n", - dstate(d->state), d->port->task, d->port->sstatus); - return i; +static void +hangck(Drive *d) +{ + 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 uint16_t olds[NCtlr*NCtlrdrv]; + +static void +doportreset(Drive *d) +{ + qlock(&d->portm); + ahciportreset(&d->portc, d->mode); + qunlock(&d->portm); + + dprint("ahci: portreset: %s [task %lux; ss %.3lux]\n", + diskstates[d->state], d->port->task, d->port->sstatus); +} + +/* drive must be locked */ static void statechange(Drive *d) { - Aportm *m; - - m = &d->portm; switch(d->state){ case Dnull: case Doffline: if(d->unit) if(d->unit->sectors != 0){ - m->sectors = 0; - m->drivechange = 1; + d->sectors = 0; + d->drivechange = 1; } case Dready: d->wait = 0; @@ -887,21 +1033,29 @@ maxmode(Ctlr *c) return (c->hba->cap & 0xf*Hiss)/Hiss; } +static void iainterrupt(Ureg*, void *); + static void checkdrive(Drive *d, int i) { uint16_t 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) d->lastseen = Ticks; if(s != olds[i]){ dprint("%s: status: %.3ux -> %.3ux: %s\n", - dnam(d), olds[i], s, dstate(d->state)); + dnam(d), olds[i], s, diskstates[d->state]); olds[i] = s; d->wait = 0; } + hangck(d); switch(d->state){ case Dnull: case Dready: @@ -938,8 +1092,8 @@ reset: if(d->unit == nil) break; if((++d->wait&Midwait) == 0){ - dprint("%s: slow reset %.3ux task=%ux; %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 = (uint8_t)d->port->task; @@ -960,7 +1114,7 @@ reset: case Derror: case Dreset: dprint("%s: reset [%s]: mode %d; status %.3ux\n", - dnam(d), dstate(d->state), d->mode, s); + dnam(d), diskstates[d->state], d->mode, s); iunlock(d); resetdisk(d); ilock(d); @@ -970,11 +1124,11 @@ portreset: if(d->wait++ & 0xff && (s & Iactive) == 0) break; dprint("%s: portreset [%s]: mode %d; status %.3ux\n", - dnam(d), dstate(d->state), d->mode, s); + 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; } @@ -992,6 +1146,8 @@ satakproc(void*) { int i; + while(waserror()) + ; for(;;){ tsleep(&up->sleep, return0, 0, Nms); for(i = 0; i < niadrive; i++) @@ -1000,7 +1156,7 @@ satakproc(void*) } static void -iainterrupt(Ureg*, void *a) +iainterrupt(Ureg *u, void *a) { int i; uint32_t cause, m; @@ -1017,11 +1173,13 @@ iainterrupt(Ureg*, void *a) cause &= ~m; d = c->rawdrive + i; ilock(d); - if(d->port->isr && c->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); } @@ -1044,6 +1202,7 @@ ahciencreset(Ctlr *c) * rebuild is preferred as locate+fail; alternate 1hz fail * we're going to assume no locate led. */ + enum { Ledsleep = 125, /* 8hz */ @@ -1087,19 +1246,17 @@ static uint16_t led2[Ibpilast*8] = { }; static int -ledstate(Drive *d, uint32_t seq) +ledstate(Ledport *p, uint32_t seq) { uint16_t i; - Ledport *p; - p = d; if(p->led == Ibpipfa && seq%32 >= 8) i = P1; else i = led2[8*p->led + seq%8]; if(i != p->ledbits){ p->ledbits = i; - ledprint("%s: led %,.011ub %ud\n", dnam(d), p->ledbits, seq%8); + ledprint("ledstate %,.011ub %ud\n", p->ledbits, seq); return 1; } return 0; @@ -1123,15 +1280,15 @@ blink(Drive *d, uint32_t t) default: panic("%s: bad led type %d", dnam(d), c->enctype); case Elmt: - jehanne_memset(&msg, 0, sizeof msg); + memset(&msg, 0, sizeof msg); msg.type = Mled; msg.dsize = 0; - msg.msize = Ledmsz - 4; + msg.msize = sizeof msg - 4; msg.led[0] = d->ledbits; msg.led[1] = d->ledbits>>8; msg.pm = 0; msg.hba = d->driveno; - jehanne_memmove(c->enctx, &msg, Ledmsz); + memmove(c->enctx, &msg, sizeof msg); break; } h->emctl |= Tmsg; @@ -1149,7 +1306,7 @@ enum { uint32_t esbbits(uint32_t s) { - uint32_t i, e; /* except after c */ + uint32_t i, e; /* except after c */ e = 0; for(i = 0; i < 3; i++) @@ -1171,7 +1328,7 @@ blinkesb(Ctlr *c, uint32_t t) } if(s == 0) return 0; - jehanne_memset(u, 0, sizeof u); + memset(u, 0, sizeof u); for(i = 0; i < c->ndrive; i++){ d = c->drive[i]; s = Esbdrv0 + Esbiota*i; @@ -1185,7 +1342,7 @@ blinkesb(Ctlr *c, uint32_t t) } static long -ahciledr(SDunit *u, Chan *ch, void *a, long n, int64_t off) +ahciledr(SDunit *u, Chan *ch, void *a, long n, vlong off) { Ctlr *c; Drive *d; @@ -1196,7 +1353,7 @@ ahciledr(SDunit *u, Chan *ch, void *a, long n, int64_t off) } static long -ahciledw(SDunit *u, Chan *ch, void *a, long n, int64_t off) +ahciledw(SDunit *u, Chan *ch, void *a, long n, vlong off) { Ctlr *c; Drive *d; @@ -1215,7 +1372,7 @@ ledkproc(void*) Drive *d; j = 0; - jehanne_memset(map, 0, sizeof map); + memset(map, 0, sizeof map); for(i = 0; i < niactlr; i++) if(iactlr[i].enctype != 0){ ahciencreset(iactlr + i); @@ -1234,8 +1391,8 @@ ledkproc(void*) t0 = Ticks; for(j = 0; j < niadrive; ){ c = iadrive[j]->ctlr; - if(map[c - iactlr] == 0) - j += c->ndrive; + if(map[j] == 0) + j += c->enctype; else if(c->enctype == Eesb){ blinkesb(c, i); j += c->ndrive; @@ -1249,6 +1406,38 @@ ledkproc(void*) } } +static int +waitready(Drive *d) +{ + uint32_t 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) { @@ -1270,6 +1459,45 @@ 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) { @@ -1285,7 +1513,7 @@ iaenable(SDev *s) if(c->ndrive == 0) panic("iaenable: zero s->ctlr->ndrive"); pcisetbme(c->pci); - jehanne_snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name); + 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); @@ -1304,140 +1532,107 @@ iadisable(SDev *s) Ctlr *c; c = s->ctlr; - c->enabled = 0; + ilock(c); ahcidisable(c->hba); - intrdisable(c->vector); + snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name); + intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name); + c->enabled = 0; + iunlock(c); return 1; } -static int -iaonline(SDunit *u) +static Alist* +ahcibuild(Drive *d, int rw, void *data, int nsect, vlong lba) { - int r; - Ctlr *c; - Drive *d; + uint8_t *c; + uint32_t flags; Aportm *m; - c = u->dev->ctlr; - d = c->drive[u->subno]; m = &d->portm; - r = 0; - - if(m->feat & Datapi && m->drivechange){ - r = scsionlinex(u, m) == SDok; - if(r > 0) - m->drivechange = 0; - return r; - } - - ilock(d); - if(m->drivechange){ - r = 2; - m->drivechange = 0; - /* devsd resets this after online is called; why? */ - u->sectors = m->sectors; - u->secsize = m->secsize; - }else if(d->state == Dready) - r = 1; - iunlock(d); - return r; + c = m->ctab->cfis; + rwfis(m, c, rw, nsect, lba); + flags = Lpref; + if(rw == SDwrite) + flags |= Lwrite; + 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) +{ + uint32_t flags; + Aportm *m; + uint8_t *c; + Actab *t; + + m = &d->portm; + t = m->ctab; + c = t->cfis; + + 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; + return mkalist(m, flags, data, n); +} + +static Alist* +ahcibuildfis(Drive *d, SDreq *r, void *data, uint32_t n) { uint32_t flags; uint8_t *c; - Actab *t; - Alist *l; + Aportm *m; - l = m->list; - t = m->ctab; - c = t->cfis; - atapirwfis(m, c, r->cmd, r->clen, n); - flags = 1<<16 | Lpref | Latapi; - if(r->write != 0 && data) - flags |= Lwrite; - mkalist(m, flags, data, n); - return l; -} + if((r->ataproto & Pprotom) == Ppkt) + return ahcibuildpkt(d, r, data, n); -static Alist* -ahcibuildfis(Aportm *m, SDreq *r, void *data, uint32_t n) -{ - uint8_t *c; - uint32_t flags, dir; - Alist *l; - - l = m->list; + m = &d->portm; c = m->ctab->cfis; - if((r->ataproto & Pprotom) != Ppkt){ - jehanne_memmove(c, r->cmd, r->clen); - flags = Lpref; - if(r->ataproto&Pout && n > 0) - flags |= Lwrite; - dir = r->ataproto&Pdatam; - if(dir == Pnd && 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; + memmove(c, r->cmd, r->clen); + flags = Lpref; + if(r->write || n == 0) + flags |= Lwrite; + return mkalist(m, flags, data, n); } static int -isready(Drive *d) +lockready(Drive *d) { - uint32_t s; - uint32_t δ; + int i; - if(d->state & (Dreset | Dportreset /*| Dnew*/)) - return 1; - δ = TK2MS(Ticks - d->lastseen); - if(d->state == Dnull || δ > 10*1000){ - dprint("%s: last seen too long ago: %ld\n", dnam(d), δ); - return -1; + qlock(&d->portm); + while ((i = waitready(d)) == 1) { + qunlock(&d->portm); + esleep(1); + qlock(&d->portm); } - ilock(d); - s = d->port->sstatus; - iunlock(d); - if((s & Imask) == 0 && δ > 1500){ - dprint("%s: phy off %ldms\n", dnam(d), δ); - return -1; - } - if(d->state & (Dready | Dnew) && (s & Smask) == Sphylink) - return 0; - return 1; + return i; } static int -waitready(Drive *d, int tk) +flushcache(Drive *d) { - int r; + int i; - for(;;){ - r = isready(d); - if(r <= 0) - return r; - if(tk - Ticks - 10 < 1ul<<31) - return -1; - esleep(10); - } + i = -1; + if(lockready(d) == 0) + i = ahciflushcache(&d->portc); + qunlock(&d->portm); + return i; } static int -io(Drive *d, uint32_t proto, int totk, int interrupt) +io(Drive *d, uint32_t proto, int to, int interrupt) { - uint32_t task, flag, rv; + uint32_t task, flag, stat, rv; Aport *p; Asleep as; - switch(waitready(d, totk)){ + switch(waitready(d)){ case -1: return SDeio; case 1: @@ -1453,42 +1648,47 @@ io(Drive *d, uint32_t proto, int totk, int interrupt) as.p = p; as.i = 1; d->totick = 0; - if(totk > 0) - d->totick = totk | 1; /* fix fencepost */ + if(to > 0) + d->totick = Ticks + MS2TK(to) | 1; /* fix fencepost */ + d->active++; while(waserror()) if(interrupt){ + d->active--; d->port->ci = 0; if(ahcicomreset(&d->portc) == -1) - setstate(d, Dreset); + dstatus(d, Dreset); return SDtimeout; } + sleep(&d->portm, ahciclear, &as); poperror(); + d->active--; ilock(d); + stat = d->state; flag = d->portm.flag; - task = p->task; + 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){ - p->ci = 0; + }else if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && stat == Dready){ + d->port->ci = 0; ahcirecover(&d->portc); - task = p->task; + task = d->port->task; flag &= ~Fdone; /* either an error or do-over */ } if(flag == 0){ - jehanne_print("%s: retry\n", dnam(d)); + print("%s: retry\n", dnam(d)); return SDretry; } if(flag & (Fahdrs | Ferror)){ if((task & Eidnf) == 0) - jehanne_print("%s: i/o error %ux\n", dnam(d), task); + print("%s: i/o error %ux\n", dnam(d), task); return SDcheck; } return rv; @@ -1497,55 +1697,105 @@ io(Drive *d, uint32_t proto, int totk, int interrupt) static int iariopkt(SDreq *r, Drive *d) { - int n, count, t, max, δ; + int try, to; uint8_t *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; - δ = r->timeout - Ticks; - for(t = r->timeout; setreqto(r, t) != -1;){ - n = count; - if(n > max) - n = max; + r->rlen = 0; + + /* + * 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++){ qlock(&d->portm); - ahcibuildpkt(&d->portm, r, r->data, n); - r->status = io(d, Ppkt, r->timeout, 0); - qunlock(&d->portm); + l = ahcibuildpkt(d, r, r->data, r->dlen); + r->status = io(d, Ppkt, to, 0); switch(r->status){ case SDeio: - return r->status = SDcheck; + qunlock(&d->portm); + return SDeio; case SDretry: + qunlock(&d->portm); continue; } -// aprint("%s: OK %.2ux :: %d :: %.4lux\n", dnam(d), r->cmd[0], r->status, d->port->task); - r->rlen = d->portm.list->len; + r->rlen = l->len; + qunlock(&d->portm); return SDok; } - jehanne_print("%s: atapi timeout %dms\n", dnam(d), TK2MS(δ)); + print("%s: bad disk\n", dnam(d)); return r->status = SDcheck; } static long -ahcibio(SDunit *u, int lun, int write, void *a, long count0, uint64_t lba) +ahcibio(SDunit *u, int lun, int write, void *a, long count, uint64_t lba) { + int n, rw, try, status, max; + uint8_t *data; Ctlr *c; Drive *d; c = u->dev->ctlr; d = c->drive[u->subno]; if(d->portm.feat & Datapi) - return scsibiox(u, &d->portm, lun, write, a, count0, lba); - return atabio(u, &d->portm, lun, write, a, count0, lba); + return scsibio(u, lun, write, a, count, lba); + + max = 128; + if(d->portm.feat & Dllba){ + max = 8192; /* ahci maximum */ + if(c->type == Tsb600) + max = 255; /* errata */ + } + 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, rw, data, n, lba); + status = io(d, Pdma, 5000, 0); + qunlock(&d->portm); + switch(status){ + case SDeio: + return -1; + case SDretry: + try++; + continue; + } + try = 0; + count -= n; + lba += n; + data += n * u->secsize; + if(count == 0) + return data - (uint8_t*)a; + } + print("%s: bad disk\n", dnam(d)); + return -1; } static int iario(SDreq *r) { + int i, n, count, rw; + uint8_t *cmd; + uint64_t lba; Ctlr *c; Drive *d; SDunit *u; @@ -1553,19 +1803,34 @@ iario(SDreq *r) u = r->unit; c = u->dev->ctlr; d = c->drive[u->subno]; - if((d->state & (Dnew | Dready)) == 0) - return sdsetsense(r, SDcheck, 3, 0x04, 0x24); - if(r->timeout == 0) - r->timeout = totk(Ms2tk(600*1000)); if(d->portm.feat & Datapi) return iariopkt(r, d); - return atariosata(u, &d->portm, r); + cmd = r->cmd; + + if(cmd[0] == 0x35 || cmd[0] == 0x91){ + if(flushcache(d) == 0) + return sdsetsense(r, SDok, 0, 0, 0); + return sdsetsense(r, SDcheck, 3, 0xc, 2); + } + + if((i = sdfakescsi(r)) != SDnostatus){ + r->status = i; + return i; + } + + if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus) + return i; + n = ahcibio(u, r->lun, r->write, r->data, count, lba); + if(n == -1) + return SDeio; + r->rlen = n; + return SDok; } static uint8_t bogusrfis[16] = { [Ftype] 0x34, [Fioport] 0x40, -[Fstatus] 0x50, +[Fstatus] 0x50, [Fdev] 0xa0, }; @@ -1575,7 +1840,7 @@ sdr0(Drive *d) uint8_t *c; c = d->portm.fis.r; - jehanne_memmove(c, bogusrfis, sizeof bogusrfis); + memmove(c, bogusrfis, sizeof bogusrfis); coherence(); } @@ -1591,7 +1856,7 @@ sdr(SDreq *r, Drive *d, int st) st = t >> 8 + 4 & 0xf; } c = d->portm.fis.r; - jehanne_memmove(r->cmd, c, 16); + memmove(r->cmd, c, 16); r->status = st; if(st == SDcheck) st = SDok; @@ -1623,34 +1888,47 @@ fisreqchk(Sfis *f, SDreq *r) static int iaataio(SDreq *r) { + int try; Ctlr *c; Drive *d; SDunit *u; + Alist *l; u = r->unit; c = u->dev->ctlr; d = c->drive[u->subno]; - if(r->timeout == 0) - r->timeout = totk(Ms2tk(600*1000)); if((r->status = fisreqchk(&d->portm, r)) != SDnostatus) return r->status; r->rlen = 0; sdr0(d); - - qlock(&d->portm); - ahcibuildfis(&d->portm, r, r->data, r->dlen); - r->status = io(d, r->ataproto & Pprotom, -1, 1); - qunlock(&d->portm); - if(r->status != SDok) - return r->status; - r->rlen = r->dlen; - if((r->ataproto & Pprotom) == Ppkt) - r->rlen = d->portm.list->len; - return sdr(r, d, r->status); + for(try = 0; try < 10; try++){ + qlock(&d->portm); + l = ahcibuildfis(d, r, r->data, r->dlen); + r->status = io(d, r->ataproto & Pprotom, -1, 1); + 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->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)); + return r->status = SDeio; } -/* configure drives 0-5 as ahci sata (c.f. errata) */ +/* + * configure drives 0-5 as ahci sata (c.f. errata) + */ static int iaahcimode(Pcidev *p) { @@ -1658,7 +1936,7 @@ iaahcimode(Pcidev *p) 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 (sic) */ + pcicfgw16(p, 0x92, u | 0xf); /* ports 0-15 */ return 0; } @@ -1687,7 +1965,7 @@ iasetupahci(Ctlr *c) static void sbsetupahci(Pcidev *p) { - jehanne_print("sbsetupahci: tweaking %.4ux ccru %.2ux ccrp %.2ux\n", + print("sbsetupahci: tweaking %.4ux ccru %.2ux ccrp %.2ux\n", p->did, p->ccru, p->ccrp); pcicfgw8(p, 0x40, pcicfgr8(p, 0x40) | 1); pcicfgw8(p, PciCCRu, 6); @@ -1709,8 +1987,7 @@ esbenc(Ctlr *c) static int ahciencinit(Ctlr *c) { - uint32_t type, sz, o; - uint32_t *bar; + uint32_t type, sz, o, *bar; Ahba *h; h = c->hba; @@ -1735,8 +2012,7 @@ ahciencinit(Ctlr *c) if(sz == 0 || o == 0) return -1; bar = c->lmmio; - ledprint("size = %#.4ux; loc = %#.4ux*4\n", sz, o); - + dprint("size = %.4lux; loc = %.4lux*4\n", sz, o); c->encsz = sz; c->enctx = bar + o; if((h->emctl & Xonly) == 0){ @@ -1749,32 +2025,46 @@ ahciencinit(Ctlr *c) return 0; } -static uint16_t itab[] = { - 0xfffc, 0x2680, Tesb, - 0xfffb, 0x27c1, Tahci, /* 82801g[bh]m */ - 0xffff, 0x2821, Tahci, /* 82801h[roh] */ - 0xfffe, 0x2824, Tahci, /* 82801h[b] */ - 0xfeff, 0x2829, Tahci, /* ich8 */ - 0xfffe, 0x2922, Tahci, /* ich9 */ - 0xffff, 0x3a02, Tahci, /* 82801jd/do */ - 0xfefe, 0x3a22, Tahci, /* ich10, pch */ - 0xfff7, 0x3b28, Tahci, /* pchm */ - 0xfffe, 0x3b22, Tahci, /* pch */ -}; - static int didtype(Pcidev *p) { - int type, i; + int type; type = Tahci; switch(p->vid){ default: return -1; case 0x8086: - for(i = 0; i < nelem(itab); i += 3) - if((p->did & itab[i]) == itab[i+1]) - return itab[i+2]; + 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; /* 82801h[b] */ + if((p->did & 0xfeff) == 0x2829) + return Tich; /* ich8 */ + if((p->did & 0xfffe) == 0x2922) + return Tich; /* ich9 */ + if((p->did & 0xffff) == 0x3a02) + return Tich; /* 82801jd/do */ + if((p->did & 0xfefe) == 0x3a22) + return Tich; /* ich10, pch */ + if((p->did & 0xfff7) == 0x3b28) + return Tich; /* pchm */ + if((p->did & 0xfffe) == 0x3b22) + return Tich; /* pch */ break; case 0x1002: if(p->ccru == 1 || p->ccrp != 1) @@ -1790,6 +2080,11 @@ didtype(Pcidev *p) if(p->did == 0x3349) return Tahci; break; + case 0x1022: + /* Hudson SATA Controller [AHCI mode] */ + if(p->did == 0x7801) + return Tahci; + break; case 0x10de: case 0x1039: case 0x1b4b: @@ -1800,7 +2095,7 @@ didtype(Pcidev *p) type = Tjmicron; break; } - if(p->ccrb == 1 && p->ccru == 6 && p->ccrp == 1) + if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1) return type; return -1; } @@ -1809,7 +2104,7 @@ static SDev* iapnp(void) { int i, n, nunit, type; - uintmem io; + uintptr io; Ctlr *c; Drive *d; Pcidev *p; @@ -1819,48 +2114,52 @@ iapnp(void) if(done) return nil; done = 1; - jehanne_memset(olds, 0xff, sizeof olds); + + 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){ - jehanne_print("iapnp: %s: too many controllers\n", cttab[type].name); + print("iapnp: %s: too many controllers\n", tname[type]); break; } c = iactlr + niactlr; s = sdevs + niactlr; - jehanne_memset(c, 0, sizeof *c); - jehanne_memset(s, 0, sizeof *s); - c->type = cttab + type; - io = p->mem[Abar].bar & ~(uintmem)0xf; + memset(c, 0, sizeof *c); + memset(s, 0, sizeof *s); c->mmio = vmap(io, p->mem[Abar].size); - if(c->mmio == nil){ - jehanne_print("%s: %T: address %#P in use\n", - tnam(c), p->tbdf, io); + if(c->mmio == 0){ + print("%s: address %#p in use did %.4ux\n", + Tname(c), io, p->did); continue; } c->lmmio = (uint32_t*)c->mmio; c->pci = p; + c->type = type; - s->ifc = &sdahciifc; + s->ifc = &sdiahciifc; s->idno = 'E'; s->ctlr = c; c->sdev = s; + ahcihandoff((Ahba*)c->mmio); if(intel(c) && p->did != 0x2681) iasetupahci(c); - ahcibioshandoff((Ahba*)c->mmio); // ahcihbareset((Ahba*)c->mmio); nunit = ahciconf(c); - c->pi = c->hba->pi; - if(0 && p->vid == 0x1002 && p->did == 0x4391){ - c->pi = 0x3f; /* noah's opteron */ - nunit = 6; - } if(intel(c) && iaahcimode(p) == -1 || nunit < 1){ vunmap(c->mmio, p->mem[Abar].size); continue; @@ -1868,33 +2167,28 @@ loop: c->ndrive = s->nunit = nunit; /* map the drives -- they don't all need to be enabled. */ - jehanne_memset(c->rawdrive, 0, sizeof c->rawdrive); + memset(c->rawdrive, 0, sizeof c->rawdrive); n = 0; for(i = 0; i < NCtlrdrv; i++){ d = c->rawdrive + i; d->portno = i; d->driveno = -1; - d->portm.tler = 5000; - d->portm.sectors = 0; - d->portm.serial[0] = ' '; - d->led = Ibpinormal; + d->sectors = 0; + d->serial[0] = ' '; d->ctlr = c; - if((c->pi & 1<hba->pi & 1<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){ - jehanne_print("%s: port %d wedged; abort\n", - tnam(c), i); - goto loop; - } for(i = 0; i < n; i++){ c->drive[i]->mode = DMautoneg; configdrive(c->drive[i]); @@ -1905,8 +2199,8 @@ loop: niactlr++; sdadddevs(s); i = (c->hba->cap >> 21) & 1; - jehanne_print("#S/%s: %s: sata-%s with %d ports\n", s->name, - tnam(c), "I\0II" + i*2, nunit); + print("#S/%s: %s: sata-%s with %d ports\n", s->name, + Tname(c), "I\0II" + i*2, nunit); } return nil; } @@ -1940,7 +2234,7 @@ capfmt(char *p, char *e, Htab *t, int n, uint32_t cap) *p = 0; for(i = 0; i < n; i++) if(cap & t[i].bit) - p = jehanne_seprint(p, e, "%s ", t[i].name); + p = seprint(p, e, "%s ", t[i].name); return p; } @@ -1959,19 +2253,29 @@ iarctl(SDunit *u, char *p, int l) e = p+l; op = p; - if(d->state == Dready) - p = sfisxrdctl(&d->portm, p, e); - else - p = jehanne_seprint(p, e, "no disk present [%s]\n", dstate(d->state)); + if(d->state == Dready){ + p = seprint(p, e, "model\t%s\n", d->model); + p = seprint(p, e, "serial\t%s\n", d->serial); + p = seprint(p, e, "firm\t%s\n", d->firmware); + if(d->wwn != 0) + p = seprint(p, e, "wwn\t%ullx\n", d->wwn); + p = seprint(p, e, "flag\t"); + p = pflag(p, e, &d->portm); + p = seprint(p, e, "udma\t%d\n", d->portm.udma); + }else + p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]); serrstr(o->serror, buf, buf + sizeof buf - 1); - p = jehanne_seprint(p, e, "reg\ttask %ux cmd %ux serr %ux %s ci %ux is %ux " - "sig %ux sstatus %.3ux\n", o->task, o->cmd, o->serror, buf, + p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux " + "sig %lux sstatus %.3lux\n", o->task, o->cmd, o->serror, buf, o->ci, o->isr, o->sig, o->sstatus); - p = jehanne_seprint(p, e, "cmd\t"); + p = seprint(p, e, "cmd\t"); p = capfmt(p, e, ctab, nelem(ctab), o->cmd); - p = jehanne_seprint(p, e, "\n"); - p = jehanne_seprint(p, e, "mode\t%s %s\n", modes[d->mode], modes[maxmode(c)]); - p = jehanne_seprint(p, e, "geometry %llud %lud\n", u->sectors, u->secsize); + 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 %d\n", d->sectors, d->secsize); + p = seprint(p, e, "alignment %d %d\n", + d->secsize<portm.physshift, d->portm.physalign); + p = seprint(p, e, "missirq\t%ud\n", c->missirq); return p - op; } @@ -1981,7 +2285,7 @@ forcemode(Drive *d, char *mode) int i; for(i = 0; i < nelem(modes); i++) - if(jehanne_strcmp(mode, modes[i]) == 0) + if(strcmp(mode, modes[i]) == 0) break; if(i == nelem(modes)) i = 0; @@ -1995,14 +2299,35 @@ forcestate(Drive *d, char *state) { int i; - for(i = 1; i < nelem(diskstates); i++) - if(jehanne_strcmp(state, diskstates[i]) == 0) + for(i = 0; i < nelem(diskstates); i++) + if(strcmp(state, diskstates[i]) == 0) break; if(i == nelem(diskstates)) error(Ebadctl); - setstate(d, 1 << i-1); + dstatus(d, i); } +static int +runsettxmode(Drive *d, char *s) +{ + int i; + Aportc *c; + Aportm *m; + + c = &d->portc; + m = &d->portm; + + i = 1; + if(lockready(d) == 0){ + m->udma = atoi(s); + if(settxmode(c, m->udma) == 0) + i = 0; + } + qunlock(m); + return i; +} + + static int iawctl(SDunit *u, Cmdbuf *cmd) { @@ -2014,11 +2339,14 @@ iawctl(SDunit *u, Cmdbuf *cmd) d = c->drive[u->subno]; f = cmd->f; - if(jehanne_strcmp(f[0], "mode") == 0) + if(strcmp(f[0], "mode") == 0) forcemode(d, f[1]? f[1]: "satai"); - else if(jehanne_strcmp(f[0], "state") == 0) + else if(strcmp(f[0], "state") == 0) forcestate(d, f[1]? f[1]: "null"); - else + else if(strcmp(f[0], "txmode") == 0){ + if(runsettxmode(d, f[1]? f[1]: "0")) + cmderror(cmd, "bad txmode / stuck port"); + }else cmderror(cmd, Ebadctl); return 0; } @@ -2033,18 +2361,18 @@ portr(char *p, char *e, uint32_t x) for(i = 0; i < 32; i++){ if((x & (1< 0) - p = jehanne_seprint(p, e, ", "); - p = jehanne_seprint(p, e, "%d", a = i); + p = seprint(p, e, ", "); + p = seprint(p, e, "%d", a = i); } } if(a != -1 && i - 1 != a) - p = jehanne_seprint(p, e, "-%d", i - 1); + p = seprint(p, e, "-%d", i - 1); return p; } @@ -2096,13 +2424,13 @@ iartopctl(SDev *s, char *p, char *e) c = s->ctlr; h = c->hba; cap = h->cap; - p = jehanne_seprint(p, e, "sd%c ahci %s port %#p: ", s->idno, tnam(c), h); + p = seprint(p, e, "sd%c ahci %s port %#p: ", s->idno, Tname(c), h); p = capfmt(p, e, htab, nelem(htab), cap); p = capfmt(p, e, htab2, nelem(htab2), h->cap2); p = capfmt(p, e, emtab, nelem(emtab), h->emctl); portr(pr, pr + sizeof pr, h->pi); - return jehanne_seprint(p, e, - "iss %d ncs %d np %d ghc %ux isr %ux pi %ux %s ver %ux\n", + return seprint(p, e, + "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), h->ghc, h->isr, h->pi, pr, h->ver); } @@ -2116,13 +2444,13 @@ iawtopctl(SDev *, Cmdbuf *cmd) f = cmd->f; v = 0; - if(jehanne_strcmp(f[0], "debug") == 0) + if(strcmp(f[0], "debug") == 0) v = &debug; - else if(jehanne_strcmp(f[0], "idprint") == 0) + else if(strcmp(f[0], "idprint") == 0) v = &prid; - else if(jehanne_strcmp(f[0], "aprint") == 0) + else if(strcmp(f[0], "aprint") == 0) v = &datapi; - else if(jehanne_strcmp(f[0], "ledprint") == 0) + else if(strcmp(f[0], "ledprint") == 0) v = &dled; else cmderror(cmd, Ebadctl); @@ -2132,14 +2460,18 @@ iawtopctl(SDev *, Cmdbuf *cmd) cmderror(cmd, Ebadarg); case 1: *v ^= 1; - return 0; + break; case 2: - *v = jehanne_strcmp(f[1], "on") == 0; - return 0; + if(f[1]) + *v = strcmp(f[1], "on") == 0; + else + *v ^= 1; + break; } + return 0; } -SDifc sdahciifc = { +SDifc sdiahciifc = { "ahci", iapnp,