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