1b1707c5dSDavid du Colombier /* 2b1707c5dSDavid du Colombier * intel/amd ahci sata controller 3014ad43fSDavid du Colombier * copyright © 2007-8 coraid, inc. 4b1707c5dSDavid du Colombier */ 5b1707c5dSDavid du Colombier 6b1707c5dSDavid du Colombier #include "u.h" 7b1707c5dSDavid du Colombier #include "../port/lib.h" 8b1707c5dSDavid du Colombier #include "mem.h" 9b1707c5dSDavid du Colombier #include "dat.h" 10b1707c5dSDavid du Colombier #include "fns.h" 11b1707c5dSDavid du Colombier #include "io.h" 12b1707c5dSDavid du Colombier #include "../port/error.h" 13b1707c5dSDavid du Colombier #include "../port/sd.h" 14b1707c5dSDavid du Colombier #include "ahci.h" 15b1707c5dSDavid du Colombier 16b1707c5dSDavid du Colombier #define dprint(...) if(debug) iprint(__VA_ARGS__); else USED(debug) 17014ad43fSDavid du Colombier #define idprint(...) if(prid) iprint(__VA_ARGS__); else USED(prid) 18014ad43fSDavid du Colombier #define aprint(...) if(datapi) iprint(__VA_ARGS__); else USED(datapi) 19b1707c5dSDavid du Colombier #define Tname(c) tname[(c)->type] 20b1707c5dSDavid du Colombier 21b1707c5dSDavid du Colombier enum { 22b1707c5dSDavid du Colombier NCtlr = 4, 23b1707c5dSDavid du Colombier NCtlrdrv= 32, 24b1707c5dSDavid du Colombier NDrive = NCtlr*NCtlrdrv, 25b1707c5dSDavid du Colombier 26b1707c5dSDavid du Colombier Read = 0, 27b1707c5dSDavid du Colombier Write, 28b1707c5dSDavid du Colombier }; 29b1707c5dSDavid du Colombier 30b1707c5dSDavid du Colombier /* pci space configuration */ 31b1707c5dSDavid du Colombier enum { 32b1707c5dSDavid du Colombier Pmap = 0x90, 33b1707c5dSDavid du Colombier Ppcs = 0x91, 34b1707c5dSDavid du Colombier Prev = 0xa8, 35b1707c5dSDavid du Colombier }; 36b1707c5dSDavid du Colombier 37b1707c5dSDavid du Colombier enum { 38b1707c5dSDavid du Colombier Tesb, 39b1707c5dSDavid du Colombier Tich, 40b1707c5dSDavid du Colombier Tsb600, 41014ad43fSDavid du Colombier Tunk, 42b1707c5dSDavid du Colombier }; 43b1707c5dSDavid du Colombier 44014ad43fSDavid du Colombier #define Intel(x) ((x)->pci->vid == 0x8086) 45b1707c5dSDavid du Colombier 46b1707c5dSDavid du Colombier static char *tname[] = { 47b1707c5dSDavid du Colombier "63xxesb", 48b1707c5dSDavid du Colombier "ich", 49b1707c5dSDavid du Colombier "sb600", 50014ad43fSDavid du Colombier "unk", 51b1707c5dSDavid du Colombier }; 52b1707c5dSDavid du Colombier 53b1707c5dSDavid du Colombier enum { 54b1707c5dSDavid du Colombier Dnull, 55b1707c5dSDavid du Colombier Dmissing, 56b1707c5dSDavid du Colombier Dnew, 57b1707c5dSDavid du Colombier Dready, 58b1707c5dSDavid du Colombier Derror, 59b1707c5dSDavid du Colombier Dreset, 60b1707c5dSDavid du Colombier Doffline, 61b1707c5dSDavid du Colombier Dportreset, 62b1707c5dSDavid du Colombier Dlast, 63b1707c5dSDavid du Colombier }; 64b1707c5dSDavid du Colombier 65b1707c5dSDavid du Colombier static char *diskstates[Dlast] = { 66b1707c5dSDavid du Colombier "null", 67b1707c5dSDavid du Colombier "missing", 68b1707c5dSDavid du Colombier "new", 69b1707c5dSDavid du Colombier "ready", 70b1707c5dSDavid du Colombier "error", 71b1707c5dSDavid du Colombier "reset", 72b1707c5dSDavid du Colombier "offline", 73b1707c5dSDavid du Colombier "portreset", 74b1707c5dSDavid du Colombier }; 75b1707c5dSDavid du Colombier 76b1707c5dSDavid du Colombier extern SDifc sdiahciifc; 77b1707c5dSDavid du Colombier typedef struct Ctlr Ctlr; 78b1707c5dSDavid du Colombier 79b1707c5dSDavid du Colombier enum { 80b1707c5dSDavid du Colombier DMautoneg, 81b1707c5dSDavid du Colombier DMsatai, 82b1707c5dSDavid du Colombier DMsataii, 83b1707c5dSDavid du Colombier }; 84b1707c5dSDavid du Colombier 85b1707c5dSDavid du Colombier static char *modename[] = { 86b1707c5dSDavid du Colombier "auto", 87b1707c5dSDavid du Colombier "satai", 88b1707c5dSDavid du Colombier "sataii", 89b1707c5dSDavid du Colombier }; 90b1707c5dSDavid du Colombier 91b1707c5dSDavid du Colombier static char *flagname[] = { 92b1707c5dSDavid du Colombier "llba", 93b1707c5dSDavid du Colombier "smart", 94b1707c5dSDavid du Colombier "power", 95b1707c5dSDavid du Colombier "nop", 96b1707c5dSDavid du Colombier "atapi", 97b1707c5dSDavid du Colombier "atapi16", 98b1707c5dSDavid du Colombier }; 99b1707c5dSDavid du Colombier 100b1707c5dSDavid du Colombier typedef struct { 101b1707c5dSDavid du Colombier Lock; 102b1707c5dSDavid du Colombier 103b1707c5dSDavid du Colombier Ctlr *ctlr; 104b1707c5dSDavid du Colombier SDunit *unit; 105b1707c5dSDavid du Colombier char name[10]; 106b1707c5dSDavid du Colombier Aport *port; 107b1707c5dSDavid du Colombier Aportm portm; 108f3112f79SDavid du Colombier Aportc portc; /* redundant ptr to port and portm */ 109b1707c5dSDavid du Colombier 110b1707c5dSDavid du Colombier uchar mediachange; 111b1707c5dSDavid du Colombier uchar state; 112b1707c5dSDavid du Colombier uchar smartrs; 113b1707c5dSDavid du Colombier 114b1707c5dSDavid du Colombier uvlong sectors; 115*58db92f4SDavid du Colombier ulong secsize; 116f3112f79SDavid du Colombier ulong intick; /* start tick of current transfer */ 11781ede731SDavid du Colombier ulong lastseen; 118b1707c5dSDavid du Colombier int wait; 119f3112f79SDavid du Colombier uchar mode; /* DMautoneg, satai or sataii */ 120b1707c5dSDavid du Colombier uchar active; 121b1707c5dSDavid du Colombier 122b1707c5dSDavid du Colombier char serial[20+1]; 123b1707c5dSDavid du Colombier char firmware[8+1]; 124b1707c5dSDavid du Colombier char model[40+1]; 125b1707c5dSDavid du Colombier 126b1707c5dSDavid du Colombier ushort info[0x200]; 127b1707c5dSDavid du Colombier 128b1707c5dSDavid du Colombier int driveno; /* ctlr*NCtlrdrv + unit */ 129b1707c5dSDavid du Colombier /* controller port # != driveno when not all ports are enabled */ 130b1707c5dSDavid du Colombier int portno; 131b1707c5dSDavid du Colombier } Drive; 132b1707c5dSDavid du Colombier 133b1707c5dSDavid du Colombier struct Ctlr { 134b1707c5dSDavid du Colombier Lock; 135b1707c5dSDavid du Colombier 136b1707c5dSDavid du Colombier int type; 137b1707c5dSDavid du Colombier int enabled; 138b1707c5dSDavid du Colombier SDev *sdev; 139b1707c5dSDavid du Colombier Pcidev *pci; 140b1707c5dSDavid du Colombier 141b1707c5dSDavid du Colombier uchar *mmio; 142b1707c5dSDavid du Colombier ulong *lmmio; 143b1707c5dSDavid du Colombier Ahba *hba; 144b1707c5dSDavid du Colombier 145b1707c5dSDavid du Colombier Drive rawdrive[NCtlrdrv]; 146b1707c5dSDavid du Colombier Drive* drive[NCtlrdrv]; 147b1707c5dSDavid du Colombier int ndrive; 148014ad43fSDavid du Colombier int mport; 149b1707c5dSDavid du Colombier }; 150b1707c5dSDavid du Colombier 151b1707c5dSDavid du Colombier static Ctlr iactlr[NCtlr]; 152b1707c5dSDavid du Colombier static SDev sdevs[NCtlr]; 153b1707c5dSDavid du Colombier static int niactlr; 154b1707c5dSDavid du Colombier 155b1707c5dSDavid du Colombier static Drive *iadrive[NDrive]; 156b1707c5dSDavid du Colombier static int niadrive; 157b1707c5dSDavid du Colombier 1589e8a50a9SDavid du Colombier /* these are fiddled in iawtopctl() */ 159b1707c5dSDavid du Colombier static int debug; 160b1707c5dSDavid du Colombier static int prid = 1; 161b1707c5dSDavid du Colombier static int datapi; 162b1707c5dSDavid du Colombier 163b1707c5dSDavid du Colombier static char stab[] = { 164b1707c5dSDavid du Colombier [0] 'i', 'm', 165b1707c5dSDavid du Colombier [8] 't', 'c', 'p', 'e', 166b1707c5dSDavid du Colombier [16] 'N', 'I', 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X' 167b1707c5dSDavid du Colombier }; 168b1707c5dSDavid du Colombier 169b1707c5dSDavid du Colombier static void 170b1707c5dSDavid du Colombier serrstr(ulong r, char *s, char *e) 171b1707c5dSDavid du Colombier { 172b1707c5dSDavid du Colombier int i; 173b1707c5dSDavid du Colombier 174b1707c5dSDavid du Colombier e -= 3; 175b1707c5dSDavid du Colombier for(i = 0; i < nelem(stab) && s < e; i++) 176b1707c5dSDavid du Colombier if(r & (1<<i) && stab[i]){ 177b1707c5dSDavid du Colombier *s++ = stab[i]; 178b1707c5dSDavid du Colombier if(SerrBad & (1<<i)) 179b1707c5dSDavid du Colombier *s++ = '*'; 180b1707c5dSDavid du Colombier } 181b1707c5dSDavid du Colombier *s = 0; 182b1707c5dSDavid du Colombier } 183b1707c5dSDavid du Colombier 184b1707c5dSDavid du Colombier static char ntab[] = "0123456789abcdef"; 185b1707c5dSDavid du Colombier 186b1707c5dSDavid du Colombier static void 187b1707c5dSDavid du Colombier preg(uchar *reg, int n) 188b1707c5dSDavid du Colombier { 189b1707c5dSDavid du Colombier int i; 190b1707c5dSDavid du Colombier char buf[25*3+1], *e; 191b1707c5dSDavid du Colombier 192b1707c5dSDavid du Colombier e = buf; 193b1707c5dSDavid du Colombier for(i = 0; i < n; i++){ 194b1707c5dSDavid du Colombier *e++ = ntab[reg[i]>>4]; 195b1707c5dSDavid du Colombier *e++ = ntab[reg[i]&0xf]; 196b1707c5dSDavid du Colombier *e++ = ' '; 197b1707c5dSDavid du Colombier } 198b1707c5dSDavid du Colombier *e++ = '\n'; 199b1707c5dSDavid du Colombier *e = 0; 200b1707c5dSDavid du Colombier dprint(buf); 201b1707c5dSDavid du Colombier } 202b1707c5dSDavid du Colombier 203b1707c5dSDavid du Colombier static void 204b1707c5dSDavid du Colombier dreg(char *s, Aport *p) 205b1707c5dSDavid du Colombier { 2063d56950aSDavid du Colombier dprint("ahci: %stask=%lux; cmd=%lux; ci=%lux; is=%lux\n", 2073d56950aSDavid du Colombier s, p->task, p->cmd, p->ci, p->isr); 208b1707c5dSDavid du Colombier } 209b1707c5dSDavid du Colombier 210b1707c5dSDavid du Colombier static void 211b1707c5dSDavid du Colombier esleep(int ms) 212b1707c5dSDavid du Colombier { 213b1707c5dSDavid du Colombier if(waserror()) 214b1707c5dSDavid du Colombier return; 215b1707c5dSDavid du Colombier tsleep(&up->sleep, return0, 0, ms); 216b1707c5dSDavid du Colombier poperror(); 217b1707c5dSDavid du Colombier } 218b1707c5dSDavid du Colombier 219b1707c5dSDavid du Colombier typedef struct { 220b1707c5dSDavid du Colombier Aport *p; 221b1707c5dSDavid du Colombier int i; 222b1707c5dSDavid du Colombier }Asleep; 223b1707c5dSDavid du Colombier 224b1707c5dSDavid du Colombier static int 225b1707c5dSDavid du Colombier ahciclear(void *v) 226b1707c5dSDavid du Colombier { 227b1707c5dSDavid du Colombier Asleep *s; 228b1707c5dSDavid du Colombier 229b1707c5dSDavid du Colombier s = v; 230b1707c5dSDavid du Colombier return (s->p->ci & s->i) == 0; 231b1707c5dSDavid du Colombier } 232b1707c5dSDavid du Colombier 233b1707c5dSDavid du Colombier static void 234b1707c5dSDavid du Colombier aesleep(Aportm *m, Asleep *a, int ms) 235b1707c5dSDavid du Colombier { 236b1707c5dSDavid du Colombier if(waserror()) 237b1707c5dSDavid du Colombier return; 238b1707c5dSDavid du Colombier tsleep(m, ahciclear, a, ms); 239b1707c5dSDavid du Colombier poperror(); 240b1707c5dSDavid du Colombier } 241b1707c5dSDavid du Colombier 242b1707c5dSDavid du Colombier static int 243b1707c5dSDavid du Colombier ahciwait(Aportc *c, int ms) 244b1707c5dSDavid du Colombier { 245b1707c5dSDavid du Colombier Asleep as; 246b1707c5dSDavid du Colombier Aport *p; 247b1707c5dSDavid du Colombier 248b1707c5dSDavid du Colombier p = c->p; 249b1707c5dSDavid du Colombier p->ci = 1; 250b1707c5dSDavid du Colombier as.p = p; 251b1707c5dSDavid du Colombier as.i = 1; 252b1707c5dSDavid du Colombier aesleep(c->m, &as, ms); 253b1707c5dSDavid du Colombier if((p->task&1) == 0 && p->ci == 0) 254b1707c5dSDavid du Colombier return 0; 255b1707c5dSDavid du Colombier dreg("ahciwait timeout ", c->p); 256b1707c5dSDavid du Colombier return -1; 257b1707c5dSDavid du Colombier } 258b1707c5dSDavid du Colombier 259b1707c5dSDavid du Colombier static int 260b1707c5dSDavid du Colombier nop(Aportc *pc) 261b1707c5dSDavid du Colombier { 262b1707c5dSDavid du Colombier uchar *c; 263b1707c5dSDavid du Colombier Actab *t; 264b1707c5dSDavid du Colombier Alist *l; 265b1707c5dSDavid du Colombier 266b1707c5dSDavid du Colombier if((pc->m->feat & Dnop) == 0) 267b1707c5dSDavid du Colombier return -1; 268b1707c5dSDavid du Colombier 269b1707c5dSDavid du Colombier t = pc->m->ctab; 270b1707c5dSDavid du Colombier c = t->cfis; 271b1707c5dSDavid du Colombier 272b1707c5dSDavid du Colombier memset(c, 0, 0x20); 273b1707c5dSDavid du Colombier c[0] = 0x27; 274b1707c5dSDavid du Colombier c[1] = 0x80; 275b1707c5dSDavid du Colombier c[2] = 0x00; 276b1707c5dSDavid du Colombier c[7] = 0xa0; /* obsolete device bits */ 277b1707c5dSDavid du Colombier 278b1707c5dSDavid du Colombier l = pc->m->list; 279b1707c5dSDavid du Colombier l->flags = Lwrite | 0x5; 280b1707c5dSDavid du Colombier l->len = 0; 281b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 282b1707c5dSDavid du Colombier l->ctabhi = 0; 283b1707c5dSDavid du Colombier 284b1707c5dSDavid du Colombier return ahciwait(pc, 3*1000); 285b1707c5dSDavid du Colombier } 286b1707c5dSDavid du Colombier 287b1707c5dSDavid du Colombier static int 288b1707c5dSDavid du Colombier setfeatures(Aportc *pc, uchar f) 289b1707c5dSDavid du Colombier { 290b1707c5dSDavid du Colombier uchar *c; 291b1707c5dSDavid du Colombier Actab *t; 292b1707c5dSDavid du Colombier Alist *l; 293b1707c5dSDavid du Colombier 294b1707c5dSDavid du Colombier t = pc->m->ctab; 295b1707c5dSDavid du Colombier c = t->cfis; 296b1707c5dSDavid du Colombier 297b1707c5dSDavid du Colombier memset(c, 0, 0x20); 298b1707c5dSDavid du Colombier c[0] = 0x27; 299b1707c5dSDavid du Colombier c[1] = 0x80; 300b1707c5dSDavid du Colombier c[2] = 0xef; 301b1707c5dSDavid du Colombier c[3] = f; 302b1707c5dSDavid du Colombier c[7] = 0xa0; /* obsolete device bits */ 303b1707c5dSDavid du Colombier 304b1707c5dSDavid du Colombier l = pc->m->list; 305b1707c5dSDavid du Colombier l->flags = Lwrite | 0x5; 306b1707c5dSDavid du Colombier l->len = 0; 307b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 308b1707c5dSDavid du Colombier l->ctabhi = 0; 309b1707c5dSDavid du Colombier 310b1707c5dSDavid du Colombier return ahciwait(pc, 3*1000); 311b1707c5dSDavid du Colombier } 312b1707c5dSDavid du Colombier 313b1707c5dSDavid du Colombier static int 314b1707c5dSDavid du Colombier setudmamode(Aportc *pc, uchar f) 315b1707c5dSDavid du Colombier { 316b1707c5dSDavid du Colombier uchar *c; 317b1707c5dSDavid du Colombier Actab *t; 318b1707c5dSDavid du Colombier Alist *l; 319b1707c5dSDavid du Colombier 320b1707c5dSDavid du Colombier /* hack */ 321b1707c5dSDavid du Colombier if((pc->p->sig >> 16) == 0xeb14) 322b1707c5dSDavid du Colombier return 0; 323b1707c5dSDavid du Colombier 324b1707c5dSDavid du Colombier t = pc->m->ctab; 325b1707c5dSDavid du Colombier c = t->cfis; 326b1707c5dSDavid du Colombier 327b1707c5dSDavid du Colombier memset(c, 0, 0x20); 328b1707c5dSDavid du Colombier c[0] = 0x27; 329b1707c5dSDavid du Colombier c[1] = 0x80; 330b1707c5dSDavid du Colombier c[2] = 0xef; 331b1707c5dSDavid du Colombier c[3] = 3; /* set transfer mode */ 332b1707c5dSDavid du Colombier c[7] = 0xa0; /* obsolete device bits */ 333b1707c5dSDavid du Colombier c[12] = 0x40 | f; /* sector count */ 334b1707c5dSDavid du Colombier 335b1707c5dSDavid du Colombier l = pc->m->list; 336b1707c5dSDavid du Colombier l->flags = Lwrite | 0x5; 337b1707c5dSDavid du Colombier l->len = 0; 338b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 339b1707c5dSDavid du Colombier l->ctabhi = 0; 340b1707c5dSDavid du Colombier 341b1707c5dSDavid du Colombier return ahciwait(pc, 3*1000); 342b1707c5dSDavid du Colombier } 343b1707c5dSDavid du Colombier 344b1707c5dSDavid du Colombier static void 345b1707c5dSDavid du Colombier asleep(int ms) 346b1707c5dSDavid du Colombier { 347b1707c5dSDavid du Colombier if(up == nil) 348b1707c5dSDavid du Colombier delay(ms); 349b1707c5dSDavid du Colombier else 350b1707c5dSDavid du Colombier esleep(ms); 351b1707c5dSDavid du Colombier } 352b1707c5dSDavid du Colombier 353b1707c5dSDavid du Colombier static int 354b1707c5dSDavid du Colombier ahciportreset(Aportc *c) 355b1707c5dSDavid du Colombier { 35691b330d9SDavid du Colombier ulong *cmd, i; 357b1707c5dSDavid du Colombier Aport *p; 358b1707c5dSDavid du Colombier 359b1707c5dSDavid du Colombier p = c->p; 360b1707c5dSDavid du Colombier cmd = &p->cmd; 361b1707c5dSDavid du Colombier *cmd &= ~(Afre|Ast); 362b1707c5dSDavid du Colombier for(i = 0; i < 500; i += 25){ 363b1707c5dSDavid du Colombier if((*cmd&Acr) == 0) 364b1707c5dSDavid du Colombier break; 365b1707c5dSDavid du Colombier asleep(25); 366b1707c5dSDavid du Colombier } 367b1707c5dSDavid du Colombier p->sctl = 1|(p->sctl&~7); 368b1707c5dSDavid du Colombier delay(1); 369b1707c5dSDavid du Colombier p->sctl &= ~7; 370b1707c5dSDavid du Colombier return 0; 371b1707c5dSDavid du Colombier } 372b1707c5dSDavid du Colombier 373b1707c5dSDavid du Colombier static int 374b1707c5dSDavid du Colombier smart(Aportc *pc, int n) 375b1707c5dSDavid du Colombier { 376b1707c5dSDavid du Colombier uchar *c; 377b1707c5dSDavid du Colombier Actab *t; 378b1707c5dSDavid du Colombier Alist *l; 379b1707c5dSDavid du Colombier 380b1707c5dSDavid du Colombier if((pc->m->feat&Dsmart) == 0) 381b1707c5dSDavid du Colombier return -1; 382b1707c5dSDavid du Colombier 383b1707c5dSDavid du Colombier t = pc->m->ctab; 384b1707c5dSDavid du Colombier c = t->cfis; 385b1707c5dSDavid du Colombier 386b1707c5dSDavid du Colombier memset(c, 0, 0x20); 387b1707c5dSDavid du Colombier c[0] = 0x27; 388b1707c5dSDavid du Colombier c[1] = 0x80; 389b1707c5dSDavid du Colombier c[2] = 0xb0; 390b1707c5dSDavid du Colombier c[3] = 0xd8 + n; /* able smart */ 391b1707c5dSDavid du Colombier c[5] = 0x4f; 392b1707c5dSDavid du Colombier c[6] = 0xc2; 393b1707c5dSDavid du Colombier c[7] = 0xa0; 394b1707c5dSDavid du Colombier 395b1707c5dSDavid du Colombier l = pc->m->list; 396b1707c5dSDavid du Colombier l->flags = Lwrite | 0x5; 397b1707c5dSDavid du Colombier l->len = 0; 398b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 399b1707c5dSDavid du Colombier l->ctabhi = 0; 400b1707c5dSDavid du Colombier 401b1707c5dSDavid du Colombier if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){ 4023d56950aSDavid du Colombier dprint("ahci: smart fail %lux\n", pc->p->task); 403b1707c5dSDavid du Colombier // preg(pc->m->fis.r, 20); 404b1707c5dSDavid du Colombier return -1; 405b1707c5dSDavid du Colombier } 406b1707c5dSDavid du Colombier if(n) 407b1707c5dSDavid du Colombier return 0; 408b1707c5dSDavid du Colombier return 1; 409b1707c5dSDavid du Colombier } 410b1707c5dSDavid du Colombier 411b1707c5dSDavid du Colombier static int 412b1707c5dSDavid du Colombier smartrs(Aportc *pc) 413b1707c5dSDavid du Colombier { 414b1707c5dSDavid du Colombier uchar *c; 415b1707c5dSDavid du Colombier Actab *t; 416b1707c5dSDavid du Colombier Alist *l; 417b1707c5dSDavid du Colombier 418b1707c5dSDavid du Colombier t = pc->m->ctab; 419b1707c5dSDavid du Colombier c = t->cfis; 420b1707c5dSDavid du Colombier 421b1707c5dSDavid du Colombier memset(c, 0, 0x20); 422b1707c5dSDavid du Colombier c[0] = 0x27; 423b1707c5dSDavid du Colombier c[1] = 0x80; 424b1707c5dSDavid du Colombier c[2] = 0xb0; 425b1707c5dSDavid du Colombier c[3] = 0xda; /* return smart status */ 426b1707c5dSDavid du Colombier c[5] = 0x4f; 427b1707c5dSDavid du Colombier c[6] = 0xc2; 428b1707c5dSDavid du Colombier c[7] = 0xa0; 429b1707c5dSDavid du Colombier 430b1707c5dSDavid du Colombier l = pc->m->list; 431b1707c5dSDavid du Colombier l->flags = Lwrite | 0x5; 432b1707c5dSDavid du Colombier l->len = 0; 433b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 434b1707c5dSDavid du Colombier l->ctabhi = 0; 435b1707c5dSDavid du Colombier 436b1707c5dSDavid du Colombier c = pc->m->fis.r; 437b1707c5dSDavid du Colombier if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){ 4383d56950aSDavid du Colombier dprint("ahci: smart fail %lux\n", pc->p->task); 439b1707c5dSDavid du Colombier preg(c, 20); 440b1707c5dSDavid du Colombier return -1; 441b1707c5dSDavid du Colombier } 442b1707c5dSDavid du Colombier if(c[5] == 0x4f && c[6] == 0xc2) 443b1707c5dSDavid du Colombier return 1; 444b1707c5dSDavid du Colombier return 0; 445b1707c5dSDavid du Colombier } 446b1707c5dSDavid du Colombier 447b1707c5dSDavid du Colombier static int 44881ede731SDavid du Colombier ahciflushcache(Aportc *pc) 449b1707c5dSDavid du Colombier { 450b1707c5dSDavid du Colombier uchar *c, llba; 451b1707c5dSDavid du Colombier Actab *t; 452b1707c5dSDavid du Colombier Alist *l; 453b1707c5dSDavid du Colombier static uchar tab[2] = {0xe7, 0xea}; 454b1707c5dSDavid du Colombier 455b1707c5dSDavid du Colombier llba = pc->m->feat&Dllba? 1: 0; 456b1707c5dSDavid du Colombier t = pc->m->ctab; 457b1707c5dSDavid du Colombier c = t->cfis; 458b1707c5dSDavid du Colombier 459b1707c5dSDavid du Colombier memset(c, 0, 0x20); 460b1707c5dSDavid du Colombier c[0] = 0x27; 461b1707c5dSDavid du Colombier c[1] = 0x80; 462b1707c5dSDavid du Colombier c[2] = tab[llba]; 463b1707c5dSDavid du Colombier c[7] = 0xa0; 464b1707c5dSDavid du Colombier 465b1707c5dSDavid du Colombier l = pc->m->list; 466b1707c5dSDavid du Colombier l->flags = Lwrite | 0x5; 467b1707c5dSDavid du Colombier l->len = 0; 468b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 469b1707c5dSDavid du Colombier l->ctabhi = 0; 470b1707c5dSDavid du Colombier 471b1707c5dSDavid du Colombier if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){ 4723d56950aSDavid du Colombier dprint("ahciflushcache: fail %lux\n", pc->p->task); 473b1707c5dSDavid du Colombier // preg( pc->m->fis.r, 20); 474b1707c5dSDavid du Colombier return -1; 475b1707c5dSDavid du Colombier } 476b1707c5dSDavid du Colombier return 0; 477b1707c5dSDavid du Colombier } 478b1707c5dSDavid du Colombier 479b1707c5dSDavid du Colombier static ushort 480b1707c5dSDavid du Colombier gbit16(void *a) 481b1707c5dSDavid du Colombier { 482b1707c5dSDavid du Colombier uchar *i; 483b1707c5dSDavid du Colombier 484b1707c5dSDavid du Colombier i = a; 48591157df7SDavid du Colombier return i[1]<<8 | i[0]; 486b1707c5dSDavid du Colombier } 487b1707c5dSDavid du Colombier 48891b330d9SDavid du Colombier static ulong 489b1707c5dSDavid du Colombier gbit32(void *a) 490b1707c5dSDavid du Colombier { 49191b330d9SDavid du Colombier ulong j; 492b1707c5dSDavid du Colombier uchar *i; 493b1707c5dSDavid du Colombier 494b1707c5dSDavid du Colombier i = a; 495b1707c5dSDavid du Colombier j = i[3] << 24; 496b1707c5dSDavid du Colombier j |= i[2] << 16; 497b1707c5dSDavid du Colombier j |= i[1] << 8; 498b1707c5dSDavid du Colombier j |= i[0]; 499b1707c5dSDavid du Colombier return j; 500b1707c5dSDavid du Colombier } 501b1707c5dSDavid du Colombier 502b1707c5dSDavid du Colombier static uvlong 503b1707c5dSDavid du Colombier gbit64(void *a) 504b1707c5dSDavid du Colombier { 505b1707c5dSDavid du Colombier uchar *i; 506b1707c5dSDavid du Colombier 507b1707c5dSDavid du Colombier i = a; 508b1707c5dSDavid du Colombier return (uvlong)gbit32(i+4) << 32 | gbit32(a); 509b1707c5dSDavid du Colombier } 510b1707c5dSDavid du Colombier 511b1707c5dSDavid du Colombier static int 512b1707c5dSDavid du Colombier ahciidentify0(Aportc *pc, void *id, int atapi) 513b1707c5dSDavid du Colombier { 514b1707c5dSDavid du Colombier uchar *c; 515b1707c5dSDavid du Colombier Actab *t; 516b1707c5dSDavid du Colombier Alist *l; 517b1707c5dSDavid du Colombier Aprdt *p; 518b1707c5dSDavid du Colombier static uchar tab[] = { 0xec, 0xa1, }; 519b1707c5dSDavid du Colombier 520b1707c5dSDavid du Colombier t = pc->m->ctab; 521b1707c5dSDavid du Colombier c = t->cfis; 522b1707c5dSDavid du Colombier 523b1707c5dSDavid du Colombier memset(c, 0, 0x20); 524b1707c5dSDavid du Colombier c[0] = 0x27; 525b1707c5dSDavid du Colombier c[1] = 0x80; 526b1707c5dSDavid du Colombier c[2] = tab[atapi]; 527b1707c5dSDavid du Colombier c[7] = 0xa0; /* obsolete device bits */ 528b1707c5dSDavid du Colombier 529b1707c5dSDavid du Colombier l = pc->m->list; 530b1707c5dSDavid du Colombier l->flags = 1<<16 | 0x5; 531b1707c5dSDavid du Colombier l->len = 0; 532b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 533b1707c5dSDavid du Colombier l->ctabhi = 0; 534b1707c5dSDavid du Colombier 535b1707c5dSDavid du Colombier memset(id, 0, 0x100); 536b1707c5dSDavid du Colombier p = &t->prdt; 537b1707c5dSDavid du Colombier p->dba = PCIWADDR(id); 538b1707c5dSDavid du Colombier p->dbahi = 0; 539b1707c5dSDavid du Colombier p->count = 1<<31 | (0x200-2) | 1; 540b1707c5dSDavid du Colombier 541b1707c5dSDavid du Colombier return ahciwait(pc, 3*1000); 542b1707c5dSDavid du Colombier } 543b1707c5dSDavid du Colombier 544b1707c5dSDavid du Colombier static vlong 545b1707c5dSDavid du Colombier ahciidentify(Aportc *pc, ushort *id) 546b1707c5dSDavid du Colombier { 547b1707c5dSDavid du Colombier int i, sig; 548b1707c5dSDavid du Colombier vlong s; 549b1707c5dSDavid du Colombier Aportm *m; 550b1707c5dSDavid du Colombier 551b1707c5dSDavid du Colombier m = pc->m; 552b1707c5dSDavid du Colombier m->feat = 0; 553b1707c5dSDavid du Colombier m->smart = 0; 554b1707c5dSDavid du Colombier i = 0; 555b1707c5dSDavid du Colombier sig = pc->p->sig >> 16; 556b1707c5dSDavid du Colombier if(sig == 0xeb14){ 557b1707c5dSDavid du Colombier m->feat |= Datapi; 558b1707c5dSDavid du Colombier i = 1; 559b1707c5dSDavid du Colombier } 560b1707c5dSDavid du Colombier if(ahciidentify0(pc, id, i) == -1) 561b1707c5dSDavid du Colombier return -1; 562b1707c5dSDavid du Colombier 563b1707c5dSDavid du Colombier i = gbit16(id+83) | gbit16(id+86); 564b1707c5dSDavid du Colombier if(i & (1<<10)){ 565b1707c5dSDavid du Colombier m->feat |= Dllba; 566b1707c5dSDavid du Colombier s = gbit64(id+100); 567b1707c5dSDavid du Colombier }else 568b1707c5dSDavid du Colombier s = gbit32(id+60); 569b1707c5dSDavid du Colombier 570b1707c5dSDavid du Colombier if(m->feat&Datapi){ 571b1707c5dSDavid du Colombier i = gbit16(id+0); 572b1707c5dSDavid du Colombier if(i&1) 573b1707c5dSDavid du Colombier m->feat |= Datapi16; 574b1707c5dSDavid du Colombier } 575b1707c5dSDavid du Colombier 576b1707c5dSDavid du Colombier i = gbit16(id+83); 577b1707c5dSDavid du Colombier if((i>>14) == 1) { 578b1707c5dSDavid du Colombier if(i & (1<<3)) 579b1707c5dSDavid du Colombier m->feat |= Dpower; 580b1707c5dSDavid du Colombier i = gbit16(id+82); 581b1707c5dSDavid du Colombier if(i & 1) 582b1707c5dSDavid du Colombier m->feat |= Dsmart; 583b1707c5dSDavid du Colombier if(i & (1<<14)) 584b1707c5dSDavid du Colombier m->feat |= Dnop; 585b1707c5dSDavid du Colombier } 586b1707c5dSDavid du Colombier return s; 587b1707c5dSDavid du Colombier } 588b1707c5dSDavid du Colombier 589b1707c5dSDavid du Colombier static int 590b1707c5dSDavid du Colombier ahciquiet(Aport *a) 591b1707c5dSDavid du Colombier { 59291b330d9SDavid du Colombier ulong *p, i; 593b1707c5dSDavid du Colombier 594b1707c5dSDavid du Colombier p = &a->cmd; 595b1707c5dSDavid du Colombier *p &= ~Ast; 596b1707c5dSDavid du Colombier for(i = 0; i < 500; i += 50){ 597b1707c5dSDavid du Colombier if((*p & Acr) == 0) 598b1707c5dSDavid du Colombier goto stop; 599b1707c5dSDavid du Colombier asleep(50); 600b1707c5dSDavid du Colombier } 601b1707c5dSDavid du Colombier return -1; 602b1707c5dSDavid du Colombier stop: 603b1707c5dSDavid du Colombier if((a->task & (ASdrq|ASbsy)) == 0){ 604b1707c5dSDavid du Colombier *p |= Ast; 605b1707c5dSDavid du Colombier return 0; 606b1707c5dSDavid du Colombier } 607b1707c5dSDavid du Colombier 608b1707c5dSDavid du Colombier *p |= Aclo; 609b1707c5dSDavid du Colombier for(i = 0; i < 500; i += 50){ 610b1707c5dSDavid du Colombier if((*p & Aclo) == 0) 611b1707c5dSDavid du Colombier goto stop1; 612b1707c5dSDavid du Colombier asleep(50); 613b1707c5dSDavid du Colombier } 614b1707c5dSDavid du Colombier return -1; 615b1707c5dSDavid du Colombier stop1: 616b1707c5dSDavid du Colombier /* extra check */ 6173d56950aSDavid du Colombier dprint("ahci: clo clear %lx\n", a->task); 618b1707c5dSDavid du Colombier if(a->task & ASbsy) 619b1707c5dSDavid du Colombier return -1; 620b1707c5dSDavid du Colombier *p |= Ast; 621b1707c5dSDavid du Colombier return 0; 622b1707c5dSDavid du Colombier } 623b1707c5dSDavid du Colombier 624b1707c5dSDavid du Colombier static int 625b1707c5dSDavid du Colombier ahcicomreset(Aportc *pc) 626b1707c5dSDavid du Colombier { 627b1707c5dSDavid du Colombier uchar *c; 628b1707c5dSDavid du Colombier Actab *t; 629b1707c5dSDavid du Colombier Alist *l; 630b1707c5dSDavid du Colombier 631b1707c5dSDavid du Colombier dprint("ahcicomreset\n"); 6323d56950aSDavid du Colombier dreg("ahci: comreset ", pc->p); 633b1707c5dSDavid du Colombier if(ahciquiet(pc->p) == -1){ 6343d56950aSDavid du Colombier dprint("ahciquiet failed\n"); 635b1707c5dSDavid du Colombier return -1; 636b1707c5dSDavid du Colombier } 637b1707c5dSDavid du Colombier dreg("comreset ", pc->p); 638b1707c5dSDavid du Colombier 639b1707c5dSDavid du Colombier t = pc->m->ctab; 640b1707c5dSDavid du Colombier c = t->cfis; 641b1707c5dSDavid du Colombier 642b1707c5dSDavid du Colombier memset(c, 0, 0x20); 643b1707c5dSDavid du Colombier c[0] = 0x27; 644b1707c5dSDavid du Colombier c[1] = 0x00; 645b1707c5dSDavid du Colombier c[7] = 0xa0; /* obsolete device bits */ 646b1707c5dSDavid du Colombier c[15] = 1<<2; /* srst */ 647b1707c5dSDavid du Colombier 648b1707c5dSDavid du Colombier l = pc->m->list; 649b1707c5dSDavid du Colombier l->flags = Lclear | Lreset | 0x5; 650b1707c5dSDavid du Colombier l->len = 0; 651b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 652b1707c5dSDavid du Colombier l->ctabhi = 0; 653b1707c5dSDavid du Colombier 654b1707c5dSDavid du Colombier if(ahciwait(pc, 500) == -1){ 6553d56950aSDavid du Colombier dprint("ahcicomreset: first command failed\n"); 656b1707c5dSDavid du Colombier return -1; 657b1707c5dSDavid du Colombier } 658b1707c5dSDavid du Colombier microdelay(250); 659b1707c5dSDavid du Colombier dreg("comreset ", pc->p); 660b1707c5dSDavid du Colombier 661b1707c5dSDavid du Colombier memset(c, 0, 0x20); 662b1707c5dSDavid du Colombier c[0] = 0x27; 663b1707c5dSDavid du Colombier c[1] = 0x00; 664b1707c5dSDavid du Colombier c[7] = 0xa0; /* obsolete device bits */ 665b1707c5dSDavid du Colombier 666b1707c5dSDavid du Colombier l = pc->m->list; 667b1707c5dSDavid du Colombier l->flags = Lwrite | 0x5; 668b1707c5dSDavid du Colombier l->len = 0; 669b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 670b1707c5dSDavid du Colombier l->ctabhi = 0; 671b1707c5dSDavid du Colombier 672b1707c5dSDavid du Colombier if(ahciwait(pc, 150) == -1){ 6733d56950aSDavid du Colombier dprint("ahcicomreset: second command failed\n"); 674b1707c5dSDavid du Colombier return -1; 675b1707c5dSDavid du Colombier } 676b1707c5dSDavid du Colombier dreg("comreset ", pc->p); 677b1707c5dSDavid du Colombier return 0; 678b1707c5dSDavid du Colombier } 679b1707c5dSDavid du Colombier 680b1707c5dSDavid du Colombier static int 681b1707c5dSDavid du Colombier ahciidle(Aport *port) 682b1707c5dSDavid du Colombier { 68391b330d9SDavid du Colombier ulong *p, i, r; 684b1707c5dSDavid du Colombier 685b1707c5dSDavid du Colombier p = &port->cmd; 686b1707c5dSDavid du Colombier if((*p & Arun) == 0) 687b1707c5dSDavid du Colombier return 0; 688b1707c5dSDavid du Colombier *p &= ~Ast; 689b1707c5dSDavid du Colombier r = 0; 690b1707c5dSDavid du Colombier for(i = 0; i < 500; i += 25){ 691b1707c5dSDavid du Colombier if((*p & Acr) == 0) 692b1707c5dSDavid du Colombier goto stop; 693b1707c5dSDavid du Colombier asleep(25); 694b1707c5dSDavid du Colombier } 695b1707c5dSDavid du Colombier r = -1; 696b1707c5dSDavid du Colombier stop: 697b1707c5dSDavid du Colombier if((*p & Afre) == 0) 698b1707c5dSDavid du Colombier return r; 699b1707c5dSDavid du Colombier *p &= ~Afre; 700b1707c5dSDavid du Colombier for(i = 0; i < 500; i += 25){ 701b1707c5dSDavid du Colombier if((*p & Afre) == 0) 702b1707c5dSDavid du Colombier return 0; 703b1707c5dSDavid du Colombier asleep(25); 704b1707c5dSDavid du Colombier } 705b1707c5dSDavid du Colombier return -1; 706b1707c5dSDavid du Colombier } 707b1707c5dSDavid du Colombier 708b1707c5dSDavid du Colombier /* 709b1707c5dSDavid du Colombier * § 6.2.2.1 first part; comreset handled by reset disk. 710b1707c5dSDavid du Colombier * - remainder is handled by configdisk. 711b1707c5dSDavid du Colombier * - ahcirecover is a quick recovery from a failed command. 712b1707c5dSDavid du Colombier */ 71391b330d9SDavid du Colombier static int 714b1707c5dSDavid du Colombier ahciswreset(Aportc *pc) 715b1707c5dSDavid du Colombier { 716b1707c5dSDavid du Colombier int i; 717b1707c5dSDavid du Colombier 718b1707c5dSDavid du Colombier i = ahciidle(pc->p); 719b1707c5dSDavid du Colombier pc->p->cmd |= Afre; 720b1707c5dSDavid du Colombier if(i == -1) 721b1707c5dSDavid du Colombier return -1; 722b1707c5dSDavid du Colombier if(pc->p->task & (ASdrq|ASbsy)) 723b1707c5dSDavid du Colombier return -1; 724b1707c5dSDavid du Colombier return 0; 725b1707c5dSDavid du Colombier } 726b1707c5dSDavid du Colombier 72791b330d9SDavid du Colombier static int 728b1707c5dSDavid du Colombier ahcirecover(Aportc *pc) 729b1707c5dSDavid du Colombier { 730b1707c5dSDavid du Colombier ahciswreset(pc); 731b1707c5dSDavid du Colombier pc->p->cmd |= Ast; 732b1707c5dSDavid du Colombier if(setudmamode(pc, 5) == -1) 733b1707c5dSDavid du Colombier return -1; 734b1707c5dSDavid du Colombier return 0; 735b1707c5dSDavid du Colombier } 736b1707c5dSDavid du Colombier 737b1707c5dSDavid du Colombier static void* 738b1707c5dSDavid du Colombier malign(int size, int align) 739b1707c5dSDavid du Colombier { 740b1707c5dSDavid du Colombier void *v; 741b1707c5dSDavid du Colombier 742b1707c5dSDavid du Colombier v = xspanalloc(size, align, 0); 743b1707c5dSDavid du Colombier memset(v, 0, size); 744b1707c5dSDavid du Colombier return v; 745b1707c5dSDavid du Colombier } 746b1707c5dSDavid du Colombier 747b1707c5dSDavid du Colombier static void 748b1707c5dSDavid du Colombier setupfis(Afis *f) 749b1707c5dSDavid du Colombier { 750b1707c5dSDavid du Colombier f->base = malign(0x100, 0x100); 751b1707c5dSDavid du Colombier f->d = f->base + 0; 752b1707c5dSDavid du Colombier f->p = f->base + 0x20; 753b1707c5dSDavid du Colombier f->r = f->base + 0x40; 754b1707c5dSDavid du Colombier f->u = f->base + 0x60; 75591b330d9SDavid du Colombier f->devicebits = (ulong*)(f->base + 0x58); 756b1707c5dSDavid du Colombier } 757b1707c5dSDavid du Colombier 758b1707c5dSDavid du Colombier static void 759b1707c5dSDavid du Colombier ahciwakeup(Aport *p) 760b1707c5dSDavid du Colombier { 761b1707c5dSDavid du Colombier ushort s; 762b1707c5dSDavid du Colombier 763b1707c5dSDavid du Colombier s = p->sstatus; 764b1707c5dSDavid du Colombier if((s & 0xF00) != 0x600) 765b1707c5dSDavid du Colombier return; 766b1707c5dSDavid du Colombier if((s & 7) != 1){ /* not (device, no phy) */ 7673d56950aSDavid du Colombier iprint("ahci: slumbering drive unwakable %ux\n", s); 768b1707c5dSDavid du Colombier return; 769b1707c5dSDavid du Colombier } 770b1707c5dSDavid du Colombier p->sctl = 3*Aipm | 0*Aspd | Adet; 771b1707c5dSDavid du Colombier delay(1); 772b1707c5dSDavid du Colombier p->sctl &= ~7; 773b1707c5dSDavid du Colombier // iprint("ahci: wake %ux -> %ux\n", s, p->sstatus); 774b1707c5dSDavid du Colombier } 775b1707c5dSDavid du Colombier 776b1707c5dSDavid du Colombier static int 777b1707c5dSDavid du Colombier ahciconfigdrive(Ahba *h, Aportc *c, int mode) 778b1707c5dSDavid du Colombier { 779b1707c5dSDavid du Colombier Aportm *m; 780b1707c5dSDavid du Colombier Aport *p; 781b1707c5dSDavid du Colombier 782b1707c5dSDavid du Colombier p = c->p; 783b1707c5dSDavid du Colombier m = c->m; 784b1707c5dSDavid du Colombier 785b1707c5dSDavid du Colombier if(m->list == 0){ 786b1707c5dSDavid du Colombier setupfis(&m->fis); 787b1707c5dSDavid du Colombier m->list = malign(sizeof *m->list, 1024); 788b1707c5dSDavid du Colombier m->ctab = malign(sizeof *m->ctab, 128); 789b1707c5dSDavid du Colombier } 790b1707c5dSDavid du Colombier 791b1707c5dSDavid du Colombier if(p->sstatus & 3 && h->cap & Hsss){ 792b1707c5dSDavid du Colombier /* device connected & staggered spin-up */ 7933d56950aSDavid du Colombier dprint("ahci: configdrive: spinning up ... [%lux]\n", 7943d56950aSDavid du Colombier p->sstatus); 795b1707c5dSDavid du Colombier p->cmd |= Apod|Asud; 796b1707c5dSDavid du Colombier asleep(1400); 797b1707c5dSDavid du Colombier } 798b1707c5dSDavid du Colombier 799b1707c5dSDavid du Colombier p->serror = SerrAll; 800b1707c5dSDavid du Colombier 801b1707c5dSDavid du Colombier p->list = PCIWADDR(m->list); 802b1707c5dSDavid du Colombier p->listhi = 0; 803b1707c5dSDavid du Colombier p->fis = PCIWADDR(m->fis.base); 804b1707c5dSDavid du Colombier p->fishi = 0; 805b1707c5dSDavid du Colombier p->cmd |= Afre|Ast; 806b1707c5dSDavid du Colombier 807b1707c5dSDavid du Colombier if((p->sstatus & 0xF0F) == 0x601) /* drive coming up in slumbering? */ 808b1707c5dSDavid du Colombier ahciwakeup(p); 809b1707c5dSDavid du Colombier 810b1707c5dSDavid du Colombier /* disable power managment sequence from book. */ 81191157df7SDavid du Colombier p->sctl = (3*Aipm) | (mode*Aspd) | (0*Adet); 812b1707c5dSDavid du Colombier p->cmd &= ~Aalpe; 813b1707c5dSDavid du Colombier 814b1707c5dSDavid du Colombier p->ie = IEM; 815b1707c5dSDavid du Colombier 816b1707c5dSDavid du Colombier return 0; 817b1707c5dSDavid du Colombier } 818b1707c5dSDavid du Colombier 819b1707c5dSDavid du Colombier static int 820b1707c5dSDavid du Colombier ahcienable(Ahba *h) 821b1707c5dSDavid du Colombier { 822b1707c5dSDavid du Colombier h->ghc |= Hie; 823b1707c5dSDavid du Colombier return 0; 824b1707c5dSDavid du Colombier } 825b1707c5dSDavid du Colombier 826b1707c5dSDavid du Colombier static int 827b1707c5dSDavid du Colombier ahcidisable(Ahba *h) 828b1707c5dSDavid du Colombier { 829b1707c5dSDavid du Colombier h->ghc &= ~Hie; 830b1707c5dSDavid du Colombier return 0; 831b1707c5dSDavid du Colombier } 832b1707c5dSDavid du Colombier 833b1707c5dSDavid du Colombier static int 834b1707c5dSDavid du Colombier countbits(ulong u) 835b1707c5dSDavid du Colombier { 836b1707c5dSDavid du Colombier int i, n; 837b1707c5dSDavid du Colombier 838b1707c5dSDavid du Colombier n = 0; 839b1707c5dSDavid du Colombier for(i = 0; i < 32; i++) 840b1707c5dSDavid du Colombier if(u & (1<<i)) 841b1707c5dSDavid du Colombier n++; 842b1707c5dSDavid du Colombier return n; 843b1707c5dSDavid du Colombier } 844b1707c5dSDavid du Colombier 845b1707c5dSDavid du Colombier static int 846b1707c5dSDavid du Colombier ahciconf(Ctlr *ctlr) 847b1707c5dSDavid du Colombier { 848b1707c5dSDavid du Colombier Ahba *h; 84991b330d9SDavid du Colombier ulong u; 850b1707c5dSDavid du Colombier 851b1707c5dSDavid du Colombier h = ctlr->hba = (Ahba*)ctlr->mmio; 852b1707c5dSDavid du Colombier u = h->cap; 853b1707c5dSDavid du Colombier 854b1707c5dSDavid du Colombier if((u&Hsam) == 0) 855b1707c5dSDavid du Colombier h->ghc |= Hae; 856b1707c5dSDavid du Colombier 857014ad43fSDavid du Colombier print("#S/sd%c: ahci %s port %#p: sss %ld ncs %ld coal %ld " 858014ad43fSDavid du Colombier "mports %ld led %ld clo %ld ems %ld\n", 859014ad43fSDavid du Colombier ctlr->sdev->idno, tname[ctlr->type], h, 860b1707c5dSDavid du Colombier (u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1, u & 0x1f, (u>>25) & 1, 861b1707c5dSDavid du Colombier (u>>24) & 1, (u>>6) & 1); 862b1707c5dSDavid du Colombier return countbits(h->pi); 863b1707c5dSDavid du Colombier } 864b1707c5dSDavid du Colombier 865b1707c5dSDavid du Colombier static int 866b1707c5dSDavid du Colombier ahcihbareset(Ahba *h) 867b1707c5dSDavid du Colombier { 868b1707c5dSDavid du Colombier int wait; 869b1707c5dSDavid du Colombier 870b1707c5dSDavid du Colombier h->ghc |= 1; 871b1707c5dSDavid du Colombier for(wait = 0; wait < 1000; wait += 100){ 872b1707c5dSDavid du Colombier if(h->ghc == 0) 873b1707c5dSDavid du Colombier return 0; 874b1707c5dSDavid du Colombier delay(100); 875b1707c5dSDavid du Colombier } 876b1707c5dSDavid du Colombier return -1; 877b1707c5dSDavid du Colombier } 878b1707c5dSDavid du Colombier 879b1707c5dSDavid du Colombier static void 880b1707c5dSDavid du Colombier idmove(char *p, ushort *a, int n) 881b1707c5dSDavid du Colombier { 882b1707c5dSDavid du Colombier int i; 883b1707c5dSDavid du Colombier char *op, *e; 884b1707c5dSDavid du Colombier 885b1707c5dSDavid du Colombier op = p; 886b1707c5dSDavid du Colombier for(i = 0; i < n/2; i++){ 887b1707c5dSDavid du Colombier *p++ = a[i] >> 8; 888b1707c5dSDavid du Colombier *p++ = a[i]; 889b1707c5dSDavid du Colombier } 890b1707c5dSDavid du Colombier *p = 0; 891b1707c5dSDavid du Colombier while(p > op && *--p == ' ') 892b1707c5dSDavid du Colombier *p = 0; 893b1707c5dSDavid du Colombier e = p; 89491157df7SDavid du Colombier for (p = op; *p == ' '; p++) 89591157df7SDavid du Colombier ; 896b1707c5dSDavid du Colombier memmove(op, p, n - (e - p)); 897b1707c5dSDavid du Colombier } 898b1707c5dSDavid du Colombier 899b1707c5dSDavid du Colombier static int 900b1707c5dSDavid du Colombier identify(Drive *d) 901b1707c5dSDavid du Colombier { 90291b330d9SDavid du Colombier ushort *id; 903b1707c5dSDavid du Colombier vlong osectors, s; 904b1707c5dSDavid du Colombier uchar oserial[21]; 905b1707c5dSDavid du Colombier SDunit *u; 906b1707c5dSDavid du Colombier 907b1707c5dSDavid du Colombier id = d->info; 908b1707c5dSDavid du Colombier s = ahciidentify(&d->portc, id); 909b1707c5dSDavid du Colombier if(s == -1){ 910b1707c5dSDavid du Colombier d->state = Derror; 911b1707c5dSDavid du Colombier return -1; 912b1707c5dSDavid du Colombier } 913b1707c5dSDavid du Colombier osectors = d->sectors; 914b1707c5dSDavid du Colombier memmove(oserial, d->serial, sizeof d->serial); 915b1707c5dSDavid du Colombier 916*58db92f4SDavid du Colombier u = d->unit; 917b1707c5dSDavid du Colombier d->sectors = s; 918*58db92f4SDavid du Colombier d->secsize = u->secsize; 919*58db92f4SDavid du Colombier if(d->secsize == 0) 920*58db92f4SDavid du Colombier d->secsize = 512; /* default */ 921b1707c5dSDavid du Colombier d->smartrs = 0; 922b1707c5dSDavid du Colombier 923b1707c5dSDavid du Colombier idmove(d->serial, id+10, 20); 924b1707c5dSDavid du Colombier idmove(d->firmware, id+23, 8); 925b1707c5dSDavid du Colombier idmove(d->model, id+27, 40); 926b1707c5dSDavid du Colombier 927b1707c5dSDavid du Colombier memset(u->inquiry, 0, sizeof u->inquiry); 928b1707c5dSDavid du Colombier u->inquiry[2] = 2; 929b1707c5dSDavid du Colombier u->inquiry[3] = 2; 930b1707c5dSDavid du Colombier u->inquiry[4] = sizeof u->inquiry - 4; 931b1707c5dSDavid du Colombier memmove(u->inquiry+8, d->model, 40); 932b1707c5dSDavid du Colombier 93391157df7SDavid du Colombier if(osectors != s || memcmp(oserial, d->serial, sizeof oserial) != 0){ 934b1707c5dSDavid du Colombier d->mediachange = 1; 935b1707c5dSDavid du Colombier u->sectors = 0; 936b1707c5dSDavid du Colombier } 937b1707c5dSDavid du Colombier return 0; 938b1707c5dSDavid du Colombier } 939b1707c5dSDavid du Colombier 940b1707c5dSDavid du Colombier static void 941b1707c5dSDavid du Colombier clearci(Aport *p) 942b1707c5dSDavid du Colombier { 943b1707c5dSDavid du Colombier if(p->cmd & Ast) { 944b1707c5dSDavid du Colombier p->cmd &= ~Ast; 945b1707c5dSDavid du Colombier p->cmd |= Ast; 946b1707c5dSDavid du Colombier } 947b1707c5dSDavid du Colombier } 948b1707c5dSDavid du Colombier 949b1707c5dSDavid du Colombier static void 950b1707c5dSDavid du Colombier updatedrive(Drive *d) 951b1707c5dSDavid du Colombier { 95291b330d9SDavid du Colombier ulong cause, serr, s0, pr, ewake; 953b1707c5dSDavid du Colombier char *name; 954b1707c5dSDavid du Colombier Aport *p; 95591b330d9SDavid du Colombier static ulong last; 956b1707c5dSDavid du Colombier 957b1707c5dSDavid du Colombier pr = 1; 958b1707c5dSDavid du Colombier ewake = 0; 959b1707c5dSDavid du Colombier p = d->port; 960b1707c5dSDavid du Colombier cause = p->isr; 961b1707c5dSDavid du Colombier serr = p->serror; 962b1707c5dSDavid du Colombier p->isr = cause; 963b1707c5dSDavid du Colombier name = "??"; 964b1707c5dSDavid du Colombier if(d->unit && d->unit->name) 965b1707c5dSDavid du Colombier name = d->unit->name; 966b1707c5dSDavid du Colombier 967b1707c5dSDavid du Colombier if(p->ci == 0){ 968b1707c5dSDavid du Colombier d->portm.flag |= Fdone; 969b1707c5dSDavid du Colombier wakeup(&d->portm); 970b1707c5dSDavid du Colombier pr = 0; 971b1707c5dSDavid du Colombier }else if(cause & Adps) 972b1707c5dSDavid du Colombier pr = 0; 973b1707c5dSDavid du Colombier if(cause & Ifatal){ 974b1707c5dSDavid du Colombier ewake = 1; 9753d56950aSDavid du Colombier dprint("ahci: updatedrive: fatal\n"); 976b1707c5dSDavid du Colombier } 977b1707c5dSDavid du Colombier if(cause & Adhrs){ 97891157df7SDavid du Colombier if(p->task & (1<<5|1)){ 9793d56950aSDavid du Colombier dprint("ahci: Adhrs cause %lux serr %lux task %lux\n", 980b1707c5dSDavid du Colombier cause, serr, p->task); 981b1707c5dSDavid du Colombier d->portm.flag |= Ferror; 982b1707c5dSDavid du Colombier ewake = 1; 983b1707c5dSDavid du Colombier } 984b1707c5dSDavid du Colombier pr = 0; 985b1707c5dSDavid du Colombier } 986b1707c5dSDavid du Colombier if(p->task & 1 && last != cause) 9873d56950aSDavid du Colombier dprint("%s: err ca %lux serr %lux task %lux sstat %lux\n", 9883d56950aSDavid du Colombier name, cause, serr, p->task, p->sstatus); 989b1707c5dSDavid du Colombier if(pr) 99091b330d9SDavid du Colombier dprint("%s: upd %lux ta %lux\n", name, cause, p->task); 991b1707c5dSDavid du Colombier 992b1707c5dSDavid du Colombier if(cause & (Aprcs|Aifs)){ 993b1707c5dSDavid du Colombier s0 = d->state; 994b1707c5dSDavid du Colombier switch(p->sstatus & 7){ 995b1707c5dSDavid du Colombier case 0: /* no device */ 996b1707c5dSDavid du Colombier d->state = Dmissing; 997b1707c5dSDavid du Colombier break; 998b1707c5dSDavid du Colombier case 1: /* device but no phy comm. */ 999b1707c5dSDavid du Colombier if((p->sstatus & 0xF00) == 0x600) 1000b1707c5dSDavid du Colombier d->state = Dnew; /* slumbering */ 1001b1707c5dSDavid du Colombier else 1002b1707c5dSDavid du Colombier d->state = Derror; 1003b1707c5dSDavid du Colombier break; 1004b1707c5dSDavid du Colombier case 3: /* device & phy comm. estab. */ 100591157df7SDavid du Colombier /* power mgnt crap for surprise removal */ 1006b1707c5dSDavid du Colombier p->ie |= Aprcs|Apcs; /* is this required? */ 1007b1707c5dSDavid du Colombier d->state = Dreset; 1008b1707c5dSDavid du Colombier break; 1009b1707c5dSDavid du Colombier case 4: /* phy off-line */ 1010b1707c5dSDavid du Colombier d->state = Doffline; 1011b1707c5dSDavid du Colombier break; 1012b1707c5dSDavid du Colombier } 10133d56950aSDavid du Colombier dprint("%s: %s → %s [Apcrs] %lux\n", name, 10143d56950aSDavid du Colombier diskstates[s0], diskstates[d->state], p->sstatus); 1015b1707c5dSDavid du Colombier /* print pulled message here. */ 1016b1707c5dSDavid du Colombier if(s0 == Dready && d->state != Dready) 1017b1707c5dSDavid du Colombier idprint("%s: pulled\n", name); 1018b1707c5dSDavid du Colombier if(d->state != Dready) 1019b1707c5dSDavid du Colombier d->portm.flag |= Ferror; 1020b1707c5dSDavid du Colombier ewake = 1; 1021b1707c5dSDavid du Colombier } 1022b1707c5dSDavid du Colombier p->serror = serr; 1023b1707c5dSDavid du Colombier if(ewake){ 1024b1707c5dSDavid du Colombier clearci(p); 1025b1707c5dSDavid du Colombier wakeup(&d->portm); 1026b1707c5dSDavid du Colombier } 1027b1707c5dSDavid du Colombier last = cause; 1028b1707c5dSDavid du Colombier } 1029b1707c5dSDavid du Colombier 1030b1707c5dSDavid du Colombier static void 1031b1707c5dSDavid du Colombier pstatus(Drive *d, ulong s) 1032b1707c5dSDavid du Colombier { 1033b1707c5dSDavid du Colombier /* 1034b1707c5dSDavid du Colombier * bogus code because the first interrupt is currently dropped. 103591157df7SDavid du Colombier * likely my fault. serror may be cleared at the wrong time. 1036b1707c5dSDavid du Colombier */ 1037b1707c5dSDavid du Colombier switch(s){ 1038b1707c5dSDavid du Colombier case 0: /* no device */ 1039b1707c5dSDavid du Colombier d->state = Dmissing; 1040b1707c5dSDavid du Colombier break; 1041b1707c5dSDavid du Colombier case 1: /* device but no phy. comm. */ 1042b1707c5dSDavid du Colombier break; 1043b1707c5dSDavid du Colombier case 2: /* should this be missing? need testcase. */ 10443d56950aSDavid du Colombier dprint("ahci: pstatus 2\n"); 1045b1707c5dSDavid du Colombier /* fallthrough */ 1046b1707c5dSDavid du Colombier case 3: /* device & phy. comm. */ 1047b1707c5dSDavid du Colombier d->wait = 0; 1048b1707c5dSDavid du Colombier d->state = Dnew; 1049b1707c5dSDavid du Colombier break; 1050b1707c5dSDavid du Colombier case 4: /* offline */ 1051b1707c5dSDavid du Colombier d->state = Doffline; 1052b1707c5dSDavid du Colombier break; 1053b1707c5dSDavid du Colombier case 6: /* ? not sure this makes sense. TODO */ 1054b1707c5dSDavid du Colombier d->state = Dnew; 1055b1707c5dSDavid du Colombier break; 1056b1707c5dSDavid du Colombier } 1057b1707c5dSDavid du Colombier } 1058b1707c5dSDavid du Colombier 1059b1707c5dSDavid du Colombier static int 1060b1707c5dSDavid du Colombier configdrive(Drive *d) 1061b1707c5dSDavid du Colombier { 1062b1707c5dSDavid du Colombier if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1) 1063b1707c5dSDavid du Colombier return -1; 1064b1707c5dSDavid du Colombier ilock(d); 1065b1707c5dSDavid du Colombier pstatus(d, d->port->sstatus & 7); 1066b1707c5dSDavid du Colombier iunlock(d); 1067b1707c5dSDavid du Colombier return 0; 1068b1707c5dSDavid du Colombier } 1069b1707c5dSDavid du Colombier 1070b1707c5dSDavid du Colombier static void 1071b1707c5dSDavid du Colombier resetdisk(Drive *d) 1072b1707c5dSDavid du Colombier { 1073b1707c5dSDavid du Colombier uint state, det, stat; 1074b1707c5dSDavid du Colombier Aport *p; 1075b1707c5dSDavid du Colombier 1076b1707c5dSDavid du Colombier p = d->port; 1077b1707c5dSDavid du Colombier det = p->sctl & 7; 1078b1707c5dSDavid du Colombier stat = p->sstatus & 7; 1079b1707c5dSDavid du Colombier state = (p->cmd>>28) & 0xf; 10803d56950aSDavid du Colombier dprint("ahci: resetdisk: icc %ux det %d sdet %d\n", state, det, stat); 1081014ad43fSDavid du Colombier 1082b1707c5dSDavid du Colombier ilock(d); 1083b1707c5dSDavid du Colombier state = d->state; 1084b1707c5dSDavid du Colombier if(d->state != Dready || d->state != Dnew) 1085b1707c5dSDavid du Colombier d->portm.flag |= Ferror; 1086b1707c5dSDavid du Colombier clearci(p); /* satisfy sleep condition. */ 1087b1707c5dSDavid du Colombier wakeup(&d->portm); 1088014ad43fSDavid du Colombier if(stat != 3){ /* device absent or phy not communicating? */ 1089014ad43fSDavid du Colombier d->state = Dportreset; 1090014ad43fSDavid du Colombier iunlock(d); 1091014ad43fSDavid du Colombier return; 1092014ad43fSDavid du Colombier } 109381ede731SDavid du Colombier d->state = Derror; 1094b1707c5dSDavid du Colombier iunlock(d); 1095b1707c5dSDavid du Colombier 1096b1707c5dSDavid du Colombier qlock(&d->portm); 1097b1707c5dSDavid du Colombier if(p->cmd&Ast && ahciswreset(&d->portc) == -1){ 1098b1707c5dSDavid du Colombier ilock(d); 1099b1707c5dSDavid du Colombier d->state = Dportreset; /* get a bigger stick. */ 1100b1707c5dSDavid du Colombier iunlock(d); 1101b1707c5dSDavid du Colombier } else { 1102b1707c5dSDavid du Colombier ilock(d); 1103b1707c5dSDavid du Colombier d->state = Dmissing; 1104b1707c5dSDavid du Colombier iunlock(d); 1105b1707c5dSDavid du Colombier 1106b1707c5dSDavid du Colombier configdrive(d); 1107b1707c5dSDavid du Colombier } 11083d56950aSDavid du Colombier dprint("ahci: resetdisk: %s → %s\n", 11093d56950aSDavid du Colombier diskstates[state], diskstates[d->state]); 1110b1707c5dSDavid du Colombier qunlock(&d->portm); 1111b1707c5dSDavid du Colombier } 1112b1707c5dSDavid du Colombier 1113b1707c5dSDavid du Colombier static int 1114b1707c5dSDavid du Colombier newdrive(Drive *d) 1115b1707c5dSDavid du Colombier { 1116*58db92f4SDavid du Colombier char *name; 1117b1707c5dSDavid du Colombier Aportc *c; 1118b1707c5dSDavid du Colombier Aportm *m; 1119b1707c5dSDavid du Colombier 1120b1707c5dSDavid du Colombier c = &d->portc; 1121b1707c5dSDavid du Colombier m = &d->portm; 1122b1707c5dSDavid du Colombier 1123b1707c5dSDavid du Colombier name = d->unit->name; 1124b1707c5dSDavid du Colombier if(name == 0) 1125b1707c5dSDavid du Colombier name = "??"; 1126b1707c5dSDavid du Colombier 1127b1707c5dSDavid du Colombier if(d->port->task == 0x80) 1128b1707c5dSDavid du Colombier return -1; 1129b1707c5dSDavid du Colombier qlock(c->m); 1130b1707c5dSDavid du Colombier if(setudmamode(c, 5) == -1){ 1131b1707c5dSDavid du Colombier dprint("%s: can't set udma mode\n", name); 1132b1707c5dSDavid du Colombier goto lose; 1133b1707c5dSDavid du Colombier } 1134b1707c5dSDavid du Colombier if(identify(d) == -1){ 1135b1707c5dSDavid du Colombier dprint("%s: identify failure\n", name); 1136b1707c5dSDavid du Colombier goto lose; 1137b1707c5dSDavid du Colombier } 1138b1707c5dSDavid du Colombier if(m->feat & Dpower && setfeatures(c, 0x85) == -1){ 1139b1707c5dSDavid du Colombier m->feat &= ~Dpower; 1140b1707c5dSDavid du Colombier if(ahcirecover(c) == -1) 1141b1707c5dSDavid du Colombier goto lose; 1142b1707c5dSDavid du Colombier } 1143b1707c5dSDavid du Colombier 1144b1707c5dSDavid du Colombier ilock(d); 1145b1707c5dSDavid du Colombier d->state = Dready; 1146b1707c5dSDavid du Colombier iunlock(d); 1147b1707c5dSDavid du Colombier 1148b1707c5dSDavid du Colombier qunlock(c->m); 1149b1707c5dSDavid du Colombier 1150*58db92f4SDavid du Colombier idprint("%s: %sLBA %,llud sectors: %s %s %s %s\n", d->unit->name, 1151*58db92f4SDavid du Colombier (m->feat & Dllba? "L": ""), d->sectors, d->model, d->firmware, 1152*58db92f4SDavid du Colombier d->serial, d->mediachange? "[mediachange]": ""); 1153b1707c5dSDavid du Colombier return 0; 1154b1707c5dSDavid du Colombier 1155b1707c5dSDavid du Colombier lose: 115681ede731SDavid du Colombier idprint("%s: can't be initialized\n", d->unit->name); 115781ede731SDavid du Colombier ilock(d); 115881ede731SDavid du Colombier d->state = Dnull; 115981ede731SDavid du Colombier iunlock(d); 1160b1707c5dSDavid du Colombier qunlock(c->m); 1161b1707c5dSDavid du Colombier return -1; 1162b1707c5dSDavid du Colombier } 1163b1707c5dSDavid du Colombier 1164b1707c5dSDavid du Colombier enum { 1165b1707c5dSDavid du Colombier Nms = 256, 1166b1707c5dSDavid du Colombier Mphywait = 2*1024/Nms - 1, 1167b1707c5dSDavid du Colombier Midwait = 16*1024/Nms - 1, 1168b1707c5dSDavid du Colombier Mcomrwait = 64*1024/Nms - 1, 1169b1707c5dSDavid du Colombier }; 1170b1707c5dSDavid du Colombier 1171b1707c5dSDavid du Colombier static void 1172b1707c5dSDavid du Colombier westerndigitalhung(Drive *d) 1173b1707c5dSDavid du Colombier { 1174f3112f79SDavid du Colombier if((d->portm.feat&Datapi) == 0 && d->active && 1175f3112f79SDavid du Colombier TK2MS(MACHP(0)->ticks - d->intick) > 5000){ 1176014ad43fSDavid du Colombier dprint("%s: drive hung; resetting [%lux] ci %lx\n", 1177b1707c5dSDavid du Colombier d->unit->name, d->port->task, d->port->ci); 1178b1707c5dSDavid du Colombier d->state = Dreset; 1179b1707c5dSDavid du Colombier } 1180b1707c5dSDavid du Colombier } 1181b1707c5dSDavid du Colombier 1182b1707c5dSDavid du Colombier static ushort olds[NCtlr*NCtlrdrv]; 1183b1707c5dSDavid du Colombier 1184b1707c5dSDavid du Colombier static int 1185b1707c5dSDavid du Colombier doportreset(Drive *d) 1186b1707c5dSDavid du Colombier { 1187b1707c5dSDavid du Colombier int i; 1188b1707c5dSDavid du Colombier 1189b1707c5dSDavid du Colombier i = -1; 1190b1707c5dSDavid du Colombier qlock(&d->portm); 1191b1707c5dSDavid du Colombier if(ahciportreset(&d->portc) == -1) 11923d56950aSDavid du Colombier dprint("ahci: doportreset: fails\n"); 1193b1707c5dSDavid du Colombier else 1194b1707c5dSDavid du Colombier i = 0; 1195b1707c5dSDavid du Colombier qunlock(&d->portm); 11963d56950aSDavid du Colombier dprint("ahci: doportreset: portreset → %s [task %lux]\n", 1197b1707c5dSDavid du Colombier diskstates[d->state], d->port->task); 1198b1707c5dSDavid du Colombier return i; 1199b1707c5dSDavid du Colombier } 1200b1707c5dSDavid du Colombier 1201014ad43fSDavid du Colombier /* drive must be locked */ 1202014ad43fSDavid du Colombier static void 1203014ad43fSDavid du Colombier statechange(Drive *d) 1204014ad43fSDavid du Colombier { 1205014ad43fSDavid du Colombier switch(d->state){ 1206014ad43fSDavid du Colombier case Dnull: 1207014ad43fSDavid du Colombier case Doffline: 1208014ad43fSDavid du Colombier if(d->unit->sectors != 0){ 1209014ad43fSDavid du Colombier d->sectors = 0; 1210014ad43fSDavid du Colombier d->mediachange = 1; 1211014ad43fSDavid du Colombier } 1212014ad43fSDavid du Colombier /* fallthrough */ 1213014ad43fSDavid du Colombier case Dready: 1214014ad43fSDavid du Colombier d->wait = 0; 1215014ad43fSDavid du Colombier break; 1216014ad43fSDavid du Colombier } 1217014ad43fSDavid du Colombier } 1218014ad43fSDavid du Colombier 1219b1707c5dSDavid du Colombier static void 1220b1707c5dSDavid du Colombier checkdrive(Drive *d, int i) 1221b1707c5dSDavid du Colombier { 1222b1707c5dSDavid du Colombier ushort s; 1223b1707c5dSDavid du Colombier char *name; 1224b1707c5dSDavid du Colombier 1225b1707c5dSDavid du Colombier ilock(d); 1226b1707c5dSDavid du Colombier name = d->unit->name; 1227b1707c5dSDavid du Colombier s = d->port->sstatus; 122881ede731SDavid du Colombier if(s) 122981ede731SDavid du Colombier d->lastseen = MACHP(0)->ticks; 1230b1707c5dSDavid du Colombier if(s != olds[i]){ 1231b1707c5dSDavid du Colombier dprint("%s: status: %04ux -> %04ux: %s\n", 1232b1707c5dSDavid du Colombier name, olds[i], s, diskstates[d->state]); 1233b1707c5dSDavid du Colombier olds[i] = s; 1234b1707c5dSDavid du Colombier d->wait = 0; 1235b1707c5dSDavid du Colombier } 1236b1707c5dSDavid du Colombier westerndigitalhung(d); 1237b1707c5dSDavid du Colombier switch(d->state){ 1238b1707c5dSDavid du Colombier case Dnull: 1239b1707c5dSDavid du Colombier case Dready: 1240b1707c5dSDavid du Colombier break; 1241b1707c5dSDavid du Colombier case Dmissing: 1242b1707c5dSDavid du Colombier case Dnew: 1243b1707c5dSDavid du Colombier switch(s & 0x107){ 1244b1707c5dSDavid du Colombier case 1: /* no device (pm), device but no phy. comm. */ 1245b1707c5dSDavid du Colombier ahciwakeup(d->port); 1246b1707c5dSDavid du Colombier /* fall through */ 1247b1707c5dSDavid du Colombier case 0: /* no device */ 1248b1707c5dSDavid du Colombier break; 1249b1707c5dSDavid du Colombier default: 1250b1707c5dSDavid du Colombier dprint("%s: unknown status %04ux\n", name, s); 1251b1707c5dSDavid du Colombier /* fall through */ 1252b1707c5dSDavid du Colombier case 0x100: /* active, no device */ 1253b1707c5dSDavid du Colombier if(++d->wait&Mphywait) 1254b1707c5dSDavid du Colombier break; 1255b1707c5dSDavid du Colombier reset: 1256b1707c5dSDavid du Colombier if(++d->mode > DMsataii) 1257b1707c5dSDavid du Colombier d->mode = 0; 1258b1707c5dSDavid du Colombier if(d->mode == DMsatai){ /* we tried everything */ 1259b1707c5dSDavid du Colombier d->state = Dportreset; 1260b1707c5dSDavid du Colombier goto portreset; 1261b1707c5dSDavid du Colombier } 1262b1707c5dSDavid du Colombier dprint("%s: reset; new mode %s\n", name, 1263b1707c5dSDavid du Colombier modename[d->mode]); 1264b1707c5dSDavid du Colombier iunlock(d); 1265b1707c5dSDavid du Colombier resetdisk(d); 1266b1707c5dSDavid du Colombier ilock(d); 1267b1707c5dSDavid du Colombier break; 1268b1707c5dSDavid du Colombier case 0x103: /* active, device, phy. comm. */ 1269b1707c5dSDavid du Colombier if((++d->wait&Midwait) == 0){ 127091b330d9SDavid du Colombier dprint("%s: slow reset %04ux task=%lux; %d\n", 1271b1707c5dSDavid du Colombier name, s, d->port->task, d->wait); 1272b1707c5dSDavid du Colombier goto reset; 1273b1707c5dSDavid du Colombier } 1274b1707c5dSDavid du Colombier s = (uchar)d->port->task; 1275b1707c5dSDavid du Colombier if(s == 0x7f || ((d->port->sig >> 16) != 0xeb14 && 1276b1707c5dSDavid du Colombier (s & ~0x17) != (1<<6))) 1277b1707c5dSDavid du Colombier break; 1278b1707c5dSDavid du Colombier iunlock(d); 1279b1707c5dSDavid du Colombier newdrive(d); 1280b1707c5dSDavid du Colombier ilock(d); 1281b1707c5dSDavid du Colombier break; 1282b1707c5dSDavid du Colombier } 1283b1707c5dSDavid du Colombier break; 1284b1707c5dSDavid du Colombier case Doffline: 1285b1707c5dSDavid du Colombier if(d->wait++ & Mcomrwait) 1286b1707c5dSDavid du Colombier break; 1287b1707c5dSDavid du Colombier /* fallthrough */ 1288b1707c5dSDavid du Colombier case Derror: 1289b1707c5dSDavid du Colombier case Dreset: 1290b1707c5dSDavid du Colombier dprint("%s: reset [%s]: mode %d; status %04ux\n", 1291b1707c5dSDavid du Colombier name, diskstates[d->state], d->mode, s); 1292b1707c5dSDavid du Colombier iunlock(d); 1293b1707c5dSDavid du Colombier resetdisk(d); 1294b1707c5dSDavid du Colombier ilock(d); 1295b1707c5dSDavid du Colombier break; 1296b1707c5dSDavid du Colombier case Dportreset: 1297b1707c5dSDavid du Colombier portreset: 1298b1707c5dSDavid du Colombier if(d->wait++ & 0xff && (s & 0x100) == 0) 1299b1707c5dSDavid du Colombier break; 1300b1707c5dSDavid du Colombier /* device is active */ 1301b1707c5dSDavid du Colombier dprint("%s: portreset [%s]: mode %d; status %04ux\n", 1302b1707c5dSDavid du Colombier name, diskstates[d->state], d->mode, s); 1303b1707c5dSDavid du Colombier d->portm.flag |= Ferror; 1304b1707c5dSDavid du Colombier clearci(d->port); 1305b1707c5dSDavid du Colombier wakeup(&d->portm); 1306b1707c5dSDavid du Colombier if((s & 7) == 0){ /* no device */ 1307b1707c5dSDavid du Colombier d->state = Dmissing; 1308b1707c5dSDavid du Colombier break; 1309b1707c5dSDavid du Colombier } 1310b1707c5dSDavid du Colombier iunlock(d); 1311b1707c5dSDavid du Colombier doportreset(d); 1312b1707c5dSDavid du Colombier ilock(d); 1313b1707c5dSDavid du Colombier break; 1314b1707c5dSDavid du Colombier } 1315014ad43fSDavid du Colombier statechange(d); 1316b1707c5dSDavid du Colombier iunlock(d); 1317b1707c5dSDavid du Colombier } 1318b1707c5dSDavid du Colombier 1319b1707c5dSDavid du Colombier static void 1320b1707c5dSDavid du Colombier satakproc(void*) 1321b1707c5dSDavid du Colombier { 1322b1707c5dSDavid du Colombier int i; 1323b1707c5dSDavid du Colombier 1324b1707c5dSDavid du Colombier for(;;){ 1325b1707c5dSDavid du Colombier tsleep(&up->sleep, return0, 0, Nms); 1326b1707c5dSDavid du Colombier for(i = 0; i < niadrive; i++) 1327b1707c5dSDavid du Colombier checkdrive(iadrive[i], i); 1328b1707c5dSDavid du Colombier } 1329b1707c5dSDavid du Colombier } 1330b1707c5dSDavid du Colombier 1331b1707c5dSDavid du Colombier static void 1332b1707c5dSDavid du Colombier iainterrupt(Ureg*, void *a) 1333b1707c5dSDavid du Colombier { 1334b1707c5dSDavid du Colombier int i; 1335b1707c5dSDavid du Colombier ulong cause, m; 1336b1707c5dSDavid du Colombier Ctlr *c; 1337b1707c5dSDavid du Colombier Drive *d; 1338b1707c5dSDavid du Colombier 1339b1707c5dSDavid du Colombier c = a; 1340b1707c5dSDavid du Colombier ilock(c); 1341b1707c5dSDavid du Colombier cause = c->hba->isr; 1342014ad43fSDavid du Colombier for(i = 0; i < c->mport; i++){ 1343b1707c5dSDavid du Colombier m = 1 << i; 1344b1707c5dSDavid du Colombier if((cause & m) == 0) 1345b1707c5dSDavid du Colombier continue; 1346b1707c5dSDavid du Colombier d = c->rawdrive + i; 1347b1707c5dSDavid du Colombier ilock(d); 1348b1707c5dSDavid du Colombier if(d->port->isr && c->hba->pi & m) 1349b1707c5dSDavid du Colombier updatedrive(d); 1350b1707c5dSDavid du Colombier c->hba->isr = m; 1351b1707c5dSDavid du Colombier iunlock(d); 1352b1707c5dSDavid du Colombier } 1353b1707c5dSDavid du Colombier iunlock(c); 1354b1707c5dSDavid du Colombier } 1355b1707c5dSDavid du Colombier 1356b1707c5dSDavid du Colombier static int 1357b1707c5dSDavid du Colombier iaverify(SDunit *u) 1358b1707c5dSDavid du Colombier { 1359b1707c5dSDavid du Colombier Ctlr *c; 1360b1707c5dSDavid du Colombier Drive *d; 1361b1707c5dSDavid du Colombier 1362b1707c5dSDavid du Colombier c = u->dev->ctlr; 1363b1707c5dSDavid du Colombier d = c->drive[u->subno]; 1364b1707c5dSDavid du Colombier ilock(c); 1365b1707c5dSDavid du Colombier ilock(d); 1366b1707c5dSDavid du Colombier d->unit = u; 1367b1707c5dSDavid du Colombier iunlock(d); 1368b1707c5dSDavid du Colombier iunlock(c); 1369014ad43fSDavid du Colombier checkdrive(d, d->driveno); /* c->d0 + d->driveno */ 1370b1707c5dSDavid du Colombier return 1; 1371b1707c5dSDavid du Colombier } 1372b1707c5dSDavid du Colombier 1373b1707c5dSDavid du Colombier static int 1374b1707c5dSDavid du Colombier iaenable(SDev *s) 1375b1707c5dSDavid du Colombier { 1376b1707c5dSDavid du Colombier char name[32]; 1377b1707c5dSDavid du Colombier Ctlr *c; 1378b1707c5dSDavid du Colombier static int once; 1379b1707c5dSDavid du Colombier 1380b1707c5dSDavid du Colombier c = s->ctlr; 1381b1707c5dSDavid du Colombier ilock(c); 1382b1707c5dSDavid du Colombier if(!c->enabled) { 1383b1707c5dSDavid du Colombier if(once == 0) { 1384b1707c5dSDavid du Colombier once = 1; 1385b1707c5dSDavid du Colombier kproc("iasata", satakproc, 0); 1386b1707c5dSDavid du Colombier } 1387b1707c5dSDavid du Colombier if(c->ndrive == 0) 1388b1707c5dSDavid du Colombier panic("iaenable: zero s->ctlr->ndrive"); 1389b1707c5dSDavid du Colombier pcisetbme(c->pci); 1390b1707c5dSDavid du Colombier snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name); 1391b1707c5dSDavid du Colombier intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name); 1392b1707c5dSDavid du Colombier /* supposed to squelch leftover interrupts here. */ 1393b1707c5dSDavid du Colombier ahcienable(c->hba); 1394b1707c5dSDavid du Colombier c->enabled = 1; 1395b1707c5dSDavid du Colombier } 1396b1707c5dSDavid du Colombier iunlock(c); 1397b1707c5dSDavid du Colombier return 1; 1398b1707c5dSDavid du Colombier } 1399b1707c5dSDavid du Colombier 1400b1707c5dSDavid du Colombier static int 1401b1707c5dSDavid du Colombier iadisable(SDev *s) 1402b1707c5dSDavid du Colombier { 1403b1707c5dSDavid du Colombier char name[32]; 1404b1707c5dSDavid du Colombier Ctlr *c; 1405b1707c5dSDavid du Colombier 1406b1707c5dSDavid du Colombier c = s->ctlr; 1407b1707c5dSDavid du Colombier ilock(c); 1408b1707c5dSDavid du Colombier ahcidisable(c->hba); 1409b1707c5dSDavid du Colombier snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name); 1410b1707c5dSDavid du Colombier intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name); 1411b1707c5dSDavid du Colombier c->enabled = 0; 1412b1707c5dSDavid du Colombier iunlock(c); 1413b1707c5dSDavid du Colombier return 1; 1414b1707c5dSDavid du Colombier } 1415b1707c5dSDavid du Colombier 1416b1707c5dSDavid du Colombier static int 1417b1707c5dSDavid du Colombier iaonline(SDunit *unit) 1418b1707c5dSDavid du Colombier { 1419b1707c5dSDavid du Colombier int r; 1420b1707c5dSDavid du Colombier Ctlr *c; 1421b1707c5dSDavid du Colombier Drive *d; 1422b1707c5dSDavid du Colombier 1423b1707c5dSDavid du Colombier c = unit->dev->ctlr; 1424b1707c5dSDavid du Colombier d = c->drive[unit->subno]; 1425b1707c5dSDavid du Colombier r = 0; 1426b1707c5dSDavid du Colombier 1427b1707c5dSDavid du Colombier if(d->portm.feat & Datapi && d->mediachange){ 1428b1707c5dSDavid du Colombier r = scsionline(unit); 1429b1707c5dSDavid du Colombier if(r > 0) 1430b1707c5dSDavid du Colombier d->mediachange = 0; 1431b1707c5dSDavid du Colombier return r; 1432b1707c5dSDavid du Colombier } 1433b1707c5dSDavid du Colombier 1434b1707c5dSDavid du Colombier ilock(d); 1435b1707c5dSDavid du Colombier if(d->mediachange){ 1436b1707c5dSDavid du Colombier r = 2; 1437b1707c5dSDavid du Colombier d->mediachange = 0; 1438b1707c5dSDavid du Colombier /* devsd resets this after online is called; why? */ 1439b1707c5dSDavid du Colombier unit->sectors = d->sectors; 1440*58db92f4SDavid du Colombier unit->secsize = 512; /* default size */ 1441b1707c5dSDavid du Colombier } else if(d->state == Dready) 1442b1707c5dSDavid du Colombier r = 1; 1443b1707c5dSDavid du Colombier iunlock(d); 1444b1707c5dSDavid du Colombier return r; 1445b1707c5dSDavid du Colombier } 1446b1707c5dSDavid du Colombier 1447b1707c5dSDavid du Colombier /* returns locked list! */ 1448b1707c5dSDavid du Colombier static Alist* 1449*58db92f4SDavid du Colombier ahcibuild(Drive *d, uchar *cmd, void *data, int n, vlong lba) 1450b1707c5dSDavid du Colombier { 1451b1707c5dSDavid du Colombier uchar *c, acmd, dir, llba; 1452b1707c5dSDavid du Colombier Alist *l; 1453b1707c5dSDavid du Colombier Actab *t; 1454*58db92f4SDavid du Colombier Aportm *m; 1455b1707c5dSDavid du Colombier Aprdt *p; 1456b1707c5dSDavid du Colombier static uchar tab[2][2] = { 0xc8, 0x25, 0xca, 0x35, }; 1457b1707c5dSDavid du Colombier 1458*58db92f4SDavid du Colombier m = &d->portm; 1459b1707c5dSDavid du Colombier dir = *cmd != 0x28; 1460b1707c5dSDavid du Colombier llba = m->feat&Dllba? 1: 0; 1461b1707c5dSDavid du Colombier acmd = tab[dir][llba]; 1462b1707c5dSDavid du Colombier qlock(m); 1463b1707c5dSDavid du Colombier l = m->list; 1464b1707c5dSDavid du Colombier t = m->ctab; 1465b1707c5dSDavid du Colombier c = t->cfis; 1466b1707c5dSDavid du Colombier 1467b1707c5dSDavid du Colombier c[0] = 0x27; 1468b1707c5dSDavid du Colombier c[1] = 0x80; 1469b1707c5dSDavid du Colombier c[2] = acmd; 1470b1707c5dSDavid du Colombier c[3] = 0; 1471b1707c5dSDavid du Colombier 1472b1707c5dSDavid du Colombier c[4] = lba; /* sector lba low 7:0 */ 1473b1707c5dSDavid du Colombier c[5] = lba >> 8; /* cylinder low lba mid 15:8 */ 1474b1707c5dSDavid du Colombier c[6] = lba >> 16; /* cylinder hi lba hi 23:16 */ 1475b1707c5dSDavid du Colombier c[7] = 0xa0 | 0x40; /* obsolete device bits + lba */ 1476b1707c5dSDavid du Colombier if(llba == 0) 1477b1707c5dSDavid du Colombier c[7] |= (lba>>24) & 7; 1478b1707c5dSDavid du Colombier 1479b1707c5dSDavid du Colombier c[8] = lba >> 24; /* sector (exp) lba 31:24 */ 1480b1707c5dSDavid du Colombier c[9] = lba >> 32; /* cylinder low (exp) lba 39:32 */ 1481b1707c5dSDavid du Colombier c[10] = lba >> 48; /* cylinder hi (exp) lba 48:40 */ 1482b1707c5dSDavid du Colombier c[11] = 0; /* features (exp); */ 1483b1707c5dSDavid du Colombier 1484b1707c5dSDavid du Colombier c[12] = n; /* sector count */ 1485b1707c5dSDavid du Colombier c[13] = n >> 8; /* sector count (exp) */ 1486b1707c5dSDavid du Colombier c[14] = 0; /* r */ 1487b1707c5dSDavid du Colombier c[15] = 0; /* control */ 1488b1707c5dSDavid du Colombier 1489b1707c5dSDavid du Colombier *(ulong*)(c + 16) = 0; 1490b1707c5dSDavid du Colombier 1491b1707c5dSDavid du Colombier l->flags = 1<<16 | Lpref | 0x5; /* Lpref ?? */ 1492b1707c5dSDavid du Colombier if(dir == Write) 1493b1707c5dSDavid du Colombier l->flags |= Lwrite; 1494b1707c5dSDavid du Colombier l->len = 0; 1495b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 1496b1707c5dSDavid du Colombier l->ctabhi = 0; 1497b1707c5dSDavid du Colombier 1498b1707c5dSDavid du Colombier p = &t->prdt; 1499b1707c5dSDavid du Colombier p->dba = PCIWADDR(data); 1500b1707c5dSDavid du Colombier p->dbahi = 0; 1501*58db92f4SDavid du Colombier if(d->unit == nil) 1502*58db92f4SDavid du Colombier panic("ahcibuild: nil d->unit"); 1503*58db92f4SDavid du Colombier p->count = 1<<31 | (d->unit->secsize*n - 2) | 1; 1504b1707c5dSDavid du Colombier 1505b1707c5dSDavid du Colombier return l; 1506b1707c5dSDavid du Colombier } 1507b1707c5dSDavid du Colombier 1508b1707c5dSDavid du Colombier static Alist* 1509b1707c5dSDavid du Colombier ahcibuildpkt(Aportm *m, SDreq *r, void *data, int n) 1510b1707c5dSDavid du Colombier { 1511b1707c5dSDavid du Colombier int fill, len; 1512b1707c5dSDavid du Colombier uchar *c; 1513b1707c5dSDavid du Colombier Alist *l; 1514b1707c5dSDavid du Colombier Actab *t; 1515b1707c5dSDavid du Colombier Aprdt *p; 1516b1707c5dSDavid du Colombier 1517b1707c5dSDavid du Colombier qlock(m); 1518b1707c5dSDavid du Colombier l = m->list; 1519b1707c5dSDavid du Colombier t = m->ctab; 1520b1707c5dSDavid du Colombier c = t->cfis; 1521b1707c5dSDavid du Colombier 1522b1707c5dSDavid du Colombier fill = m->feat&Datapi16? 16: 12; 1523b1707c5dSDavid du Colombier if((len = r->clen) > fill) 1524b1707c5dSDavid du Colombier len = fill; 1525b1707c5dSDavid du Colombier memmove(t->atapi, r->cmd, len); 1526b1707c5dSDavid du Colombier memset(t->atapi+len, 0, fill-len); 1527b1707c5dSDavid du Colombier 1528b1707c5dSDavid du Colombier c[0] = 0x27; 1529b1707c5dSDavid du Colombier c[1] = 0x80; 1530b1707c5dSDavid du Colombier c[2] = 0xa0; 1531b1707c5dSDavid du Colombier if(n != 0) 1532b1707c5dSDavid du Colombier c[3] = 1; /* dma */ 1533b1707c5dSDavid du Colombier else 1534b1707c5dSDavid du Colombier c[3] = 0; /* features (exp); */ 1535b1707c5dSDavid du Colombier 1536b1707c5dSDavid du Colombier c[4] = 0; /* sector lba low 7:0 */ 1537b1707c5dSDavid du Colombier c[5] = n; /* cylinder low lba mid 15:8 */ 1538b1707c5dSDavid du Colombier c[6] = n >> 8; /* cylinder hi lba hi 23:16 */ 1539b1707c5dSDavid du Colombier c[7] = 0xa0; /* obsolete device bits */ 1540b1707c5dSDavid du Colombier 1541b1707c5dSDavid du Colombier *(ulong*)(c + 8) = 0; 1542b1707c5dSDavid du Colombier *(ulong*)(c + 12) = 0; 1543b1707c5dSDavid du Colombier *(ulong*)(c + 16) = 0; 1544b1707c5dSDavid du Colombier 1545b1707c5dSDavid du Colombier l->flags = 1<<16 | Lpref | Latapi | 0x5; 1546b1707c5dSDavid du Colombier if(r->write != 0 && data) 1547b1707c5dSDavid du Colombier l->flags |= Lwrite; 1548b1707c5dSDavid du Colombier l->len = 0; 1549b1707c5dSDavid du Colombier l->ctab = PCIWADDR(t); 1550b1707c5dSDavid du Colombier l->ctabhi = 0; 1551b1707c5dSDavid du Colombier 1552b1707c5dSDavid du Colombier if(data == 0) 1553b1707c5dSDavid du Colombier return l; 1554b1707c5dSDavid du Colombier 1555b1707c5dSDavid du Colombier p = &t->prdt; 1556b1707c5dSDavid du Colombier p->dba = PCIWADDR(data); 1557b1707c5dSDavid du Colombier p->dbahi = 0; 1558b1707c5dSDavid du Colombier p->count = 1<<31 | (n - 2) | 1; 1559b1707c5dSDavid du Colombier 1560b1707c5dSDavid du Colombier return l; 1561b1707c5dSDavid du Colombier } 1562b1707c5dSDavid du Colombier 1563b1707c5dSDavid du Colombier static int 1564b1707c5dSDavid du Colombier waitready(Drive *d) 1565b1707c5dSDavid du Colombier { 156691b330d9SDavid du Colombier ulong s, i, δ; 1567b1707c5dSDavid du Colombier 156881ede731SDavid du Colombier for(i = 0; i < 15000; i += 250){ 156981ede731SDavid du Colombier if(d->state == Dreset || d->state == Dportreset || 157081ede731SDavid du Colombier d->state == Dnew) 157181ede731SDavid du Colombier return 1; 157281ede731SDavid du Colombier δ = MACHP(0)->ticks - d->lastseen; 157381ede731SDavid du Colombier if(d->state == Dnull || δ > 10*1000) 157481ede731SDavid du Colombier return -1; 1575b1707c5dSDavid du Colombier ilock(d); 1576b1707c5dSDavid du Colombier s = d->port->sstatus; 1577b1707c5dSDavid du Colombier iunlock(d); 157881ede731SDavid du Colombier if((s & 0x700) == 0 && δ > 1500) 157981ede731SDavid du Colombier return -1; /* no detect */ 1580b1707c5dSDavid du Colombier if(d->state == Dready && (s & 7) == 3) 1581b1707c5dSDavid du Colombier return 0; /* ready, present & phy. comm. */ 158281ede731SDavid du Colombier esleep(250); 1583b1707c5dSDavid du Colombier } 1584b1707c5dSDavid du Colombier print("%s: not responding; offline\n", d->unit->name); 1585b1707c5dSDavid du Colombier ilock(d); 1586b1707c5dSDavid du Colombier d->state = Doffline; 1587b1707c5dSDavid du Colombier iunlock(d); 1588b1707c5dSDavid du Colombier return -1; 1589b1707c5dSDavid du Colombier } 1590b1707c5dSDavid du Colombier 1591b1707c5dSDavid du Colombier static int 159281ede731SDavid du Colombier lockready(Drive *d) 159381ede731SDavid du Colombier { 159481ede731SDavid du Colombier int i; 159581ede731SDavid du Colombier 159681ede731SDavid du Colombier qlock(&d->portm); 159781ede731SDavid du Colombier while ((i = waitready(d)) == 1) { 159881ede731SDavid du Colombier qunlock(&d->portm); 159981ede731SDavid du Colombier esleep(1); 160081ede731SDavid du Colombier qlock(&d->portm); 160181ede731SDavid du Colombier } 160281ede731SDavid du Colombier return i; 160381ede731SDavid du Colombier } 160481ede731SDavid du Colombier 160581ede731SDavid du Colombier static int 160681ede731SDavid du Colombier flushcache(Drive *d) 160781ede731SDavid du Colombier { 160881ede731SDavid du Colombier int i; 160981ede731SDavid du Colombier 161081ede731SDavid du Colombier i = -1; 161181ede731SDavid du Colombier if(lockready(d) == 0) 161281ede731SDavid du Colombier i = ahciflushcache(&d->portc); 161381ede731SDavid du Colombier qunlock(&d->portm); 161481ede731SDavid du Colombier return i; 161581ede731SDavid du Colombier } 161681ede731SDavid du Colombier 161781ede731SDavid du Colombier static int 1618b1707c5dSDavid du Colombier iariopkt(SDreq *r, Drive *d) 1619b1707c5dSDavid du Colombier { 1620b1707c5dSDavid du Colombier int n, count, try, max, flag, task; 1621b1707c5dSDavid du Colombier char *name; 1622b1707c5dSDavid du Colombier uchar *cmd, *data; 1623b1707c5dSDavid du Colombier Aport *p; 1624b1707c5dSDavid du Colombier Asleep as; 1625b1707c5dSDavid du Colombier 1626b1707c5dSDavid du Colombier cmd = r->cmd; 1627b1707c5dSDavid du Colombier name = d->unit->name; 1628b1707c5dSDavid du Colombier p = d->port; 1629b1707c5dSDavid du Colombier 16303d56950aSDavid du Colombier aprint("ahci: iariopkt: %02ux %02ux %c %d %p\n", 16313d56950aSDavid du Colombier cmd[0], cmd[2], "rw"[r->write], r->dlen, r->data); 1632b1707c5dSDavid du Colombier if(cmd[0] == 0x5a && (cmd[2] & 0x3f) == 0x3f) 1633b1707c5dSDavid du Colombier return sdmodesense(r, cmd, d->info, sizeof d->info); 1634b1707c5dSDavid du Colombier r->rlen = 0; 1635b1707c5dSDavid du Colombier count = r->dlen; 1636b1707c5dSDavid du Colombier max = 65536; 1637b1707c5dSDavid du Colombier 1638b1707c5dSDavid du Colombier try = 0; 1639b1707c5dSDavid du Colombier retry: 1640b1707c5dSDavid du Colombier data = r->data; 1641b1707c5dSDavid du Colombier n = count; 1642b1707c5dSDavid du Colombier if(n > max) 1643b1707c5dSDavid du Colombier n = max; 1644b1707c5dSDavid du Colombier ahcibuildpkt(&d->portm, r, data, n); 164581ede731SDavid du Colombier switch(waitready(d)){ 164681ede731SDavid du Colombier case -1: 164781ede731SDavid du Colombier qunlock(&d->portm); 164881ede731SDavid du Colombier return SDeio; 164981ede731SDavid du Colombier case 1: 165081ede731SDavid du Colombier qunlock(&d->portm); 165181ede731SDavid du Colombier esleep(1); 165281ede731SDavid du Colombier goto retry; 165381ede731SDavid du Colombier } 165481ede731SDavid du Colombier 1655b1707c5dSDavid du Colombier ilock(d); 1656b1707c5dSDavid du Colombier d->portm.flag = 0; 1657b1707c5dSDavid du Colombier iunlock(d); 1658b1707c5dSDavid du Colombier p->ci = 1; 1659b1707c5dSDavid du Colombier 1660b1707c5dSDavid du Colombier as.p = p; 1661b1707c5dSDavid du Colombier as.i = 1; 1662b1707c5dSDavid du Colombier d->intick = MACHP(0)->ticks; 1663014ad43fSDavid du Colombier d->active++; 1664b1707c5dSDavid du Colombier 1665b1707c5dSDavid du Colombier while(waserror()) 1666b1707c5dSDavid du Colombier ; 1667b1707c5dSDavid du Colombier sleep(&d->portm, ahciclear, &as); 1668b1707c5dSDavid du Colombier poperror(); 1669b1707c5dSDavid du Colombier 1670014ad43fSDavid du Colombier d->active--; 1671b1707c5dSDavid du Colombier ilock(d); 1672b1707c5dSDavid du Colombier flag = d->portm.flag; 1673b1707c5dSDavid du Colombier task = d->port->task; 1674b1707c5dSDavid du Colombier iunlock(d); 1675b1707c5dSDavid du Colombier 1676b1707c5dSDavid du Colombier if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){ 1677014ad43fSDavid du Colombier d->port->ci = 0; 1678b1707c5dSDavid du Colombier ahcirecover(&d->portc); 1679b1707c5dSDavid du Colombier task = d->port->task; 168081ede731SDavid du Colombier flag &= ~Fdone; /* either an error or do-over */ 1681b1707c5dSDavid du Colombier } 1682b1707c5dSDavid du Colombier qunlock(&d->portm); 1683b1707c5dSDavid du Colombier if(flag == 0){ 1684b1707c5dSDavid du Colombier if(++try == 10){ 1685b1707c5dSDavid du Colombier print("%s: bad disk\n", name); 1686b1707c5dSDavid du Colombier r->status = SDcheck; 1687b1707c5dSDavid du Colombier return SDcheck; 1688b1707c5dSDavid du Colombier } 168981ede731SDavid du Colombier print("%s: retry\n", name); 1690b1707c5dSDavid du Colombier goto retry; 1691b1707c5dSDavid du Colombier } 1692b1707c5dSDavid du Colombier if(flag & Ferror){ 169381ede731SDavid du Colombier if((task&Eidnf) == 0) 169481ede731SDavid du Colombier print("%s: i/o error %ux\n", name, task); 1695b1707c5dSDavid du Colombier r->status = SDcheck; 1696b1707c5dSDavid du Colombier return SDcheck; 1697b1707c5dSDavid du Colombier } 1698b1707c5dSDavid du Colombier 1699b1707c5dSDavid du Colombier data += n; 1700b1707c5dSDavid du Colombier 1701b1707c5dSDavid du Colombier r->rlen = data - (uchar*)r->data; 1702b1707c5dSDavid du Colombier r->status = SDok; 1703b1707c5dSDavid du Colombier return SDok; 1704b1707c5dSDavid du Colombier } 1705b1707c5dSDavid du Colombier 1706b1707c5dSDavid du Colombier static int 1707b1707c5dSDavid du Colombier iario(SDreq *r) 1708b1707c5dSDavid du Colombier { 1709b1707c5dSDavid du Colombier int i, n, count, try, max, flag, task; 1710b1707c5dSDavid du Colombier vlong lba; 1711b1707c5dSDavid du Colombier char *name; 1712b1707c5dSDavid du Colombier uchar *cmd, *data; 1713b1707c5dSDavid du Colombier Aport *p; 1714b1707c5dSDavid du Colombier Asleep as; 1715b1707c5dSDavid du Colombier Ctlr *c; 1716b1707c5dSDavid du Colombier Drive *d; 1717b1707c5dSDavid du Colombier SDunit *unit; 1718b1707c5dSDavid du Colombier 1719b1707c5dSDavid du Colombier unit = r->unit; 1720b1707c5dSDavid du Colombier c = unit->dev->ctlr; 1721b1707c5dSDavid du Colombier d = c->drive[unit->subno]; 1722b1707c5dSDavid du Colombier if(d->portm.feat & Datapi) 1723b1707c5dSDavid du Colombier return iariopkt(r, d); 1724b1707c5dSDavid du Colombier cmd = r->cmd; 1725b1707c5dSDavid du Colombier name = d->unit->name; 1726b1707c5dSDavid du Colombier p = d->port; 1727b1707c5dSDavid du Colombier 1728b1707c5dSDavid du Colombier if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){ 172981ede731SDavid du Colombier if(flushcache(d) == 0) 1730b1707c5dSDavid du Colombier return sdsetsense(r, SDok, 0, 0, 0); 1731b1707c5dSDavid du Colombier return sdsetsense(r, SDcheck, 3, 0xc, 2); 1732b1707c5dSDavid du Colombier } 1733b1707c5dSDavid du Colombier 1734b1707c5dSDavid du Colombier if((i = sdfakescsi(r, d->info, sizeof d->info)) != SDnostatus){ 1735b1707c5dSDavid du Colombier r->status = i; 1736b1707c5dSDavid du Colombier return i; 1737b1707c5dSDavid du Colombier } 1738b1707c5dSDavid du Colombier 1739b1707c5dSDavid du Colombier if(*cmd != 0x28 && *cmd != 0x2a){ 1740b1707c5dSDavid du Colombier print("%s: bad cmd 0x%.2ux\n", name, cmd[0]); 1741b1707c5dSDavid du Colombier r->status = SDcheck; 1742b1707c5dSDavid du Colombier return SDcheck; 1743b1707c5dSDavid du Colombier } 1744b1707c5dSDavid du Colombier 1745b1707c5dSDavid du Colombier lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5]; 1746b1707c5dSDavid du Colombier count = cmd[7]<<8 | cmd[8]; 1747b1707c5dSDavid du Colombier if(r->data == nil) 1748b1707c5dSDavid du Colombier return SDok; 1749b1707c5dSDavid du Colombier if(r->dlen < count * unit->secsize) 1750b1707c5dSDavid du Colombier count = r->dlen / unit->secsize; 1751b1707c5dSDavid du Colombier max = 128; 1752b1707c5dSDavid du Colombier 1753b1707c5dSDavid du Colombier try = 0; 1754b1707c5dSDavid du Colombier retry: 1755b1707c5dSDavid du Colombier data = r->data; 1756b1707c5dSDavid du Colombier while(count > 0){ 1757b1707c5dSDavid du Colombier n = count; 1758b1707c5dSDavid du Colombier if(n > max) 1759b1707c5dSDavid du Colombier n = max; 1760*58db92f4SDavid du Colombier ahcibuild(d, cmd, data, n, lba); 176181ede731SDavid du Colombier switch(waitready(d)){ 176281ede731SDavid du Colombier case -1: 176381ede731SDavid du Colombier qunlock(&d->portm); 176481ede731SDavid du Colombier return SDeio; 176581ede731SDavid du Colombier case 1: 176681ede731SDavid du Colombier qunlock(&d->portm); 176781ede731SDavid du Colombier esleep(1); 176881ede731SDavid du Colombier goto retry; 176981ede731SDavid du Colombier } 1770b1707c5dSDavid du Colombier ilock(d); 1771b1707c5dSDavid du Colombier d->portm.flag = 0; 1772b1707c5dSDavid du Colombier iunlock(d); 1773b1707c5dSDavid du Colombier p->ci = 1; 1774b1707c5dSDavid du Colombier 1775b1707c5dSDavid du Colombier as.p = p; 1776b1707c5dSDavid du Colombier as.i = 1; 1777b1707c5dSDavid du Colombier d->intick = MACHP(0)->ticks; 1778014ad43fSDavid du Colombier d->active++; 1779b1707c5dSDavid du Colombier 1780b1707c5dSDavid du Colombier while(waserror()) 1781b1707c5dSDavid du Colombier ; 1782b1707c5dSDavid du Colombier sleep(&d->portm, ahciclear, &as); 1783b1707c5dSDavid du Colombier poperror(); 1784b1707c5dSDavid du Colombier 1785014ad43fSDavid du Colombier d->active--; 1786b1707c5dSDavid du Colombier ilock(d); 1787b1707c5dSDavid du Colombier flag = d->portm.flag; 1788b1707c5dSDavid du Colombier task = d->port->task; 1789b1707c5dSDavid du Colombier iunlock(d); 1790b1707c5dSDavid du Colombier 1791b1707c5dSDavid du Colombier if(task & (Efatal<<8) || 1792b1707c5dSDavid du Colombier task & (ASbsy|ASdrq) && d->state == Dready){ 1793014ad43fSDavid du Colombier d->port->ci = 0; 1794b1707c5dSDavid du Colombier ahcirecover(&d->portc); 1795b1707c5dSDavid du Colombier task = d->port->task; 1796b1707c5dSDavid du Colombier } 1797b1707c5dSDavid du Colombier qunlock(&d->portm); 1798b1707c5dSDavid du Colombier if(flag == 0){ 1799b1707c5dSDavid du Colombier if(++try == 10){ 1800b1707c5dSDavid du Colombier print("%s: bad disk\n", name); 1801b1707c5dSDavid du Colombier r->status = SDeio; 1802b1707c5dSDavid du Colombier return SDeio; 1803b1707c5dSDavid du Colombier } 1804b1707c5dSDavid du Colombier iprint("%s: retry %lld\n", name, lba); 1805b1707c5dSDavid du Colombier goto retry; 1806b1707c5dSDavid du Colombier } 1807b1707c5dSDavid du Colombier if(flag & Ferror){ 1808b1707c5dSDavid du Colombier iprint("%s: i/o error %ux @%,lld\n", name, task, lba); 1809b1707c5dSDavid du Colombier r->status = SDeio; 1810b1707c5dSDavid du Colombier return SDeio; 1811b1707c5dSDavid du Colombier } 1812b1707c5dSDavid du Colombier 1813b1707c5dSDavid du Colombier count -= n; 1814b1707c5dSDavid du Colombier lba += n; 1815b1707c5dSDavid du Colombier data += n * unit->secsize; 1816b1707c5dSDavid du Colombier } 1817b1707c5dSDavid du Colombier r->rlen = data - (uchar*)r->data; 1818b1707c5dSDavid du Colombier r->status = SDok; 1819b1707c5dSDavid du Colombier return SDok; 1820b1707c5dSDavid du Colombier } 1821b1707c5dSDavid du Colombier 1822b1707c5dSDavid du Colombier /* 1823b1707c5dSDavid du Colombier * configure drives 0-5 as ahci sata (c.f. errata) 1824b1707c5dSDavid du Colombier */ 1825b1707c5dSDavid du Colombier static int 1826b1707c5dSDavid du Colombier iaahcimode(Pcidev *p) 1827b1707c5dSDavid du Colombier { 18283d56950aSDavid du Colombier dprint("iaahcimode: %ux %ux %ux\n", pcicfgr8(p, 0x91), pcicfgr8(p, 92), 1829b1707c5dSDavid du Colombier pcicfgr8(p, 93)); 1830b1707c5dSDavid du Colombier pcicfgw16(p, 0x92, pcicfgr32(p, 0x92) | 0xf); /* ports 0-3 */ 1831b1707c5dSDavid du Colombier // pcicfgw8(p, 0x93, pcicfgr32(p, 9x93) | 3); /* ports 4-5 */ 1832b1707c5dSDavid du Colombier return 0; 1833b1707c5dSDavid du Colombier } 1834b1707c5dSDavid du Colombier 1835b1707c5dSDavid du Colombier static void 1836b1707c5dSDavid du Colombier iasetupahci(Ctlr *c) 1837b1707c5dSDavid du Colombier { 1838b1707c5dSDavid du Colombier /* disable cmd block decoding. */ 1839b1707c5dSDavid du Colombier pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~(1<<15)); 1840b1707c5dSDavid du Colombier pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~(1<<15)); 1841b1707c5dSDavid du Colombier 1842b1707c5dSDavid du Colombier c->lmmio[0x4/4] |= 1 << 31; /* enable ahci mode (ghc register) */ 1843b1707c5dSDavid du Colombier c->lmmio[0xc/4] = (1 << 6) - 1; /* 5 ports. (supposedly ro pi reg.) */ 1844b1707c5dSDavid du Colombier 1845b1707c5dSDavid du Colombier /* enable ahci mode; from ich9 datasheet */ 1846014ad43fSDavid du Colombier pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5); 1847014ad43fSDavid du Colombier } 1848014ad43fSDavid du Colombier 1849014ad43fSDavid du Colombier static int 1850014ad43fSDavid du Colombier didtype(Pcidev *p) 1851014ad43fSDavid du Colombier { 1852014ad43fSDavid du Colombier switch(p->vid){ 1853014ad43fSDavid du Colombier case 0x8086: 1854014ad43fSDavid du Colombier if((p->did & 0xfffc) == 0x2680) 1855014ad43fSDavid du Colombier return Tesb; 185691157df7SDavid du Colombier /* 185791157df7SDavid du Colombier * 0x27c4 is the intel 82801 in compatibility (not sata) mode. 185891157df7SDavid du Colombier */ 185991157df7SDavid du Colombier if ((p->did & 0xfffb) == 0x27c1 || /* 82801g[bh]m ich7 */ 186091157df7SDavid du Colombier p->did == 0x2821 || /* 82801h[roh] */ 186191157df7SDavid du Colombier (p->did & 0xfffe) == 0x2824 || /* 82801h[b] */ 186291157df7SDavid du Colombier (p->did & 0xfeff) == 0x2829 || /* ich8/9m */ 1863014ad43fSDavid du Colombier (p->did & 0xfffe) == 0x2922 || /* ich9 */ 186491157df7SDavid du Colombier p->did == 0x3a02 || /* 82801jd/do */ 186591157df7SDavid du Colombier (p->did & 0xfefe) == 0x3a22 || /* ich10, pch */ 186691157df7SDavid du Colombier (p->did & 0xfff7) == 0x3b28) /* pchm */ 1867014ad43fSDavid du Colombier return Tich; 1868014ad43fSDavid du Colombier break; 1869014ad43fSDavid du Colombier case 0x1002: 1870014ad43fSDavid du Colombier if(p->did == 0x4380) 1871014ad43fSDavid du Colombier return Tsb600; 1872014ad43fSDavid du Colombier break; 1873014ad43fSDavid du Colombier } 1874014ad43fSDavid du Colombier if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1) 1875014ad43fSDavid du Colombier return Tunk; 1876014ad43fSDavid du Colombier return -1; 1877b1707c5dSDavid du Colombier } 1878b1707c5dSDavid du Colombier 1879b1707c5dSDavid du Colombier static SDev* 1880b1707c5dSDavid du Colombier iapnp(void) 1881b1707c5dSDavid du Colombier { 1882b1707c5dSDavid du Colombier int i, n, nunit, type; 1883b1707c5dSDavid du Colombier ulong io; 1884b1707c5dSDavid du Colombier Ctlr *c; 1885b1707c5dSDavid du Colombier Drive *d; 1886b1707c5dSDavid du Colombier Pcidev *p; 1887b1707c5dSDavid du Colombier SDev *head, *tail, *s; 1888b1707c5dSDavid du Colombier static int done; 1889b1707c5dSDavid du Colombier 1890b1707c5dSDavid du Colombier if(done++) 1891b1707c5dSDavid du Colombier return nil; 1892b1707c5dSDavid du Colombier 1893014ad43fSDavid du Colombier memset(olds, 0xff, sizeof olds); 1894b1707c5dSDavid du Colombier p = nil; 1895b1707c5dSDavid du Colombier head = tail = nil; 1896b1707c5dSDavid du Colombier loop: 1897b1707c5dSDavid du Colombier while((p = pcimatch(p, 0, 0)) != nil){ 1898014ad43fSDavid du Colombier type = didtype(p); 1899014ad43fSDavid du Colombier if (type == -1 || p->mem[Abar].bar == 0) 1900b1707c5dSDavid du Colombier continue; 1901b1707c5dSDavid du Colombier if(niactlr == NCtlr){ 190291157df7SDavid du Colombier print("ahci: iapnp: %s: too many controllers\n", 19033d56950aSDavid du Colombier tname[type]); 1904b1707c5dSDavid du Colombier break; 1905b1707c5dSDavid du Colombier } 1906b1707c5dSDavid du Colombier c = iactlr + niactlr; 1907b1707c5dSDavid du Colombier s = sdevs + niactlr; 1908b1707c5dSDavid du Colombier memset(c, 0, sizeof *c); 1909b1707c5dSDavid du Colombier memset(s, 0, sizeof *s); 1910b1707c5dSDavid du Colombier io = p->mem[Abar].bar & ~0xf; 1911b1707c5dSDavid du Colombier c->mmio = vmap(io, p->mem[Abar].size); 1912b1707c5dSDavid du Colombier if(c->mmio == 0){ 19133d56950aSDavid du Colombier print("ahci: %s: address 0x%luX in use did=%x\n", 1914b1707c5dSDavid du Colombier Tname(c), io, p->did); 1915b1707c5dSDavid du Colombier continue; 1916b1707c5dSDavid du Colombier } 1917b1707c5dSDavid du Colombier c->lmmio = (ulong*)c->mmio; 1918b1707c5dSDavid du Colombier c->pci = p; 1919b1707c5dSDavid du Colombier c->type = type; 1920b1707c5dSDavid du Colombier 1921b1707c5dSDavid du Colombier s->ifc = &sdiahciifc; 1922b1707c5dSDavid du Colombier s->idno = 'E' + niactlr; 1923b1707c5dSDavid du Colombier s->ctlr = c; 1924b1707c5dSDavid du Colombier c->sdev = s; 1925b1707c5dSDavid du Colombier 1926014ad43fSDavid du Colombier if(Intel(c) && p->did != 0x2681) 1927b1707c5dSDavid du Colombier iasetupahci(c); 1928b1707c5dSDavid du Colombier nunit = ahciconf(c); 1929b1707c5dSDavid du Colombier // ahcihbareset((Ahba*)c->mmio); 1930014ad43fSDavid du Colombier if(Intel(c) && iaahcimode(p) == -1) 1931b1707c5dSDavid du Colombier break; 1932b1707c5dSDavid du Colombier if(nunit < 1){ 1933b1707c5dSDavid du Colombier vunmap(c->mmio, p->mem[Abar].size); 1934b1707c5dSDavid du Colombier continue; 1935b1707c5dSDavid du Colombier } 1936b1707c5dSDavid du Colombier c->ndrive = s->nunit = nunit; 1937014ad43fSDavid du Colombier c->mport = c->hba->cap & ((1<<5)-1); 1938b1707c5dSDavid du Colombier 1939b1707c5dSDavid du Colombier i = (c->hba->cap >> 21) & 1; 1940b1707c5dSDavid du Colombier print("#S/sd%c: %s: sata-%s with %d ports\n", s->idno, 1941b1707c5dSDavid du Colombier Tname(c), "I\0II" + i*2, nunit); 1942b1707c5dSDavid du Colombier 1943b1707c5dSDavid du Colombier /* map the drives -- they don't all need to be enabled. */ 1944b1707c5dSDavid du Colombier memset(c->rawdrive, 0, sizeof c->rawdrive); 1945b1707c5dSDavid du Colombier n = 0; 1946b1707c5dSDavid du Colombier for(i = 0; i < NCtlrdrv; i++) { 1947b1707c5dSDavid du Colombier d = c->rawdrive + i; 1948b1707c5dSDavid du Colombier d->portno = i; 1949b1707c5dSDavid du Colombier d->driveno = -1; 1950b1707c5dSDavid du Colombier d->sectors = 0; 195197902b0cSDavid du Colombier d->serial[0] = ' '; 1952b1707c5dSDavid du Colombier d->ctlr = c; 1953b1707c5dSDavid du Colombier if((c->hba->pi & (1<<i)) == 0) 1954b1707c5dSDavid du Colombier continue; 1955b1707c5dSDavid du Colombier d->port = (Aport*)(c->mmio + 0x80*i + 0x100); 1956b1707c5dSDavid du Colombier d->portc.p = d->port; 1957b1707c5dSDavid du Colombier d->portc.m = &d->portm; 1958b1707c5dSDavid du Colombier d->driveno = n++; 195981ede731SDavid du Colombier c->drive[d->driveno] = d; 196081ede731SDavid du Colombier iadrive[niadrive + d->driveno] = d; 1961b1707c5dSDavid du Colombier } 1962b1707c5dSDavid du Colombier for(i = 0; i < n; i++) 1963b1707c5dSDavid du Colombier if(ahciidle(c->drive[i]->port) == -1){ 19643d56950aSDavid du Colombier dprint("ahci: %s: port %d wedged; abort\n", 1965b1707c5dSDavid du Colombier Tname(c), i); 1966b1707c5dSDavid du Colombier goto loop; 1967b1707c5dSDavid du Colombier } 1968b1707c5dSDavid du Colombier for(i = 0; i < n; i++){ 1969b1707c5dSDavid du Colombier c->drive[i]->mode = DMsatai; 1970b1707c5dSDavid du Colombier configdrive(c->drive[i]); 1971b1707c5dSDavid du Colombier } 1972b1707c5dSDavid du Colombier 1973014ad43fSDavid du Colombier niadrive += n; 1974b1707c5dSDavid du Colombier niactlr++; 1975b1707c5dSDavid du Colombier if(head) 1976b1707c5dSDavid du Colombier tail->next = s; 1977b1707c5dSDavid du Colombier else 1978b1707c5dSDavid du Colombier head = s; 1979b1707c5dSDavid du Colombier tail = s; 1980b1707c5dSDavid du Colombier } 1981b1707c5dSDavid du Colombier return head; 1982b1707c5dSDavid du Colombier } 1983b1707c5dSDavid du Colombier 1984b1707c5dSDavid du Colombier static char* smarttab[] = { 1985b1707c5dSDavid du Colombier "unset", 1986b1707c5dSDavid du Colombier "error", 1987b1707c5dSDavid du Colombier "threshold exceeded", 1988b1707c5dSDavid du Colombier "normal" 1989b1707c5dSDavid du Colombier }; 1990b1707c5dSDavid du Colombier 1991b1707c5dSDavid du Colombier static char * 1992b1707c5dSDavid du Colombier pflag(char *s, char *e, uchar f) 1993b1707c5dSDavid du Colombier { 1994b1707c5dSDavid du Colombier uchar i; 1995b1707c5dSDavid du Colombier 1996b1707c5dSDavid du Colombier for(i = 0; i < 8; i++) 1997b1707c5dSDavid du Colombier if(f & (1 << i)) 1998b1707c5dSDavid du Colombier s = seprint(s, e, "%s ", flagname[i]); 1999b1707c5dSDavid du Colombier return seprint(s, e, "\n"); 2000b1707c5dSDavid du Colombier } 2001b1707c5dSDavid du Colombier 2002b1707c5dSDavid du Colombier static int 2003b1707c5dSDavid du Colombier iarctl(SDunit *u, char *p, int l) 2004b1707c5dSDavid du Colombier { 2005b1707c5dSDavid du Colombier char buf[32]; 2006b1707c5dSDavid du Colombier char *e, *op; 2007b1707c5dSDavid du Colombier Aport *o; 2008b1707c5dSDavid du Colombier Ctlr *c; 2009b1707c5dSDavid du Colombier Drive *d; 2010b1707c5dSDavid du Colombier 2011*58db92f4SDavid du Colombier c = u->dev->ctlr; 2012*58db92f4SDavid du Colombier if(c == nil) { 2013*58db92f4SDavid du Colombier print("iarctl: nil u->dev->ctlr\n"); 2014b1707c5dSDavid du Colombier return 0; 2015*58db92f4SDavid du Colombier } 2016b1707c5dSDavid du Colombier d = c->drive[u->subno]; 2017b1707c5dSDavid du Colombier o = d->port; 2018b1707c5dSDavid du Colombier 2019b1707c5dSDavid du Colombier e = p+l; 2020b1707c5dSDavid du Colombier op = p; 2021b1707c5dSDavid du Colombier if(d->state == Dready){ 2022b1707c5dSDavid du Colombier p = seprint(p, e, "model\t%s\n", d->model); 2023b1707c5dSDavid du Colombier p = seprint(p, e, "serial\t%s\n", d->serial); 2024b1707c5dSDavid du Colombier p = seprint(p, e, "firm\t%s\n", d->firmware); 2025b1707c5dSDavid du Colombier if(d->smartrs == 0xff) 2026b1707c5dSDavid du Colombier p = seprint(p, e, "smart\tenable error\n"); 2027b1707c5dSDavid du Colombier else if(d->smartrs == 0) 2028b1707c5dSDavid du Colombier p = seprint(p, e, "smart\tdisabled\n"); 2029b1707c5dSDavid du Colombier else 2030b1707c5dSDavid du Colombier p = seprint(p, e, "smart\t%s\n", 2031b1707c5dSDavid du Colombier smarttab[d->portm.smart]); 2032b1707c5dSDavid du Colombier p = seprint(p, e, "flag\t"); 2033b1707c5dSDavid du Colombier p = pflag(p, e, d->portm.feat); 2034b1707c5dSDavid du Colombier }else 2035b1707c5dSDavid du Colombier p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]); 2036b1707c5dSDavid du Colombier serrstr(o->serror, buf, buf + sizeof buf - 1); 203791b330d9SDavid du Colombier p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux; " 203891b330d9SDavid du Colombier "sig %lux sstatus %04lux\n", o->task, o->cmd, o->serror, buf, 2039b1707c5dSDavid du Colombier o->ci, o->isr, o->sig, o->sstatus); 2040*58db92f4SDavid du Colombier if(d->unit == nil) 2041*58db92f4SDavid du Colombier panic("iarctl: nil d->unit"); 2042*58db92f4SDavid du Colombier p = seprint(p, e, "geometry %llud %lud\n", d->sectors, d->unit->secsize); 2043b1707c5dSDavid du Colombier return p - op; 2044b1707c5dSDavid du Colombier } 2045b1707c5dSDavid du Colombier 2046b1707c5dSDavid du Colombier static void 2047b1707c5dSDavid du Colombier runflushcache(Drive *d) 2048b1707c5dSDavid du Colombier { 2049b1707c5dSDavid du Colombier long t0; 2050b1707c5dSDavid du Colombier 2051b1707c5dSDavid du Colombier t0 = MACHP(0)->ticks; 205281ede731SDavid du Colombier if(flushcache(d) != 0) 205381ede731SDavid du Colombier error(Eio); 20543d56950aSDavid du Colombier dprint("ahci: flush in %ld ms\n", MACHP(0)->ticks - t0); 2055b1707c5dSDavid du Colombier } 2056b1707c5dSDavid du Colombier 2057b1707c5dSDavid du Colombier static void 2058b1707c5dSDavid du Colombier forcemode(Drive *d, char *mode) 2059b1707c5dSDavid du Colombier { 2060b1707c5dSDavid du Colombier int i; 2061b1707c5dSDavid du Colombier 2062b1707c5dSDavid du Colombier for(i = 0; i < nelem(modename); i++) 2063b1707c5dSDavid du Colombier if(strcmp(mode, modename[i]) == 0) 2064b1707c5dSDavid du Colombier break; 2065b1707c5dSDavid du Colombier if(i == nelem(modename)) 2066b1707c5dSDavid du Colombier i = 0; 2067b1707c5dSDavid du Colombier ilock(d); 2068b1707c5dSDavid du Colombier d->mode = i; 2069b1707c5dSDavid du Colombier iunlock(d); 2070b1707c5dSDavid du Colombier } 2071b1707c5dSDavid du Colombier 2072b1707c5dSDavid du Colombier static void 2073b1707c5dSDavid du Colombier runsmartable(Drive *d, int i) 2074b1707c5dSDavid du Colombier { 2075b1707c5dSDavid du Colombier if(waserror()){ 2076b1707c5dSDavid du Colombier qunlock(&d->portm); 2077b1707c5dSDavid du Colombier d->smartrs = 0; 2078b1707c5dSDavid du Colombier nexterror(); 2079b1707c5dSDavid du Colombier } 208081ede731SDavid du Colombier if(lockready(d) == -1) 208181ede731SDavid du Colombier error(Eio); 2082b1707c5dSDavid du Colombier d->smartrs = smart(&d->portc, i); 2083b1707c5dSDavid du Colombier d->portm.smart = 0; 2084b1707c5dSDavid du Colombier qunlock(&d->portm); 2085b1707c5dSDavid du Colombier poperror(); 2086b1707c5dSDavid du Colombier } 2087b1707c5dSDavid du Colombier 2088b1707c5dSDavid du Colombier static void 2089b1707c5dSDavid du Colombier forcestate(Drive *d, char *state) 2090b1707c5dSDavid du Colombier { 2091b1707c5dSDavid du Colombier int i; 2092b1707c5dSDavid du Colombier 2093b1707c5dSDavid du Colombier for(i = 0; i < nelem(diskstates); i++) 2094b1707c5dSDavid du Colombier if(strcmp(state, diskstates[i]) == 0) 2095b1707c5dSDavid du Colombier break; 2096b1707c5dSDavid du Colombier if(i == nelem(diskstates)) 2097014ad43fSDavid du Colombier error(Ebadctl); 2098b1707c5dSDavid du Colombier ilock(d); 2099b1707c5dSDavid du Colombier d->state = i; 2100b1707c5dSDavid du Colombier iunlock(d); 2101b1707c5dSDavid du Colombier } 2102b1707c5dSDavid du Colombier 2103b1707c5dSDavid du Colombier 2104b1707c5dSDavid du Colombier static int 2105b1707c5dSDavid du Colombier iawctl(SDunit *u, Cmdbuf *cmd) 2106b1707c5dSDavid du Colombier { 2107b1707c5dSDavid du Colombier char **f; 2108b1707c5dSDavid du Colombier Ctlr *c; 2109b1707c5dSDavid du Colombier Drive *d; 211081ede731SDavid du Colombier uint i; 2111b1707c5dSDavid du Colombier 2112b1707c5dSDavid du Colombier c = u->dev->ctlr; 2113b1707c5dSDavid du Colombier d = c->drive[u->subno]; 2114b1707c5dSDavid du Colombier f = cmd->f; 2115b1707c5dSDavid du Colombier 2116b1707c5dSDavid du Colombier if(strcmp(f[0], "flushcache") == 0) 2117b1707c5dSDavid du Colombier runflushcache(d); 2118b1707c5dSDavid du Colombier else if(strcmp(f[0], "identify") == 0){ 2119b1707c5dSDavid du Colombier i = strtoul(f[1]? f[1]: "0", 0, 0); 2120b1707c5dSDavid du Colombier if(i > 0xff) 2121b1707c5dSDavid du Colombier i = 0; 21223d56950aSDavid du Colombier dprint("ahci: %04d %ux\n", i, d->info[i]); 2123b1707c5dSDavid du Colombier }else if(strcmp(f[0], "mode") == 0) 2124b1707c5dSDavid du Colombier forcemode(d, f[1]? f[1]: "satai"); 2125b1707c5dSDavid du Colombier else if(strcmp(f[0], "nop") == 0){ 2126b1707c5dSDavid du Colombier if((d->portm.feat & Dnop) == 0){ 212781ede731SDavid du Colombier cmderror(cmd, "no drive support"); 2128b1707c5dSDavid du Colombier return -1; 2129b1707c5dSDavid du Colombier } 2130b1707c5dSDavid du Colombier if(waserror()){ 2131b1707c5dSDavid du Colombier qunlock(&d->portm); 2132b1707c5dSDavid du Colombier nexterror(); 2133b1707c5dSDavid du Colombier } 213481ede731SDavid du Colombier if(lockready(d) == -1) 213581ede731SDavid du Colombier error(Eio); 2136b1707c5dSDavid du Colombier nop(&d->portc); 2137b1707c5dSDavid du Colombier qunlock(&d->portm); 2138b1707c5dSDavid du Colombier poperror(); 2139b1707c5dSDavid du Colombier }else if(strcmp(f[0], "reset") == 0) 2140b1707c5dSDavid du Colombier forcestate(d, "reset"); 2141b1707c5dSDavid du Colombier else if(strcmp(f[0], "smart") == 0){ 2142b1707c5dSDavid du Colombier if(d->smartrs == 0){ 2143b1707c5dSDavid du Colombier cmderror(cmd, "smart not enabled"); 2144b1707c5dSDavid du Colombier return -1; 2145b1707c5dSDavid du Colombier } 2146b1707c5dSDavid du Colombier if(waserror()){ 2147b1707c5dSDavid du Colombier qunlock(&d->portm); 2148b1707c5dSDavid du Colombier d->smartrs = 0; 2149b1707c5dSDavid du Colombier nexterror(); 2150b1707c5dSDavid du Colombier } 215181ede731SDavid du Colombier if(lockready(d) == -1) 215281ede731SDavid du Colombier error(Eio); 2153b1707c5dSDavid du Colombier d->portm.smart = 2 + smartrs(&d->portc); 2154b1707c5dSDavid du Colombier qunlock(&d->portm); 2155b1707c5dSDavid du Colombier poperror(); 2156b1707c5dSDavid du Colombier }else if(strcmp(f[0], "smartdisable") == 0) 2157b1707c5dSDavid du Colombier runsmartable(d, 1); 2158b1707c5dSDavid du Colombier else if(strcmp(f[0], "smartenable") == 0) 2159b1707c5dSDavid du Colombier runsmartable(d, 0); 2160b1707c5dSDavid du Colombier else if(strcmp(f[0], "state") == 0) 2161b1707c5dSDavid du Colombier forcestate(d, f[1]? f[1]: "null"); 2162b1707c5dSDavid du Colombier else{ 2163b1707c5dSDavid du Colombier cmderror(cmd, Ebadctl); 2164b1707c5dSDavid du Colombier return -1; 2165b1707c5dSDavid du Colombier } 2166b1707c5dSDavid du Colombier return 0; 2167b1707c5dSDavid du Colombier } 2168b1707c5dSDavid du Colombier 2169b1707c5dSDavid du Colombier static char * 2170b1707c5dSDavid du Colombier portr(char *p, char *e, uint x) 2171b1707c5dSDavid du Colombier { 2172b1707c5dSDavid du Colombier int i, a; 2173b1707c5dSDavid du Colombier 2174b1707c5dSDavid du Colombier p[0] = 0; 2175b1707c5dSDavid du Colombier a = -1; 2176b1707c5dSDavid du Colombier for(i = 0; i < 32; i++){ 2177b1707c5dSDavid du Colombier if((x & (1<<i)) == 0){ 2178b1707c5dSDavid du Colombier if(a != -1 && i - 1 != a) 2179b1707c5dSDavid du Colombier p = seprint(p, e, "-%d", i - 1); 2180b1707c5dSDavid du Colombier a = -1; 2181b1707c5dSDavid du Colombier continue; 2182b1707c5dSDavid du Colombier } 2183b1707c5dSDavid du Colombier if(a == -1){ 2184b1707c5dSDavid du Colombier if(i > 0) 2185b1707c5dSDavid du Colombier p = seprint(p, e, ", "); 2186b1707c5dSDavid du Colombier p = seprint(p, e, "%d", a = i); 2187b1707c5dSDavid du Colombier } 2188b1707c5dSDavid du Colombier } 2189b1707c5dSDavid du Colombier if(a != -1 && i - 1 != a) 2190b1707c5dSDavid du Colombier p = seprint(p, e, "-%d", i - 1); 2191b1707c5dSDavid du Colombier return p; 2192b1707c5dSDavid du Colombier } 2193b1707c5dSDavid du Colombier 2194b1707c5dSDavid du Colombier /* must emit exactly one line per controller (sd(3)) */ 2195b1707c5dSDavid du Colombier static char* 2196b1707c5dSDavid du Colombier iartopctl(SDev *sdev, char *p, char *e) 2197b1707c5dSDavid du Colombier { 219891b330d9SDavid du Colombier ulong cap; 2199b1707c5dSDavid du Colombier char pr[25]; 2200b1707c5dSDavid du Colombier Ahba *hba; 2201b1707c5dSDavid du Colombier Ctlr *ctlr; 2202b1707c5dSDavid du Colombier 2203b1707c5dSDavid du Colombier #define has(x, str) if(cap & (x)) p = seprint(p, e, "%s ", (str)) 2204b1707c5dSDavid du Colombier 2205b1707c5dSDavid du Colombier ctlr = sdev->ctlr; 2206b1707c5dSDavid du Colombier hba = ctlr->hba; 2207b1707c5dSDavid du Colombier p = seprint(p, e, "sd%c ahci port %#p: ", sdev->idno, hba); 2208b1707c5dSDavid du Colombier cap = hba->cap; 2209b1707c5dSDavid du Colombier has(Hs64a, "64a"); 2210b1707c5dSDavid du Colombier has(Hsalp, "alp"); 2211b1707c5dSDavid du Colombier has(Hsam, "am"); 2212b1707c5dSDavid du Colombier has(Hsclo, "clo"); 2213b1707c5dSDavid du Colombier has(Hcccs, "coal"); 2214b1707c5dSDavid du Colombier has(Hems, "ems"); 2215b1707c5dSDavid du Colombier has(Hsal, "led"); 2216b1707c5dSDavid du Colombier has(Hsmps, "mps"); 2217b1707c5dSDavid du Colombier has(Hsncq, "ncq"); 2218b1707c5dSDavid du Colombier has(Hssntf, "ntf"); 2219b1707c5dSDavid du Colombier has(Hspm, "pm"); 2220b1707c5dSDavid du Colombier has(Hpsc, "pslum"); 2221b1707c5dSDavid du Colombier has(Hssc, "slum"); 2222b1707c5dSDavid du Colombier has(Hsss, "ss"); 2223b1707c5dSDavid du Colombier has(Hsxs, "sxs"); 2224b1707c5dSDavid du Colombier portr(pr, pr + sizeof pr, hba->pi); 2225b1707c5dSDavid du Colombier return seprint(p, e, 222691b330d9SDavid du Colombier "iss %ld ncs %ld np %ld; ghc %lux isr %lux pi %lux %s ver %lux\n", 2227b1707c5dSDavid du Colombier (cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f), 2228b1707c5dSDavid du Colombier hba->ghc, hba->isr, hba->pi, pr, hba->ver); 2229b1707c5dSDavid du Colombier #undef has 2230b1707c5dSDavid du Colombier } 2231b1707c5dSDavid du Colombier 2232b1707c5dSDavid du Colombier static int 2233b1707c5dSDavid du Colombier iawtopctl(SDev *, Cmdbuf *cmd) 2234b1707c5dSDavid du Colombier { 2235b1707c5dSDavid du Colombier int *v; 2236b1707c5dSDavid du Colombier char **f; 2237b1707c5dSDavid du Colombier 2238b1707c5dSDavid du Colombier f = cmd->f; 2239b1707c5dSDavid du Colombier v = 0; 2240b1707c5dSDavid du Colombier 22419e8a50a9SDavid du Colombier if (f[0] == nil) 22429e8a50a9SDavid du Colombier return 0; 2243b1707c5dSDavid du Colombier if(strcmp(f[0], "debug") == 0) 2244b1707c5dSDavid du Colombier v = &debug; 2245b1707c5dSDavid du Colombier else if(strcmp(f[0], "idprint") == 0) 2246b1707c5dSDavid du Colombier v = &prid; 2247b1707c5dSDavid du Colombier else if(strcmp(f[0], "aprint") == 0) 2248b1707c5dSDavid du Colombier v = &datapi; 2249b1707c5dSDavid du Colombier else 2250b1707c5dSDavid du Colombier cmderror(cmd, Ebadctl); 2251b1707c5dSDavid du Colombier 2252b1707c5dSDavid du Colombier switch(cmd->nf){ 2253b1707c5dSDavid du Colombier default: 2254b1707c5dSDavid du Colombier cmderror(cmd, Ebadarg); 2255b1707c5dSDavid du Colombier case 1: 2256b1707c5dSDavid du Colombier *v ^= 1; 2257b1707c5dSDavid du Colombier break; 2258b1707c5dSDavid du Colombier case 2: 22599e8a50a9SDavid du Colombier if(f[1]) 22609e8a50a9SDavid du Colombier *v = strcmp(f[1], "on") == 0; 22619e8a50a9SDavid du Colombier else 22629e8a50a9SDavid du Colombier *v ^= 1; 2263b1707c5dSDavid du Colombier break; 2264b1707c5dSDavid du Colombier } 2265b1707c5dSDavid du Colombier return 0; 2266b1707c5dSDavid du Colombier } 2267b1707c5dSDavid du Colombier 2268b1707c5dSDavid du Colombier SDifc sdiahciifc = { 2269b1707c5dSDavid du Colombier "iahci", 2270b1707c5dSDavid du Colombier 2271b1707c5dSDavid du Colombier iapnp, 2272b1707c5dSDavid du Colombier nil, /* legacy */ 2273b1707c5dSDavid du Colombier iaenable, 2274b1707c5dSDavid du Colombier iadisable, 2275b1707c5dSDavid du Colombier 2276b1707c5dSDavid du Colombier iaverify, 2277b1707c5dSDavid du Colombier iaonline, 2278b1707c5dSDavid du Colombier iario, 2279b1707c5dSDavid du Colombier iarctl, 2280b1707c5dSDavid du Colombier iawctl, 2281b1707c5dSDavid du Colombier 2282b1707c5dSDavid du Colombier scsibio, 2283b1707c5dSDavid du Colombier nil, /* probe */ 2284b1707c5dSDavid du Colombier nil, /* clear */ 2285b1707c5dSDavid du Colombier iartopctl, 2286b1707c5dSDavid du Colombier iawtopctl, 2287b1707c5dSDavid du Colombier }; 2288