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