]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/sdiahci.c
audiohda: first attempt on audio recording support for intel hda audio, distinguish...
[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                 return;
907         }
908
909         ilock(d);
910         switch(d->port->sstatus & Smask){
911         case Smissing:
912                 d->state = Dmissing;
913                 break;
914         case Spresent:
915                 if(d->state == Dnull)
916                         d->state = Dportreset;
917                 break;
918         case Sphylink:
919                 if(d->state == Dready)
920                         break;
921                 d->wait = 0;
922                 d->state = Dnew;
923                 break;
924         case Sbist:
925                 d->state = Doffline;
926                 break;
927         }
928         iunlock(d);
929
930         dprint("%s: configdrive: %s\n", dnam(d), diskstates[d->state]);
931 }
932
933 static void
934 resetdisk(Drive *d)
935 {
936         uint state, det, stat;
937         Aport *p;
938
939         p = d->port;
940         det = p->sctl & 7;
941         stat = p->sstatus & Smask;
942         state = (p->cmd>>28) & 0xf;
943         dprint("%s: resetdisk [icc %ux; det %.3ux; sdet %.3ux]\n", dnam(d), state, det, stat);
944
945         ilock(d);
946         if(d->state != Dready && d->state != Dnew)
947                 d->portm.flag |= Ferror;
948         if(stat != Sphylink)
949                 d->state = Dportreset;
950         else
951                 d->state = Dreset;
952         clearci(p);                     /* satisfy sleep condition. */
953         wakeup(&d->portm);
954         iunlock(d);
955
956         if(stat != Sphylink)
957                 return;
958
959         qlock(&d->portm);
960         if(p->cmd&Ast && ahciswreset(&d->portc) == -1)
961                 dstatus(d, Dportreset); /* get a bigger stick. */
962         else
963                 configdrive(d);
964         qunlock(&d->portm);
965 }
966
967 static int
968 newdrive(Drive *d)
969 {
970         char *s;
971         Aportc *c;
972         Aportm *m;
973
974         c = &d->portc;
975         m = &d->portm;
976
977         qlock(c->m);
978         setfissig(m, c->p->sig);
979         if(identify(d) == -1){
980                 dprint("%s: identify failure\n", dnam(d));
981                 goto lose;
982         }
983         if(settxmode(c, m->udma) == -1){
984                 dprint("%s: can't set udma mode\n", dnam(d));
985                 goto lose;
986         }
987         if(m->feat & Dpower && setfeatures(c, 0x85, 3*1000) == -1){
988                 dprint("%s: can't disable apm\n", dnam(d));
989                 m->feat &= ~Dpower;
990                 if(ahcirecover(c) == -1)
991                         goto lose;
992         }
993         dstatus(d, Dready);
994         qunlock(c->m);
995
996         s = "";
997         if(m->feat & Dllba)
998                 s = "L";
999         idprint("%s: %sLBA %,lld sectors\n", dnam(d), s, d->sectors);
1000         idprint("  %s %s %s %s\n", d->model, d->firmware, d->serial,
1001                 d->drivechange? "[newdrive]": "");
1002         return 0;
1003
1004 lose:
1005         idprint("%s: can't be initialized\n", dnam(d));
1006         dstatus(d, Dnull);
1007         qunlock(c->m);
1008         return -1;
1009 }
1010
1011 enum {
1012         Nms             = 256,
1013         Mphywait        =  2*1024/Nms - 1,
1014         Midwait         = 16*1024/Nms - 1,
1015         Mcomrwait       = 64*1024/Nms - 1,
1016 };
1017
1018 static void
1019 hangck(Drive *d)
1020 {
1021         if(d->active && d->totick != 0 && (long)(Ticks - d->totick) > 0){
1022                 dprint("%s: drive hung [task %lux; ci %lux; serr %lux]%s\n",
1023                         dnam(d), d->port->task, d->port->ci, d->port->serror,
1024                         d->nodma == 0 ? "; disabling dma" : "");
1025                 d->nodma = 1;
1026                 d->state = Dreset;
1027         }
1028 }
1029
1030 static ushort olds[NCtlr*NCtlrdrv];
1031
1032 static void
1033 doportreset(Drive *d)
1034 {
1035         qlock(&d->portm);
1036         ahciportreset(&d->portc, d->mode);
1037         qunlock(&d->portm);
1038
1039         dprint("ahci: portreset: %s [task %lux; ss %.3lux]\n",
1040                 diskstates[d->state], d->port->task, d->port->sstatus);
1041 }
1042
1043 /* drive must be locked */
1044 static void
1045 statechange(Drive *d)
1046 {
1047         switch(d->state){
1048         case Dnull:
1049         case Doffline:
1050                 if(d->unit)
1051                 if(d->unit->sectors != 0){
1052                         d->sectors = 0;
1053                         d->drivechange = 1;
1054                 }
1055         case Dready:
1056                 d->wait = 0;
1057         }
1058 }
1059
1060 static uint
1061 maxmode(Ctlr *c)
1062 {
1063         return (c->hba->cap & 0xf*Hiss)/Hiss;
1064 }
1065
1066 static void iainterrupt(Ureg*, void *);
1067
1068 static void
1069 checkdrive(Drive *d, int i)
1070 {
1071         ushort s, sig;
1072
1073         if(d->ctlr->enabled == 0)
1074                 return;
1075         if(d->driveno == 0)
1076                 iainterrupt(0, d->ctlr);        /* check for missed irq's */
1077
1078         ilock(d);
1079         s = d->port->sstatus;
1080         if(s)
1081                 d->lastseen = Ticks;
1082         if(s != olds[i]){
1083                 dprint("%s: status: %.3ux -> %.3ux: %s\n",
1084                         dnam(d), olds[i], s, diskstates[d->state]);
1085                 olds[i] = s;
1086                 d->wait = 0;
1087         }
1088         hangck(d);
1089         switch(d->state){
1090         case Dnull:
1091         case Dready:
1092                 break;
1093         case Dmissing:
1094         case Dnew:
1095                 switch(s & (Iactive|Smask)){
1096                 case Spresent:
1097                         ahciwakeup(&d->portc, d->mode);
1098                 case Smissing:
1099                         break;
1100                 default:
1101                         dprint("%s: unknown status %.3ux\n", dnam(d), s);
1102                         /* fall through */
1103                 case Iactive:           /* active, no device */
1104                         if(++d->wait&Mphywait)
1105                                 break;
1106 reset:
1107                         if(d->mode == 0)
1108                                 d->mode = maxmode(d->ctlr);
1109                         else
1110                                 d->mode--;
1111                         if(d->mode == DMautoneg){
1112                                 d->state = Dportreset;
1113                                 goto portreset;
1114                         }
1115                         dprint("%s: reset; new mode %s\n", dnam(d),
1116                                 modes[d->mode]);
1117                         iunlock(d);
1118                         resetdisk(d);
1119                         ilock(d);
1120                         break;
1121                 case Iactive | Sphylink:
1122                         if(d->unit == nil)
1123                                 break;
1124                         if((++d->wait&Midwait) == 0){
1125                                 dprint("%s: slow reset [task %lux; ss %.3ux; wait %d]\n",
1126                                         dnam(d), d->port->task, s, d->wait);
1127                                 goto reset;
1128                         }
1129                         s = (uchar)d->port->task;
1130                         sig = d->port->sig >> 16;
1131                         if(s == 0x7f || s&ASbsy ||
1132                             (sig != 0xeb14 && (s & ASdrdy) == 0))
1133                                 break;
1134                         iunlock(d);
1135                         newdrive(d);
1136                         ilock(d);
1137                         break;
1138                 }
1139                 break;
1140         case Doffline:
1141                 if(d->wait++ & Mcomrwait)
1142                         break;
1143                 /* fallthrough */
1144         case Derror:
1145         case Dreset:
1146                 dprint("%s: reset [%s]: mode %d; status %.3ux\n",
1147                         dnam(d), diskstates[d->state], d->mode, s);
1148                 iunlock(d);
1149                 resetdisk(d);
1150                 ilock(d);
1151                 break;
1152         case Dportreset:
1153 portreset:
1154                 if(d->wait++ & 0xff && (s & Iactive) == 0)
1155                         break;
1156                 dprint("%s: portreset [%s]: mode %d; status %.3ux\n",
1157                         dnam(d), diskstates[d->state], d->mode, s);
1158                 d->portm.flag |= Ferror;
1159                 clearci(d->port);
1160                 wakeup(&d->portm);
1161                 if((s & Smask) == Smissing){
1162                         d->state = Dmissing;
1163                         break;
1164                 }
1165                 iunlock(d);
1166                 doportreset(d);
1167                 ilock(d);
1168                 break;
1169         }
1170         statechange(d);
1171         iunlock(d);
1172 }
1173
1174 static void
1175 satakproc(void*)
1176 {
1177         int i;
1178
1179         for(;;){
1180                 tsleep(&up->sleep, return0, 0, Nms);
1181                 for(i = 0; i < niadrive; i++)
1182                         checkdrive(iadrive[i], i);
1183         }
1184 }
1185
1186 static void
1187 iainterrupt(Ureg *u, void *a)
1188 {
1189         int i;
1190         ulong cause, m;
1191         Ctlr *c;
1192         Drive *d;
1193
1194         c = a;
1195         ilock(c);
1196         cause = c->hba->isr;
1197         for(i = 0; cause; i++){
1198                 m = 1 << i;
1199                 if((cause & m) == 0)
1200                         continue;
1201                 cause &= ~m;
1202                 d = c->rawdrive + i;
1203                 ilock(d);
1204                 if(d->port->isr && c->hba->pi & m)
1205                         updatedrive(d);
1206                 c->hba->isr = m;
1207                 iunlock(d);
1208         }
1209         if(u == 0 && i > 0)
1210                 c->missirq++;
1211         iunlock(c);
1212 }
1213
1214 static int
1215 ahciencreset(Ctlr *c)
1216 {
1217         Ahba *h;
1218
1219         if(c->enctype == Eesb)
1220                 return 0;
1221         h = c->hba;
1222         h->emctl |= Emrst;
1223         while(h->emctl & Emrst)
1224                 delay(1);
1225         return 0;
1226 }
1227
1228 /*
1229  * from the standard: (http://en.wikipedia.org/wiki/IBPI)
1230  * rebuild is preferred as locate+fail; alternate 1hz fail
1231  * we're going to assume no locate led.
1232  */
1233
1234 enum {
1235         Ledsleep        = 125,          /* 8hz */
1236
1237         N0      = Ledon*Aled,
1238         L0      = Ledon*Aled | Ledon*Locled,
1239         L1      = Ledon*Aled | Ledoff*Locled,
1240         R0      = Ledon*Aled | Ledon*Locled |   Ledon*Errled,
1241         R1      = Ledon*Aled |                  Ledoff*Errled,
1242         S0      = Ledon*Aled |  Ledon*Locled /*|        Ledon*Errled*/, /* botch */
1243         S1      = Ledon*Aled |                  Ledoff*Errled,
1244         P0      = Ledon*Aled |                  Ledon*Errled,
1245         P1      = Ledon*Aled |                  Ledoff*Errled,
1246         F0      = Ledon*Aled |                  Ledon*Errled,
1247         C0      = Ledon*Aled | Ledon*Locled,
1248         C1      = Ledon*Aled | Ledoff*Locled,
1249
1250 };
1251
1252 //static ushort led3[Ibpilast*8] = {
1253 //[Ibpinone*8]  0,      0,      0,      0,      0,      0,      0,      0,
1254 //[Ibpinormal*8]        N0,     N0,     N0,     N0,     N0,     N0,     N0,     N0,
1255 //[Ibpirebuild*8]       R0,     R0,     R0,     R0,     R1,     R1,     R1,     R1,
1256 //[Ibpilocate*8]        L0,     L1,     L0,     L1,     L0,     L1,     L0,     L1,
1257 //[Ibpispare*8] S0,     S1,     S0,     S1,     S1,     S1,     S1,     S1,
1258 //[Ibpipfa*8]   P0,     P1,     P0,     P1,     P1,     P1,     P1,     P1,     /* first 1 sec */
1259 //[Ibpifail*8]  F0,     F0,     F0,     F0,     F0,     F0,     F0,     F0,
1260 //[Ibpicritarray*8]     C0,     C0,     C0,     C0,     C1,     C1,     C1,     C1,
1261 //[Ibpifailarray*8]     C0,     C1,     C0,     C1,     C0,     C1,     C0,     C1,
1262 //};
1263
1264 static ushort led2[Ibpilast*8] = {
1265 [Ibpinone*8]    0,      0,      0,      0,      0,      0,      0,      0,
1266 [Ibpinormal*8]  N0,     N0,     N0,     N0,     N0,     N0,     N0,     N0,
1267 [Ibpirebuild*8] R0,     R0,     R0,     R0,     R1,     R1,     R1,     R1,
1268 [Ibpilocate*8]  L0,     L0,     L0,     L0,     L0,     L0,     L0,     L0,
1269 [Ibpispare*8]   S0,     S0,     S0,     S0,     S1,     S1,     S1,     S1,
1270 [Ibpipfa*8]     P0,     P1,     P0,     P1,     P1,     P1,     P1,     P1,     /* first 1 sec */
1271 [Ibpifail*8]    F0,     F0,     F0,     F0,     F0,     F0,     F0,     F0,
1272 [Ibpicritarray*8]       C0,     C0,     C0,     C0,     C1,     C1,     C1,     C1,
1273 [Ibpifailarray*8]       C0,     C1,     C0,     C1,     C0,     C1,     C0,     C1,
1274 };
1275
1276 static int
1277 ledstate(Ledport *p, uint seq)
1278 {
1279         ushort i;
1280
1281         if(p->led == Ibpipfa && seq%32 >= 8)
1282                 i = P1;
1283         else
1284                 i = led2[8*p->led + seq%8];
1285         if(i != p->ledbits){
1286                 p->ledbits = i;
1287                 ledprint("ledstate %,.011ub %ud\n", p->ledbits, seq);
1288                 return 1;
1289         }
1290         return 0;
1291 }
1292
1293 static int
1294 blink(Drive *d, ulong t)
1295 {
1296         Ahba *h;
1297         Ctlr *c;
1298         Aledmsg msg;
1299
1300         if(ledstate(d, t) == 0)
1301                 return 0;
1302         c = d->ctlr;
1303         h = c->hba;
1304         /* ensure last message has been transmitted */
1305         while(h->emctl & Tmsg)
1306                 microdelay(1);
1307         switch(c->enctype){
1308         default:
1309                 panic("%s: bad led type %d", dnam(d), c->enctype);
1310         case Elmt:
1311                 memset(&msg, 0, sizeof msg);
1312                 msg.type = Mled;
1313                 msg.dsize = 0;
1314                 msg.msize = sizeof msg - 4;
1315                 msg.led[0] = d->ledbits;
1316                 msg.led[1] = d->ledbits>>8;
1317                 msg.pm = 0;
1318                 msg.hba = d->driveno;
1319                 memmove(c->enctx, &msg, sizeof msg);
1320                 break;
1321         }
1322         h->emctl |= Tmsg;
1323         return 1;
1324 }
1325
1326 enum {
1327         Esbdrv0 = 4,            /* start pos in bits */
1328         Esbiota = 3,            /* shift in bits */
1329         Esbact  = 1,
1330         Esbloc  = 2,
1331         Esberr  = 4,
1332 };
1333
1334 uint
1335 esbbits(uint s)
1336 {
1337         uint i, e;                              /* except after c */
1338
1339         e = 0;
1340         for(i = 0; i < 3; i++)
1341                 e |= ((s>>3*i & 7) != 0)<<i;
1342         return e;
1343 }
1344
1345 static int
1346 blinkesb(Ctlr *c, ulong t)
1347 {
1348         uint i, s, u[32/4];
1349         uvlong v;
1350         Drive *d;
1351
1352         s = 0;
1353         for(i = 0; i < c->ndrive; i++){
1354                 d = c->drive[i];
1355                 s |= ledstate(d, t);            /* no port mapping */
1356         }
1357         if(s == 0)
1358                 return 0;
1359         memset(u, 0, sizeof u);
1360         for(i = 0; i < c->ndrive; i++){
1361                 d = c->drive[i];
1362                 s = Esbdrv0 + Esbiota*i;
1363                 v = esbbits(d->ledbits) * (1ull << s%32);
1364                 u[s/32 + 0] |= v;
1365                 u[s/32 + 1] |= v>>32;
1366         }
1367         for(i = 0; i < c->encsz; i++)
1368                 c->enctx[i] = u[i];
1369         return 1;
1370 }
1371
1372 static long
1373 ahciledr(SDunit *u, Chan *ch, void *a, long n, vlong off)
1374 {
1375         Ctlr *c;
1376         Drive *d;
1377
1378         c = u->dev->ctlr;
1379         d = c->drive[u->subno];
1380         return ledr(d, ch, a, n, off);
1381 }
1382
1383 static long
1384 ahciledw(SDunit *u, Chan *ch, void *a, long n, vlong off)
1385 {
1386         Ctlr *c;
1387         Drive *d;
1388
1389         c = u->dev->ctlr;
1390         d = c->drive[u->subno];
1391         return ledw(d, ch, a, n, off);
1392 }
1393
1394 static void
1395 ledkproc(void*)
1396 {
1397         uchar map[NCtlr];
1398         uint i, j, t0, t1;
1399         Ctlr *c;
1400         Drive *d;
1401
1402         j = 0;
1403         memset(map, 0, sizeof map);
1404         for(i = 0; i < niactlr; i++)
1405                 if(iactlr[i].enctype != 0){
1406                         ahciencreset(iactlr + i);
1407                         map[i] = 1;
1408                         j++;
1409                 }
1410         if(j == 0)
1411                 pexit("no work", 1);
1412         for(i = 0; i < niadrive; i++){
1413                 iadrive[i]->nled = 3;           /* hardcoded */
1414                 if(iadrive[i]->ctlr->enctype == Eesb)
1415                         iadrive[i]->nled = 3;
1416                 iadrive[i]->ledbits = -1;
1417         }
1418         for(i = 0; ; i++){
1419                 t0 = Ticks;
1420                 for(j = 0; j < niadrive; ){
1421                         c = iadrive[j]->ctlr;
1422                         if(map[j] == 0)
1423                                 j += c->enctype;
1424                         else if(c->enctype == Eesb){
1425                                 blinkesb(c, i);
1426                                 j += c->ndrive;
1427                         }else{
1428                                 d = iadrive[j++];
1429                                 blink(d, i);
1430                         }
1431                 }
1432                 t1 = Ticks;
1433                 esleep(Ledsleep - TK2MS(t1 - t0));
1434         }
1435 }
1436
1437 static int
1438 waitready(Drive *d)
1439 {
1440         ulong s, i, δ;
1441
1442         for(i = 0;; i += 250){
1443                 if(d->state == Dreset || d->state == Dportreset || d->state == Dnew)
1444                         return 1;
1445                 ilock(d);
1446                 s = d->port->sstatus;
1447                 if(d->state == Dready && (s & Smask) == Sphylink){
1448                         iunlock(d);
1449                         return 0;
1450                 }
1451                 δ = Ticks - d->lastseen;
1452                 if(d->state == Dnull || δ > 10*1000)
1453                         break;
1454                 if((s & Imask) == 0 && δ > 1500)
1455                         break;
1456                 if(i >= 15*1000){
1457                         d->state = Doffline;
1458                         iunlock(d);
1459                         print("%s: not responding; offline\n", dnam(d));
1460                         return -1;
1461                 }
1462                 iunlock(d);
1463                 esleep(250);
1464         }
1465         iunlock(d);
1466         return -1;
1467 }
1468
1469 static int
1470 iaverify(SDunit *u)
1471 {
1472         Ctlr *c;
1473         Drive *d;
1474
1475         c = u->dev->ctlr;
1476         d = c->drive[u->subno];
1477         ilock(c);
1478         ilock(d);
1479         if(d->unit == nil){
1480                 d->unit = u;
1481                 if(c->enctype != 0)
1482                         sdaddfile(u, "led", 0644, eve, ahciledr, ahciledw);
1483         }
1484         iunlock(d);
1485         iunlock(c);
1486         checkdrive(d, d->driveno);              /* c->d0 + d->driveno */
1487         return 1;
1488 }
1489
1490 static int
1491 iaonline(SDunit *u)
1492 {
1493         int r;
1494         Ctlr *c;
1495         Drive *d;
1496
1497         c = u->dev->ctlr;
1498         d = c->drive[u->subno];
1499
1500         while(waitready(d) == 1)
1501                 esleep(1);
1502
1503         dprint("%s: iaonline: %s\n", dnam(d), diskstates[d->state]);
1504
1505         ilock(d);
1506         if(d->portm.feat & Datapi){
1507                 r = d->drivechange;
1508                 d->drivechange = 0;
1509                 iunlock(d);
1510                 if(r != 0)
1511                         scsiverify(u);
1512                 return scsionline(u);
1513         }
1514         r = 0;
1515         if(d->drivechange){
1516                 d->drivechange = 0;
1517                 r = 2;
1518         }else if(d->state == Dready)
1519                 r = 1;
1520         if(r){
1521                 u->sectors = d->sectors;
1522                 u->secsize = d->secsize;
1523         }
1524         iunlock(d);
1525
1526         return r;
1527 }
1528
1529 static int
1530 iaenable(SDev *s)
1531 {
1532         char name[32];
1533         Ctlr *c;
1534         static int once;
1535
1536         c = s->ctlr;
1537         ilock(c);
1538         if(!c->enabled){
1539                 if(once == 0)
1540                         kproc("iasata", satakproc, 0);
1541                 if(c->ndrive == 0)
1542                         panic("iaenable: zero s->ctlr->ndrive");
1543                 pcisetbme(c->pci);
1544                 snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1545                 intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1546                 /* supposed to squelch leftover interrupts here. */
1547                 ahcienable(c->hba);
1548                 c->enabled = 1;
1549                 if(++once == niactlr)
1550                         kproc("ialed", ledkproc, 0);
1551         }
1552         iunlock(c);
1553         return 1;
1554 }
1555
1556 static int
1557 iadisable(SDev *s)
1558 {
1559         char name[32];
1560         Ctlr *c;
1561
1562         c = s->ctlr;
1563         ilock(c);
1564         ahcidisable(c->hba);
1565         snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1566         intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1567         c->enabled = 0;
1568         iunlock(c);
1569         return 1;
1570 }
1571
1572 static Alist*
1573 ahcibuild(Drive *d, int rw, void *data, uint n, vlong lba)
1574 {
1575         uchar *c;
1576         uint flags;
1577         Aportm *m;
1578
1579         m = &d->portm;
1580         c = m->ctab->cfis;
1581         rwfis(m, c, rw, n, lba);
1582         flags = Lpref;
1583         if(rw == SDwrite)
1584                 flags |= Lwrite;
1585         return mkalist(m, flags, data, 512*n);
1586 }
1587
1588 static Alist*
1589 ahcibuildpkt(Drive *d, SDreq *r, void *data, int n)
1590 {
1591         uint flags;
1592         Aportm *m;
1593         uchar *c;
1594         Actab *t;
1595
1596         m = &d->portm;
1597         t = m->ctab;
1598         c = t->cfis;
1599
1600         atapirwfis(m, c, r->cmd, r->clen, 0x2000);
1601         if((n & 15) != 0 || d->nodma)
1602                 c[Ffeat] &= ~1; /* use pio */
1603         else if(c[Ffeat] & 1 && d->info[62] & (1<<15))  /* dma direction */
1604                 c[Ffeat] = (c[Ffeat] & ~(1<<2)) | ((r->write == 0) << 2);
1605         flags = Lpref | Latapi;
1606         if(r->write != 0 && data)
1607                 flags |= Lwrite;
1608         return mkalist(m, flags, data, n);
1609 }
1610
1611 static Alist*
1612 ahcibuildfis(Drive *d, SDreq *r, void *data, uint n)
1613 {
1614         uint flags;
1615         uchar *c;
1616         Aportm *m;
1617
1618         if((r->ataproto & Pprotom) == Ppkt)
1619                 return ahcibuildpkt(d, r, data, n);
1620
1621         m = &d->portm;
1622         c = m->ctab->cfis;
1623         memmove(c, r->cmd, r->clen);
1624         flags = Lpref;
1625         if(r->write || n == 0)
1626                 flags |= Lwrite;
1627         return mkalist(m, flags, data, n);
1628 }
1629
1630 static int
1631 lockready(Drive *d)
1632 {
1633         int i;
1634
1635         qlock(&d->portm);
1636         while ((i = waitready(d)) == 1) {
1637                 qunlock(&d->portm);
1638                 esleep(1);
1639                 qlock(&d->portm);
1640         }
1641         return i;
1642 }
1643
1644 static int
1645 flushcache(Drive *d)
1646 {
1647         int i;
1648
1649         i = -1;
1650         if(lockready(d) == 0)
1651                 i = ahciflushcache(&d->portc);
1652         qunlock(&d->portm);
1653         return i;
1654 }
1655
1656 static int
1657 io(Drive *d, uint proto, int to, int interrupt)
1658 {
1659         uint task, flag, stat, rv;
1660         Aport *p;
1661         Asleep as;
1662
1663         switch(waitready(d)){
1664         case -1:
1665                 return SDeio;
1666         case 1:
1667                 return SDretry;
1668         }
1669
1670         ilock(d);
1671         d->portm.flag = 0;
1672         iunlock(d);
1673         p = d->port;
1674         p->ci = 1;
1675
1676         as.p = p;
1677         as.i = 1;
1678         d->totick = 0;
1679         if(to > 0)
1680                 d->totick = Ticks + MS2TK(to) | 1;      /* fix fencepost */
1681         d->active++;
1682
1683         while(waserror())
1684                 if(interrupt){
1685                         d->active--;
1686                         d->port->ci = 0;
1687                         if(ahcicomreset(&d->portc) == -1)
1688                                 dstatus(d, Dreset);
1689                         return SDtimeout;
1690                 }
1691         
1692         sleep(&d->portm, ahciclear, &as);
1693         poperror();
1694
1695         d->active--;
1696         ilock(d);
1697         stat = d->state;
1698         flag = d->portm.flag;
1699         task = d->port->task;
1700         iunlock(d);
1701
1702         rv = SDok;
1703         if(proto & Ppkt && stat == Dready){
1704                 rv = task >> 8 + 4 & 0xf;
1705                 flag &= ~Fahdrs;
1706                 flag |= Fdone;
1707         }else if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && stat == Dready){
1708                 d->port->ci = 0;
1709                 ahcirecover(&d->portc);
1710                 task = d->port->task;
1711                 flag &= ~Fdone;         /* either an error or do-over */
1712         }
1713         if(flag == 0){
1714                 print("%s: retry\n", dnam(d));
1715                 return SDretry;
1716         }
1717         if(flag & (Fahdrs | Ferror)){
1718                 if((task & Eidnf) == 0)
1719                         print("%s: i/o error %ux\n", dnam(d), task);
1720                 return SDcheck;
1721         }
1722         return rv;
1723 }
1724
1725 static int
1726 iariopkt(SDreq *r, Drive *d)
1727 {
1728         int try, to;
1729         uchar *cmd;
1730         Alist *l;
1731
1732         cmd = r->cmd;
1733         aprint("%s: %.2ux %.2ux %c %d %p\n", dnam(d), cmd[0], cmd[2],
1734                 "rw"[r->write], r->dlen, r->data);
1735
1736         r->rlen = 0;
1737
1738         /*
1739          * prevent iaonline() to hang forever by timing out
1740          * inquiry and capacity commands after 5 seconds.
1741          */
1742         to = 30*1000;
1743         switch(cmd[0]){
1744         case 0x9e: if(cmd[1] != 0x10) break;
1745         case 0x25:
1746         case 0x12:
1747                 to = 5*1000;
1748                 break;
1749         }
1750
1751         for(try = 0; try < 10; try++){
1752                 qlock(&d->portm);
1753                 l = ahcibuildpkt(d, r, r->data, r->dlen);
1754                 r->status = io(d, Ppkt, to, 0);
1755                 switch(r->status){
1756                 case SDeio:
1757                         qunlock(&d->portm);
1758                         return SDeio;
1759                 case SDretry:
1760                         qunlock(&d->portm);
1761                         continue;
1762                 }
1763                 r->rlen = l->len;
1764                 qunlock(&d->portm);
1765                 return SDok;
1766         }
1767         print("%s: bad disk\n", dnam(d));
1768         return r->status = SDcheck;
1769 }
1770
1771 static long
1772 ahcibio(SDunit *u, int lun, int write, void *a, long count, uvlong lba)
1773 {
1774         int n, rw, try, status, max;
1775         uchar *data;
1776         Ctlr *c;
1777         Drive *d;
1778
1779         c = u->dev->ctlr;
1780         d = c->drive[u->subno];
1781         if(d->portm.feat & Datapi)
1782                 return scsibio(u, lun, write, a, count, lba);
1783
1784         max = 128;
1785         if(d->portm.feat & Dllba){
1786                 max = 8192;             /* ahci maximum */
1787                 if(c->type == Tsb600)
1788                         max = 255;      /* errata */
1789         }
1790         rw = write? SDwrite: SDread;
1791         data = a;
1792         dprint("%s: bio: %llud %c %lud %p\n",
1793                 dnam(d), lba, "rw"[rw], count, data);
1794
1795         for(try = 0; try < 10;){
1796                 n = count;
1797                 if(n > max)
1798                         n = max;
1799                 qlock(&d->portm);
1800                 ahcibuild(d, rw, data, n, lba);
1801                 status = io(d, Pdma, 5000, 0);
1802                 qunlock(&d->portm);
1803                 switch(status){
1804                 case SDeio:
1805                         return -1;
1806                 case SDretry:
1807                         try++;
1808                         continue;
1809                 }
1810                 try = 0;
1811                 count -= n;
1812                 lba   += n;
1813                 data += n * u->secsize;
1814                 if(count == 0)
1815                         return data - (uchar*)a;
1816         }
1817         print("%s: bad disk\n", dnam(d));
1818         return -1;
1819 }
1820
1821 static int
1822 iario(SDreq *r)
1823 {
1824         int i, n, count, rw;
1825         uchar *cmd;
1826         uvlong lba;
1827         Ctlr *c;
1828         Drive *d;
1829         SDunit *u;
1830
1831         u = r->unit;
1832         c = u->dev->ctlr;
1833         d = c->drive[u->subno];
1834         if(d->portm.feat & Datapi)
1835                 return iariopkt(r, d);
1836         cmd = r->cmd;
1837
1838         if(cmd[0] == 0x35 || cmd[0] == 0x91){
1839                 if(flushcache(d) == 0)
1840                         return sdsetsense(r, SDok, 0, 0, 0);
1841                 return sdsetsense(r, SDcheck, 3, 0xc, 2);
1842         }
1843
1844         if((i = sdfakescsi(r)) != SDnostatus){
1845                 r->status = i;
1846                 return i;
1847         }
1848
1849         if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
1850                 return i;
1851         n = ahcibio(u, r->lun, r->write, r->data, count, lba);
1852         if(n == -1)
1853                 return SDeio;
1854         r->rlen = n;
1855         return SDok;
1856 }
1857
1858 static uchar bogusrfis[16] = {
1859 [Ftype]         0x34,
1860 [Fioport]       0x40,
1861 [Fstatus]       0x50,
1862 [Fdev]          0xa0,
1863 };
1864
1865 static void
1866 sdr0(Drive *d)
1867 {
1868         uchar *c;
1869
1870         c = d->portm.fis.r;
1871         memmove(c, bogusrfis, sizeof bogusrfis);
1872         coherence();
1873 }
1874
1875 static int
1876 sdr(SDreq *r, Drive *d, int st)
1877 {
1878         uchar *c;
1879         uint t;
1880
1881         if((r->ataproto & Pprotom) == Ppkt){
1882                 t = d->port->task;
1883                 if(t & ASerr)
1884                         st = t >> 8 + 4 & 0xf;
1885         }
1886         c = d->portm.fis.r;
1887         memmove(r->cmd, c, 16);
1888         r->status = st;
1889         if(st == SDcheck)
1890                 st = SDok;
1891         return st;
1892 }
1893
1894 static int
1895 fisreqchk(Sfis *f, SDreq *r)
1896 {
1897         if((r->ataproto & Pprotom) == Ppkt)
1898                 return SDnostatus;
1899         /*
1900          * handle oob requests;
1901          *    restrict & sanitize commands
1902          */
1903         if(r->clen != 16)
1904                 error(Eio);
1905         if(r->cmd[0] == 0xf0){
1906                 sigtofis(f, r->cmd);
1907                 r->status = SDok;
1908                 return SDok;
1909         }
1910         r->cmd[0] = 0x27;
1911         r->cmd[1] = 0x80;
1912         r->cmd[7] |= 0xa0;
1913         return SDnostatus;
1914 }
1915
1916 static int
1917 iaataio(SDreq *r)
1918 {
1919         int try;
1920         Ctlr *c;
1921         Drive *d;
1922         SDunit *u;
1923         Alist *l;
1924
1925         u = r->unit;
1926         c = u->dev->ctlr;
1927         d = c->drive[u->subno];
1928
1929         if((r->status = fisreqchk(&d->portm, r)) != SDnostatus)
1930                 return r->status;
1931         r->rlen = 0;
1932         sdr0(d);
1933         for(try = 0; try < 10; try++){
1934                 qlock(&d->portm);
1935                 l = ahcibuildfis(d, r, r->data, r->dlen);
1936                 r->status = io(d, r->ataproto & Pprotom, -1, 1);
1937                 switch(r->status){
1938                 case SDtimeout:
1939                         qunlock(&d->portm);
1940                         return sdsetsense(r, SDcheck, 11, 0, 6);
1941                 case SDeio:
1942                         qunlock(&d->portm);
1943                         return SDeio;
1944                 case SDretry:
1945                         qunlock(&d->portm);
1946                         continue;
1947                 }
1948                 r->rlen = (r->ataproto & Pprotom) == Ppkt ? l->len : r->dlen;
1949                 try = sdr(r, d, r->status);
1950                 qunlock(&d->portm);
1951                 return try;
1952         }
1953         print("%s: bad disk\n", dnam(d));
1954         return r->status = SDeio;
1955 }
1956
1957 /*
1958  * configure drives 0-5 as ahci sata  (c.f. errata)
1959  */
1960 static int
1961 iaahcimode(Pcidev *p)
1962 {
1963         uint u;
1964
1965         u = pcicfgr16(p, 0x92);
1966         dprint("ahci: %T: iaahcimode %.2ux %.4ux\n", p->tbdf, pcicfgr8(p, 0x91), u);
1967         pcicfgw16(p, 0x92, u | 0xf);    /* ports 0-15 */
1968         return 0;
1969 }
1970
1971 enum{
1972         Ghc     = 0x04/4,       /* global host control */
1973         Pi      = 0x0c/4,       /* ports implemented */
1974         Cmddec  = 1<<15,        /* enable command block decode */
1975
1976         /* Ghc bits */
1977         Ahcien  = 1<<31,        /* ahci enable */
1978 };
1979
1980 static void
1981 iasetupahci(Ctlr *c)
1982 {
1983         pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~Cmddec);
1984         pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~Cmddec);
1985
1986         c->lmmio[Ghc] |= Ahcien;
1987         c->lmmio[Pi] = (1 << 6) - 1;    /* 5 ports (supposedly ro pi reg) */
1988
1989         /* enable ahci mode; from ich9 datasheet */
1990         pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5);
1991 }
1992
1993 static void
1994 sbsetupahci(Pcidev *p)
1995 {
1996         print("sbsetupahci: tweaking %.4ux ccru %.2ux ccrp %.2ux\n",
1997                 p->did, p->ccru, p->ccrp);
1998         pcicfgw8(p, 0x40, pcicfgr8(p, 0x40) | 1);
1999         pcicfgw8(p, PciCCRu, 6);
2000         pcicfgw8(p, PciCCRp, 1);
2001         p->ccru = 6;
2002         p->ccrp = 1;
2003 }
2004
2005 static int
2006 esbenc(Ctlr *c)
2007 {
2008         c->encsz = 1;
2009         c->enctx = (ulong*)(c->mmio + 0xa0);
2010         c->enctype = Eesb;
2011         c->enctx[0] = 0;
2012         return 0;
2013 }
2014
2015 static int
2016 ahciencinit(Ctlr *c)
2017 {
2018         ulong type, sz, o, *bar;
2019         Ahba *h;
2020
2021         h = c->hba;
2022         if(c->type == Tesb)
2023                 return esbenc(c);
2024         if((h->cap & Hems) == 0)
2025                 return -1;
2026         type = h->emctl & Emtype;
2027         switch(type){
2028         case Esgpio:
2029         case Eses2:
2030         case Esafte:
2031                 return -1;
2032         case Elmt:
2033                 break;
2034         default:
2035                 return -1;
2036         }
2037
2038         sz = h->emloc & 0xffff;
2039         o = h->emloc>>16;
2040         if(sz == 0 || o == 0)
2041                 return -1;
2042         bar = c->lmmio;
2043         dprint("size = %.4lux; loc = %.4lux*4\n", sz, o);
2044         c->encsz = sz;
2045         c->enctx = bar + o;
2046         if((h->emctl & Xonly) == 0){
2047                 if(h->emctl & Smb)
2048                         c->encrx = bar + o;
2049                 else
2050                         c->encrx = bar + o*2;
2051         }
2052         c->enctype = type;
2053         return 0;
2054 }
2055
2056 static int
2057 didtype(Pcidev *p)
2058 {
2059         int type;
2060
2061         type = Tahci;
2062         switch(p->vid){
2063         default:
2064                 return -1;
2065         case 0x8086:
2066                 if((p->did & 0xffff) == 0x2653)
2067                         return Tich;            /* 82801fbm */
2068                 if((p->did & 0xfffc) == 0x2680)
2069                         return Tesb;
2070                 if((p->did & 0xfffb) == 0x27c1)
2071                         return Tich;            /* 82801g[bh]m */
2072                 if((p->did & 0xffff) == 0x2822)
2073                         return Tich;            /* 82801 SATA RAID */
2074                 if((p->did & 0xffff) == 0x2821)
2075                         return Tich;            /* 82801h[roh] */
2076                 if((p->did & 0xfffe) == 0x2824)
2077                         return Tich;            /* 82801h[b] */
2078                 if((p->did & 0xfeff) == 0x2829)
2079                         return Tich;            /* ich8 */
2080                 if((p->did & 0xfffe) == 0x2922)
2081                         return Tich;            /* ich9 */
2082                 if((p->did & 0xffff)  == 0x3a02)
2083                         return Tich;            /* 82801jd/do */
2084                 if((p->did & 0xfefe)  == 0x3a22)
2085                         return Tich;            /* ich10, pch */
2086                 if((p->did & 0xfff7)  == 0x3b28)
2087                         return Tich;            /* pchm */
2088                 if((p->did & 0xfffe) == 0x3b22)
2089                         return Tich;            /* pch */
2090                 break;
2091         case 0x1002:
2092                 if(p->ccru == 1 || p->ccrp != 1)
2093                 if(p->did == 0x4380 || p->did == 0x4390)
2094                         sbsetupahci(p);
2095                 type = Tsb600;
2096                 break;
2097         case 0x1106:
2098                 /*
2099                  * unconfirmed report that the programming
2100                  * interface is set incorrectly.
2101                  */
2102                 if(p->did == 0x3349)
2103                         return Tahci;
2104                 break;
2105         case 0x1022:
2106                 /* Hudson SATA Controller [AHCI mode] */
2107                 if(p->did == 0x7801)
2108                         return Tahci;
2109                 break;
2110         case 0x10de:
2111         case 0x1039:
2112         case 0x1b4b:
2113         case 0x11ab:
2114                 break;
2115         case 0x197b:
2116         case 0x10b9:
2117                 type = Tjmicron;
2118                 break;
2119         }
2120         if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1)
2121                 return type;
2122         return -1;
2123 }
2124
2125 static SDev*
2126 iapnp(void)
2127 {
2128         int i, n, nunit, type;
2129         ulong io;
2130         Ctlr *c;
2131         Drive *d;
2132         Pcidev *p;
2133         SDev *s;
2134         static int done;
2135
2136         if(done)
2137                 return nil;
2138         done = 1;
2139
2140         if(getconf("*ahcidebug") != nil){
2141                 debug = 1;
2142                 datapi = 1;
2143         }
2144
2145         memset(olds, 0xff, sizeof olds);
2146         p = nil;
2147         while((p = pcimatch(p, 0, 0)) != nil){
2148                 if((type = didtype(p)) == -1)
2149                         continue;
2150                 if(p->mem[Abar].bar == 0)
2151                         continue;
2152                 if(niactlr == NCtlr){
2153                         print("iapnp: %s: too many controllers\n", tname[type]);
2154                         break;
2155                 }
2156                 c = iactlr + niactlr;
2157                 s = sdevs + niactlr;
2158                 memset(c, 0, sizeof *c);
2159                 memset(s, 0, sizeof *s);
2160                 io = p->mem[Abar].bar & ~0xf;
2161                 c->mmio = vmap(io, p->mem[Abar].size);
2162                 if(c->mmio == 0){
2163                         print("%s: address %#p in use did %.4ux\n",
2164                                 Tname(c), io, p->did);
2165                         continue;
2166                 }
2167                 c->lmmio = (ulong*)c->mmio;
2168                 c->pci = p;
2169                 c->type = type;
2170
2171                 s->ifc = &sdiahciifc;
2172                 s->idno = 'E';
2173                 s->ctlr = c;
2174                 c->sdev = s;
2175
2176                 ahcihandoff((Ahba*)c->mmio);
2177                 if(intel(c) && p->did != 0x2681)
2178                         iasetupahci(c);
2179 //              ahcihbareset((Ahba*)c->mmio);
2180                 nunit = ahciconf(c);
2181                 if(intel(c) && iaahcimode(p) == -1 || nunit < 1){
2182                         vunmap(c->mmio, p->mem[Abar].size);
2183                         continue;
2184                 }
2185                 c->ndrive = s->nunit = nunit;
2186
2187                 /* map the drives -- they don't all need to be enabled. */
2188                 memset(c->rawdrive, 0, sizeof c->rawdrive);
2189                 n = 0;
2190                 for(i = 0; i < NCtlrdrv; i++){
2191                         d = c->rawdrive + i;
2192                         d->portno = i;
2193                         d->driveno = -1;
2194                         d->sectors = 0;
2195                         d->serial[0] = ' ';
2196                         d->ctlr = c;
2197                         if((c->hba->pi & 1<<i) == 0)
2198                                 continue;
2199                         snprint(d->name, sizeof d->name, "iahci%d.%d", niactlr, i);
2200                         d->port = (Aport*)(c->mmio + 0x80*i + 0x100);
2201                         d->portc.p = d->port;
2202                         d->portc.m = &d->portm;
2203                         d->driveno = n++;
2204                         c->drive[d->driveno] = d;
2205                         iadrive[niadrive + d->driveno] = d;
2206                 }
2207                 for(i = 0; i < n; i++){
2208                         c->drive[i]->mode = DMautoneg;
2209                         configdrive(c->drive[i]);
2210                 }
2211                 ahciencinit(c);
2212
2213                 niadrive += n;
2214                 niactlr++;
2215                 sdadddevs(s);
2216                 i = (c->hba->cap >> 21) & 1;
2217                 print("#S/%s: %s: sata-%s with %d ports\n", s->name,
2218                         Tname(c), "I\0II" + i*2, nunit);
2219         }
2220         return nil;
2221 }
2222
2223 static Htab ctab[] = {
2224         Aasp,   "asp",
2225         Aalpe , "alpe ",
2226         Adlae,  "dlae",
2227         Aatapi, "atapi",
2228         Apste,  "pste",
2229         Afbsc,  "fbsc",
2230         Aesp,   "esp",
2231         Acpd,   "cpd",
2232         Ampsp,  "mpsp",
2233         Ahpcp,  "hpcp",
2234         Apma,   "pma",
2235         Acps,   "cps",
2236         Acr,    "cr",
2237         Afr,    "fr",
2238         Ampss,  "mpss",
2239         Apod,   "pod",
2240         Asud,   "sud",
2241         Ast,    "st",
2242 };
2243
2244 static char*
2245 capfmt(char *p, char *e, Htab *t, int n, ulong cap)
2246 {
2247         uint i;
2248
2249         *p = 0;
2250         for(i = 0; i < n; i++)
2251                 if(cap & t[i].bit)
2252                         p = seprint(p, e, "%s ", t[i].name);
2253         return p;
2254 }
2255
2256 static int
2257 iarctl(SDunit *u, char *p, int l)
2258 {
2259         char buf[32], *e, *op;
2260         Aport *o;
2261         Ctlr *c;
2262         Drive *d;
2263
2264         if((c = u->dev->ctlr) == nil)
2265                 return 0;
2266         d = c->drive[u->subno];
2267         o = d->port;
2268
2269         e = p+l;
2270         op = p;
2271         if(d->state == Dready){
2272                 p = seprint(p, e, "model\t%s\n", d->model);
2273                 p = seprint(p, e, "serial\t%s\n", d->serial);
2274                 p = seprint(p, e, "firm\t%s\n", d->firmware);
2275                 if(d->wwn != 0)
2276                         p = seprint(p, e, "wwn\t%ullx\n", d->wwn);
2277                 p = seprint(p, e, "flag\t");
2278                 p = pflag(p, e, &d->portm);
2279                 p = seprint(p, e, "udma\t%d\n", d->portm.udma);
2280         }else
2281                 p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]);
2282         serrstr(o->serror, buf, buf + sizeof buf - 1);
2283         p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux "
2284                 "sig %lux sstatus %.3lux\n", o->task, o->cmd, o->serror, buf,
2285                 o->ci, o->isr, o->sig, o->sstatus);
2286         p = seprint(p, e, "cmd\t");
2287         p = capfmt(p, e, ctab, nelem(ctab), o->cmd);
2288         p = seprint(p, e, "\n");
2289         p = seprint(p, e, "mode\t%s %s\n", modes[d->mode], modes[maxmode(c)]);
2290         p = seprint(p, e, "geometry %llud %lud\n", d->sectors, u->secsize);
2291         p = seprint(p, e, "alignment %d %d\n", 
2292                 d->secsize<<d->portm.physshift, d->portm.physalign);
2293         p = seprint(p, e, "missirq\t%ud\n", c->missirq);
2294         return p - op;
2295 }
2296
2297 static void
2298 forcemode(Drive *d, char *mode)
2299 {
2300         int i;
2301
2302         for(i = 0; i < nelem(modes); i++)
2303                 if(strcmp(mode, modes[i]) == 0)
2304                         break;
2305         if(i == nelem(modes))
2306                 i = 0;
2307         ilock(d);
2308         d->mode = i;
2309         iunlock(d);
2310 }
2311
2312 static void
2313 forcestate(Drive *d, char *state)
2314 {
2315         int i;
2316
2317         for(i = 0; i < nelem(diskstates); i++)
2318                 if(strcmp(state, diskstates[i]) == 0)
2319                         break;
2320         if(i == nelem(diskstates))
2321                 error(Ebadctl);
2322         dstatus(d, i);
2323 }
2324
2325 static int
2326 runsettxmode(Drive *d, char *s)
2327 {
2328         int i;
2329         Aportc *c;
2330         Aportm *m;
2331
2332         c = &d->portc;
2333         m = &d->portm;
2334
2335         i = 1;
2336         if(lockready(d) == 0){
2337                 m->udma = atoi(s);
2338                 if(settxmode(c, m->udma) == 0)
2339                         i = 0;
2340         }
2341         qunlock(m);
2342         return i;
2343 }
2344
2345
2346 static int
2347 iawctl(SDunit *u, Cmdbuf *cmd)
2348 {
2349         char **f;
2350         Ctlr *c;
2351         Drive *d;
2352
2353         c = u->dev->ctlr;
2354         d = c->drive[u->subno];
2355         f = cmd->f;
2356
2357         if(strcmp(f[0], "mode") == 0)
2358                 forcemode(d, f[1]? f[1]: "satai");
2359         else if(strcmp(f[0], "state") == 0)
2360                 forcestate(d, f[1]? f[1]: "null");
2361         else if(strcmp(f[0], "txmode") == 0){
2362                 if(runsettxmode(d, f[1]? f[1]: "0"))
2363                         cmderror(cmd, "bad txmode / stuck port");
2364         }else
2365                 cmderror(cmd, Ebadctl);
2366         return 0;
2367 }
2368
2369 static char *
2370 portr(char *p, char *e, uint x)
2371 {
2372         int i, a;
2373
2374         p[0] = 0;
2375         a = -1;
2376         for(i = 0; i < 32; i++){
2377                 if((x & (1<<i)) == 0){
2378                         if(a != -1 && i - 1 != a)
2379                                 p = seprint(p, e, "-%d", i - 1);
2380                         a = -1;
2381                         continue;
2382                 }
2383                 if(a == -1){
2384                         if(i > 0)
2385                                 p = seprint(p, e, ", ");
2386                         p = seprint(p, e, "%d", a = i);
2387                 }
2388         }
2389         if(a != -1 && i - 1 != a)
2390                 p = seprint(p, e, "-%d", i - 1);
2391         return p;
2392 }
2393
2394 static Htab htab[] = {
2395         H64a,   "64a",
2396         Hncq,   "ncq",
2397         Hsntf,  "ntf",
2398         Hmps,   "mps",
2399         Hss,    "ss",
2400         Halp,   "alp",
2401         Hal,    "led",
2402         Hclo,   "clo",
2403         Ham,    "am",
2404         Hpm,    "pm",
2405         Hfbs,   "fbs",
2406         Hpmb,   "pmb",
2407         Hssc,   "slum",
2408         Hpsc,   "pslum",
2409         Hcccs,  "coal",
2410         Hems,   "ems",
2411         Hxs,    "xs",
2412 };
2413
2414 static Htab htab2[] = {
2415         Apts,   "apts",
2416         Nvmp,   "nvmp",
2417         Boh,    "boh",
2418 };
2419
2420 static Htab emtab[] = {
2421         Pm,     "pm",
2422         Alhd,   "alhd",
2423         Xonly,  "xonly",
2424         Smb,    "smb",
2425         Esgpio, "esgpio",
2426         Eses2,  "eses2",
2427         Esafte, "esafte",
2428         Elmt,   "elmt",
2429 };
2430
2431 static char*
2432 iartopctl(SDev *s, char *p, char *e)
2433 {
2434         char pr[25];
2435         ulong cap;
2436         Ahba *h;
2437         Ctlr *c;
2438
2439         c = s->ctlr;
2440         h = c->hba;
2441         cap = h->cap;
2442         p = seprint(p, e, "sd%c ahci %s port %#p: ", s->idno, Tname(c), h);
2443         p = capfmt(p, e, htab, nelem(htab), cap);
2444         p = capfmt(p, e, htab2, nelem(htab2), h->cap2);
2445         p = capfmt(p, e, emtab, nelem(emtab), h->emctl);
2446         portr(pr, pr + sizeof pr, h->pi);
2447         return seprint(p, e,
2448                 "iss %ld ncs %ld np %ld ghc %lux isr %lux pi %lux %s ver %lux\n",
2449                 (cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f),
2450                 h->ghc, h->isr, h->pi, pr, h->ver);
2451 }
2452
2453 static int
2454 iawtopctl(SDev *, Cmdbuf *cmd)
2455 {
2456         int *v;
2457         char **f;
2458
2459         f = cmd->f;
2460         v = 0;
2461
2462         if(strcmp(f[0], "debug") == 0)
2463                 v = &debug;
2464         else if(strcmp(f[0], "idprint") == 0)
2465                 v = &prid;
2466         else if(strcmp(f[0], "aprint") == 0)
2467                 v = &datapi;
2468         else if(strcmp(f[0], "ledprint") == 0)
2469                 v = &dled;
2470         else
2471                 cmderror(cmd, Ebadctl);
2472
2473         switch(cmd->nf){
2474         default:
2475                 cmderror(cmd, Ebadarg);
2476         case 1:
2477                 *v ^= 1;
2478                 break;
2479         case 2:
2480                 if(f[1])
2481                         *v = strcmp(f[1], "on") == 0;
2482                 else
2483                         *v ^= 1;
2484                 break;
2485         }
2486         return 0;
2487 }
2488
2489 SDifc sdiahciifc = {
2490         "ahci",
2491
2492         iapnp,
2493         nil,            /* legacy */
2494         iaenable,
2495         iadisable,
2496
2497         iaverify,
2498         iaonline,
2499         iario,
2500         iarctl,
2501         iawctl,
2502
2503         ahcibio,
2504         nil,            /* probe */
2505         nil,            /* clear */
2506         iartopctl,
2507         iawtopctl,
2508         iaataio,
2509 };