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