]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/sdiahci.c
sdahci: get dvd drive to work with x301
[plan9front.git] / sys / src / 9 / pc / sdiahci.c
1 /*
2  * intel/amd ahci sata controller
3  * copyright © 2007-10 coraid, inc.
4  */
5
6 #include "u.h"
7 #include "../port/lib.h"
8 #include "mem.h"
9 #include "dat.h"
10 #include "fns.h"
11 #include "io.h"
12 #include "../port/error.h"
13 #include "../port/sd.h"
14 #include <fis.h>
15 #include "ahci.h"
16 #include "../port/led.h"
17
18 #pragma varargck        type    "T"     int
19 #define dprint(...)     if(debug)       print(__VA_ARGS__); else USED(debug)
20 #define idprint(...)    if(prid)        print(__VA_ARGS__); else USED(prid)
21 #define aprint(...)     if(datapi)      print(__VA_ARGS__); else USED(datapi)
22 #define ledprint(...)   if(dled)        print(__VA_ARGS__); else USED(dled)
23 #define Pciwaddrh(a)    0
24 #define Tname(c)        tname[(c)->type]
25 #define Ticks           MACHP(0)->ticks
26 #define MS2TK(t)        (((ulong)(t)*HZ)/1000)
27
28 enum {
29         NCtlr   = 4,
30         NCtlrdrv= 32,
31         NDrive  = NCtlr*NCtlrdrv,
32
33         Fahdrs  = 4,
34
35         Read    = 0,
36         Write,
37
38         Eesb    = 1<<0, /* must have (Eesb & Emtype) == 0 */
39 };
40
41 /* pci space configuration */
42 enum {
43         Pmap    = 0x90,
44         Ppcs    = 0x91,
45         Prev    = 0xa8,
46 };
47
48 enum {
49         Tesb,
50         Tich,
51         Tsb600,
52         Tjmicron,
53         Tahci,
54 };
55
56 static char *tname[] = {
57         "63xxesb",
58         "ich",
59         "sb600",
60         "jmicron",
61         "ahci",
62 };
63
64 enum {
65         Dnull,
66         Dmissing,
67         Dnew,
68         Dready,
69         Derror,
70         Dreset,
71         Doffline,
72         Dportreset,
73         Dlast,
74 };
75
76 static char *diskstates[Dlast] = {
77         "null",
78         "missing",
79         "new",
80         "ready",
81         "error",
82         "reset",
83         "offline",
84         "portreset",
85 };
86
87 extern SDifc sdiahciifc;
88 typedef struct Ctlr Ctlr;
89
90 enum {
91         DMautoneg,
92         DMsatai,
93         DMsataii,
94         DMsataiii,
95         DMlast,
96 };
97
98 static char *modes[DMlast] = {
99         "auto",
100         "satai",
101         "sataii",
102         "sataiii",
103 };
104
105 typedef struct Htab Htab;
106 struct Htab {
107         ulong   bit;
108         char    *name;
109 };
110
111 typedef struct {
112         Lock;
113
114         Ctlr    *ctlr;
115         SDunit  *unit;
116         char    name[10];
117         Aport   *port;
118         Aportm  portm;
119         Aportc  portc;  /* redundant ptr to port and portm. */
120         Ledport;
121
122         uchar   drivechange;
123         uchar   nodma;
124         uchar   state;
125
126         uvlong  sectors;
127         uint    secsize;
128         ulong   totick;
129         ulong   lastseen;
130         uint    wait;
131         uchar   mode;
132         uchar   active;
133
134         char    serial[20+1];
135         char    firmware[8+1];
136         char    model[40+1];
137         uvlong  wwn;
138
139         ushort  info[0x200];
140
141         /*
142          * ahci allows non-sequential ports.
143          * to avoid this hassle, we let
144          * driveno      ctlr*NCtlrdrv + unit
145          * portno       nth available port
146          */
147         uint    driveno;
148         uint    portno;
149 } Drive;
150
151 struct Ctlr {
152         Lock;
153
154         int     type;
155         int     enabled;
156         SDev    *sdev;
157         Pcidev  *pci;
158
159         uchar   *mmio;
160         ulong   *lmmio;
161         Ahba    *hba;
162         Aenc;
163         uint    enctype;
164
165         Drive   rawdrive[NCtlrdrv];
166         Drive*  drive[NCtlrdrv];
167         int     ndrive;
168
169         uint    missirq;
170 };
171
172 static  Ctlr    iactlr[NCtlr];
173 static  SDev    sdevs[NCtlr];
174 static  int     niactlr;
175
176 static  Drive   *iadrive[NDrive];
177 static  int     niadrive;
178
179 static  int     debug;
180 static  int     prid = 1;
181 static  int     datapi;
182 static  int     dled;
183
184 static char stab[] = {
185 [0]     'i', 'm',
186 [8]     't', 'c', 'p', 'e',
187 [16]    'N', 'I', 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X'
188 };
189
190 static void
191 serrstr(ulong r, char *s, char *e)
192 {
193         int i;
194
195         e -= 3;
196         for(i = 0; i < nelem(stab) && s < e; i++)
197                 if(r & (1<<i) && stab[i]){
198                         *s++ = stab[i];
199                         if(SerrBad & (1<<i))
200                                 *s++ = '*';
201                 }
202         *s = 0;
203 }
204
205 static char ntab[] = "0123456789abcdef";
206
207 static void
208 preg(uchar *reg, int n)
209 {
210         char buf[25*3+1], *e;
211         int i;
212
213         e = buf;
214         for(i = 0; i < n; i++){
215                 *e++ = ntab[reg[i] >> 4];
216                 *e++ = ntab[reg[i] & 0xf];
217                 *e++ = ' ';
218         }
219         *e++ = '\n';
220         *e = 0;
221         dprint(buf);
222 }
223
224 static void
225 dreg(char *s, Aport *p)
226 {
227         dprint("%stask=%lux; cmd=%lux; ci=%lux; is=%lux\n",
228                 s, p->task, p->cmd, p->ci, p->isr);
229 }
230
231 static void
232 esleep(int ms)
233 {
234         if(waserror())
235                 return;
236         tsleep(&up->sleep, return0, 0, ms);
237         poperror();
238 }
239
240 typedef struct {
241         Aport   *p;
242         int     i;
243 } Asleep;
244
245 static int
246 ahciclear(void *v)
247 {
248         Asleep *s;
249
250         s = v;
251         return (s->p->ci & s->i) == 0;
252 }
253
254 static void
255 aesleep(Aportm *m, Asleep *a, int ms)
256 {
257         if(waserror())
258                 return;
259         tsleep(m, ahciclear, a, ms);
260         poperror();
261 }
262
263 static int
264 ahciwait(Aportc *c, int ms)
265 {
266         Aport *p;
267         Asleep as;
268
269         p = c->p;
270         p->ci = 1;
271         as.p = p;
272         as.i = 1;
273         aesleep(c->m, &as, ms);
274         if((p->task & 1) == 0 && p->ci == 0)
275                 return 0;
276         dreg("ahciwait fail/timeout ", c->p);
277         return -1;
278 }
279
280 static Alist*
281 mkalist(Aportm *m, uint flags, uchar *data, int len)
282 {
283         Actab *t;
284         Alist *l;
285         Aprdt *p;
286
287         t = m->ctab;
288         if(data && len > 0){
289                 p = &t->prdt;
290                 p->dba = PCIWADDR(data);
291                 p->dbahi = Pciwaddrh(data);
292                 p->count = 1<<31 | len - 2 | 1;
293                 flags |= 1<<16;
294         }
295         l = m->list;
296         l->flags = flags | 0x5;
297         l->len = 0;
298         l->ctab = PCIWADDR(t);
299         l->ctabhi = Pciwaddrh(t);
300         return l;
301 }
302
303 static int
304 nop(Aportc *pc)
305 {
306         uchar *c;
307
308         if((pc->m->feat & Dnop) == 0)
309                 return -1;
310         c = pc->m->ctab->cfis;
311         nopfis(pc->m, c, 0);
312         mkalist(pc->m, Lwrite, 0, 0);
313         return ahciwait(pc, 3*1000);
314 }
315
316 static int
317 setfeatures(Aportc *pc, uchar f, uint w)
318 {
319         uchar *c;
320
321         c = pc->m->ctab->cfis;
322         featfis(pc->m, c, f);
323         mkalist(pc->m, Lwrite, 0, 0);
324         return ahciwait(pc, w);
325 }
326
327 /*
328  * ata 7, required for sata, requires that all devices "support"
329  * udma mode 5,   however sata:pata bridges allow older devices
330  * which may not.  the innodisk satadom, for example allows
331  * only udma mode 2.  on the assumption that actual udma is
332  * taking place on these bridges, we set the highest udma mode
333  * available, or pio if there is no udma mode available.
334  */
335 static int
336 settxmode(Aportc *pc, uchar f)
337 {
338         uchar *c;
339
340         c = pc->m->ctab->cfis;
341         if(txmodefis(pc->m, c, f) == -1)
342                 return 0;
343         mkalist(pc->m, Lwrite, 0, 0);
344         return ahciwait(pc, 3*1000);
345 }
346
347 static void
348 asleep(int ms)
349 {
350         if(up == nil || !islo())
351                 delay(ms);
352         else
353                 esleep(ms);
354 }
355
356 static int
357 ahciportreset(Aportc *c, uint mode)
358 {
359         ulong *cmd, i;
360         Aport *p;
361
362         p = c->p;
363         cmd = &p->cmd;
364         *cmd &= ~(Afre|Ast);
365         for(i = 0; i < 500; i += 25){
366                 if((*cmd & Acr) == 0)
367                         break;
368                 asleep(25);
369         }
370         if((*cmd & Apwr) != Apwr)
371                 *cmd |= Apwr;
372         p->sctl = 3*Aipm | 0*Aspd | Adet;
373         delay(1);
374         p->sctl = 3*Aipm | mode*Aspd;
375         return 0;
376 }
377
378 static int
379 ahciflushcache(Aportc *pc)
380 {
381         uchar *c;
382
383         c = pc->m->ctab->cfis;
384         flushcachefis(pc->m, c);
385         mkalist(pc->m, Lwrite, 0, 0);
386
387         if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
388                 dprint("ahciflushcache fail [task %lux]\n", pc->p->task);
389 //              preg(pc->m->fis.r, 20);
390                 return -1;
391         }
392         return 0;
393 }
394
395 static int
396 ahciidentify0(Aportc *pc, void *id)
397 {
398         uchar *c;
399         Actab *t;
400
401         t = pc->m->ctab;
402         c = t->cfis;
403         memset(id, 0, 0x200);
404         identifyfis(pc->m, c);
405         mkalist(pc->m, 0, id, 0x200);
406         return ahciwait(pc, 3*1000);
407 }
408
409 static vlong
410 ahciidentify(Aportc *pc, ushort *id, uint *ss, char *d)
411 {
412         int i, n;
413         vlong s;
414         Aportm *m;
415
416         m = pc->m;
417         for(i = 0;; i++){
418                 if(i > 5 || ahciidentify0(pc, id) != 0)
419                         return -1;
420                 n = idpuis(id);
421                 if(n & Pspinup && setfeatures(pc, 7, 20*1000) == -1)
422                         print("%s: puis spinup fail\n", d);
423                 if(n & Pidready)
424                         break;
425                 print("%s: puis waiting\n", d);
426         }
427         s = idfeat(m, id);
428         *ss = idss(m, id);
429         if(s == -1 || (m->feat&Dlba) == 0){
430                 if((m->feat&Dlba) == 0)
431                         dprint("%s: no lba support\n", d);
432                 return -1;
433         }
434         return s;
435 }
436
437 static int
438 ahciquiet(Aport *a)
439 {
440         ulong *p, i;
441
442         p = &a->cmd;
443         *p &= ~Ast;
444         for(i = 0; i < 500; i += 50){
445                 if((*p & Acr) == 0)
446                         goto stop;
447                 asleep(50);
448         }
449         return -1;
450 stop:
451         if((a->task & (ASdrq|ASbsy)) == 0){
452                 *p |= Ast;
453                 return 0;
454         }
455
456         *p |= Aclo;
457         for(i = 0; i < 500; i += 50){
458                 if((*p & Aclo) == 0)
459                         goto stop1;
460                 asleep(50);
461         }
462         return -1;
463 stop1:
464         /* extra check */
465         dprint("ahci: clo clear [task %lux]\n", a->task);
466         if(a->task & ASbsy)
467                 return -1;
468         *p |= Afre | Ast;
469         return 0;
470 }
471
472 static int
473 ahcicomreset(Aportc *pc)
474 {
475         uchar *c;
476
477         dreg("comreset ", pc->p);
478         if(ahciquiet(pc->p) == -1){
479                 dprint("ahci: ahciquiet failed\n");
480                 return -1;
481         }
482         dreg("comreset ", pc->p);
483
484         c = pc->m->ctab->cfis;
485         nopfis(pc->m, c, 1);
486         mkalist(pc->m, Lclear | Lreset, 0, 0);
487         if(ahciwait(pc, 500) == -1){
488                 dprint("ahci: comreset1 failed\n");
489                 return -1;
490         }
491         microdelay(250);
492         dreg("comreset ", pc->p);
493
494         nopfis(pc->m, c, 0);
495         mkalist(pc->m, Lwrite, 0, 0);
496         if(ahciwait(pc, 150) == -1){
497                 dprint("ahci: comreset2 failed\n");
498                 return -1;
499         }
500         dreg("comreset ", pc->p);
501         return 0;
502 }
503
504 static int
505 ahciidle(Aport *port)
506 {
507         ulong *p, i, r;
508
509         p = &port->cmd;
510         if((*p & Arun) == 0)
511                 return 0;
512         *p &= ~Ast;
513         r = 0;
514         for(i = 0; i < 500; i += 25){
515                 if((*p & Acr) == 0)
516                         goto stop;
517                 asleep(25);
518         }
519         r = -1;
520 stop:
521         if((*p & Afre) == 0)
522                 return r;
523         *p &= ~Afre;
524         for(i = 0; i < 500; i += 25){
525                 if((*p & Afre) == 0)
526                         return 0;
527                 asleep(25);
528         }
529         return -1;
530 }
531
532 /*
533  * §6.2.2.1  first part; comreset handled by reset disk.
534  *      - remainder is handled by configdisk.
535  *      - ahcirecover is a quick recovery from a failed command.
536  */
537 static int
538 ahciswreset(Aportc *pc)
539 {
540         int i;
541
542         i = ahciidle(pc->p);
543         pc->p->cmd |= Afre;
544         if(i == -1)
545                 return -1;
546         if(pc->p->task & (ASdrq|ASbsy))
547                 return -1;
548         return 0;
549 }
550
551 static int
552 ahcirecover(Aportc *pc)
553 {
554         ahciswreset(pc);
555         pc->p->cmd |= Ast;
556         if(settxmode(pc, pc->m->udma) == -1)
557                 return -1;
558         return 0;
559 }
560
561 static void*
562 malign(int size, int align)
563 {
564         void *v;
565
566         v = xspanalloc(size, align, 0);
567         memset(v, 0, size);
568         return v;
569 }
570
571 static void
572 setupfis(Afis *f)
573 {
574         f->base = malign(0x100, 0x100);
575         f->d = f->base + 0;
576         f->p = f->base + 0x20;
577         f->r = f->base + 0x40;
578         f->u = f->base + 0x60;
579         f->devicebits = (ulong*)(f->base + 0x58);
580 }
581
582 static void
583 ahciwakeup(Aportc *c, uint mode)
584 {
585         ushort s;
586         Aport *p;
587
588         p = c->p;
589         s = p->sstatus;
590         if((s & Isleepy) != 0)
591                 return;
592         if((s & Smask) != Spresent){
593                 dprint("ahci: slumbering drive missing [ss %.3ux]\n", s);
594                 return;
595         }
596         ahciportreset(c, mode);
597         dprint("ahci: wake %.3ux -> %.3lux\n", s, c->p->sstatus);
598 }
599
600 static int
601 ahciconfigdrive(Ahba *h, Aportc *c, int mode)
602 {
603         Aportm *m;
604         Aport *p;
605         int i;
606
607         p = c->p;
608         m = c->m;
609
610         if(m->list == 0){
611                 setupfis(&m->fis);
612                 m->list = malign(sizeof *m->list, 1024);
613                 m->ctab = malign(sizeof *m->ctab, 128);
614         }
615
616         if(ahciidle(p) == -1){
617                 dprint("ahci: port not idle\n");
618                 return -1;
619         }
620
621         p->list = PCIWADDR(m->list);
622         p->listhi = Pciwaddrh(m->list);
623         p->fis = PCIWADDR(m->fis.base);
624         p->fishi = Pciwaddrh(m->fis.base);
625
626         p->cmd |= Afre;
627
628         if((p->cmd & Apwr) != Apwr)
629                 p->cmd |= Apwr;
630
631         if((h->cap & Hss) != 0){
632                 dprint("ahci: spin up ... [%.3lux]\n", p->sstatus);
633                 for(i = 0; i < 1400; i += 50){
634                         if((p->sstatus & Sbist) != 0)
635                                 break;
636                         if((p->sstatus & Smask) == Sphylink)
637                                 break;
638                         asleep(50);
639                 }
640         }
641
642         if((p->sstatus & SSmask) == (Isleepy | Spresent))
643                 ahciwakeup(c, mode);
644
645         p->serror = SerrAll;
646         p->ie = IEM;
647
648         /* we will get called again once phylink has been established */
649         if((p->sstatus & Smask) != Sphylink)
650                 return 0;
651
652         /* disable power managment sequence from book. */
653         p->sctl = 3*Aipm | mode*Aspd | 0*Adet;
654         p->cmd &= ~Aalpe;
655
656         p->cmd |= Afre | Ast;
657
658         return 0;
659 }
660
661 static int
662 ahcienable(Ahba *h)
663 {
664         h->ghc |= Hie;
665         return 0;
666 }
667
668 static int
669 ahcidisable(Ahba *h)
670 {
671         h->ghc &= ~Hie;
672         return 0;
673 }
674
675 static int
676 countbits(ulong u)
677 {
678         int i, n;
679
680         n = 0;
681         for(i = 0; i < 32; i++)
682                 if(u & (1<<i))
683                         n++;
684         return n;
685 }
686
687 static int
688 ahciconf(Ctlr *c)
689 {
690         uint u;
691         Ahba *h;
692
693         h = c->hba = (Ahba*)c->mmio;
694         u = h->cap;
695
696         if((u & Ham) == 0)
697                 h->ghc |= Hae;
698
699         return countbits(h->pi);
700 }
701
702 static int
703 ahcihandoff(Ahba *h)
704 {
705         int wait;
706
707         if((h->cap2 & Boh) == 0)
708                 return 0;
709         h->bios |= Oos;
710         for(wait = 0; wait < 2000; wait += 100){
711                 if((h->bios & Bos) == 0)
712                         return 0;
713                 delay(100);
714         }
715         iprint("ahci: bios handoff timed out\n");
716         return -1;
717 }
718
719 static int
720 ahcihbareset(Ahba *h)
721 {
722         int wait;
723
724         h->ghc |= Hhr;
725         for(wait = 0; wait < 1000; wait += 100){
726                 if(h->ghc == 0)
727                         return 0;
728                 delay(100);
729         }
730         return -1;
731 }
732
733 static char*
734 dnam(Drive *d)
735 {
736         char *s;
737
738         s = d->name;
739         if(d->unit && d->unit->name)
740                 s = d->unit->name;
741         return s;
742 }
743
744 static int
745 identify(Drive *d)
746 {
747         uchar oserial[21];
748         ushort *id;
749         vlong osectors, s;
750         SDunit *u;
751
752         id = d->info;
753         s = ahciidentify(&d->portc, id, &d->secsize, dnam(d));
754         if(s == -1)
755                 return -1;
756         osectors = d->sectors;
757         memmove(oserial, d->serial, sizeof d->serial);
758
759         d->sectors = s;
760
761         idmove(d->serial, id+10, 20);
762         idmove(d->firmware, id+23, 8);
763         idmove(d->model, id+27, 40);
764         d->wwn = idwwn(d->portc.m, id);
765
766         u = d->unit;
767         memset(u->inquiry, 0, sizeof u->inquiry);
768         u->inquiry[2] = 2;
769         u->inquiry[3] = 2;
770         u->inquiry[4] = sizeof u->inquiry - 4;
771         memmove(u->inquiry+8, d->model, 40);
772
773         if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
774                 d->drivechange = 1;
775                 d->nodma = 0;
776                 u->sectors = 0;
777         }
778         return 0;
779 }
780
781 static void
782 clearci(Aport *p)
783 {
784         if(p->cmd & Ast){
785                 p->cmd &= ~Ast;
786                 p->cmd |=  Ast;
787         }
788 }
789
790 static int
791 intel(Ctlr *c)
792 {
793         return c->pci->vid == 0x8086;
794 }
795
796 static int
797 ignoreahdrs(Drive *d)
798 {
799         return d->portm.feat & Datapi && d->ctlr->type == Tsb600;
800 }
801
802 static void
803 updatedrive(Drive *d)
804 {
805         ulong f, cause, serr, s0, pr, ewake;
806         Aport *p;
807         static ulong last;
808
809         pr = 1;
810         ewake = 0;
811         f = 0;
812         p = d->port;
813         cause = p->isr;
814         if(d->ctlr->type == Tjmicron)
815                 cause &= ~Aifs;
816         serr = p->serror;
817         p->isr = cause;
818
819         if(p->ci == 0){
820                 f |= Fdone;
821                 pr = 0;
822         }else if(cause & Adps){
823                 pr = 0;
824         }else if(cause & Atfes){
825                 f |= Ferror;
826                 ewake = 1;
827                 pr = 0;
828         }
829         if(cause & Ifatal){
830                 ewake = 1;
831                 dprint("%s: fatal\n", dnam(d));
832         }
833         if(cause & Adhrs){
834                 if(p->task & 33){
835                         if(ignoreahdrs(d) && serr & ErrE)
836                                 f |= Fahdrs;
837                         dprint("%s: Adhrs cause %lux serr %lux task %lux\n",
838                                 dnam(d), cause, serr, p->task);
839                         f |= Ferror;
840                         ewake = 1;
841                 }
842                 pr = 0;
843         }
844         if(p->task & 1 && last != cause)
845                 dprint("%s: err ca %lux serr %lux task %lux sstat %.3lux\n",
846                         dnam(d), cause, serr, p->task, p->sstatus);
847         if(pr)
848                 dprint("%s: upd %lux ta %lux\n", dnam(d), cause, p->task);
849
850         if(cause & (Aprcs|Aifs)){
851                 s0 = d->state;
852                 switch(p->sstatus & Smask){
853                 case Smissing:
854                         d->state = Dmissing;
855                         break;
856                 case Spresent:
857                         if((p->sstatus & Imask) == Islumber)
858                                 d->state = Dnew;
859                         else
860                                 d->state = Derror;
861                         break;
862                 case Sphylink:
863                         /* power mgnt crap for suprise removal */
864                         p->ie |= Aprcs|Apcs;    /* is this required? */
865                         d->state = Dreset;
866                         break;
867                 case Sbist:
868                         d->state = Doffline;
869                         break;
870                 }
871                 dprint("%s: updatedrive: %s → %s [ss %.3lux]\n",
872                         dnam(d), diskstates[s0], diskstates[d->state], p->sstatus);
873                 if(s0 == Dready && d->state != Dready)
874                         dprint("%s: pulled\n", dnam(d));
875                 if(d->state != Dready)
876                         f |= Ferror;
877                 if(d->state != Dready || p->ci)
878                         ewake = 1;
879         }
880         p->serror = serr;
881         if(ewake)
882                 clearci(p);
883         if(f){
884                 d->portm.flag = f;
885                 wakeup(&d->portm);
886         }
887         last = cause;
888 }
889
890 static void
891 dstatus(Drive *d, int s)
892 {
893         dprint("%s: dstatus: %s → %s from pc=%p\n", dnam(d), 
894                 diskstates[d->state], diskstates[s], getcallerpc(&d));
895
896         ilock(d);
897         d->state = s;
898         iunlock(d);
899 }
900
901 static void
902 configdrive(Drive *d)
903 {
904         if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1)
905                 dstatus(d, Dportreset);
906
907         ilock(d);
908         switch(d->port->sstatus & Smask){
909         case Smissing:
910                 d->state = Dmissing;
911                 break;
912         case Spresent:
913                 break;
914         case Sphylink:
915                 if(d->state == Dready)
916                         break;
917                 d->wait = 0;
918                 d->state = Dnew;
919                 break;
920         case Sbist:
921                 d->state = Doffline;
922                 break;
923         }
924         iunlock(d);
925
926         dprint("%s: configdrive: %s\n", dnam(d), diskstates[d->state]);
927 }
928
929 static void
930 resetdisk(Drive *d)
931 {
932         uint state, det, stat;
933         Aport *p;
934
935         p = d->port;
936         det = p->sctl & 7;
937         stat = p->sstatus & Smask;
938         state = (p->cmd>>28) & 0xf;
939         dprint("%s: resetdisk [icc %ux; det %.3ux; sdet %.3ux]\n", dnam(d), state, det, stat);
940
941         ilock(d);
942         if(d->state != Dready && d->state != Dnew)
943                 d->portm.flag |= Ferror;
944         if(stat != Sphylink)
945                 d->state = Dportreset;
946         else
947                 d->state = Dreset;
948         clearci(p);                     /* satisfy sleep condition. */
949         wakeup(&d->portm);
950         iunlock(d);
951
952         if(stat != Sphylink)
953                 return;
954
955         qlock(&d->portm);
956         if(p->cmd&Ast && ahciswreset(&d->portc) == -1)
957                 dstatus(d, Dportreset); /* get a bigger stick. */
958         else
959                 configdrive(d);
960         qunlock(&d->portm);
961 }
962
963 static int
964 newdrive(Drive *d)
965 {
966         char *s;
967         Aportc *c;
968         Aportm *m;
969
970         c = &d->portc;
971         m = &d->portm;
972
973         qlock(c->m);
974         setfissig(m, c->p->sig);
975         if(identify(d) == -1){
976                 dprint("%s: identify failure\n", dnam(d));
977                 goto lose;
978         }
979         if(settxmode(c, m->udma) == -1){
980                 dprint("%s: can't set udma mode\n", dnam(d));
981                 goto lose;
982         }
983         if(m->feat & Dpower && setfeatures(c, 0x85, 3*1000) == -1){
984                 dprint("%s: can't disable apm\n", dnam(d));
985                 m->feat &= ~Dpower;
986                 if(ahcirecover(c) == -1)
987                         goto lose;
988         }
989         dstatus(d, Dready);
990         qunlock(c->m);
991
992         s = "";
993         if(m->feat & Dllba)
994                 s = "L";
995         idprint("%s: %sLBA %,lld sectors\n", dnam(d), s, d->sectors);
996         idprint("  %s %s %s %s\n", d->model, d->firmware, d->serial,
997                 d->drivechange? "[newdrive]": "");
998         return 0;
999
1000 lose:
1001         idprint("%s: can't be initialized\n", dnam(d));
1002         dstatus(d, Dnull);
1003         qunlock(c->m);
1004         return -1;
1005 }
1006
1007 enum {
1008         Nms             = 256,
1009         Mphywait        =  2*1024/Nms - 1,
1010         Midwait         = 16*1024/Nms - 1,
1011         Mcomrwait       = 64*1024/Nms - 1,
1012 };
1013
1014 static void
1015 hangck(Drive *d)
1016 {
1017         if(d->active && d->totick != 0 && (long)(Ticks - d->totick) > 0){
1018                 dprint("%s: drive hung [task %lux; ci %lux; serr %lux]%s\n",
1019                         dnam(d), d->port->task, d->port->ci, d->port->serror,
1020                         d->nodma == 0 ? "; disabling dma" : "");
1021                 d->nodma = 1;
1022                 d->state = Dreset;
1023         }
1024 }
1025
1026 static ushort olds[NCtlr*NCtlrdrv];
1027
1028 static void
1029 doportreset(Drive *d)
1030 {
1031         qlock(&d->portm);
1032         ahciportreset(&d->portc, d->mode);
1033         qunlock(&d->portm);
1034
1035         dprint("ahci: portreset: %s [task %lux; ss %.3lux]\n",
1036                 diskstates[d->state], d->port->task, d->port->sstatus);
1037 }
1038
1039 /* drive must be locked */
1040 static void
1041 statechange(Drive *d)
1042 {
1043         switch(d->state){
1044         case Dnull:
1045         case Doffline:
1046                 if(d->unit)
1047                 if(d->unit->sectors != 0){
1048                         d->sectors = 0;
1049                         d->drivechange = 1;
1050                 }
1051         case Dready:
1052                 d->wait = 0;
1053         }
1054 }
1055
1056 static uint
1057 maxmode(Ctlr *c)
1058 {
1059         return (c->hba->cap & 0xf*Hiss)/Hiss;
1060 }
1061
1062 static void iainterrupt(Ureg*, void *);
1063
1064 static void
1065 checkdrive(Drive *d, int i)
1066 {
1067         ushort s, sig;
1068
1069         if(d->ctlr->enabled == 0)
1070                 return;
1071         if(d->driveno == 0)
1072                 iainterrupt(0, d->ctlr);        /* check for missed irq's */
1073
1074         ilock(d);
1075         s = d->port->sstatus;
1076         if(s)
1077                 d->lastseen = Ticks;
1078         if(s != olds[i]){
1079                 dprint("%s: status: %.3ux -> %.3ux: %s\n",
1080                         dnam(d), olds[i], s, diskstates[d->state]);
1081                 olds[i] = s;
1082                 d->wait = 0;
1083         }
1084         hangck(d);
1085         switch(d->state){
1086         case Dnull:
1087         case Dready:
1088                 break;
1089         case Dmissing:
1090         case Dnew:
1091                 switch(s & (Iactive|Smask)){
1092                 case Spresent:
1093                         ahciwakeup(&d->portc, d->mode);
1094                 case Smissing:
1095                         break;
1096                 default:
1097                         dprint("%s: unknown status %.3ux\n", dnam(d), s);
1098                         /* fall through */
1099                 case Iactive:           /* active, no device */
1100                         if(++d->wait&Mphywait)
1101                                 break;
1102 reset:
1103                         if(d->mode == 0)
1104                                 d->mode = maxmode(d->ctlr);
1105                         else
1106                                 d->mode--;
1107                         if(d->mode == DMautoneg){
1108                                 d->state = Dportreset;
1109                                 goto portreset;
1110                         }
1111                         dprint("%s: reset; new mode %s\n", dnam(d),
1112                                 modes[d->mode]);
1113                         iunlock(d);
1114                         resetdisk(d);
1115                         ilock(d);
1116                         break;
1117                 case Iactive | Sphylink:
1118                         if(d->unit == nil)
1119                                 break;
1120                         if((++d->wait&Midwait) == 0){
1121                                 dprint("%s: slow reset [task %lux; ss %.3ux; wait %d]\n",
1122                                         dnam(d), d->port->task, s, d->wait);
1123                                 goto reset;
1124                         }
1125                         s = (uchar)d->port->task;
1126                         sig = d->port->sig >> 16;
1127                         if(s == 0x7f || s&ASbsy ||
1128                             (sig != 0xeb14 && (s & ASdrdy) == 0))
1129                                 break;
1130                         iunlock(d);
1131                         newdrive(d);
1132                         ilock(d);
1133                         break;
1134                 }
1135                 break;
1136         case Doffline:
1137                 if(d->wait++ & Mcomrwait)
1138                         break;
1139                 /* fallthrough */
1140         case Derror:
1141         case Dreset:
1142                 dprint("%s: reset [%s]: mode %d; status %.3ux\n",
1143                         dnam(d), diskstates[d->state], d->mode, s);
1144                 iunlock(d);
1145                 resetdisk(d);
1146                 ilock(d);
1147                 break;
1148         case Dportreset:
1149 portreset:
1150                 if(d->wait++ & 0xff && (s & Iactive) == 0)
1151                         break;
1152                 dprint("%s: portreset [%s]: mode %d; status %.3ux\n",
1153                         dnam(d), diskstates[d->state], d->mode, s);
1154                 d->portm.flag |= Ferror;
1155                 clearci(d->port);
1156                 wakeup(&d->portm);
1157                 if((s & Smask) == Smissing){
1158                         d->state = Dmissing;
1159                         break;
1160                 }
1161                 iunlock(d);
1162                 doportreset(d);
1163                 ilock(d);
1164                 break;
1165         }
1166         statechange(d);
1167         iunlock(d);
1168 }
1169
1170 static void
1171 satakproc(void*)
1172 {
1173         int i;
1174
1175         for(;;){
1176                 tsleep(&up->sleep, return0, 0, Nms);
1177                 for(i = 0; i < niadrive; i++)
1178                         checkdrive(iadrive[i], i);
1179         }
1180 }
1181
1182 static void
1183 iainterrupt(Ureg *u, void *a)
1184 {
1185         int i;
1186         ulong cause, m;
1187         Ctlr *c;
1188         Drive *d;
1189
1190         c = a;
1191         ilock(c);
1192         cause = c->hba->isr;
1193         for(i = 0; cause; i++){
1194                 m = 1 << i;
1195                 if((cause & m) == 0)
1196                         continue;
1197                 cause &= ~m;
1198                 d = c->rawdrive + i;
1199                 ilock(d);
1200                 if(d->port->isr && c->hba->pi & m)
1201                         updatedrive(d);
1202                 c->hba->isr = m;
1203                 iunlock(d);
1204         }
1205         if(u == 0 && i > 0)
1206                 c->missirq++;
1207         iunlock(c);
1208 }
1209
1210 static int
1211 ahciencreset(Ctlr *c)
1212 {
1213         Ahba *h;
1214
1215         if(c->enctype == Eesb)
1216                 return 0;
1217         h = c->hba;
1218         h->emctl |= Emrst;
1219         while(h->emctl & Emrst)
1220                 delay(1);
1221         return 0;
1222 }
1223
1224 /*
1225  * from the standard: (http://en.wikipedia.org/wiki/IBPI)
1226  * rebuild is preferred as locate+fail; alternate 1hz fail
1227  * we're going to assume no locate led.
1228  */
1229
1230 enum {
1231         Ledsleep        = 125,          /* 8hz */
1232
1233         N0      = Ledon*Aled,
1234         L0      = Ledon*Aled | Ledon*Locled,
1235         L1      = Ledon*Aled | Ledoff*Locled,
1236         R0      = Ledon*Aled | Ledon*Locled |   Ledon*Errled,
1237         R1      = Ledon*Aled |                  Ledoff*Errled,
1238         S0      = Ledon*Aled |  Ledon*Locled /*|        Ledon*Errled*/, /* botch */
1239         S1      = Ledon*Aled |                  Ledoff*Errled,
1240         P0      = Ledon*Aled |                  Ledon*Errled,
1241         P1      = Ledon*Aled |                  Ledoff*Errled,
1242         F0      = Ledon*Aled |                  Ledon*Errled,
1243         C0      = Ledon*Aled | Ledon*Locled,
1244         C1      = Ledon*Aled | Ledoff*Locled,
1245
1246 };
1247
1248 //static ushort led3[Ibpilast*8] = {
1249 //[Ibpinone*8]  0,      0,      0,      0,      0,      0,      0,      0,
1250 //[Ibpinormal*8]        N0,     N0,     N0,     N0,     N0,     N0,     N0,     N0,
1251 //[Ibpirebuild*8]       R0,     R0,     R0,     R0,     R1,     R1,     R1,     R1,
1252 //[Ibpilocate*8]        L0,     L1,     L0,     L1,     L0,     L1,     L0,     L1,
1253 //[Ibpispare*8] S0,     S1,     S0,     S1,     S1,     S1,     S1,     S1,
1254 //[Ibpipfa*8]   P0,     P1,     P0,     P1,     P1,     P1,     P1,     P1,     /* first 1 sec */
1255 //[Ibpifail*8]  F0,     F0,     F0,     F0,     F0,     F0,     F0,     F0,
1256 //[Ibpicritarray*8]     C0,     C0,     C0,     C0,     C1,     C1,     C1,     C1,
1257 //[Ibpifailarray*8]     C0,     C1,     C0,     C1,     C0,     C1,     C0,     C1,
1258 //};
1259
1260 static ushort led2[Ibpilast*8] = {
1261 [Ibpinone*8]    0,      0,      0,      0,      0,      0,      0,      0,
1262 [Ibpinormal*8]  N0,     N0,     N0,     N0,     N0,     N0,     N0,     N0,
1263 [Ibpirebuild*8] R0,     R0,     R0,     R0,     R1,     R1,     R1,     R1,
1264 [Ibpilocate*8]  L0,     L0,     L0,     L0,     L0,     L0,     L0,     L0,
1265 [Ibpispare*8]   S0,     S0,     S0,     S0,     S1,     S1,     S1,     S1,
1266 [Ibpipfa*8]     P0,     P1,     P0,     P1,     P1,     P1,     P1,     P1,     /* first 1 sec */
1267 [Ibpifail*8]    F0,     F0,     F0,     F0,     F0,     F0,     F0,     F0,
1268 [Ibpicritarray*8]       C0,     C0,     C0,     C0,     C1,     C1,     C1,     C1,
1269 [Ibpifailarray*8]       C0,     C1,     C0,     C1,     C0,     C1,     C0,     C1,
1270 };
1271
1272 static int
1273 ledstate(Ledport *p, uint seq)
1274 {
1275         ushort i;
1276
1277         if(p->led == Ibpipfa && seq%32 >= 8)
1278                 i = P1;
1279         else
1280                 i = led2[8*p->led + seq%8];
1281         if(i != p->ledbits){
1282                 p->ledbits = i;
1283                 ledprint("ledstate %,.011ub %ud\n", p->ledbits, seq);
1284                 return 1;
1285         }
1286         return 0;
1287 }
1288
1289 static int
1290 blink(Drive *d, ulong t)
1291 {
1292         Ahba *h;
1293         Ctlr *c;
1294         Aledmsg msg;
1295
1296         if(ledstate(d, t) == 0)
1297                 return 0;
1298         c = d->ctlr;
1299         h = c->hba;
1300         /* ensure last message has been transmitted */
1301         while(h->emctl & Tmsg)
1302                 microdelay(1);
1303         switch(c->enctype){
1304         default:
1305                 panic("%s: bad led type %d", dnam(d), c->enctype);
1306         case Elmt:
1307                 memset(&msg, 0, sizeof msg);
1308                 msg.type = Mled;
1309                 msg.dsize = 0;
1310                 msg.msize = sizeof msg - 4;
1311                 msg.led[0] = d->ledbits;
1312                 msg.led[1] = d->ledbits>>8;
1313                 msg.pm = 0;
1314                 msg.hba = d->driveno;
1315                 memmove(c->enctx, &msg, sizeof msg);
1316                 break;
1317         }
1318         h->emctl |= Tmsg;
1319         return 1;
1320 }
1321
1322 enum {
1323         Esbdrv0 = 4,            /* start pos in bits */
1324         Esbiota = 3,            /* shift in bits */
1325         Esbact  = 1,
1326         Esbloc  = 2,
1327         Esberr  = 4,
1328 };
1329
1330 uint
1331 esbbits(uint s)
1332 {
1333         uint i, e;                              /* except after c */
1334
1335         e = 0;
1336         for(i = 0; i < 3; i++)
1337                 e |= ((s>>3*i & 7) != 0)<<i;
1338         return e;
1339 }
1340
1341 static int
1342 blinkesb(Ctlr *c, ulong t)
1343 {
1344         uint i, s, u[32/4];
1345         uvlong v;
1346         Drive *d;
1347
1348         s = 0;
1349         for(i = 0; i < c->ndrive; i++){
1350                 d = c->drive[i];
1351                 s |= ledstate(d, t);            /* no port mapping */
1352         }
1353         if(s == 0)
1354                 return 0;
1355         memset(u, 0, sizeof u);
1356         for(i = 0; i < c->ndrive; i++){
1357                 d = c->drive[i];
1358                 s = Esbdrv0 + Esbiota*i;
1359                 v = esbbits(d->ledbits) * (1ull << s%32);
1360                 u[s/32 + 0] |= v;
1361                 u[s/32 + 1] |= v>>32;
1362         }
1363         for(i = 0; i < c->encsz; i++)
1364                 c->enctx[i] = u[i];
1365         return 1;
1366 }
1367
1368 static long
1369 ahciledr(SDunit *u, Chan *ch, void *a, long n, vlong off)
1370 {
1371         Ctlr *c;
1372         Drive *d;
1373
1374         c = u->dev->ctlr;
1375         d = c->drive[u->subno];
1376         return ledr(d, ch, a, n, off);
1377 }
1378
1379 static long
1380 ahciledw(SDunit *u, Chan *ch, void *a, long n, vlong off)
1381 {
1382         Ctlr *c;
1383         Drive *d;
1384
1385         c = u->dev->ctlr;
1386         d = c->drive[u->subno];
1387         return ledw(d, ch, a, n, off);
1388 }
1389
1390 static void
1391 ledkproc(void*)
1392 {
1393         uchar map[NCtlr];
1394         uint i, j, t0, t1;
1395         Ctlr *c;
1396         Drive *d;
1397
1398         j = 0;
1399         memset(map, 0, sizeof map);
1400         for(i = 0; i < niactlr; i++)
1401                 if(iactlr[i].enctype != 0){
1402                         ahciencreset(iactlr + i);
1403                         map[i] = 1;
1404                         j++;
1405                 }
1406         if(j == 0)
1407                 pexit("no work", 1);
1408         for(i = 0; i < niadrive; i++){
1409                 iadrive[i]->nled = 3;           /* hardcoded */
1410                 if(iadrive[i]->ctlr->enctype == Eesb)
1411                         iadrive[i]->nled = 3;
1412                 iadrive[i]->ledbits = -1;
1413         }
1414         for(i = 0; ; i++){
1415                 t0 = Ticks;
1416                 for(j = 0; j < niadrive; ){
1417                         c = iadrive[j]->ctlr;
1418                         if(map[j] == 0)
1419                                 j += c->enctype;
1420                         else if(c->enctype == Eesb){
1421                                 blinkesb(c, i);
1422                                 j += c->ndrive;
1423                         }else{
1424                                 d = iadrive[j++];
1425                                 blink(d, i);
1426                         }
1427                 }
1428                 t1 = Ticks;
1429                 esleep(Ledsleep - TK2MS(t1 - t0));
1430         }
1431 }
1432
1433 static int
1434 waitready(Drive *d)
1435 {
1436         ulong s, i, δ;
1437
1438         for(i = 0;; i += 250){
1439                 if(d->state == Dreset || d->state == Dportreset || d->state == Dnew)
1440                         return 1;
1441                 ilock(d);
1442                 s = d->port->sstatus;
1443                 if(d->state == Dready && (s & Smask) == Sphylink){
1444                         iunlock(d);
1445                         return 0;
1446                 }
1447                 δ = Ticks - d->lastseen;
1448                 if(d->state == Dnull || δ > 10*1000)
1449                         break;
1450                 if((s & Imask) == 0 && δ > 1500)
1451                         break;
1452                 if(i >= 15*1000){
1453                         d->state = Doffline;
1454                         iunlock(d);
1455                         print("%s: not responding; offline\n", dnam(d));
1456                         return -1;
1457                 }
1458                 iunlock(d);
1459                 esleep(250);
1460         }
1461         iunlock(d);
1462         return -1;
1463 }
1464
1465 static int
1466 iaverify(SDunit *u)
1467 {
1468         Ctlr *c;
1469         Drive *d;
1470
1471         c = u->dev->ctlr;
1472         d = c->drive[u->subno];
1473         ilock(c);
1474         ilock(d);
1475         if(d->unit == nil){
1476                 d->unit = u;
1477                 if(c->enctype != 0)
1478                         sdaddfile(u, "led", 0644, eve, ahciledr, ahciledw);
1479         }
1480         iunlock(d);
1481         iunlock(c);
1482         checkdrive(d, d->driveno);              /* c->d0 + d->driveno */
1483         return 1;
1484 }
1485
1486 static int
1487 iaonline(SDunit *u)
1488 {
1489         int r;
1490         Ctlr *c;
1491         Drive *d;
1492
1493         c = u->dev->ctlr;
1494         d = c->drive[u->subno];
1495
1496         while(waitready(d) == 1)
1497                 esleep(1);
1498
1499         dprint("%s: iaonline: %s\n", dnam(d), diskstates[d->state]);
1500
1501         ilock(d);
1502         if(d->portm.feat & Datapi){
1503                 r = d->drivechange;
1504                 d->drivechange = 0;
1505                 iunlock(d);
1506                 if(r != 0)
1507                         scsiverify(u);
1508                 return scsionline(u);
1509         }
1510         r = 0;
1511         if(d->drivechange){
1512                 d->drivechange = 0;
1513                 r = 2;
1514         }else if(d->state == Dready)
1515                 r = 1;
1516         if(r){
1517                 u->sectors = d->sectors;
1518                 u->secsize = d->secsize;
1519         }
1520         iunlock(d);
1521
1522         return r;
1523 }
1524
1525 static int
1526 iaenable(SDev *s)
1527 {
1528         char name[32];
1529         Ctlr *c;
1530         static int once;
1531
1532         c = s->ctlr;
1533         ilock(c);
1534         if(!c->enabled){
1535                 if(once == 0)
1536                         kproc("iasata", satakproc, 0);
1537                 if(c->ndrive == 0)
1538                         panic("iaenable: zero s->ctlr->ndrive");
1539                 pcisetbme(c->pci);
1540                 snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1541                 intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1542                 /* supposed to squelch leftover interrupts here. */
1543                 ahcienable(c->hba);
1544                 c->enabled = 1;
1545                 if(++once == niactlr)
1546                         kproc("ialed", ledkproc, 0);
1547         }
1548         iunlock(c);
1549         return 1;
1550 }
1551
1552 static int
1553 iadisable(SDev *s)
1554 {
1555         char name[32];
1556         Ctlr *c;
1557
1558         c = s->ctlr;
1559         ilock(c);
1560         ahcidisable(c->hba);
1561         snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1562         intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1563         c->enabled = 0;
1564         iunlock(c);
1565         return 1;
1566 }
1567
1568 static Alist*
1569 ahcibuild(Drive *d, int rw, void *data, uint n, vlong lba)
1570 {
1571         uchar *c;
1572         uint flags;
1573         Aportm *m;
1574
1575         m = &d->portm;
1576         c = m->ctab->cfis;
1577         rwfis(m, c, rw, n, lba);
1578         flags = Lpref;
1579         if(rw == SDwrite)
1580                 flags |= Lwrite;
1581         return mkalist(m, flags, data, 512*n);
1582 }
1583
1584 static Alist*
1585 ahcibuildpkt(Drive *d, SDreq *r, void *data, int n)
1586 {
1587         uint flags;
1588         Aportm *m;
1589         uchar *c;
1590         Actab *t;
1591
1592         m = &d->portm;
1593         t = m->ctab;
1594         c = t->cfis;
1595
1596         atapirwfis(m, c, r->cmd, r->clen, 0x2000);
1597         if((n & 15) != 0 || d->nodma)
1598                 c[Ffeat] &= ~1; /* use pio */
1599         else if(c[Ffeat] & 1 && d->info[62] & (1<<15))  /* dma direction */
1600                 c[Ffeat] = (c[Ffeat] & ~(1<<2)) | ((r->write == 0) << 2);
1601         flags = Lpref | Latapi;
1602         if(r->write != 0 && data)
1603                 flags |= Lwrite;
1604         return mkalist(m, flags, data, n);
1605 }
1606
1607 static Alist*
1608 ahcibuildfis(Drive *d, SDreq *r, void *data, uint n)
1609 {
1610         uint flags;
1611         uchar *c;
1612         Aportm *m;
1613
1614         if((r->ataproto & Pprotom) == Ppkt)
1615                 return ahcibuildpkt(d, r, data, n);
1616
1617         m = &d->portm;
1618         c = m->ctab->cfis;
1619         memmove(c, r->cmd, r->clen);
1620         flags = Lpref;
1621         if(r->write || n == 0)
1622                 flags |= Lwrite;
1623         return mkalist(m, flags, data, n);
1624 }
1625
1626 static int
1627 lockready(Drive *d)
1628 {
1629         int i;
1630
1631         qlock(&d->portm);
1632         while ((i = waitready(d)) == 1) {
1633                 qunlock(&d->portm);
1634                 esleep(1);
1635                 qlock(&d->portm);
1636         }
1637         return i;
1638 }
1639
1640 static int
1641 flushcache(Drive *d)
1642 {
1643         int i;
1644
1645         i = -1;
1646         if(lockready(d) == 0)
1647                 i = ahciflushcache(&d->portc);
1648         qunlock(&d->portm);
1649         return i;
1650 }
1651
1652 static int
1653 io(Drive *d, uint proto, int to, int interrupt)
1654 {
1655         uint task, flag, stat, rv;
1656         Aport *p;
1657         Asleep as;
1658
1659         switch(waitready(d)){
1660         case -1:
1661                 return SDeio;
1662         case 1:
1663                 return SDretry;
1664         }
1665
1666         ilock(d);
1667         d->portm.flag = 0;
1668         iunlock(d);
1669         p = d->port;
1670         p->ci = 1;
1671
1672         as.p = p;
1673         as.i = 1;
1674         d->totick = 0;
1675         if(to > 0)
1676                 d->totick = Ticks + MS2TK(to) | 1;      /* fix fencepost */
1677         d->active++;
1678
1679         while(waserror())
1680                 if(interrupt){
1681                         d->active--;
1682                         d->port->ci = 0;
1683                         if(ahcicomreset(&d->portc) == -1)
1684                                 dstatus(d, Dreset);
1685                         return SDtimeout;
1686                 }
1687         
1688         sleep(&d->portm, ahciclear, &as);
1689         poperror();
1690
1691         d->active--;
1692         ilock(d);
1693         stat = d->state;
1694         flag = d->portm.flag;
1695         task = d->port->task;
1696         iunlock(d);
1697
1698         rv = SDok;
1699         if(proto & Ppkt && stat == Dready){
1700                 rv = task >> 8 + 4 & 0xf;
1701                 flag &= ~Fahdrs;
1702                 flag |= Fdone;
1703         }else if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && stat == Dready){
1704                 d->port->ci = 0;
1705                 ahcirecover(&d->portc);
1706                 task = d->port->task;
1707                 flag &= ~Fdone;         /* either an error or do-over */
1708         }
1709         if(flag == 0){
1710                 print("%s: retry\n", dnam(d));
1711                 return SDretry;
1712         }
1713         if(flag & (Fahdrs | Ferror)){
1714                 if((task & Eidnf) == 0)
1715                         print("%s: i/o error %ux\n", dnam(d), task);
1716                 return SDcheck;
1717         }
1718         return rv;
1719 }
1720
1721 static int
1722 iariopkt(SDreq *r, Drive *d)
1723 {
1724         int try, to;
1725         uchar *cmd;
1726         Alist *l;
1727
1728         cmd = r->cmd;
1729         aprint("%s: %.2ux %.2ux %c %d %p\n", dnam(d), cmd[0], cmd[2],
1730                 "rw"[r->write], r->dlen, r->data);
1731
1732         r->rlen = 0;
1733
1734         /*
1735          * prevent iaonline() to hang forever by timing out
1736          * inquiry and capacity commands after 5 seconds.
1737          */
1738         to = 30*1000;
1739         switch(cmd[0]){
1740         case 0x9e: if(cmd[1] != 0x10) break;
1741         case 0x25:
1742         case 0x12:
1743                 to = 5*1000;
1744                 break;
1745         }
1746
1747         for(try = 0; try < 10; try++){
1748                 qlock(&d->portm);
1749                 l = ahcibuildpkt(d, r, r->data, r->dlen);
1750                 r->status = io(d, Ppkt, to, 0);
1751                 switch(r->status){
1752                 case SDeio:
1753                         qunlock(&d->portm);
1754                         return SDeio;
1755                 case SDretry:
1756                         qunlock(&d->portm);
1757                         continue;
1758                 }
1759                 r->rlen = l->len;
1760                 qunlock(&d->portm);
1761                 return SDok;
1762         }
1763         print("%s: bad disk\n", dnam(d));
1764         return r->status = SDcheck;
1765 }
1766
1767 static long
1768 ahcibio(SDunit *u, int lun, int write, void *a, long count, uvlong lba)
1769 {
1770         int n, rw, try, status, max;
1771         uchar *data;
1772         Ctlr *c;
1773         Drive *d;
1774
1775         c = u->dev->ctlr;
1776         d = c->drive[u->subno];
1777         if(d->portm.feat & Datapi)
1778                 return scsibio(u, lun, write, a, count, lba);
1779
1780         max = 128;
1781         if(d->portm.feat & Dllba){
1782                 max = 8192;             /* ahci maximum */
1783                 if(c->type == Tsb600)
1784                         max = 255;      /* errata */
1785         }
1786         rw = write? SDwrite: SDread;
1787         data = a;
1788         dprint("%s: bio: %llud %c %lud %p\n",
1789                 dnam(d), lba, "rw"[rw], count, data);
1790
1791         for(try = 0; try < 10;){
1792                 n = count;
1793                 if(n > max)
1794                         n = max;
1795                 qlock(&d->portm);
1796                 ahcibuild(d, rw, data, n, lba);
1797                 status = io(d, Pdma, 5000, 0);
1798                 qunlock(&d->portm);
1799                 switch(status){
1800                 case SDeio:
1801                         return -1;
1802                 case SDretry:
1803                         try++;
1804                         continue;
1805                 }
1806                 try = 0;
1807                 count -= n;
1808                 lba   += n;
1809                 data += n * u->secsize;
1810                 if(count == 0)
1811                         return data - (uchar*)a;
1812         }
1813         print("%s: bad disk\n", dnam(d));
1814         return -1;
1815 }
1816
1817 static int
1818 iario(SDreq *r)
1819 {
1820         int i, n, count, rw;
1821         uchar *cmd;
1822         uvlong lba;
1823         Ctlr *c;
1824         Drive *d;
1825         SDunit *u;
1826
1827         u = r->unit;
1828         c = u->dev->ctlr;
1829         d = c->drive[u->subno];
1830         if(d->portm.feat & Datapi)
1831                 return iariopkt(r, d);
1832         cmd = r->cmd;
1833
1834         if(cmd[0] == 0x35 || cmd[0] == 0x91){
1835                 if(flushcache(d) == 0)
1836                         return sdsetsense(r, SDok, 0, 0, 0);
1837                 return sdsetsense(r, SDcheck, 3, 0xc, 2);
1838         }
1839
1840         if((i = sdfakescsi(r)) != SDnostatus){
1841                 r->status = i;
1842                 return i;
1843         }
1844
1845         if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
1846                 return i;
1847         n = ahcibio(u, r->lun, r->write, r->data, count, lba);
1848         if(n == -1)
1849                 return SDeio;
1850         r->rlen = n;
1851         return SDok;
1852 }
1853
1854 static uchar bogusrfis[16] = {
1855 [Ftype]         0x34,
1856 [Fioport]       0x40,
1857 [Fstatus]       0x50,
1858 [Fdev]          0xa0,
1859 };
1860
1861 static void
1862 sdr0(Drive *d)
1863 {
1864         uchar *c;
1865
1866         c = d->portm.fis.r;
1867         memmove(c, bogusrfis, sizeof bogusrfis);
1868         coherence();
1869 }
1870
1871 static int
1872 sdr(SDreq *r, Drive *d, int st)
1873 {
1874         uchar *c;
1875         uint t;
1876
1877         if((r->ataproto & Pprotom) == Ppkt){
1878                 t = d->port->task;
1879                 if(t & ASerr)
1880                         st = t >> 8 + 4 & 0xf;
1881         }
1882         c = d->portm.fis.r;
1883         memmove(r->cmd, c, 16);
1884         r->status = st;
1885         if(st == SDcheck)
1886                 st = SDok;
1887         return st;
1888 }
1889
1890 static int
1891 fisreqchk(Sfis *f, SDreq *r)
1892 {
1893         if((r->ataproto & Pprotom) == Ppkt)
1894                 return SDnostatus;
1895         /*
1896          * handle oob requests;
1897          *    restrict & sanitize commands
1898          */
1899         if(r->clen != 16)
1900                 error(Eio);
1901         if(r->cmd[0] == 0xf0){
1902                 sigtofis(f, r->cmd);
1903                 r->status = SDok;
1904                 return SDok;
1905         }
1906         r->cmd[0] = 0x27;
1907         r->cmd[1] = 0x80;
1908         r->cmd[7] |= 0xa0;
1909         return SDnostatus;
1910 }
1911
1912 static int
1913 iaataio(SDreq *r)
1914 {
1915         int try;
1916         Ctlr *c;
1917         Drive *d;
1918         SDunit *u;
1919         Alist *l;
1920
1921         u = r->unit;
1922         c = u->dev->ctlr;
1923         d = c->drive[u->subno];
1924
1925         if((r->status = fisreqchk(&d->portm, r)) != SDnostatus)
1926                 return r->status;
1927         r->rlen = 0;
1928         sdr0(d);
1929         for(try = 0; try < 10; try++){
1930                 qlock(&d->portm);
1931                 l = ahcibuildfis(d, r, r->data, r->dlen);
1932                 r->status = io(d, r->ataproto & Pprotom, -1, 1);
1933                 switch(r->status){
1934                 case SDtimeout:
1935                         qunlock(&d->portm);
1936                         return sdsetsense(r, SDcheck, 11, 0, 6);
1937                 case SDeio:
1938                         qunlock(&d->portm);
1939                         return SDeio;
1940                 case SDretry:
1941                         qunlock(&d->portm);
1942                         continue;
1943                 }
1944                 r->rlen = (r->ataproto & Pprotom) == Ppkt ? l->len : r->dlen;
1945                 try = sdr(r, d, r->status);
1946                 qunlock(&d->portm);
1947                 return try;
1948         }
1949         print("%s: bad disk\n", dnam(d));
1950         return r->status = SDeio;
1951 }
1952
1953 /*
1954  * configure drives 0-5 as ahci sata  (c.f. errata)
1955  */
1956 static int
1957 iaahcimode(Pcidev *p)
1958 {
1959         uint u;
1960
1961         u = pcicfgr16(p, 0x92);
1962         dprint("ahci: %T: iaahcimode %.2ux %.4ux\n", p->tbdf, pcicfgr8(p, 0x91), u);
1963         pcicfgw16(p, 0x92, u | 0xf);    /* ports 0-15 */
1964         return 0;
1965 }
1966
1967 enum{
1968         Ghc     = 0x04/4,       /* global host control */
1969         Pi      = 0x0c/4,       /* ports implemented */
1970         Cmddec  = 1<<15,        /* enable command block decode */
1971
1972         /* Ghc bits */
1973         Ahcien  = 1<<31,        /* ahci enable */
1974 };
1975
1976 static void
1977 iasetupahci(Ctlr *c)
1978 {
1979         pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~Cmddec);
1980         pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~Cmddec);
1981
1982         c->lmmio[Ghc] |= Ahcien;
1983         c->lmmio[Pi] = (1 << 6) - 1;    /* 5 ports (supposedly ro pi reg) */
1984
1985         /* enable ahci mode; from ich9 datasheet */
1986         pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5);
1987 }
1988
1989 static void
1990 sbsetupahci(Pcidev *p)
1991 {
1992         print("sbsetupahci: tweaking %.4ux ccru %.2ux ccrp %.2ux\n",
1993                 p->did, p->ccru, p->ccrp);
1994         pcicfgw8(p, 0x40, pcicfgr8(p, 0x40) | 1);
1995         pcicfgw8(p, PciCCRu, 6);
1996         pcicfgw8(p, PciCCRp, 1);
1997         p->ccru = 6;
1998         p->ccrp = 1;
1999 }
2000
2001 static int
2002 esbenc(Ctlr *c)
2003 {
2004         c->encsz = 1;
2005         c->enctx = (ulong*)(c->mmio + 0xa0);
2006         c->enctype = Eesb;
2007         c->enctx[0] = 0;
2008         return 0;
2009 }
2010
2011 static int
2012 ahciencinit(Ctlr *c)
2013 {
2014         ulong type, sz, o, *bar;
2015         Ahba *h;
2016
2017         h = c->hba;
2018         if(c->type == Tesb)
2019                 return esbenc(c);
2020         if((h->cap & Hems) == 0)
2021                 return -1;
2022         type = h->emctl & Emtype;
2023         switch(type){
2024         case Esgpio:
2025         case Eses2:
2026         case Esafte:
2027                 return -1;
2028         case Elmt:
2029                 break;
2030         default:
2031                 return -1;
2032         }
2033
2034         sz = h->emloc & 0xffff;
2035         o = h->emloc>>16;
2036         if(sz == 0 || o == 0)
2037                 return -1;
2038         bar = c->lmmio;
2039         dprint("size = %.4lux; loc = %.4lux*4\n", sz, o);
2040         c->encsz = sz;
2041         c->enctx = bar + o;
2042         if((h->emctl & Xonly) == 0){
2043                 if(h->emctl & Smb)
2044                         c->encrx = bar + o;
2045                 else
2046                         c->encrx = bar + o*2;
2047         }
2048         c->enctype = type;
2049         return 0;
2050 }
2051
2052 static int
2053 didtype(Pcidev *p)
2054 {
2055         int type;
2056
2057         type = Tahci;
2058         switch(p->vid){
2059         default:
2060                 return -1;
2061         case 0x8086:
2062                 if((p->did & 0xfffc) == 0x2680)
2063                         return Tesb;
2064                 if((p->did & 0xfffb) == 0x27c1)
2065                         return Tich;            /* 82801g[bh]m */
2066                 if((p->did & 0xffff) == 0x2822)
2067                         return Tich;            /* 82801 SATA RAID */
2068                 if((p->did & 0xffff) == 0x2821)
2069                         return Tich;            /* 82801h[roh] */
2070                 if((p->did & 0xfffe) == 0x2824)
2071                         return Tich;            /* 82801h[b] */
2072                 if((p->did & 0xfeff) == 0x2829)
2073                         return Tich;            /* ich8 */
2074                 if((p->did & 0xfffe) == 0x2922)
2075                         return Tich;            /* ich9 */
2076                 if((p->did & 0xffff)  == 0x3a02)
2077                         return Tich;            /* 82801jd/do */
2078                 if((p->did & 0xfefe)  == 0x3a22)
2079                         return Tich;            /* ich10, pch */
2080                 if((p->did & 0xfff7)  == 0x3b28)
2081                         return Tich;            /* pchm */
2082                 if((p->did & 0xfffe) == 0x3b22)
2083                         return Tich;            /* pch */
2084                 break;
2085         case 0x1002:
2086                 if(p->ccru == 1 || p->ccrp != 1)
2087                 if(p->did == 0x4380 || p->did == 0x4390)
2088                         sbsetupahci(p);
2089                 type = Tsb600;
2090                 break;
2091         case 0x1106:
2092                 /*
2093                  * unconfirmed report that the programming
2094                  * interface is set incorrectly.
2095                  */
2096                 if(p->did == 0x3349)
2097                         return Tahci;
2098                 break;
2099         case 0x1022:
2100                 /* Hudson SATA Controller [AHCI mode] */
2101                 if(p->did == 0x7801)
2102                         return Tahci;
2103                 break;
2104         case 0x10de:
2105         case 0x1039:
2106         case 0x1b4b:
2107         case 0x11ab:
2108                 break;
2109         case 0x197b:
2110         case 0x10b9:
2111                 type = Tjmicron;
2112                 break;
2113         }
2114         if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1)
2115                 return type;
2116         return -1;
2117 }
2118
2119 static SDev*
2120 iapnp(void)
2121 {
2122         int i, n, nunit, type;
2123         ulong io;
2124         Ctlr *c;
2125         Drive *d;
2126         Pcidev *p;
2127         SDev *s;
2128         static int done;
2129
2130         if(done)
2131                 return nil;
2132         done = 1;
2133
2134         if(getconf("*ahcidebug") != nil){
2135                 debug = 1;
2136                 datapi = 1;
2137         }
2138
2139         memset(olds, 0xff, sizeof olds);
2140         p = nil;
2141         while((p = pcimatch(p, 0, 0)) != nil){
2142                 if((type = didtype(p)) == -1)
2143                         continue;
2144                 if(p->mem[Abar].bar == 0)
2145                         continue;
2146                 if(niactlr == NCtlr){
2147                         print("iapnp: %s: too many controllers\n", tname[type]);
2148                         break;
2149                 }
2150                 c = iactlr + niactlr;
2151                 s = sdevs + niactlr;
2152                 memset(c, 0, sizeof *c);
2153                 memset(s, 0, sizeof *s);
2154                 io = p->mem[Abar].bar & ~0xf;
2155                 c->mmio = vmap(io, p->mem[Abar].size);
2156                 if(c->mmio == 0){
2157                         print("%s: address %#p in use did %.4ux\n",
2158                                 Tname(c), io, p->did);
2159                         continue;
2160                 }
2161                 c->lmmio = (ulong*)c->mmio;
2162                 c->pci = p;
2163                 c->type = type;
2164
2165                 s->ifc = &sdiahciifc;
2166                 s->idno = 'E';
2167                 s->ctlr = c;
2168                 c->sdev = s;
2169
2170                 ahcihandoff((Ahba*)c->mmio);
2171                 if(intel(c) && p->did != 0x2681)
2172                         iasetupahci(c);
2173 //              ahcihbareset((Ahba*)c->mmio);
2174                 nunit = ahciconf(c);
2175                 if(intel(c) && iaahcimode(p) == -1 || nunit < 1){
2176                         vunmap(c->mmio, p->mem[Abar].size);
2177                         continue;
2178                 }
2179                 c->ndrive = s->nunit = nunit;
2180
2181                 /* map the drives -- they don't all need to be enabled. */
2182                 memset(c->rawdrive, 0, sizeof c->rawdrive);
2183                 n = 0;
2184                 for(i = 0; i < NCtlrdrv; i++){
2185                         d = c->rawdrive + i;
2186                         d->portno = i;
2187                         d->driveno = -1;
2188                         d->sectors = 0;
2189                         d->serial[0] = ' ';
2190                         d->ctlr = c;
2191                         if((c->hba->pi & 1<<i) == 0)
2192                                 continue;
2193                         snprint(d->name, sizeof d->name, "iahci%d.%d", niactlr, i);
2194                         d->port = (Aport*)(c->mmio + 0x80*i + 0x100);
2195                         d->portc.p = d->port;
2196                         d->portc.m = &d->portm;
2197                         d->driveno = n++;
2198                         c->drive[d->driveno] = d;
2199                         iadrive[niadrive + d->driveno] = d;
2200                 }
2201                 for(i = 0; i < n; i++){
2202                         c->drive[i]->mode = DMautoneg;
2203                         configdrive(c->drive[i]);
2204                 }
2205                 ahciencinit(c);
2206
2207                 niadrive += n;
2208                 niactlr++;
2209                 sdadddevs(s);
2210                 i = (c->hba->cap >> 21) & 1;
2211                 print("#S/%s: %s: sata-%s with %d ports\n", s->name,
2212                         Tname(c), "I\0II" + i*2, nunit);
2213         }
2214         return nil;
2215 }
2216
2217 static Htab ctab[] = {
2218         Aasp,   "asp",
2219         Aalpe , "alpe ",
2220         Adlae,  "dlae",
2221         Aatapi, "atapi",
2222         Apste,  "pste",
2223         Afbsc,  "fbsc",
2224         Aesp,   "esp",
2225         Acpd,   "cpd",
2226         Ampsp,  "mpsp",
2227         Ahpcp,  "hpcp",
2228         Apma,   "pma",
2229         Acps,   "cps",
2230         Acr,    "cr",
2231         Afr,    "fr",
2232         Ampss,  "mpss",
2233         Apod,   "pod",
2234         Asud,   "sud",
2235         Ast,    "st",
2236 };
2237
2238 static char*
2239 capfmt(char *p, char *e, Htab *t, int n, ulong cap)
2240 {
2241         uint i;
2242
2243         *p = 0;
2244         for(i = 0; i < n; i++)
2245                 if(cap & t[i].bit)
2246                         p = seprint(p, e, "%s ", t[i].name);
2247         return p;
2248 }
2249
2250 static int
2251 iarctl(SDunit *u, char *p, int l)
2252 {
2253         char buf[32], *e, *op;
2254         Aport *o;
2255         Ctlr *c;
2256         Drive *d;
2257
2258         if((c = u->dev->ctlr) == nil)
2259                 return 0;
2260         d = c->drive[u->subno];
2261         o = d->port;
2262
2263         e = p+l;
2264         op = p;
2265         if(d->state == Dready){
2266                 p = seprint(p, e, "model\t%s\n", d->model);
2267                 p = seprint(p, e, "serial\t%s\n", d->serial);
2268                 p = seprint(p, e, "firm\t%s\n", d->firmware);
2269                 if(d->wwn != 0)
2270                         p = seprint(p, e, "wwn\t%ullx\n", d->wwn);
2271                 p = seprint(p, e, "flag\t");
2272                 p = pflag(p, e, &d->portm);
2273                 p = seprint(p, e, "udma\t%d\n", d->portm.udma);
2274         }else
2275                 p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]);
2276         serrstr(o->serror, buf, buf + sizeof buf - 1);
2277         p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux "
2278                 "sig %lux sstatus %.3lux\n", o->task, o->cmd, o->serror, buf,
2279                 o->ci, o->isr, o->sig, o->sstatus);
2280         p = seprint(p, e, "cmd\t");
2281         p = capfmt(p, e, ctab, nelem(ctab), o->cmd);
2282         p = seprint(p, e, "\n");
2283         p = seprint(p, e, "mode\t%s %s\n", modes[d->mode], modes[maxmode(c)]);
2284         p = seprint(p, e, "geometry %llud %lud\n", d->sectors, u->secsize);
2285         p = seprint(p, e, "alignment %d %d\n", 
2286                 d->secsize<<d->portm.physshift, d->portm.physalign);
2287         p = seprint(p, e, "missirq\t%ud\n", c->missirq);
2288         return p - op;
2289 }
2290
2291 static void
2292 forcemode(Drive *d, char *mode)
2293 {
2294         int i;
2295
2296         for(i = 0; i < nelem(modes); i++)
2297                 if(strcmp(mode, modes[i]) == 0)
2298                         break;
2299         if(i == nelem(modes))
2300                 i = 0;
2301         ilock(d);
2302         d->mode = i;
2303         iunlock(d);
2304 }
2305
2306 static void
2307 forcestate(Drive *d, char *state)
2308 {
2309         int i;
2310
2311         for(i = 0; i < nelem(diskstates); i++)
2312                 if(strcmp(state, diskstates[i]) == 0)
2313                         break;
2314         if(i == nelem(diskstates))
2315                 error(Ebadctl);
2316         dstatus(d, i);
2317 }
2318
2319 static int
2320 runsettxmode(Drive *d, char *s)
2321 {
2322         int i;
2323         Aportc *c;
2324         Aportm *m;
2325
2326         c = &d->portc;
2327         m = &d->portm;
2328
2329         i = 1;
2330         if(lockready(d) == 0){
2331                 m->udma = atoi(s);
2332                 if(settxmode(c, m->udma) == 0)
2333                         i = 0;
2334         }
2335         qunlock(m);
2336         return i;
2337 }
2338
2339
2340 static int
2341 iawctl(SDunit *u, Cmdbuf *cmd)
2342 {
2343         char **f;
2344         Ctlr *c;
2345         Drive *d;
2346
2347         c = u->dev->ctlr;
2348         d = c->drive[u->subno];
2349         f = cmd->f;
2350
2351         if(strcmp(f[0], "mode") == 0)
2352                 forcemode(d, f[1]? f[1]: "satai");
2353         else if(strcmp(f[0], "state") == 0)
2354                 forcestate(d, f[1]? f[1]: "null");
2355         else if(strcmp(f[0], "txmode") == 0){
2356                 if(runsettxmode(d, f[1]? f[1]: "0"))
2357                         cmderror(cmd, "bad txmode / stuck port");
2358         }else
2359                 cmderror(cmd, Ebadctl);
2360         return 0;
2361 }
2362
2363 static char *
2364 portr(char *p, char *e, uint x)
2365 {
2366         int i, a;
2367
2368         p[0] = 0;
2369         a = -1;
2370         for(i = 0; i < 32; i++){
2371                 if((x & (1<<i)) == 0){
2372                         if(a != -1 && i - 1 != a)
2373                                 p = seprint(p, e, "-%d", i - 1);
2374                         a = -1;
2375                         continue;
2376                 }
2377                 if(a == -1){
2378                         if(i > 0)
2379                                 p = seprint(p, e, ", ");
2380                         p = seprint(p, e, "%d", a = i);
2381                 }
2382         }
2383         if(a != -1 && i - 1 != a)
2384                 p = seprint(p, e, "-%d", i - 1);
2385         return p;
2386 }
2387
2388 static Htab htab[] = {
2389         H64a,   "64a",
2390         Hncq,   "ncq",
2391         Hsntf,  "ntf",
2392         Hmps,   "mps",
2393         Hss,    "ss",
2394         Halp,   "alp",
2395         Hal,    "led",
2396         Hclo,   "clo",
2397         Ham,    "am",
2398         Hpm,    "pm",
2399         Hfbs,   "fbs",
2400         Hpmb,   "pmb",
2401         Hssc,   "slum",
2402         Hpsc,   "pslum",
2403         Hcccs,  "coal",
2404         Hems,   "ems",
2405         Hxs,    "xs",
2406 };
2407
2408 static Htab htab2[] = {
2409         Apts,   "apts",
2410         Nvmp,   "nvmp",
2411         Boh,    "boh",
2412 };
2413
2414 static Htab emtab[] = {
2415         Pm,     "pm",
2416         Alhd,   "alhd",
2417         Xonly,  "xonly",
2418         Smb,    "smb",
2419         Esgpio, "esgpio",
2420         Eses2,  "eses2",
2421         Esafte, "esafte",
2422         Elmt,   "elmt",
2423 };
2424
2425 static char*
2426 iartopctl(SDev *s, char *p, char *e)
2427 {
2428         char pr[25];
2429         ulong cap;
2430         Ahba *h;
2431         Ctlr *c;
2432
2433         c = s->ctlr;
2434         h = c->hba;
2435         cap = h->cap;
2436         p = seprint(p, e, "sd%c ahci %s port %#p: ", s->idno, Tname(c), h);
2437         p = capfmt(p, e, htab, nelem(htab), cap);
2438         p = capfmt(p, e, htab2, nelem(htab2), h->cap2);
2439         p = capfmt(p, e, emtab, nelem(emtab), h->emctl);
2440         portr(pr, pr + sizeof pr, h->pi);
2441         return seprint(p, e,
2442                 "iss %ld ncs %ld np %ld ghc %lux isr %lux pi %lux %s ver %lux\n",
2443                 (cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f),
2444                 h->ghc, h->isr, h->pi, pr, h->ver);
2445 }
2446
2447 static int
2448 iawtopctl(SDev *, Cmdbuf *cmd)
2449 {
2450         int *v;
2451         char **f;
2452
2453         f = cmd->f;
2454         v = 0;
2455
2456         if(strcmp(f[0], "debug") == 0)
2457                 v = &debug;
2458         else if(strcmp(f[0], "idprint") == 0)
2459                 v = &prid;
2460         else if(strcmp(f[0], "aprint") == 0)
2461                 v = &datapi;
2462         else if(strcmp(f[0], "ledprint") == 0)
2463                 v = &dled;
2464         else
2465                 cmderror(cmd, Ebadctl);
2466
2467         switch(cmd->nf){
2468         default:
2469                 cmderror(cmd, Ebadarg);
2470         case 1:
2471                 *v ^= 1;
2472                 break;
2473         case 2:
2474                 if(f[1])
2475                         *v = strcmp(f[1], "on") == 0;
2476                 else
2477                         *v ^= 1;
2478                 break;
2479         }
2480         return 0;
2481 }
2482
2483 SDifc sdiahciifc = {
2484         "ahci",
2485
2486         iapnp,
2487         nil,            /* legacy */
2488         iaenable,
2489         iadisable,
2490
2491         iaverify,
2492         iaonline,
2493         iario,
2494         iarctl,
2495         iawctl,
2496
2497         ahcibio,
2498         nil,            /* probe */
2499         nil,            /* clear */
2500         iartopctl,
2501         iawtopctl,
2502         iaataio,
2503 };