xref: /plan9/sys/src/9/pc/sdiahci.c (revision c39c2eb30a7108f9760ec16f908a5fb85ac7496a)
1b1707c5dSDavid du Colombier /*
2b1707c5dSDavid du Colombier  * intel/amd ahci sata controller
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)
19b1707c5dSDavid du Colombier #define Tname(c)	tname[(c)->type]
20b1707c5dSDavid du Colombier 
21b1707c5dSDavid du Colombier enum {
22b1707c5dSDavid du Colombier 	NCtlr	= 4,
23b1707c5dSDavid du Colombier 	NCtlrdrv= 32,
24b1707c5dSDavid du Colombier 	NDrive	= NCtlr*NCtlrdrv,
25b1707c5dSDavid du Colombier 
26b1707c5dSDavid du Colombier 	Read	= 0,
27b1707c5dSDavid du Colombier 	Write,
28b1707c5dSDavid du Colombier };
29b1707c5dSDavid du Colombier 
30b1707c5dSDavid du Colombier /* pci space configuration */
31b1707c5dSDavid du Colombier enum {
32b1707c5dSDavid du Colombier 	Pmap	= 0x90,
33b1707c5dSDavid du Colombier 	Ppcs	= 0x91,
34b1707c5dSDavid du Colombier 	Prev	= 0xa8,
35b1707c5dSDavid du Colombier };
36b1707c5dSDavid du Colombier 
37b1707c5dSDavid du Colombier enum {
38b1707c5dSDavid du Colombier 	Tesb,
39b1707c5dSDavid du Colombier 	Tich,
40b1707c5dSDavid du Colombier 	Tsb600,
41014ad43fSDavid du Colombier 	Tunk,
42b1707c5dSDavid du Colombier };
43b1707c5dSDavid du Colombier 
44014ad43fSDavid du Colombier #define Intel(x)	((x)->pci->vid == 0x8086)
45b1707c5dSDavid du Colombier 
46b1707c5dSDavid du Colombier static char *tname[] = {
47b1707c5dSDavid du Colombier 	"63xxesb",
48b1707c5dSDavid du Colombier 	"ich",
49b1707c5dSDavid du Colombier 	"sb600",
50014ad43fSDavid du Colombier 	"unk",
51b1707c5dSDavid du Colombier };
52b1707c5dSDavid du Colombier 
53b1707c5dSDavid du Colombier enum {
54b1707c5dSDavid du Colombier 	Dnull,
55b1707c5dSDavid du Colombier 	Dmissing,
56b1707c5dSDavid du Colombier 	Dnew,
57b1707c5dSDavid du Colombier 	Dready,
58b1707c5dSDavid du Colombier 	Derror,
59b1707c5dSDavid du Colombier 	Dreset,
60b1707c5dSDavid du Colombier 	Doffline,
61b1707c5dSDavid du Colombier 	Dportreset,
62b1707c5dSDavid du Colombier 	Dlast,
63b1707c5dSDavid du Colombier };
64b1707c5dSDavid du Colombier 
65b1707c5dSDavid du Colombier static char *diskstates[Dlast] = {
66b1707c5dSDavid du Colombier 	"null",
67b1707c5dSDavid du Colombier 	"missing",
68b1707c5dSDavid du Colombier 	"new",
69b1707c5dSDavid du Colombier 	"ready",
70b1707c5dSDavid du Colombier 	"error",
71b1707c5dSDavid du Colombier 	"reset",
72b1707c5dSDavid du Colombier 	"offline",
73b1707c5dSDavid du Colombier 	"portreset",
74b1707c5dSDavid du Colombier };
75b1707c5dSDavid du Colombier 
76b1707c5dSDavid du Colombier extern SDifc sdiahciifc;
77b1707c5dSDavid du Colombier typedef struct Ctlr Ctlr;
78b1707c5dSDavid du Colombier 
79b1707c5dSDavid du Colombier enum {
80b1707c5dSDavid du Colombier 	DMautoneg,
81b1707c5dSDavid du Colombier 	DMsatai,
82b1707c5dSDavid du Colombier 	DMsataii,
83b1707c5dSDavid du Colombier };
84b1707c5dSDavid du Colombier 
85b1707c5dSDavid du Colombier static char *modename[] = {
86b1707c5dSDavid du Colombier 	"auto",
87b1707c5dSDavid du Colombier 	"satai",
88b1707c5dSDavid du Colombier 	"sataii",
89b1707c5dSDavid du Colombier };
90b1707c5dSDavid du Colombier 
91b1707c5dSDavid du Colombier static char *flagname[] = {
92b1707c5dSDavid du Colombier 	"llba",
93b1707c5dSDavid du Colombier 	"smart",
94b1707c5dSDavid du Colombier 	"power",
95b1707c5dSDavid du Colombier 	"nop",
96b1707c5dSDavid du Colombier 	"atapi",
97b1707c5dSDavid du Colombier 	"atapi16",
98b1707c5dSDavid du Colombier };
99b1707c5dSDavid du Colombier 
100b1707c5dSDavid du Colombier typedef struct {
101b1707c5dSDavid du Colombier 	Lock;
102b1707c5dSDavid du Colombier 
103b1707c5dSDavid du Colombier 	Ctlr	*ctlr;
104b1707c5dSDavid du Colombier 	SDunit	*unit;
105b1707c5dSDavid du Colombier 	char	name[10];
106b1707c5dSDavid du Colombier 	Aport	*port;
107b1707c5dSDavid du Colombier 	Aportm	portm;
108f3112f79SDavid du Colombier 	Aportc	portc;		/* redundant ptr to port and portm */
109b1707c5dSDavid du Colombier 
110b1707c5dSDavid du Colombier 	uchar	mediachange;
111b1707c5dSDavid du Colombier 	uchar	state;
112b1707c5dSDavid du Colombier 	uchar	smartrs;
113b1707c5dSDavid du Colombier 
114b1707c5dSDavid du Colombier 	uvlong	sectors;
11558db92f4SDavid du Colombier 	ulong	secsize;
116f3112f79SDavid du Colombier 	ulong	intick;		/* start tick of current transfer */
11781ede731SDavid du Colombier 	ulong	lastseen;
118b1707c5dSDavid du Colombier 	int	wait;
119f3112f79SDavid du Colombier 	uchar	mode;		/* DMautoneg, satai or sataii */
120b1707c5dSDavid du Colombier 	uchar	active;
121b1707c5dSDavid du Colombier 
122b1707c5dSDavid du Colombier 	char	serial[20+1];
123b1707c5dSDavid du Colombier 	char	firmware[8+1];
124b1707c5dSDavid du Colombier 	char	model[40+1];
125b1707c5dSDavid du Colombier 
126b1707c5dSDavid du Colombier 	ushort	info[0x200];
127b1707c5dSDavid du Colombier 
128b1707c5dSDavid du Colombier 	int	driveno;	/* ctlr*NCtlrdrv + unit */
129b1707c5dSDavid du Colombier 	/* controller port # != driveno when not all ports are enabled */
130b1707c5dSDavid du Colombier 	int	portno;
131b1707c5dSDavid du Colombier } Drive;
132b1707c5dSDavid du Colombier 
133b1707c5dSDavid du Colombier struct Ctlr {
134b1707c5dSDavid du Colombier 	Lock;
135b1707c5dSDavid du Colombier 
136b1707c5dSDavid du Colombier 	int	type;
137b1707c5dSDavid du Colombier 	int	enabled;
138b1707c5dSDavid du Colombier 	SDev	*sdev;
139b1707c5dSDavid du Colombier 	Pcidev	*pci;
140b1707c5dSDavid du Colombier 
141b1707c5dSDavid du Colombier 	uchar	*mmio;
142b1707c5dSDavid du Colombier 	ulong	*lmmio;
143b1707c5dSDavid du Colombier 	Ahba	*hba;
144b1707c5dSDavid du Colombier 
145b1707c5dSDavid du Colombier 	Drive	rawdrive[NCtlrdrv];
146b1707c5dSDavid du Colombier 	Drive*	drive[NCtlrdrv];
147b1707c5dSDavid du Colombier 	int	ndrive;
148014ad43fSDavid du Colombier 	int	mport;
149b1707c5dSDavid du Colombier };
150b1707c5dSDavid du Colombier 
151b1707c5dSDavid du Colombier static	Ctlr	iactlr[NCtlr];
152b1707c5dSDavid du Colombier static	SDev	sdevs[NCtlr];
153b1707c5dSDavid du Colombier static	int	niactlr;
154b1707c5dSDavid du Colombier 
155b1707c5dSDavid du Colombier static	Drive	*iadrive[NDrive];
156b1707c5dSDavid du Colombier static	int	niadrive;
157b1707c5dSDavid du Colombier 
1589e8a50a9SDavid du Colombier /* these are fiddled in iawtopctl() */
159b1707c5dSDavid du Colombier static	int	debug;
160b1707c5dSDavid du Colombier static	int	prid = 1;
161b1707c5dSDavid du Colombier static	int	datapi;
162b1707c5dSDavid du Colombier 
163b1707c5dSDavid du Colombier static char stab[] = {
164b1707c5dSDavid du Colombier [0]	'i', 'm',
165b1707c5dSDavid du Colombier [8]	't', 'c', 'p', 'e',
166b1707c5dSDavid du Colombier [16]	'N', 'I', 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X'
167b1707c5dSDavid du Colombier };
168b1707c5dSDavid du Colombier 
169b1707c5dSDavid du Colombier static void
170b1707c5dSDavid du Colombier serrstr(ulong r, char *s, char *e)
171b1707c5dSDavid du Colombier {
172b1707c5dSDavid du Colombier 	int i;
173b1707c5dSDavid du Colombier 
174b1707c5dSDavid du Colombier 	e -= 3;
175b1707c5dSDavid du Colombier 	for(i = 0; i < nelem(stab) && s < e; i++)
176b1707c5dSDavid du Colombier 		if(r & (1<<i) && stab[i]){
177b1707c5dSDavid du Colombier 			*s++ = stab[i];
178b1707c5dSDavid du Colombier 			if(SerrBad & (1<<i))
179b1707c5dSDavid du Colombier 				*s++ = '*';
180b1707c5dSDavid du Colombier 		}
181b1707c5dSDavid du Colombier 	*s = 0;
182b1707c5dSDavid du Colombier }
183b1707c5dSDavid du Colombier 
184b1707c5dSDavid du Colombier static char ntab[] = "0123456789abcdef";
185b1707c5dSDavid du Colombier 
186b1707c5dSDavid du Colombier static void
187b1707c5dSDavid du Colombier preg(uchar *reg, int n)
188b1707c5dSDavid du Colombier {
189b1707c5dSDavid du Colombier 	int i;
190b1707c5dSDavid du Colombier 	char buf[25*3+1], *e;
191b1707c5dSDavid du Colombier 
192b1707c5dSDavid du Colombier 	e = buf;
193b1707c5dSDavid du Colombier 	for(i = 0; i < n; i++){
194b1707c5dSDavid du Colombier 		*e++ = ntab[reg[i]>>4];
195b1707c5dSDavid du Colombier 		*e++ = ntab[reg[i]&0xf];
196b1707c5dSDavid du Colombier 		*e++ = ' ';
197b1707c5dSDavid du Colombier 	}
198b1707c5dSDavid du Colombier 	*e++ = '\n';
199b1707c5dSDavid du Colombier 	*e = 0;
200b1707c5dSDavid du Colombier 	dprint(buf);
201b1707c5dSDavid du Colombier }
202b1707c5dSDavid du Colombier 
203b1707c5dSDavid du Colombier static void
204b1707c5dSDavid du Colombier dreg(char *s, Aport *p)
205b1707c5dSDavid du Colombier {
2063d56950aSDavid du Colombier 	dprint("ahci: %stask=%lux; cmd=%lux; ci=%lux; is=%lux\n",
2073d56950aSDavid du Colombier 		s, p->task, p->cmd, p->ci, p->isr);
208b1707c5dSDavid du Colombier }
209b1707c5dSDavid du Colombier 
210b1707c5dSDavid du Colombier static void
211b1707c5dSDavid du Colombier esleep(int ms)
212b1707c5dSDavid du Colombier {
213b1707c5dSDavid du Colombier 	if(waserror())
214b1707c5dSDavid du Colombier 		return;
215b1707c5dSDavid du Colombier 	tsleep(&up->sleep, return0, 0, ms);
216b1707c5dSDavid du Colombier 	poperror();
217b1707c5dSDavid du Colombier }
218b1707c5dSDavid du Colombier 
219b1707c5dSDavid du Colombier typedef struct {
220b1707c5dSDavid du Colombier 	Aport	*p;
221b1707c5dSDavid du Colombier 	int	i;
222b1707c5dSDavid du Colombier }Asleep;
223b1707c5dSDavid du Colombier 
224b1707c5dSDavid du Colombier static int
225b1707c5dSDavid du Colombier ahciclear(void *v)
226b1707c5dSDavid du Colombier {
227b1707c5dSDavid du Colombier 	Asleep *s;
228b1707c5dSDavid du Colombier 
229b1707c5dSDavid du Colombier 	s = v;
230b1707c5dSDavid du Colombier 	return (s->p->ci & s->i) == 0;
231b1707c5dSDavid du Colombier }
232b1707c5dSDavid du Colombier 
233b1707c5dSDavid du Colombier static void
234b1707c5dSDavid du Colombier aesleep(Aportm *m, Asleep *a, int ms)
235b1707c5dSDavid du Colombier {
236b1707c5dSDavid du Colombier 	if(waserror())
237b1707c5dSDavid du Colombier 		return;
238b1707c5dSDavid du Colombier 	tsleep(m, ahciclear, a, ms);
239b1707c5dSDavid du Colombier 	poperror();
240b1707c5dSDavid du Colombier }
241b1707c5dSDavid du Colombier 
242b1707c5dSDavid du Colombier static int
243b1707c5dSDavid du Colombier ahciwait(Aportc *c, int ms)
244b1707c5dSDavid du Colombier {
245b1707c5dSDavid du Colombier 	Asleep as;
246b1707c5dSDavid du Colombier 	Aport *p;
247b1707c5dSDavid du Colombier 
248b1707c5dSDavid du Colombier 	p = c->p;
249b1707c5dSDavid du Colombier 	p->ci = 1;
250b1707c5dSDavid du Colombier 	as.p = p;
251b1707c5dSDavid du Colombier 	as.i = 1;
252b1707c5dSDavid du Colombier 	aesleep(c->m, &as, ms);
253b1707c5dSDavid du Colombier 	if((p->task&1) == 0 && p->ci == 0)
254b1707c5dSDavid du Colombier 		return 0;
255b1707c5dSDavid du Colombier 	dreg("ahciwait timeout ", c->p);
256b1707c5dSDavid du Colombier 	return -1;
257b1707c5dSDavid du Colombier }
258b1707c5dSDavid du Colombier 
259b1707c5dSDavid du Colombier static int
260b1707c5dSDavid du Colombier nop(Aportc *pc)
261b1707c5dSDavid du Colombier {
262b1707c5dSDavid du Colombier 	uchar *c;
263b1707c5dSDavid du Colombier 	Actab *t;
264b1707c5dSDavid du Colombier 	Alist *l;
265b1707c5dSDavid du Colombier 
266b1707c5dSDavid du Colombier 	if((pc->m->feat & Dnop) == 0)
267b1707c5dSDavid du Colombier 		return -1;
268b1707c5dSDavid du Colombier 
269b1707c5dSDavid du Colombier 	t = pc->m->ctab;
270b1707c5dSDavid du Colombier 	c = t->cfis;
271b1707c5dSDavid du Colombier 
272b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
273b1707c5dSDavid du Colombier 	c[0] = 0x27;
274b1707c5dSDavid du Colombier 	c[1] = 0x80;
275b1707c5dSDavid du Colombier 	c[2] = 0x00;
276b1707c5dSDavid du Colombier 	c[7] = 0xa0;		/* obsolete device bits */
277b1707c5dSDavid du Colombier 
278b1707c5dSDavid du Colombier 	l = pc->m->list;
279b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
280b1707c5dSDavid du Colombier 	l->len = 0;
281b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
282b1707c5dSDavid du Colombier 	l->ctabhi = 0;
283b1707c5dSDavid du Colombier 
284b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
285b1707c5dSDavid du Colombier }
286b1707c5dSDavid du Colombier 
287b1707c5dSDavid du Colombier static int
288b1707c5dSDavid du Colombier setfeatures(Aportc *pc, uchar f)
289b1707c5dSDavid du Colombier {
290b1707c5dSDavid du Colombier 	uchar *c;
291b1707c5dSDavid du Colombier 	Actab *t;
292b1707c5dSDavid du Colombier 	Alist *l;
293b1707c5dSDavid du Colombier 
294b1707c5dSDavid du Colombier 	t = pc->m->ctab;
295b1707c5dSDavid du Colombier 	c = t->cfis;
296b1707c5dSDavid du Colombier 
297b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
298b1707c5dSDavid du Colombier 	c[0] = 0x27;
299b1707c5dSDavid du Colombier 	c[1] = 0x80;
300b1707c5dSDavid du Colombier 	c[2] = 0xef;
301b1707c5dSDavid du Colombier 	c[3] = f;
302b1707c5dSDavid du Colombier 	c[7] = 0xa0;		/* obsolete device bits */
303b1707c5dSDavid du Colombier 
304b1707c5dSDavid du Colombier 	l = pc->m->list;
305b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
306b1707c5dSDavid du Colombier 	l->len = 0;
307b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
308b1707c5dSDavid du Colombier 	l->ctabhi = 0;
309b1707c5dSDavid du Colombier 
310b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
311b1707c5dSDavid du Colombier }
312b1707c5dSDavid du Colombier 
313b1707c5dSDavid du Colombier static int
314b1707c5dSDavid du Colombier setudmamode(Aportc *pc, uchar f)
315b1707c5dSDavid du Colombier {
316b1707c5dSDavid du Colombier 	uchar *c;
317b1707c5dSDavid du Colombier 	Actab *t;
318b1707c5dSDavid du Colombier 	Alist *l;
319b1707c5dSDavid du Colombier 
320b1707c5dSDavid du Colombier 	/* hack */
321b1707c5dSDavid du Colombier 	if((pc->p->sig >> 16) == 0xeb14)
322b1707c5dSDavid du Colombier 		return 0;
323b1707c5dSDavid du Colombier 
324b1707c5dSDavid du Colombier 	t = pc->m->ctab;
325b1707c5dSDavid du Colombier 	c = t->cfis;
326b1707c5dSDavid du Colombier 
327b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
328b1707c5dSDavid du Colombier 	c[0] = 0x27;
329b1707c5dSDavid du Colombier 	c[1] = 0x80;
330b1707c5dSDavid du Colombier 	c[2] = 0xef;
331b1707c5dSDavid du Colombier 	c[3] = 3;		/* set transfer mode */
332b1707c5dSDavid du Colombier 	c[7] = 0xa0;		/* obsolete device bits */
333b1707c5dSDavid du Colombier 	c[12] = 0x40 | f;	/* sector count */
334b1707c5dSDavid du Colombier 
335b1707c5dSDavid du Colombier 	l = pc->m->list;
336b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
337b1707c5dSDavid du Colombier 	l->len = 0;
338b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
339b1707c5dSDavid du Colombier 	l->ctabhi = 0;
340b1707c5dSDavid du Colombier 
341b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
342b1707c5dSDavid du Colombier }
343b1707c5dSDavid du Colombier 
344b1707c5dSDavid du Colombier static void
345b1707c5dSDavid du Colombier asleep(int ms)
346b1707c5dSDavid du Colombier {
347b1707c5dSDavid du Colombier 	if(up == nil)
348b1707c5dSDavid du Colombier 		delay(ms);
349b1707c5dSDavid du Colombier 	else
350b1707c5dSDavid du Colombier 		esleep(ms);
351b1707c5dSDavid du Colombier }
352b1707c5dSDavid du Colombier 
353b1707c5dSDavid du Colombier static int
354b1707c5dSDavid du Colombier ahciportreset(Aportc *c)
355b1707c5dSDavid du Colombier {
35691b330d9SDavid du Colombier 	ulong *cmd, i;
357b1707c5dSDavid du Colombier 	Aport *p;
358b1707c5dSDavid du Colombier 
359b1707c5dSDavid du Colombier 	p = c->p;
360b1707c5dSDavid du Colombier 	cmd = &p->cmd;
361b1707c5dSDavid du Colombier 	*cmd &= ~(Afre|Ast);
362b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 25){
363b1707c5dSDavid du Colombier 		if((*cmd&Acr) == 0)
364b1707c5dSDavid du Colombier 			break;
365b1707c5dSDavid du Colombier 		asleep(25);
366b1707c5dSDavid du Colombier 	}
367b1707c5dSDavid du Colombier 	p->sctl = 1|(p->sctl&~7);
368b1707c5dSDavid du Colombier 	delay(1);
369b1707c5dSDavid du Colombier 	p->sctl &= ~7;
370b1707c5dSDavid du Colombier 	return 0;
371b1707c5dSDavid du Colombier }
372b1707c5dSDavid du Colombier 
373b1707c5dSDavid du Colombier static int
374b1707c5dSDavid du Colombier smart(Aportc *pc, int n)
375b1707c5dSDavid du Colombier {
376b1707c5dSDavid du Colombier 	uchar *c;
377b1707c5dSDavid du Colombier 	Actab *t;
378b1707c5dSDavid du Colombier 	Alist *l;
379b1707c5dSDavid du Colombier 
380b1707c5dSDavid du Colombier 	if((pc->m->feat&Dsmart) == 0)
381b1707c5dSDavid du Colombier 		return -1;
382b1707c5dSDavid du Colombier 
383b1707c5dSDavid du Colombier 	t = pc->m->ctab;
384b1707c5dSDavid du Colombier 	c = t->cfis;
385b1707c5dSDavid du Colombier 
386b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
387b1707c5dSDavid du Colombier 	c[0] = 0x27;
388b1707c5dSDavid du Colombier 	c[1] = 0x80;
389b1707c5dSDavid du Colombier 	c[2] = 0xb0;
390b1707c5dSDavid du Colombier 	c[3] = 0xd8 + n;	/* able smart */
391b1707c5dSDavid du Colombier 	c[5] = 0x4f;
392b1707c5dSDavid du Colombier 	c[6] = 0xc2;
393b1707c5dSDavid du Colombier 	c[7] = 0xa0;
394b1707c5dSDavid du Colombier 
395b1707c5dSDavid du Colombier 	l = pc->m->list;
396b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
397b1707c5dSDavid du Colombier 	l->len = 0;
398b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
399b1707c5dSDavid du Colombier 	l->ctabhi = 0;
400b1707c5dSDavid du Colombier 
401b1707c5dSDavid du Colombier 	if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
4023d56950aSDavid du Colombier 		dprint("ahci: smart fail %lux\n", pc->p->task);
403b1707c5dSDavid du Colombier //		preg(pc->m->fis.r, 20);
404b1707c5dSDavid du Colombier 		return -1;
405b1707c5dSDavid du Colombier 	}
406b1707c5dSDavid du Colombier 	if(n)
407b1707c5dSDavid du Colombier 		return 0;
408b1707c5dSDavid du Colombier 	return 1;
409b1707c5dSDavid du Colombier }
410b1707c5dSDavid du Colombier 
411b1707c5dSDavid du Colombier static int
412b1707c5dSDavid du Colombier smartrs(Aportc *pc)
413b1707c5dSDavid du Colombier {
414b1707c5dSDavid du Colombier 	uchar *c;
415b1707c5dSDavid du Colombier 	Actab *t;
416b1707c5dSDavid du Colombier 	Alist *l;
417b1707c5dSDavid du Colombier 
418b1707c5dSDavid du Colombier 	t = pc->m->ctab;
419b1707c5dSDavid du Colombier 	c = t->cfis;
420b1707c5dSDavid du Colombier 
421b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
422b1707c5dSDavid du Colombier 	c[0] = 0x27;
423b1707c5dSDavid du Colombier 	c[1] = 0x80;
424b1707c5dSDavid du Colombier 	c[2] = 0xb0;
425b1707c5dSDavid du Colombier 	c[3] = 0xda;		/* return smart status */
426b1707c5dSDavid du Colombier 	c[5] = 0x4f;
427b1707c5dSDavid du Colombier 	c[6] = 0xc2;
428b1707c5dSDavid du Colombier 	c[7] = 0xa0;
429b1707c5dSDavid du Colombier 
430b1707c5dSDavid du Colombier 	l = pc->m->list;
431b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
432b1707c5dSDavid du Colombier 	l->len = 0;
433b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
434b1707c5dSDavid du Colombier 	l->ctabhi = 0;
435b1707c5dSDavid du Colombier 
436b1707c5dSDavid du Colombier 	c = pc->m->fis.r;
437b1707c5dSDavid du Colombier 	if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
4383d56950aSDavid du Colombier 		dprint("ahci: smart fail %lux\n", pc->p->task);
439b1707c5dSDavid du Colombier 		preg(c, 20);
440b1707c5dSDavid du Colombier 		return -1;
441b1707c5dSDavid du Colombier 	}
442b1707c5dSDavid du Colombier 	if(c[5] == 0x4f && c[6] == 0xc2)
443b1707c5dSDavid du Colombier 		return 1;
444b1707c5dSDavid du Colombier 	return 0;
445b1707c5dSDavid du Colombier }
446b1707c5dSDavid du Colombier 
447b1707c5dSDavid du Colombier static int
44881ede731SDavid du Colombier ahciflushcache(Aportc *pc)
449b1707c5dSDavid du Colombier {
450b1707c5dSDavid du Colombier 	uchar *c, llba;
451b1707c5dSDavid du Colombier 	Actab *t;
452b1707c5dSDavid du Colombier 	Alist *l;
453b1707c5dSDavid du Colombier 	static uchar tab[2] = {0xe7, 0xea};
454b1707c5dSDavid du Colombier 
455b1707c5dSDavid du Colombier 	llba = pc->m->feat&Dllba? 1: 0;
456b1707c5dSDavid du Colombier 	t = pc->m->ctab;
457b1707c5dSDavid du Colombier 	c = t->cfis;
458b1707c5dSDavid du Colombier 
459b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
460b1707c5dSDavid du Colombier 	c[0] = 0x27;
461b1707c5dSDavid du Colombier 	c[1] = 0x80;
462b1707c5dSDavid du Colombier 	c[2] = tab[llba];
463b1707c5dSDavid du Colombier 	c[7] = 0xa0;
464b1707c5dSDavid du Colombier 
465b1707c5dSDavid du Colombier 	l = pc->m->list;
466b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
467b1707c5dSDavid du Colombier 	l->len = 0;
468b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
469b1707c5dSDavid du Colombier 	l->ctabhi = 0;
470b1707c5dSDavid du Colombier 
471b1707c5dSDavid du Colombier 	if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
4723d56950aSDavid du Colombier 		dprint("ahciflushcache: fail %lux\n", pc->p->task);
473b1707c5dSDavid du Colombier //		preg( pc->m->fis.r, 20);
474b1707c5dSDavid du Colombier 		return -1;
475b1707c5dSDavid du Colombier 	}
476b1707c5dSDavid du Colombier 	return 0;
477b1707c5dSDavid du Colombier }
478b1707c5dSDavid du Colombier 
479b1707c5dSDavid du Colombier static ushort
480b1707c5dSDavid du Colombier gbit16(void *a)
481b1707c5dSDavid du Colombier {
482b1707c5dSDavid du Colombier 	uchar *i;
483b1707c5dSDavid du Colombier 
484b1707c5dSDavid du Colombier 	i = a;
48591157df7SDavid du Colombier 	return i[1]<<8 | i[0];
486b1707c5dSDavid du Colombier }
487b1707c5dSDavid du Colombier 
48891b330d9SDavid du Colombier static ulong
489b1707c5dSDavid du Colombier gbit32(void *a)
490b1707c5dSDavid du Colombier {
49191b330d9SDavid du Colombier 	ulong j;
492b1707c5dSDavid du Colombier 	uchar *i;
493b1707c5dSDavid du Colombier 
494b1707c5dSDavid du Colombier 	i = a;
495b1707c5dSDavid du Colombier 	j  = i[3] << 24;
496b1707c5dSDavid du Colombier 	j |= i[2] << 16;
497b1707c5dSDavid du Colombier 	j |= i[1] << 8;
498b1707c5dSDavid du Colombier 	j |= i[0];
499b1707c5dSDavid du Colombier 	return j;
500b1707c5dSDavid du Colombier }
501b1707c5dSDavid du Colombier 
502b1707c5dSDavid du Colombier static uvlong
503b1707c5dSDavid du Colombier gbit64(void *a)
504b1707c5dSDavid du Colombier {
505b1707c5dSDavid du Colombier 	uchar *i;
506b1707c5dSDavid du Colombier 
507b1707c5dSDavid du Colombier 	i = a;
508b1707c5dSDavid du Colombier 	return (uvlong)gbit32(i+4) << 32 | gbit32(a);
509b1707c5dSDavid du Colombier }
510b1707c5dSDavid du Colombier 
511b1707c5dSDavid du Colombier static int
512b1707c5dSDavid du Colombier ahciidentify0(Aportc *pc, void *id, int atapi)
513b1707c5dSDavid du Colombier {
514b1707c5dSDavid du Colombier 	uchar *c;
515b1707c5dSDavid du Colombier 	Actab *t;
516b1707c5dSDavid du Colombier 	Alist *l;
517b1707c5dSDavid du Colombier 	Aprdt *p;
518b1707c5dSDavid du Colombier 	static uchar tab[] = { 0xec, 0xa1, };
519b1707c5dSDavid du Colombier 
520b1707c5dSDavid du Colombier 	t = pc->m->ctab;
521b1707c5dSDavid du Colombier 	c = t->cfis;
522b1707c5dSDavid du Colombier 
523b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
524b1707c5dSDavid du Colombier 	c[0] = 0x27;
525b1707c5dSDavid du Colombier 	c[1] = 0x80;
526b1707c5dSDavid du Colombier 	c[2] = tab[atapi];
527b1707c5dSDavid du Colombier 	c[7] = 0xa0;		/* obsolete device bits */
528b1707c5dSDavid du Colombier 
529b1707c5dSDavid du Colombier 	l = pc->m->list;
530b1707c5dSDavid du Colombier 	l->flags = 1<<16 | 0x5;
531b1707c5dSDavid du Colombier 	l->len = 0;
532b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
533b1707c5dSDavid du Colombier 	l->ctabhi = 0;
534b1707c5dSDavid du Colombier 
535b1707c5dSDavid du Colombier 	memset(id, 0, 0x100);
536b1707c5dSDavid du Colombier 	p = &t->prdt;
537b1707c5dSDavid du Colombier 	p->dba = PCIWADDR(id);
538b1707c5dSDavid du Colombier 	p->dbahi = 0;
539b1707c5dSDavid du Colombier 	p->count = 1<<31 | (0x200-2) | 1;
540b1707c5dSDavid du Colombier 
541b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
542b1707c5dSDavid du Colombier }
543b1707c5dSDavid du Colombier 
544b1707c5dSDavid du Colombier static vlong
545b1707c5dSDavid du Colombier ahciidentify(Aportc *pc, ushort *id)
546b1707c5dSDavid du Colombier {
547b1707c5dSDavid du Colombier 	int i, sig;
548b1707c5dSDavid du Colombier 	vlong s;
549b1707c5dSDavid du Colombier 	Aportm *m;
550b1707c5dSDavid du Colombier 
551b1707c5dSDavid du Colombier 	m = pc->m;
552b1707c5dSDavid du Colombier 	m->feat = 0;
553b1707c5dSDavid du Colombier 	m->smart = 0;
554b1707c5dSDavid du Colombier 	i = 0;
555b1707c5dSDavid du Colombier 	sig = pc->p->sig >> 16;
556b1707c5dSDavid du Colombier 	if(sig == 0xeb14){
557b1707c5dSDavid du Colombier 		m->feat |= Datapi;
558b1707c5dSDavid du Colombier 		i = 1;
559b1707c5dSDavid du Colombier 	}
560b1707c5dSDavid du Colombier 	if(ahciidentify0(pc, id, i) == -1)
561b1707c5dSDavid du Colombier 		return -1;
562b1707c5dSDavid du Colombier 
563b1707c5dSDavid du Colombier 	i = gbit16(id+83) | gbit16(id+86);
564b1707c5dSDavid du Colombier 	if(i & (1<<10)){
565b1707c5dSDavid du Colombier 		m->feat |= Dllba;
566b1707c5dSDavid du Colombier 		s = gbit64(id+100);
567b1707c5dSDavid du Colombier 	}else
568b1707c5dSDavid du Colombier 		s = gbit32(id+60);
569b1707c5dSDavid du Colombier 
570b1707c5dSDavid du Colombier 	if(m->feat&Datapi){
571b1707c5dSDavid du Colombier 		i = gbit16(id+0);
572b1707c5dSDavid du Colombier 		if(i&1)
573b1707c5dSDavid du Colombier 			m->feat |= Datapi16;
574b1707c5dSDavid du Colombier 	}
575b1707c5dSDavid du Colombier 
576b1707c5dSDavid du Colombier 	i = gbit16(id+83);
577b1707c5dSDavid du Colombier 	if((i>>14) == 1) {
578b1707c5dSDavid du Colombier 		if(i & (1<<3))
579b1707c5dSDavid du Colombier 			m->feat |= Dpower;
580b1707c5dSDavid du Colombier 		i = gbit16(id+82);
581b1707c5dSDavid du Colombier 		if(i & 1)
582b1707c5dSDavid du Colombier 			m->feat |= Dsmart;
583b1707c5dSDavid du Colombier 		if(i & (1<<14))
584b1707c5dSDavid du Colombier 			m->feat |= Dnop;
585b1707c5dSDavid du Colombier 	}
586b1707c5dSDavid du Colombier 	return s;
587b1707c5dSDavid du Colombier }
588b1707c5dSDavid du Colombier 
589b1707c5dSDavid du Colombier static int
590b1707c5dSDavid du Colombier ahciquiet(Aport *a)
591b1707c5dSDavid du Colombier {
59291b330d9SDavid du Colombier 	ulong *p, i;
593b1707c5dSDavid du Colombier 
594b1707c5dSDavid du Colombier 	p = &a->cmd;
595b1707c5dSDavid du Colombier 	*p &= ~Ast;
596b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 50){
597b1707c5dSDavid du Colombier 		if((*p & Acr) == 0)
598b1707c5dSDavid du Colombier 			goto stop;
599b1707c5dSDavid du Colombier 		asleep(50);
600b1707c5dSDavid du Colombier 	}
601b1707c5dSDavid du Colombier 	return -1;
602b1707c5dSDavid du Colombier stop:
603b1707c5dSDavid du Colombier 	if((a->task & (ASdrq|ASbsy)) == 0){
604b1707c5dSDavid du Colombier 		*p |= Ast;
605b1707c5dSDavid du Colombier 		return 0;
606b1707c5dSDavid du Colombier 	}
607b1707c5dSDavid du Colombier 
608b1707c5dSDavid du Colombier 	*p |= Aclo;
609b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 50){
610b1707c5dSDavid du Colombier 		if((*p & Aclo) == 0)
611b1707c5dSDavid du Colombier 			goto stop1;
612b1707c5dSDavid du Colombier 		asleep(50);
613b1707c5dSDavid du Colombier 	}
614b1707c5dSDavid du Colombier 	return -1;
615b1707c5dSDavid du Colombier stop1:
616b1707c5dSDavid du Colombier 	/* extra check */
6173d56950aSDavid du Colombier 	dprint("ahci: clo clear %lx\n", a->task);
618b1707c5dSDavid du Colombier 	if(a->task & ASbsy)
619b1707c5dSDavid du Colombier 		return -1;
620b1707c5dSDavid du Colombier 	*p |= Ast;
621b1707c5dSDavid du Colombier 	return 0;
622b1707c5dSDavid du Colombier }
623b1707c5dSDavid du Colombier 
624b1707c5dSDavid du Colombier static int
625b1707c5dSDavid du Colombier ahcicomreset(Aportc *pc)
626b1707c5dSDavid du Colombier {
627b1707c5dSDavid du Colombier 	uchar *c;
628b1707c5dSDavid du Colombier 	Actab *t;
629b1707c5dSDavid du Colombier 	Alist *l;
630b1707c5dSDavid du Colombier 
631b1707c5dSDavid du Colombier 	dprint("ahcicomreset\n");
6323d56950aSDavid du Colombier 	dreg("ahci: comreset ", pc->p);
633b1707c5dSDavid du Colombier 	if(ahciquiet(pc->p) == -1){
6343d56950aSDavid du Colombier 		dprint("ahciquiet failed\n");
635b1707c5dSDavid du Colombier 		return -1;
636b1707c5dSDavid du Colombier 	}
637b1707c5dSDavid du Colombier 	dreg("comreset ", pc->p);
638b1707c5dSDavid du Colombier 
639b1707c5dSDavid du Colombier 	t = pc->m->ctab;
640b1707c5dSDavid du Colombier 	c = t->cfis;
641b1707c5dSDavid du Colombier 
642b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
643b1707c5dSDavid du Colombier 	c[0] = 0x27;
644b1707c5dSDavid du Colombier 	c[1] = 0x00;
645b1707c5dSDavid du Colombier 	c[7] = 0xa0;		/* obsolete device bits */
646b1707c5dSDavid du Colombier 	c[15] = 1<<2;		/* srst */
647b1707c5dSDavid du Colombier 
648b1707c5dSDavid du Colombier 	l = pc->m->list;
649b1707c5dSDavid du Colombier 	l->flags = Lclear | Lreset | 0x5;
650b1707c5dSDavid du Colombier 	l->len = 0;
651b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
652b1707c5dSDavid du Colombier 	l->ctabhi = 0;
653b1707c5dSDavid du Colombier 
654b1707c5dSDavid du Colombier 	if(ahciwait(pc, 500) == -1){
6553d56950aSDavid du Colombier 		dprint("ahcicomreset: first command failed\n");
656b1707c5dSDavid du Colombier 		return -1;
657b1707c5dSDavid du Colombier 	}
658b1707c5dSDavid du Colombier 	microdelay(250);
659b1707c5dSDavid du Colombier 	dreg("comreset ", pc->p);
660b1707c5dSDavid du Colombier 
661b1707c5dSDavid du Colombier 	memset(c, 0, 0x20);
662b1707c5dSDavid du Colombier 	c[0] = 0x27;
663b1707c5dSDavid du Colombier 	c[1] = 0x00;
664b1707c5dSDavid du Colombier 	c[7] = 0xa0;		/* obsolete device bits */
665b1707c5dSDavid du Colombier 
666b1707c5dSDavid du Colombier 	l = pc->m->list;
667b1707c5dSDavid du Colombier 	l->flags = Lwrite | 0x5;
668b1707c5dSDavid du Colombier 	l->len = 0;
669b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
670b1707c5dSDavid du Colombier 	l->ctabhi = 0;
671b1707c5dSDavid du Colombier 
672b1707c5dSDavid du Colombier 	if(ahciwait(pc, 150) == -1){
6733d56950aSDavid du Colombier 		dprint("ahcicomreset: second command failed\n");
674b1707c5dSDavid du Colombier 		return -1;
675b1707c5dSDavid du Colombier 	}
676b1707c5dSDavid du Colombier 	dreg("comreset ", pc->p);
677b1707c5dSDavid du Colombier 	return 0;
678b1707c5dSDavid du Colombier }
679b1707c5dSDavid du Colombier 
680b1707c5dSDavid du Colombier static int
681b1707c5dSDavid du Colombier ahciidle(Aport *port)
682b1707c5dSDavid du Colombier {
68391b330d9SDavid du Colombier 	ulong *p, i, r;
684b1707c5dSDavid du Colombier 
685b1707c5dSDavid du Colombier 	p = &port->cmd;
686b1707c5dSDavid du Colombier 	if((*p & Arun) == 0)
687b1707c5dSDavid du Colombier 		return 0;
688b1707c5dSDavid du Colombier 	*p &= ~Ast;
689b1707c5dSDavid du Colombier 	r = 0;
690b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 25){
691b1707c5dSDavid du Colombier 		if((*p & Acr) == 0)
692b1707c5dSDavid du Colombier 			goto stop;
693b1707c5dSDavid du Colombier 		asleep(25);
694b1707c5dSDavid du Colombier 	}
695b1707c5dSDavid du Colombier 	r = -1;
696b1707c5dSDavid du Colombier stop:
697b1707c5dSDavid du Colombier 	if((*p & Afre) == 0)
698b1707c5dSDavid du Colombier 		return r;
699b1707c5dSDavid du Colombier 	*p &= ~Afre;
700b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 25){
701b1707c5dSDavid du Colombier 		if((*p & Afre) == 0)
702b1707c5dSDavid du Colombier 			return 0;
703b1707c5dSDavid du Colombier 		asleep(25);
704b1707c5dSDavid du Colombier 	}
705b1707c5dSDavid du Colombier 	return -1;
706b1707c5dSDavid du Colombier }
707b1707c5dSDavid du Colombier 
708b1707c5dSDavid du Colombier /*
709b1707c5dSDavid du Colombier  * § 6.2.2.1  first part; comreset handled by reset disk.
710b1707c5dSDavid du Colombier  *	- remainder is handled by configdisk.
711b1707c5dSDavid du Colombier  *	- ahcirecover is a quick recovery from a failed command.
712b1707c5dSDavid du Colombier  */
71391b330d9SDavid du Colombier static int
714b1707c5dSDavid du Colombier ahciswreset(Aportc *pc)
715b1707c5dSDavid du Colombier {
716b1707c5dSDavid du Colombier 	int i;
717b1707c5dSDavid du Colombier 
718b1707c5dSDavid du Colombier 	i = ahciidle(pc->p);
719b1707c5dSDavid du Colombier 	pc->p->cmd |= Afre;
720b1707c5dSDavid du Colombier 	if(i == -1)
721b1707c5dSDavid du Colombier 		return -1;
722b1707c5dSDavid du Colombier 	if(pc->p->task & (ASdrq|ASbsy))
723b1707c5dSDavid du Colombier 		return -1;
724b1707c5dSDavid du Colombier 	return 0;
725b1707c5dSDavid du Colombier }
726b1707c5dSDavid du Colombier 
72791b330d9SDavid du Colombier static int
728b1707c5dSDavid du Colombier ahcirecover(Aportc *pc)
729b1707c5dSDavid du Colombier {
730b1707c5dSDavid du Colombier 	ahciswreset(pc);
731b1707c5dSDavid du Colombier 	pc->p->cmd |= Ast;
732b1707c5dSDavid du Colombier 	if(setudmamode(pc, 5) == -1)
733b1707c5dSDavid du Colombier 		return -1;
734b1707c5dSDavid du Colombier 	return 0;
735b1707c5dSDavid du Colombier }
736b1707c5dSDavid du Colombier 
737b1707c5dSDavid du Colombier static void*
738b1707c5dSDavid du Colombier malign(int size, int align)
739b1707c5dSDavid du Colombier {
740b1707c5dSDavid du Colombier 	void *v;
741b1707c5dSDavid du Colombier 
742b1707c5dSDavid du Colombier 	v = xspanalloc(size, align, 0);
743b1707c5dSDavid du Colombier 	memset(v, 0, size);
744b1707c5dSDavid du Colombier 	return v;
745b1707c5dSDavid du Colombier }
746b1707c5dSDavid du Colombier 
747b1707c5dSDavid du Colombier static void
748b1707c5dSDavid du Colombier setupfis(Afis *f)
749b1707c5dSDavid du Colombier {
750b1707c5dSDavid du Colombier 	f->base = malign(0x100, 0x100);
751b1707c5dSDavid du Colombier 	f->d = f->base + 0;
752b1707c5dSDavid du Colombier 	f->p = f->base + 0x20;
753b1707c5dSDavid du Colombier 	f->r = f->base + 0x40;
754b1707c5dSDavid du Colombier 	f->u = f->base + 0x60;
75591b330d9SDavid du Colombier 	f->devicebits = (ulong*)(f->base + 0x58);
756b1707c5dSDavid du Colombier }
757b1707c5dSDavid du Colombier 
758b1707c5dSDavid du Colombier static void
759b1707c5dSDavid du Colombier ahciwakeup(Aport *p)
760b1707c5dSDavid du Colombier {
761b1707c5dSDavid du Colombier 	ushort s;
762b1707c5dSDavid du Colombier 
763b1707c5dSDavid du Colombier 	s = p->sstatus;
764b1707c5dSDavid du Colombier 	if((s & 0xF00) != 0x600)
765b1707c5dSDavid du Colombier 		return;
766b1707c5dSDavid du Colombier 	if((s & 7) != 1){		/* not (device, no phy) */
7673d56950aSDavid du Colombier 		iprint("ahci: slumbering drive unwakable %ux\n", s);
768b1707c5dSDavid du Colombier 		return;
769b1707c5dSDavid du Colombier 	}
770b1707c5dSDavid du Colombier 	p->sctl = 3*Aipm | 0*Aspd | Adet;
771b1707c5dSDavid du Colombier 	delay(1);
772b1707c5dSDavid du Colombier 	p->sctl &= ~7;
773b1707c5dSDavid du Colombier //	iprint("ahci: wake %ux -> %ux\n", s, p->sstatus);
774b1707c5dSDavid du Colombier }
775b1707c5dSDavid du Colombier 
776b1707c5dSDavid du Colombier static int
777b1707c5dSDavid du Colombier ahciconfigdrive(Ahba *h, Aportc *c, int mode)
778b1707c5dSDavid du Colombier {
779b1707c5dSDavid du Colombier 	Aportm *m;
780b1707c5dSDavid du Colombier 	Aport *p;
781b1707c5dSDavid du Colombier 
782b1707c5dSDavid du Colombier 	p = c->p;
783b1707c5dSDavid du Colombier 	m = c->m;
784b1707c5dSDavid du Colombier 
785b1707c5dSDavid du Colombier 	if(m->list == 0){
786b1707c5dSDavid du Colombier 		setupfis(&m->fis);
787b1707c5dSDavid du Colombier 		m->list = malign(sizeof *m->list, 1024);
788b1707c5dSDavid du Colombier 		m->ctab = malign(sizeof *m->ctab, 128);
789b1707c5dSDavid du Colombier 	}
790b1707c5dSDavid du Colombier 
791b1707c5dSDavid du Colombier 	if(p->sstatus & 3 && h->cap & Hsss){
792b1707c5dSDavid du Colombier 		/* device connected & staggered spin-up */
7933d56950aSDavid du Colombier 		dprint("ahci: configdrive:  spinning up ... [%lux]\n",
7943d56950aSDavid du Colombier 			p->sstatus);
795b1707c5dSDavid du Colombier 		p->cmd |= Apod|Asud;
796b1707c5dSDavid du Colombier 		asleep(1400);
797b1707c5dSDavid du Colombier 	}
798b1707c5dSDavid du Colombier 
799b1707c5dSDavid du Colombier 	p->serror = SerrAll;
800b1707c5dSDavid du Colombier 
801b1707c5dSDavid du Colombier 	p->list = PCIWADDR(m->list);
802b1707c5dSDavid du Colombier 	p->listhi = 0;
803b1707c5dSDavid du Colombier 	p->fis = PCIWADDR(m->fis.base);
804b1707c5dSDavid du Colombier 	p->fishi = 0;
805b1707c5dSDavid du Colombier 	p->cmd |= Afre|Ast;
806b1707c5dSDavid du Colombier 
807b1707c5dSDavid du Colombier 	if((p->sstatus & 0xF0F) == 0x601) /* drive coming up in slumbering? */
808b1707c5dSDavid du Colombier 		ahciwakeup(p);
809b1707c5dSDavid du Colombier 
810b1707c5dSDavid du Colombier 	/* disable power managment sequence from book. */
81191157df7SDavid du Colombier 	p->sctl = (3*Aipm) | (mode*Aspd) | (0*Adet);
812b1707c5dSDavid du Colombier 	p->cmd &= ~Aalpe;
813b1707c5dSDavid du Colombier 
814b1707c5dSDavid du Colombier 	p->ie = IEM;
815b1707c5dSDavid du Colombier 
816b1707c5dSDavid du Colombier 	return 0;
817b1707c5dSDavid du Colombier }
818b1707c5dSDavid du Colombier 
819b1707c5dSDavid du Colombier static int
820b1707c5dSDavid du Colombier ahcienable(Ahba *h)
821b1707c5dSDavid du Colombier {
822b1707c5dSDavid du Colombier 	h->ghc |= Hie;
823b1707c5dSDavid du Colombier 	return 0;
824b1707c5dSDavid du Colombier }
825b1707c5dSDavid du Colombier 
826b1707c5dSDavid du Colombier static int
827b1707c5dSDavid du Colombier ahcidisable(Ahba *h)
828b1707c5dSDavid du Colombier {
829b1707c5dSDavid du Colombier 	h->ghc &= ~Hie;
830b1707c5dSDavid du Colombier 	return 0;
831b1707c5dSDavid du Colombier }
832b1707c5dSDavid du Colombier 
833b1707c5dSDavid du Colombier static int
834b1707c5dSDavid du Colombier countbits(ulong u)
835b1707c5dSDavid du Colombier {
836b1707c5dSDavid du Colombier 	int i, n;
837b1707c5dSDavid du Colombier 
838b1707c5dSDavid du Colombier 	n = 0;
839b1707c5dSDavid du Colombier 	for(i = 0; i < 32; i++)
840b1707c5dSDavid du Colombier 		if(u & (1<<i))
841b1707c5dSDavid du Colombier 			n++;
842b1707c5dSDavid du Colombier 	return n;
843b1707c5dSDavid du Colombier }
844b1707c5dSDavid du Colombier 
845b1707c5dSDavid du Colombier static int
846b1707c5dSDavid du Colombier ahciconf(Ctlr *ctlr)
847b1707c5dSDavid du Colombier {
848b1707c5dSDavid du Colombier 	Ahba *h;
84991b330d9SDavid du Colombier 	ulong u;
850b1707c5dSDavid du Colombier 
851b1707c5dSDavid du Colombier 	h = ctlr->hba = (Ahba*)ctlr->mmio;
852b1707c5dSDavid du Colombier 	u = h->cap;
853b1707c5dSDavid du Colombier 
854b1707c5dSDavid du Colombier 	if((u&Hsam) == 0)
855b1707c5dSDavid du Colombier 		h->ghc |= Hae;
856b1707c5dSDavid du Colombier 
857014ad43fSDavid du Colombier 	print("#S/sd%c: ahci %s port %#p: sss %ld ncs %ld coal %ld "
858014ad43fSDavid du Colombier 		"mports %ld led %ld clo %ld ems %ld\n",
859014ad43fSDavid du Colombier 		ctlr->sdev->idno, tname[ctlr->type], h,
860b1707c5dSDavid du Colombier 		(u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1, u & 0x1f, (u>>25) & 1,
861b1707c5dSDavid du Colombier 		(u>>24) & 1, (u>>6) & 1);
862b1707c5dSDavid du Colombier 	return countbits(h->pi);
863b1707c5dSDavid du Colombier }
864b1707c5dSDavid du Colombier 
865b1707c5dSDavid du Colombier static int
866b1707c5dSDavid du Colombier ahcihbareset(Ahba *h)
867b1707c5dSDavid du Colombier {
868b1707c5dSDavid du Colombier 	int wait;
869b1707c5dSDavid du Colombier 
870b1707c5dSDavid du Colombier 	h->ghc |= 1;
871b1707c5dSDavid du Colombier 	for(wait = 0; wait < 1000; wait += 100){
872b1707c5dSDavid du Colombier 		if(h->ghc == 0)
873b1707c5dSDavid du Colombier 			return 0;
874b1707c5dSDavid du Colombier 		delay(100);
875b1707c5dSDavid du Colombier 	}
876b1707c5dSDavid du Colombier 	return -1;
877b1707c5dSDavid du Colombier }
878b1707c5dSDavid du Colombier 
879b1707c5dSDavid du Colombier static void
880b1707c5dSDavid du Colombier idmove(char *p, ushort *a, int n)
881b1707c5dSDavid du Colombier {
882b1707c5dSDavid du Colombier 	int i;
883b1707c5dSDavid du Colombier 	char *op, *e;
884b1707c5dSDavid du Colombier 
885b1707c5dSDavid du Colombier 	op = p;
886b1707c5dSDavid du Colombier 	for(i = 0; i < n/2; i++){
887b1707c5dSDavid du Colombier 		*p++ = a[i] >> 8;
888b1707c5dSDavid du Colombier 		*p++ = a[i];
889b1707c5dSDavid du Colombier 	}
890b1707c5dSDavid du Colombier 	*p = 0;
891b1707c5dSDavid du Colombier 	while(p > op && *--p == ' ')
892b1707c5dSDavid du Colombier 		*p = 0;
893b1707c5dSDavid du Colombier 	e = p;
89491157df7SDavid du Colombier 	for (p = op; *p == ' '; p++)
89591157df7SDavid du Colombier 		;
896b1707c5dSDavid du Colombier 	memmove(op, p, n - (e - p));
897b1707c5dSDavid du Colombier }
898b1707c5dSDavid du Colombier 
899b1707c5dSDavid du Colombier static int
900b1707c5dSDavid du Colombier identify(Drive *d)
901b1707c5dSDavid du Colombier {
90291b330d9SDavid du Colombier 	ushort *id;
903b1707c5dSDavid du Colombier 	vlong osectors, s;
904b1707c5dSDavid du Colombier 	uchar oserial[21];
905b1707c5dSDavid du Colombier 	SDunit *u;
906b1707c5dSDavid du Colombier 
907b1707c5dSDavid du Colombier 	id = d->info;
908b1707c5dSDavid du Colombier 	s = ahciidentify(&d->portc, id);
909b1707c5dSDavid du Colombier 	if(s == -1){
910b1707c5dSDavid du Colombier 		d->state = Derror;
911b1707c5dSDavid du Colombier 		return -1;
912b1707c5dSDavid du Colombier 	}
913b1707c5dSDavid du Colombier 	osectors = d->sectors;
914b1707c5dSDavid du Colombier 	memmove(oserial, d->serial, sizeof d->serial);
915b1707c5dSDavid du Colombier 
91658db92f4SDavid du Colombier 	u = d->unit;
917b1707c5dSDavid du Colombier 	d->sectors = s;
91858db92f4SDavid du Colombier 	d->secsize = u->secsize;
91958db92f4SDavid du Colombier 	if(d->secsize == 0)
92058db92f4SDavid du Colombier 		d->secsize = 512;		/* default */
921b1707c5dSDavid du Colombier 	d->smartrs = 0;
922b1707c5dSDavid du Colombier 
923b1707c5dSDavid du Colombier 	idmove(d->serial, id+10, 20);
924b1707c5dSDavid du Colombier 	idmove(d->firmware, id+23, 8);
925b1707c5dSDavid du Colombier 	idmove(d->model, id+27, 40);
926b1707c5dSDavid du Colombier 
927b1707c5dSDavid du Colombier 	memset(u->inquiry, 0, sizeof u->inquiry);
928b1707c5dSDavid du Colombier 	u->inquiry[2] = 2;
929b1707c5dSDavid du Colombier 	u->inquiry[3] = 2;
930b1707c5dSDavid du Colombier 	u->inquiry[4] = sizeof u->inquiry - 4;
931b1707c5dSDavid du Colombier 	memmove(u->inquiry+8, d->model, 40);
932b1707c5dSDavid du Colombier 
93391157df7SDavid du Colombier 	if(osectors != s || memcmp(oserial, d->serial, sizeof oserial) != 0){
934b1707c5dSDavid du Colombier 		d->mediachange = 1;
935b1707c5dSDavid du Colombier 		u->sectors = 0;
936b1707c5dSDavid du Colombier 	}
937b1707c5dSDavid du Colombier 	return 0;
938b1707c5dSDavid du Colombier }
939b1707c5dSDavid du Colombier 
940b1707c5dSDavid du Colombier static void
941b1707c5dSDavid du Colombier clearci(Aport *p)
942b1707c5dSDavid du Colombier {
943b1707c5dSDavid du Colombier 	if(p->cmd & Ast) {
944b1707c5dSDavid du Colombier 		p->cmd &= ~Ast;
945b1707c5dSDavid du Colombier 		p->cmd |=  Ast;
946b1707c5dSDavid du Colombier 	}
947b1707c5dSDavid du Colombier }
948b1707c5dSDavid du Colombier 
949b1707c5dSDavid du Colombier static void
950b1707c5dSDavid du Colombier updatedrive(Drive *d)
951b1707c5dSDavid du Colombier {
95291b330d9SDavid du Colombier 	ulong cause, serr, s0, pr, ewake;
953b1707c5dSDavid du Colombier 	char *name;
954b1707c5dSDavid du Colombier 	Aport *p;
95591b330d9SDavid du Colombier 	static ulong last;
956b1707c5dSDavid du Colombier 
957b1707c5dSDavid du Colombier 	pr = 1;
958b1707c5dSDavid du Colombier 	ewake = 0;
959b1707c5dSDavid du Colombier 	p = d->port;
960b1707c5dSDavid du Colombier 	cause = p->isr;
961b1707c5dSDavid du Colombier 	serr = p->serror;
962b1707c5dSDavid du Colombier 	p->isr = cause;
963b1707c5dSDavid du Colombier 	name = "??";
964b1707c5dSDavid du Colombier 	if(d->unit && d->unit->name)
965b1707c5dSDavid du Colombier 		name = d->unit->name;
966b1707c5dSDavid du Colombier 
967b1707c5dSDavid du Colombier 	if(p->ci == 0){
968b1707c5dSDavid du Colombier 		d->portm.flag |= Fdone;
969b1707c5dSDavid du Colombier 		wakeup(&d->portm);
970b1707c5dSDavid du Colombier 		pr = 0;
971b1707c5dSDavid du Colombier 	}else if(cause & Adps)
972b1707c5dSDavid du Colombier 		pr = 0;
973b1707c5dSDavid du Colombier 	if(cause & Ifatal){
974b1707c5dSDavid du Colombier 		ewake = 1;
9753d56950aSDavid du Colombier 		dprint("ahci: updatedrive: fatal\n");
976b1707c5dSDavid du Colombier 	}
977b1707c5dSDavid du Colombier 	if(cause & Adhrs){
97891157df7SDavid du Colombier 		if(p->task & (1<<5|1)){
9793d56950aSDavid du Colombier 			dprint("ahci: Adhrs cause %lux serr %lux task %lux\n",
980b1707c5dSDavid du Colombier 				cause, serr, p->task);
981b1707c5dSDavid du Colombier 			d->portm.flag |= Ferror;
982b1707c5dSDavid du Colombier 			ewake = 1;
983b1707c5dSDavid du Colombier 		}
984b1707c5dSDavid du Colombier 		pr = 0;
985b1707c5dSDavid du Colombier 	}
986b1707c5dSDavid du Colombier 	if(p->task & 1 && last != cause)
9873d56950aSDavid du Colombier 		dprint("%s: err ca %lux serr %lux task %lux sstat %lux\n",
9883d56950aSDavid du Colombier 			name, cause, serr, p->task, p->sstatus);
989b1707c5dSDavid du Colombier 	if(pr)
99091b330d9SDavid du Colombier 		dprint("%s: upd %lux ta %lux\n", name, cause, p->task);
991b1707c5dSDavid du Colombier 
992b1707c5dSDavid du Colombier 	if(cause & (Aprcs|Aifs)){
993b1707c5dSDavid du Colombier 		s0 = d->state;
994b1707c5dSDavid du Colombier 		switch(p->sstatus & 7){
995b1707c5dSDavid du Colombier 		case 0:				/* no device */
996b1707c5dSDavid du Colombier 			d->state = Dmissing;
997b1707c5dSDavid du Colombier 			break;
998b1707c5dSDavid du Colombier 		case 1:				/* device but no phy comm. */
999b1707c5dSDavid du Colombier 			if((p->sstatus & 0xF00) == 0x600)
1000b1707c5dSDavid du Colombier 				d->state = Dnew; /* slumbering */
1001b1707c5dSDavid du Colombier 			else
1002b1707c5dSDavid du Colombier 				d->state = Derror;
1003b1707c5dSDavid du Colombier 			break;
1004b1707c5dSDavid du Colombier 		case 3:				/* device & phy comm. estab. */
100591157df7SDavid du Colombier 			/* power mgnt crap for surprise removal */
1006b1707c5dSDavid du Colombier 			p->ie |= Aprcs|Apcs;	/* is this required? */
1007b1707c5dSDavid du Colombier 			d->state = Dreset;
1008b1707c5dSDavid du Colombier 			break;
1009b1707c5dSDavid du Colombier 		case 4:				/* phy off-line */
1010b1707c5dSDavid du Colombier 			d->state = Doffline;
1011b1707c5dSDavid du Colombier 			break;
1012b1707c5dSDavid du Colombier 		}
10133d56950aSDavid du Colombier 		dprint("%s: %s → %s [Apcrs] %lux\n", name,
10143d56950aSDavid du Colombier 			diskstates[s0], diskstates[d->state], p->sstatus);
1015b1707c5dSDavid du Colombier 		/* print pulled message here. */
1016b1707c5dSDavid du Colombier 		if(s0 == Dready && d->state != Dready)
1017b1707c5dSDavid du Colombier 			idprint("%s: pulled\n", name);
1018b1707c5dSDavid du Colombier 		if(d->state != Dready)
1019b1707c5dSDavid du Colombier 			d->portm.flag |= Ferror;
1020b1707c5dSDavid du Colombier 		ewake = 1;
1021b1707c5dSDavid du Colombier 	}
1022b1707c5dSDavid du Colombier 	p->serror = serr;
1023b1707c5dSDavid du Colombier 	if(ewake){
1024b1707c5dSDavid du Colombier 		clearci(p);
1025b1707c5dSDavid du Colombier 		wakeup(&d->portm);
1026b1707c5dSDavid du Colombier 	}
1027b1707c5dSDavid du Colombier 	last = cause;
1028b1707c5dSDavid du Colombier }
1029b1707c5dSDavid du Colombier 
1030b1707c5dSDavid du Colombier static void
1031b1707c5dSDavid du Colombier pstatus(Drive *d, ulong s)
1032b1707c5dSDavid du Colombier {
1033b1707c5dSDavid du Colombier 	/*
1034b1707c5dSDavid du Colombier 	 * bogus code because the first interrupt is currently dropped.
103591157df7SDavid du Colombier 	 * likely my fault.  serror may be cleared at the wrong time.
1036b1707c5dSDavid du Colombier 	 */
1037b1707c5dSDavid du Colombier 	switch(s){
1038b1707c5dSDavid du Colombier 	case 0:			/* no device */
1039b1707c5dSDavid du Colombier 		d->state = Dmissing;
1040b1707c5dSDavid du Colombier 		break;
1041b1707c5dSDavid du Colombier 	case 1:			/* device but no phy. comm. */
1042b1707c5dSDavid du Colombier 		break;
1043b1707c5dSDavid du Colombier 	case 2:			/* should this be missing?  need testcase. */
10443d56950aSDavid du Colombier 		dprint("ahci: pstatus 2\n");
1045b1707c5dSDavid du Colombier 		/* fallthrough */
1046b1707c5dSDavid du Colombier 	case 3:			/* device & phy. comm. */
1047b1707c5dSDavid du Colombier 		d->wait = 0;
1048b1707c5dSDavid du Colombier 		d->state = Dnew;
1049b1707c5dSDavid du Colombier 		break;
1050b1707c5dSDavid du Colombier 	case 4:			/* offline */
1051b1707c5dSDavid du Colombier 		d->state = Doffline;
1052b1707c5dSDavid du Colombier 		break;
1053b1707c5dSDavid du Colombier 	case 6:			/* ? not sure this makes sense. TODO */
1054b1707c5dSDavid du Colombier 		d->state = Dnew;
1055b1707c5dSDavid du Colombier 		break;
1056b1707c5dSDavid du Colombier 	}
1057b1707c5dSDavid du Colombier }
1058b1707c5dSDavid du Colombier 
1059b1707c5dSDavid du Colombier static int
1060b1707c5dSDavid du Colombier configdrive(Drive *d)
1061b1707c5dSDavid du Colombier {
1062b1707c5dSDavid du Colombier 	if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1)
1063b1707c5dSDavid du Colombier 		return -1;
1064b1707c5dSDavid du Colombier 	ilock(d);
1065b1707c5dSDavid du Colombier 	pstatus(d, d->port->sstatus & 7);
1066b1707c5dSDavid du Colombier 	iunlock(d);
1067b1707c5dSDavid du Colombier 	return 0;
1068b1707c5dSDavid du Colombier }
1069b1707c5dSDavid du Colombier 
1070b1707c5dSDavid du Colombier static void
1071b1707c5dSDavid du Colombier resetdisk(Drive *d)
1072b1707c5dSDavid du Colombier {
1073b1707c5dSDavid du Colombier 	uint state, det, stat;
1074b1707c5dSDavid du Colombier 	Aport *p;
1075b1707c5dSDavid du Colombier 
1076b1707c5dSDavid du Colombier 	p = d->port;
1077b1707c5dSDavid du Colombier 	det = p->sctl & 7;
1078b1707c5dSDavid du Colombier 	stat = p->sstatus & 7;
1079b1707c5dSDavid du Colombier 	state = (p->cmd>>28) & 0xf;
10803d56950aSDavid du Colombier 	dprint("ahci: resetdisk: icc %ux  det %d sdet %d\n", state, det, stat);
1081014ad43fSDavid du Colombier 
1082b1707c5dSDavid du Colombier 	ilock(d);
1083b1707c5dSDavid du Colombier 	state = d->state;
1084b1707c5dSDavid du Colombier 	if(d->state != Dready || d->state != Dnew)
1085b1707c5dSDavid du Colombier 		d->portm.flag |= Ferror;
1086b1707c5dSDavid du Colombier 	clearci(p);			/* satisfy sleep condition. */
1087b1707c5dSDavid du Colombier 	wakeup(&d->portm);
1088014ad43fSDavid du Colombier 	if(stat != 3){		/* device absent or phy not communicating? */
1089014ad43fSDavid du Colombier 		d->state = Dportreset;
1090014ad43fSDavid du Colombier 		iunlock(d);
1091014ad43fSDavid du Colombier 		return;
1092014ad43fSDavid du Colombier 	}
109381ede731SDavid du Colombier 	d->state = Derror;
1094b1707c5dSDavid du Colombier 	iunlock(d);
1095b1707c5dSDavid du Colombier 
1096b1707c5dSDavid du Colombier 	qlock(&d->portm);
1097b1707c5dSDavid du Colombier 	if(p->cmd&Ast && ahciswreset(&d->portc) == -1){
1098b1707c5dSDavid du Colombier 		ilock(d);
1099b1707c5dSDavid du Colombier 		d->state = Dportreset;	/* get a bigger stick. */
1100b1707c5dSDavid du Colombier 		iunlock(d);
1101b1707c5dSDavid du Colombier 	} else {
1102b1707c5dSDavid du Colombier 		ilock(d);
1103b1707c5dSDavid du Colombier 		d->state = Dmissing;
1104b1707c5dSDavid du Colombier 		iunlock(d);
1105b1707c5dSDavid du Colombier 
1106b1707c5dSDavid du Colombier 		configdrive(d);
1107b1707c5dSDavid du Colombier 	}
11083d56950aSDavid du Colombier 	dprint("ahci: resetdisk: %s → %s\n",
11093d56950aSDavid du Colombier 		diskstates[state], diskstates[d->state]);
1110b1707c5dSDavid du Colombier 	qunlock(&d->portm);
1111b1707c5dSDavid du Colombier }
1112b1707c5dSDavid du Colombier 
1113b1707c5dSDavid du Colombier static int
1114b1707c5dSDavid du Colombier newdrive(Drive *d)
1115b1707c5dSDavid du Colombier {
111658db92f4SDavid du Colombier 	char *name;
1117b1707c5dSDavid du Colombier 	Aportc *c;
1118b1707c5dSDavid du Colombier 	Aportm *m;
1119b1707c5dSDavid du Colombier 
1120b1707c5dSDavid du Colombier 	c = &d->portc;
1121b1707c5dSDavid du Colombier 	m = &d->portm;
1122b1707c5dSDavid du Colombier 
1123b1707c5dSDavid du Colombier 	name = d->unit->name;
1124b1707c5dSDavid du Colombier 	if(name == 0)
1125b1707c5dSDavid du Colombier 		name = "??";
1126b1707c5dSDavid du Colombier 
1127b1707c5dSDavid du Colombier 	if(d->port->task == 0x80)
1128b1707c5dSDavid du Colombier 		return -1;
1129b1707c5dSDavid du Colombier 	qlock(c->m);
1130b1707c5dSDavid du Colombier 	if(setudmamode(c, 5) == -1){
1131b1707c5dSDavid du Colombier 		dprint("%s: can't set udma mode\n", name);
1132b1707c5dSDavid du Colombier 		goto lose;
1133b1707c5dSDavid du Colombier 	}
1134b1707c5dSDavid du Colombier 	if(identify(d) == -1){
1135b1707c5dSDavid du Colombier 		dprint("%s: identify failure\n", name);
1136b1707c5dSDavid du Colombier 		goto lose;
1137b1707c5dSDavid du Colombier 	}
1138b1707c5dSDavid du Colombier 	if(m->feat & Dpower && setfeatures(c, 0x85) == -1){
1139b1707c5dSDavid du Colombier 		m->feat &= ~Dpower;
1140b1707c5dSDavid du Colombier 		if(ahcirecover(c) == -1)
1141b1707c5dSDavid du Colombier 			goto lose;
1142b1707c5dSDavid du Colombier 	}
1143b1707c5dSDavid du Colombier 
1144b1707c5dSDavid du Colombier 	ilock(d);
1145b1707c5dSDavid du Colombier 	d->state = Dready;
1146b1707c5dSDavid du Colombier 	iunlock(d);
1147b1707c5dSDavid du Colombier 
1148b1707c5dSDavid du Colombier 	qunlock(c->m);
1149b1707c5dSDavid du Colombier 
115058db92f4SDavid du Colombier 	idprint("%s: %sLBA %,llud sectors: %s %s %s %s\n", d->unit->name,
115158db92f4SDavid du Colombier 		(m->feat & Dllba? "L": ""), d->sectors, d->model, d->firmware,
115258db92f4SDavid du Colombier 		d->serial, d->mediachange? "[mediachange]": "");
1153b1707c5dSDavid du Colombier 	return 0;
1154b1707c5dSDavid du Colombier 
1155b1707c5dSDavid du Colombier lose:
115681ede731SDavid du Colombier 	idprint("%s: can't be initialized\n", d->unit->name);
115781ede731SDavid du Colombier 	ilock(d);
115881ede731SDavid du Colombier 	d->state = Dnull;
115981ede731SDavid du Colombier 	iunlock(d);
1160b1707c5dSDavid du Colombier 	qunlock(c->m);
1161b1707c5dSDavid du Colombier 	return -1;
1162b1707c5dSDavid du Colombier }
1163b1707c5dSDavid du Colombier 
1164b1707c5dSDavid du Colombier enum {
1165b1707c5dSDavid du Colombier 	Nms		= 256,
1166b1707c5dSDavid du Colombier 	Mphywait	=  2*1024/Nms - 1,
1167b1707c5dSDavid du Colombier 	Midwait		= 16*1024/Nms - 1,
1168b1707c5dSDavid du Colombier 	Mcomrwait	= 64*1024/Nms - 1,
1169b1707c5dSDavid du Colombier };
1170b1707c5dSDavid du Colombier 
1171b1707c5dSDavid du Colombier static void
1172b1707c5dSDavid du Colombier westerndigitalhung(Drive *d)
1173b1707c5dSDavid du Colombier {
1174f3112f79SDavid du Colombier 	if((d->portm.feat&Datapi) == 0 && d->active &&
1175f3112f79SDavid du Colombier 	    TK2MS(MACHP(0)->ticks - d->intick) > 5000){
1176014ad43fSDavid du Colombier 		dprint("%s: drive hung; resetting [%lux] ci %lx\n",
1177b1707c5dSDavid du Colombier 			d->unit->name, d->port->task, d->port->ci);
1178b1707c5dSDavid du Colombier 		d->state = Dreset;
1179b1707c5dSDavid du Colombier 	}
1180b1707c5dSDavid du Colombier }
1181b1707c5dSDavid du Colombier 
1182b1707c5dSDavid du Colombier static ushort olds[NCtlr*NCtlrdrv];
1183b1707c5dSDavid du Colombier 
1184b1707c5dSDavid du Colombier static int
1185b1707c5dSDavid du Colombier doportreset(Drive *d)
1186b1707c5dSDavid du Colombier {
1187b1707c5dSDavid du Colombier 	int i;
1188b1707c5dSDavid du Colombier 
1189b1707c5dSDavid du Colombier 	i = -1;
1190b1707c5dSDavid du Colombier 	qlock(&d->portm);
1191b1707c5dSDavid du Colombier 	if(ahciportreset(&d->portc) == -1)
11923d56950aSDavid du Colombier 		dprint("ahci: doportreset: fails\n");
1193b1707c5dSDavid du Colombier 	else
1194b1707c5dSDavid du Colombier 		i = 0;
1195b1707c5dSDavid du Colombier 	qunlock(&d->portm);
11963d56950aSDavid du Colombier 	dprint("ahci: doportreset: portreset → %s  [task %lux]\n",
1197b1707c5dSDavid du Colombier 		diskstates[d->state], d->port->task);
1198b1707c5dSDavid du Colombier 	return i;
1199b1707c5dSDavid du Colombier }
1200b1707c5dSDavid du Colombier 
1201014ad43fSDavid du Colombier /* drive must be locked */
1202014ad43fSDavid du Colombier static void
1203014ad43fSDavid du Colombier statechange(Drive *d)
1204014ad43fSDavid du Colombier {
1205014ad43fSDavid du Colombier 	switch(d->state){
1206014ad43fSDavid du Colombier 	case Dnull:
1207014ad43fSDavid du Colombier 	case Doffline:
1208014ad43fSDavid du Colombier 		if(d->unit->sectors != 0){
1209014ad43fSDavid du Colombier 			d->sectors = 0;
1210014ad43fSDavid du Colombier 			d->mediachange = 1;
1211014ad43fSDavid du Colombier 		}
1212014ad43fSDavid du Colombier 		/* fallthrough */
1213014ad43fSDavid du Colombier 	case Dready:
1214014ad43fSDavid du Colombier 		d->wait = 0;
1215014ad43fSDavid du Colombier 		break;
1216014ad43fSDavid du Colombier 	}
1217014ad43fSDavid du Colombier }
1218014ad43fSDavid du Colombier 
1219b1707c5dSDavid du Colombier static void
1220b1707c5dSDavid du Colombier checkdrive(Drive *d, int i)
1221b1707c5dSDavid du Colombier {
1222b1707c5dSDavid du Colombier 	ushort s;
1223b1707c5dSDavid du Colombier 	char *name;
1224b1707c5dSDavid du Colombier 
1225*c39c2eb3SDavid du Colombier 	if(d == nil) {
1226*c39c2eb3SDavid du Colombier 		print("checkdrive: nil d\n");
1227*c39c2eb3SDavid du Colombier 		return;
1228*c39c2eb3SDavid du Colombier 	}
1229b1707c5dSDavid du Colombier 	ilock(d);
1230*c39c2eb3SDavid du Colombier 	if(d->unit == nil || d->port == nil) {
1231*c39c2eb3SDavid du Colombier 		if(0)
1232*c39c2eb3SDavid du Colombier 			print("checkdrive: nil d->%s\n",
1233*c39c2eb3SDavid du Colombier 				d->unit == nil? "unit": "port");
1234*c39c2eb3SDavid du Colombier 		iunlock(d);
1235*c39c2eb3SDavid du Colombier 		return;
1236*c39c2eb3SDavid du Colombier 	}
1237b1707c5dSDavid du Colombier 	name = d->unit->name;
1238b1707c5dSDavid du Colombier 	s = d->port->sstatus;
123981ede731SDavid du Colombier 	if(s)
124081ede731SDavid du Colombier 		d->lastseen = MACHP(0)->ticks;
1241b1707c5dSDavid du Colombier 	if(s != olds[i]){
1242b1707c5dSDavid du Colombier 		dprint("%s: status: %04ux -> %04ux: %s\n",
1243b1707c5dSDavid du Colombier 			name, olds[i], s, diskstates[d->state]);
1244b1707c5dSDavid du Colombier 		olds[i] = s;
1245b1707c5dSDavid du Colombier 		d->wait = 0;
1246b1707c5dSDavid du Colombier 	}
1247b1707c5dSDavid du Colombier 	westerndigitalhung(d);
1248b1707c5dSDavid du Colombier 	switch(d->state){
1249b1707c5dSDavid du Colombier 	case Dnull:
1250b1707c5dSDavid du Colombier 	case Dready:
1251b1707c5dSDavid du Colombier 		break;
1252b1707c5dSDavid du Colombier 	case Dmissing:
1253b1707c5dSDavid du Colombier 	case Dnew:
1254b1707c5dSDavid du Colombier 		switch(s & 0x107){
1255b1707c5dSDavid du Colombier 		case 1:		/* no device (pm), device but no phy. comm. */
1256b1707c5dSDavid du Colombier 			ahciwakeup(d->port);
1257b1707c5dSDavid du Colombier 			/* fall through */
1258b1707c5dSDavid du Colombier 		case 0:		/* no device */
1259b1707c5dSDavid du Colombier 			break;
1260b1707c5dSDavid du Colombier 		default:
1261b1707c5dSDavid du Colombier 			dprint("%s: unknown status %04ux\n", name, s);
1262b1707c5dSDavid du Colombier 			/* fall through */
1263b1707c5dSDavid du Colombier 		case 0x100:		/* active, no device */
1264b1707c5dSDavid du Colombier 			if(++d->wait&Mphywait)
1265b1707c5dSDavid du Colombier 				break;
1266b1707c5dSDavid du Colombier reset:
1267b1707c5dSDavid du Colombier 			if(++d->mode > DMsataii)
1268b1707c5dSDavid du Colombier 				d->mode = 0;
1269b1707c5dSDavid du Colombier 			if(d->mode == DMsatai){	/* we tried everything */
1270b1707c5dSDavid du Colombier 				d->state = Dportreset;
1271b1707c5dSDavid du Colombier 				goto portreset;
1272b1707c5dSDavid du Colombier 			}
1273b1707c5dSDavid du Colombier 			dprint("%s: reset; new mode %s\n", name,
1274b1707c5dSDavid du Colombier 				modename[d->mode]);
1275b1707c5dSDavid du Colombier 			iunlock(d);
1276b1707c5dSDavid du Colombier 			resetdisk(d);
1277b1707c5dSDavid du Colombier 			ilock(d);
1278b1707c5dSDavid du Colombier 			break;
1279b1707c5dSDavid du Colombier 		case 0x103:		/* active, device, phy. comm. */
1280b1707c5dSDavid du Colombier 			if((++d->wait&Midwait) == 0){
128191b330d9SDavid du Colombier 				dprint("%s: slow reset %04ux task=%lux; %d\n",
1282b1707c5dSDavid du Colombier 					name, s, d->port->task, d->wait);
1283b1707c5dSDavid du Colombier 				goto reset;
1284b1707c5dSDavid du Colombier 			}
1285b1707c5dSDavid du Colombier 			s = (uchar)d->port->task;
1286b1707c5dSDavid du Colombier 			if(s == 0x7f || ((d->port->sig >> 16) != 0xeb14 &&
1287b1707c5dSDavid du Colombier 			    (s & ~0x17) != (1<<6)))
1288b1707c5dSDavid du Colombier 				break;
1289b1707c5dSDavid du Colombier 			iunlock(d);
1290b1707c5dSDavid du Colombier 			newdrive(d);
1291b1707c5dSDavid du Colombier 			ilock(d);
1292b1707c5dSDavid du Colombier 			break;
1293b1707c5dSDavid du Colombier 		}
1294b1707c5dSDavid du Colombier 		break;
1295b1707c5dSDavid du Colombier 	case Doffline:
1296b1707c5dSDavid du Colombier 		if(d->wait++ & Mcomrwait)
1297b1707c5dSDavid du Colombier 			break;
1298b1707c5dSDavid du Colombier 		/* fallthrough */
1299b1707c5dSDavid du Colombier 	case Derror:
1300b1707c5dSDavid du Colombier 	case Dreset:
1301b1707c5dSDavid du Colombier 		dprint("%s: reset [%s]: mode %d; status %04ux\n",
1302b1707c5dSDavid du Colombier 			name, diskstates[d->state], d->mode, s);
1303b1707c5dSDavid du Colombier 		iunlock(d);
1304b1707c5dSDavid du Colombier 		resetdisk(d);
1305b1707c5dSDavid du Colombier 		ilock(d);
1306b1707c5dSDavid du Colombier 		break;
1307b1707c5dSDavid du Colombier 	case Dportreset:
1308b1707c5dSDavid du Colombier portreset:
1309b1707c5dSDavid du Colombier 		if(d->wait++ & 0xff && (s & 0x100) == 0)
1310b1707c5dSDavid du Colombier 			break;
1311b1707c5dSDavid du Colombier 		/* device is active */
1312b1707c5dSDavid du Colombier 		dprint("%s: portreset [%s]: mode %d; status %04ux\n",
1313b1707c5dSDavid du Colombier 			name, diskstates[d->state], d->mode, s);
1314b1707c5dSDavid du Colombier 		d->portm.flag |= Ferror;
1315b1707c5dSDavid du Colombier 		clearci(d->port);
1316b1707c5dSDavid du Colombier 		wakeup(&d->portm);
1317b1707c5dSDavid du Colombier 		if((s & 7) == 0){	/* no device */
1318b1707c5dSDavid du Colombier 			d->state = Dmissing;
1319b1707c5dSDavid du Colombier 			break;
1320b1707c5dSDavid du Colombier 		}
1321b1707c5dSDavid du Colombier 		iunlock(d);
1322b1707c5dSDavid du Colombier 		doportreset(d);
1323b1707c5dSDavid du Colombier 		ilock(d);
1324b1707c5dSDavid du Colombier 		break;
1325b1707c5dSDavid du Colombier 	}
1326014ad43fSDavid du Colombier 	statechange(d);
1327b1707c5dSDavid du Colombier 	iunlock(d);
1328b1707c5dSDavid du Colombier }
1329b1707c5dSDavid du Colombier 
1330b1707c5dSDavid du Colombier static void
1331b1707c5dSDavid du Colombier satakproc(void*)
1332b1707c5dSDavid du Colombier {
1333b1707c5dSDavid du Colombier 	int i;
1334b1707c5dSDavid du Colombier 
1335b1707c5dSDavid du Colombier 	for(;;){
1336b1707c5dSDavid du Colombier 		tsleep(&up->sleep, return0, 0, Nms);
1337b1707c5dSDavid du Colombier 		for(i = 0; i < niadrive; i++)
1338b1707c5dSDavid du Colombier 			checkdrive(iadrive[i], i);
1339b1707c5dSDavid du Colombier 	}
1340b1707c5dSDavid du Colombier }
1341b1707c5dSDavid du Colombier 
1342b1707c5dSDavid du Colombier static void
1343b1707c5dSDavid du Colombier iainterrupt(Ureg*, void *a)
1344b1707c5dSDavid du Colombier {
1345b1707c5dSDavid du Colombier 	int i;
1346b1707c5dSDavid du Colombier 	ulong cause, m;
1347b1707c5dSDavid du Colombier 	Ctlr *c;
1348b1707c5dSDavid du Colombier 	Drive *d;
1349b1707c5dSDavid du Colombier 
1350b1707c5dSDavid du Colombier 	c = a;
1351b1707c5dSDavid du Colombier 	ilock(c);
1352b1707c5dSDavid du Colombier 	cause = c->hba->isr;
1353014ad43fSDavid du Colombier 	for(i = 0; i < c->mport; i++){
1354b1707c5dSDavid du Colombier 		m = 1 << i;
1355b1707c5dSDavid du Colombier 		if((cause & m) == 0)
1356b1707c5dSDavid du Colombier 			continue;
1357b1707c5dSDavid du Colombier 		d = c->rawdrive + i;
1358b1707c5dSDavid du Colombier 		ilock(d);
1359b1707c5dSDavid du Colombier 		if(d->port->isr && c->hba->pi & m)
1360b1707c5dSDavid du Colombier 			updatedrive(d);
1361b1707c5dSDavid du Colombier 		c->hba->isr = m;
1362b1707c5dSDavid du Colombier 		iunlock(d);
1363b1707c5dSDavid du Colombier 	}
1364b1707c5dSDavid du Colombier 	iunlock(c);
1365b1707c5dSDavid du Colombier }
1366b1707c5dSDavid du Colombier 
1367b1707c5dSDavid du Colombier static int
1368b1707c5dSDavid du Colombier iaverify(SDunit *u)
1369b1707c5dSDavid du Colombier {
1370b1707c5dSDavid du Colombier 	Ctlr *c;
1371b1707c5dSDavid du Colombier 	Drive *d;
1372b1707c5dSDavid du Colombier 
1373b1707c5dSDavid du Colombier 	c = u->dev->ctlr;
1374b1707c5dSDavid du Colombier 	d = c->drive[u->subno];
1375b1707c5dSDavid du Colombier 	ilock(c);
1376b1707c5dSDavid du Colombier 	ilock(d);
1377b1707c5dSDavid du Colombier 	d->unit = u;
1378b1707c5dSDavid du Colombier 	iunlock(d);
1379b1707c5dSDavid du Colombier 	iunlock(c);
1380014ad43fSDavid du Colombier 	checkdrive(d, d->driveno);		/* c->d0 + d->driveno */
1381b1707c5dSDavid du Colombier 	return 1;
1382b1707c5dSDavid du Colombier }
1383b1707c5dSDavid du Colombier 
1384b1707c5dSDavid du Colombier static int
1385b1707c5dSDavid du Colombier iaenable(SDev *s)
1386b1707c5dSDavid du Colombier {
1387b1707c5dSDavid du Colombier 	char name[32];
1388b1707c5dSDavid du Colombier 	Ctlr *c;
1389b1707c5dSDavid du Colombier 	static int once;
1390b1707c5dSDavid du Colombier 
1391b1707c5dSDavid du Colombier 	c = s->ctlr;
1392b1707c5dSDavid du Colombier 	ilock(c);
1393b1707c5dSDavid du Colombier 	if(!c->enabled) {
1394b1707c5dSDavid du Colombier 		if(once == 0) {
1395b1707c5dSDavid du Colombier 			once = 1;
1396b1707c5dSDavid du Colombier 			kproc("iasata", satakproc, 0);
1397b1707c5dSDavid du Colombier 		}
1398b1707c5dSDavid du Colombier 		if(c->ndrive == 0)
1399b1707c5dSDavid du Colombier 			panic("iaenable: zero s->ctlr->ndrive");
1400b1707c5dSDavid du Colombier 		pcisetbme(c->pci);
1401b1707c5dSDavid du Colombier 		snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1402b1707c5dSDavid du Colombier 		intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1403b1707c5dSDavid du Colombier 		/* supposed to squelch leftover interrupts here. */
1404b1707c5dSDavid du Colombier 		ahcienable(c->hba);
1405b1707c5dSDavid du Colombier 		c->enabled = 1;
1406b1707c5dSDavid du Colombier 	}
1407b1707c5dSDavid du Colombier 	iunlock(c);
1408b1707c5dSDavid du Colombier 	return 1;
1409b1707c5dSDavid du Colombier }
1410b1707c5dSDavid du Colombier 
1411b1707c5dSDavid du Colombier static int
1412b1707c5dSDavid du Colombier iadisable(SDev *s)
1413b1707c5dSDavid du Colombier {
1414b1707c5dSDavid du Colombier 	char name[32];
1415b1707c5dSDavid du Colombier 	Ctlr *c;
1416b1707c5dSDavid du Colombier 
1417b1707c5dSDavid du Colombier 	c = s->ctlr;
1418b1707c5dSDavid du Colombier 	ilock(c);
1419b1707c5dSDavid du Colombier 	ahcidisable(c->hba);
1420b1707c5dSDavid du Colombier 	snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1421b1707c5dSDavid du Colombier 	intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1422b1707c5dSDavid du Colombier 	c->enabled = 0;
1423b1707c5dSDavid du Colombier 	iunlock(c);
1424b1707c5dSDavid du Colombier 	return 1;
1425b1707c5dSDavid du Colombier }
1426b1707c5dSDavid du Colombier 
1427b1707c5dSDavid du Colombier static int
1428b1707c5dSDavid du Colombier iaonline(SDunit *unit)
1429b1707c5dSDavid du Colombier {
1430b1707c5dSDavid du Colombier 	int r;
1431b1707c5dSDavid du Colombier 	Ctlr *c;
1432b1707c5dSDavid du Colombier 	Drive *d;
1433b1707c5dSDavid du Colombier 
1434b1707c5dSDavid du Colombier 	c = unit->dev->ctlr;
1435b1707c5dSDavid du Colombier 	d = c->drive[unit->subno];
1436b1707c5dSDavid du Colombier 	r = 0;
1437b1707c5dSDavid du Colombier 
1438b1707c5dSDavid du Colombier 	if(d->portm.feat & Datapi && d->mediachange){
1439b1707c5dSDavid du Colombier 		r = scsionline(unit);
1440b1707c5dSDavid du Colombier 		if(r > 0)
1441b1707c5dSDavid du Colombier 			d->mediachange = 0;
1442b1707c5dSDavid du Colombier 		return r;
1443b1707c5dSDavid du Colombier 	}
1444b1707c5dSDavid du Colombier 
1445b1707c5dSDavid du Colombier 	ilock(d);
1446b1707c5dSDavid du Colombier 	if(d->mediachange){
1447b1707c5dSDavid du Colombier 		r = 2;
1448b1707c5dSDavid du Colombier 		d->mediachange = 0;
1449b1707c5dSDavid du Colombier 		/* devsd resets this after online is called; why? */
1450b1707c5dSDavid du Colombier 		unit->sectors = d->sectors;
145158db92f4SDavid du Colombier 		unit->secsize = 512;		/* default size */
1452b1707c5dSDavid du Colombier 	} else if(d->state == Dready)
1453b1707c5dSDavid du Colombier 		r = 1;
1454b1707c5dSDavid du Colombier 	iunlock(d);
1455b1707c5dSDavid du Colombier 	return r;
1456b1707c5dSDavid du Colombier }
1457b1707c5dSDavid du Colombier 
1458b1707c5dSDavid du Colombier /* returns locked list! */
1459b1707c5dSDavid du Colombier static Alist*
146058db92f4SDavid du Colombier ahcibuild(Drive *d, uchar *cmd, void *data, int n, vlong lba)
1461b1707c5dSDavid du Colombier {
1462b1707c5dSDavid du Colombier 	uchar *c, acmd, dir, llba;
1463b1707c5dSDavid du Colombier 	Alist *l;
1464b1707c5dSDavid du Colombier 	Actab *t;
146558db92f4SDavid du Colombier 	Aportm *m;
1466b1707c5dSDavid du Colombier 	Aprdt *p;
1467b1707c5dSDavid du Colombier 	static uchar tab[2][2] = { 0xc8, 0x25, 0xca, 0x35, };
1468b1707c5dSDavid du Colombier 
146958db92f4SDavid du Colombier 	m = &d->portm;
1470b1707c5dSDavid du Colombier 	dir = *cmd != 0x28;
1471b1707c5dSDavid du Colombier 	llba = m->feat&Dllba? 1: 0;
1472b1707c5dSDavid du Colombier 	acmd = tab[dir][llba];
1473b1707c5dSDavid du Colombier 	qlock(m);
1474b1707c5dSDavid du Colombier 	l = m->list;
1475b1707c5dSDavid du Colombier 	t = m->ctab;
1476b1707c5dSDavid du Colombier 	c = t->cfis;
1477b1707c5dSDavid du Colombier 
1478b1707c5dSDavid du Colombier 	c[0] = 0x27;
1479b1707c5dSDavid du Colombier 	c[1] = 0x80;
1480b1707c5dSDavid du Colombier 	c[2] = acmd;
1481b1707c5dSDavid du Colombier 	c[3] = 0;
1482b1707c5dSDavid du Colombier 
1483b1707c5dSDavid du Colombier 	c[4] = lba;		/* sector		lba low	7:0 */
1484b1707c5dSDavid du Colombier 	c[5] = lba >> 8;	/* cylinder low		lba mid	15:8 */
1485b1707c5dSDavid du Colombier 	c[6] = lba >> 16;	/* cylinder hi		lba hi	23:16 */
1486b1707c5dSDavid du Colombier 	c[7] = 0xa0 | 0x40;	/* obsolete device bits + lba */
1487b1707c5dSDavid du Colombier 	if(llba == 0)
1488b1707c5dSDavid du Colombier 		c[7] |= (lba>>24) & 7;
1489b1707c5dSDavid du Colombier 
1490b1707c5dSDavid du Colombier 	c[8] = lba >> 24;	/* sector (exp)		lba 	31:24 */
1491b1707c5dSDavid du Colombier 	c[9] = lba >> 32;	/* cylinder low (exp)	lba	39:32 */
1492b1707c5dSDavid du Colombier 	c[10] = lba >> 48;	/* cylinder hi (exp)	lba	48:40 */
1493b1707c5dSDavid du Colombier 	c[11] = 0;		/* features (exp); */
1494b1707c5dSDavid du Colombier 
1495b1707c5dSDavid du Colombier 	c[12] = n;		/* sector count */
1496b1707c5dSDavid du Colombier 	c[13] = n >> 8;		/* sector count (exp) */
1497b1707c5dSDavid du Colombier 	c[14] = 0;		/* r */
1498b1707c5dSDavid du Colombier 	c[15] = 0;		/* control */
1499b1707c5dSDavid du Colombier 
1500b1707c5dSDavid du Colombier 	*(ulong*)(c + 16) = 0;
1501b1707c5dSDavid du Colombier 
1502b1707c5dSDavid du Colombier 	l->flags = 1<<16 | Lpref | 0x5;	/* Lpref ?? */
1503b1707c5dSDavid du Colombier 	if(dir == Write)
1504b1707c5dSDavid du Colombier 		l->flags |= Lwrite;
1505b1707c5dSDavid du Colombier 	l->len = 0;
1506b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
1507b1707c5dSDavid du Colombier 	l->ctabhi = 0;
1508b1707c5dSDavid du Colombier 
1509b1707c5dSDavid du Colombier 	p = &t->prdt;
1510b1707c5dSDavid du Colombier 	p->dba = PCIWADDR(data);
1511b1707c5dSDavid du Colombier 	p->dbahi = 0;
151258db92f4SDavid du Colombier 	if(d->unit == nil)
151358db92f4SDavid du Colombier 		panic("ahcibuild: nil d->unit");
151458db92f4SDavid du Colombier 	p->count = 1<<31 | (d->unit->secsize*n - 2) | 1;
1515b1707c5dSDavid du Colombier 
1516b1707c5dSDavid du Colombier 	return l;
1517b1707c5dSDavid du Colombier }
1518b1707c5dSDavid du Colombier 
1519b1707c5dSDavid du Colombier static Alist*
1520b1707c5dSDavid du Colombier ahcibuildpkt(Aportm *m, SDreq *r, void *data, int n)
1521b1707c5dSDavid du Colombier {
1522b1707c5dSDavid du Colombier 	int fill, len;
1523b1707c5dSDavid du Colombier 	uchar *c;
1524b1707c5dSDavid du Colombier 	Alist *l;
1525b1707c5dSDavid du Colombier 	Actab *t;
1526b1707c5dSDavid du Colombier 	Aprdt *p;
1527b1707c5dSDavid du Colombier 
1528b1707c5dSDavid du Colombier 	qlock(m);
1529b1707c5dSDavid du Colombier 	l = m->list;
1530b1707c5dSDavid du Colombier 	t = m->ctab;
1531b1707c5dSDavid du Colombier 	c = t->cfis;
1532b1707c5dSDavid du Colombier 
1533b1707c5dSDavid du Colombier 	fill = m->feat&Datapi16? 16: 12;
1534b1707c5dSDavid du Colombier 	if((len = r->clen) > fill)
1535b1707c5dSDavid du Colombier 		len = fill;
1536b1707c5dSDavid du Colombier 	memmove(t->atapi, r->cmd, len);
1537b1707c5dSDavid du Colombier 	memset(t->atapi+len, 0, fill-len);
1538b1707c5dSDavid du Colombier 
1539b1707c5dSDavid du Colombier 	c[0] = 0x27;
1540b1707c5dSDavid du Colombier 	c[1] = 0x80;
1541b1707c5dSDavid du Colombier 	c[2] = 0xa0;
1542b1707c5dSDavid du Colombier 	if(n != 0)
1543b1707c5dSDavid du Colombier 		c[3] = 1;	/* dma */
1544b1707c5dSDavid du Colombier 	else
1545b1707c5dSDavid du Colombier 		c[3] = 0;	/* features (exp); */
1546b1707c5dSDavid du Colombier 
1547b1707c5dSDavid du Colombier 	c[4] = 0;		/* sector		lba low	7:0 */
1548b1707c5dSDavid du Colombier 	c[5] = n;		/* cylinder low		lba mid	15:8 */
1549b1707c5dSDavid du Colombier 	c[6] = n >> 8;		/* cylinder hi		lba hi	23:16 */
1550b1707c5dSDavid du Colombier 	c[7] = 0xa0;		/* obsolete device bits */
1551b1707c5dSDavid du Colombier 
1552b1707c5dSDavid du Colombier 	*(ulong*)(c + 8) = 0;
1553b1707c5dSDavid du Colombier 	*(ulong*)(c + 12) = 0;
1554b1707c5dSDavid du Colombier 	*(ulong*)(c + 16) = 0;
1555b1707c5dSDavid du Colombier 
1556b1707c5dSDavid du Colombier 	l->flags = 1<<16 | Lpref | Latapi | 0x5;
1557b1707c5dSDavid du Colombier 	if(r->write != 0 && data)
1558b1707c5dSDavid du Colombier 		l->flags |= Lwrite;
1559b1707c5dSDavid du Colombier 	l->len = 0;
1560b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
1561b1707c5dSDavid du Colombier 	l->ctabhi = 0;
1562b1707c5dSDavid du Colombier 
1563b1707c5dSDavid du Colombier 	if(data == 0)
1564b1707c5dSDavid du Colombier 		return l;
1565b1707c5dSDavid du Colombier 
1566b1707c5dSDavid du Colombier 	p = &t->prdt;
1567b1707c5dSDavid du Colombier 	p->dba = PCIWADDR(data);
1568b1707c5dSDavid du Colombier 	p->dbahi = 0;
1569b1707c5dSDavid du Colombier 	p->count = 1<<31 | (n - 2) | 1;
1570b1707c5dSDavid du Colombier 
1571b1707c5dSDavid du Colombier 	return l;
1572b1707c5dSDavid du Colombier }
1573b1707c5dSDavid du Colombier 
1574b1707c5dSDavid du Colombier static int
1575b1707c5dSDavid du Colombier waitready(Drive *d)
1576b1707c5dSDavid du Colombier {
157791b330d9SDavid du Colombier 	ulong s, i, δ;
1578b1707c5dSDavid du Colombier 
157981ede731SDavid du Colombier 	for(i = 0; i < 15000; i += 250){
158081ede731SDavid du Colombier 		if(d->state == Dreset || d->state == Dportreset ||
158181ede731SDavid du Colombier 		    d->state == Dnew)
158281ede731SDavid du Colombier 			return 1;
158381ede731SDavid du Colombier 		δ = MACHP(0)->ticks - d->lastseen;
158481ede731SDavid du Colombier 		if(d->state == Dnull || δ > 10*1000)
158581ede731SDavid du Colombier 			return -1;
1586b1707c5dSDavid du Colombier 		ilock(d);
1587b1707c5dSDavid du Colombier 		s = d->port->sstatus;
1588b1707c5dSDavid du Colombier 		iunlock(d);
158981ede731SDavid du Colombier 		if((s & 0x700) == 0 && δ > 1500)
159081ede731SDavid du Colombier 			return -1;	/* no detect */
1591b1707c5dSDavid du Colombier 		if(d->state == Dready && (s & 7) == 3)
1592b1707c5dSDavid du Colombier 			return 0;	/* ready, present & phy. comm. */
159381ede731SDavid du Colombier 		esleep(250);
1594b1707c5dSDavid du Colombier 	}
1595b1707c5dSDavid du Colombier 	print("%s: not responding; offline\n", d->unit->name);
1596b1707c5dSDavid du Colombier 	ilock(d);
1597b1707c5dSDavid du Colombier 	d->state = Doffline;
1598b1707c5dSDavid du Colombier 	iunlock(d);
1599b1707c5dSDavid du Colombier 	return -1;
1600b1707c5dSDavid du Colombier }
1601b1707c5dSDavid du Colombier 
1602b1707c5dSDavid du Colombier static int
160381ede731SDavid du Colombier lockready(Drive *d)
160481ede731SDavid du Colombier {
160581ede731SDavid du Colombier 	int i;
160681ede731SDavid du Colombier 
160781ede731SDavid du Colombier 	qlock(&d->portm);
160881ede731SDavid du Colombier 	while ((i = waitready(d)) == 1) {
160981ede731SDavid du Colombier 		qunlock(&d->portm);
161081ede731SDavid du Colombier 		esleep(1);
161181ede731SDavid du Colombier 		qlock(&d->portm);
161281ede731SDavid du Colombier 	}
161381ede731SDavid du Colombier 	return i;
161481ede731SDavid du Colombier }
161581ede731SDavid du Colombier 
161681ede731SDavid du Colombier static int
161781ede731SDavid du Colombier flushcache(Drive *d)
161881ede731SDavid du Colombier {
161981ede731SDavid du Colombier 	int i;
162081ede731SDavid du Colombier 
162181ede731SDavid du Colombier 	i = -1;
162281ede731SDavid du Colombier 	if(lockready(d) == 0)
162381ede731SDavid du Colombier 		i = ahciflushcache(&d->portc);
162481ede731SDavid du Colombier 	qunlock(&d->portm);
162581ede731SDavid du Colombier 	return i;
162681ede731SDavid du Colombier }
162781ede731SDavid du Colombier 
162881ede731SDavid du Colombier static int
1629b1707c5dSDavid du Colombier iariopkt(SDreq *r, Drive *d)
1630b1707c5dSDavid du Colombier {
1631b1707c5dSDavid du Colombier 	int n, count, try, max, flag, task;
1632b1707c5dSDavid du Colombier 	char *name;
1633b1707c5dSDavid du Colombier 	uchar *cmd, *data;
1634b1707c5dSDavid du Colombier 	Aport *p;
1635b1707c5dSDavid du Colombier 	Asleep as;
1636b1707c5dSDavid du Colombier 
1637b1707c5dSDavid du Colombier 	cmd = r->cmd;
1638b1707c5dSDavid du Colombier 	name = d->unit->name;
1639b1707c5dSDavid du Colombier 	p = d->port;
1640b1707c5dSDavid du Colombier 
16413d56950aSDavid du Colombier 	aprint("ahci: iariopkt: %02ux %02ux %c %d %p\n",
16423d56950aSDavid du Colombier 		cmd[0], cmd[2], "rw"[r->write], r->dlen, r->data);
1643b1707c5dSDavid du Colombier 	if(cmd[0] == 0x5a && (cmd[2] & 0x3f) == 0x3f)
1644b1707c5dSDavid du Colombier 		return sdmodesense(r, cmd, d->info, sizeof d->info);
1645b1707c5dSDavid du Colombier 	r->rlen = 0;
1646b1707c5dSDavid du Colombier 	count = r->dlen;
1647b1707c5dSDavid du Colombier 	max = 65536;
1648b1707c5dSDavid du Colombier 
1649b1707c5dSDavid du Colombier 	try = 0;
1650b1707c5dSDavid du Colombier retry:
1651b1707c5dSDavid du Colombier 	data = r->data;
1652b1707c5dSDavid du Colombier 	n = count;
1653b1707c5dSDavid du Colombier 	if(n > max)
1654b1707c5dSDavid du Colombier 		n = max;
1655b1707c5dSDavid du Colombier 	ahcibuildpkt(&d->portm, r, data, n);
165681ede731SDavid du Colombier 	switch(waitready(d)){
165781ede731SDavid du Colombier 	case -1:
165881ede731SDavid du Colombier 		qunlock(&d->portm);
165981ede731SDavid du Colombier 		return SDeio;
166081ede731SDavid du Colombier 	case 1:
166181ede731SDavid du Colombier 		qunlock(&d->portm);
166281ede731SDavid du Colombier 		esleep(1);
166381ede731SDavid du Colombier 		goto retry;
166481ede731SDavid du Colombier 	}
166581ede731SDavid du Colombier 
1666b1707c5dSDavid du Colombier 	ilock(d);
1667b1707c5dSDavid du Colombier 	d->portm.flag = 0;
1668b1707c5dSDavid du Colombier 	iunlock(d);
1669b1707c5dSDavid du Colombier 	p->ci = 1;
1670b1707c5dSDavid du Colombier 
1671b1707c5dSDavid du Colombier 	as.p = p;
1672b1707c5dSDavid du Colombier 	as.i = 1;
1673b1707c5dSDavid du Colombier 	d->intick = MACHP(0)->ticks;
1674014ad43fSDavid du Colombier 	d->active++;
1675b1707c5dSDavid du Colombier 
1676b1707c5dSDavid du Colombier 	while(waserror())
1677b1707c5dSDavid du Colombier 		;
1678b1707c5dSDavid du Colombier 	sleep(&d->portm, ahciclear, &as);
1679b1707c5dSDavid du Colombier 	poperror();
1680b1707c5dSDavid du Colombier 
1681014ad43fSDavid du Colombier 	d->active--;
1682b1707c5dSDavid du Colombier 	ilock(d);
1683b1707c5dSDavid du Colombier 	flag = d->portm.flag;
1684b1707c5dSDavid du Colombier 	task = d->port->task;
1685b1707c5dSDavid du Colombier 	iunlock(d);
1686b1707c5dSDavid du Colombier 
1687b1707c5dSDavid du Colombier 	if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){
1688014ad43fSDavid du Colombier 		d->port->ci = 0;
1689b1707c5dSDavid du Colombier 		ahcirecover(&d->portc);
1690b1707c5dSDavid du Colombier 		task = d->port->task;
169181ede731SDavid du Colombier 		flag &= ~Fdone;		/* either an error or do-over */
1692b1707c5dSDavid du Colombier 	}
1693b1707c5dSDavid du Colombier 	qunlock(&d->portm);
1694b1707c5dSDavid du Colombier 	if(flag == 0){
1695b1707c5dSDavid du Colombier 		if(++try == 10){
1696b1707c5dSDavid du Colombier 			print("%s: bad disk\n", name);
1697b1707c5dSDavid du Colombier 			r->status = SDcheck;
1698b1707c5dSDavid du Colombier 			return SDcheck;
1699b1707c5dSDavid du Colombier 		}
170081ede731SDavid du Colombier 		print("%s: retry\n", name);
1701b1707c5dSDavid du Colombier 		goto retry;
1702b1707c5dSDavid du Colombier 	}
1703b1707c5dSDavid du Colombier 	if(flag & Ferror){
170481ede731SDavid du Colombier 		if((task&Eidnf) == 0)
170581ede731SDavid du Colombier 			print("%s: i/o error %ux\n", name, task);
1706b1707c5dSDavid du Colombier 		r->status = SDcheck;
1707b1707c5dSDavid du Colombier 		return SDcheck;
1708b1707c5dSDavid du Colombier 	}
1709b1707c5dSDavid du Colombier 
1710b1707c5dSDavid du Colombier 	data += n;
1711b1707c5dSDavid du Colombier 
1712b1707c5dSDavid du Colombier 	r->rlen = data - (uchar*)r->data;
1713b1707c5dSDavid du Colombier 	r->status = SDok;
1714b1707c5dSDavid du Colombier 	return SDok;
1715b1707c5dSDavid du Colombier }
1716b1707c5dSDavid du Colombier 
1717b1707c5dSDavid du Colombier static int
1718b1707c5dSDavid du Colombier iario(SDreq *r)
1719b1707c5dSDavid du Colombier {
1720b1707c5dSDavid du Colombier 	int i, n, count, try, max, flag, task;
1721b1707c5dSDavid du Colombier 	vlong lba;
1722b1707c5dSDavid du Colombier 	char *name;
1723b1707c5dSDavid du Colombier 	uchar *cmd, *data;
1724b1707c5dSDavid du Colombier 	Aport *p;
1725b1707c5dSDavid du Colombier 	Asleep as;
1726b1707c5dSDavid du Colombier 	Ctlr *c;
1727b1707c5dSDavid du Colombier 	Drive *d;
1728b1707c5dSDavid du Colombier 	SDunit *unit;
1729b1707c5dSDavid du Colombier 
1730b1707c5dSDavid du Colombier 	unit = r->unit;
1731b1707c5dSDavid du Colombier 	c = unit->dev->ctlr;
1732b1707c5dSDavid du Colombier 	d = c->drive[unit->subno];
1733b1707c5dSDavid du Colombier 	if(d->portm.feat & Datapi)
1734b1707c5dSDavid du Colombier 		return iariopkt(r, d);
1735b1707c5dSDavid du Colombier 	cmd = r->cmd;
1736b1707c5dSDavid du Colombier 	name = d->unit->name;
1737b1707c5dSDavid du Colombier 	p = d->port;
1738b1707c5dSDavid du Colombier 
1739b1707c5dSDavid du Colombier 	if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
174081ede731SDavid du Colombier 		if(flushcache(d) == 0)
1741b1707c5dSDavid du Colombier 			return sdsetsense(r, SDok, 0, 0, 0);
1742b1707c5dSDavid du Colombier 		return sdsetsense(r, SDcheck, 3, 0xc, 2);
1743b1707c5dSDavid du Colombier 	}
1744b1707c5dSDavid du Colombier 
1745b1707c5dSDavid du Colombier 	if((i = sdfakescsi(r, d->info, sizeof d->info)) != SDnostatus){
1746b1707c5dSDavid du Colombier 		r->status = i;
1747b1707c5dSDavid du Colombier 		return i;
1748b1707c5dSDavid du Colombier 	}
1749b1707c5dSDavid du Colombier 
1750b1707c5dSDavid du Colombier 	if(*cmd != 0x28 && *cmd != 0x2a){
1751b1707c5dSDavid du Colombier 		print("%s: bad cmd 0x%.2ux\n", name, cmd[0]);
1752b1707c5dSDavid du Colombier 		r->status = SDcheck;
1753b1707c5dSDavid du Colombier 		return SDcheck;
1754b1707c5dSDavid du Colombier 	}
1755b1707c5dSDavid du Colombier 
1756b1707c5dSDavid du Colombier 	lba   = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
1757b1707c5dSDavid du Colombier 	count = cmd[7]<<8 | cmd[8];
1758b1707c5dSDavid du Colombier 	if(r->data == nil)
1759b1707c5dSDavid du Colombier 		return SDok;
1760b1707c5dSDavid du Colombier 	if(r->dlen < count * unit->secsize)
1761b1707c5dSDavid du Colombier 		count = r->dlen / unit->secsize;
1762b1707c5dSDavid du Colombier 	max = 128;
1763b1707c5dSDavid du Colombier 
1764b1707c5dSDavid du Colombier 	try = 0;
1765b1707c5dSDavid du Colombier retry:
1766b1707c5dSDavid du Colombier 	data = r->data;
1767b1707c5dSDavid du Colombier 	while(count > 0){
1768b1707c5dSDavid du Colombier 		n = count;
1769b1707c5dSDavid du Colombier 		if(n > max)
1770b1707c5dSDavid du Colombier 			n = max;
177158db92f4SDavid du Colombier 		ahcibuild(d, cmd, data, n, lba);
177281ede731SDavid du Colombier 		switch(waitready(d)){
177381ede731SDavid du Colombier 		case -1:
177481ede731SDavid du Colombier 			qunlock(&d->portm);
177581ede731SDavid du Colombier 			return SDeio;
177681ede731SDavid du Colombier 		case 1:
177781ede731SDavid du Colombier 			qunlock(&d->portm);
177881ede731SDavid du Colombier 			esleep(1);
177981ede731SDavid du Colombier 			goto retry;
178081ede731SDavid du Colombier 		}
1781b1707c5dSDavid du Colombier 		ilock(d);
1782b1707c5dSDavid du Colombier 		d->portm.flag = 0;
1783b1707c5dSDavid du Colombier 		iunlock(d);
1784b1707c5dSDavid du Colombier 		p->ci = 1;
1785b1707c5dSDavid du Colombier 
1786b1707c5dSDavid du Colombier 		as.p = p;
1787b1707c5dSDavid du Colombier 		as.i = 1;
1788b1707c5dSDavid du Colombier 		d->intick = MACHP(0)->ticks;
1789014ad43fSDavid du Colombier 		d->active++;
1790b1707c5dSDavid du Colombier 
1791b1707c5dSDavid du Colombier 		while(waserror())
1792b1707c5dSDavid du Colombier 			;
1793b1707c5dSDavid du Colombier 		sleep(&d->portm, ahciclear, &as);
1794b1707c5dSDavid du Colombier 		poperror();
1795b1707c5dSDavid du Colombier 
1796014ad43fSDavid du Colombier 		d->active--;
1797b1707c5dSDavid du Colombier 		ilock(d);
1798b1707c5dSDavid du Colombier 		flag = d->portm.flag;
1799b1707c5dSDavid du Colombier 		task = d->port->task;
1800b1707c5dSDavid du Colombier 		iunlock(d);
1801b1707c5dSDavid du Colombier 
1802b1707c5dSDavid du Colombier 		if(task & (Efatal<<8) ||
1803b1707c5dSDavid du Colombier 		    task & (ASbsy|ASdrq) && d->state == Dready){
1804014ad43fSDavid du Colombier 			d->port->ci = 0;
1805b1707c5dSDavid du Colombier 			ahcirecover(&d->portc);
1806b1707c5dSDavid du Colombier 			task = d->port->task;
1807b1707c5dSDavid du Colombier 		}
1808b1707c5dSDavid du Colombier 		qunlock(&d->portm);
1809b1707c5dSDavid du Colombier 		if(flag == 0){
1810b1707c5dSDavid du Colombier 			if(++try == 10){
1811b1707c5dSDavid du Colombier 				print("%s: bad disk\n", name);
1812b1707c5dSDavid du Colombier 				r->status = SDeio;
1813b1707c5dSDavid du Colombier 				return SDeio;
1814b1707c5dSDavid du Colombier 			}
1815b1707c5dSDavid du Colombier 			iprint("%s: retry %lld\n", name, lba);
1816b1707c5dSDavid du Colombier 			goto retry;
1817b1707c5dSDavid du Colombier 		}
1818b1707c5dSDavid du Colombier 		if(flag & Ferror){
1819b1707c5dSDavid du Colombier 			iprint("%s: i/o error %ux @%,lld\n", name, task, lba);
1820b1707c5dSDavid du Colombier 			r->status = SDeio;
1821b1707c5dSDavid du Colombier 			return SDeio;
1822b1707c5dSDavid du Colombier 		}
1823b1707c5dSDavid du Colombier 
1824b1707c5dSDavid du Colombier 		count -= n;
1825b1707c5dSDavid du Colombier 		lba   += n;
1826b1707c5dSDavid du Colombier 		data += n * unit->secsize;
1827b1707c5dSDavid du Colombier 	}
1828b1707c5dSDavid du Colombier 	r->rlen = data - (uchar*)r->data;
1829b1707c5dSDavid du Colombier 	r->status = SDok;
1830b1707c5dSDavid du Colombier 	return SDok;
1831b1707c5dSDavid du Colombier }
1832b1707c5dSDavid du Colombier 
1833b1707c5dSDavid du Colombier /*
1834b1707c5dSDavid du Colombier  * configure drives 0-5 as ahci sata  (c.f. errata)
1835b1707c5dSDavid du Colombier  */
1836b1707c5dSDavid du Colombier static int
1837b1707c5dSDavid du Colombier iaahcimode(Pcidev *p)
1838b1707c5dSDavid du Colombier {
18393d56950aSDavid du Colombier 	dprint("iaahcimode: %ux %ux %ux\n", pcicfgr8(p, 0x91), pcicfgr8(p, 92),
1840b1707c5dSDavid du Colombier 		pcicfgr8(p, 93));
1841b1707c5dSDavid du Colombier 	pcicfgw16(p, 0x92, pcicfgr32(p, 0x92) | 0xf);	/* ports 0-3 */
1842b1707c5dSDavid du Colombier //	pcicfgw8(p, 0x93, pcicfgr32(p, 9x93) | 3);	/* ports 4-5 */
1843b1707c5dSDavid du Colombier 	return 0;
1844b1707c5dSDavid du Colombier }
1845b1707c5dSDavid du Colombier 
1846b1707c5dSDavid du Colombier static void
1847b1707c5dSDavid du Colombier iasetupahci(Ctlr *c)
1848b1707c5dSDavid du Colombier {
1849b1707c5dSDavid du Colombier 	/* disable cmd block decoding. */
1850b1707c5dSDavid du Colombier 	pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~(1<<15));
1851b1707c5dSDavid du Colombier 	pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~(1<<15));
1852b1707c5dSDavid du Colombier 
1853b1707c5dSDavid du Colombier 	c->lmmio[0x4/4] |= 1 << 31;	/* enable ahci mode (ghc register) */
1854b1707c5dSDavid du Colombier 	c->lmmio[0xc/4] = (1 << 6) - 1;	/* 5 ports. (supposedly ro pi reg.) */
1855b1707c5dSDavid du Colombier 
1856b1707c5dSDavid du Colombier 	/* enable ahci mode; from ich9 datasheet */
1857014ad43fSDavid du Colombier 	pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5);
1858014ad43fSDavid du Colombier }
1859014ad43fSDavid du Colombier 
1860014ad43fSDavid du Colombier static int
1861014ad43fSDavid du Colombier didtype(Pcidev *p)
1862014ad43fSDavid du Colombier {
1863014ad43fSDavid du Colombier 	switch(p->vid){
1864014ad43fSDavid du Colombier 	case 0x8086:
1865014ad43fSDavid du Colombier 		if((p->did & 0xfffc) == 0x2680)
1866014ad43fSDavid du Colombier 			return Tesb;
186791157df7SDavid du Colombier 		/*
186891157df7SDavid du Colombier 		 * 0x27c4 is the intel 82801 in compatibility (not sata) mode.
186991157df7SDavid du Colombier 		 */
187091157df7SDavid du Colombier 		if ((p->did & 0xfffb) == 0x27c1 ||	/* 82801g[bh]m ich7 */
187191157df7SDavid du Colombier 		    p->did == 0x2821 ||			/* 82801h[roh] */
187291157df7SDavid du Colombier 		    (p->did & 0xfffe) == 0x2824 ||	/* 82801h[b] */
187391157df7SDavid du Colombier 		    (p->did & 0xfeff) == 0x2829 ||	/* ich8/9m */
1874014ad43fSDavid du Colombier 		    (p->did & 0xfffe) == 0x2922 ||	/* ich9 */
187591157df7SDavid du Colombier 		    p->did == 0x3a02 ||			/* 82801jd/do */
187691157df7SDavid du Colombier 		    (p->did & 0xfefe) == 0x3a22 ||	/* ich10, pch */
187791157df7SDavid du Colombier 		    (p->did & 0xfff7) == 0x3b28)	/* pchm */
1878014ad43fSDavid du Colombier 			return Tich;
1879014ad43fSDavid du Colombier 		break;
1880014ad43fSDavid du Colombier 	case 0x1002:
1881014ad43fSDavid du Colombier 		if(p->did == 0x4380)
1882014ad43fSDavid du Colombier 			return Tsb600;
1883014ad43fSDavid du Colombier 		break;
1884014ad43fSDavid du Colombier 	}
1885014ad43fSDavid du Colombier 	if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1)
1886014ad43fSDavid du Colombier 		return Tunk;
1887014ad43fSDavid du Colombier 	return -1;
1888b1707c5dSDavid du Colombier }
1889b1707c5dSDavid du Colombier 
1890b1707c5dSDavid du Colombier static SDev*
1891b1707c5dSDavid du Colombier iapnp(void)
1892b1707c5dSDavid du Colombier {
1893b1707c5dSDavid du Colombier 	int i, n, nunit, type;
1894b1707c5dSDavid du Colombier 	ulong io;
1895b1707c5dSDavid du Colombier 	Ctlr *c;
1896b1707c5dSDavid du Colombier 	Drive *d;
1897b1707c5dSDavid du Colombier 	Pcidev *p;
1898b1707c5dSDavid du Colombier 	SDev *head, *tail, *s;
1899b1707c5dSDavid du Colombier 	static int done;
1900b1707c5dSDavid du Colombier 
1901b1707c5dSDavid du Colombier 	if(done++)
1902b1707c5dSDavid du Colombier 		return nil;
1903b1707c5dSDavid du Colombier 
1904014ad43fSDavid du Colombier 	memset(olds, 0xff, sizeof olds);
1905b1707c5dSDavid du Colombier 	p = nil;
1906b1707c5dSDavid du Colombier 	head = tail = nil;
1907b1707c5dSDavid du Colombier loop:
1908b1707c5dSDavid du Colombier 	while((p = pcimatch(p, 0, 0)) != nil){
1909014ad43fSDavid du Colombier 		type = didtype(p);
1910014ad43fSDavid du Colombier 		if (type == -1 || p->mem[Abar].bar == 0)
1911b1707c5dSDavid du Colombier 			continue;
1912b1707c5dSDavid du Colombier 		if(niactlr == NCtlr){
191391157df7SDavid du Colombier 			print("ahci: iapnp: %s: too many controllers\n",
19143d56950aSDavid du Colombier 				tname[type]);
1915b1707c5dSDavid du Colombier 			break;
1916b1707c5dSDavid du Colombier 		}
1917b1707c5dSDavid du Colombier 		c = iactlr + niactlr;
1918b1707c5dSDavid du Colombier 		s = sdevs  + niactlr;
1919b1707c5dSDavid du Colombier 		memset(c, 0, sizeof *c);
1920b1707c5dSDavid du Colombier 		memset(s, 0, sizeof *s);
1921b1707c5dSDavid du Colombier 		io = p->mem[Abar].bar & ~0xf;
1922b1707c5dSDavid du Colombier 		c->mmio = vmap(io, p->mem[Abar].size);
1923b1707c5dSDavid du Colombier 		if(c->mmio == 0){
19243d56950aSDavid du Colombier 			print("ahci: %s: address 0x%luX in use did=%x\n",
1925b1707c5dSDavid du Colombier 				Tname(c), io, p->did);
1926b1707c5dSDavid du Colombier 			continue;
1927b1707c5dSDavid du Colombier 		}
1928b1707c5dSDavid du Colombier 		c->lmmio = (ulong*)c->mmio;
1929b1707c5dSDavid du Colombier 		c->pci = p;
1930b1707c5dSDavid du Colombier 		c->type = type;
1931b1707c5dSDavid du Colombier 
1932b1707c5dSDavid du Colombier 		s->ifc = &sdiahciifc;
1933b1707c5dSDavid du Colombier 		s->idno = 'E' + niactlr;
1934b1707c5dSDavid du Colombier 		s->ctlr = c;
1935b1707c5dSDavid du Colombier 		c->sdev = s;
1936b1707c5dSDavid du Colombier 
1937014ad43fSDavid du Colombier 		if(Intel(c) && p->did != 0x2681)
1938b1707c5dSDavid du Colombier 			iasetupahci(c);
1939b1707c5dSDavid du Colombier 		nunit = ahciconf(c);
1940b1707c5dSDavid du Colombier //		ahcihbareset((Ahba*)c->mmio);
1941014ad43fSDavid du Colombier 		if(Intel(c) && iaahcimode(p) == -1)
1942b1707c5dSDavid du Colombier 			break;
1943b1707c5dSDavid du Colombier 		if(nunit < 1){
1944b1707c5dSDavid du Colombier 			vunmap(c->mmio, p->mem[Abar].size);
1945b1707c5dSDavid du Colombier 			continue;
1946b1707c5dSDavid du Colombier 		}
1947b1707c5dSDavid du Colombier 		c->ndrive = s->nunit = nunit;
1948014ad43fSDavid du Colombier 		c->mport = c->hba->cap & ((1<<5)-1);
1949b1707c5dSDavid du Colombier 
1950b1707c5dSDavid du Colombier 		i = (c->hba->cap >> 21) & 1;
1951b1707c5dSDavid du Colombier 		print("#S/sd%c: %s: sata-%s with %d ports\n", s->idno,
1952b1707c5dSDavid du Colombier 			Tname(c), "I\0II" + i*2, nunit);
1953b1707c5dSDavid du Colombier 
1954b1707c5dSDavid du Colombier 		/* map the drives -- they don't all need to be enabled. */
1955b1707c5dSDavid du Colombier 		memset(c->rawdrive, 0, sizeof c->rawdrive);
1956b1707c5dSDavid du Colombier 		n = 0;
1957b1707c5dSDavid du Colombier 		for(i = 0; i < NCtlrdrv; i++) {
1958b1707c5dSDavid du Colombier 			d = c->rawdrive + i;
1959b1707c5dSDavid du Colombier 			d->portno = i;
1960b1707c5dSDavid du Colombier 			d->driveno = -1;
1961b1707c5dSDavid du Colombier 			d->sectors = 0;
196297902b0cSDavid du Colombier 			d->serial[0] = ' ';
1963b1707c5dSDavid du Colombier 			d->ctlr = c;
1964b1707c5dSDavid du Colombier 			if((c->hba->pi & (1<<i)) == 0)
1965b1707c5dSDavid du Colombier 				continue;
1966b1707c5dSDavid du Colombier 			d->port = (Aport*)(c->mmio + 0x80*i + 0x100);
1967b1707c5dSDavid du Colombier 			d->portc.p = d->port;
1968b1707c5dSDavid du Colombier 			d->portc.m = &d->portm;
1969b1707c5dSDavid du Colombier 			d->driveno = n++;
197081ede731SDavid du Colombier 			c->drive[d->driveno] = d;
197181ede731SDavid du Colombier 			iadrive[niadrive + d->driveno] = d;
1972b1707c5dSDavid du Colombier 		}
1973b1707c5dSDavid du Colombier 		for(i = 0; i < n; i++)
1974b1707c5dSDavid du Colombier 			if(ahciidle(c->drive[i]->port) == -1){
19753d56950aSDavid du Colombier 				dprint("ahci: %s: port %d wedged; abort\n",
1976b1707c5dSDavid du Colombier 					Tname(c), i);
1977b1707c5dSDavid du Colombier 				goto loop;
1978b1707c5dSDavid du Colombier 			}
1979b1707c5dSDavid du Colombier 		for(i = 0; i < n; i++){
1980b1707c5dSDavid du Colombier 			c->drive[i]->mode = DMsatai;
1981b1707c5dSDavid du Colombier 			configdrive(c->drive[i]);
1982b1707c5dSDavid du Colombier 		}
1983b1707c5dSDavid du Colombier 
1984014ad43fSDavid du Colombier 		niadrive += n;
1985b1707c5dSDavid du Colombier 		niactlr++;
1986b1707c5dSDavid du Colombier 		if(head)
1987b1707c5dSDavid du Colombier 			tail->next = s;
1988b1707c5dSDavid du Colombier 		else
1989b1707c5dSDavid du Colombier 			head = s;
1990b1707c5dSDavid du Colombier 		tail = s;
1991b1707c5dSDavid du Colombier 	}
1992b1707c5dSDavid du Colombier 	return head;
1993b1707c5dSDavid du Colombier }
1994b1707c5dSDavid du Colombier 
1995b1707c5dSDavid du Colombier static char* smarttab[] = {
1996b1707c5dSDavid du Colombier 	"unset",
1997b1707c5dSDavid du Colombier 	"error",
1998b1707c5dSDavid du Colombier 	"threshold exceeded",
1999b1707c5dSDavid du Colombier 	"normal"
2000b1707c5dSDavid du Colombier };
2001b1707c5dSDavid du Colombier 
2002b1707c5dSDavid du Colombier static char *
2003b1707c5dSDavid du Colombier pflag(char *s, char *e, uchar f)
2004b1707c5dSDavid du Colombier {
2005b1707c5dSDavid du Colombier 	uchar i;
2006b1707c5dSDavid du Colombier 
2007b1707c5dSDavid du Colombier 	for(i = 0; i < 8; i++)
2008b1707c5dSDavid du Colombier 		if(f & (1 << i))
2009b1707c5dSDavid du Colombier 			s = seprint(s, e, "%s ", flagname[i]);
2010b1707c5dSDavid du Colombier 	return seprint(s, e, "\n");
2011b1707c5dSDavid du Colombier }
2012b1707c5dSDavid du Colombier 
2013b1707c5dSDavid du Colombier static int
2014b1707c5dSDavid du Colombier iarctl(SDunit *u, char *p, int l)
2015b1707c5dSDavid du Colombier {
2016b1707c5dSDavid du Colombier 	char buf[32];
2017b1707c5dSDavid du Colombier 	char *e, *op;
2018b1707c5dSDavid du Colombier 	Aport *o;
2019b1707c5dSDavid du Colombier 	Ctlr *c;
2020b1707c5dSDavid du Colombier 	Drive *d;
2021b1707c5dSDavid du Colombier 
202258db92f4SDavid du Colombier 	c = u->dev->ctlr;
202358db92f4SDavid du Colombier 	if(c == nil) {
202458db92f4SDavid du Colombier print("iarctl: nil u->dev->ctlr\n");
2025b1707c5dSDavid du Colombier 		return 0;
202658db92f4SDavid du Colombier 	}
2027b1707c5dSDavid du Colombier 	d = c->drive[u->subno];
2028b1707c5dSDavid du Colombier 	o = d->port;
2029b1707c5dSDavid du Colombier 
2030b1707c5dSDavid du Colombier 	e = p+l;
2031b1707c5dSDavid du Colombier 	op = p;
2032b1707c5dSDavid du Colombier 	if(d->state == Dready){
2033b1707c5dSDavid du Colombier 		p = seprint(p, e, "model\t%s\n", d->model);
2034b1707c5dSDavid du Colombier 		p = seprint(p, e, "serial\t%s\n", d->serial);
2035b1707c5dSDavid du Colombier 		p = seprint(p, e, "firm\t%s\n", d->firmware);
2036b1707c5dSDavid du Colombier 		if(d->smartrs == 0xff)
2037b1707c5dSDavid du Colombier 			p = seprint(p, e, "smart\tenable error\n");
2038b1707c5dSDavid du Colombier 		else if(d->smartrs == 0)
2039b1707c5dSDavid du Colombier 			p = seprint(p, e, "smart\tdisabled\n");
2040b1707c5dSDavid du Colombier 		else
2041b1707c5dSDavid du Colombier 			p = seprint(p, e, "smart\t%s\n",
2042b1707c5dSDavid du Colombier 				smarttab[d->portm.smart]);
2043b1707c5dSDavid du Colombier 		p = seprint(p, e, "flag\t");
2044b1707c5dSDavid du Colombier 		p = pflag(p, e, d->portm.feat);
2045b1707c5dSDavid du Colombier 	}else
2046b1707c5dSDavid du Colombier 		p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]);
2047b1707c5dSDavid du Colombier 	serrstr(o->serror, buf, buf + sizeof buf - 1);
204891b330d9SDavid du Colombier 	p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux; "
204991b330d9SDavid du Colombier 		"sig %lux sstatus %04lux\n", o->task, o->cmd, o->serror, buf,
2050b1707c5dSDavid du Colombier 		o->ci, o->isr, o->sig, o->sstatus);
205158db92f4SDavid du Colombier 	if(d->unit == nil)
205258db92f4SDavid du Colombier 		panic("iarctl: nil d->unit");
205358db92f4SDavid du Colombier 	p = seprint(p, e, "geometry %llud %lud\n", d->sectors, d->unit->secsize);
2054b1707c5dSDavid du Colombier 	return p - op;
2055b1707c5dSDavid du Colombier }
2056b1707c5dSDavid du Colombier 
2057b1707c5dSDavid du Colombier static void
2058b1707c5dSDavid du Colombier runflushcache(Drive *d)
2059b1707c5dSDavid du Colombier {
2060b1707c5dSDavid du Colombier 	long t0;
2061b1707c5dSDavid du Colombier 
2062b1707c5dSDavid du Colombier 	t0 = MACHP(0)->ticks;
206381ede731SDavid du Colombier 	if(flushcache(d) != 0)
206481ede731SDavid du Colombier 		error(Eio);
20653d56950aSDavid du Colombier 	dprint("ahci: flush in %ld ms\n", MACHP(0)->ticks - t0);
2066b1707c5dSDavid du Colombier }
2067b1707c5dSDavid du Colombier 
2068b1707c5dSDavid du Colombier static void
2069b1707c5dSDavid du Colombier forcemode(Drive *d, char *mode)
2070b1707c5dSDavid du Colombier {
2071b1707c5dSDavid du Colombier 	int i;
2072b1707c5dSDavid du Colombier 
2073b1707c5dSDavid du Colombier 	for(i = 0; i < nelem(modename); i++)
2074b1707c5dSDavid du Colombier 		if(strcmp(mode, modename[i]) == 0)
2075b1707c5dSDavid du Colombier 			break;
2076b1707c5dSDavid du Colombier 	if(i == nelem(modename))
2077b1707c5dSDavid du Colombier 		i = 0;
2078b1707c5dSDavid du Colombier 	ilock(d);
2079b1707c5dSDavid du Colombier 	d->mode = i;
2080b1707c5dSDavid du Colombier 	iunlock(d);
2081b1707c5dSDavid du Colombier }
2082b1707c5dSDavid du Colombier 
2083b1707c5dSDavid du Colombier static void
2084b1707c5dSDavid du Colombier runsmartable(Drive *d, int i)
2085b1707c5dSDavid du Colombier {
2086b1707c5dSDavid du Colombier 	if(waserror()){
2087b1707c5dSDavid du Colombier 		qunlock(&d->portm);
2088b1707c5dSDavid du Colombier 		d->smartrs = 0;
2089b1707c5dSDavid du Colombier 		nexterror();
2090b1707c5dSDavid du Colombier 	}
209181ede731SDavid du Colombier 	if(lockready(d) == -1)
209281ede731SDavid du Colombier 		error(Eio);
2093b1707c5dSDavid du Colombier 	d->smartrs = smart(&d->portc, i);
2094b1707c5dSDavid du Colombier 	d->portm.smart = 0;
2095b1707c5dSDavid du Colombier 	qunlock(&d->portm);
2096b1707c5dSDavid du Colombier 	poperror();
2097b1707c5dSDavid du Colombier }
2098b1707c5dSDavid du Colombier 
2099b1707c5dSDavid du Colombier static void
2100b1707c5dSDavid du Colombier forcestate(Drive *d, char *state)
2101b1707c5dSDavid du Colombier {
2102b1707c5dSDavid du Colombier 	int i;
2103b1707c5dSDavid du Colombier 
2104b1707c5dSDavid du Colombier 	for(i = 0; i < nelem(diskstates); i++)
2105b1707c5dSDavid du Colombier 		if(strcmp(state, diskstates[i]) == 0)
2106b1707c5dSDavid du Colombier 			break;
2107b1707c5dSDavid du Colombier 	if(i == nelem(diskstates))
2108014ad43fSDavid du Colombier 		error(Ebadctl);
2109b1707c5dSDavid du Colombier 	ilock(d);
2110b1707c5dSDavid du Colombier 	d->state = i;
2111b1707c5dSDavid du Colombier 	iunlock(d);
2112b1707c5dSDavid du Colombier }
2113b1707c5dSDavid du Colombier 
2114b1707c5dSDavid du Colombier 
2115b1707c5dSDavid du Colombier static int
2116b1707c5dSDavid du Colombier iawctl(SDunit *u, Cmdbuf *cmd)
2117b1707c5dSDavid du Colombier {
2118b1707c5dSDavid du Colombier 	char **f;
2119b1707c5dSDavid du Colombier 	Ctlr *c;
2120b1707c5dSDavid du Colombier 	Drive *d;
212181ede731SDavid du Colombier 	uint i;
2122b1707c5dSDavid du Colombier 
2123b1707c5dSDavid du Colombier 	c = u->dev->ctlr;
2124b1707c5dSDavid du Colombier 	d = c->drive[u->subno];
2125b1707c5dSDavid du Colombier 	f = cmd->f;
2126b1707c5dSDavid du Colombier 
2127b1707c5dSDavid du Colombier 	if(strcmp(f[0], "flushcache") == 0)
2128b1707c5dSDavid du Colombier 		runflushcache(d);
2129b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "identify") ==  0){
2130b1707c5dSDavid du Colombier 		i = strtoul(f[1]? f[1]: "0", 0, 0);
2131b1707c5dSDavid du Colombier 		if(i > 0xff)
2132b1707c5dSDavid du Colombier 			i = 0;
21333d56950aSDavid du Colombier 		dprint("ahci: %04d %ux\n", i, d->info[i]);
2134b1707c5dSDavid du Colombier 	}else if(strcmp(f[0], "mode") == 0)
2135b1707c5dSDavid du Colombier 		forcemode(d, f[1]? f[1]: "satai");
2136b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "nop") == 0){
2137b1707c5dSDavid du Colombier 		if((d->portm.feat & Dnop) == 0){
213881ede731SDavid du Colombier 			cmderror(cmd, "no drive support");
2139b1707c5dSDavid du Colombier 			return -1;
2140b1707c5dSDavid du Colombier 		}
2141b1707c5dSDavid du Colombier 		if(waserror()){
2142b1707c5dSDavid du Colombier 			qunlock(&d->portm);
2143b1707c5dSDavid du Colombier 			nexterror();
2144b1707c5dSDavid du Colombier 		}
214581ede731SDavid du Colombier 		if(lockready(d) == -1)
214681ede731SDavid du Colombier 			error(Eio);
2147b1707c5dSDavid du Colombier 		nop(&d->portc);
2148b1707c5dSDavid du Colombier 		qunlock(&d->portm);
2149b1707c5dSDavid du Colombier 		poperror();
2150b1707c5dSDavid du Colombier 	}else if(strcmp(f[0], "reset") == 0)
2151b1707c5dSDavid du Colombier 		forcestate(d, "reset");
2152b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "smart") == 0){
2153b1707c5dSDavid du Colombier 		if(d->smartrs == 0){
2154b1707c5dSDavid du Colombier 			cmderror(cmd, "smart not enabled");
2155b1707c5dSDavid du Colombier 			return -1;
2156b1707c5dSDavid du Colombier 		}
2157b1707c5dSDavid du Colombier 		if(waserror()){
2158b1707c5dSDavid du Colombier 			qunlock(&d->portm);
2159b1707c5dSDavid du Colombier 			d->smartrs = 0;
2160b1707c5dSDavid du Colombier 			nexterror();
2161b1707c5dSDavid du Colombier 		}
216281ede731SDavid du Colombier 		if(lockready(d) == -1)
216381ede731SDavid du Colombier 			error(Eio);
2164b1707c5dSDavid du Colombier 		d->portm.smart = 2 + smartrs(&d->portc);
2165b1707c5dSDavid du Colombier 		qunlock(&d->portm);
2166b1707c5dSDavid du Colombier 		poperror();
2167b1707c5dSDavid du Colombier 	}else if(strcmp(f[0], "smartdisable") == 0)
2168b1707c5dSDavid du Colombier 		runsmartable(d, 1);
2169b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "smartenable") == 0)
2170b1707c5dSDavid du Colombier 		runsmartable(d, 0);
2171b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "state") == 0)
2172b1707c5dSDavid du Colombier 		forcestate(d, f[1]? f[1]: "null");
2173b1707c5dSDavid du Colombier 	else{
2174b1707c5dSDavid du Colombier 		cmderror(cmd, Ebadctl);
2175b1707c5dSDavid du Colombier 		return -1;
2176b1707c5dSDavid du Colombier 	}
2177b1707c5dSDavid du Colombier 	return 0;
2178b1707c5dSDavid du Colombier }
2179b1707c5dSDavid du Colombier 
2180b1707c5dSDavid du Colombier static char *
2181b1707c5dSDavid du Colombier portr(char *p, char *e, uint x)
2182b1707c5dSDavid du Colombier {
2183b1707c5dSDavid du Colombier 	int i, a;
2184b1707c5dSDavid du Colombier 
2185b1707c5dSDavid du Colombier 	p[0] = 0;
2186b1707c5dSDavid du Colombier 	a = -1;
2187b1707c5dSDavid du Colombier 	for(i = 0; i < 32; i++){
2188b1707c5dSDavid du Colombier 		if((x & (1<<i)) == 0){
2189b1707c5dSDavid du Colombier 			if(a != -1 && i - 1 != a)
2190b1707c5dSDavid du Colombier 				p = seprint(p, e, "-%d", i - 1);
2191b1707c5dSDavid du Colombier 			a = -1;
2192b1707c5dSDavid du Colombier 			continue;
2193b1707c5dSDavid du Colombier 		}
2194b1707c5dSDavid du Colombier 		if(a == -1){
2195b1707c5dSDavid du Colombier 			if(i > 0)
2196b1707c5dSDavid du Colombier 				p = seprint(p, e, ", ");
2197b1707c5dSDavid du Colombier 			p = seprint(p, e, "%d", a = i);
2198b1707c5dSDavid du Colombier 		}
2199b1707c5dSDavid du Colombier 	}
2200b1707c5dSDavid du Colombier 	if(a != -1 && i - 1 != a)
2201b1707c5dSDavid du Colombier 		p = seprint(p, e, "-%d", i - 1);
2202b1707c5dSDavid du Colombier 	return p;
2203b1707c5dSDavid du Colombier }
2204b1707c5dSDavid du Colombier 
2205b1707c5dSDavid du Colombier /* must emit exactly one line per controller (sd(3)) */
2206b1707c5dSDavid du Colombier static char*
2207b1707c5dSDavid du Colombier iartopctl(SDev *sdev, char *p, char *e)
2208b1707c5dSDavid du Colombier {
220991b330d9SDavid du Colombier 	ulong cap;
2210b1707c5dSDavid du Colombier 	char pr[25];
2211b1707c5dSDavid du Colombier 	Ahba *hba;
2212b1707c5dSDavid du Colombier 	Ctlr *ctlr;
2213b1707c5dSDavid du Colombier 
2214b1707c5dSDavid du Colombier #define has(x, str) if(cap & (x)) p = seprint(p, e, "%s ", (str))
2215b1707c5dSDavid du Colombier 
2216b1707c5dSDavid du Colombier 	ctlr = sdev->ctlr;
2217b1707c5dSDavid du Colombier 	hba = ctlr->hba;
2218b1707c5dSDavid du Colombier 	p = seprint(p, e, "sd%c ahci port %#p: ", sdev->idno, hba);
2219b1707c5dSDavid du Colombier 	cap = hba->cap;
2220b1707c5dSDavid du Colombier 	has(Hs64a, "64a");
2221b1707c5dSDavid du Colombier 	has(Hsalp, "alp");
2222b1707c5dSDavid du Colombier 	has(Hsam, "am");
2223b1707c5dSDavid du Colombier 	has(Hsclo, "clo");
2224b1707c5dSDavid du Colombier 	has(Hcccs, "coal");
2225b1707c5dSDavid du Colombier 	has(Hems, "ems");
2226b1707c5dSDavid du Colombier 	has(Hsal, "led");
2227b1707c5dSDavid du Colombier 	has(Hsmps, "mps");
2228b1707c5dSDavid du Colombier 	has(Hsncq, "ncq");
2229b1707c5dSDavid du Colombier 	has(Hssntf, "ntf");
2230b1707c5dSDavid du Colombier 	has(Hspm, "pm");
2231b1707c5dSDavid du Colombier 	has(Hpsc, "pslum");
2232b1707c5dSDavid du Colombier 	has(Hssc, "slum");
2233b1707c5dSDavid du Colombier 	has(Hsss, "ss");
2234b1707c5dSDavid du Colombier 	has(Hsxs, "sxs");
2235b1707c5dSDavid du Colombier 	portr(pr, pr + sizeof pr, hba->pi);
2236b1707c5dSDavid du Colombier 	return seprint(p, e,
223791b330d9SDavid du Colombier 		"iss %ld ncs %ld np %ld; ghc %lux isr %lux pi %lux %s ver %lux\n",
2238b1707c5dSDavid du Colombier 		(cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f),
2239b1707c5dSDavid du Colombier 		hba->ghc, hba->isr, hba->pi, pr, hba->ver);
2240b1707c5dSDavid du Colombier #undef has
2241b1707c5dSDavid du Colombier }
2242b1707c5dSDavid du Colombier 
2243b1707c5dSDavid du Colombier static int
2244b1707c5dSDavid du Colombier iawtopctl(SDev *, Cmdbuf *cmd)
2245b1707c5dSDavid du Colombier {
2246b1707c5dSDavid du Colombier 	int *v;
2247b1707c5dSDavid du Colombier 	char **f;
2248b1707c5dSDavid du Colombier 
2249b1707c5dSDavid du Colombier 	f = cmd->f;
2250b1707c5dSDavid du Colombier 	v = 0;
2251b1707c5dSDavid du Colombier 
22529e8a50a9SDavid du Colombier 	if (f[0] == nil)
22539e8a50a9SDavid du Colombier 		return 0;
2254b1707c5dSDavid du Colombier 	if(strcmp(f[0], "debug") == 0)
2255b1707c5dSDavid du Colombier 		v = &debug;
2256b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "idprint") == 0)
2257b1707c5dSDavid du Colombier 		v = &prid;
2258b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "aprint") == 0)
2259b1707c5dSDavid du Colombier 		v = &datapi;
2260b1707c5dSDavid du Colombier 	else
2261b1707c5dSDavid du Colombier 		cmderror(cmd, Ebadctl);
2262b1707c5dSDavid du Colombier 
2263b1707c5dSDavid du Colombier 	switch(cmd->nf){
2264b1707c5dSDavid du Colombier 	default:
2265b1707c5dSDavid du Colombier 		cmderror(cmd, Ebadarg);
2266b1707c5dSDavid du Colombier 	case 1:
2267b1707c5dSDavid du Colombier 		*v ^= 1;
2268b1707c5dSDavid du Colombier 		break;
2269b1707c5dSDavid du Colombier 	case 2:
22709e8a50a9SDavid du Colombier 		if(f[1])
22719e8a50a9SDavid du Colombier 			*v = strcmp(f[1], "on") == 0;
22729e8a50a9SDavid du Colombier 		else
22739e8a50a9SDavid du Colombier 			*v ^= 1;
2274b1707c5dSDavid du Colombier 		break;
2275b1707c5dSDavid du Colombier 	}
2276b1707c5dSDavid du Colombier 	return 0;
2277b1707c5dSDavid du Colombier }
2278b1707c5dSDavid du Colombier 
2279b1707c5dSDavid du Colombier SDifc sdiahciifc = {
2280b1707c5dSDavid du Colombier 	"iahci",
2281b1707c5dSDavid du Colombier 
2282b1707c5dSDavid du Colombier 	iapnp,
2283b1707c5dSDavid du Colombier 	nil,		/* legacy */
2284b1707c5dSDavid du Colombier 	iaenable,
2285b1707c5dSDavid du Colombier 	iadisable,
2286b1707c5dSDavid du Colombier 
2287b1707c5dSDavid du Colombier 	iaverify,
2288b1707c5dSDavid du Colombier 	iaonline,
2289b1707c5dSDavid du Colombier 	iario,
2290b1707c5dSDavid du Colombier 	iarctl,
2291b1707c5dSDavid du Colombier 	iawctl,
2292b1707c5dSDavid du Colombier 
2293b1707c5dSDavid du Colombier 	scsibio,
2294b1707c5dSDavid du Colombier 	nil,		/* probe */
2295b1707c5dSDavid du Colombier 	nil,		/* clear */
2296b1707c5dSDavid du Colombier 	iartopctl,
2297b1707c5dSDavid du Colombier 	iawtopctl,
2298b1707c5dSDavid du Colombier };
2299