xref: /plan9/sys/src/9/pc/sdiahci.c (revision 75cb58dbb29a66e75e2e3ad617d2dea92d2b0dbe)
1b1707c5dSDavid du Colombier /*
2*75cb58dbSDavid du Colombier  * ahci serial ata driver
3014ad43fSDavid du Colombier  * copyright © 2007-8 coraid, inc.
4b1707c5dSDavid du Colombier  */
5b1707c5dSDavid du Colombier 
6b1707c5dSDavid du Colombier #include "u.h"
7b1707c5dSDavid du Colombier #include "../port/lib.h"
8b1707c5dSDavid du Colombier #include "mem.h"
9b1707c5dSDavid du Colombier #include "dat.h"
10b1707c5dSDavid du Colombier #include "fns.h"
11b1707c5dSDavid du Colombier #include "io.h"
12b1707c5dSDavid du Colombier #include "../port/error.h"
13b1707c5dSDavid du Colombier #include "../port/sd.h"
14b1707c5dSDavid du Colombier #include "ahci.h"
15b1707c5dSDavid du Colombier 
16b1707c5dSDavid du Colombier #define	dprint(...)	if(debug)	iprint(__VA_ARGS__); else USED(debug)
17014ad43fSDavid du Colombier #define	idprint(...)	if(prid)	iprint(__VA_ARGS__);  else USED(prid)
18014ad43fSDavid du Colombier #define	aprint(...)	if(datapi)	iprint(__VA_ARGS__);  else USED(datapi)
19*75cb58dbSDavid du Colombier 
20b1707c5dSDavid du Colombier #define Tname(c)	tname[(c)->type]
21*75cb58dbSDavid du Colombier #define Intel(x)	((x)->pci->vid == Vintel)
22b1707c5dSDavid du Colombier 
23b1707c5dSDavid du Colombier enum {
24b1707c5dSDavid du Colombier 	NCtlr	= 4,
25b1707c5dSDavid du Colombier 	NCtlrdrv= 32,
26b1707c5dSDavid du Colombier 	NDrive	= NCtlr*NCtlrdrv,
27b1707c5dSDavid du Colombier 
28b1707c5dSDavid du Colombier 	Read	= 0,
29b1707c5dSDavid du Colombier 	Write,
30*75cb58dbSDavid du Colombier 
31*75cb58dbSDavid du Colombier 	Nms	= 256,
32*75cb58dbSDavid du Colombier 	Mphywait=  2*1024/Nms - 1,
33*75cb58dbSDavid du Colombier 	Midwait	= 16*1024/Nms - 1,
34*75cb58dbSDavid du Colombier 	Mcomrwait= 64*1024/Nms - 1,
35*75cb58dbSDavid du Colombier 
36*75cb58dbSDavid du Colombier 	Obs	= 0xa0,			/* obsolete device bits */
37b1707c5dSDavid du Colombier };
38b1707c5dSDavid du Colombier 
39b1707c5dSDavid du Colombier /* pci space configuration */
40b1707c5dSDavid du Colombier enum {
41b1707c5dSDavid du Colombier 	Pmap	= 0x90,
42b1707c5dSDavid du Colombier 	Ppcs	= 0x91,
43b1707c5dSDavid du Colombier 	Prev	= 0xa8,
44b1707c5dSDavid du Colombier };
45b1707c5dSDavid du Colombier 
46b1707c5dSDavid du Colombier enum {
47b1707c5dSDavid du Colombier 	Tesb,
48b1707c5dSDavid du Colombier 	Tich,
49b1707c5dSDavid du Colombier 	Tsb600,
50014ad43fSDavid du Colombier 	Tunk,
51b1707c5dSDavid du Colombier };
52b1707c5dSDavid du Colombier 
53b1707c5dSDavid du Colombier static char *tname[] = {
54b1707c5dSDavid du Colombier 	"63xxesb",
55b1707c5dSDavid du Colombier 	"ich",
56b1707c5dSDavid du Colombier 	"sb600",
57*75cb58dbSDavid du Colombier 	"unknown",
58b1707c5dSDavid du Colombier };
59b1707c5dSDavid du Colombier 
60b1707c5dSDavid du Colombier enum {
61b1707c5dSDavid du Colombier 	Dnull,
62b1707c5dSDavid du Colombier 	Dmissing,
63b1707c5dSDavid du Colombier 	Dnew,
64b1707c5dSDavid du Colombier 	Dready,
65b1707c5dSDavid du Colombier 	Derror,
66b1707c5dSDavid du Colombier 	Dreset,
67b1707c5dSDavid du Colombier 	Doffline,
68b1707c5dSDavid du Colombier 	Dportreset,
69b1707c5dSDavid du Colombier 	Dlast,
70b1707c5dSDavid du Colombier };
71b1707c5dSDavid du Colombier 
72b1707c5dSDavid du Colombier static char *diskstates[Dlast] = {
73b1707c5dSDavid du Colombier 	"null",
74b1707c5dSDavid du Colombier 	"missing",
75b1707c5dSDavid du Colombier 	"new",
76b1707c5dSDavid du Colombier 	"ready",
77b1707c5dSDavid du Colombier 	"error",
78b1707c5dSDavid du Colombier 	"reset",
79b1707c5dSDavid du Colombier 	"offline",
80b1707c5dSDavid du Colombier 	"portreset",
81b1707c5dSDavid du Colombier };
82b1707c5dSDavid du Colombier 
83b1707c5dSDavid du Colombier enum {
84b1707c5dSDavid du Colombier 	DMautoneg,
85b1707c5dSDavid du Colombier 	DMsatai,
86b1707c5dSDavid du Colombier 	DMsataii,
87*75cb58dbSDavid du Colombier 	DMsata3,
88b1707c5dSDavid du Colombier };
89b1707c5dSDavid du Colombier 
90*75cb58dbSDavid du Colombier static char *modename[] = {		/* used in control messages */
91b1707c5dSDavid du Colombier 	"auto",
92b1707c5dSDavid du Colombier 	"satai",
93b1707c5dSDavid du Colombier 	"sataii",
94*75cb58dbSDavid du Colombier 	"sata3",
95*75cb58dbSDavid du Colombier };
96*75cb58dbSDavid du Colombier static char *descmode[] = {		/*  only printed */
97*75cb58dbSDavid du Colombier 	"auto",
98*75cb58dbSDavid du Colombier 	"sata 1",
99*75cb58dbSDavid du Colombier 	"sata 2",
100*75cb58dbSDavid du Colombier 	"sata 3",
101b1707c5dSDavid du Colombier };
102b1707c5dSDavid du Colombier 
103b1707c5dSDavid du Colombier static char *flagname[] = {
104b1707c5dSDavid du Colombier 	"llba",
105b1707c5dSDavid du Colombier 	"smart",
106b1707c5dSDavid du Colombier 	"power",
107b1707c5dSDavid du Colombier 	"nop",
108b1707c5dSDavid du Colombier 	"atapi",
109b1707c5dSDavid du Colombier 	"atapi16",
110b1707c5dSDavid du Colombier };
111b1707c5dSDavid du Colombier 
112*75cb58dbSDavid du Colombier typedef struct Asleep Asleep;
113*75cb58dbSDavid du Colombier typedef struct Ctlr Ctlr;
114*75cb58dbSDavid du Colombier typedef struct Drive Drive;
115*75cb58dbSDavid du Colombier 
116*75cb58dbSDavid du Colombier struct Drive {
117b1707c5dSDavid du Colombier 	Lock;
118b1707c5dSDavid du Colombier 
119b1707c5dSDavid du Colombier 	Ctlr	*ctlr;
120b1707c5dSDavid du Colombier 	SDunit	*unit;
121b1707c5dSDavid du Colombier 	char	name[10];
122b1707c5dSDavid du Colombier 	Aport	*port;
123b1707c5dSDavid du Colombier 	Aportm	portm;
124f3112f79SDavid du Colombier 	Aportc	portc;		/* redundant ptr to port and portm */
125b1707c5dSDavid du Colombier 
126b1707c5dSDavid du Colombier 	uchar	mediachange;
127b1707c5dSDavid du Colombier 	uchar	state;
128b1707c5dSDavid du Colombier 	uchar	smartrs;
129b1707c5dSDavid du Colombier 
130b1707c5dSDavid du Colombier 	uvlong	sectors;
13158db92f4SDavid du Colombier 	ulong	secsize;
132f3112f79SDavid du Colombier 	ulong	intick;		/* start tick of current transfer */
13381ede731SDavid du Colombier 	ulong	lastseen;
134b1707c5dSDavid du Colombier 	int	wait;
135f3112f79SDavid du Colombier 	uchar	mode;		/* DMautoneg, satai or sataii */
136b1707c5dSDavid du Colombier 	uchar	active;
137b1707c5dSDavid du Colombier 
138b1707c5dSDavid du Colombier 	char	serial[20+1];
139b1707c5dSDavid du Colombier 	char	firmware[8+1];
140b1707c5dSDavid du Colombier 	char	model[40+1];
141b1707c5dSDavid du Colombier 
142b1707c5dSDavid du Colombier 	ushort	info[0x200];
143b1707c5dSDavid du Colombier 
144b1707c5dSDavid du Colombier 	int	driveno;	/* ctlr*NCtlrdrv + unit */
145b1707c5dSDavid du Colombier 	/* controller port # != driveno when not all ports are enabled */
146b1707c5dSDavid du Colombier 	int	portno;
147*75cb58dbSDavid du Colombier };
148b1707c5dSDavid du Colombier 
149b1707c5dSDavid du Colombier struct Ctlr {
150b1707c5dSDavid du Colombier 	Lock;
151b1707c5dSDavid du Colombier 
152b1707c5dSDavid du Colombier 	int	type;
153b1707c5dSDavid du Colombier 	int	enabled;
154b1707c5dSDavid du Colombier 	SDev	*sdev;
155b1707c5dSDavid du Colombier 	Pcidev	*pci;
156b1707c5dSDavid du Colombier 
157*75cb58dbSDavid du Colombier 	/* virtual register addresses */
158b1707c5dSDavid du Colombier 	uchar	*mmio;
159b1707c5dSDavid du Colombier 	ulong	*lmmio;
160b1707c5dSDavid du Colombier 	Ahba	*hba;
161b1707c5dSDavid du Colombier 
162*75cb58dbSDavid du Colombier 	/* phyical register address */
163*75cb58dbSDavid du Colombier 	uchar	*physio;
164*75cb58dbSDavid du Colombier 
165b1707c5dSDavid du Colombier 	Drive	rawdrive[NCtlrdrv];
166b1707c5dSDavid du Colombier 	Drive*	drive[NCtlrdrv];
167b1707c5dSDavid du Colombier 	int	ndrive;
168014ad43fSDavid du Colombier 	int	mport;
169b1707c5dSDavid du Colombier };
170b1707c5dSDavid du Colombier 
171*75cb58dbSDavid du Colombier struct Asleep {
172*75cb58dbSDavid du Colombier 	Aport	*p;
173*75cb58dbSDavid du Colombier 	int	i;
174*75cb58dbSDavid du Colombier };
175*75cb58dbSDavid du Colombier 
176*75cb58dbSDavid du Colombier extern SDifc sdiahciifc;
177*75cb58dbSDavid du Colombier 
178b1707c5dSDavid du Colombier static	Ctlr	iactlr[NCtlr];
179b1707c5dSDavid du Colombier static	SDev	sdevs[NCtlr];
180b1707c5dSDavid du Colombier static	int	niactlr;
181b1707c5dSDavid du Colombier 
182b1707c5dSDavid du Colombier static	Drive	*iadrive[NDrive];
183b1707c5dSDavid du Colombier static	int	niadrive;
184b1707c5dSDavid du Colombier 
1859e8a50a9SDavid du Colombier /* these are fiddled in iawtopctl() */
186b1707c5dSDavid du Colombier static	int	debug;
187b1707c5dSDavid du Colombier static	int	prid = 1;
188b1707c5dSDavid du Colombier static	int	datapi;
189b1707c5dSDavid du Colombier 
190b1707c5dSDavid du Colombier static char stab[] = {
191b1707c5dSDavid du Colombier [0]	'i', 'm',
192b1707c5dSDavid du Colombier [8]	't', 'c', 'p', 'e',
193b1707c5dSDavid du Colombier [16]	'N', 'I', 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X'
194b1707c5dSDavid du Colombier };
195b1707c5dSDavid du Colombier 
196b1707c5dSDavid du Colombier static void
197b1707c5dSDavid du Colombier serrstr(ulong r, char *s, char *e)
198b1707c5dSDavid du Colombier {
199b1707c5dSDavid du Colombier 	int i;
200b1707c5dSDavid du Colombier 
201b1707c5dSDavid du Colombier 	e -= 3;
202b1707c5dSDavid du Colombier 	for(i = 0; i < nelem(stab) && s < e; i++)
203b1707c5dSDavid du Colombier 		if(r & (1<<i) && stab[i]){
204b1707c5dSDavid du Colombier 			*s++ = stab[i];
205b1707c5dSDavid du Colombier 			if(SerrBad & (1<<i))
206b1707c5dSDavid du Colombier 				*s++ = '*';
207b1707c5dSDavid du Colombier 		}
208b1707c5dSDavid du Colombier 	*s = 0;
209b1707c5dSDavid du Colombier }
210b1707c5dSDavid du Colombier 
211b1707c5dSDavid du Colombier static char ntab[] = "0123456789abcdef";
212b1707c5dSDavid du Colombier 
213b1707c5dSDavid du Colombier static void
214b1707c5dSDavid du Colombier preg(uchar *reg, int n)
215b1707c5dSDavid du Colombier {
216b1707c5dSDavid du Colombier 	int i;
217b1707c5dSDavid du Colombier 	char buf[25*3+1], *e;
218b1707c5dSDavid du Colombier 
219b1707c5dSDavid du Colombier 	e = buf;
220b1707c5dSDavid du Colombier 	for(i = 0; i < n; i++){
221b1707c5dSDavid du Colombier 		*e++ = ntab[reg[i]>>4];
222b1707c5dSDavid du Colombier 		*e++ = ntab[reg[i]&0xf];
223b1707c5dSDavid du Colombier 		*e++ = ' ';
224b1707c5dSDavid du Colombier 	}
225b1707c5dSDavid du Colombier 	*e++ = '\n';
226b1707c5dSDavid du Colombier 	*e = 0;
227b1707c5dSDavid du Colombier 	dprint(buf);
228b1707c5dSDavid du Colombier }
229b1707c5dSDavid du Colombier 
230b1707c5dSDavid du Colombier static void
231b1707c5dSDavid du Colombier dreg(char *s, Aport *p)
232b1707c5dSDavid du Colombier {
2333d56950aSDavid du Colombier 	dprint("ahci: %stask=%lux; cmd=%lux; ci=%lux; is=%lux\n",
2343d56950aSDavid du Colombier 		s, p->task, p->cmd, p->ci, p->isr);
235b1707c5dSDavid du Colombier }
236b1707c5dSDavid du Colombier 
237b1707c5dSDavid du Colombier static void
238b1707c5dSDavid du Colombier esleep(int ms)
239b1707c5dSDavid du Colombier {
240b1707c5dSDavid du Colombier 	if(waserror())
241b1707c5dSDavid du Colombier 		return;
242b1707c5dSDavid du Colombier 	tsleep(&up->sleep, return0, 0, ms);
243b1707c5dSDavid du Colombier 	poperror();
244b1707c5dSDavid du Colombier }
245b1707c5dSDavid du Colombier 
246b1707c5dSDavid du Colombier static int
247b1707c5dSDavid du Colombier ahciclear(void *v)
248b1707c5dSDavid du Colombier {
249b1707c5dSDavid du Colombier 	Asleep *s;
250b1707c5dSDavid du Colombier 
251b1707c5dSDavid du Colombier 	s = v;
252b1707c5dSDavid du Colombier 	return (s->p->ci & s->i) == 0;
253b1707c5dSDavid du Colombier }
254b1707c5dSDavid du Colombier 
255b1707c5dSDavid du Colombier static void
256b1707c5dSDavid du Colombier aesleep(Aportm *m, Asleep *a, int ms)
257b1707c5dSDavid du Colombier {
258b1707c5dSDavid du Colombier 	if(waserror())
259b1707c5dSDavid du Colombier 		return;
260b1707c5dSDavid du Colombier 	tsleep(m, ahciclear, a, ms);
261b1707c5dSDavid du Colombier 	poperror();
262b1707c5dSDavid du Colombier }
263b1707c5dSDavid du Colombier 
264b1707c5dSDavid du Colombier static int
265b1707c5dSDavid du Colombier ahciwait(Aportc *c, int ms)
266b1707c5dSDavid du Colombier {
267b1707c5dSDavid du Colombier 	Asleep as;
268b1707c5dSDavid du Colombier 	Aport *p;
269b1707c5dSDavid du Colombier 
270b1707c5dSDavid du Colombier 	p = c->p;
271b1707c5dSDavid du Colombier 	p->ci = 1;
272b1707c5dSDavid du Colombier 	as.p = p;
273b1707c5dSDavid du Colombier 	as.i = 1;
274b1707c5dSDavid du Colombier 	aesleep(c->m, &as, ms);
275b1707c5dSDavid du Colombier 	if((p->task&1) == 0 && p->ci == 0)
276b1707c5dSDavid du Colombier 		return 0;
277b1707c5dSDavid du Colombier 	dreg("ahciwait timeout ", c->p);
278b1707c5dSDavid du Colombier 	return -1;
279b1707c5dSDavid du Colombier }
280b1707c5dSDavid du Colombier 
281b1707c5dSDavid du Colombier static int
282b1707c5dSDavid du Colombier nop(Aportc *pc)
283b1707c5dSDavid du Colombier {
284b1707c5dSDavid du Colombier 	uchar *c;
285b1707c5dSDavid du Colombier 	Actab *t;
286b1707c5dSDavid du Colombier 	Alist *l;
287b1707c5dSDavid du Colombier 
288b1707c5dSDavid du Colombier 	if((pc->m->feat & Dnop) == 0)
289b1707c5dSDavid du Colombier 		return -1;
290b1707c5dSDavid du Colombier 
291b1707c5dSDavid du Colombier 	t = pc->m->ctab;
292b1707c5dSDavid du Colombier 	c = t->cfis;
293b1707c5dSDavid du Colombier 
294b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
295b1707c5dSDavid du Colombier 	c[0] = 0x27;
296b1707c5dSDavid du Colombier 	c[1] = 0x80;
297b1707c5dSDavid du Colombier 	c[2] = 0x00;
298*75cb58dbSDavid du Colombier 	c[7] = Obs;
299b1707c5dSDavid du Colombier 
300b1707c5dSDavid du Colombier 	l = pc->m->list;
301b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
302b1707c5dSDavid du Colombier 	l->len = 0;
303b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
304b1707c5dSDavid du Colombier 	l->ctabhi = 0;
305b1707c5dSDavid du Colombier 
306b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
307b1707c5dSDavid du Colombier }
308b1707c5dSDavid du Colombier 
309b1707c5dSDavid du Colombier static int
310b1707c5dSDavid du Colombier setfeatures(Aportc *pc, uchar f)
311b1707c5dSDavid du Colombier {
312b1707c5dSDavid du Colombier 	uchar *c;
313b1707c5dSDavid du Colombier 	Actab *t;
314b1707c5dSDavid du Colombier 	Alist *l;
315b1707c5dSDavid du Colombier 
316b1707c5dSDavid du Colombier 	t = pc->m->ctab;
317b1707c5dSDavid du Colombier 	c = t->cfis;
318b1707c5dSDavid du Colombier 
319b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
320b1707c5dSDavid du Colombier 	c[0] = 0x27;
321b1707c5dSDavid du Colombier 	c[1] = 0x80;
322b1707c5dSDavid du Colombier 	c[2] = 0xef;
323b1707c5dSDavid du Colombier 	c[3] = f;
324*75cb58dbSDavid du Colombier 	c[7] = Obs;
325b1707c5dSDavid du Colombier 
326b1707c5dSDavid du Colombier 	l = pc->m->list;
327b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
328b1707c5dSDavid du Colombier 	l->len = 0;
329b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
330b1707c5dSDavid du Colombier 	l->ctabhi = 0;
331b1707c5dSDavid du Colombier 
332b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
333b1707c5dSDavid du Colombier }
334b1707c5dSDavid du Colombier 
335b1707c5dSDavid du Colombier static int
336b1707c5dSDavid du Colombier setudmamode(Aportc *pc, uchar f)
337b1707c5dSDavid du Colombier {
338b1707c5dSDavid du Colombier 	uchar *c;
339b1707c5dSDavid du Colombier 	Actab *t;
340b1707c5dSDavid du Colombier 	Alist *l;
341b1707c5dSDavid du Colombier 
342b1707c5dSDavid du Colombier 	/* hack */
343b1707c5dSDavid du Colombier 	if((pc->p->sig >> 16) == 0xeb14)
344b1707c5dSDavid du Colombier 		return 0;
345b1707c5dSDavid du Colombier 
346b1707c5dSDavid du Colombier 	t = pc->m->ctab;
347b1707c5dSDavid du Colombier 	c = t->cfis;
348b1707c5dSDavid du Colombier 
349b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
350b1707c5dSDavid du Colombier 	c[0] = 0x27;
351b1707c5dSDavid du Colombier 	c[1] = 0x80;
352b1707c5dSDavid du Colombier 	c[2] = 0xef;
353b1707c5dSDavid du Colombier 	c[3] = 3;		/* set transfer mode */
354*75cb58dbSDavid du Colombier 	c[7] = Obs;
355b1707c5dSDavid du Colombier 	c[12] = 0x40 | f;	/* sector count */
356b1707c5dSDavid du Colombier 
357b1707c5dSDavid du Colombier 	l = pc->m->list;
358b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
359b1707c5dSDavid du Colombier 	l->len = 0;
360b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
361b1707c5dSDavid du Colombier 	l->ctabhi = 0;
362b1707c5dSDavid du Colombier 
363b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
364b1707c5dSDavid du Colombier }
365b1707c5dSDavid du Colombier 
366b1707c5dSDavid du Colombier static void
367b1707c5dSDavid du Colombier asleep(int ms)
368b1707c5dSDavid du Colombier {
369b1707c5dSDavid du Colombier 	if(up == nil)
370b1707c5dSDavid du Colombier 		delay(ms);
371b1707c5dSDavid du Colombier 	else
372b1707c5dSDavid du Colombier 		esleep(ms);
373b1707c5dSDavid du Colombier }
374b1707c5dSDavid du Colombier 
375b1707c5dSDavid du Colombier static int
376b1707c5dSDavid du Colombier ahciportreset(Aportc *c)
377b1707c5dSDavid du Colombier {
37891b330d9SDavid du Colombier 	ulong *cmd, i;
379b1707c5dSDavid du Colombier 	Aport *p;
380b1707c5dSDavid du Colombier 
381b1707c5dSDavid du Colombier 	p = c->p;
382b1707c5dSDavid du Colombier 	cmd = &p->cmd;
383b1707c5dSDavid du Colombier 	*cmd &= ~(Afre|Ast);
384b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 25){
385b1707c5dSDavid du Colombier 		if((*cmd&Acr) == 0)
386b1707c5dSDavid du Colombier 			break;
387b1707c5dSDavid du Colombier 		asleep(25);
388b1707c5dSDavid du Colombier 	}
389b1707c5dSDavid du Colombier 	p->sctl = 1|(p->sctl&~7);
390b1707c5dSDavid du Colombier 	delay(1);
391b1707c5dSDavid du Colombier 	p->sctl &= ~7;
392b1707c5dSDavid du Colombier 	return 0;
393b1707c5dSDavid du Colombier }
394b1707c5dSDavid du Colombier 
395b1707c5dSDavid du Colombier static int
396b1707c5dSDavid du Colombier smart(Aportc *pc, int n)
397b1707c5dSDavid du Colombier {
398b1707c5dSDavid du Colombier 	uchar *c;
399b1707c5dSDavid du Colombier 	Actab *t;
400b1707c5dSDavid du Colombier 	Alist *l;
401b1707c5dSDavid du Colombier 
402b1707c5dSDavid du Colombier 	if((pc->m->feat&Dsmart) == 0)
403b1707c5dSDavid du Colombier 		return -1;
404b1707c5dSDavid du Colombier 
405b1707c5dSDavid du Colombier 	t = pc->m->ctab;
406b1707c5dSDavid du Colombier 	c = t->cfis;
407b1707c5dSDavid du Colombier 
408b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
409b1707c5dSDavid du Colombier 	c[0] = 0x27;
410b1707c5dSDavid du Colombier 	c[1] = 0x80;
411b1707c5dSDavid du Colombier 	c[2] = 0xb0;
412b1707c5dSDavid du Colombier 	c[3] = 0xd8 + n;	/* able smart */
413b1707c5dSDavid du Colombier 	c[5] = 0x4f;
414b1707c5dSDavid du Colombier 	c[6] = 0xc2;
415*75cb58dbSDavid du Colombier 	c[7] = Obs;
416b1707c5dSDavid du Colombier 
417b1707c5dSDavid du Colombier 	l = pc->m->list;
418b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
419b1707c5dSDavid du Colombier 	l->len = 0;
420b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
421b1707c5dSDavid du Colombier 	l->ctabhi = 0;
422b1707c5dSDavid du Colombier 
423b1707c5dSDavid du Colombier 	if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
4243d56950aSDavid du Colombier 		dprint("ahci: smart fail %lux\n", pc->p->task);
425b1707c5dSDavid du Colombier //		preg(pc->m->fis.r, 20);
426b1707c5dSDavid du Colombier 		return -1;
427b1707c5dSDavid du Colombier 	}
428b1707c5dSDavid du Colombier 	if(n)
429b1707c5dSDavid du Colombier 		return 0;
430b1707c5dSDavid du Colombier 	return 1;
431b1707c5dSDavid du Colombier }
432b1707c5dSDavid du Colombier 
433b1707c5dSDavid du Colombier static int
434b1707c5dSDavid du Colombier smartrs(Aportc *pc)
435b1707c5dSDavid du Colombier {
436b1707c5dSDavid du Colombier 	uchar *c;
437b1707c5dSDavid du Colombier 	Actab *t;
438b1707c5dSDavid du Colombier 	Alist *l;
439b1707c5dSDavid du Colombier 
440b1707c5dSDavid du Colombier 	t = pc->m->ctab;
441b1707c5dSDavid du Colombier 	c = t->cfis;
442b1707c5dSDavid du Colombier 
443b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
444b1707c5dSDavid du Colombier 	c[0] = 0x27;
445b1707c5dSDavid du Colombier 	c[1] = 0x80;
446b1707c5dSDavid du Colombier 	c[2] = 0xb0;
447b1707c5dSDavid du Colombier 	c[3] = 0xda;		/* return smart status */
448b1707c5dSDavid du Colombier 	c[5] = 0x4f;
449b1707c5dSDavid du Colombier 	c[6] = 0xc2;
450*75cb58dbSDavid du Colombier 	c[7] = Obs;
451b1707c5dSDavid du Colombier 
452b1707c5dSDavid du Colombier 	l = pc->m->list;
453b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
454b1707c5dSDavid du Colombier 	l->len = 0;
455b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
456b1707c5dSDavid du Colombier 	l->ctabhi = 0;
457b1707c5dSDavid du Colombier 
458b1707c5dSDavid du Colombier 	c = pc->m->fis.r;
459b1707c5dSDavid du Colombier 	if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
4603d56950aSDavid du Colombier 		dprint("ahci: smart fail %lux\n", pc->p->task);
461b1707c5dSDavid du Colombier 		preg(c, 20);
462b1707c5dSDavid du Colombier 		return -1;
463b1707c5dSDavid du Colombier 	}
464b1707c5dSDavid du Colombier 	if(c[5] == 0x4f && c[6] == 0xc2)
465b1707c5dSDavid du Colombier 		return 1;
466b1707c5dSDavid du Colombier 	return 0;
467b1707c5dSDavid du Colombier }
468b1707c5dSDavid du Colombier 
469b1707c5dSDavid du Colombier static int
47081ede731SDavid du Colombier ahciflushcache(Aportc *pc)
471b1707c5dSDavid du Colombier {
472b1707c5dSDavid du Colombier 	uchar *c, llba;
473b1707c5dSDavid du Colombier 	Actab *t;
474b1707c5dSDavid du Colombier 	Alist *l;
475b1707c5dSDavid du Colombier 	static uchar tab[2] = {0xe7, 0xea};
476b1707c5dSDavid du Colombier 
477b1707c5dSDavid du Colombier 	llba = pc->m->feat&Dllba? 1: 0;
478b1707c5dSDavid du Colombier 	t = pc->m->ctab;
479b1707c5dSDavid du Colombier 	c = t->cfis;
480b1707c5dSDavid du Colombier 
481b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
482b1707c5dSDavid du Colombier 	c[0] = 0x27;
483b1707c5dSDavid du Colombier 	c[1] = 0x80;
484b1707c5dSDavid du Colombier 	c[2] = tab[llba];
485*75cb58dbSDavid du Colombier 	c[7] = Obs;
486b1707c5dSDavid du Colombier 
487b1707c5dSDavid du Colombier 	l = pc->m->list;
488b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
489b1707c5dSDavid du Colombier 	l->len = 0;
490b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
491b1707c5dSDavid du Colombier 	l->ctabhi = 0;
492b1707c5dSDavid du Colombier 
493b1707c5dSDavid du Colombier 	if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
4943d56950aSDavid du Colombier 		dprint("ahciflushcache: fail %lux\n", pc->p->task);
495b1707c5dSDavid du Colombier //		preg( pc->m->fis.r, 20);
496b1707c5dSDavid du Colombier 		return -1;
497b1707c5dSDavid du Colombier 	}
498b1707c5dSDavid du Colombier 	return 0;
499b1707c5dSDavid du Colombier }
500b1707c5dSDavid du Colombier 
501b1707c5dSDavid du Colombier static ushort
502b1707c5dSDavid du Colombier gbit16(void *a)
503b1707c5dSDavid du Colombier {
504b1707c5dSDavid du Colombier 	uchar *i;
505b1707c5dSDavid du Colombier 
506b1707c5dSDavid du Colombier 	i = a;
50791157df7SDavid du Colombier 	return i[1]<<8 | i[0];
508b1707c5dSDavid du Colombier }
509b1707c5dSDavid du Colombier 
51091b330d9SDavid du Colombier static ulong
511b1707c5dSDavid du Colombier gbit32(void *a)
512b1707c5dSDavid du Colombier {
51391b330d9SDavid du Colombier 	ulong j;
514b1707c5dSDavid du Colombier 	uchar *i;
515b1707c5dSDavid du Colombier 
516b1707c5dSDavid du Colombier 	i = a;
517b1707c5dSDavid du Colombier 	j  = i[3] << 24;
518b1707c5dSDavid du Colombier 	j |= i[2] << 16;
519b1707c5dSDavid du Colombier 	j |= i[1] << 8;
520b1707c5dSDavid du Colombier 	j |= i[0];
521b1707c5dSDavid du Colombier 	return j;
522b1707c5dSDavid du Colombier }
523b1707c5dSDavid du Colombier 
524b1707c5dSDavid du Colombier static uvlong
525b1707c5dSDavid du Colombier gbit64(void *a)
526b1707c5dSDavid du Colombier {
527b1707c5dSDavid du Colombier 	uchar *i;
528b1707c5dSDavid du Colombier 
529b1707c5dSDavid du Colombier 	i = a;
530b1707c5dSDavid du Colombier 	return (uvlong)gbit32(i+4) << 32 | gbit32(a);
531b1707c5dSDavid du Colombier }
532b1707c5dSDavid du Colombier 
533b1707c5dSDavid du Colombier static int
534b1707c5dSDavid du Colombier ahciidentify0(Aportc *pc, void *id, int atapi)
535b1707c5dSDavid du Colombier {
536b1707c5dSDavid du Colombier 	uchar *c;
537b1707c5dSDavid du Colombier 	Actab *t;
538b1707c5dSDavid du Colombier 	Alist *l;
539b1707c5dSDavid du Colombier 	Aprdt *p;
540b1707c5dSDavid du Colombier 	static uchar tab[] = { 0xec, 0xa1, };
541b1707c5dSDavid du Colombier 
542b1707c5dSDavid du Colombier 	t = pc->m->ctab;
543b1707c5dSDavid du Colombier 	c = t->cfis;
544b1707c5dSDavid du Colombier 
545b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
546b1707c5dSDavid du Colombier 	c[0] = 0x27;
547b1707c5dSDavid du Colombier 	c[1] = 0x80;
548b1707c5dSDavid du Colombier 	c[2] = tab[atapi];
549*75cb58dbSDavid du Colombier 	c[7] = Obs;
550b1707c5dSDavid du Colombier 
551b1707c5dSDavid du Colombier 	l = pc->m->list;
552b1707c5dSDavid du Colombier 	l->flags = 1<<16 | 0x5;
553b1707c5dSDavid du Colombier 	l->len = 0;
554b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
555b1707c5dSDavid du Colombier 	l->ctabhi = 0;
556b1707c5dSDavid du Colombier 
557b1707c5dSDavid du Colombier 	memset(id, 0, 0x100);
558b1707c5dSDavid du Colombier 	p = &t->prdt;
559b1707c5dSDavid du Colombier 	p->dba = PCIWADDR(id);
560b1707c5dSDavid du Colombier 	p->dbahi = 0;
561b1707c5dSDavid du Colombier 	p->count = 1<<31 | (0x200-2) | 1;
562b1707c5dSDavid du Colombier 
563b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
564b1707c5dSDavid du Colombier }
565b1707c5dSDavid du Colombier 
566b1707c5dSDavid du Colombier static vlong
567b1707c5dSDavid du Colombier ahciidentify(Aportc *pc, ushort *id)
568b1707c5dSDavid du Colombier {
569b1707c5dSDavid du Colombier 	int i, sig;
570b1707c5dSDavid du Colombier 	vlong s;
571b1707c5dSDavid du Colombier 	Aportm *m;
572b1707c5dSDavid du Colombier 
573b1707c5dSDavid du Colombier 	m = pc->m;
574b1707c5dSDavid du Colombier 	m->feat = 0;
575b1707c5dSDavid du Colombier 	m->smart = 0;
576b1707c5dSDavid du Colombier 	i = 0;
577b1707c5dSDavid du Colombier 	sig = pc->p->sig >> 16;
578b1707c5dSDavid du Colombier 	if(sig == 0xeb14){
579b1707c5dSDavid du Colombier 		m->feat |= Datapi;
580b1707c5dSDavid du Colombier 		i = 1;
581b1707c5dSDavid du Colombier 	}
582b1707c5dSDavid du Colombier 	if(ahciidentify0(pc, id, i) == -1)
583b1707c5dSDavid du Colombier 		return -1;
584b1707c5dSDavid du Colombier 
585b1707c5dSDavid du Colombier 	i = gbit16(id+83) | gbit16(id+86);
586b1707c5dSDavid du Colombier 	if(i & (1<<10)){
587b1707c5dSDavid du Colombier 		m->feat |= Dllba;
588b1707c5dSDavid du Colombier 		s = gbit64(id+100);
589b1707c5dSDavid du Colombier 	}else
590b1707c5dSDavid du Colombier 		s = gbit32(id+60);
591b1707c5dSDavid du Colombier 
592b1707c5dSDavid du Colombier 	if(m->feat&Datapi){
593b1707c5dSDavid du Colombier 		i = gbit16(id+0);
594b1707c5dSDavid du Colombier 		if(i&1)
595b1707c5dSDavid du Colombier 			m->feat |= Datapi16;
596b1707c5dSDavid du Colombier 	}
597b1707c5dSDavid du Colombier 
598b1707c5dSDavid du Colombier 	i = gbit16(id+83);
599b1707c5dSDavid du Colombier 	if((i>>14) == 1) {
600b1707c5dSDavid du Colombier 		if(i & (1<<3))
601b1707c5dSDavid du Colombier 			m->feat |= Dpower;
602b1707c5dSDavid du Colombier 		i = gbit16(id+82);
603b1707c5dSDavid du Colombier 		if(i & 1)
604b1707c5dSDavid du Colombier 			m->feat |= Dsmart;
605b1707c5dSDavid du Colombier 		if(i & (1<<14))
606b1707c5dSDavid du Colombier 			m->feat |= Dnop;
607b1707c5dSDavid du Colombier 	}
608b1707c5dSDavid du Colombier 	return s;
609b1707c5dSDavid du Colombier }
610b1707c5dSDavid du Colombier 
611b1707c5dSDavid du Colombier static int
612b1707c5dSDavid du Colombier ahciquiet(Aport *a)
613b1707c5dSDavid du Colombier {
61491b330d9SDavid du Colombier 	ulong *p, i;
615b1707c5dSDavid du Colombier 
616b1707c5dSDavid du Colombier 	p = &a->cmd;
617b1707c5dSDavid du Colombier 	*p &= ~Ast;
618b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 50){
619b1707c5dSDavid du Colombier 		if((*p & Acr) == 0)
620b1707c5dSDavid du Colombier 			goto stop;
621b1707c5dSDavid du Colombier 		asleep(50);
622b1707c5dSDavid du Colombier 	}
623b1707c5dSDavid du Colombier 	return -1;
624b1707c5dSDavid du Colombier stop:
625b1707c5dSDavid du Colombier 	if((a->task & (ASdrq|ASbsy)) == 0){
626b1707c5dSDavid du Colombier 		*p |= Ast;
627b1707c5dSDavid du Colombier 		return 0;
628b1707c5dSDavid du Colombier 	}
629b1707c5dSDavid du Colombier 
630b1707c5dSDavid du Colombier 	*p |= Aclo;
631b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 50){
632b1707c5dSDavid du Colombier 		if((*p & Aclo) == 0)
633b1707c5dSDavid du Colombier 			goto stop1;
634b1707c5dSDavid du Colombier 		asleep(50);
635b1707c5dSDavid du Colombier 	}
636b1707c5dSDavid du Colombier 	return -1;
637b1707c5dSDavid du Colombier stop1:
638b1707c5dSDavid du Colombier 	/* extra check */
6393d56950aSDavid du Colombier 	dprint("ahci: clo clear %lx\n", a->task);
640b1707c5dSDavid du Colombier 	if(a->task & ASbsy)
641b1707c5dSDavid du Colombier 		return -1;
642b1707c5dSDavid du Colombier 	*p |= Ast;
643b1707c5dSDavid du Colombier 	return 0;
644b1707c5dSDavid du Colombier }
645b1707c5dSDavid du Colombier 
646b1707c5dSDavid du Colombier static int
647b1707c5dSDavid du Colombier ahcicomreset(Aportc *pc)
648b1707c5dSDavid du Colombier {
649b1707c5dSDavid du Colombier 	uchar *c;
650b1707c5dSDavid du Colombier 	Actab *t;
651b1707c5dSDavid du Colombier 	Alist *l;
652b1707c5dSDavid du Colombier 
653b1707c5dSDavid du Colombier 	dprint("ahcicomreset\n");
6543d56950aSDavid du Colombier 	dreg("ahci: comreset ", pc->p);
655b1707c5dSDavid du Colombier 	if(ahciquiet(pc->p) == -1){
6563d56950aSDavid du Colombier 		dprint("ahciquiet failed\n");
657b1707c5dSDavid du Colombier 		return -1;
658b1707c5dSDavid du Colombier 	}
659b1707c5dSDavid du Colombier 	dreg("comreset ", pc->p);
660b1707c5dSDavid du Colombier 
661b1707c5dSDavid du Colombier 	t = pc->m->ctab;
662b1707c5dSDavid du Colombier 	c = t->cfis;
663b1707c5dSDavid du Colombier 
664b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
665b1707c5dSDavid du Colombier 	c[0] = 0x27;
666b1707c5dSDavid du Colombier 	c[1] = 0x00;
667*75cb58dbSDavid du Colombier 	c[7] = Obs;
668b1707c5dSDavid du Colombier 	c[15] = 1<<2;		/* srst */
669b1707c5dSDavid du Colombier 
670b1707c5dSDavid du Colombier 	l = pc->m->list;
671b1707c5dSDavid du Colombier 	l->flags = Lclear | Lreset | 0x5;
672b1707c5dSDavid du Colombier 	l->len = 0;
673b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
674b1707c5dSDavid du Colombier 	l->ctabhi = 0;
675b1707c5dSDavid du Colombier 
676b1707c5dSDavid du Colombier 	if(ahciwait(pc, 500) == -1){
6773d56950aSDavid du Colombier 		dprint("ahcicomreset: first command failed\n");
678b1707c5dSDavid du Colombier 		return -1;
679b1707c5dSDavid du Colombier 	}
680b1707c5dSDavid du Colombier 	microdelay(250);
681b1707c5dSDavid du Colombier 	dreg("comreset ", pc->p);
682b1707c5dSDavid du Colombier 
683b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
684b1707c5dSDavid du Colombier 	c[0] = 0x27;
685b1707c5dSDavid du Colombier 	c[1] = 0x00;
686*75cb58dbSDavid du Colombier 	c[7] = Obs;
687b1707c5dSDavid du Colombier 
688b1707c5dSDavid du Colombier 	l = pc->m->list;
689b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
690b1707c5dSDavid du Colombier 	l->len = 0;
691b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
692b1707c5dSDavid du Colombier 	l->ctabhi = 0;
693b1707c5dSDavid du Colombier 
694b1707c5dSDavid du Colombier 	if(ahciwait(pc, 150) == -1){
6953d56950aSDavid du Colombier 		dprint("ahcicomreset: second command failed\n");
696b1707c5dSDavid du Colombier 		return -1;
697b1707c5dSDavid du Colombier 	}
698b1707c5dSDavid du Colombier 	dreg("comreset ", pc->p);
699b1707c5dSDavid du Colombier 	return 0;
700b1707c5dSDavid du Colombier }
701b1707c5dSDavid du Colombier 
702b1707c5dSDavid du Colombier static int
703b1707c5dSDavid du Colombier ahciidle(Aport *port)
704b1707c5dSDavid du Colombier {
70591b330d9SDavid du Colombier 	ulong *p, i, r;
706b1707c5dSDavid du Colombier 
707b1707c5dSDavid du Colombier 	p = &port->cmd;
708b1707c5dSDavid du Colombier 	if((*p & Arun) == 0)
709b1707c5dSDavid du Colombier 		return 0;
710b1707c5dSDavid du Colombier 	*p &= ~Ast;
711b1707c5dSDavid du Colombier 	r = 0;
712b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 25){
713b1707c5dSDavid du Colombier 		if((*p & Acr) == 0)
714b1707c5dSDavid du Colombier 			goto stop;
715b1707c5dSDavid du Colombier 		asleep(25);
716b1707c5dSDavid du Colombier 	}
717b1707c5dSDavid du Colombier 	r = -1;
718b1707c5dSDavid du Colombier stop:
719b1707c5dSDavid du Colombier 	if((*p & Afre) == 0)
720b1707c5dSDavid du Colombier 		return r;
721b1707c5dSDavid du Colombier 	*p &= ~Afre;
722b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 25){
723b1707c5dSDavid du Colombier 		if((*p & Afre) == 0)
724b1707c5dSDavid du Colombier 			return 0;
725b1707c5dSDavid du Colombier 		asleep(25);
726b1707c5dSDavid du Colombier 	}
727b1707c5dSDavid du Colombier 	return -1;
728b1707c5dSDavid du Colombier }
729b1707c5dSDavid du Colombier 
730b1707c5dSDavid du Colombier /*
731b1707c5dSDavid du Colombier  * § 6.2.2.1  first part; comreset handled by reset disk.
732b1707c5dSDavid du Colombier  *	- remainder is handled by configdisk.
733b1707c5dSDavid du Colombier  *	- ahcirecover is a quick recovery from a failed command.
734b1707c5dSDavid du Colombier  */
73591b330d9SDavid du Colombier static int
736b1707c5dSDavid du Colombier ahciswreset(Aportc *pc)
737b1707c5dSDavid du Colombier {
738b1707c5dSDavid du Colombier 	int i;
739b1707c5dSDavid du Colombier 
740b1707c5dSDavid du Colombier 	i = ahciidle(pc->p);
741b1707c5dSDavid du Colombier 	pc->p->cmd |= Afre;
742b1707c5dSDavid du Colombier 	if(i == -1)
743b1707c5dSDavid du Colombier 		return -1;
744b1707c5dSDavid du Colombier 	if(pc->p->task & (ASdrq|ASbsy))
745b1707c5dSDavid du Colombier 		return -1;
746b1707c5dSDavid du Colombier 	return 0;
747b1707c5dSDavid du Colombier }
748b1707c5dSDavid du Colombier 
74991b330d9SDavid du Colombier static int
750b1707c5dSDavid du Colombier ahcirecover(Aportc *pc)
751b1707c5dSDavid du Colombier {
752b1707c5dSDavid du Colombier 	ahciswreset(pc);
753b1707c5dSDavid du Colombier 	pc->p->cmd |= Ast;
754b1707c5dSDavid du Colombier 	if(setudmamode(pc, 5) == -1)
755b1707c5dSDavid du Colombier 		return -1;
756b1707c5dSDavid du Colombier 	return 0;
757b1707c5dSDavid du Colombier }
758b1707c5dSDavid du Colombier 
759b1707c5dSDavid du Colombier static void*
760b1707c5dSDavid du Colombier malign(int size, int align)
761b1707c5dSDavid du Colombier {
762b1707c5dSDavid du Colombier 	void *v;
763b1707c5dSDavid du Colombier 
764b1707c5dSDavid du Colombier 	v = xspanalloc(size, align, 0);
765b1707c5dSDavid du Colombier 	memset(v, 0, size);
766b1707c5dSDavid du Colombier 	return v;
767b1707c5dSDavid du Colombier }
768b1707c5dSDavid du Colombier 
769b1707c5dSDavid du Colombier static void
770b1707c5dSDavid du Colombier setupfis(Afis *f)
771b1707c5dSDavid du Colombier {
772b1707c5dSDavid du Colombier 	f->base = malign(0x100, 0x100);
773b1707c5dSDavid du Colombier 	f->d = f->base + 0;
774b1707c5dSDavid du Colombier 	f->p = f->base + 0x20;
775b1707c5dSDavid du Colombier 	f->r = f->base + 0x40;
776b1707c5dSDavid du Colombier 	f->u = f->base + 0x60;
77791b330d9SDavid du Colombier 	f->devicebits = (ulong*)(f->base + 0x58);
778b1707c5dSDavid du Colombier }
779b1707c5dSDavid du Colombier 
780b1707c5dSDavid du Colombier static void
781b1707c5dSDavid du Colombier ahciwakeup(Aport *p)
782b1707c5dSDavid du Colombier {
783b1707c5dSDavid du Colombier 	ushort s;
784b1707c5dSDavid du Colombier 
785b1707c5dSDavid du Colombier 	s = p->sstatus;
786b1707c5dSDavid du Colombier 	if((s & 0xF00) != 0x600)
787b1707c5dSDavid du Colombier 		return;
788b1707c5dSDavid du Colombier 	if((s & 7) != 1){		/* not (device, no phy) */
7893d56950aSDavid du Colombier 		iprint("ahci: slumbering drive unwakable %ux\n", s);
790b1707c5dSDavid du Colombier 		return;
791b1707c5dSDavid du Colombier 	}
792b1707c5dSDavid du Colombier 	p->sctl = 3*Aipm | 0*Aspd | Adet;
793b1707c5dSDavid du Colombier 	delay(1);
794b1707c5dSDavid du Colombier 	p->sctl &= ~7;
795b1707c5dSDavid du Colombier //	iprint("ahci: wake %ux -> %ux\n", s, p->sstatus);
796b1707c5dSDavid du Colombier }
797b1707c5dSDavid du Colombier 
798b1707c5dSDavid du Colombier static int
799b1707c5dSDavid du Colombier ahciconfigdrive(Ahba *h, Aportc *c, int mode)
800b1707c5dSDavid du Colombier {
801b1707c5dSDavid du Colombier 	Aportm *m;
802b1707c5dSDavid du Colombier 	Aport *p;
803b1707c5dSDavid du Colombier 
804b1707c5dSDavid du Colombier 	p = c->p;
805b1707c5dSDavid du Colombier 	m = c->m;
806b1707c5dSDavid du Colombier 
807b1707c5dSDavid du Colombier 	if(m->list == 0){
808b1707c5dSDavid du Colombier 		setupfis(&m->fis);
809b1707c5dSDavid du Colombier 		m->list = malign(sizeof *m->list, 1024);
810b1707c5dSDavid du Colombier 		m->ctab = malign(sizeof *m->ctab, 128);
811b1707c5dSDavid du Colombier 	}
812b1707c5dSDavid du Colombier 
813b1707c5dSDavid du Colombier 	if(p->sstatus & 3 && h->cap & Hsss){
814b1707c5dSDavid du Colombier 		/* device connected & staggered spin-up */
8153d56950aSDavid du Colombier 		dprint("ahci: configdrive:  spinning up ... [%lux]\n",
8163d56950aSDavid du Colombier 			p->sstatus);
817b1707c5dSDavid du Colombier 		p->cmd |= Apod|Asud;
818b1707c5dSDavid du Colombier 		asleep(1400);
819b1707c5dSDavid du Colombier 	}
820b1707c5dSDavid du Colombier 
821b1707c5dSDavid du Colombier 	p->serror = SerrAll;
822b1707c5dSDavid du Colombier 
823b1707c5dSDavid du Colombier 	p->list = PCIWADDR(m->list);
824b1707c5dSDavid du Colombier 	p->listhi = 0;
825b1707c5dSDavid du Colombier 	p->fis = PCIWADDR(m->fis.base);
826b1707c5dSDavid du Colombier 	p->fishi = 0;
827b1707c5dSDavid du Colombier 	p->cmd |= Afre|Ast;
828b1707c5dSDavid du Colombier 
829b1707c5dSDavid du Colombier 	if((p->sstatus & 0xF0F) == 0x601) /* drive coming up in slumbering? */
830b1707c5dSDavid du Colombier 		ahciwakeup(p);
831b1707c5dSDavid du Colombier 
832b1707c5dSDavid du Colombier 	/* disable power managment sequence from book. */
83391157df7SDavid du Colombier 	p->sctl = (3*Aipm) | (mode*Aspd) | (0*Adet);
834b1707c5dSDavid du Colombier 	p->cmd &= ~Aalpe;
835b1707c5dSDavid du Colombier 
836b1707c5dSDavid du Colombier 	p->ie = IEM;
837b1707c5dSDavid du Colombier 
838b1707c5dSDavid du Colombier 	return 0;
839b1707c5dSDavid du Colombier }
840b1707c5dSDavid du Colombier 
841b1707c5dSDavid du Colombier static int
842b1707c5dSDavid du Colombier ahcienable(Ahba *h)
843b1707c5dSDavid du Colombier {
844b1707c5dSDavid du Colombier 	h->ghc |= Hie;
845b1707c5dSDavid du Colombier 	return 0;
846b1707c5dSDavid du Colombier }
847b1707c5dSDavid du Colombier 
848b1707c5dSDavid du Colombier static int
849b1707c5dSDavid du Colombier ahcidisable(Ahba *h)
850b1707c5dSDavid du Colombier {
851b1707c5dSDavid du Colombier 	h->ghc &= ~Hie;
852b1707c5dSDavid du Colombier 	return 0;
853b1707c5dSDavid du Colombier }
854b1707c5dSDavid du Colombier 
855b1707c5dSDavid du Colombier static int
856b1707c5dSDavid du Colombier countbits(ulong u)
857b1707c5dSDavid du Colombier {
858b1707c5dSDavid du Colombier 	int i, n;
859b1707c5dSDavid du Colombier 
860b1707c5dSDavid du Colombier 	n = 0;
861b1707c5dSDavid du Colombier 	for(i = 0; i < 32; i++)
862b1707c5dSDavid du Colombier 		if(u & (1<<i))
863b1707c5dSDavid du Colombier 			n++;
864b1707c5dSDavid du Colombier 	return n;
865b1707c5dSDavid du Colombier }
866b1707c5dSDavid du Colombier 
867b1707c5dSDavid du Colombier static int
868b1707c5dSDavid du Colombier ahciconf(Ctlr *ctlr)
869b1707c5dSDavid du Colombier {
870b1707c5dSDavid du Colombier 	Ahba *h;
87191b330d9SDavid du Colombier 	ulong u;
872b1707c5dSDavid du Colombier 
873b1707c5dSDavid du Colombier 	h = ctlr->hba = (Ahba*)ctlr->mmio;
874b1707c5dSDavid du Colombier 	u = h->cap;
875b1707c5dSDavid du Colombier 
876b1707c5dSDavid du Colombier 	if((u&Hsam) == 0)
877b1707c5dSDavid du Colombier 		h->ghc |= Hae;
878b1707c5dSDavid du Colombier 
879*75cb58dbSDavid du Colombier 	dprint("#S/sd%c: type %s port %#p: sss %ld ncs %ld coal %ld "
880014ad43fSDavid du Colombier 		"mports %ld led %ld clo %ld ems %ld\n",
881014ad43fSDavid du Colombier 		ctlr->sdev->idno, tname[ctlr->type], h,
882b1707c5dSDavid du Colombier 		(u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1, u & 0x1f, (u>>25) & 1,
883b1707c5dSDavid du Colombier 		(u>>24) & 1, (u>>6) & 1);
884b1707c5dSDavid du Colombier 	return countbits(h->pi);
885b1707c5dSDavid du Colombier }
886b1707c5dSDavid du Colombier 
887b1707c5dSDavid du Colombier static int
888b1707c5dSDavid du Colombier ahcihbareset(Ahba *h)
889b1707c5dSDavid du Colombier {
890b1707c5dSDavid du Colombier 	int wait;
891b1707c5dSDavid du Colombier 
892b1707c5dSDavid du Colombier 	h->ghc |= 1;
893b1707c5dSDavid du Colombier 	for(wait = 0; wait < 1000; wait += 100){
894b1707c5dSDavid du Colombier 		if(h->ghc == 0)
895b1707c5dSDavid du Colombier 			return 0;
896b1707c5dSDavid du Colombier 		delay(100);
897b1707c5dSDavid du Colombier 	}
898b1707c5dSDavid du Colombier 	return -1;
899b1707c5dSDavid du Colombier }
900b1707c5dSDavid du Colombier 
901b1707c5dSDavid du Colombier static void
902b1707c5dSDavid du Colombier idmove(char *p, ushort *a, int n)
903b1707c5dSDavid du Colombier {
904b1707c5dSDavid du Colombier 	int i;
905b1707c5dSDavid du Colombier 	char *op, *e;
906b1707c5dSDavid du Colombier 
907b1707c5dSDavid du Colombier 	op = p;
908b1707c5dSDavid du Colombier 	for(i = 0; i < n/2; i++){
909b1707c5dSDavid du Colombier 		*p++ = a[i] >> 8;
910b1707c5dSDavid du Colombier 		*p++ = a[i];
911b1707c5dSDavid du Colombier 	}
912b1707c5dSDavid du Colombier 	*p = 0;
913b1707c5dSDavid du Colombier 	while(p > op && *--p == ' ')
914b1707c5dSDavid du Colombier 		*p = 0;
915b1707c5dSDavid du Colombier 	e = p;
91691157df7SDavid du Colombier 	for (p = op; *p == ' '; p++)
91791157df7SDavid du Colombier 		;
918b1707c5dSDavid du Colombier 	memmove(op, p, n - (e - p));
919b1707c5dSDavid du Colombier }
920b1707c5dSDavid du Colombier 
921b1707c5dSDavid du Colombier static int
922b1707c5dSDavid du Colombier identify(Drive *d)
923b1707c5dSDavid du Colombier {
92491b330d9SDavid du Colombier 	ushort *id;
925b1707c5dSDavid du Colombier 	vlong osectors, s;
926b1707c5dSDavid du Colombier 	uchar oserial[21];
927b1707c5dSDavid du Colombier 	SDunit *u;
928b1707c5dSDavid du Colombier 
929b1707c5dSDavid du Colombier 	id = d->info;
930b1707c5dSDavid du Colombier 	s = ahciidentify(&d->portc, id);
931b1707c5dSDavid du Colombier 	if(s == -1){
932b1707c5dSDavid du Colombier 		d->state = Derror;
933b1707c5dSDavid du Colombier 		return -1;
934b1707c5dSDavid du Colombier 	}
935b1707c5dSDavid du Colombier 	osectors = d->sectors;
936b1707c5dSDavid du Colombier 	memmove(oserial, d->serial, sizeof d->serial);
937b1707c5dSDavid du Colombier 
93858db92f4SDavid du Colombier 	u = d->unit;
939b1707c5dSDavid du Colombier 	d->sectors = s;
94058db92f4SDavid du Colombier 	d->secsize = u->secsize;
94158db92f4SDavid du Colombier 	if(d->secsize == 0)
94258db92f4SDavid du Colombier 		d->secsize = 512;		/* default */
943b1707c5dSDavid du Colombier 	d->smartrs = 0;
944b1707c5dSDavid du Colombier 
945b1707c5dSDavid du Colombier 	idmove(d->serial, id+10, 20);
946b1707c5dSDavid du Colombier 	idmove(d->firmware, id+23, 8);
947b1707c5dSDavid du Colombier 	idmove(d->model, id+27, 40);
948b1707c5dSDavid du Colombier 
949b1707c5dSDavid du Colombier 	memset(u->inquiry, 0, sizeof u->inquiry);
950b1707c5dSDavid du Colombier 	u->inquiry[2] = 2;
951b1707c5dSDavid du Colombier 	u->inquiry[3] = 2;
952b1707c5dSDavid du Colombier 	u->inquiry[4] = sizeof u->inquiry - 4;
953b1707c5dSDavid du Colombier 	memmove(u->inquiry+8, d->model, 40);
954b1707c5dSDavid du Colombier 
95591157df7SDavid du Colombier 	if(osectors != s || memcmp(oserial, d->serial, sizeof oserial) != 0){
956b1707c5dSDavid du Colombier 		d->mediachange = 1;
957b1707c5dSDavid du Colombier 		u->sectors = 0;
958b1707c5dSDavid du Colombier 	}
959b1707c5dSDavid du Colombier 	return 0;
960b1707c5dSDavid du Colombier }
961b1707c5dSDavid du Colombier 
962b1707c5dSDavid du Colombier static void
963b1707c5dSDavid du Colombier clearci(Aport *p)
964b1707c5dSDavid du Colombier {
965b1707c5dSDavid du Colombier 	if(p->cmd & Ast) {
966b1707c5dSDavid du Colombier 		p->cmd &= ~Ast;
967b1707c5dSDavid du Colombier 		p->cmd |=  Ast;
968b1707c5dSDavid du Colombier 	}
969b1707c5dSDavid du Colombier }
970b1707c5dSDavid du Colombier 
971b1707c5dSDavid du Colombier static void
972b1707c5dSDavid du Colombier updatedrive(Drive *d)
973b1707c5dSDavid du Colombier {
97491b330d9SDavid du Colombier 	ulong cause, serr, s0, pr, ewake;
975b1707c5dSDavid du Colombier 	char *name;
976b1707c5dSDavid du Colombier 	Aport *p;
97791b330d9SDavid du Colombier 	static ulong last;
978b1707c5dSDavid du Colombier 
979b1707c5dSDavid du Colombier 	pr = 1;
980b1707c5dSDavid du Colombier 	ewake = 0;
981b1707c5dSDavid du Colombier 	p = d->port;
982b1707c5dSDavid du Colombier 	cause = p->isr;
983b1707c5dSDavid du Colombier 	serr = p->serror;
984b1707c5dSDavid du Colombier 	p->isr = cause;
985b1707c5dSDavid du Colombier 	name = "??";
986b1707c5dSDavid du Colombier 	if(d->unit && d->unit->name)
987b1707c5dSDavid du Colombier 		name = d->unit->name;
988b1707c5dSDavid du Colombier 
989b1707c5dSDavid du Colombier 	if(p->ci == 0){
990b1707c5dSDavid du Colombier 		d->portm.flag |= Fdone;
991b1707c5dSDavid du Colombier 		wakeup(&d->portm);
992b1707c5dSDavid du Colombier 		pr = 0;
993b1707c5dSDavid du Colombier 	}else if(cause & Adps)
994b1707c5dSDavid du Colombier 		pr = 0;
995b1707c5dSDavid du Colombier 	if(cause & Ifatal){
996b1707c5dSDavid du Colombier 		ewake = 1;
9973d56950aSDavid du Colombier 		dprint("ahci: updatedrive: fatal\n");
998b1707c5dSDavid du Colombier 	}
999b1707c5dSDavid du Colombier 	if(cause & Adhrs){
100091157df7SDavid du Colombier 		if(p->task & (1<<5|1)){
10013d56950aSDavid du Colombier 			dprint("ahci: Adhrs cause %lux serr %lux task %lux\n",
1002b1707c5dSDavid du Colombier 				cause, serr, p->task);
1003b1707c5dSDavid du Colombier 			d->portm.flag |= Ferror;
1004b1707c5dSDavid du Colombier 			ewake = 1;
1005b1707c5dSDavid du Colombier 		}
1006b1707c5dSDavid du Colombier 		pr = 0;
1007b1707c5dSDavid du Colombier 	}
1008b1707c5dSDavid du Colombier 	if(p->task & 1 && last != cause)
10093d56950aSDavid du Colombier 		dprint("%s: err ca %lux serr %lux task %lux sstat %lux\n",
10103d56950aSDavid du Colombier 			name, cause, serr, p->task, p->sstatus);
1011b1707c5dSDavid du Colombier 	if(pr)
101291b330d9SDavid du Colombier 		dprint("%s: upd %lux ta %lux\n", name, cause, p->task);
1013b1707c5dSDavid du Colombier 
1014b1707c5dSDavid du Colombier 	if(cause & (Aprcs|Aifs)){
1015b1707c5dSDavid du Colombier 		s0 = d->state;
1016b1707c5dSDavid du Colombier 		switch(p->sstatus & 7){
1017b1707c5dSDavid du Colombier 		case 0:				/* no device */
1018b1707c5dSDavid du Colombier 			d->state = Dmissing;
1019b1707c5dSDavid du Colombier 			break;
1020b1707c5dSDavid du Colombier 		case 1:				/* device but no phy comm. */
1021b1707c5dSDavid du Colombier 			if((p->sstatus & 0xF00) == 0x600)
1022b1707c5dSDavid du Colombier 				d->state = Dnew; /* slumbering */
1023b1707c5dSDavid du Colombier 			else
1024b1707c5dSDavid du Colombier 				d->state = Derror;
1025b1707c5dSDavid du Colombier 			break;
1026b1707c5dSDavid du Colombier 		case 3:				/* device & phy comm. estab. */
102791157df7SDavid du Colombier 			/* power mgnt crap for surprise removal */
1028b1707c5dSDavid du Colombier 			p->ie |= Aprcs|Apcs;	/* is this required? */
1029b1707c5dSDavid du Colombier 			d->state = Dreset;
1030b1707c5dSDavid du Colombier 			break;
1031b1707c5dSDavid du Colombier 		case 4:				/* phy off-line */
1032b1707c5dSDavid du Colombier 			d->state = Doffline;
1033b1707c5dSDavid du Colombier 			break;
1034b1707c5dSDavid du Colombier 		}
10353d56950aSDavid du Colombier 		dprint("%s: %s → %s [Apcrs] %lux\n", name,
10363d56950aSDavid du Colombier 			diskstates[s0], diskstates[d->state], p->sstatus);
1037b1707c5dSDavid du Colombier 		/* print pulled message here. */
1038b1707c5dSDavid du Colombier 		if(s0 == Dready && d->state != Dready)
1039b1707c5dSDavid du Colombier 			idprint("%s: pulled\n", name);
1040b1707c5dSDavid du Colombier 		if(d->state != Dready)
1041b1707c5dSDavid du Colombier 			d->portm.flag |= Ferror;
1042b1707c5dSDavid du Colombier 		ewake = 1;
1043b1707c5dSDavid du Colombier 	}
1044b1707c5dSDavid du Colombier 	p->serror = serr;
1045b1707c5dSDavid du Colombier 	if(ewake){
1046b1707c5dSDavid du Colombier 		clearci(p);
1047b1707c5dSDavid du Colombier 		wakeup(&d->portm);
1048b1707c5dSDavid du Colombier 	}
1049b1707c5dSDavid du Colombier 	last = cause;
1050b1707c5dSDavid du Colombier }
1051b1707c5dSDavid du Colombier 
1052b1707c5dSDavid du Colombier static void
1053b1707c5dSDavid du Colombier pstatus(Drive *d, ulong s)
1054b1707c5dSDavid du Colombier {
1055b1707c5dSDavid du Colombier 	/*
1056b1707c5dSDavid du Colombier 	 * bogus code because the first interrupt is currently dropped.
105791157df7SDavid du Colombier 	 * likely my fault.  serror may be cleared at the wrong time.
1058b1707c5dSDavid du Colombier 	 */
1059b1707c5dSDavid du Colombier 	switch(s){
1060b1707c5dSDavid du Colombier 	case 0:			/* no device */
1061b1707c5dSDavid du Colombier 		d->state = Dmissing;
1062b1707c5dSDavid du Colombier 		break;
1063b1707c5dSDavid du Colombier 	case 1:			/* device but no phy. comm. */
1064b1707c5dSDavid du Colombier 		break;
1065b1707c5dSDavid du Colombier 	case 2:			/* should this be missing?  need testcase. */
10663d56950aSDavid du Colombier 		dprint("ahci: pstatus 2\n");
1067b1707c5dSDavid du Colombier 		/* fallthrough */
1068b1707c5dSDavid du Colombier 	case 3:			/* device & phy. comm. */
1069b1707c5dSDavid du Colombier 		d->wait = 0;
1070b1707c5dSDavid du Colombier 		d->state = Dnew;
1071b1707c5dSDavid du Colombier 		break;
1072b1707c5dSDavid du Colombier 	case 4:			/* offline */
1073b1707c5dSDavid du Colombier 		d->state = Doffline;
1074b1707c5dSDavid du Colombier 		break;
1075*75cb58dbSDavid du Colombier 	case 6:			/* does this make sense? */
1076b1707c5dSDavid du Colombier 		d->state = Dnew;
1077b1707c5dSDavid du Colombier 		break;
1078b1707c5dSDavid du Colombier 	}
1079b1707c5dSDavid du Colombier }
1080b1707c5dSDavid du Colombier 
1081b1707c5dSDavid du Colombier static int
1082b1707c5dSDavid du Colombier configdrive(Drive *d)
1083b1707c5dSDavid du Colombier {
1084b1707c5dSDavid du Colombier 	if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1)
1085b1707c5dSDavid du Colombier 		return -1;
1086b1707c5dSDavid du Colombier 	ilock(d);
1087b1707c5dSDavid du Colombier 	pstatus(d, d->port->sstatus & 7);
1088b1707c5dSDavid du Colombier 	iunlock(d);
1089b1707c5dSDavid du Colombier 	return 0;
1090b1707c5dSDavid du Colombier }
1091b1707c5dSDavid du Colombier 
1092b1707c5dSDavid du Colombier static void
1093b1707c5dSDavid du Colombier resetdisk(Drive *d)
1094b1707c5dSDavid du Colombier {
1095b1707c5dSDavid du Colombier 	uint state, det, stat;
1096b1707c5dSDavid du Colombier 	Aport *p;
1097b1707c5dSDavid du Colombier 
1098b1707c5dSDavid du Colombier 	p = d->port;
1099b1707c5dSDavid du Colombier 	det = p->sctl & 7;
1100b1707c5dSDavid du Colombier 	stat = p->sstatus & 7;
1101b1707c5dSDavid du Colombier 	state = (p->cmd>>28) & 0xf;
11023d56950aSDavid du Colombier 	dprint("ahci: resetdisk: icc %ux  det %d sdet %d\n", state, det, stat);
1103014ad43fSDavid du Colombier 
1104b1707c5dSDavid du Colombier 	ilock(d);
1105b1707c5dSDavid du Colombier 	state = d->state;
1106b1707c5dSDavid du Colombier 	if(d->state != Dready || d->state != Dnew)
1107b1707c5dSDavid du Colombier 		d->portm.flag |= Ferror;
1108b1707c5dSDavid du Colombier 	clearci(p);			/* satisfy sleep condition. */
1109b1707c5dSDavid du Colombier 	wakeup(&d->portm);
1110014ad43fSDavid du Colombier 	if(stat != 3){		/* device absent or phy not communicating? */
1111014ad43fSDavid du Colombier 		d->state = Dportreset;
1112014ad43fSDavid du Colombier 		iunlock(d);
1113014ad43fSDavid du Colombier 		return;
1114014ad43fSDavid du Colombier 	}
111581ede731SDavid du Colombier 	d->state = Derror;
1116b1707c5dSDavid du Colombier 	iunlock(d);
1117b1707c5dSDavid du Colombier 
1118b1707c5dSDavid du Colombier 	qlock(&d->portm);
1119b1707c5dSDavid du Colombier 	if(p->cmd&Ast && ahciswreset(&d->portc) == -1){
1120b1707c5dSDavid du Colombier 		ilock(d);
1121b1707c5dSDavid du Colombier 		d->state = Dportreset;	/* get a bigger stick. */
1122b1707c5dSDavid du Colombier 		iunlock(d);
1123b1707c5dSDavid du Colombier 	} else {
1124b1707c5dSDavid du Colombier 		ilock(d);
1125b1707c5dSDavid du Colombier 		d->state = Dmissing;
1126b1707c5dSDavid du Colombier 		iunlock(d);
1127b1707c5dSDavid du Colombier 
1128b1707c5dSDavid du Colombier 		configdrive(d);
1129b1707c5dSDavid du Colombier 	}
11303d56950aSDavid du Colombier 	dprint("ahci: resetdisk: %s → %s\n",
11313d56950aSDavid du Colombier 		diskstates[state], diskstates[d->state]);
1132b1707c5dSDavid du Colombier 	qunlock(&d->portm);
1133b1707c5dSDavid du Colombier }
1134b1707c5dSDavid du Colombier 
1135b1707c5dSDavid du Colombier static int
1136b1707c5dSDavid du Colombier newdrive(Drive *d)
1137b1707c5dSDavid du Colombier {
113858db92f4SDavid du Colombier 	char *name;
1139b1707c5dSDavid du Colombier 	Aportc *c;
1140b1707c5dSDavid du Colombier 	Aportm *m;
1141b1707c5dSDavid du Colombier 
1142b1707c5dSDavid du Colombier 	c = &d->portc;
1143b1707c5dSDavid du Colombier 	m = &d->portm;
1144b1707c5dSDavid du Colombier 
1145b1707c5dSDavid du Colombier 	name = d->unit->name;
1146b1707c5dSDavid du Colombier 	if(name == 0)
1147b1707c5dSDavid du Colombier 		name = "??";
1148b1707c5dSDavid du Colombier 
1149b1707c5dSDavid du Colombier 	if(d->port->task == 0x80)
1150b1707c5dSDavid du Colombier 		return -1;
1151b1707c5dSDavid du Colombier 	qlock(c->m);
1152b1707c5dSDavid du Colombier 	if(setudmamode(c, 5) == -1){
1153b1707c5dSDavid du Colombier 		dprint("%s: can't set udma mode\n", name);
1154b1707c5dSDavid du Colombier 		goto lose;
1155b1707c5dSDavid du Colombier 	}
1156b1707c5dSDavid du Colombier 	if(identify(d) == -1){
1157b1707c5dSDavid du Colombier 		dprint("%s: identify failure\n", name);
1158b1707c5dSDavid du Colombier 		goto lose;
1159b1707c5dSDavid du Colombier 	}
1160b1707c5dSDavid du Colombier 	if(m->feat & Dpower && setfeatures(c, 0x85) == -1){
1161b1707c5dSDavid du Colombier 		m->feat &= ~Dpower;
1162b1707c5dSDavid du Colombier 		if(ahcirecover(c) == -1)
1163b1707c5dSDavid du Colombier 			goto lose;
1164b1707c5dSDavid du Colombier 	}
1165b1707c5dSDavid du Colombier 
1166b1707c5dSDavid du Colombier 	ilock(d);
1167b1707c5dSDavid du Colombier 	d->state = Dready;
1168b1707c5dSDavid du Colombier 	iunlock(d);
1169b1707c5dSDavid du Colombier 
1170b1707c5dSDavid du Colombier 	qunlock(c->m);
1171b1707c5dSDavid du Colombier 
117258db92f4SDavid du Colombier 	idprint("%s: %sLBA %,llud sectors: %s %s %s %s\n", d->unit->name,
117358db92f4SDavid du Colombier 		(m->feat & Dllba? "L": ""), d->sectors, d->model, d->firmware,
117458db92f4SDavid du Colombier 		d->serial, d->mediachange? "[mediachange]": "");
1175b1707c5dSDavid du Colombier 	return 0;
1176b1707c5dSDavid du Colombier 
1177b1707c5dSDavid du Colombier lose:
117881ede731SDavid du Colombier 	idprint("%s: can't be initialized\n", d->unit->name);
117981ede731SDavid du Colombier 	ilock(d);
118081ede731SDavid du Colombier 	d->state = Dnull;
118181ede731SDavid du Colombier 	iunlock(d);
1182b1707c5dSDavid du Colombier 	qunlock(c->m);
1183b1707c5dSDavid du Colombier 	return -1;
1184b1707c5dSDavid du Colombier }
1185b1707c5dSDavid du Colombier 
1186b1707c5dSDavid du Colombier static void
1187b1707c5dSDavid du Colombier westerndigitalhung(Drive *d)
1188b1707c5dSDavid du Colombier {
1189f3112f79SDavid du Colombier 	if((d->portm.feat&Datapi) == 0 && d->active &&
1190f3112f79SDavid du Colombier 	    TK2MS(MACHP(0)->ticks - d->intick) > 5000){
1191014ad43fSDavid du Colombier 		dprint("%s: drive hung; resetting [%lux] ci %lx\n",
1192b1707c5dSDavid du Colombier 			d->unit->name, d->port->task, d->port->ci);
1193b1707c5dSDavid du Colombier 		d->state = Dreset;
1194b1707c5dSDavid du Colombier 	}
1195b1707c5dSDavid du Colombier }
1196b1707c5dSDavid du Colombier 
1197b1707c5dSDavid du Colombier static ushort olds[NCtlr*NCtlrdrv];
1198b1707c5dSDavid du Colombier 
1199b1707c5dSDavid du Colombier static int
1200b1707c5dSDavid du Colombier doportreset(Drive *d)
1201b1707c5dSDavid du Colombier {
1202b1707c5dSDavid du Colombier 	int i;
1203b1707c5dSDavid du Colombier 
1204b1707c5dSDavid du Colombier 	i = -1;
1205b1707c5dSDavid du Colombier 	qlock(&d->portm);
1206b1707c5dSDavid du Colombier 	if(ahciportreset(&d->portc) == -1)
12073d56950aSDavid du Colombier 		dprint("ahci: doportreset: fails\n");
1208b1707c5dSDavid du Colombier 	else
1209b1707c5dSDavid du Colombier 		i = 0;
1210b1707c5dSDavid du Colombier 	qunlock(&d->portm);
12113d56950aSDavid du Colombier 	dprint("ahci: doportreset: portreset → %s  [task %lux]\n",
1212b1707c5dSDavid du Colombier 		diskstates[d->state], d->port->task);
1213b1707c5dSDavid du Colombier 	return i;
1214b1707c5dSDavid du Colombier }
1215b1707c5dSDavid du Colombier 
1216014ad43fSDavid du Colombier /* drive must be locked */
1217014ad43fSDavid du Colombier static void
1218014ad43fSDavid du Colombier statechange(Drive *d)
1219014ad43fSDavid du Colombier {
1220014ad43fSDavid du Colombier 	switch(d->state){
1221014ad43fSDavid du Colombier 	case Dnull:
1222014ad43fSDavid du Colombier 	case Doffline:
1223014ad43fSDavid du Colombier 		if(d->unit->sectors != 0){
1224014ad43fSDavid du Colombier 			d->sectors = 0;
1225014ad43fSDavid du Colombier 			d->mediachange = 1;
1226014ad43fSDavid du Colombier 		}
1227014ad43fSDavid du Colombier 		/* fallthrough */
1228014ad43fSDavid du Colombier 	case Dready:
1229014ad43fSDavid du Colombier 		d->wait = 0;
1230014ad43fSDavid du Colombier 		break;
1231014ad43fSDavid du Colombier 	}
1232014ad43fSDavid du Colombier }
1233014ad43fSDavid du Colombier 
1234b1707c5dSDavid du Colombier static void
1235b1707c5dSDavid du Colombier checkdrive(Drive *d, int i)
1236b1707c5dSDavid du Colombier {
1237b1707c5dSDavid du Colombier 	ushort s;
1238b1707c5dSDavid du Colombier 	char *name;
1239b1707c5dSDavid du Colombier 
1240c39c2eb3SDavid du Colombier 	if(d == nil) {
1241c39c2eb3SDavid du Colombier 		print("checkdrive: nil d\n");
1242c39c2eb3SDavid du Colombier 		return;
1243c39c2eb3SDavid du Colombier 	}
1244b1707c5dSDavid du Colombier 	ilock(d);
1245c39c2eb3SDavid du Colombier 	if(d->unit == nil || d->port == nil) {
1246c39c2eb3SDavid du Colombier 		if(0)
1247c39c2eb3SDavid du Colombier 			print("checkdrive: nil d->%s\n",
1248c39c2eb3SDavid du Colombier 				d->unit == nil? "unit": "port");
1249c39c2eb3SDavid du Colombier 		iunlock(d);
1250c39c2eb3SDavid du Colombier 		return;
1251c39c2eb3SDavid du Colombier 	}
1252b1707c5dSDavid du Colombier 	name = d->unit->name;
1253b1707c5dSDavid du Colombier 	s = d->port->sstatus;
125481ede731SDavid du Colombier 	if(s)
125581ede731SDavid du Colombier 		d->lastseen = MACHP(0)->ticks;
1256b1707c5dSDavid du Colombier 	if(s != olds[i]){
1257b1707c5dSDavid du Colombier 		dprint("%s: status: %04ux -> %04ux: %s\n",
1258b1707c5dSDavid du Colombier 			name, olds[i], s, diskstates[d->state]);
1259b1707c5dSDavid du Colombier 		olds[i] = s;
1260b1707c5dSDavid du Colombier 		d->wait = 0;
1261b1707c5dSDavid du Colombier 	}
1262b1707c5dSDavid du Colombier 	westerndigitalhung(d);
1263b1707c5dSDavid du Colombier 	switch(d->state){
1264b1707c5dSDavid du Colombier 	case Dnull:
1265b1707c5dSDavid du Colombier 	case Dready:
1266b1707c5dSDavid du Colombier 		break;
1267b1707c5dSDavid du Colombier 	case Dmissing:
1268b1707c5dSDavid du Colombier 	case Dnew:
1269b1707c5dSDavid du Colombier 		switch(s & 0x107){
1270b1707c5dSDavid du Colombier 		case 1:		/* no device (pm), device but no phy. comm. */
1271b1707c5dSDavid du Colombier 			ahciwakeup(d->port);
1272b1707c5dSDavid du Colombier 			/* fall through */
1273b1707c5dSDavid du Colombier 		case 0:		/* no device */
1274b1707c5dSDavid du Colombier 			break;
1275b1707c5dSDavid du Colombier 		default:
1276b1707c5dSDavid du Colombier 			dprint("%s: unknown status %04ux\n", name, s);
1277b1707c5dSDavid du Colombier 			/* fall through */
1278b1707c5dSDavid du Colombier 		case 0x100:		/* active, no device */
1279b1707c5dSDavid du Colombier 			if(++d->wait&Mphywait)
1280b1707c5dSDavid du Colombier 				break;
1281b1707c5dSDavid du Colombier reset:
1282b1707c5dSDavid du Colombier 			if(++d->mode > DMsataii)
1283b1707c5dSDavid du Colombier 				d->mode = 0;
1284b1707c5dSDavid du Colombier 			if(d->mode == DMsatai){	/* we tried everything */
1285b1707c5dSDavid du Colombier 				d->state = Dportreset;
1286b1707c5dSDavid du Colombier 				goto portreset;
1287b1707c5dSDavid du Colombier 			}
1288b1707c5dSDavid du Colombier 			dprint("%s: reset; new mode %s\n", name,
1289b1707c5dSDavid du Colombier 				modename[d->mode]);
1290b1707c5dSDavid du Colombier 			iunlock(d);
1291b1707c5dSDavid du Colombier 			resetdisk(d);
1292b1707c5dSDavid du Colombier 			ilock(d);
1293b1707c5dSDavid du Colombier 			break;
1294b1707c5dSDavid du Colombier 		case 0x103:		/* active, device, phy. comm. */
1295b1707c5dSDavid du Colombier 			if((++d->wait&Midwait) == 0){
129691b330d9SDavid du Colombier 				dprint("%s: slow reset %04ux task=%lux; %d\n",
1297b1707c5dSDavid du Colombier 					name, s, d->port->task, d->wait);
1298b1707c5dSDavid du Colombier 				goto reset;
1299b1707c5dSDavid du Colombier 			}
1300b1707c5dSDavid du Colombier 			s = (uchar)d->port->task;
1301b1707c5dSDavid du Colombier 			if(s == 0x7f || ((d->port->sig >> 16) != 0xeb14 &&
1302b1707c5dSDavid du Colombier 			    (s & ~0x17) != (1<<6)))
1303b1707c5dSDavid du Colombier 				break;
1304b1707c5dSDavid du Colombier 			iunlock(d);
1305b1707c5dSDavid du Colombier 			newdrive(d);
1306b1707c5dSDavid du Colombier 			ilock(d);
1307b1707c5dSDavid du Colombier 			break;
1308b1707c5dSDavid du Colombier 		}
1309b1707c5dSDavid du Colombier 		break;
1310b1707c5dSDavid du Colombier 	case Doffline:
1311b1707c5dSDavid du Colombier 		if(d->wait++ & Mcomrwait)
1312b1707c5dSDavid du Colombier 			break;
1313b1707c5dSDavid du Colombier 		/* fallthrough */
1314b1707c5dSDavid du Colombier 	case Derror:
1315b1707c5dSDavid du Colombier 	case Dreset:
1316b1707c5dSDavid du Colombier 		dprint("%s: reset [%s]: mode %d; status %04ux\n",
1317b1707c5dSDavid du Colombier 			name, diskstates[d->state], d->mode, s);
1318b1707c5dSDavid du Colombier 		iunlock(d);
1319b1707c5dSDavid du Colombier 		resetdisk(d);
1320b1707c5dSDavid du Colombier 		ilock(d);
1321b1707c5dSDavid du Colombier 		break;
1322b1707c5dSDavid du Colombier 	case Dportreset:
1323b1707c5dSDavid du Colombier portreset:
1324b1707c5dSDavid du Colombier 		if(d->wait++ & 0xff && (s & 0x100) == 0)
1325b1707c5dSDavid du Colombier 			break;
1326b1707c5dSDavid du Colombier 		/* device is active */
1327b1707c5dSDavid du Colombier 		dprint("%s: portreset [%s]: mode %d; status %04ux\n",
1328b1707c5dSDavid du Colombier 			name, diskstates[d->state], d->mode, s);
1329b1707c5dSDavid du Colombier 		d->portm.flag |= Ferror;
1330b1707c5dSDavid du Colombier 		clearci(d->port);
1331b1707c5dSDavid du Colombier 		wakeup(&d->portm);
1332b1707c5dSDavid du Colombier 		if((s & 7) == 0){	/* no device */
1333b1707c5dSDavid du Colombier 			d->state = Dmissing;
1334b1707c5dSDavid du Colombier 			break;
1335b1707c5dSDavid du Colombier 		}
1336b1707c5dSDavid du Colombier 		iunlock(d);
1337b1707c5dSDavid du Colombier 		doportreset(d);
1338b1707c5dSDavid du Colombier 		ilock(d);
1339b1707c5dSDavid du Colombier 		break;
1340b1707c5dSDavid du Colombier 	}
1341014ad43fSDavid du Colombier 	statechange(d);
1342b1707c5dSDavid du Colombier 	iunlock(d);
1343b1707c5dSDavid du Colombier }
1344b1707c5dSDavid du Colombier 
1345b1707c5dSDavid du Colombier static void
1346b1707c5dSDavid du Colombier satakproc(void*)
1347b1707c5dSDavid du Colombier {
1348b1707c5dSDavid du Colombier 	int i;
1349b1707c5dSDavid du Colombier 
1350b1707c5dSDavid du Colombier 	for(;;){
1351b1707c5dSDavid du Colombier 		tsleep(&up->sleep, return0, 0, Nms);
1352b1707c5dSDavid du Colombier 		for(i = 0; i < niadrive; i++)
1353b1707c5dSDavid du Colombier 			checkdrive(iadrive[i], i);
1354b1707c5dSDavid du Colombier 	}
1355b1707c5dSDavid du Colombier }
1356b1707c5dSDavid du Colombier 
1357b1707c5dSDavid du Colombier static void
1358b1707c5dSDavid du Colombier iainterrupt(Ureg*, void *a)
1359b1707c5dSDavid du Colombier {
1360b1707c5dSDavid du Colombier 	int i;
1361b1707c5dSDavid du Colombier 	ulong cause, m;
1362b1707c5dSDavid du Colombier 	Ctlr *c;
1363b1707c5dSDavid du Colombier 	Drive *d;
1364b1707c5dSDavid du Colombier 
1365b1707c5dSDavid du Colombier 	c = a;
1366b1707c5dSDavid du Colombier 	ilock(c);
1367b1707c5dSDavid du Colombier 	cause = c->hba->isr;
1368014ad43fSDavid du Colombier 	for(i = 0; i < c->mport; i++){
1369b1707c5dSDavid du Colombier 		m = 1 << i;
1370b1707c5dSDavid du Colombier 		if((cause & m) == 0)
1371b1707c5dSDavid du Colombier 			continue;
1372b1707c5dSDavid du Colombier 		d = c->rawdrive + i;
1373b1707c5dSDavid du Colombier 		ilock(d);
1374b1707c5dSDavid du Colombier 		if(d->port->isr && c->hba->pi & m)
1375b1707c5dSDavid du Colombier 			updatedrive(d);
1376b1707c5dSDavid du Colombier 		c->hba->isr = m;
1377b1707c5dSDavid du Colombier 		iunlock(d);
1378b1707c5dSDavid du Colombier 	}
1379b1707c5dSDavid du Colombier 	iunlock(c);
1380b1707c5dSDavid du Colombier }
1381b1707c5dSDavid du Colombier 
1382b1707c5dSDavid du Colombier static int
1383b1707c5dSDavid du Colombier iaverify(SDunit *u)
1384b1707c5dSDavid du Colombier {
1385b1707c5dSDavid du Colombier 	Ctlr *c;
1386b1707c5dSDavid du Colombier 	Drive *d;
1387b1707c5dSDavid du Colombier 
1388b1707c5dSDavid du Colombier 	c = u->dev->ctlr;
1389b1707c5dSDavid du Colombier 	d = c->drive[u->subno];
1390b1707c5dSDavid du Colombier 	ilock(c);
1391b1707c5dSDavid du Colombier 	ilock(d);
1392b1707c5dSDavid du Colombier 	d->unit = u;
1393b1707c5dSDavid du Colombier 	iunlock(d);
1394b1707c5dSDavid du Colombier 	iunlock(c);
1395014ad43fSDavid du Colombier 	checkdrive(d, d->driveno);		/* c->d0 + d->driveno */
1396b1707c5dSDavid du Colombier 	return 1;
1397b1707c5dSDavid du Colombier }
1398b1707c5dSDavid du Colombier 
1399b1707c5dSDavid du Colombier static int
1400b1707c5dSDavid du Colombier iaenable(SDev *s)
1401b1707c5dSDavid du Colombier {
1402b1707c5dSDavid du Colombier 	char name[32];
1403b1707c5dSDavid du Colombier 	Ctlr *c;
1404b1707c5dSDavid du Colombier 	static int once;
1405b1707c5dSDavid du Colombier 
1406b1707c5dSDavid du Colombier 	c = s->ctlr;
1407b1707c5dSDavid du Colombier 	ilock(c);
1408b1707c5dSDavid du Colombier 	if(!c->enabled) {
1409b1707c5dSDavid du Colombier 		if(once == 0) {
1410b1707c5dSDavid du Colombier 			once = 1;
1411b1707c5dSDavid du Colombier 			kproc("iasata", satakproc, 0);
1412b1707c5dSDavid du Colombier 		}
1413b1707c5dSDavid du Colombier 		if(c->ndrive == 0)
1414b1707c5dSDavid du Colombier 			panic("iaenable: zero s->ctlr->ndrive");
1415b1707c5dSDavid du Colombier 		pcisetbme(c->pci);
1416b1707c5dSDavid du Colombier 		snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1417b1707c5dSDavid du Colombier 		intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1418b1707c5dSDavid du Colombier 		/* supposed to squelch leftover interrupts here. */
1419b1707c5dSDavid du Colombier 		ahcienable(c->hba);
1420b1707c5dSDavid du Colombier 		c->enabled = 1;
1421b1707c5dSDavid du Colombier 	}
1422b1707c5dSDavid du Colombier 	iunlock(c);
1423b1707c5dSDavid du Colombier 	return 1;
1424b1707c5dSDavid du Colombier }
1425b1707c5dSDavid du Colombier 
1426b1707c5dSDavid du Colombier static int
1427b1707c5dSDavid du Colombier iadisable(SDev *s)
1428b1707c5dSDavid du Colombier {
1429b1707c5dSDavid du Colombier 	char name[32];
1430b1707c5dSDavid du Colombier 	Ctlr *c;
1431b1707c5dSDavid du Colombier 
1432b1707c5dSDavid du Colombier 	c = s->ctlr;
1433b1707c5dSDavid du Colombier 	ilock(c);
1434b1707c5dSDavid du Colombier 	ahcidisable(c->hba);
1435b1707c5dSDavid du Colombier 	snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1436b1707c5dSDavid du Colombier 	intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1437b1707c5dSDavid du Colombier 	c->enabled = 0;
1438b1707c5dSDavid du Colombier 	iunlock(c);
1439b1707c5dSDavid du Colombier 	return 1;
1440b1707c5dSDavid du Colombier }
1441b1707c5dSDavid du Colombier 
1442b1707c5dSDavid du Colombier static int
1443b1707c5dSDavid du Colombier iaonline(SDunit *unit)
1444b1707c5dSDavid du Colombier {
1445b1707c5dSDavid du Colombier 	int r;
1446b1707c5dSDavid du Colombier 	Ctlr *c;
1447b1707c5dSDavid du Colombier 	Drive *d;
1448b1707c5dSDavid du Colombier 
1449b1707c5dSDavid du Colombier 	c = unit->dev->ctlr;
1450b1707c5dSDavid du Colombier 	d = c->drive[unit->subno];
1451b1707c5dSDavid du Colombier 	r = 0;
1452b1707c5dSDavid du Colombier 
1453b1707c5dSDavid du Colombier 	if(d->portm.feat & Datapi && d->mediachange){
1454b1707c5dSDavid du Colombier 		r = scsionline(unit);
1455b1707c5dSDavid du Colombier 		if(r > 0)
1456b1707c5dSDavid du Colombier 			d->mediachange = 0;
1457b1707c5dSDavid du Colombier 		return r;
1458b1707c5dSDavid du Colombier 	}
1459b1707c5dSDavid du Colombier 
1460b1707c5dSDavid du Colombier 	ilock(d);
1461b1707c5dSDavid du Colombier 	if(d->mediachange){
1462b1707c5dSDavid du Colombier 		r = 2;
1463b1707c5dSDavid du Colombier 		d->mediachange = 0;
1464b1707c5dSDavid du Colombier 		/* devsd resets this after online is called; why? */
1465b1707c5dSDavid du Colombier 		unit->sectors = d->sectors;
146658db92f4SDavid du Colombier 		unit->secsize = 512;		/* default size */
1467b1707c5dSDavid du Colombier 	} else if(d->state == Dready)
1468b1707c5dSDavid du Colombier 		r = 1;
1469b1707c5dSDavid du Colombier 	iunlock(d);
1470b1707c5dSDavid du Colombier 	return r;
1471b1707c5dSDavid du Colombier }
1472b1707c5dSDavid du Colombier 
1473b1707c5dSDavid du Colombier /* returns locked list! */
1474b1707c5dSDavid du Colombier static Alist*
147558db92f4SDavid du Colombier ahcibuild(Drive *d, uchar *cmd, void *data, int n, vlong lba)
1476b1707c5dSDavid du Colombier {
1477b1707c5dSDavid du Colombier 	uchar *c, acmd, dir, llba;
1478b1707c5dSDavid du Colombier 	Alist *l;
1479b1707c5dSDavid du Colombier 	Actab *t;
148058db92f4SDavid du Colombier 	Aportm *m;
1481b1707c5dSDavid du Colombier 	Aprdt *p;
1482b1707c5dSDavid du Colombier 	static uchar tab[2][2] = { 0xc8, 0x25, 0xca, 0x35, };
1483b1707c5dSDavid du Colombier 
148458db92f4SDavid du Colombier 	m = &d->portm;
1485b1707c5dSDavid du Colombier 	dir = *cmd != 0x28;
1486b1707c5dSDavid du Colombier 	llba = m->feat&Dllba? 1: 0;
1487b1707c5dSDavid du Colombier 	acmd = tab[dir][llba];
1488b1707c5dSDavid du Colombier 	qlock(m);
1489b1707c5dSDavid du Colombier 	l = m->list;
1490b1707c5dSDavid du Colombier 	t = m->ctab;
1491b1707c5dSDavid du Colombier 	c = t->cfis;
1492b1707c5dSDavid du Colombier 
1493b1707c5dSDavid du Colombier 	c[0] = 0x27;
1494b1707c5dSDavid du Colombier 	c[1] = 0x80;
1495b1707c5dSDavid du Colombier 	c[2] = acmd;
1496b1707c5dSDavid du Colombier 	c[3] = 0;
1497b1707c5dSDavid du Colombier 
1498b1707c5dSDavid du Colombier 	c[4] = lba;		/* sector		lba low	7:0 */
1499b1707c5dSDavid du Colombier 	c[5] = lba >> 8;	/* cylinder low		lba mid	15:8 */
1500b1707c5dSDavid du Colombier 	c[6] = lba >> 16;	/* cylinder hi		lba hi	23:16 */
1501*75cb58dbSDavid du Colombier 	c[7] = Obs | 0x40;	/* 0x40 == lba */
1502b1707c5dSDavid du Colombier 	if(llba == 0)
1503b1707c5dSDavid du Colombier 		c[7] |= (lba>>24) & 7;
1504b1707c5dSDavid du Colombier 
1505b1707c5dSDavid du Colombier 	c[8] = lba >> 24;	/* sector (exp)		lba 	31:24 */
1506b1707c5dSDavid du Colombier 	c[9] = lba >> 32;	/* cylinder low (exp)	lba	39:32 */
1507b1707c5dSDavid du Colombier 	c[10] = lba >> 48;	/* cylinder hi (exp)	lba	48:40 */
1508b1707c5dSDavid du Colombier 	c[11] = 0;		/* features (exp); */
1509b1707c5dSDavid du Colombier 
1510b1707c5dSDavid du Colombier 	c[12] = n;		/* sector count */
1511b1707c5dSDavid du Colombier 	c[13] = n >> 8;		/* sector count (exp) */
1512b1707c5dSDavid du Colombier 	c[14] = 0;		/* r */
1513b1707c5dSDavid du Colombier 	c[15] = 0;		/* control */
1514b1707c5dSDavid du Colombier 
1515b1707c5dSDavid du Colombier 	*(ulong*)(c + 16) = 0;
1516b1707c5dSDavid du Colombier 
1517b1707c5dSDavid du Colombier 	l->flags = 1<<16 | Lpref | 0x5;	/* Lpref ?? */
1518b1707c5dSDavid du Colombier 	if(dir == Write)
1519b1707c5dSDavid du Colombier 		l->flags |= Lwrite;
1520b1707c5dSDavid du Colombier 	l->len = 0;
1521b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
1522b1707c5dSDavid du Colombier 	l->ctabhi = 0;
1523b1707c5dSDavid du Colombier 
1524b1707c5dSDavid du Colombier 	p = &t->prdt;
1525b1707c5dSDavid du Colombier 	p->dba = PCIWADDR(data);
1526b1707c5dSDavid du Colombier 	p->dbahi = 0;
152758db92f4SDavid du Colombier 	if(d->unit == nil)
152858db92f4SDavid du Colombier 		panic("ahcibuild: nil d->unit");
152958db92f4SDavid du Colombier 	p->count = 1<<31 | (d->unit->secsize*n - 2) | 1;
1530b1707c5dSDavid du Colombier 
1531b1707c5dSDavid du Colombier 	return l;
1532b1707c5dSDavid du Colombier }
1533b1707c5dSDavid du Colombier 
1534b1707c5dSDavid du Colombier static Alist*
1535b1707c5dSDavid du Colombier ahcibuildpkt(Aportm *m, SDreq *r, void *data, int n)
1536b1707c5dSDavid du Colombier {
1537b1707c5dSDavid du Colombier 	int fill, len;
1538b1707c5dSDavid du Colombier 	uchar *c;
1539b1707c5dSDavid du Colombier 	Alist *l;
1540b1707c5dSDavid du Colombier 	Actab *t;
1541b1707c5dSDavid du Colombier 	Aprdt *p;
1542b1707c5dSDavid du Colombier 
1543b1707c5dSDavid du Colombier 	qlock(m);
1544b1707c5dSDavid du Colombier 	l = m->list;
1545b1707c5dSDavid du Colombier 	t = m->ctab;
1546b1707c5dSDavid du Colombier 	c = t->cfis;
1547b1707c5dSDavid du Colombier 
1548b1707c5dSDavid du Colombier 	fill = m->feat&Datapi16? 16: 12;
1549b1707c5dSDavid du Colombier 	if((len = r->clen) > fill)
1550b1707c5dSDavid du Colombier 		len = fill;
1551b1707c5dSDavid du Colombier 	memmove(t->atapi, r->cmd, len);
1552b1707c5dSDavid du Colombier 	memset(t->atapi+len, 0, fill-len);
1553b1707c5dSDavid du Colombier 
1554b1707c5dSDavid du Colombier 	c[0] = 0x27;
1555b1707c5dSDavid du Colombier 	c[1] = 0x80;
1556b1707c5dSDavid du Colombier 	c[2] = 0xa0;
1557b1707c5dSDavid du Colombier 	if(n != 0)
1558b1707c5dSDavid du Colombier 		c[3] = 1;	/* dma */
1559b1707c5dSDavid du Colombier 	else
1560b1707c5dSDavid du Colombier 		c[3] = 0;	/* features (exp); */
1561b1707c5dSDavid du Colombier 
1562b1707c5dSDavid du Colombier 	c[4] = 0;		/* sector		lba low	7:0 */
1563b1707c5dSDavid du Colombier 	c[5] = n;		/* cylinder low		lba mid	15:8 */
1564b1707c5dSDavid du Colombier 	c[6] = n >> 8;		/* cylinder hi		lba hi	23:16 */
1565*75cb58dbSDavid du Colombier 	c[7] = Obs;
1566b1707c5dSDavid du Colombier 
1567b1707c5dSDavid du Colombier 	*(ulong*)(c + 8) = 0;
1568b1707c5dSDavid du Colombier 	*(ulong*)(c + 12) = 0;
1569b1707c5dSDavid du Colombier 	*(ulong*)(c + 16) = 0;
1570b1707c5dSDavid du Colombier 
1571b1707c5dSDavid du Colombier 	l->flags = 1<<16 | Lpref | Latapi | 0x5;
1572b1707c5dSDavid du Colombier 	if(r->write != 0 && data)
1573b1707c5dSDavid du Colombier 		l->flags |= Lwrite;
1574b1707c5dSDavid du Colombier 	l->len = 0;
1575b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
1576b1707c5dSDavid du Colombier 	l->ctabhi = 0;
1577b1707c5dSDavid du Colombier 
1578b1707c5dSDavid du Colombier 	if(data == 0)
1579b1707c5dSDavid du Colombier 		return l;
1580b1707c5dSDavid du Colombier 
1581b1707c5dSDavid du Colombier 	p = &t->prdt;
1582b1707c5dSDavid du Colombier 	p->dba = PCIWADDR(data);
1583b1707c5dSDavid du Colombier 	p->dbahi = 0;
1584b1707c5dSDavid du Colombier 	p->count = 1<<31 | (n - 2) | 1;
1585b1707c5dSDavid du Colombier 
1586b1707c5dSDavid du Colombier 	return l;
1587b1707c5dSDavid du Colombier }
1588b1707c5dSDavid du Colombier 
1589b1707c5dSDavid du Colombier static int
1590b1707c5dSDavid du Colombier waitready(Drive *d)
1591b1707c5dSDavid du Colombier {
159291b330d9SDavid du Colombier 	ulong s, i, δ;
1593b1707c5dSDavid du Colombier 
159481ede731SDavid du Colombier 	for(i = 0; i < 15000; i += 250){
159581ede731SDavid du Colombier 		if(d->state == Dreset || d->state == Dportreset ||
159681ede731SDavid du Colombier 		    d->state == Dnew)
159781ede731SDavid du Colombier 			return 1;
159881ede731SDavid du Colombier 		δ = MACHP(0)->ticks - d->lastseen;
159981ede731SDavid du Colombier 		if(d->state == Dnull || δ > 10*1000)
160081ede731SDavid du Colombier 			return -1;
1601b1707c5dSDavid du Colombier 		ilock(d);
1602b1707c5dSDavid du Colombier 		s = d->port->sstatus;
1603b1707c5dSDavid du Colombier 		iunlock(d);
160481ede731SDavid du Colombier 		if((s & 0x700) == 0 && δ > 1500)
160581ede731SDavid du Colombier 			return -1;	/* no detect */
1606b1707c5dSDavid du Colombier 		if(d->state == Dready && (s & 7) == 3)
1607b1707c5dSDavid du Colombier 			return 0;	/* ready, present & phy. comm. */
160881ede731SDavid du Colombier 		esleep(250);
1609b1707c5dSDavid du Colombier 	}
1610b1707c5dSDavid du Colombier 	print("%s: not responding; offline\n", d->unit->name);
1611b1707c5dSDavid du Colombier 	ilock(d);
1612b1707c5dSDavid du Colombier 	d->state = Doffline;
1613b1707c5dSDavid du Colombier 	iunlock(d);
1614b1707c5dSDavid du Colombier 	return -1;
1615b1707c5dSDavid du Colombier }
1616b1707c5dSDavid du Colombier 
1617b1707c5dSDavid du Colombier static int
161881ede731SDavid du Colombier lockready(Drive *d)
161981ede731SDavid du Colombier {
162081ede731SDavid du Colombier 	int i;
162181ede731SDavid du Colombier 
162281ede731SDavid du Colombier 	qlock(&d->portm);
162381ede731SDavid du Colombier 	while ((i = waitready(d)) == 1) {
162481ede731SDavid du Colombier 		qunlock(&d->portm);
162581ede731SDavid du Colombier 		esleep(1);
162681ede731SDavid du Colombier 		qlock(&d->portm);
162781ede731SDavid du Colombier 	}
162881ede731SDavid du Colombier 	return i;
162981ede731SDavid du Colombier }
163081ede731SDavid du Colombier 
163181ede731SDavid du Colombier static int
163281ede731SDavid du Colombier flushcache(Drive *d)
163381ede731SDavid du Colombier {
163481ede731SDavid du Colombier 	int i;
163581ede731SDavid du Colombier 
163681ede731SDavid du Colombier 	i = -1;
163781ede731SDavid du Colombier 	if(lockready(d) == 0)
163881ede731SDavid du Colombier 		i = ahciflushcache(&d->portc);
163981ede731SDavid du Colombier 	qunlock(&d->portm);
164081ede731SDavid du Colombier 	return i;
164181ede731SDavid du Colombier }
164281ede731SDavid du Colombier 
164381ede731SDavid du Colombier static int
1644b1707c5dSDavid du Colombier iariopkt(SDreq *r, Drive *d)
1645b1707c5dSDavid du Colombier {
1646b1707c5dSDavid du Colombier 	int n, count, try, max, flag, task;
1647b1707c5dSDavid du Colombier 	char *name;
1648b1707c5dSDavid du Colombier 	uchar *cmd, *data;
1649b1707c5dSDavid du Colombier 	Aport *p;
1650b1707c5dSDavid du Colombier 	Asleep as;
1651b1707c5dSDavid du Colombier 
1652b1707c5dSDavid du Colombier 	cmd = r->cmd;
1653b1707c5dSDavid du Colombier 	name = d->unit->name;
1654b1707c5dSDavid du Colombier 	p = d->port;
1655b1707c5dSDavid du Colombier 
16563d56950aSDavid du Colombier 	aprint("ahci: iariopkt: %02ux %02ux %c %d %p\n",
16573d56950aSDavid du Colombier 		cmd[0], cmd[2], "rw"[r->write], r->dlen, r->data);
1658b1707c5dSDavid du Colombier 	if(cmd[0] == 0x5a && (cmd[2] & 0x3f) == 0x3f)
1659b1707c5dSDavid du Colombier 		return sdmodesense(r, cmd, d->info, sizeof d->info);
1660b1707c5dSDavid du Colombier 	r->rlen = 0;
1661b1707c5dSDavid du Colombier 	count = r->dlen;
1662b1707c5dSDavid du Colombier 	max = 65536;
1663b1707c5dSDavid du Colombier 
1664b1707c5dSDavid du Colombier 	try = 0;
1665b1707c5dSDavid du Colombier retry:
1666b1707c5dSDavid du Colombier 	data = r->data;
1667b1707c5dSDavid du Colombier 	n = count;
1668b1707c5dSDavid du Colombier 	if(n > max)
1669b1707c5dSDavid du Colombier 		n = max;
1670b1707c5dSDavid du Colombier 	ahcibuildpkt(&d->portm, r, data, n);
167181ede731SDavid du Colombier 	switch(waitready(d)){
167281ede731SDavid du Colombier 	case -1:
167381ede731SDavid du Colombier 		qunlock(&d->portm);
167481ede731SDavid du Colombier 		return SDeio;
167581ede731SDavid du Colombier 	case 1:
167681ede731SDavid du Colombier 		qunlock(&d->portm);
167781ede731SDavid du Colombier 		esleep(1);
167881ede731SDavid du Colombier 		goto retry;
167981ede731SDavid du Colombier 	}
168081ede731SDavid du Colombier 
1681b1707c5dSDavid du Colombier 	ilock(d);
1682b1707c5dSDavid du Colombier 	d->portm.flag = 0;
1683b1707c5dSDavid du Colombier 	iunlock(d);
1684b1707c5dSDavid du Colombier 	p->ci = 1;
1685b1707c5dSDavid du Colombier 
1686b1707c5dSDavid du Colombier 	as.p = p;
1687b1707c5dSDavid du Colombier 	as.i = 1;
1688b1707c5dSDavid du Colombier 	d->intick = MACHP(0)->ticks;
1689014ad43fSDavid du Colombier 	d->active++;
1690b1707c5dSDavid du Colombier 
1691b1707c5dSDavid du Colombier 	while(waserror())
1692b1707c5dSDavid du Colombier 		;
1693b1707c5dSDavid du Colombier 	sleep(&d->portm, ahciclear, &as);
1694b1707c5dSDavid du Colombier 	poperror();
1695b1707c5dSDavid du Colombier 
1696014ad43fSDavid du Colombier 	d->active--;
1697b1707c5dSDavid du Colombier 	ilock(d);
1698b1707c5dSDavid du Colombier 	flag = d->portm.flag;
1699b1707c5dSDavid du Colombier 	task = d->port->task;
1700b1707c5dSDavid du Colombier 	iunlock(d);
1701b1707c5dSDavid du Colombier 
1702b1707c5dSDavid du Colombier 	if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){
1703014ad43fSDavid du Colombier 		d->port->ci = 0;
1704b1707c5dSDavid du Colombier 		ahcirecover(&d->portc);
1705b1707c5dSDavid du Colombier 		task = d->port->task;
170681ede731SDavid du Colombier 		flag &= ~Fdone;		/* either an error or do-over */
1707b1707c5dSDavid du Colombier 	}
1708b1707c5dSDavid du Colombier 	qunlock(&d->portm);
1709b1707c5dSDavid du Colombier 	if(flag == 0){
1710b1707c5dSDavid du Colombier 		if(++try == 10){
1711b1707c5dSDavid du Colombier 			print("%s: bad disk\n", name);
1712b1707c5dSDavid du Colombier 			r->status = SDcheck;
1713b1707c5dSDavid du Colombier 			return SDcheck;
1714b1707c5dSDavid du Colombier 		}
171581ede731SDavid du Colombier 		print("%s: retry\n", name);
1716b1707c5dSDavid du Colombier 		goto retry;
1717b1707c5dSDavid du Colombier 	}
1718b1707c5dSDavid du Colombier 	if(flag & Ferror){
171981ede731SDavid du Colombier 		if((task&Eidnf) == 0)
172081ede731SDavid du Colombier 			print("%s: i/o error %ux\n", name, task);
1721b1707c5dSDavid du Colombier 		r->status = SDcheck;
1722b1707c5dSDavid du Colombier 		return SDcheck;
1723b1707c5dSDavid du Colombier 	}
1724b1707c5dSDavid du Colombier 
1725b1707c5dSDavid du Colombier 	data += n;
1726b1707c5dSDavid du Colombier 
1727b1707c5dSDavid du Colombier 	r->rlen = data - (uchar*)r->data;
1728b1707c5dSDavid du Colombier 	r->status = SDok;
1729b1707c5dSDavid du Colombier 	return SDok;
1730b1707c5dSDavid du Colombier }
1731b1707c5dSDavid du Colombier 
1732b1707c5dSDavid du Colombier static int
1733b1707c5dSDavid du Colombier iario(SDreq *r)
1734b1707c5dSDavid du Colombier {
1735b1707c5dSDavid du Colombier 	int i, n, count, try, max, flag, task;
1736b1707c5dSDavid du Colombier 	vlong lba;
1737b1707c5dSDavid du Colombier 	char *name;
1738b1707c5dSDavid du Colombier 	uchar *cmd, *data;
1739b1707c5dSDavid du Colombier 	Aport *p;
1740b1707c5dSDavid du Colombier 	Asleep as;
1741b1707c5dSDavid du Colombier 	Ctlr *c;
1742b1707c5dSDavid du Colombier 	Drive *d;
1743b1707c5dSDavid du Colombier 	SDunit *unit;
1744b1707c5dSDavid du Colombier 
1745b1707c5dSDavid du Colombier 	unit = r->unit;
1746b1707c5dSDavid du Colombier 	c = unit->dev->ctlr;
1747b1707c5dSDavid du Colombier 	d = c->drive[unit->subno];
1748b1707c5dSDavid du Colombier 	if(d->portm.feat & Datapi)
1749b1707c5dSDavid du Colombier 		return iariopkt(r, d);
1750b1707c5dSDavid du Colombier 	cmd = r->cmd;
1751b1707c5dSDavid du Colombier 	name = d->unit->name;
1752b1707c5dSDavid du Colombier 	p = d->port;
1753b1707c5dSDavid du Colombier 
1754b1707c5dSDavid du Colombier 	if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
175581ede731SDavid du Colombier 		if(flushcache(d) == 0)
1756b1707c5dSDavid du Colombier 			return sdsetsense(r, SDok, 0, 0, 0);
1757b1707c5dSDavid du Colombier 		return sdsetsense(r, SDcheck, 3, 0xc, 2);
1758b1707c5dSDavid du Colombier 	}
1759b1707c5dSDavid du Colombier 
1760b1707c5dSDavid du Colombier 	if((i = sdfakescsi(r, d->info, sizeof d->info)) != SDnostatus){
1761b1707c5dSDavid du Colombier 		r->status = i;
1762b1707c5dSDavid du Colombier 		return i;
1763b1707c5dSDavid du Colombier 	}
1764b1707c5dSDavid du Colombier 
1765b1707c5dSDavid du Colombier 	if(*cmd != 0x28 && *cmd != 0x2a){
1766b1707c5dSDavid du Colombier 		print("%s: bad cmd 0x%.2ux\n", name, cmd[0]);
1767b1707c5dSDavid du Colombier 		r->status = SDcheck;
1768b1707c5dSDavid du Colombier 		return SDcheck;
1769b1707c5dSDavid du Colombier 	}
1770b1707c5dSDavid du Colombier 
1771b1707c5dSDavid du Colombier 	lba   = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
1772b1707c5dSDavid du Colombier 	count = cmd[7]<<8 | cmd[8];
1773b1707c5dSDavid du Colombier 	if(r->data == nil)
1774b1707c5dSDavid du Colombier 		return SDok;
1775b1707c5dSDavid du Colombier 	if(r->dlen < count * unit->secsize)
1776b1707c5dSDavid du Colombier 		count = r->dlen / unit->secsize;
1777b1707c5dSDavid du Colombier 	max = 128;
1778b1707c5dSDavid du Colombier 
1779b1707c5dSDavid du Colombier 	try = 0;
1780b1707c5dSDavid du Colombier retry:
1781b1707c5dSDavid du Colombier 	data = r->data;
1782b1707c5dSDavid du Colombier 	while(count > 0){
1783b1707c5dSDavid du Colombier 		n = count;
1784b1707c5dSDavid du Colombier 		if(n > max)
1785b1707c5dSDavid du Colombier 			n = max;
178658db92f4SDavid du Colombier 		ahcibuild(d, cmd, data, n, lba);
178781ede731SDavid du Colombier 		switch(waitready(d)){
178881ede731SDavid du Colombier 		case -1:
178981ede731SDavid du Colombier 			qunlock(&d->portm);
179081ede731SDavid du Colombier 			return SDeio;
179181ede731SDavid du Colombier 		case 1:
179281ede731SDavid du Colombier 			qunlock(&d->portm);
179381ede731SDavid du Colombier 			esleep(1);
179481ede731SDavid du Colombier 			goto retry;
179581ede731SDavid du Colombier 		}
1796b1707c5dSDavid du Colombier 		ilock(d);
1797b1707c5dSDavid du Colombier 		d->portm.flag = 0;
1798b1707c5dSDavid du Colombier 		iunlock(d);
1799b1707c5dSDavid du Colombier 		p->ci = 1;
1800b1707c5dSDavid du Colombier 
1801b1707c5dSDavid du Colombier 		as.p = p;
1802b1707c5dSDavid du Colombier 		as.i = 1;
1803b1707c5dSDavid du Colombier 		d->intick = MACHP(0)->ticks;
1804014ad43fSDavid du Colombier 		d->active++;
1805b1707c5dSDavid du Colombier 
1806b1707c5dSDavid du Colombier 		while(waserror())
1807b1707c5dSDavid du Colombier 			;
1808b1707c5dSDavid du Colombier 		sleep(&d->portm, ahciclear, &as);
1809b1707c5dSDavid du Colombier 		poperror();
1810b1707c5dSDavid du Colombier 
1811014ad43fSDavid du Colombier 		d->active--;
1812b1707c5dSDavid du Colombier 		ilock(d);
1813b1707c5dSDavid du Colombier 		flag = d->portm.flag;
1814b1707c5dSDavid du Colombier 		task = d->port->task;
1815b1707c5dSDavid du Colombier 		iunlock(d);
1816b1707c5dSDavid du Colombier 
1817b1707c5dSDavid du Colombier 		if(task & (Efatal<<8) ||
1818b1707c5dSDavid du Colombier 		    task & (ASbsy|ASdrq) && d->state == Dready){
1819014ad43fSDavid du Colombier 			d->port->ci = 0;
1820b1707c5dSDavid du Colombier 			ahcirecover(&d->portc);
1821b1707c5dSDavid du Colombier 			task = d->port->task;
1822b1707c5dSDavid du Colombier 		}
1823b1707c5dSDavid du Colombier 		qunlock(&d->portm);
1824b1707c5dSDavid du Colombier 		if(flag == 0){
1825b1707c5dSDavid du Colombier 			if(++try == 10){
1826b1707c5dSDavid du Colombier 				print("%s: bad disk\n", name);
1827b1707c5dSDavid du Colombier 				r->status = SDeio;
1828b1707c5dSDavid du Colombier 				return SDeio;
1829b1707c5dSDavid du Colombier 			}
1830b1707c5dSDavid du Colombier 			iprint("%s: retry %lld\n", name, lba);
1831b1707c5dSDavid du Colombier 			goto retry;
1832b1707c5dSDavid du Colombier 		}
1833b1707c5dSDavid du Colombier 		if(flag & Ferror){
1834b1707c5dSDavid du Colombier 			iprint("%s: i/o error %ux @%,lld\n", name, task, lba);
1835b1707c5dSDavid du Colombier 			r->status = SDeio;
1836b1707c5dSDavid du Colombier 			return SDeio;
1837b1707c5dSDavid du Colombier 		}
1838b1707c5dSDavid du Colombier 
1839b1707c5dSDavid du Colombier 		count -= n;
1840b1707c5dSDavid du Colombier 		lba   += n;
1841b1707c5dSDavid du Colombier 		data += n * unit->secsize;
1842b1707c5dSDavid du Colombier 	}
1843b1707c5dSDavid du Colombier 	r->rlen = data - (uchar*)r->data;
1844b1707c5dSDavid du Colombier 	r->status = SDok;
1845b1707c5dSDavid du Colombier 	return SDok;
1846b1707c5dSDavid du Colombier }
1847b1707c5dSDavid du Colombier 
1848b1707c5dSDavid du Colombier /*
1849b1707c5dSDavid du Colombier  * configure drives 0-5 as ahci sata  (c.f. errata)
1850b1707c5dSDavid du Colombier  */
1851b1707c5dSDavid du Colombier static int
1852b1707c5dSDavid du Colombier iaahcimode(Pcidev *p)
1853b1707c5dSDavid du Colombier {
18543d56950aSDavid du Colombier 	dprint("iaahcimode: %ux %ux %ux\n", pcicfgr8(p, 0x91), pcicfgr8(p, 92),
1855b1707c5dSDavid du Colombier 		pcicfgr8(p, 93));
1856b1707c5dSDavid du Colombier 	pcicfgw16(p, 0x92, pcicfgr32(p, 0x92) | 0xf);	/* ports 0-3 */
1857b1707c5dSDavid du Colombier //	pcicfgw8(p, 0x93, pcicfgr32(p, 9x93) | 3);	/* ports 4-5 */
1858b1707c5dSDavid du Colombier 	return 0;
1859b1707c5dSDavid du Colombier }
1860b1707c5dSDavid du Colombier 
1861b1707c5dSDavid du Colombier static void
1862b1707c5dSDavid du Colombier iasetupahci(Ctlr *c)
1863b1707c5dSDavid du Colombier {
1864b1707c5dSDavid du Colombier 	/* disable cmd block decoding. */
1865b1707c5dSDavid du Colombier 	pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~(1<<15));
1866b1707c5dSDavid du Colombier 	pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~(1<<15));
1867b1707c5dSDavid du Colombier 
1868b1707c5dSDavid du Colombier 	c->lmmio[0x4/4] |= 1 << 31;	/* enable ahci mode (ghc register) */
1869b1707c5dSDavid du Colombier 	c->lmmio[0xc/4] = (1 << 6) - 1;	/* 5 ports. (supposedly ro pi reg.) */
1870b1707c5dSDavid du Colombier 
1871b1707c5dSDavid du Colombier 	/* enable ahci mode; from ich9 datasheet */
1872014ad43fSDavid du Colombier 	pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5);
1873014ad43fSDavid du Colombier }
1874014ad43fSDavid du Colombier 
1875014ad43fSDavid du Colombier static int
1876014ad43fSDavid du Colombier didtype(Pcidev *p)
1877014ad43fSDavid du Colombier {
1878014ad43fSDavid du Colombier 	switch(p->vid){
1879*75cb58dbSDavid du Colombier 	case Vintel:
1880014ad43fSDavid du Colombier 		if((p->did & 0xfffc) == 0x2680)
1881014ad43fSDavid du Colombier 			return Tesb;
188291157df7SDavid du Colombier 		/*
188391157df7SDavid du Colombier 		 * 0x27c4 is the intel 82801 in compatibility (not sata) mode.
188491157df7SDavid du Colombier 		 */
188591157df7SDavid du Colombier 		if ((p->did & 0xfffb) == 0x27c1 ||	/* 82801g[bh]m ich7 */
188691157df7SDavid du Colombier 		    p->did == 0x2821 ||			/* 82801h[roh] */
188791157df7SDavid du Colombier 		    (p->did & 0xfffe) == 0x2824 ||	/* 82801h[b] */
188891157df7SDavid du Colombier 		    (p->did & 0xfeff) == 0x2829 ||	/* ich8/9m */
1889014ad43fSDavid du Colombier 		    (p->did & 0xfffe) == 0x2922 ||	/* ich9 */
189091157df7SDavid du Colombier 		    p->did == 0x3a02 ||			/* 82801jd/do */
189191157df7SDavid du Colombier 		    (p->did & 0xfefe) == 0x3a22 ||	/* ich10, pch */
189291157df7SDavid du Colombier 		    (p->did & 0xfff7) == 0x3b28)	/* pchm */
1893014ad43fSDavid du Colombier 			return Tich;
1894014ad43fSDavid du Colombier 		break;
1895*75cb58dbSDavid du Colombier 	case Vatiamd:
1896014ad43fSDavid du Colombier 		if(p->did == 0x4380)
1897014ad43fSDavid du Colombier 			return Tsb600;
1898014ad43fSDavid du Colombier 		break;
1899*75cb58dbSDavid du Colombier 	case Vmarvell:
1900*75cb58dbSDavid du Colombier 		/* can't cope with sata 3 yet; touching sd files will hang */
1901*75cb58dbSDavid du Colombier 		if (p->did == 0x9123) {
1902*75cb58dbSDavid du Colombier 			print("ahci: ignoring sata 3 controller\n");
1903*75cb58dbSDavid du Colombier 			return -1;
1904014ad43fSDavid du Colombier 		}
1905*75cb58dbSDavid du Colombier 		break;
1906*75cb58dbSDavid du Colombier 	}
1907*75cb58dbSDavid du Colombier 	if(p->ccrb == Pcibcstore && p->ccru == Pciscsata && p->ccrp == 1)
1908014ad43fSDavid du Colombier 		return Tunk;
1909014ad43fSDavid du Colombier 	return -1;
1910b1707c5dSDavid du Colombier }
1911b1707c5dSDavid du Colombier 
1912b1707c5dSDavid du Colombier static SDev*
1913b1707c5dSDavid du Colombier iapnp(void)
1914b1707c5dSDavid du Colombier {
1915b1707c5dSDavid du Colombier 	int i, n, nunit, type;
1916b1707c5dSDavid du Colombier 	ulong io;
1917b1707c5dSDavid du Colombier 	Ctlr *c;
1918b1707c5dSDavid du Colombier 	Drive *d;
1919b1707c5dSDavid du Colombier 	Pcidev *p;
1920b1707c5dSDavid du Colombier 	SDev *head, *tail, *s;
1921b1707c5dSDavid du Colombier 	static int done;
1922b1707c5dSDavid du Colombier 
1923b1707c5dSDavid du Colombier 	if(done++)
1924b1707c5dSDavid du Colombier 		return nil;
1925b1707c5dSDavid du Colombier 
1926014ad43fSDavid du Colombier 	memset(olds, 0xff, sizeof olds);
1927b1707c5dSDavid du Colombier 	p = nil;
1928b1707c5dSDavid du Colombier 	head = tail = nil;
1929b1707c5dSDavid du Colombier loop:
1930b1707c5dSDavid du Colombier 	while((p = pcimatch(p, 0, 0)) != nil){
1931014ad43fSDavid du Colombier 		type = didtype(p);
1932014ad43fSDavid du Colombier 		if (type == -1 || p->mem[Abar].bar == 0)
1933b1707c5dSDavid du Colombier 			continue;
1934b1707c5dSDavid du Colombier 		if(niactlr == NCtlr){
193591157df7SDavid du Colombier 			print("ahci: iapnp: %s: too many controllers\n",
19363d56950aSDavid du Colombier 				tname[type]);
1937b1707c5dSDavid du Colombier 			break;
1938b1707c5dSDavid du Colombier 		}
1939b1707c5dSDavid du Colombier 		c = iactlr + niactlr;
1940b1707c5dSDavid du Colombier 		s = sdevs  + niactlr;
1941b1707c5dSDavid du Colombier 		memset(c, 0, sizeof *c);
1942b1707c5dSDavid du Colombier 		memset(s, 0, sizeof *s);
1943b1707c5dSDavid du Colombier 		io = p->mem[Abar].bar & ~0xf;
1944*75cb58dbSDavid du Colombier 		c->physio = (uchar *)io;
1945b1707c5dSDavid du Colombier 		c->mmio = vmap(io, p->mem[Abar].size);
1946b1707c5dSDavid du Colombier 		if(c->mmio == 0){
1947*75cb58dbSDavid du Colombier 			print("ahci: %s: address %#luX in use did=%x\n",
1948b1707c5dSDavid du Colombier 				Tname(c), io, p->did);
1949b1707c5dSDavid du Colombier 			continue;
1950b1707c5dSDavid du Colombier 		}
1951b1707c5dSDavid du Colombier 		c->lmmio = (ulong*)c->mmio;
1952b1707c5dSDavid du Colombier 		c->pci = p;
1953b1707c5dSDavid du Colombier 		c->type = type;
1954b1707c5dSDavid du Colombier 
1955b1707c5dSDavid du Colombier 		s->ifc = &sdiahciifc;
1956b1707c5dSDavid du Colombier 		s->idno = 'E' + niactlr;
1957b1707c5dSDavid du Colombier 		s->ctlr = c;
1958b1707c5dSDavid du Colombier 		c->sdev = s;
1959b1707c5dSDavid du Colombier 
1960014ad43fSDavid du Colombier 		if(Intel(c) && p->did != 0x2681)
1961b1707c5dSDavid du Colombier 			iasetupahci(c);
1962b1707c5dSDavid du Colombier 		nunit = ahciconf(c);
1963b1707c5dSDavid du Colombier //		ahcihbareset((Ahba*)c->mmio);
1964014ad43fSDavid du Colombier 		if(Intel(c) && iaahcimode(p) == -1)
1965b1707c5dSDavid du Colombier 			break;
1966b1707c5dSDavid du Colombier 		if(nunit < 1){
1967b1707c5dSDavid du Colombier 			vunmap(c->mmio, p->mem[Abar].size);
1968b1707c5dSDavid du Colombier 			continue;
1969b1707c5dSDavid du Colombier 		}
1970b1707c5dSDavid du Colombier 		c->ndrive = s->nunit = nunit;
1971014ad43fSDavid du Colombier 		c->mport = c->hba->cap & ((1<<5)-1);
1972b1707c5dSDavid du Colombier 
1973*75cb58dbSDavid du Colombier 		i = (c->hba->cap >> 20) & ((1<<4)-1);		/* iss */
1974*75cb58dbSDavid du Colombier 		print("#S/sd%c: %s: %#p %s, %d ports, irq %d\n", s->idno,
1975*75cb58dbSDavid du Colombier 			Tname(c), c->physio, descmode[i], nunit, c->pci->intl);
1976b1707c5dSDavid du Colombier 		/* map the drives -- they don't all need to be enabled. */
1977b1707c5dSDavid du Colombier 		memset(c->rawdrive, 0, sizeof c->rawdrive);
1978b1707c5dSDavid du Colombier 		n = 0;
1979b1707c5dSDavid du Colombier 		for(i = 0; i < NCtlrdrv; i++) {
1980b1707c5dSDavid du Colombier 			d = c->rawdrive + i;
1981b1707c5dSDavid du Colombier 			d->portno = i;
1982b1707c5dSDavid du Colombier 			d->driveno = -1;
1983b1707c5dSDavid du Colombier 			d->sectors = 0;
198497902b0cSDavid du Colombier 			d->serial[0] = ' ';
1985b1707c5dSDavid du Colombier 			d->ctlr = c;
1986b1707c5dSDavid du Colombier 			if((c->hba->pi & (1<<i)) == 0)
1987b1707c5dSDavid du Colombier 				continue;
1988b1707c5dSDavid du Colombier 			d->port = (Aport*)(c->mmio + 0x80*i + 0x100);
1989b1707c5dSDavid du Colombier 			d->portc.p = d->port;
1990b1707c5dSDavid du Colombier 			d->portc.m = &d->portm;
1991b1707c5dSDavid du Colombier 			d->driveno = n++;
199281ede731SDavid du Colombier 			c->drive[d->driveno] = d;
199381ede731SDavid du Colombier 			iadrive[niadrive + d->driveno] = d;
1994b1707c5dSDavid du Colombier 		}
1995b1707c5dSDavid du Colombier 		for(i = 0; i < n; i++)
1996b1707c5dSDavid du Colombier 			if(ahciidle(c->drive[i]->port) == -1){
19973d56950aSDavid du Colombier 				dprint("ahci: %s: port %d wedged; abort\n",
1998b1707c5dSDavid du Colombier 					Tname(c), i);
1999b1707c5dSDavid du Colombier 				goto loop;
2000b1707c5dSDavid du Colombier 			}
2001b1707c5dSDavid du Colombier 		for(i = 0; i < n; i++){
2002b1707c5dSDavid du Colombier 			c->drive[i]->mode = DMsatai;
2003b1707c5dSDavid du Colombier 			configdrive(c->drive[i]);
2004b1707c5dSDavid du Colombier 		}
2005b1707c5dSDavid du Colombier 
2006014ad43fSDavid du Colombier 		niadrive += n;
2007b1707c5dSDavid du Colombier 		niactlr++;
2008b1707c5dSDavid du Colombier 		if(head)
2009b1707c5dSDavid du Colombier 			tail->next = s;
2010b1707c5dSDavid du Colombier 		else
2011b1707c5dSDavid du Colombier 			head = s;
2012b1707c5dSDavid du Colombier 		tail = s;
2013b1707c5dSDavid du Colombier 	}
2014b1707c5dSDavid du Colombier 	return head;
2015b1707c5dSDavid du Colombier }
2016b1707c5dSDavid du Colombier 
2017b1707c5dSDavid du Colombier static char* smarttab[] = {
2018b1707c5dSDavid du Colombier 	"unset",
2019b1707c5dSDavid du Colombier 	"error",
2020b1707c5dSDavid du Colombier 	"threshold exceeded",
2021b1707c5dSDavid du Colombier 	"normal"
2022b1707c5dSDavid du Colombier };
2023b1707c5dSDavid du Colombier 
2024b1707c5dSDavid du Colombier static char *
2025b1707c5dSDavid du Colombier pflag(char *s, char *e, uchar f)
2026b1707c5dSDavid du Colombier {
2027b1707c5dSDavid du Colombier 	uchar i;
2028b1707c5dSDavid du Colombier 
2029b1707c5dSDavid du Colombier 	for(i = 0; i < 8; i++)
2030b1707c5dSDavid du Colombier 		if(f & (1 << i))
2031b1707c5dSDavid du Colombier 			s = seprint(s, e, "%s ", flagname[i]);
2032b1707c5dSDavid du Colombier 	return seprint(s, e, "\n");
2033b1707c5dSDavid du Colombier }
2034b1707c5dSDavid du Colombier 
2035b1707c5dSDavid du Colombier static int
2036b1707c5dSDavid du Colombier iarctl(SDunit *u, char *p, int l)
2037b1707c5dSDavid du Colombier {
2038b1707c5dSDavid du Colombier 	char buf[32];
2039b1707c5dSDavid du Colombier 	char *e, *op;
2040b1707c5dSDavid du Colombier 	Aport *o;
2041b1707c5dSDavid du Colombier 	Ctlr *c;
2042b1707c5dSDavid du Colombier 	Drive *d;
2043b1707c5dSDavid du Colombier 
204458db92f4SDavid du Colombier 	c = u->dev->ctlr;
204558db92f4SDavid du Colombier 	if(c == nil) {
204658db92f4SDavid du Colombier print("iarctl: nil u->dev->ctlr\n");
2047b1707c5dSDavid du Colombier 		return 0;
204858db92f4SDavid du Colombier 	}
2049b1707c5dSDavid du Colombier 	d = c->drive[u->subno];
2050b1707c5dSDavid du Colombier 	o = d->port;
2051b1707c5dSDavid du Colombier 
2052b1707c5dSDavid du Colombier 	e = p+l;
2053b1707c5dSDavid du Colombier 	op = p;
2054b1707c5dSDavid du Colombier 	if(d->state == Dready){
2055b1707c5dSDavid du Colombier 		p = seprint(p, e, "model\t%s\n", d->model);
2056b1707c5dSDavid du Colombier 		p = seprint(p, e, "serial\t%s\n", d->serial);
2057b1707c5dSDavid du Colombier 		p = seprint(p, e, "firm\t%s\n", d->firmware);
2058b1707c5dSDavid du Colombier 		if(d->smartrs == 0xff)
2059b1707c5dSDavid du Colombier 			p = seprint(p, e, "smart\tenable error\n");
2060b1707c5dSDavid du Colombier 		else if(d->smartrs == 0)
2061b1707c5dSDavid du Colombier 			p = seprint(p, e, "smart\tdisabled\n");
2062b1707c5dSDavid du Colombier 		else
2063b1707c5dSDavid du Colombier 			p = seprint(p, e, "smart\t%s\n",
2064b1707c5dSDavid du Colombier 				smarttab[d->portm.smart]);
2065b1707c5dSDavid du Colombier 		p = seprint(p, e, "flag\t");
2066b1707c5dSDavid du Colombier 		p = pflag(p, e, d->portm.feat);
2067b1707c5dSDavid du Colombier 	}else
2068b1707c5dSDavid du Colombier 		p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]);
2069b1707c5dSDavid du Colombier 	serrstr(o->serror, buf, buf + sizeof buf - 1);
207091b330d9SDavid du Colombier 	p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux; "
207191b330d9SDavid du Colombier 		"sig %lux sstatus %04lux\n", o->task, o->cmd, o->serror, buf,
2072b1707c5dSDavid du Colombier 		o->ci, o->isr, o->sig, o->sstatus);
207358db92f4SDavid du Colombier 	if(d->unit == nil)
207458db92f4SDavid du Colombier 		panic("iarctl: nil d->unit");
207558db92f4SDavid du Colombier 	p = seprint(p, e, "geometry %llud %lud\n", d->sectors, d->unit->secsize);
2076b1707c5dSDavid du Colombier 	return p - op;
2077b1707c5dSDavid du Colombier }
2078b1707c5dSDavid du Colombier 
2079b1707c5dSDavid du Colombier static void
2080b1707c5dSDavid du Colombier runflushcache(Drive *d)
2081b1707c5dSDavid du Colombier {
2082b1707c5dSDavid du Colombier 	long t0;
2083b1707c5dSDavid du Colombier 
2084b1707c5dSDavid du Colombier 	t0 = MACHP(0)->ticks;
208581ede731SDavid du Colombier 	if(flushcache(d) != 0)
208681ede731SDavid du Colombier 		error(Eio);
20873d56950aSDavid du Colombier 	dprint("ahci: flush in %ld ms\n", MACHP(0)->ticks - t0);
2088b1707c5dSDavid du Colombier }
2089b1707c5dSDavid du Colombier 
2090b1707c5dSDavid du Colombier static void
2091b1707c5dSDavid du Colombier forcemode(Drive *d, char *mode)
2092b1707c5dSDavid du Colombier {
2093b1707c5dSDavid du Colombier 	int i;
2094b1707c5dSDavid du Colombier 
2095b1707c5dSDavid du Colombier 	for(i = 0; i < nelem(modename); i++)
2096b1707c5dSDavid du Colombier 		if(strcmp(mode, modename[i]) == 0)
2097b1707c5dSDavid du Colombier 			break;
2098b1707c5dSDavid du Colombier 	if(i == nelem(modename))
2099b1707c5dSDavid du Colombier 		i = 0;
2100b1707c5dSDavid du Colombier 	ilock(d);
2101b1707c5dSDavid du Colombier 	d->mode = i;
2102b1707c5dSDavid du Colombier 	iunlock(d);
2103b1707c5dSDavid du Colombier }
2104b1707c5dSDavid du Colombier 
2105b1707c5dSDavid du Colombier static void
2106b1707c5dSDavid du Colombier runsmartable(Drive *d, int i)
2107b1707c5dSDavid du Colombier {
2108b1707c5dSDavid du Colombier 	if(waserror()){
2109b1707c5dSDavid du Colombier 		qunlock(&d->portm);
2110b1707c5dSDavid du Colombier 		d->smartrs = 0;
2111b1707c5dSDavid du Colombier 		nexterror();
2112b1707c5dSDavid du Colombier 	}
211381ede731SDavid du Colombier 	if(lockready(d) == -1)
211481ede731SDavid du Colombier 		error(Eio);
2115b1707c5dSDavid du Colombier 	d->smartrs = smart(&d->portc, i);
2116b1707c5dSDavid du Colombier 	d->portm.smart = 0;
2117b1707c5dSDavid du Colombier 	qunlock(&d->portm);
2118b1707c5dSDavid du Colombier 	poperror();
2119b1707c5dSDavid du Colombier }
2120b1707c5dSDavid du Colombier 
2121b1707c5dSDavid du Colombier static void
2122b1707c5dSDavid du Colombier forcestate(Drive *d, char *state)
2123b1707c5dSDavid du Colombier {
2124b1707c5dSDavid du Colombier 	int i;
2125b1707c5dSDavid du Colombier 
2126b1707c5dSDavid du Colombier 	for(i = 0; i < nelem(diskstates); i++)
2127b1707c5dSDavid du Colombier 		if(strcmp(state, diskstates[i]) == 0)
2128b1707c5dSDavid du Colombier 			break;
2129b1707c5dSDavid du Colombier 	if(i == nelem(diskstates))
2130014ad43fSDavid du Colombier 		error(Ebadctl);
2131b1707c5dSDavid du Colombier 	ilock(d);
2132b1707c5dSDavid du Colombier 	d->state = i;
2133b1707c5dSDavid du Colombier 	iunlock(d);
2134b1707c5dSDavid du Colombier }
2135b1707c5dSDavid du Colombier 
2136b1707c5dSDavid du Colombier 
2137b1707c5dSDavid du Colombier static int
2138b1707c5dSDavid du Colombier iawctl(SDunit *u, Cmdbuf *cmd)
2139b1707c5dSDavid du Colombier {
2140b1707c5dSDavid du Colombier 	char **f;
2141b1707c5dSDavid du Colombier 	Ctlr *c;
2142b1707c5dSDavid du Colombier 	Drive *d;
214381ede731SDavid du Colombier 	uint i;
2144b1707c5dSDavid du Colombier 
2145b1707c5dSDavid du Colombier 	c = u->dev->ctlr;
2146b1707c5dSDavid du Colombier 	d = c->drive[u->subno];
2147b1707c5dSDavid du Colombier 	f = cmd->f;
2148b1707c5dSDavid du Colombier 
2149b1707c5dSDavid du Colombier 	if(strcmp(f[0], "flushcache") == 0)
2150b1707c5dSDavid du Colombier 		runflushcache(d);
2151b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "identify") ==  0){
2152b1707c5dSDavid du Colombier 		i = strtoul(f[1]? f[1]: "0", 0, 0);
2153b1707c5dSDavid du Colombier 		if(i > 0xff)
2154b1707c5dSDavid du Colombier 			i = 0;
21553d56950aSDavid du Colombier 		dprint("ahci: %04d %ux\n", i, d->info[i]);
2156b1707c5dSDavid du Colombier 	}else if(strcmp(f[0], "mode") == 0)
2157b1707c5dSDavid du Colombier 		forcemode(d, f[1]? f[1]: "satai");
2158b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "nop") == 0){
2159b1707c5dSDavid du Colombier 		if((d->portm.feat & Dnop) == 0){
216081ede731SDavid du Colombier 			cmderror(cmd, "no drive support");
2161b1707c5dSDavid du Colombier 			return -1;
2162b1707c5dSDavid du Colombier 		}
2163b1707c5dSDavid du Colombier 		if(waserror()){
2164b1707c5dSDavid du Colombier 			qunlock(&d->portm);
2165b1707c5dSDavid du Colombier 			nexterror();
2166b1707c5dSDavid du Colombier 		}
216781ede731SDavid du Colombier 		if(lockready(d) == -1)
216881ede731SDavid du Colombier 			error(Eio);
2169b1707c5dSDavid du Colombier 		nop(&d->portc);
2170b1707c5dSDavid du Colombier 		qunlock(&d->portm);
2171b1707c5dSDavid du Colombier 		poperror();
2172b1707c5dSDavid du Colombier 	}else if(strcmp(f[0], "reset") == 0)
2173b1707c5dSDavid du Colombier 		forcestate(d, "reset");
2174b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "smart") == 0){
2175b1707c5dSDavid du Colombier 		if(d->smartrs == 0){
2176b1707c5dSDavid du Colombier 			cmderror(cmd, "smart not enabled");
2177b1707c5dSDavid du Colombier 			return -1;
2178b1707c5dSDavid du Colombier 		}
2179b1707c5dSDavid du Colombier 		if(waserror()){
2180b1707c5dSDavid du Colombier 			qunlock(&d->portm);
2181b1707c5dSDavid du Colombier 			d->smartrs = 0;
2182b1707c5dSDavid du Colombier 			nexterror();
2183b1707c5dSDavid du Colombier 		}
218481ede731SDavid du Colombier 		if(lockready(d) == -1)
218581ede731SDavid du Colombier 			error(Eio);
2186b1707c5dSDavid du Colombier 		d->portm.smart = 2 + smartrs(&d->portc);
2187b1707c5dSDavid du Colombier 		qunlock(&d->portm);
2188b1707c5dSDavid du Colombier 		poperror();
2189b1707c5dSDavid du Colombier 	}else if(strcmp(f[0], "smartdisable") == 0)
2190b1707c5dSDavid du Colombier 		runsmartable(d, 1);
2191b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "smartenable") == 0)
2192b1707c5dSDavid du Colombier 		runsmartable(d, 0);
2193b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "state") == 0)
2194b1707c5dSDavid du Colombier 		forcestate(d, f[1]? f[1]: "null");
2195b1707c5dSDavid du Colombier 	else{
2196b1707c5dSDavid du Colombier 		cmderror(cmd, Ebadctl);
2197b1707c5dSDavid du Colombier 		return -1;
2198b1707c5dSDavid du Colombier 	}
2199b1707c5dSDavid du Colombier 	return 0;
2200b1707c5dSDavid du Colombier }
2201b1707c5dSDavid du Colombier 
2202b1707c5dSDavid du Colombier static char *
2203b1707c5dSDavid du Colombier portr(char *p, char *e, uint x)
2204b1707c5dSDavid du Colombier {
2205b1707c5dSDavid du Colombier 	int i, a;
2206b1707c5dSDavid du Colombier 
2207b1707c5dSDavid du Colombier 	p[0] = 0;
2208b1707c5dSDavid du Colombier 	a = -1;
2209b1707c5dSDavid du Colombier 	for(i = 0; i < 32; i++){
2210b1707c5dSDavid du Colombier 		if((x & (1<<i)) == 0){
2211b1707c5dSDavid du Colombier 			if(a != -1 && i - 1 != a)
2212b1707c5dSDavid du Colombier 				p = seprint(p, e, "-%d", i - 1);
2213b1707c5dSDavid du Colombier 			a = -1;
2214b1707c5dSDavid du Colombier 			continue;
2215b1707c5dSDavid du Colombier 		}
2216b1707c5dSDavid du Colombier 		if(a == -1){
2217b1707c5dSDavid du Colombier 			if(i > 0)
2218b1707c5dSDavid du Colombier 				p = seprint(p, e, ", ");
2219b1707c5dSDavid du Colombier 			p = seprint(p, e, "%d", a = i);
2220b1707c5dSDavid du Colombier 		}
2221b1707c5dSDavid du Colombier 	}
2222b1707c5dSDavid du Colombier 	if(a != -1 && i - 1 != a)
2223b1707c5dSDavid du Colombier 		p = seprint(p, e, "-%d", i - 1);
2224b1707c5dSDavid du Colombier 	return p;
2225b1707c5dSDavid du Colombier }
2226b1707c5dSDavid du Colombier 
2227b1707c5dSDavid du Colombier /* must emit exactly one line per controller (sd(3)) */
2228b1707c5dSDavid du Colombier static char*
2229b1707c5dSDavid du Colombier iartopctl(SDev *sdev, char *p, char *e)
2230b1707c5dSDavid du Colombier {
223191b330d9SDavid du Colombier 	ulong cap;
2232b1707c5dSDavid du Colombier 	char pr[25];
2233b1707c5dSDavid du Colombier 	Ahba *hba;
2234b1707c5dSDavid du Colombier 	Ctlr *ctlr;
2235b1707c5dSDavid du Colombier 
2236b1707c5dSDavid du Colombier #define has(x, str) if(cap & (x)) p = seprint(p, e, "%s ", (str))
2237b1707c5dSDavid du Colombier 
2238b1707c5dSDavid du Colombier 	ctlr = sdev->ctlr;
2239b1707c5dSDavid du Colombier 	hba = ctlr->hba;
2240*75cb58dbSDavid du Colombier 	p = seprint(p, e, "sd%c ahci port %#p: ", sdev->idno, ctlr->physio);
2241b1707c5dSDavid du Colombier 	cap = hba->cap;
2242b1707c5dSDavid du Colombier 	has(Hs64a, "64a");
2243b1707c5dSDavid du Colombier 	has(Hsalp, "alp");
2244b1707c5dSDavid du Colombier 	has(Hsam, "am");
2245b1707c5dSDavid du Colombier 	has(Hsclo, "clo");
2246b1707c5dSDavid du Colombier 	has(Hcccs, "coal");
2247b1707c5dSDavid du Colombier 	has(Hems, "ems");
2248b1707c5dSDavid du Colombier 	has(Hsal, "led");
2249b1707c5dSDavid du Colombier 	has(Hsmps, "mps");
2250b1707c5dSDavid du Colombier 	has(Hsncq, "ncq");
2251b1707c5dSDavid du Colombier 	has(Hssntf, "ntf");
2252b1707c5dSDavid du Colombier 	has(Hspm, "pm");
2253b1707c5dSDavid du Colombier 	has(Hpsc, "pslum");
2254b1707c5dSDavid du Colombier 	has(Hssc, "slum");
2255b1707c5dSDavid du Colombier 	has(Hsss, "ss");
2256b1707c5dSDavid du Colombier 	has(Hsxs, "sxs");
2257b1707c5dSDavid du Colombier 	portr(pr, pr + sizeof pr, hba->pi);
2258b1707c5dSDavid du Colombier 	return seprint(p, e,
225991b330d9SDavid du Colombier 		"iss %ld ncs %ld np %ld; ghc %lux isr %lux pi %lux %s ver %lux\n",
2260b1707c5dSDavid du Colombier 		(cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f),
2261b1707c5dSDavid du Colombier 		hba->ghc, hba->isr, hba->pi, pr, hba->ver);
2262b1707c5dSDavid du Colombier #undef has
2263b1707c5dSDavid du Colombier }
2264b1707c5dSDavid du Colombier 
2265b1707c5dSDavid du Colombier static int
2266b1707c5dSDavid du Colombier iawtopctl(SDev *, Cmdbuf *cmd)
2267b1707c5dSDavid du Colombier {
2268b1707c5dSDavid du Colombier 	int *v;
2269b1707c5dSDavid du Colombier 	char **f;
2270b1707c5dSDavid du Colombier 
2271b1707c5dSDavid du Colombier 	f = cmd->f;
2272b1707c5dSDavid du Colombier 	v = 0;
2273b1707c5dSDavid du Colombier 
22749e8a50a9SDavid du Colombier 	if (f[0] == nil)
22759e8a50a9SDavid du Colombier 		return 0;
2276b1707c5dSDavid du Colombier 	if(strcmp(f[0], "debug") == 0)
2277b1707c5dSDavid du Colombier 		v = &debug;
2278b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "idprint") == 0)
2279b1707c5dSDavid du Colombier 		v = &prid;
2280b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "aprint") == 0)
2281b1707c5dSDavid du Colombier 		v = &datapi;
2282b1707c5dSDavid du Colombier 	else
2283b1707c5dSDavid du Colombier 		cmderror(cmd, Ebadctl);
2284b1707c5dSDavid du Colombier 
2285b1707c5dSDavid du Colombier 	switch(cmd->nf){
2286b1707c5dSDavid du Colombier 	default:
2287b1707c5dSDavid du Colombier 		cmderror(cmd, Ebadarg);
2288b1707c5dSDavid du Colombier 	case 1:
2289b1707c5dSDavid du Colombier 		*v ^= 1;
2290b1707c5dSDavid du Colombier 		break;
2291b1707c5dSDavid du Colombier 	case 2:
22929e8a50a9SDavid du Colombier 		if(f[1])
22939e8a50a9SDavid du Colombier 			*v = strcmp(f[1], "on") == 0;
22949e8a50a9SDavid du Colombier 		else
22959e8a50a9SDavid du Colombier 			*v ^= 1;
2296b1707c5dSDavid du Colombier 		break;
2297b1707c5dSDavid du Colombier 	}
2298b1707c5dSDavid du Colombier 	return 0;
2299b1707c5dSDavid du Colombier }
2300b1707c5dSDavid du Colombier 
2301b1707c5dSDavid du Colombier SDifc sdiahciifc = {
2302b1707c5dSDavid du Colombier 	"iahci",
2303b1707c5dSDavid du Colombier 
2304b1707c5dSDavid du Colombier 	iapnp,
2305b1707c5dSDavid du Colombier 	nil,		/* legacy */
2306b1707c5dSDavid du Colombier 	iaenable,
2307b1707c5dSDavid du Colombier 	iadisable,
2308b1707c5dSDavid du Colombier 
2309b1707c5dSDavid du Colombier 	iaverify,
2310b1707c5dSDavid du Colombier 	iaonline,
2311b1707c5dSDavid du Colombier 	iario,
2312b1707c5dSDavid du Colombier 	iarctl,
2313b1707c5dSDavid du Colombier 	iawctl,
2314b1707c5dSDavid du Colombier 
2315b1707c5dSDavid du Colombier 	scsibio,
2316b1707c5dSDavid du Colombier 	nil,		/* probe */
2317b1707c5dSDavid du Colombier 	nil,		/* clear */
2318b1707c5dSDavid du Colombier 	iartopctl,
2319b1707c5dSDavid du Colombier 	iawtopctl,
2320b1707c5dSDavid du Colombier };
2321