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