xref: /plan9/sys/src/9/pc/sdiahci.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
1b1707c5dSDavid du Colombier /*
275cb58dbSDavid du Colombier  * ahci serial ata driver
3014ad43fSDavid du Colombier  * copyright © 2007-8 coraid, inc.
4b1707c5dSDavid du Colombier  */
5b1707c5dSDavid du Colombier 
6b1707c5dSDavid du Colombier #include "u.h"
7b1707c5dSDavid du Colombier #include "../port/lib.h"
8b1707c5dSDavid du Colombier #include "mem.h"
9b1707c5dSDavid du Colombier #include "dat.h"
10b1707c5dSDavid du Colombier #include "fns.h"
11b1707c5dSDavid du Colombier #include "io.h"
12b1707c5dSDavid du Colombier #include "../port/error.h"
13b1707c5dSDavid du Colombier #include "../port/sd.h"
14b1707c5dSDavid du Colombier #include "ahci.h"
15b1707c5dSDavid du Colombier 
16b1707c5dSDavid du Colombier #define	dprint(...)	if(debug)	iprint(__VA_ARGS__); else USED(debug)
17014ad43fSDavid du Colombier #define	idprint(...)	if(prid)	iprint(__VA_ARGS__);  else USED(prid)
18014ad43fSDavid du Colombier #define	aprint(...)	if(datapi)	iprint(__VA_ARGS__);  else USED(datapi)
1975cb58dbSDavid du Colombier 
20b1707c5dSDavid du Colombier #define Tname(c)	tname[(c)->type]
2175cb58dbSDavid du Colombier #define Intel(x)	((x)->pci->vid == Vintel)
22b1707c5dSDavid du Colombier 
23b1707c5dSDavid du Colombier enum {
2476f70593SDavid du Colombier 	NCtlr	= 16,
25b1707c5dSDavid du Colombier 	NCtlrdrv= 32,
26b1707c5dSDavid du Colombier 	NDrive	= NCtlr*NCtlrdrv,
27b1707c5dSDavid du Colombier 
28b1707c5dSDavid du Colombier 	Read	= 0,
29b1707c5dSDavid du Colombier 	Write,
3075cb58dbSDavid du Colombier 
3155329247SDavid du Colombier 	Nms	= 256,			/* ms. between drive checks */
3275cb58dbSDavid du Colombier 	Mphywait=  2*1024/Nms - 1,
3375cb58dbSDavid du Colombier 	Midwait	= 16*1024/Nms - 1,
3475cb58dbSDavid du Colombier 	Mcomrwait= 64*1024/Nms - 1,
3575cb58dbSDavid du Colombier 
3675cb58dbSDavid du Colombier 	Obs	= 0xa0,			/* obsolete device bits */
37b4124be8SDavid du Colombier 
38b4124be8SDavid du Colombier 	/*
39b4124be8SDavid du Colombier 	 * if we get more than this many interrupts per tick for a drive,
40b4124be8SDavid du Colombier 	 * either the hardware is broken or we've got a bug in this driver.
41b4124be8SDavid du Colombier 	 */
42c02f0a41SDavid du Colombier 	Maxintrspertick = 2000,		/* was 1000 */
43b1707c5dSDavid du Colombier };
44b1707c5dSDavid du Colombier 
45b1707c5dSDavid du Colombier /* pci space configuration */
46b1707c5dSDavid du Colombier enum {
47b1707c5dSDavid du Colombier 	Pmap	= 0x90,
48b1707c5dSDavid du Colombier 	Ppcs	= 0x91,
49b1707c5dSDavid du Colombier 	Prev	= 0xa8,
50b1707c5dSDavid du Colombier };
51b1707c5dSDavid du Colombier 
52b1707c5dSDavid du Colombier enum {
53b1707c5dSDavid du Colombier 	Tesb,
54b1707c5dSDavid du Colombier 	Tich,
55b1707c5dSDavid du Colombier 	Tsb600,
56014ad43fSDavid du Colombier 	Tunk,
57b1707c5dSDavid du Colombier };
58b1707c5dSDavid du Colombier 
59b1707c5dSDavid du Colombier static char *tname[] = {
60b1707c5dSDavid du Colombier 	"63xxesb",
61b1707c5dSDavid du Colombier 	"ich",
62b1707c5dSDavid du Colombier 	"sb600",
6375cb58dbSDavid du Colombier 	"unknown",
64b1707c5dSDavid du Colombier };
65b1707c5dSDavid du Colombier 
66b1707c5dSDavid du Colombier enum {
67b1707c5dSDavid du Colombier 	Dnull,
68b1707c5dSDavid du Colombier 	Dmissing,
69b1707c5dSDavid du Colombier 	Dnew,
70b1707c5dSDavid du Colombier 	Dready,
71b1707c5dSDavid du Colombier 	Derror,
72b1707c5dSDavid du Colombier 	Dreset,
73b1707c5dSDavid du Colombier 	Doffline,
74b1707c5dSDavid du Colombier 	Dportreset,
75b1707c5dSDavid du Colombier 	Dlast,
76b1707c5dSDavid du Colombier };
77b1707c5dSDavid du Colombier 
78b1707c5dSDavid du Colombier static char *diskstates[Dlast] = {
79b1707c5dSDavid du Colombier 	"null",
80b1707c5dSDavid du Colombier 	"missing",
81b1707c5dSDavid du Colombier 	"new",
82b1707c5dSDavid du Colombier 	"ready",
83b1707c5dSDavid du Colombier 	"error",
84b1707c5dSDavid du Colombier 	"reset",
85b1707c5dSDavid du Colombier 	"offline",
86b1707c5dSDavid du Colombier 	"portreset",
87b1707c5dSDavid du Colombier };
88b1707c5dSDavid du Colombier 
89b1707c5dSDavid du Colombier enum {
90b1707c5dSDavid du Colombier 	DMautoneg,
91b1707c5dSDavid du Colombier 	DMsatai,
92b1707c5dSDavid du Colombier 	DMsataii,
9375cb58dbSDavid du Colombier 	DMsata3,
94b1707c5dSDavid du Colombier };
95b1707c5dSDavid du Colombier 
9675cb58dbSDavid du Colombier static char *modename[] = {		/* used in control messages */
97b1707c5dSDavid du Colombier 	"auto",
98b1707c5dSDavid du Colombier 	"satai",
99b1707c5dSDavid du Colombier 	"sataii",
10075cb58dbSDavid du Colombier 	"sata3",
10175cb58dbSDavid du Colombier };
10275cb58dbSDavid du Colombier static char *descmode[] = {		/*  only printed */
10375cb58dbSDavid du Colombier 	"auto",
10475cb58dbSDavid du Colombier 	"sata 1",
10575cb58dbSDavid du Colombier 	"sata 2",
10675cb58dbSDavid du Colombier 	"sata 3",
107b1707c5dSDavid du Colombier };
108b1707c5dSDavid du Colombier 
109b1707c5dSDavid du Colombier static char *flagname[] = {
110b1707c5dSDavid du Colombier 	"llba",
111b1707c5dSDavid du Colombier 	"smart",
112b1707c5dSDavid du Colombier 	"power",
113b1707c5dSDavid du Colombier 	"nop",
114b1707c5dSDavid du Colombier 	"atapi",
115b1707c5dSDavid du Colombier 	"atapi16",
116b1707c5dSDavid du Colombier };
117b1707c5dSDavid du Colombier 
11875cb58dbSDavid du Colombier typedef struct Asleep Asleep;
11975cb58dbSDavid du Colombier typedef struct Ctlr Ctlr;
12075cb58dbSDavid du Colombier typedef struct Drive Drive;
12175cb58dbSDavid du Colombier 
12275cb58dbSDavid du Colombier struct Drive {
123b1707c5dSDavid du Colombier 	Lock;
124b1707c5dSDavid du Colombier 
125b1707c5dSDavid du Colombier 	Ctlr	*ctlr;
126b1707c5dSDavid du Colombier 	SDunit	*unit;
127b1707c5dSDavid du Colombier 	char	name[10];
128b1707c5dSDavid du Colombier 	Aport	*port;
129b1707c5dSDavid du Colombier 	Aportm	portm;
130f3112f79SDavid du Colombier 	Aportc	portc;		/* redundant ptr to port and portm */
131b1707c5dSDavid du Colombier 
132b1707c5dSDavid du Colombier 	uchar	mediachange;
133b1707c5dSDavid du Colombier 	uchar	state;
134b1707c5dSDavid du Colombier 	uchar	smartrs;
135b1707c5dSDavid du Colombier 
136b1707c5dSDavid du Colombier 	uvlong	sectors;
13758db92f4SDavid du Colombier 	ulong	secsize;
138f3112f79SDavid du Colombier 	ulong	intick;		/* start tick of current transfer */
13981ede731SDavid du Colombier 	ulong	lastseen;
140b1707c5dSDavid du Colombier 	int	wait;
141f3112f79SDavid du Colombier 	uchar	mode;		/* DMautoneg, satai or sataii */
142b1707c5dSDavid du Colombier 	uchar	active;
143b1707c5dSDavid du Colombier 
144b1707c5dSDavid du Colombier 	char	serial[20+1];
145b1707c5dSDavid du Colombier 	char	firmware[8+1];
146b1707c5dSDavid du Colombier 	char	model[40+1];
147b1707c5dSDavid du Colombier 
14876f70593SDavid du Colombier 	int	infosz;
14976f70593SDavid du Colombier 	ushort	*info;
15076f70593SDavid du Colombier 	ushort	tinyinfo[2];	/* used iff malloc fails */
151b1707c5dSDavid du Colombier 
152b1707c5dSDavid du Colombier 	int	driveno;	/* ctlr*NCtlrdrv + unit */
153b1707c5dSDavid du Colombier 	/* controller port # != driveno when not all ports are enabled */
154b1707c5dSDavid du Colombier 	int	portno;
155b4124be8SDavid du Colombier 
156b4124be8SDavid du Colombier 	ulong	lastintr0;
157b4124be8SDavid du Colombier 	ulong	intrs;
15875cb58dbSDavid du Colombier };
159b1707c5dSDavid du Colombier 
160b1707c5dSDavid du Colombier struct Ctlr {
161b1707c5dSDavid du Colombier 	Lock;
162b1707c5dSDavid du Colombier 
163b1707c5dSDavid du Colombier 	int	type;
164b1707c5dSDavid du Colombier 	int	enabled;
165b1707c5dSDavid du Colombier 	SDev	*sdev;
166b1707c5dSDavid du Colombier 	Pcidev	*pci;
167b1707c5dSDavid du Colombier 
16875cb58dbSDavid du Colombier 	/* virtual register addresses */
169b1707c5dSDavid du Colombier 	uchar	*mmio;
170b1707c5dSDavid du Colombier 	ulong	*lmmio;
171b1707c5dSDavid du Colombier 	Ahba	*hba;
172b1707c5dSDavid du Colombier 
17375cb58dbSDavid du Colombier 	/* phyical register address */
17475cb58dbSDavid du Colombier 	uchar	*physio;
17575cb58dbSDavid du Colombier 
17676f70593SDavid du Colombier 	Drive	*rawdrive;
177b1707c5dSDavid du Colombier 	Drive	*drive[NCtlrdrv];
178b1707c5dSDavid du Colombier 	int	ndrive;
179b4124be8SDavid du Colombier 	int	mport;		/* highest drive # (0-origin) on ich9 at least */
180b4124be8SDavid du Colombier 
181b4124be8SDavid du Colombier 	ulong	lastintr0;
182b4124be8SDavid du Colombier 	ulong	intrs;		/* not attributable to any drive */
183b1707c5dSDavid du Colombier };
184b1707c5dSDavid du Colombier 
18575cb58dbSDavid du Colombier struct Asleep {
18675cb58dbSDavid du Colombier 	Aport	*p;
18775cb58dbSDavid du Colombier 	int	i;
18875cb58dbSDavid du Colombier };
18975cb58dbSDavid du Colombier 
19075cb58dbSDavid du Colombier extern SDifc sdiahciifc;
19175cb58dbSDavid du Colombier 
192b1707c5dSDavid du Colombier static	Ctlr	iactlr[NCtlr];
193b1707c5dSDavid du Colombier static	SDev	sdevs[NCtlr];
194b1707c5dSDavid du Colombier static	int	niactlr;
195b1707c5dSDavid du Colombier 
196b1707c5dSDavid du Colombier static	Drive	*iadrive[NDrive];
197b1707c5dSDavid du Colombier static	int	niadrive;
198b1707c5dSDavid du Colombier 
1999e8a50a9SDavid du Colombier /* these are fiddled in iawtopctl() */
200b1707c5dSDavid du Colombier static	int	debug;
201b1707c5dSDavid du Colombier static	int	prid = 1;
202b1707c5dSDavid du Colombier static	int	datapi;
203b1707c5dSDavid du Colombier 
204b1707c5dSDavid du Colombier static char stab[] = {
205b1707c5dSDavid du Colombier [0]	'i', 'm',
206b1707c5dSDavid du Colombier [8]	't', 'c', 'p', 'e',
207b1707c5dSDavid du Colombier [16]	'N', 'I', 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X'
208b1707c5dSDavid du Colombier };
209b1707c5dSDavid du Colombier 
210b1707c5dSDavid du Colombier static void
serrstr(ulong r,char * s,char * e)211b1707c5dSDavid du Colombier serrstr(ulong r, char *s, char *e)
212b1707c5dSDavid du Colombier {
213b1707c5dSDavid du Colombier 	int i;
214b1707c5dSDavid du Colombier 
215b1707c5dSDavid du Colombier 	e -= 3;
216b1707c5dSDavid du Colombier 	for(i = 0; i < nelem(stab) && s < e; i++)
217b1707c5dSDavid du Colombier 		if(r & (1<<i) && stab[i]){
218b1707c5dSDavid du Colombier 			*s++ = stab[i];
219b1707c5dSDavid du Colombier 			if(SerrBad & (1<<i))
220b1707c5dSDavid du Colombier 				*s++ = '*';
221b1707c5dSDavid du Colombier 		}
222b1707c5dSDavid du Colombier 	*s = 0;
223b1707c5dSDavid du Colombier }
224b1707c5dSDavid du Colombier 
225b1707c5dSDavid du Colombier static char ntab[] = "0123456789abcdef";
226b1707c5dSDavid du Colombier 
227b1707c5dSDavid du Colombier static void
preg(uchar * reg,int n)228b1707c5dSDavid du Colombier preg(uchar *reg, int n)
229b1707c5dSDavid du Colombier {
230b1707c5dSDavid du Colombier 	int i;
231b1707c5dSDavid du Colombier 	char buf[25*3+1], *e;
232b1707c5dSDavid du Colombier 
233b1707c5dSDavid du Colombier 	e = buf;
234b1707c5dSDavid du Colombier 	for(i = 0; i < n; i++){
235b1707c5dSDavid du Colombier 		*e++ = ntab[reg[i]>>4];
236b1707c5dSDavid du Colombier 		*e++ = ntab[reg[i]&0xf];
237b1707c5dSDavid du Colombier 		*e++ = ' ';
238b1707c5dSDavid du Colombier 	}
239b1707c5dSDavid du Colombier 	*e++ = '\n';
240b1707c5dSDavid du Colombier 	*e = 0;
241b1707c5dSDavid du Colombier 	dprint(buf);
242b1707c5dSDavid du Colombier }
243b1707c5dSDavid du Colombier 
244b1707c5dSDavid du Colombier static void
dreg(char * s,Aport * p)245b1707c5dSDavid du Colombier dreg(char *s, Aport *p)
246b1707c5dSDavid du Colombier {
247f0499952SDavid du Colombier 	dprint("ahci: %stask=%#lux; cmd=%#lux; ci=%#lux; is=%#lux\n",
2483d56950aSDavid du Colombier 		s, p->task, p->cmd, p->ci, p->isr);
249b1707c5dSDavid du Colombier }
250b1707c5dSDavid du Colombier 
251b1707c5dSDavid du Colombier static void
esleep(int ms)252b1707c5dSDavid du Colombier esleep(int ms)
253b1707c5dSDavid du Colombier {
254b1707c5dSDavid du Colombier 	if(waserror())
255b1707c5dSDavid du Colombier 		return;
256b1707c5dSDavid du Colombier 	tsleep(&up->sleep, return0, 0, ms);
257b1707c5dSDavid du Colombier 	poperror();
258b1707c5dSDavid du Colombier }
259b1707c5dSDavid du Colombier 
260b1707c5dSDavid du Colombier static int
ahciclear(void * v)261b1707c5dSDavid du Colombier ahciclear(void *v)
262b1707c5dSDavid du Colombier {
263b1707c5dSDavid du Colombier 	Asleep *s;
264b1707c5dSDavid du Colombier 
265b1707c5dSDavid du Colombier 	s = v;
266b1707c5dSDavid du Colombier 	return (s->p->ci & s->i) == 0;
267b1707c5dSDavid du Colombier }
268b1707c5dSDavid du Colombier 
269b1707c5dSDavid du Colombier static void
aesleep(Aportm * pm,Asleep * a,int ms)270060a30c6SDavid du Colombier aesleep(Aportm *pm, Asleep *a, int ms)
271b1707c5dSDavid du Colombier {
272b1707c5dSDavid du Colombier 	if(waserror())
273b1707c5dSDavid du Colombier 		return;
274060a30c6SDavid du Colombier 	tsleep(pm, ahciclear, a, ms);
275b1707c5dSDavid du Colombier 	poperror();
276b1707c5dSDavid du Colombier }
277b1707c5dSDavid du Colombier 
278b1707c5dSDavid du Colombier static int
ahciwait(Aportc * c,int ms)279b1707c5dSDavid du Colombier ahciwait(Aportc *c, int ms)
280b1707c5dSDavid du Colombier {
281b1707c5dSDavid du Colombier 	Asleep as;
282b1707c5dSDavid du Colombier 	Aport *p;
283b1707c5dSDavid du Colombier 
284b1707c5dSDavid du Colombier 	p = c->p;
285b1707c5dSDavid du Colombier 	p->ci = 1;
286b1707c5dSDavid du Colombier 	as.p = p;
287b1707c5dSDavid du Colombier 	as.i = 1;
288*58da3067SDavid du Colombier 	aesleep(c->pm, &as, ms);
289b1707c5dSDavid du Colombier 	if((p->task&1) == 0 && p->ci == 0)
290b1707c5dSDavid du Colombier 		return 0;
291b1707c5dSDavid du Colombier 	dreg("ahciwait timeout ", c->p);
292b1707c5dSDavid du Colombier 	return -1;
293b1707c5dSDavid du Colombier }
294b1707c5dSDavid du Colombier 
295113b9010SDavid du Colombier /* fill in cfis boilerplate */
296113b9010SDavid du Colombier static uchar *
cfissetup(Aportc * pc)297113b9010SDavid du Colombier cfissetup(Aportc *pc)
298113b9010SDavid du Colombier {
299113b9010SDavid du Colombier 	uchar *cfis;
300113b9010SDavid du Colombier 
301*58da3067SDavid du Colombier 	cfis = pc->pm->ctab->cfis;
302113b9010SDavid du Colombier 	memset(cfis, 0, 0x20);
303113b9010SDavid du Colombier 	cfis[0] = 0x27;
304113b9010SDavid du Colombier 	cfis[1] = 0x80;
305113b9010SDavid du Colombier 	cfis[7] = Obs;
306113b9010SDavid du Colombier 	return cfis;
307113b9010SDavid du Colombier }
308113b9010SDavid du Colombier 
309113b9010SDavid du Colombier /* initialise pc's list */
310113b9010SDavid du Colombier static void
listsetup(Aportc * pc,int flags)311113b9010SDavid du Colombier listsetup(Aportc *pc, int flags)
312113b9010SDavid du Colombier {
313113b9010SDavid du Colombier 	Alist *list;
314113b9010SDavid du Colombier 
315*58da3067SDavid du Colombier 	list = pc->pm->list;
316113b9010SDavid du Colombier 	list->flags = flags | 5;
317113b9010SDavid du Colombier 	list->len = 0;
318*58da3067SDavid du Colombier 	list->ctab = PCIWADDR(pc->pm->ctab);
319113b9010SDavid du Colombier 	list->ctabhi = 0;
320113b9010SDavid du Colombier }
321113b9010SDavid du Colombier 
322b1707c5dSDavid du Colombier static int
nop(Aportc * pc)323b1707c5dSDavid du Colombier nop(Aportc *pc)
324b1707c5dSDavid du Colombier {
325b1707c5dSDavid du Colombier 	uchar *c;
326b1707c5dSDavid du Colombier 
327*58da3067SDavid du Colombier 	if((pc->pm->feat & Dnop) == 0)
328b1707c5dSDavid du Colombier 		return -1;
329113b9010SDavid du Colombier 	c = cfissetup(pc);
330113b9010SDavid du Colombier 	c[2] = 0;
331113b9010SDavid du Colombier 	listsetup(pc, Lwrite);
332b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
333b1707c5dSDavid du Colombier }
334b1707c5dSDavid du Colombier 
335b1707c5dSDavid du Colombier static int
setfeatures(Aportc * pc,uchar f)336b1707c5dSDavid du Colombier setfeatures(Aportc *pc, uchar f)
337b1707c5dSDavid du Colombier {
338b1707c5dSDavid du Colombier 	uchar *c;
339b1707c5dSDavid du Colombier 
340113b9010SDavid du Colombier 	c = cfissetup(pc);
341b1707c5dSDavid du Colombier 	c[2] = 0xef;
342b1707c5dSDavid du Colombier 	c[3] = f;
343113b9010SDavid du Colombier 	listsetup(pc, Lwrite);
344b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
345b1707c5dSDavid du Colombier }
346b1707c5dSDavid du Colombier 
347b1707c5dSDavid du Colombier static int
setudmamode(Aportc * pc,uchar f)348b1707c5dSDavid du Colombier setudmamode(Aportc *pc, uchar f)
349b1707c5dSDavid du Colombier {
350b1707c5dSDavid du Colombier 	uchar *c;
351b1707c5dSDavid du Colombier 
352b1707c5dSDavid du Colombier 	/* hack */
353b1707c5dSDavid du Colombier 	if((pc->p->sig >> 16) == 0xeb14)
354b1707c5dSDavid du Colombier 		return 0;
355113b9010SDavid du Colombier 	c = cfissetup(pc);
356b1707c5dSDavid du Colombier 	c[2] = 0xef;
357b1707c5dSDavid du Colombier 	c[3] = 3;		/* set transfer mode */
358b1707c5dSDavid du Colombier 	c[12] = 0x40 | f;	/* sector count */
359113b9010SDavid du Colombier 	listsetup(pc, Lwrite);
360b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
361b1707c5dSDavid du Colombier }
362b1707c5dSDavid du Colombier 
363b1707c5dSDavid du Colombier static void
asleep(int ms)364b1707c5dSDavid du Colombier asleep(int ms)
365b1707c5dSDavid du Colombier {
366b1707c5dSDavid du Colombier 	if(up == nil)
367b1707c5dSDavid du Colombier 		delay(ms);
368b1707c5dSDavid du Colombier 	else
369b1707c5dSDavid du Colombier 		esleep(ms);
370b1707c5dSDavid du Colombier }
371b1707c5dSDavid du Colombier 
372b1707c5dSDavid du Colombier static int
ahciportreset(Aportc * c)373b1707c5dSDavid du Colombier ahciportreset(Aportc *c)
374b1707c5dSDavid du Colombier {
37591b330d9SDavid du Colombier 	ulong *cmd, i;
376b1707c5dSDavid du Colombier 	Aport *p;
377b1707c5dSDavid du Colombier 
378b1707c5dSDavid du Colombier 	p = c->p;
379b1707c5dSDavid du Colombier 	cmd = &p->cmd;
380b1707c5dSDavid du Colombier 	*cmd &= ~(Afre|Ast);
381b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 25){
382b1707c5dSDavid du Colombier 		if((*cmd&Acr) == 0)
383b1707c5dSDavid du Colombier 			break;
384b1707c5dSDavid du Colombier 		asleep(25);
385b1707c5dSDavid du Colombier 	}
386b1707c5dSDavid du Colombier 	p->sctl = 1|(p->sctl&~7);
387b1707c5dSDavid du Colombier 	delay(1);
388b1707c5dSDavid du Colombier 	p->sctl &= ~7;
389b1707c5dSDavid du Colombier 	return 0;
390b1707c5dSDavid du Colombier }
391b1707c5dSDavid du Colombier 
392b1707c5dSDavid du Colombier static int
smart(Aportc * pc,int n)393b1707c5dSDavid du Colombier smart(Aportc *pc, int n)
394b1707c5dSDavid du Colombier {
395b1707c5dSDavid du Colombier 	uchar *c;
396b1707c5dSDavid du Colombier 
397*58da3067SDavid du Colombier 	if((pc->pm->feat&Dsmart) == 0)
398b1707c5dSDavid du Colombier 		return -1;
399113b9010SDavid du Colombier 	c = cfissetup(pc);
400b1707c5dSDavid du Colombier 	c[2] = 0xb0;
401b1707c5dSDavid du Colombier 	c[3] = 0xd8 + n;	/* able smart */
402b1707c5dSDavid du Colombier 	c[5] = 0x4f;
403b1707c5dSDavid du Colombier 	c[6] = 0xc2;
404113b9010SDavid du Colombier 	listsetup(pc, Lwrite);
405b1707c5dSDavid du Colombier 	if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
406f0499952SDavid du Colombier 		dprint("ahci: smart fail %#lux\n", pc->p->task);
407b1707c5dSDavid du Colombier //		preg(pc->m->fis.r, 20);
408b1707c5dSDavid du Colombier 		return -1;
409b1707c5dSDavid du Colombier 	}
410b1707c5dSDavid du Colombier 	if(n)
411b1707c5dSDavid du Colombier 		return 0;
412b1707c5dSDavid du Colombier 	return 1;
413b1707c5dSDavid du Colombier }
414b1707c5dSDavid du Colombier 
415b1707c5dSDavid du Colombier static int
smartrs(Aportc * pc)416b1707c5dSDavid du Colombier smartrs(Aportc *pc)
417b1707c5dSDavid du Colombier {
418b1707c5dSDavid du Colombier 	uchar *c;
419b1707c5dSDavid du Colombier 
420113b9010SDavid du Colombier 	c = cfissetup(pc);
421b1707c5dSDavid du Colombier 	c[2] = 0xb0;
422b1707c5dSDavid du Colombier 	c[3] = 0xda;		/* return smart status */
423b1707c5dSDavid du Colombier 	c[5] = 0x4f;
424b1707c5dSDavid du Colombier 	c[6] = 0xc2;
425113b9010SDavid du Colombier 	listsetup(pc, Lwrite);
426b1707c5dSDavid du Colombier 
427*58da3067SDavid du Colombier 	c = pc->pm->fis.r;
428b1707c5dSDavid du Colombier 	if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
429f0499952SDavid du Colombier 		dprint("ahci: smart fail %#lux\n", pc->p->task);
430b1707c5dSDavid du Colombier 		preg(c, 20);
431b1707c5dSDavid du Colombier 		return -1;
432b1707c5dSDavid du Colombier 	}
433b1707c5dSDavid du Colombier 	if(c[5] == 0x4f && c[6] == 0xc2)
434b1707c5dSDavid du Colombier 		return 1;
435b1707c5dSDavid du Colombier 	return 0;
436b1707c5dSDavid du Colombier }
437b1707c5dSDavid du Colombier 
438b1707c5dSDavid du Colombier static int
ahciflushcache(Aportc * pc)43981ede731SDavid du Colombier ahciflushcache(Aportc *pc)
440b1707c5dSDavid du Colombier {
441113b9010SDavid du Colombier 	uchar *c;
442b1707c5dSDavid du Colombier 
443113b9010SDavid du Colombier 	c = cfissetup(pc);
444*58da3067SDavid du Colombier 	c[2] = pc->pm->feat & Dllba? 0xea: 0xe7;
445113b9010SDavid du Colombier 	listsetup(pc, Lwrite);
446b1707c5dSDavid du Colombier 	if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
447f0499952SDavid du Colombier 		dprint("ahciflushcache: fail %#lux\n", pc->p->task);
448b1707c5dSDavid du Colombier //		preg(pc->m->fis.r, 20);
449b1707c5dSDavid du Colombier 		return -1;
450b1707c5dSDavid du Colombier 	}
451b1707c5dSDavid du Colombier 	return 0;
452b1707c5dSDavid du Colombier }
453b1707c5dSDavid du Colombier 
454b1707c5dSDavid du Colombier static ushort
gbit16(void * a)455b1707c5dSDavid du Colombier gbit16(void *a)
456b1707c5dSDavid du Colombier {
457b1707c5dSDavid du Colombier 	uchar *i;
458b1707c5dSDavid du Colombier 
459b1707c5dSDavid du Colombier 	i = a;
46091157df7SDavid du Colombier 	return i[1]<<8 | i[0];
461b1707c5dSDavid du Colombier }
462b1707c5dSDavid du Colombier 
46391b330d9SDavid du Colombier static ulong
gbit32(void * a)464b1707c5dSDavid du Colombier gbit32(void *a)
465b1707c5dSDavid du Colombier {
46691b330d9SDavid du Colombier 	ulong j;
467b1707c5dSDavid du Colombier 	uchar *i;
468b1707c5dSDavid du Colombier 
469b1707c5dSDavid du Colombier 	i = a;
470b1707c5dSDavid du Colombier 	j  = i[3] << 24;
471b1707c5dSDavid du Colombier 	j |= i[2] << 16;
472b1707c5dSDavid du Colombier 	j |= i[1] << 8;
473b1707c5dSDavid du Colombier 	j |= i[0];
474b1707c5dSDavid du Colombier 	return j;
475b1707c5dSDavid du Colombier }
476b1707c5dSDavid du Colombier 
477b1707c5dSDavid du Colombier static uvlong
gbit64(void * a)478b1707c5dSDavid du Colombier gbit64(void *a)
479b1707c5dSDavid du Colombier {
480b1707c5dSDavid du Colombier 	uchar *i;
481b1707c5dSDavid du Colombier 
482b1707c5dSDavid du Colombier 	i = a;
483b1707c5dSDavid du Colombier 	return (uvlong)gbit32(i+4) << 32 | gbit32(a);
484b1707c5dSDavid du Colombier }
485b1707c5dSDavid du Colombier 
486b1707c5dSDavid du Colombier static int
ahciidentify0(Aportc * pc,void * id,int atapi)487b1707c5dSDavid du Colombier ahciidentify0(Aportc *pc, void *id, int atapi)
488b1707c5dSDavid du Colombier {
489b1707c5dSDavid du Colombier 	uchar *c;
490b1707c5dSDavid du Colombier 	Aprdt *p;
491b1707c5dSDavid du Colombier 	static uchar tab[] = { 0xec, 0xa1, };
492b1707c5dSDavid du Colombier 
493113b9010SDavid du Colombier 	c = cfissetup(pc);
494b1707c5dSDavid du Colombier 	c[2] = tab[atapi];
495113b9010SDavid du Colombier 	listsetup(pc, 1<<16);
496b1707c5dSDavid du Colombier 
49755329247SDavid du Colombier 	memset(id, 0, 0x100);			/* magic */
498*58da3067SDavid du Colombier 	p = &pc->pm->ctab->prdt;
499b1707c5dSDavid du Colombier 	p->dba = PCIWADDR(id);
500b1707c5dSDavid du Colombier 	p->dbahi = 0;
501b1707c5dSDavid du Colombier 	p->count = 1<<31 | (0x200-2) | 1;
502b1707c5dSDavid du Colombier 	return ahciwait(pc, 3*1000);
503b1707c5dSDavid du Colombier }
504b1707c5dSDavid du Colombier 
505b1707c5dSDavid du Colombier static vlong
ahciidentify(Aportc * pc,ushort * id)506b1707c5dSDavid du Colombier ahciidentify(Aportc *pc, ushort *id)
507b1707c5dSDavid du Colombier {
508b1707c5dSDavid du Colombier 	int i, sig;
509b1707c5dSDavid du Colombier 	vlong s;
510060a30c6SDavid du Colombier 	Aportm *pm;
511b1707c5dSDavid du Colombier 
512*58da3067SDavid du Colombier 	pm = pc->pm;
513060a30c6SDavid du Colombier 	pm->feat = 0;
514060a30c6SDavid du Colombier 	pm->smart = 0;
515b1707c5dSDavid du Colombier 	i = 0;
516b1707c5dSDavid du Colombier 	sig = pc->p->sig >> 16;
517b1707c5dSDavid du Colombier 	if(sig == 0xeb14){
518060a30c6SDavid du Colombier 		pm->feat |= Datapi;
519b1707c5dSDavid du Colombier 		i = 1;
520b1707c5dSDavid du Colombier 	}
521b1707c5dSDavid du Colombier 	if(ahciidentify0(pc, id, i) == -1)
522b1707c5dSDavid du Colombier 		return -1;
523b1707c5dSDavid du Colombier 
524b1707c5dSDavid du Colombier 	i = gbit16(id+83) | gbit16(id+86);
525b1707c5dSDavid du Colombier 	if(i & (1<<10)){
526060a30c6SDavid du Colombier 		pm->feat |= Dllba;
527b1707c5dSDavid du Colombier 		s = gbit64(id+100);
528b1707c5dSDavid du Colombier 	}else
529b1707c5dSDavid du Colombier 		s = gbit32(id+60);
530b1707c5dSDavid du Colombier 
531060a30c6SDavid du Colombier 	if(pm->feat&Datapi){
532b1707c5dSDavid du Colombier 		i = gbit16(id+0);
533b1707c5dSDavid du Colombier 		if(i&1)
534060a30c6SDavid du Colombier 			pm->feat |= Datapi16;
535b1707c5dSDavid du Colombier 	}
536b1707c5dSDavid du Colombier 
537b1707c5dSDavid du Colombier 	i = gbit16(id+83);
538b1707c5dSDavid du Colombier 	if((i>>14) == 1) {
539b1707c5dSDavid du Colombier 		if(i & (1<<3))
540060a30c6SDavid du Colombier 			pm->feat |= Dpower;
541b1707c5dSDavid du Colombier 		i = gbit16(id+82);
542b1707c5dSDavid du Colombier 		if(i & 1)
543060a30c6SDavid du Colombier 			pm->feat |= Dsmart;
544b1707c5dSDavid du Colombier 		if(i & (1<<14))
545060a30c6SDavid du Colombier 			pm->feat |= Dnop;
546b1707c5dSDavid du Colombier 	}
547b1707c5dSDavid du Colombier 	return s;
548b1707c5dSDavid du Colombier }
549b1707c5dSDavid du Colombier 
550b1707c5dSDavid du Colombier static int
ahciquiet(Aport * a)551b1707c5dSDavid du Colombier ahciquiet(Aport *a)
552b1707c5dSDavid du Colombier {
55391b330d9SDavid du Colombier 	ulong *p, i;
554b1707c5dSDavid du Colombier 
555b1707c5dSDavid du Colombier 	p = &a->cmd;
556b1707c5dSDavid du Colombier 	*p &= ~Ast;
557b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 50){
558b1707c5dSDavid du Colombier 		if((*p & Acr) == 0)
559b1707c5dSDavid du Colombier 			goto stop;
560b1707c5dSDavid du Colombier 		asleep(50);
561b1707c5dSDavid du Colombier 	}
562b1707c5dSDavid du Colombier 	return -1;
563b1707c5dSDavid du Colombier stop:
564b1707c5dSDavid du Colombier 	if((a->task & (ASdrq|ASbsy)) == 0){
565b1707c5dSDavid du Colombier 		*p |= Ast;
566b1707c5dSDavid du Colombier 		return 0;
567b1707c5dSDavid du Colombier 	}
568b1707c5dSDavid du Colombier 
569b1707c5dSDavid du Colombier 	*p |= Aclo;
570b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 50){
571b1707c5dSDavid du Colombier 		if((*p & Aclo) == 0)
572b1707c5dSDavid du Colombier 			goto stop1;
573b1707c5dSDavid du Colombier 		asleep(50);
574b1707c5dSDavid du Colombier 	}
575b1707c5dSDavid du Colombier 	return -1;
576b1707c5dSDavid du Colombier stop1:
577b1707c5dSDavid du Colombier 	/* extra check */
578f0499952SDavid du Colombier 	dprint("ahci: clo clear %#lx\n", a->task);
579b1707c5dSDavid du Colombier 	if(a->task & ASbsy)
580b1707c5dSDavid du Colombier 		return -1;
581b1707c5dSDavid du Colombier 	*p |= Ast;
582b1707c5dSDavid du Colombier 	return 0;
583b1707c5dSDavid du Colombier }
584b1707c5dSDavid du Colombier 
585b1707c5dSDavid du Colombier static int
ahcicomreset(Aportc * pc)586b1707c5dSDavid du Colombier ahcicomreset(Aportc *pc)
587b1707c5dSDavid du Colombier {
588b1707c5dSDavid du Colombier 	uchar *c;
589b1707c5dSDavid du Colombier 
590b1707c5dSDavid du Colombier 	dprint("ahcicomreset\n");
5913d56950aSDavid du Colombier 	dreg("ahci: comreset ", pc->p);
592b1707c5dSDavid du Colombier 	if(ahciquiet(pc->p) == -1){
5933d56950aSDavid du Colombier 		dprint("ahciquiet failed\n");
594b1707c5dSDavid du Colombier 		return -1;
595b1707c5dSDavid du Colombier 	}
596b1707c5dSDavid du Colombier 	dreg("comreset ", pc->p);
597b1707c5dSDavid du Colombier 
598113b9010SDavid du Colombier 	c = cfissetup(pc);
599113b9010SDavid du Colombier 	c[1] = 0;
600b1707c5dSDavid du Colombier 	c[15] = 1<<2;		/* srst */
601113b9010SDavid du Colombier 	listsetup(pc, Lclear | Lreset);
602b1707c5dSDavid du Colombier 	if(ahciwait(pc, 500) == -1){
6033d56950aSDavid du Colombier 		dprint("ahcicomreset: first command failed\n");
604b1707c5dSDavid du Colombier 		return -1;
605b1707c5dSDavid du Colombier 	}
606b1707c5dSDavid du Colombier 	microdelay(250);
607b1707c5dSDavid du Colombier 	dreg("comreset ", pc->p);
608b1707c5dSDavid du Colombier 
609113b9010SDavid du Colombier 	c = cfissetup(pc);
610113b9010SDavid du Colombier 	c[1] = 0;
611113b9010SDavid du Colombier 	listsetup(pc, Lwrite);
612b1707c5dSDavid du Colombier 	if(ahciwait(pc, 150) == -1){
6133d56950aSDavid du Colombier 		dprint("ahcicomreset: second command failed\n");
614b1707c5dSDavid du Colombier 		return -1;
615b1707c5dSDavid du Colombier 	}
616b1707c5dSDavid du Colombier 	dreg("comreset ", pc->p);
617b1707c5dSDavid du Colombier 	return 0;
618b1707c5dSDavid du Colombier }
619b1707c5dSDavid du Colombier 
620b1707c5dSDavid du Colombier static int
ahciidle(Aport * port)621b1707c5dSDavid du Colombier ahciidle(Aport *port)
622b1707c5dSDavid du Colombier {
62391b330d9SDavid du Colombier 	ulong *p, i, r;
624b1707c5dSDavid du Colombier 
625b1707c5dSDavid du Colombier 	p = &port->cmd;
626b1707c5dSDavid du Colombier 	if((*p & Arun) == 0)
627b1707c5dSDavid du Colombier 		return 0;
628b1707c5dSDavid du Colombier 	*p &= ~Ast;
629b1707c5dSDavid du Colombier 	r = 0;
630b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 25){
631b1707c5dSDavid du Colombier 		if((*p & Acr) == 0)
632b1707c5dSDavid du Colombier 			goto stop;
633b1707c5dSDavid du Colombier 		asleep(25);
634b1707c5dSDavid du Colombier 	}
635b1707c5dSDavid du Colombier 	r = -1;
636b1707c5dSDavid du Colombier stop:
637b1707c5dSDavid du Colombier 	if((*p & Afre) == 0)
638b1707c5dSDavid du Colombier 		return r;
639b1707c5dSDavid du Colombier 	*p &= ~Afre;
640b1707c5dSDavid du Colombier 	for(i = 0; i < 500; i += 25){
641b1707c5dSDavid du Colombier 		if((*p & Afre) == 0)
642b1707c5dSDavid du Colombier 			return 0;
643b1707c5dSDavid du Colombier 		asleep(25);
644b1707c5dSDavid du Colombier 	}
645b1707c5dSDavid du Colombier 	return -1;
646b1707c5dSDavid du Colombier }
647b1707c5dSDavid du Colombier 
648b1707c5dSDavid du Colombier /*
649b1707c5dSDavid du Colombier  * § 6.2.2.1  first part; comreset handled by reset disk.
650b1707c5dSDavid du Colombier  *	- remainder is handled by configdisk.
651b1707c5dSDavid du Colombier  *	- ahcirecover is a quick recovery from a failed command.
652b1707c5dSDavid du Colombier  */
65391b330d9SDavid du Colombier static int
ahciswreset(Aportc * pc)654b1707c5dSDavid du Colombier ahciswreset(Aportc *pc)
655b1707c5dSDavid du Colombier {
656b1707c5dSDavid du Colombier 	int i;
657b1707c5dSDavid du Colombier 
658b1707c5dSDavid du Colombier 	i = ahciidle(pc->p);
659b1707c5dSDavid du Colombier 	pc->p->cmd |= Afre;
660b1707c5dSDavid du Colombier 	if(i == -1)
661b1707c5dSDavid du Colombier 		return -1;
662b1707c5dSDavid du Colombier 	if(pc->p->task & (ASdrq|ASbsy))
663b1707c5dSDavid du Colombier 		return -1;
664b1707c5dSDavid du Colombier 	return 0;
665b1707c5dSDavid du Colombier }
666b1707c5dSDavid du Colombier 
66791b330d9SDavid du Colombier static int
ahcirecover(Aportc * pc)668b1707c5dSDavid du Colombier ahcirecover(Aportc *pc)
669b1707c5dSDavid du Colombier {
670b1707c5dSDavid du Colombier 	ahciswreset(pc);
671b1707c5dSDavid du Colombier 	pc->p->cmd |= Ast;
672b1707c5dSDavid du Colombier 	if(setudmamode(pc, 5) == -1)
673b1707c5dSDavid du Colombier 		return -1;
674b1707c5dSDavid du Colombier 	return 0;
675b1707c5dSDavid du Colombier }
676b1707c5dSDavid du Colombier 
677b1707c5dSDavid du Colombier static void*
malign(int size,int align)678b1707c5dSDavid du Colombier malign(int size, int align)
679b1707c5dSDavid du Colombier {
680b1707c5dSDavid du Colombier 	void *v;
681b1707c5dSDavid du Colombier 
682b1707c5dSDavid du Colombier 	v = xspanalloc(size, align, 0);
683b1707c5dSDavid du Colombier 	memset(v, 0, size);
684b1707c5dSDavid du Colombier 	return v;
685b1707c5dSDavid du Colombier }
686b1707c5dSDavid du Colombier 
687b1707c5dSDavid du Colombier static void
setupfis(Afis * f)688b1707c5dSDavid du Colombier setupfis(Afis *f)
689b1707c5dSDavid du Colombier {
69055329247SDavid du Colombier 	f->base = malign(0x100, 0x100);		/* magic */
691b1707c5dSDavid du Colombier 	f->d = f->base + 0;
692b1707c5dSDavid du Colombier 	f->p = f->base + 0x20;
693b1707c5dSDavid du Colombier 	f->r = f->base + 0x40;
694b1707c5dSDavid du Colombier 	f->u = f->base + 0x60;
69591b330d9SDavid du Colombier 	f->devicebits = (ulong*)(f->base + 0x58);
696b1707c5dSDavid du Colombier }
697b1707c5dSDavid du Colombier 
698b1707c5dSDavid du Colombier static void
ahciwakeup(Aport * p)699b1707c5dSDavid du Colombier ahciwakeup(Aport *p)
700b1707c5dSDavid du Colombier {
701b1707c5dSDavid du Colombier 	ushort s;
702b1707c5dSDavid du Colombier 
703b1707c5dSDavid du Colombier 	s = p->sstatus;
70439b91b2bSDavid du Colombier 	if((s & Intpm) != Intslumber && (s & Intpm) != Intpartpwr)
705b1707c5dSDavid du Colombier 		return;
70639b91b2bSDavid du Colombier 	if((s & Devdet) != Devpresent){	/* not (device, no phy) */
707f0499952SDavid du Colombier 		iprint("ahci: slumbering drive unwakable %#ux\n", s);
708b1707c5dSDavid du Colombier 		return;
709b1707c5dSDavid du Colombier 	}
710b1707c5dSDavid du Colombier 	p->sctl = 3*Aipm | 0*Aspd | Adet;
711b1707c5dSDavid du Colombier 	delay(1);
712b1707c5dSDavid du Colombier 	p->sctl &= ~7;
713f0499952SDavid du Colombier //	iprint("ahci: wake %#ux -> %#ux\n", s, p->sstatus);
714b1707c5dSDavid du Colombier }
715b1707c5dSDavid du Colombier 
716b1707c5dSDavid du Colombier static int
ahciconfigdrive(Drive * d)71739b91b2bSDavid du Colombier ahciconfigdrive(Drive *d)
718b1707c5dSDavid du Colombier {
71939b91b2bSDavid du Colombier 	char *name;
72039b91b2bSDavid du Colombier 	Ahba *h;
721b1707c5dSDavid du Colombier 	Aport *p;
722060a30c6SDavid du Colombier 	Aportm *pm;
723b1707c5dSDavid du Colombier 
72439b91b2bSDavid du Colombier 	h = d->ctlr->hba;
72539b91b2bSDavid du Colombier 	p = d->portc.p;
726*58da3067SDavid du Colombier 	pm = d->portc.pm;
727060a30c6SDavid du Colombier 	if(pm->list == 0){
728060a30c6SDavid du Colombier 		setupfis(&pm->fis);
729060a30c6SDavid du Colombier 		pm->list = malign(sizeof *pm->list, 1024);
730060a30c6SDavid du Colombier 		pm->ctab = malign(sizeof *pm->ctab, 128);
731b1707c5dSDavid du Colombier 	}
732b1707c5dSDavid du Colombier 
73339b91b2bSDavid du Colombier 	if (d->unit)
73439b91b2bSDavid du Colombier 		name = d->unit->name;
73539b91b2bSDavid du Colombier 	else
73639b91b2bSDavid du Colombier 		name = nil;
73739b91b2bSDavid du Colombier 	if(p->sstatus & (Devphycomm|Devpresent) && h->cap & Hsss){
738b1707c5dSDavid du Colombier 		/* device connected & staggered spin-up */
73939b91b2bSDavid du Colombier 		dprint("ahci: configdrive: %s: spinning up ... [%#lux]\n",
74039b91b2bSDavid du Colombier 			name, p->sstatus);
741b1707c5dSDavid du Colombier 		p->cmd |= Apod|Asud;
742b1707c5dSDavid du Colombier 		asleep(1400);
743b1707c5dSDavid du Colombier 	}
744b1707c5dSDavid du Colombier 
745b1707c5dSDavid du Colombier 	p->serror = SerrAll;
746b1707c5dSDavid du Colombier 
747060a30c6SDavid du Colombier 	p->list = PCIWADDR(pm->list);
748b1707c5dSDavid du Colombier 	p->listhi = 0;
749060a30c6SDavid du Colombier 	p->fis = PCIWADDR(pm->fis.base);
750b1707c5dSDavid du Colombier 	p->fishi = 0;
751b1707c5dSDavid du Colombier 	p->cmd |= Afre|Ast;
752b1707c5dSDavid du Colombier 
75339b91b2bSDavid du Colombier 	/* drive coming up in slumbering? */
75439b91b2bSDavid du Colombier 	if((p->sstatus & Devdet) == Devpresent &&
75539b91b2bSDavid du Colombier 	   ((p->sstatus & Intpm) == Intslumber ||
75639b91b2bSDavid du Colombier 	    (p->sstatus & Intpm) == Intpartpwr))
757b1707c5dSDavid du Colombier 		ahciwakeup(p);
758b1707c5dSDavid du Colombier 
75939b91b2bSDavid du Colombier 	/* "disable power managment" sequence from book. */
76039b91b2bSDavid du Colombier 	p->sctl = (3*Aipm) | (d->mode*Aspd) | (0*Adet);
761b1707c5dSDavid du Colombier 	p->cmd &= ~Aalpe;
762b1707c5dSDavid du Colombier 
763b1707c5dSDavid du Colombier 	p->ie = IEM;
764b1707c5dSDavid du Colombier 
765b1707c5dSDavid du Colombier 	return 0;
766b1707c5dSDavid du Colombier }
767b1707c5dSDavid du Colombier 
76855329247SDavid du Colombier static void
ahcienable(Ahba * h)769b1707c5dSDavid du Colombier ahcienable(Ahba *h)
770b1707c5dSDavid du Colombier {
771b1707c5dSDavid du Colombier 	h->ghc |= Hie;
772b1707c5dSDavid du Colombier }
773b1707c5dSDavid du Colombier 
77455329247SDavid du Colombier static void
ahcidisable(Ahba * h)775b1707c5dSDavid du Colombier ahcidisable(Ahba *h)
776b1707c5dSDavid du Colombier {
777b1707c5dSDavid du Colombier 	h->ghc &= ~Hie;
778b1707c5dSDavid du Colombier }
779b1707c5dSDavid du Colombier 
780b1707c5dSDavid du Colombier static int
countbits(ulong u)781b1707c5dSDavid du Colombier countbits(ulong u)
782b1707c5dSDavid du Colombier {
78355329247SDavid du Colombier 	int n;
784b1707c5dSDavid du Colombier 
785b1707c5dSDavid du Colombier 	n = 0;
78655329247SDavid du Colombier 	for (; u != 0; u >>= 1)
78755329247SDavid du Colombier 		if(u & 1)
788b1707c5dSDavid du Colombier 			n++;
789b1707c5dSDavid du Colombier 	return n;
790b1707c5dSDavid du Colombier }
791b1707c5dSDavid du Colombier 
792b1707c5dSDavid du Colombier static int
ahciconf(Ctlr * ctlr)793b1707c5dSDavid du Colombier ahciconf(Ctlr *ctlr)
794b1707c5dSDavid du Colombier {
795b1707c5dSDavid du Colombier 	Ahba *h;
79691b330d9SDavid du Colombier 	ulong u;
797b1707c5dSDavid du Colombier 
798b1707c5dSDavid du Colombier 	h = ctlr->hba = (Ahba*)ctlr->mmio;
799b1707c5dSDavid du Colombier 	u = h->cap;
800b1707c5dSDavid du Colombier 
801b1707c5dSDavid du Colombier 	if((u&Hsam) == 0)
802b1707c5dSDavid du Colombier 		h->ghc |= Hae;
803b1707c5dSDavid du Colombier 
80475cb58dbSDavid du Colombier 	dprint("#S/sd%c: type %s port %#p: sss %ld ncs %ld coal %ld "
805b4124be8SDavid du Colombier 		"%ld ports, led %ld clo %ld ems %ld\n",
806014ad43fSDavid du Colombier 		ctlr->sdev->idno, tname[ctlr->type], h,
807b4124be8SDavid du Colombier 		(u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1,
808b4124be8SDavid du Colombier 		(u & 0x1f) + 1, (u>>25) & 1, (u>>24) & 1, (u>>6) & 1);
809b1707c5dSDavid du Colombier 	return countbits(h->pi);
810b1707c5dSDavid du Colombier }
811b1707c5dSDavid du Colombier 
812b1707c5dSDavid du Colombier static int
ahcihbareset(Ahba * h)813b1707c5dSDavid du Colombier ahcihbareset(Ahba *h)
814b1707c5dSDavid du Colombier {
815b1707c5dSDavid du Colombier 	int wait;
816b1707c5dSDavid du Colombier 
817b1707c5dSDavid du Colombier 	h->ghc |= 1;
818b1707c5dSDavid du Colombier 	for(wait = 0; wait < 1000; wait += 100){
819b1707c5dSDavid du Colombier 		if(h->ghc == 0)
820b1707c5dSDavid du Colombier 			return 0;
821b1707c5dSDavid du Colombier 		delay(100);
822b1707c5dSDavid du Colombier 	}
823b1707c5dSDavid du Colombier 	return -1;
824b1707c5dSDavid du Colombier }
825b1707c5dSDavid du Colombier 
826b1707c5dSDavid du Colombier static void
idmove(char * p,ushort * a,int n)827b1707c5dSDavid du Colombier idmove(char *p, ushort *a, int n)
828b1707c5dSDavid du Colombier {
829b1707c5dSDavid du Colombier 	int i;
830b1707c5dSDavid du Colombier 	char *op, *e;
831b1707c5dSDavid du Colombier 
832b1707c5dSDavid du Colombier 	op = p;
833b1707c5dSDavid du Colombier 	for(i = 0; i < n/2; i++){
834b1707c5dSDavid du Colombier 		*p++ = a[i] >> 8;
835b1707c5dSDavid du Colombier 		*p++ = a[i];
836b1707c5dSDavid du Colombier 	}
837b1707c5dSDavid du Colombier 	*p = 0;
838b1707c5dSDavid du Colombier 	while(p > op && *--p == ' ')
839b1707c5dSDavid du Colombier 		*p = 0;
840b1707c5dSDavid du Colombier 	e = p;
84191157df7SDavid du Colombier 	for (p = op; *p == ' '; p++)
84291157df7SDavid du Colombier 		;
843b1707c5dSDavid du Colombier 	memmove(op, p, n - (e - p));
844b1707c5dSDavid du Colombier }
845b1707c5dSDavid du Colombier 
846b1707c5dSDavid du Colombier static int
identify(Drive * d)847b1707c5dSDavid du Colombier identify(Drive *d)
848b1707c5dSDavid du Colombier {
84991b330d9SDavid du Colombier 	ushort *id;
850b1707c5dSDavid du Colombier 	vlong osectors, s;
851b1707c5dSDavid du Colombier 	uchar oserial[21];
852b1707c5dSDavid du Colombier 	SDunit *u;
853b1707c5dSDavid du Colombier 
85476f70593SDavid du Colombier 	if(d->info == nil) {
85576f70593SDavid du Colombier 		d->infosz = 512 * sizeof(ushort);
85676f70593SDavid du Colombier 		d->info = malloc(d->infosz);
85776f70593SDavid du Colombier 	}
85876f70593SDavid du Colombier 	if(d->info == nil) {
85976f70593SDavid du Colombier 		d->info = d->tinyinfo;
86076f70593SDavid du Colombier 		d->infosz = sizeof d->tinyinfo;
86176f70593SDavid du Colombier 	}
862b1707c5dSDavid du Colombier 	id = d->info;
863b1707c5dSDavid du Colombier 	s = ahciidentify(&d->portc, id);
864b1707c5dSDavid du Colombier 	if(s == -1){
865b1707c5dSDavid du Colombier 		d->state = Derror;
866b1707c5dSDavid du Colombier 		return -1;
867b1707c5dSDavid du Colombier 	}
868b1707c5dSDavid du Colombier 	osectors = d->sectors;
869b1707c5dSDavid du Colombier 	memmove(oserial, d->serial, sizeof d->serial);
870b1707c5dSDavid du Colombier 
87158db92f4SDavid du Colombier 	u = d->unit;
872b1707c5dSDavid du Colombier 	d->sectors = s;
87358db92f4SDavid du Colombier 	d->secsize = u->secsize;
87458db92f4SDavid du Colombier 	if(d->secsize == 0)
87558db92f4SDavid du Colombier 		d->secsize = 512;		/* default */
876b1707c5dSDavid du Colombier 	d->smartrs = 0;
877b1707c5dSDavid du Colombier 
878b1707c5dSDavid du Colombier 	idmove(d->serial, id+10, 20);
879b1707c5dSDavid du Colombier 	idmove(d->firmware, id+23, 8);
880b1707c5dSDavid du Colombier 	idmove(d->model, id+27, 40);
881b1707c5dSDavid du Colombier 
882b1707c5dSDavid du Colombier 	memset(u->inquiry, 0, sizeof u->inquiry);
883b1707c5dSDavid du Colombier 	u->inquiry[2] = 2;
884b1707c5dSDavid du Colombier 	u->inquiry[3] = 2;
885b1707c5dSDavid du Colombier 	u->inquiry[4] = sizeof u->inquiry - 4;
886b1707c5dSDavid du Colombier 	memmove(u->inquiry+8, d->model, 40);
887b1707c5dSDavid du Colombier 
88891157df7SDavid du Colombier 	if(osectors != s || memcmp(oserial, d->serial, sizeof oserial) != 0){
889b1707c5dSDavid du Colombier 		d->mediachange = 1;
890b1707c5dSDavid du Colombier 		u->sectors = 0;
891b1707c5dSDavid du Colombier 	}
892b1707c5dSDavid du Colombier 	return 0;
893b1707c5dSDavid du Colombier }
894b1707c5dSDavid du Colombier 
895b1707c5dSDavid du Colombier static void
clearci(Aport * p)896b1707c5dSDavid du Colombier clearci(Aport *p)
897b1707c5dSDavid du Colombier {
898b1707c5dSDavid du Colombier 	if(p->cmd & Ast) {
899b1707c5dSDavid du Colombier 		p->cmd &= ~Ast;
900b1707c5dSDavid du Colombier 		p->cmd |=  Ast;
901b1707c5dSDavid du Colombier 	}
902b1707c5dSDavid du Colombier }
903b1707c5dSDavid du Colombier 
904b1707c5dSDavid du Colombier static void
updatedrive(Drive * d)905b1707c5dSDavid du Colombier updatedrive(Drive *d)
906b1707c5dSDavid du Colombier {
90791b330d9SDavid du Colombier 	ulong cause, serr, s0, pr, ewake;
908b1707c5dSDavid du Colombier 	char *name;
909b1707c5dSDavid du Colombier 	Aport *p;
91091b330d9SDavid du Colombier 	static ulong last;
911b1707c5dSDavid du Colombier 
912b1707c5dSDavid du Colombier 	pr = 1;
913b1707c5dSDavid du Colombier 	ewake = 0;
914b1707c5dSDavid du Colombier 	p = d->port;
915b1707c5dSDavid du Colombier 	cause = p->isr;
916b1707c5dSDavid du Colombier 	serr = p->serror;
917b1707c5dSDavid du Colombier 	p->isr = cause;
918b1707c5dSDavid du Colombier 	name = "??";
919b1707c5dSDavid du Colombier 	if(d->unit && d->unit->name)
920b1707c5dSDavid du Colombier 		name = d->unit->name;
921b1707c5dSDavid du Colombier 
922b1707c5dSDavid du Colombier 	if(p->ci == 0){
923b1707c5dSDavid du Colombier 		d->portm.flag |= Fdone;
924b1707c5dSDavid du Colombier 		wakeup(&d->portm);
925b1707c5dSDavid du Colombier 		pr = 0;
926b1707c5dSDavid du Colombier 	}else if(cause & Adps)
927b1707c5dSDavid du Colombier 		pr = 0;
928b1707c5dSDavid du Colombier 	if(cause & Ifatal){
929b1707c5dSDavid du Colombier 		ewake = 1;
93039b91b2bSDavid du Colombier 		dprint("ahci: updatedrive: %s: fatal\n", name);
931b1707c5dSDavid du Colombier 	}
932b1707c5dSDavid du Colombier 	if(cause & Adhrs){
93391157df7SDavid du Colombier 		if(p->task & (1<<5|1)){
93439b91b2bSDavid du Colombier 			dprint("ahci: %s: Adhrs cause %#lux serr %#lux task %#lux\n",
93539b91b2bSDavid du Colombier 				name, cause, serr, p->task);
936b1707c5dSDavid du Colombier 			d->portm.flag |= Ferror;
937b1707c5dSDavid du Colombier 			ewake = 1;
938b1707c5dSDavid du Colombier 		}
939b1707c5dSDavid du Colombier 		pr = 0;
940b1707c5dSDavid du Colombier 	}
941b1707c5dSDavid du Colombier 	if(p->task & 1 && last != cause)
942f0499952SDavid du Colombier 		dprint("%s: err ca %#lux serr %#lux task %#lux sstat %#lux\n",
9433d56950aSDavid du Colombier 			name, cause, serr, p->task, p->sstatus);
944b1707c5dSDavid du Colombier 	if(pr)
945f0499952SDavid du Colombier 		dprint("%s: upd %#lux ta %#lux\n", name, cause, p->task);
946b1707c5dSDavid du Colombier 
947b1707c5dSDavid du Colombier 	if(cause & (Aprcs|Aifs)){
948b1707c5dSDavid du Colombier 		s0 = d->state;
94939b91b2bSDavid du Colombier 		switch(p->sstatus & Devdet){
950b1707c5dSDavid du Colombier 		case 0:				/* no device */
951b1707c5dSDavid du Colombier 			d->state = Dmissing;
952b1707c5dSDavid du Colombier 			break;
95339b91b2bSDavid du Colombier 		case Devpresent:		/* device but no phy comm. */
95439b91b2bSDavid du Colombier 			if((p->sstatus & Intpm) == Intslumber ||
95539b91b2bSDavid du Colombier 			   (p->sstatus & Intpm) == Intpartpwr)
956b1707c5dSDavid du Colombier 				d->state = Dnew;	/* slumbering */
957b1707c5dSDavid du Colombier 			else
958b1707c5dSDavid du Colombier 				d->state = Derror;
959b1707c5dSDavid du Colombier 			break;
96039b91b2bSDavid du Colombier 		case Devpresent|Devphycomm:
96191157df7SDavid du Colombier 			/* power mgnt crap for surprise removal */
962b1707c5dSDavid du Colombier 			p->ie |= Aprcs|Apcs;	/* is this required? */
963b1707c5dSDavid du Colombier 			d->state = Dreset;
964b1707c5dSDavid du Colombier 			break;
96539b91b2bSDavid du Colombier 		case Devphyoffline:
966b1707c5dSDavid du Colombier 			d->state = Doffline;
967b1707c5dSDavid du Colombier 			break;
968b1707c5dSDavid du Colombier 		}
969f0499952SDavid du Colombier 		dprint("%s: %s → %s [Apcrs] %#lux\n", name,
9703d56950aSDavid du Colombier 			diskstates[s0], diskstates[d->state], p->sstatus);
971b1707c5dSDavid du Colombier 		/* print pulled message here. */
972b1707c5dSDavid du Colombier 		if(s0 == Dready && d->state != Dready)
973*58da3067SDavid du Colombier 			idprint("%s: pulled\n", name);		/* wtf? */
974b1707c5dSDavid du Colombier 		if(d->state != Dready)
975b1707c5dSDavid du Colombier 			d->portm.flag |= Ferror;
976b1707c5dSDavid du Colombier 		ewake = 1;
977b1707c5dSDavid du Colombier 	}
978b1707c5dSDavid du Colombier 	p->serror = serr;
979b1707c5dSDavid du Colombier 	if(ewake){
980b1707c5dSDavid du Colombier 		clearci(p);
981b1707c5dSDavid du Colombier 		wakeup(&d->portm);
982b1707c5dSDavid du Colombier 	}
983b1707c5dSDavid du Colombier 	last = cause;
984b1707c5dSDavid du Colombier }
985b1707c5dSDavid du Colombier 
986b1707c5dSDavid du Colombier static void
pstatus(Drive * d,ulong s)987b1707c5dSDavid du Colombier pstatus(Drive *d, ulong s)
988b1707c5dSDavid du Colombier {
989b1707c5dSDavid du Colombier 	/*
99039b91b2bSDavid du Colombier 	 * s is masked with Devdet.
99139b91b2bSDavid du Colombier 	 *
992b1707c5dSDavid du Colombier 	 * bogus code because the first interrupt is currently dropped.
99391157df7SDavid du Colombier 	 * likely my fault.  serror may be cleared at the wrong time.
994b1707c5dSDavid du Colombier 	 */
995b1707c5dSDavid du Colombier 	switch(s){
996b1707c5dSDavid du Colombier 	case 0:			/* no device */
997b1707c5dSDavid du Colombier 		d->state = Dmissing;
998b1707c5dSDavid du Colombier 		break;
99939b91b2bSDavid du Colombier 	case Devpresent:	/* device but no phy. comm. */
1000b1707c5dSDavid du Colombier 		break;
100139b91b2bSDavid du Colombier 	case Devphycomm:	/* should this be missing?  need testcase. */
10023d56950aSDavid du Colombier 		dprint("ahci: pstatus 2\n");
1003b1707c5dSDavid du Colombier 		/* fallthrough */
100439b91b2bSDavid du Colombier 	case Devpresent|Devphycomm:
1005b1707c5dSDavid du Colombier 		d->wait = 0;
1006b1707c5dSDavid du Colombier 		d->state = Dnew;
1007b1707c5dSDavid du Colombier 		break;
100839b91b2bSDavid du Colombier 	case Devphyoffline:
1009b1707c5dSDavid du Colombier 		d->state = Doffline;
1010b1707c5dSDavid du Colombier 		break;
101139b91b2bSDavid du Colombier 	case Devphyoffline|Devphycomm:	/* does this make sense? */
1012b1707c5dSDavid du Colombier 		d->state = Dnew;
1013b1707c5dSDavid du Colombier 		break;
1014b1707c5dSDavid du Colombier 	}
1015b1707c5dSDavid du Colombier }
1016b1707c5dSDavid du Colombier 
1017b1707c5dSDavid du Colombier static int
configdrive(Drive * d)1018b1707c5dSDavid du Colombier configdrive(Drive *d)
1019b1707c5dSDavid du Colombier {
102039b91b2bSDavid du Colombier 	if(ahciconfigdrive(d) == -1)
1021b1707c5dSDavid du Colombier 		return -1;
1022b1707c5dSDavid du Colombier 	ilock(d);
102339b91b2bSDavid du Colombier 	pstatus(d, d->port->sstatus & Devdet);
1024b1707c5dSDavid du Colombier 	iunlock(d);
1025b1707c5dSDavid du Colombier 	return 0;
1026b1707c5dSDavid du Colombier }
1027b1707c5dSDavid du Colombier 
1028b1707c5dSDavid du Colombier static void
setstate(Drive * d,int state)1029aec4d1fcSDavid du Colombier setstate(Drive *d, int state)
1030aec4d1fcSDavid du Colombier {
1031aec4d1fcSDavid du Colombier 	ilock(d);
1032aec4d1fcSDavid du Colombier 	d->state = state;
1033aec4d1fcSDavid du Colombier 	iunlock(d);
1034aec4d1fcSDavid du Colombier }
1035aec4d1fcSDavid du Colombier 
1036aec4d1fcSDavid du Colombier static void
resetdisk(Drive * d)1037b1707c5dSDavid du Colombier resetdisk(Drive *d)
1038b1707c5dSDavid du Colombier {
1039b1707c5dSDavid du Colombier 	uint state, det, stat;
1040b1707c5dSDavid du Colombier 	Aport *p;
1041b1707c5dSDavid du Colombier 
1042b1707c5dSDavid du Colombier 	p = d->port;
1043b1707c5dSDavid du Colombier 	det = p->sctl & 7;
104439b91b2bSDavid du Colombier 	stat = p->sstatus & Devdet;
1045b1707c5dSDavid du Colombier 	state = (p->cmd>>28) & 0xf;
1046f0499952SDavid du Colombier 	dprint("ahci: resetdisk: icc %#ux  det %d sdet %d\n", state, det, stat);
1047014ad43fSDavid du Colombier 
1048b1707c5dSDavid du Colombier 	ilock(d);
1049b1707c5dSDavid du Colombier 	state = d->state;
1050b1707c5dSDavid du Colombier 	if(d->state != Dready || d->state != Dnew)
1051b1707c5dSDavid du Colombier 		d->portm.flag |= Ferror;
1052b1707c5dSDavid du Colombier 	clearci(p);			/* satisfy sleep condition. */
1053b1707c5dSDavid du Colombier 	wakeup(&d->portm);
105439b91b2bSDavid du Colombier 	if(stat != (Devpresent|Devphycomm)){
105539b91b2bSDavid du Colombier 		/* device absent or phy not communicating */
1056014ad43fSDavid du Colombier 		d->state = Dportreset;
1057014ad43fSDavid du Colombier 		iunlock(d);
1058014ad43fSDavid du Colombier 		return;
1059014ad43fSDavid du Colombier 	}
106081ede731SDavid du Colombier 	d->state = Derror;
1061b1707c5dSDavid du Colombier 	iunlock(d);
1062b1707c5dSDavid du Colombier 
1063b1707c5dSDavid du Colombier 	qlock(&d->portm);
1064aec4d1fcSDavid du Colombier 	if(p->cmd&Ast && ahciswreset(&d->portc) == -1)
1065aec4d1fcSDavid du Colombier 		setstate(d, Dportreset);	/* get a bigger stick. */
1066aec4d1fcSDavid du Colombier 	else {
1067aec4d1fcSDavid du Colombier 		setstate(d, Dmissing);
1068b1707c5dSDavid du Colombier 		configdrive(d);
1069b1707c5dSDavid du Colombier 	}
107039b91b2bSDavid du Colombier 	dprint("ahci: %s: resetdisk: %s → %s\n", (d->unit? d->unit->name: nil),
10713d56950aSDavid du Colombier 		diskstates[state], diskstates[d->state]);
1072b1707c5dSDavid du Colombier 	qunlock(&d->portm);
1073b1707c5dSDavid du Colombier }
1074b1707c5dSDavid du Colombier 
1075b1707c5dSDavid du Colombier static int
newdrive(Drive * d)1076b1707c5dSDavid du Colombier newdrive(Drive *d)
1077b1707c5dSDavid du Colombier {
107858db92f4SDavid du Colombier 	char *name;
1079b1707c5dSDavid du Colombier 	Aportc *c;
1080060a30c6SDavid du Colombier 	Aportm *pm;
1081b1707c5dSDavid du Colombier 
1082b1707c5dSDavid du Colombier 	c = &d->portc;
1083060a30c6SDavid du Colombier 	pm = &d->portm;
1084b1707c5dSDavid du Colombier 
1085b1707c5dSDavid du Colombier 	name = d->unit->name;
1086b1707c5dSDavid du Colombier 	if(name == 0)
1087b1707c5dSDavid du Colombier 		name = "??";
1088b1707c5dSDavid du Colombier 
1089b1707c5dSDavid du Colombier 	if(d->port->task == 0x80)
1090b1707c5dSDavid du Colombier 		return -1;
1091*58da3067SDavid du Colombier 	qlock(c->pm);
1092b1707c5dSDavid du Colombier 	if(setudmamode(c, 5) == -1){
1093b1707c5dSDavid du Colombier 		dprint("%s: can't set udma mode\n", name);
1094b1707c5dSDavid du Colombier 		goto lose;
1095b1707c5dSDavid du Colombier 	}
1096b1707c5dSDavid du Colombier 	if(identify(d) == -1){
1097b1707c5dSDavid du Colombier 		dprint("%s: identify failure\n", name);
1098b1707c5dSDavid du Colombier 		goto lose;
1099b1707c5dSDavid du Colombier 	}
1100060a30c6SDavid du Colombier 	if(pm->feat & Dpower && setfeatures(c, 0x85) == -1){
1101060a30c6SDavid du Colombier 		pm->feat &= ~Dpower;
1102b1707c5dSDavid du Colombier 		if(ahcirecover(c) == -1)
1103b1707c5dSDavid du Colombier 			goto lose;
1104b1707c5dSDavid du Colombier 	}
1105aec4d1fcSDavid du Colombier 	setstate(d, Dready);
1106*58da3067SDavid du Colombier 	qunlock(c->pm);
1107b1707c5dSDavid du Colombier 
110858db92f4SDavid du Colombier 	idprint("%s: %sLBA %,llud sectors: %s %s %s %s\n", d->unit->name,
1109060a30c6SDavid du Colombier 		(pm->feat & Dllba? "L": ""), d->sectors, d->model, d->firmware,
111058db92f4SDavid du Colombier 		d->serial, d->mediachange? "[mediachange]": "");
1111b1707c5dSDavid du Colombier 	return 0;
1112b1707c5dSDavid du Colombier 
1113b1707c5dSDavid du Colombier lose:
111481ede731SDavid du Colombier 	idprint("%s: can't be initialized\n", d->unit->name);
1115aec4d1fcSDavid du Colombier 	setstate(d, Dnull);
1116*58da3067SDavid du Colombier 	qunlock(c->pm);
1117b1707c5dSDavid du Colombier 	return -1;
1118b1707c5dSDavid du Colombier }
1119b1707c5dSDavid du Colombier 
1120b1707c5dSDavid du Colombier static void
westerndigitalhung(Drive * d)1121b1707c5dSDavid du Colombier westerndigitalhung(Drive *d)
1122b1707c5dSDavid du Colombier {
1123f3112f79SDavid du Colombier 	if((d->portm.feat&Datapi) == 0 && d->active &&
1124f3112f79SDavid du Colombier 	    TK2MS(MACHP(0)->ticks - d->intick) > 5000){
1125f0499952SDavid du Colombier 		dprint("%s: drive hung; resetting [%#lux] ci %#lx\n",
1126b1707c5dSDavid du Colombier 			d->unit->name, d->port->task, d->port->ci);
1127b1707c5dSDavid du Colombier 		d->state = Dreset;
1128b1707c5dSDavid du Colombier 	}
1129b1707c5dSDavid du Colombier }
1130b1707c5dSDavid du Colombier 
1131b1707c5dSDavid du Colombier static ushort olds[NCtlr*NCtlrdrv];
1132b1707c5dSDavid du Colombier 
1133b1707c5dSDavid du Colombier static int
doportreset(Drive * d)1134b1707c5dSDavid du Colombier doportreset(Drive *d)
1135b1707c5dSDavid du Colombier {
1136b1707c5dSDavid du Colombier 	int i;
1137b1707c5dSDavid du Colombier 
1138b1707c5dSDavid du Colombier 	i = -1;
1139b1707c5dSDavid du Colombier 	qlock(&d->portm);
1140b1707c5dSDavid du Colombier 	if(ahciportreset(&d->portc) == -1)
11413d56950aSDavid du Colombier 		dprint("ahci: doportreset: fails\n");
1142b1707c5dSDavid du Colombier 	else
1143b1707c5dSDavid du Colombier 		i = 0;
1144b1707c5dSDavid du Colombier 	qunlock(&d->portm);
1145f0499952SDavid du Colombier 	dprint("ahci: doportreset: portreset → %s  [task %#lux]\n",
1146b1707c5dSDavid du Colombier 		diskstates[d->state], d->port->task);
1147b1707c5dSDavid du Colombier 	return i;
1148b1707c5dSDavid du Colombier }
1149b1707c5dSDavid du Colombier 
1150014ad43fSDavid du Colombier /* drive must be locked */
1151014ad43fSDavid du Colombier static void
statechange(Drive * d)1152014ad43fSDavid du Colombier statechange(Drive *d)
1153014ad43fSDavid du Colombier {
1154014ad43fSDavid du Colombier 	switch(d->state){
1155014ad43fSDavid du Colombier 	case Dnull:
1156014ad43fSDavid du Colombier 	case Doffline:
1157014ad43fSDavid du Colombier 		if(d->unit->sectors != 0){
1158014ad43fSDavid du Colombier 			d->sectors = 0;
1159014ad43fSDavid du Colombier 			d->mediachange = 1;
1160014ad43fSDavid du Colombier 		}
1161014ad43fSDavid du Colombier 		/* fallthrough */
1162014ad43fSDavid du Colombier 	case Dready:
1163014ad43fSDavid du Colombier 		d->wait = 0;
1164014ad43fSDavid du Colombier 		break;
1165014ad43fSDavid du Colombier 	}
1166014ad43fSDavid du Colombier }
1167014ad43fSDavid du Colombier 
1168b1707c5dSDavid du Colombier static void
checkdrive(Drive * d,int i)1169b1707c5dSDavid du Colombier checkdrive(Drive *d, int i)
1170b1707c5dSDavid du Colombier {
1171b1707c5dSDavid du Colombier 	ushort s;
1172b1707c5dSDavid du Colombier 	char *name;
1173b1707c5dSDavid du Colombier 
1174c39c2eb3SDavid du Colombier 	if(d == nil) {
1175c39c2eb3SDavid du Colombier 		print("checkdrive: nil d\n");
1176c39c2eb3SDavid du Colombier 		return;
1177c39c2eb3SDavid du Colombier 	}
1178b1707c5dSDavid du Colombier 	ilock(d);
1179c39c2eb3SDavid du Colombier 	if(d->unit == nil || d->port == nil) {
1180c39c2eb3SDavid du Colombier 		if(0)
1181c39c2eb3SDavid du Colombier 			print("checkdrive: nil d->%s\n",
1182c39c2eb3SDavid du Colombier 				d->unit == nil? "unit": "port");
1183c39c2eb3SDavid du Colombier 		iunlock(d);
1184c39c2eb3SDavid du Colombier 		return;
1185c39c2eb3SDavid du Colombier 	}
1186b1707c5dSDavid du Colombier 	name = d->unit->name;
1187b1707c5dSDavid du Colombier 	s = d->port->sstatus;
118881ede731SDavid du Colombier 	if(s)
118981ede731SDavid du Colombier 		d->lastseen = MACHP(0)->ticks;
1190b1707c5dSDavid du Colombier 	if(s != olds[i]){
119139b91b2bSDavid du Colombier 		dprint("%s: status: %06#ux -> %06#ux: %s\n",
1192b1707c5dSDavid du Colombier 			name, olds[i], s, diskstates[d->state]);
1193b1707c5dSDavid du Colombier 		olds[i] = s;
1194b1707c5dSDavid du Colombier 		d->wait = 0;
1195b1707c5dSDavid du Colombier 	}
1196b1707c5dSDavid du Colombier 	westerndigitalhung(d);
119755329247SDavid du Colombier 
1198b1707c5dSDavid du Colombier 	switch(d->state){
1199b1707c5dSDavid du Colombier 	case Dnull:
1200b1707c5dSDavid du Colombier 	case Dready:
1201b1707c5dSDavid du Colombier 		break;
1202b1707c5dSDavid du Colombier 	case Dmissing:
1203b1707c5dSDavid du Colombier 	case Dnew:
120439b91b2bSDavid du Colombier 		switch(s & (Intactive | Devdet)){
120539b91b2bSDavid du Colombier 		case Devpresent:  /* no device (pm), device but no phy. comm. */
1206b1707c5dSDavid du Colombier 			ahciwakeup(d->port);
1207b1707c5dSDavid du Colombier 			/* fall through */
1208b1707c5dSDavid du Colombier 		case 0:			/* no device */
1209b1707c5dSDavid du Colombier 			break;
1210b1707c5dSDavid du Colombier 		default:
121139b91b2bSDavid du Colombier 			dprint("%s: unknown status %06#ux\n", name, s);
1212b1707c5dSDavid du Colombier 			/* fall through */
121339b91b2bSDavid du Colombier 		case Intactive:		/* active, no device */
1214b1707c5dSDavid du Colombier 			if(++d->wait&Mphywait)
1215b1707c5dSDavid du Colombier 				break;
1216b1707c5dSDavid du Colombier reset:
1217b1707c5dSDavid du Colombier 			if(++d->mode > DMsataii)
1218b1707c5dSDavid du Colombier 				d->mode = 0;
1219b1707c5dSDavid du Colombier 			if(d->mode == DMsatai){	/* we tried everything */
1220b1707c5dSDavid du Colombier 				d->state = Dportreset;
1221b1707c5dSDavid du Colombier 				goto portreset;
1222b1707c5dSDavid du Colombier 			}
1223b1707c5dSDavid du Colombier 			dprint("%s: reset; new mode %s\n", name,
1224b1707c5dSDavid du Colombier 				modename[d->mode]);
1225b1707c5dSDavid du Colombier 			iunlock(d);
1226b1707c5dSDavid du Colombier 			resetdisk(d);
1227b1707c5dSDavid du Colombier 			ilock(d);
1228b1707c5dSDavid du Colombier 			break;
122939b91b2bSDavid du Colombier 		case Intactive|Devphycomm|Devpresent:
1230b1707c5dSDavid du Colombier 			if((++d->wait&Midwait) == 0){
123139b91b2bSDavid du Colombier 				dprint("%s: slow reset %06#ux task=%#lux; %d\n",
1232b1707c5dSDavid du Colombier 					name, s, d->port->task, d->wait);
1233b1707c5dSDavid du Colombier 				goto reset;
1234b1707c5dSDavid du Colombier 			}
1235b1707c5dSDavid du Colombier 			s = (uchar)d->port->task;
1236b1707c5dSDavid du Colombier 			if(s == 0x7f || ((d->port->sig >> 16) != 0xeb14 &&
1237b1707c5dSDavid du Colombier 			    (s & ~0x17) != (1<<6)))
1238b1707c5dSDavid du Colombier 				break;
1239b1707c5dSDavid du Colombier 			iunlock(d);
1240b1707c5dSDavid du Colombier 			newdrive(d);
1241b1707c5dSDavid du Colombier 			ilock(d);
1242b1707c5dSDavid du Colombier 			break;
1243b1707c5dSDavid du Colombier 		}
1244b1707c5dSDavid du Colombier 		break;
1245b1707c5dSDavid du Colombier 	case Doffline:
1246b1707c5dSDavid du Colombier 		if(d->wait++ & Mcomrwait)
1247b1707c5dSDavid du Colombier 			break;
1248b1707c5dSDavid du Colombier 		/* fallthrough */
1249b1707c5dSDavid du Colombier 	case Derror:
1250b1707c5dSDavid du Colombier 	case Dreset:
125139b91b2bSDavid du Colombier 		dprint("%s: reset [%s]: mode %d; status %06#ux\n",
1252b1707c5dSDavid du Colombier 			name, diskstates[d->state], d->mode, s);
1253b1707c5dSDavid du Colombier 		iunlock(d);
1254b1707c5dSDavid du Colombier 		resetdisk(d);
1255b1707c5dSDavid du Colombier 		ilock(d);
1256b1707c5dSDavid du Colombier 		break;
1257b1707c5dSDavid du Colombier 	case Dportreset:
1258b1707c5dSDavid du Colombier portreset:
125955329247SDavid du Colombier 		if(d->wait++ & 0xff && (s & Intactive) == 0)
1260b1707c5dSDavid du Colombier 			break;
1261b1707c5dSDavid du Colombier 		/* device is active */
126239b91b2bSDavid du Colombier 		dprint("%s: portreset [%s]: mode %d; status %06#ux\n",
1263b1707c5dSDavid du Colombier 			name, diskstates[d->state], d->mode, s);
1264b1707c5dSDavid du Colombier 		d->portm.flag |= Ferror;
1265b1707c5dSDavid du Colombier 		clearci(d->port);
1266b1707c5dSDavid du Colombier 		wakeup(&d->portm);
126755329247SDavid du Colombier 		if((s & Devdet) == 0){	/* no device */
1268b1707c5dSDavid du Colombier 			d->state = Dmissing;
1269b1707c5dSDavid du Colombier 			break;
1270b1707c5dSDavid du Colombier 		}
1271b1707c5dSDavid du Colombier 		iunlock(d);
1272b1707c5dSDavid du Colombier 		doportreset(d);
1273b1707c5dSDavid du Colombier 		ilock(d);
1274b1707c5dSDavid du Colombier 		break;
1275b1707c5dSDavid du Colombier 	}
1276014ad43fSDavid du Colombier 	statechange(d);
1277b1707c5dSDavid du Colombier 	iunlock(d);
1278b1707c5dSDavid du Colombier }
1279b1707c5dSDavid du Colombier 
1280b1707c5dSDavid du Colombier static void
satakproc(void *)1281b1707c5dSDavid du Colombier satakproc(void*)
1282b1707c5dSDavid du Colombier {
1283b1707c5dSDavid du Colombier 	int i;
1284b1707c5dSDavid du Colombier 
1285b1707c5dSDavid du Colombier 	for(;;){
1286b1707c5dSDavid du Colombier 		tsleep(&up->sleep, return0, 0, Nms);
1287b1707c5dSDavid du Colombier 		for(i = 0; i < niadrive; i++)
128876f70593SDavid du Colombier 			if(iadrive[i] != nil)
1289b1707c5dSDavid du Colombier 				checkdrive(iadrive[i], i);
1290b1707c5dSDavid du Colombier 	}
1291b1707c5dSDavid du Colombier }
1292b1707c5dSDavid du Colombier 
1293b1707c5dSDavid du Colombier static void
isctlrjabbering(Ctlr * c,ulong cause)1294b4124be8SDavid du Colombier isctlrjabbering(Ctlr *c, ulong cause)
1295b4124be8SDavid du Colombier {
1296b4124be8SDavid du Colombier 	ulong now;
1297b4124be8SDavid du Colombier 
1298b4124be8SDavid du Colombier 	now = TK2MS(MACHP(0)->ticks);
1299b4124be8SDavid du Colombier 	if (now > c->lastintr0) {
1300b4124be8SDavid du Colombier 		c->intrs = 0;
1301b4124be8SDavid du Colombier 		c->lastintr0 = now;
1302b4124be8SDavid du Colombier 	}
1303aec4d1fcSDavid du Colombier 	if (++c->intrs > Maxintrspertick) {
1304aec4d1fcSDavid du Colombier 		iprint("sdiahci: %lud intrs per tick for no serviced "
1305aec4d1fcSDavid du Colombier 			"drive; cause %#lux mport %d\n",
1306aec4d1fcSDavid du Colombier 			c->intrs, cause, c->mport);
1307aec4d1fcSDavid du Colombier 		c->intrs = 0;
1308aec4d1fcSDavid du Colombier 	}
1309b4124be8SDavid du Colombier }
1310b4124be8SDavid du Colombier 
1311b4124be8SDavid du Colombier static void
isdrivejabbering(Drive * d)1312b4124be8SDavid du Colombier isdrivejabbering(Drive *d)
1313b4124be8SDavid du Colombier {
1314b4124be8SDavid du Colombier 	ulong now;
1315b4124be8SDavid du Colombier 
1316b4124be8SDavid du Colombier 	now = TK2MS(MACHP(0)->ticks);
1317b4124be8SDavid du Colombier 	if (now > d->lastintr0) {
1318b4124be8SDavid du Colombier 		d->intrs = 0;
1319b4124be8SDavid du Colombier 		d->lastintr0 = now;
1320b4124be8SDavid du Colombier 	}
1321aec4d1fcSDavid du Colombier 	if (++d->intrs > Maxintrspertick) {
1322aec4d1fcSDavid du Colombier 		iprint("sdiahci: %lud interrupts per tick for %s\n",
1323aec4d1fcSDavid du Colombier 			d->intrs, d->unit->name);
1324aec4d1fcSDavid du Colombier 		d->intrs = 0;
1325aec4d1fcSDavid du Colombier 	}
1326b4124be8SDavid du Colombier }
1327b4124be8SDavid du Colombier 
1328b4124be8SDavid du Colombier static void
iainterrupt(Ureg *,void * a)1329b1707c5dSDavid du Colombier iainterrupt(Ureg*, void *a)
1330b1707c5dSDavid du Colombier {
1331b1707c5dSDavid du Colombier 	int i;
1332060a30c6SDavid du Colombier 	ulong cause, mask;
1333b1707c5dSDavid du Colombier 	Ctlr *c;
1334b1707c5dSDavid du Colombier 	Drive *d;
1335b1707c5dSDavid du Colombier 
1336b1707c5dSDavid du Colombier 	c = a;
1337b1707c5dSDavid du Colombier 	ilock(c);
1338b1707c5dSDavid du Colombier 	cause = c->hba->isr;
1339b4124be8SDavid du Colombier 	if (cause == 0) {
1340b4124be8SDavid du Colombier 		isctlrjabbering(c, cause);
1341abfa367dSDavid du Colombier 		// iprint("sdiahci: interrupt for no drive\n");
1342b4124be8SDavid du Colombier 		iunlock(c);
1343b4124be8SDavid du Colombier 		return;
1344b4124be8SDavid du Colombier 	}
1345b4124be8SDavid du Colombier 	for(i = 0; cause && i <= c->mport; i++){
1346060a30c6SDavid du Colombier 		mask = 1 << i;
1347060a30c6SDavid du Colombier 		if((cause & mask) == 0)
1348b1707c5dSDavid du Colombier 			continue;
1349b1707c5dSDavid du Colombier 		d = c->rawdrive + i;
1350b1707c5dSDavid du Colombier 		ilock(d);
1351b4124be8SDavid du Colombier 		isdrivejabbering(d);
1352060a30c6SDavid du Colombier 		if(d->port->isr && c->hba->pi & mask)
1353b1707c5dSDavid du Colombier 			updatedrive(d);
1354060a30c6SDavid du Colombier 		c->hba->isr = mask;
1355b1707c5dSDavid du Colombier 		iunlock(d);
1356b4124be8SDavid du Colombier 
1357060a30c6SDavid du Colombier 		cause &= ~mask;
1358b4124be8SDavid du Colombier 	}
1359b4124be8SDavid du Colombier 	if (cause) {
1360b4124be8SDavid du Colombier 		isctlrjabbering(c, cause);
1361b4124be8SDavid du Colombier 		iprint("sdiachi: intr cause unserviced: %#lux\n", cause);
1362b1707c5dSDavid du Colombier 	}
1363b1707c5dSDavid du Colombier 	iunlock(c);
1364b1707c5dSDavid du Colombier }
1365b1707c5dSDavid du Colombier 
136655329247SDavid du Colombier /* checkdrive, called from satakproc, will prod the drive while we wait */
136755329247SDavid du Colombier static void
awaitspinup(Drive * d)136855329247SDavid du Colombier awaitspinup(Drive *d)
136955329247SDavid du Colombier {
137055329247SDavid du Colombier 	int ms;
137155329247SDavid du Colombier 	ushort s;
137255329247SDavid du Colombier 	char *name;
137355329247SDavid du Colombier 
137455329247SDavid du Colombier 	ilock(d);
137555329247SDavid du Colombier 	if(d->unit == nil || d->port == nil) {
137655329247SDavid du Colombier 		panic("awaitspinup: nil d->unit or d->port");
137755329247SDavid du Colombier 		iunlock(d);
137855329247SDavid du Colombier 		return;
137955329247SDavid du Colombier 	}
138055329247SDavid du Colombier 	name = (d->unit? d->unit->name: nil);
138155329247SDavid du Colombier 	s = d->port->sstatus;
138255329247SDavid du Colombier 	if(!(s & Devpresent)) {			/* never going to be ready */
138355329247SDavid du Colombier 		dprint("awaitspinup: %s absent, not waiting\n", name);
138455329247SDavid du Colombier 		iunlock(d);
138555329247SDavid du Colombier 		return;
138655329247SDavid du Colombier 	}
138755329247SDavid du Colombier 
138855329247SDavid du Colombier 	for (ms = 20000; ms > 0; ms -= 50)
138955329247SDavid du Colombier 		switch(d->state){
139055329247SDavid du Colombier 		case Dnull:
139155329247SDavid du Colombier 			/* absent; done */
139255329247SDavid du Colombier 			iunlock(d);
139355329247SDavid du Colombier 			dprint("awaitspinup: %s in null state\n", name);
139455329247SDavid du Colombier 			return;
139555329247SDavid du Colombier 		case Dready:
139655329247SDavid du Colombier 		case Dnew:
139755329247SDavid du Colombier 			if(d->sectors || d->mediachange) {
139855329247SDavid du Colombier 				/* ready to use; done */
139955329247SDavid du Colombier 				iunlock(d);
140055329247SDavid du Colombier 				dprint("awaitspinup: %s ready!\n", name);
140155329247SDavid du Colombier 				return;
140255329247SDavid du Colombier 			}
140355329247SDavid du Colombier 			/* fall through */
140455329247SDavid du Colombier 		default:
140555329247SDavid du Colombier 		case Dmissing:			/* normal waiting states */
140655329247SDavid du Colombier 		case Dreset:
140755329247SDavid du Colombier 		case Doffline:			/* transitional states */
140855329247SDavid du Colombier 		case Derror:
140955329247SDavid du Colombier 		case Dportreset:
141055329247SDavid du Colombier 			iunlock(d);
141155329247SDavid du Colombier 			asleep(50);
141255329247SDavid du Colombier 			ilock(d);
141355329247SDavid du Colombier 			break;
141455329247SDavid du Colombier 		}
141555329247SDavid du Colombier 	print("awaitspinup: %s didn't spin up after 20 seconds\n", name);
141655329247SDavid du Colombier 	iunlock(d);
141755329247SDavid du Colombier }
141855329247SDavid du Colombier 
1419b1707c5dSDavid du Colombier static int
iaverify(SDunit * u)1420b1707c5dSDavid du Colombier iaverify(SDunit *u)
1421b1707c5dSDavid du Colombier {
1422b1707c5dSDavid du Colombier 	Ctlr *c;
1423b1707c5dSDavid du Colombier 	Drive *d;
1424b1707c5dSDavid du Colombier 
1425b1707c5dSDavid du Colombier 	c = u->dev->ctlr;
1426b1707c5dSDavid du Colombier 	d = c->drive[u->subno];
1427b1707c5dSDavid du Colombier 	ilock(c);
1428b1707c5dSDavid du Colombier 	ilock(d);
1429b1707c5dSDavid du Colombier 	d->unit = u;
1430b1707c5dSDavid du Colombier 	iunlock(d);
1431b1707c5dSDavid du Colombier 	iunlock(c);
1432014ad43fSDavid du Colombier 	checkdrive(d, d->driveno);		/* c->d0 + d->driveno */
143355329247SDavid du Colombier 
143455329247SDavid du Colombier 	/*
143555329247SDavid du Colombier 	 * hang around until disks are spun up and thus available as
143655329247SDavid du Colombier 	 * nvram, dos file systems, etc.  you wouldn't expect it, but
143755329247SDavid du Colombier 	 * the intel 330 ssd takes a while to `spin up'.
143855329247SDavid du Colombier 	 */
143955329247SDavid du Colombier 	awaitspinup(d);
1440b1707c5dSDavid du Colombier 	return 1;
1441b1707c5dSDavid du Colombier }
1442b1707c5dSDavid du Colombier 
1443b1707c5dSDavid du Colombier static int
iaenable(SDev * s)1444b1707c5dSDavid du Colombier iaenable(SDev *s)
1445b1707c5dSDavid du Colombier {
1446b1707c5dSDavid du Colombier 	char name[32];
1447b1707c5dSDavid du Colombier 	Ctlr *c;
1448b1707c5dSDavid du Colombier 	static int once;
1449b1707c5dSDavid du Colombier 
1450b1707c5dSDavid du Colombier 	c = s->ctlr;
1451b1707c5dSDavid du Colombier 	ilock(c);
1452b1707c5dSDavid du Colombier 	if(!c->enabled) {
1453b1707c5dSDavid du Colombier 		if(once == 0) {
1454b1707c5dSDavid du Colombier 			once = 1;
1455fb579fa2SDavid du Colombier 			kproc("ahci", satakproc, 0);
1456b1707c5dSDavid du Colombier 		}
1457b1707c5dSDavid du Colombier 		if(c->ndrive == 0)
1458b1707c5dSDavid du Colombier 			panic("iaenable: zero s->ctlr->ndrive");
1459b1707c5dSDavid du Colombier 		pcisetbme(c->pci);
1460b1707c5dSDavid du Colombier 		snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1461b1707c5dSDavid du Colombier 		intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1462b1707c5dSDavid du Colombier 		/* supposed to squelch leftover interrupts here. */
1463b1707c5dSDavid du Colombier 		ahcienable(c->hba);
1464b1707c5dSDavid du Colombier 		c->enabled = 1;
1465b1707c5dSDavid du Colombier 	}
1466b1707c5dSDavid du Colombier 	iunlock(c);
1467b1707c5dSDavid du Colombier 	return 1;
1468b1707c5dSDavid du Colombier }
1469b1707c5dSDavid du Colombier 
1470b1707c5dSDavid du Colombier static int
iadisable(SDev * s)1471b1707c5dSDavid du Colombier iadisable(SDev *s)
1472b1707c5dSDavid du Colombier {
1473b1707c5dSDavid du Colombier 	char name[32];
1474b1707c5dSDavid du Colombier 	Ctlr *c;
1475b1707c5dSDavid du Colombier 
1476b1707c5dSDavid du Colombier 	c = s->ctlr;
1477b1707c5dSDavid du Colombier 	ilock(c);
1478b1707c5dSDavid du Colombier 	ahcidisable(c->hba);
1479b1707c5dSDavid du Colombier 	snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name);
1480b1707c5dSDavid du Colombier 	intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name);
1481b1707c5dSDavid du Colombier 	c->enabled = 0;
1482b1707c5dSDavid du Colombier 	iunlock(c);
1483b1707c5dSDavid du Colombier 	return 1;
1484b1707c5dSDavid du Colombier }
1485b1707c5dSDavid du Colombier 
1486b1707c5dSDavid du Colombier static int
iaonline(SDunit * unit)1487b1707c5dSDavid du Colombier iaonline(SDunit *unit)
1488b1707c5dSDavid du Colombier {
1489b1707c5dSDavid du Colombier 	int r;
1490b1707c5dSDavid du Colombier 	Ctlr *c;
1491b1707c5dSDavid du Colombier 	Drive *d;
1492b1707c5dSDavid du Colombier 
1493b1707c5dSDavid du Colombier 	c = unit->dev->ctlr;
1494b1707c5dSDavid du Colombier 	d = c->drive[unit->subno];
1495b1707c5dSDavid du Colombier 	r = 0;
1496b1707c5dSDavid du Colombier 
1497b1707c5dSDavid du Colombier 	if(d->portm.feat & Datapi && d->mediachange){
1498b1707c5dSDavid du Colombier 		r = scsionline(unit);
1499b1707c5dSDavid du Colombier 		if(r > 0)
1500b1707c5dSDavid du Colombier 			d->mediachange = 0;
1501b1707c5dSDavid du Colombier 		return r;
1502b1707c5dSDavid du Colombier 	}
1503b1707c5dSDavid du Colombier 
1504b1707c5dSDavid du Colombier 	ilock(d);
1505b1707c5dSDavid du Colombier 	if(d->mediachange){
1506b1707c5dSDavid du Colombier 		r = 2;
1507b1707c5dSDavid du Colombier 		d->mediachange = 0;
1508b1707c5dSDavid du Colombier 		/* devsd resets this after online is called; why? */
1509b1707c5dSDavid du Colombier 		unit->sectors = d->sectors;
151058db92f4SDavid du Colombier 		unit->secsize = 512;		/* default size */
1511b1707c5dSDavid du Colombier 	} else if(d->state == Dready)
1512b1707c5dSDavid du Colombier 		r = 1;
1513b1707c5dSDavid du Colombier 	iunlock(d);
1514b1707c5dSDavid du Colombier 	return r;
1515b1707c5dSDavid du Colombier }
1516b1707c5dSDavid du Colombier 
1517b1707c5dSDavid du Colombier /* returns locked list! */
1518b1707c5dSDavid du Colombier static Alist*
ahcibuild(Drive * d,uchar * cmd,void * data,int n,vlong lba)151958db92f4SDavid du Colombier ahcibuild(Drive *d, uchar *cmd, void *data, int n, vlong lba)
1520b1707c5dSDavid du Colombier {
1521b1707c5dSDavid du Colombier 	uchar *c, acmd, dir, llba;
1522b1707c5dSDavid du Colombier 	Alist *l;
1523b1707c5dSDavid du Colombier 	Actab *t;
1524060a30c6SDavid du Colombier 	Aportm *pm;
1525b1707c5dSDavid du Colombier 	Aprdt *p;
1526b1707c5dSDavid du Colombier 	static uchar tab[2][2] = { 0xc8, 0x25, 0xca, 0x35, };
1527b1707c5dSDavid du Colombier 
1528060a30c6SDavid du Colombier 	pm = &d->portm;
1529b1707c5dSDavid du Colombier 	dir = *cmd != 0x28;
1530060a30c6SDavid du Colombier 	llba = pm->feat&Dllba? 1: 0;
1531b1707c5dSDavid du Colombier 	acmd = tab[dir][llba];
1532060a30c6SDavid du Colombier 	qlock(pm);
1533060a30c6SDavid du Colombier 	l = pm->list;
1534060a30c6SDavid du Colombier 	t = pm->ctab;
1535b1707c5dSDavid du Colombier 	c = t->cfis;
1536b1707c5dSDavid du Colombier 
1537b1707c5dSDavid du Colombier 	c[0] = 0x27;
1538b1707c5dSDavid du Colombier 	c[1] = 0x80;
1539b1707c5dSDavid du Colombier 	c[2] = acmd;
1540b1707c5dSDavid du Colombier 	c[3] = 0;
1541b1707c5dSDavid du Colombier 
1542b1707c5dSDavid du Colombier 	c[4] = lba;		/* sector		lba low	7:0 */
1543b1707c5dSDavid du Colombier 	c[5] = lba >> 8;	/* cylinder low		lba mid	15:8 */
1544b1707c5dSDavid du Colombier 	c[6] = lba >> 16;	/* cylinder hi		lba hi	23:16 */
154575cb58dbSDavid du Colombier 	c[7] = Obs | 0x40;	/* 0x40 == lba */
1546b1707c5dSDavid du Colombier 	if(llba == 0)
1547b1707c5dSDavid du Colombier 		c[7] |= (lba>>24) & 7;
1548b1707c5dSDavid du Colombier 
1549b1707c5dSDavid du Colombier 	c[8] = lba >> 24;	/* sector (exp)		lba 	31:24 */
1550b1707c5dSDavid du Colombier 	c[9] = lba >> 32;	/* cylinder low (exp)	lba	39:32 */
1551b1707c5dSDavid du Colombier 	c[10] = lba >> 48;	/* cylinder hi (exp)	lba	48:40 */
1552b1707c5dSDavid du Colombier 	c[11] = 0;		/* features (exp); */
1553b1707c5dSDavid du Colombier 
1554b1707c5dSDavid du Colombier 	c[12] = n;		/* sector count */
1555b1707c5dSDavid du Colombier 	c[13] = n >> 8;		/* sector count (exp) */
1556b1707c5dSDavid du Colombier 	c[14] = 0;		/* r */
1557b1707c5dSDavid du Colombier 	c[15] = 0;		/* control */
1558b1707c5dSDavid du Colombier 
1559b1707c5dSDavid du Colombier 	*(ulong*)(c + 16) = 0;
1560b1707c5dSDavid du Colombier 
1561b1707c5dSDavid du Colombier 	l->flags = 1<<16 | Lpref | 0x5;	/* Lpref ?? */
1562b1707c5dSDavid du Colombier 	if(dir == Write)
1563b1707c5dSDavid du Colombier 		l->flags |= Lwrite;
1564b1707c5dSDavid du Colombier 	l->len = 0;
1565b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
1566b1707c5dSDavid du Colombier 	l->ctabhi = 0;
1567b1707c5dSDavid du Colombier 
1568b1707c5dSDavid du Colombier 	p = &t->prdt;
1569b1707c5dSDavid du Colombier 	p->dba = PCIWADDR(data);
1570b1707c5dSDavid du Colombier 	p->dbahi = 0;
157158db92f4SDavid du Colombier 	if(d->unit == nil)
157258db92f4SDavid du Colombier 		panic("ahcibuild: nil d->unit");
157358db92f4SDavid du Colombier 	p->count = 1<<31 | (d->unit->secsize*n - 2) | 1;
1574b1707c5dSDavid du Colombier 
1575b1707c5dSDavid du Colombier 	return l;
1576b1707c5dSDavid du Colombier }
1577b1707c5dSDavid du Colombier 
1578b1707c5dSDavid du Colombier static Alist*
ahcibuildpkt(Aportm * pm,SDreq * r,void * data,int n)1579060a30c6SDavid du Colombier ahcibuildpkt(Aportm *pm, SDreq *r, void *data, int n)
1580b1707c5dSDavid du Colombier {
1581b1707c5dSDavid du Colombier 	int fill, len;
1582b1707c5dSDavid du Colombier 	uchar *c;
1583b1707c5dSDavid du Colombier 	Alist *l;
1584b1707c5dSDavid du Colombier 	Actab *t;
1585b1707c5dSDavid du Colombier 	Aprdt *p;
1586b1707c5dSDavid du Colombier 
1587060a30c6SDavid du Colombier 	qlock(pm);
1588060a30c6SDavid du Colombier 	l = pm->list;
1589060a30c6SDavid du Colombier 	t = pm->ctab;
1590b1707c5dSDavid du Colombier 	c = t->cfis;
1591b1707c5dSDavid du Colombier 
1592060a30c6SDavid du Colombier 	fill = pm->feat&Datapi16? 16: 12;
1593b1707c5dSDavid du Colombier 	if((len = r->clen) > fill)
1594b1707c5dSDavid du Colombier 		len = fill;
1595b1707c5dSDavid du Colombier 	memmove(t->atapi, r->cmd, len);
1596b1707c5dSDavid du Colombier 	memset(t->atapi+len, 0, fill-len);
1597b1707c5dSDavid du Colombier 
1598b1707c5dSDavid du Colombier 	c[0] = 0x27;
1599b1707c5dSDavid du Colombier 	c[1] = 0x80;
1600b1707c5dSDavid du Colombier 	c[2] = 0xa0;
1601b1707c5dSDavid du Colombier 	if(n != 0)
1602b1707c5dSDavid du Colombier 		c[3] = 1;	/* dma */
1603b1707c5dSDavid du Colombier 	else
1604b1707c5dSDavid du Colombier 		c[3] = 0;	/* features (exp); */
1605b1707c5dSDavid du Colombier 
1606b1707c5dSDavid du Colombier 	c[4] = 0;		/* sector		lba low	7:0 */
1607b1707c5dSDavid du Colombier 	c[5] = n;		/* cylinder low		lba mid	15:8 */
1608b1707c5dSDavid du Colombier 	c[6] = n >> 8;		/* cylinder hi		lba hi	23:16 */
160975cb58dbSDavid du Colombier 	c[7] = Obs;
1610b1707c5dSDavid du Colombier 
1611b1707c5dSDavid du Colombier 	*(ulong*)(c + 8) = 0;
1612b1707c5dSDavid du Colombier 	*(ulong*)(c + 12) = 0;
1613b1707c5dSDavid du Colombier 	*(ulong*)(c + 16) = 0;
1614b1707c5dSDavid du Colombier 
1615b1707c5dSDavid du Colombier 	l->flags = 1<<16 | Lpref | Latapi | 0x5;
1616b1707c5dSDavid du Colombier 	if(r->write != 0 && data)
1617b1707c5dSDavid du Colombier 		l->flags |= Lwrite;
1618b1707c5dSDavid du Colombier 	l->len = 0;
1619b1707c5dSDavid du Colombier 	l->ctab = PCIWADDR(t);
1620b1707c5dSDavid du Colombier 	l->ctabhi = 0;
1621b1707c5dSDavid du Colombier 
1622b1707c5dSDavid du Colombier 	if(data == 0)
1623b1707c5dSDavid du Colombier 		return l;
1624b1707c5dSDavid du Colombier 
1625b1707c5dSDavid du Colombier 	p = &t->prdt;
1626b1707c5dSDavid du Colombier 	p->dba = PCIWADDR(data);
1627b1707c5dSDavid du Colombier 	p->dbahi = 0;
1628b1707c5dSDavid du Colombier 	p->count = 1<<31 | (n - 2) | 1;
1629b1707c5dSDavid du Colombier 
1630b1707c5dSDavid du Colombier 	return l;
1631b1707c5dSDavid du Colombier }
1632b1707c5dSDavid du Colombier 
1633b1707c5dSDavid du Colombier static int
waitready(Drive * d)1634b1707c5dSDavid du Colombier waitready(Drive *d)
1635b1707c5dSDavid du Colombier {
163691b330d9SDavid du Colombier 	ulong s, i, δ;
1637b1707c5dSDavid du Colombier 
163881ede731SDavid du Colombier 	for(i = 0; i < 15000; i += 250){
163981ede731SDavid du Colombier 		if(d->state == Dreset || d->state == Dportreset ||
164081ede731SDavid du Colombier 		    d->state == Dnew)
164181ede731SDavid du Colombier 			return 1;
164281ede731SDavid du Colombier 		δ = MACHP(0)->ticks - d->lastseen;
164381ede731SDavid du Colombier 		if(d->state == Dnull || δ > 10*1000)
164481ede731SDavid du Colombier 			return -1;
1645b1707c5dSDavid du Colombier 		ilock(d);
1646b1707c5dSDavid du Colombier 		s = d->port->sstatus;
1647b1707c5dSDavid du Colombier 		iunlock(d);
164855329247SDavid du Colombier 		if((s & Intpm) == 0 && δ > 1500)
164981ede731SDavid du Colombier 			return -1;	/* no detect */
165055329247SDavid du Colombier 		if(d->state == Dready &&
165155329247SDavid du Colombier 		    (s & Devdet) == (Devphycomm|Devpresent))
1652b1707c5dSDavid du Colombier 			return 0;	/* ready, present & phy. comm. */
165381ede731SDavid du Colombier 		esleep(250);
1654b1707c5dSDavid du Colombier 	}
1655b1707c5dSDavid du Colombier 	print("%s: not responding; offline\n", d->unit->name);
1656aec4d1fcSDavid du Colombier 	setstate(d, Doffline);
1657b1707c5dSDavid du Colombier 	return -1;
1658b1707c5dSDavid du Colombier }
1659b1707c5dSDavid du Colombier 
1660b1707c5dSDavid du Colombier static int
lockready(Drive * d)166181ede731SDavid du Colombier lockready(Drive *d)
166281ede731SDavid du Colombier {
166381ede731SDavid du Colombier 	int i;
166481ede731SDavid du Colombier 
166581ede731SDavid du Colombier 	qlock(&d->portm);
1666c09c4750SDavid du Colombier 	while ((i = waitready(d)) == 1) {	/* could wait forever? */
166781ede731SDavid du Colombier 		qunlock(&d->portm);
166881ede731SDavid du Colombier 		esleep(1);
166981ede731SDavid du Colombier 		qlock(&d->portm);
167081ede731SDavid du Colombier 	}
167181ede731SDavid du Colombier 	return i;
167281ede731SDavid du Colombier }
167381ede731SDavid du Colombier 
167481ede731SDavid du Colombier static int
flushcache(Drive * d)167581ede731SDavid du Colombier flushcache(Drive *d)
167681ede731SDavid du Colombier {
167781ede731SDavid du Colombier 	int i;
167881ede731SDavid du Colombier 
167981ede731SDavid du Colombier 	i = -1;
168081ede731SDavid du Colombier 	if(lockready(d) == 0)
168181ede731SDavid du Colombier 		i = ahciflushcache(&d->portc);
168281ede731SDavid du Colombier 	qunlock(&d->portm);
168381ede731SDavid du Colombier 	return i;
168481ede731SDavid du Colombier }
168581ede731SDavid du Colombier 
168681ede731SDavid du Colombier static int
iariopkt(SDreq * r,Drive * d)1687b1707c5dSDavid du Colombier iariopkt(SDreq *r, Drive *d)
1688b1707c5dSDavid du Colombier {
168986abb9fbSDavid du Colombier 	int n, count, try, max, flag, task, wormwrite;
1690b1707c5dSDavid du Colombier 	char *name;
1691b1707c5dSDavid du Colombier 	uchar *cmd, *data;
1692b1707c5dSDavid du Colombier 	Aport *p;
1693b1707c5dSDavid du Colombier 	Asleep as;
1694b1707c5dSDavid du Colombier 
1695b1707c5dSDavid du Colombier 	cmd = r->cmd;
1696b1707c5dSDavid du Colombier 	name = d->unit->name;
1697b1707c5dSDavid du Colombier 	p = d->port;
1698b1707c5dSDavid du Colombier 
169939b91b2bSDavid du Colombier 	aprint("ahci: iariopkt: %04#ux %04#ux %c %d %p\n",
17003d56950aSDavid du Colombier 		cmd[0], cmd[2], "rw"[r->write], r->dlen, r->data);
1701b1707c5dSDavid du Colombier 	if(cmd[0] == 0x5a && (cmd[2] & 0x3f) == 0x3f)
170276f70593SDavid du Colombier 		return sdmodesense(r, cmd, d->info, d->infosz);
1703b1707c5dSDavid du Colombier 	r->rlen = 0;
1704b1707c5dSDavid du Colombier 	count = r->dlen;
1705b1707c5dSDavid du Colombier 	max = 65536;
1706b1707c5dSDavid du Colombier 
1707b1707c5dSDavid du Colombier 	try = 0;
1708b1707c5dSDavid du Colombier retry:
1709b1707c5dSDavid du Colombier 	data = r->data;
1710b1707c5dSDavid du Colombier 	n = count;
1711b1707c5dSDavid du Colombier 	if(n > max)
1712b1707c5dSDavid du Colombier 		n = max;
1713b1707c5dSDavid du Colombier 	ahcibuildpkt(&d->portm, r, data, n);
171481ede731SDavid du Colombier 	switch(waitready(d)){
171581ede731SDavid du Colombier 	case -1:
171681ede731SDavid du Colombier 		qunlock(&d->portm);
171781ede731SDavid du Colombier 		return SDeio;
171881ede731SDavid du Colombier 	case 1:
171981ede731SDavid du Colombier 		qunlock(&d->portm);
172081ede731SDavid du Colombier 		esleep(1);
172181ede731SDavid du Colombier 		goto retry;
172281ede731SDavid du Colombier 	}
1723c09c4750SDavid du Colombier 	/* d->portm qlock held here */
172481ede731SDavid du Colombier 
1725b1707c5dSDavid du Colombier 	ilock(d);
1726b1707c5dSDavid du Colombier 	d->portm.flag = 0;
1727b1707c5dSDavid du Colombier 	iunlock(d);
1728b1707c5dSDavid du Colombier 	p->ci = 1;
1729b1707c5dSDavid du Colombier 
1730b1707c5dSDavid du Colombier 	as.p = p;
1731b1707c5dSDavid du Colombier 	as.i = 1;
1732b1707c5dSDavid du Colombier 	d->intick = MACHP(0)->ticks;
1733014ad43fSDavid du Colombier 	d->active++;
1734b1707c5dSDavid du Colombier 
1735b1707c5dSDavid du Colombier 	while(waserror())
1736b1707c5dSDavid du Colombier 		;
1737c09c4750SDavid du Colombier 	/* don't sleep here forever */
1738c09c4750SDavid du Colombier 	tsleep(&d->portm, ahciclear, &as, 3*1000);
1739b1707c5dSDavid du Colombier 	poperror();
1740c09c4750SDavid du Colombier 	if(!ahciclear(&as)) {
1741c09c4750SDavid du Colombier 		qunlock(&d->portm);
1742c09c4750SDavid du Colombier 		print("%s: ahciclear not true after 3 seconds\n", name);
1743c09c4750SDavid du Colombier 		r->status = SDcheck;
1744c09c4750SDavid du Colombier 		return SDcheck;
1745c09c4750SDavid du Colombier 	}
1746b1707c5dSDavid du Colombier 
1747014ad43fSDavid du Colombier 	d->active--;
1748b1707c5dSDavid du Colombier 	ilock(d);
1749b1707c5dSDavid du Colombier 	flag = d->portm.flag;
1750b1707c5dSDavid du Colombier 	task = d->port->task;
1751b1707c5dSDavid du Colombier 	iunlock(d);
1752b1707c5dSDavid du Colombier 
1753b1707c5dSDavid du Colombier 	if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){
1754014ad43fSDavid du Colombier 		d->port->ci = 0;
1755b1707c5dSDavid du Colombier 		ahcirecover(&d->portc);
1756b1707c5dSDavid du Colombier 		task = d->port->task;
175781ede731SDavid du Colombier 		flag &= ~Fdone;		/* either an error or do-over */
1758b1707c5dSDavid du Colombier 	}
1759b1707c5dSDavid du Colombier 	qunlock(&d->portm);
1760b1707c5dSDavid du Colombier 	if(flag == 0){
1761b1707c5dSDavid du Colombier 		if(++try == 10){
1762b1707c5dSDavid du Colombier 			print("%s: bad disk\n", name);
1763b1707c5dSDavid du Colombier 			r->status = SDcheck;
1764b1707c5dSDavid du Colombier 			return SDcheck;
1765b1707c5dSDavid du Colombier 		}
176686abb9fbSDavid du Colombier 		/*
176786abb9fbSDavid du Colombier 		 * write retries cannot succeed on write-once media,
176886abb9fbSDavid du Colombier 		 * so just accept any failure.
176986abb9fbSDavid du Colombier 		 */
177086abb9fbSDavid du Colombier 		wormwrite = 0;
177186abb9fbSDavid du Colombier 		switch(d->unit->inquiry[0] & SDinq0periphtype){
177286abb9fbSDavid du Colombier 		case SDperworm:
177386abb9fbSDavid du Colombier 		case SDpercd:
177486abb9fbSDavid du Colombier 			switch(cmd[0]){
177586abb9fbSDavid du Colombier 			case 0x0a:		/* write (6?) */
177686abb9fbSDavid du Colombier 			case 0x2a:		/* write (10) */
177786abb9fbSDavid du Colombier 			case 0x8a:		/* long write (16) */
177886abb9fbSDavid du Colombier 			case 0x2e:		/* write and verify (10) */
177986abb9fbSDavid du Colombier 				wormwrite = 1;
178086abb9fbSDavid du Colombier 				break;
178186abb9fbSDavid du Colombier 			}
178286abb9fbSDavid du Colombier 			break;
178386abb9fbSDavid du Colombier 		}
178486abb9fbSDavid du Colombier 		if (!wormwrite) {
178581ede731SDavid du Colombier 			print("%s: retry\n", name);
1786b1707c5dSDavid du Colombier 			goto retry;
1787b1707c5dSDavid du Colombier 		}
178886abb9fbSDavid du Colombier 	}
1789b1707c5dSDavid du Colombier 	if(flag & Ferror){
179081ede731SDavid du Colombier 		if((task&Eidnf) == 0)
1791f0499952SDavid du Colombier 			print("%s: i/o error task=%#ux\n", name, task);
1792b1707c5dSDavid du Colombier 		r->status = SDcheck;
1793b1707c5dSDavid du Colombier 		return SDcheck;
1794b1707c5dSDavid du Colombier 	}
1795b1707c5dSDavid du Colombier 
1796b1707c5dSDavid du Colombier 	data += n;
1797b1707c5dSDavid du Colombier 
1798b1707c5dSDavid du Colombier 	r->rlen = data - (uchar*)r->data;
1799b1707c5dSDavid du Colombier 	r->status = SDok;
1800b1707c5dSDavid du Colombier 	return SDok;
1801b1707c5dSDavid du Colombier }
1802b1707c5dSDavid du Colombier 
1803b1707c5dSDavid du Colombier static int
iario(SDreq * r)1804b1707c5dSDavid du Colombier iario(SDreq *r)
1805b1707c5dSDavid du Colombier {
1806b1707c5dSDavid du Colombier 	int i, n, count, try, max, flag, task;
1807b1707c5dSDavid du Colombier 	vlong lba;
1808b1707c5dSDavid du Colombier 	char *name;
1809b1707c5dSDavid du Colombier 	uchar *cmd, *data;
1810b1707c5dSDavid du Colombier 	Aport *p;
1811b1707c5dSDavid du Colombier 	Asleep as;
1812b1707c5dSDavid du Colombier 	Ctlr *c;
1813b1707c5dSDavid du Colombier 	Drive *d;
1814b1707c5dSDavid du Colombier 	SDunit *unit;
1815b1707c5dSDavid du Colombier 
1816b1707c5dSDavid du Colombier 	unit = r->unit;
1817b1707c5dSDavid du Colombier 	c = unit->dev->ctlr;
1818b1707c5dSDavid du Colombier 	d = c->drive[unit->subno];
1819b1707c5dSDavid du Colombier 	if(d->portm.feat & Datapi)
1820b1707c5dSDavid du Colombier 		return iariopkt(r, d);
1821b1707c5dSDavid du Colombier 	cmd = r->cmd;
1822b1707c5dSDavid du Colombier 	name = d->unit->name;
1823b1707c5dSDavid du Colombier 	p = d->port;
1824b1707c5dSDavid du Colombier 
1825b1707c5dSDavid du Colombier 	if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
182681ede731SDavid du Colombier 		if(flushcache(d) == 0)
1827b1707c5dSDavid du Colombier 			return sdsetsense(r, SDok, 0, 0, 0);
1828b1707c5dSDavid du Colombier 		return sdsetsense(r, SDcheck, 3, 0xc, 2);
1829b1707c5dSDavid du Colombier 	}
1830b1707c5dSDavid du Colombier 
183176f70593SDavid du Colombier 	if((i = sdfakescsi(r, d->info, d->infosz)) != SDnostatus){
1832b1707c5dSDavid du Colombier 		r->status = i;
1833b1707c5dSDavid du Colombier 		return i;
1834b1707c5dSDavid du Colombier 	}
1835b1707c5dSDavid du Colombier 
1836b1707c5dSDavid du Colombier 	if(*cmd != 0x28 && *cmd != 0x2a){
1837f0499952SDavid du Colombier 		print("%s: bad cmd %.2#ux\n", name, cmd[0]);
1838b1707c5dSDavid du Colombier 		r->status = SDcheck;
1839b1707c5dSDavid du Colombier 		return SDcheck;
1840b1707c5dSDavid du Colombier 	}
1841b1707c5dSDavid du Colombier 
1842b1707c5dSDavid du Colombier 	lba   = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
1843b1707c5dSDavid du Colombier 	count = cmd[7]<<8 | cmd[8];
1844b1707c5dSDavid du Colombier 	if(r->data == nil)
1845b1707c5dSDavid du Colombier 		return SDok;
1846b1707c5dSDavid du Colombier 	if(r->dlen < count * unit->secsize)
1847b1707c5dSDavid du Colombier 		count = r->dlen / unit->secsize;
1848b1707c5dSDavid du Colombier 	max = 128;
1849b1707c5dSDavid du Colombier 
1850b1707c5dSDavid du Colombier 	try = 0;
1851b1707c5dSDavid du Colombier retry:
1852b1707c5dSDavid du Colombier 	data = r->data;
1853b1707c5dSDavid du Colombier 	while(count > 0){
1854b1707c5dSDavid du Colombier 		n = count;
1855b1707c5dSDavid du Colombier 		if(n > max)
1856b1707c5dSDavid du Colombier 			n = max;
185758db92f4SDavid du Colombier 		ahcibuild(d, cmd, data, n, lba);
185881ede731SDavid du Colombier 		switch(waitready(d)){
185981ede731SDavid du Colombier 		case -1:
186081ede731SDavid du Colombier 			qunlock(&d->portm);
186181ede731SDavid du Colombier 			return SDeio;
186281ede731SDavid du Colombier 		case 1:
186381ede731SDavid du Colombier 			qunlock(&d->portm);
186481ede731SDavid du Colombier 			esleep(1);
186581ede731SDavid du Colombier 			goto retry;
186681ede731SDavid du Colombier 		}
1867c09c4750SDavid du Colombier 		/* d->portm qlock held here */
1868b1707c5dSDavid du Colombier 		ilock(d);
1869b1707c5dSDavid du Colombier 		d->portm.flag = 0;
1870b1707c5dSDavid du Colombier 		iunlock(d);
1871b1707c5dSDavid du Colombier 		p->ci = 1;
1872b1707c5dSDavid du Colombier 
1873b1707c5dSDavid du Colombier 		as.p = p;
1874b1707c5dSDavid du Colombier 		as.i = 1;
1875b1707c5dSDavid du Colombier 		d->intick = MACHP(0)->ticks;
1876014ad43fSDavid du Colombier 		d->active++;
1877b1707c5dSDavid du Colombier 
1878b1707c5dSDavid du Colombier 		while(waserror())
1879b1707c5dSDavid du Colombier 			;
1880c09c4750SDavid du Colombier 		/* don't sleep here forever */
1881c09c4750SDavid du Colombier 		tsleep(&d->portm, ahciclear, &as, 3*1000);
1882b1707c5dSDavid du Colombier 		poperror();
1883c09c4750SDavid du Colombier 		if(!ahciclear(&as)) {
1884c09c4750SDavid du Colombier 			qunlock(&d->portm);
1885c09c4750SDavid du Colombier 			print("%s: ahciclear not true after 3 seconds\n", name);
1886c09c4750SDavid du Colombier 			r->status = SDcheck;
1887c09c4750SDavid du Colombier 			return SDcheck;
1888c09c4750SDavid du Colombier 		}
1889b1707c5dSDavid du Colombier 
1890014ad43fSDavid du Colombier 		d->active--;
1891b1707c5dSDavid du Colombier 		ilock(d);
1892b1707c5dSDavid du Colombier 		flag = d->portm.flag;
1893b1707c5dSDavid du Colombier 		task = d->port->task;
1894b1707c5dSDavid du Colombier 		iunlock(d);
1895b1707c5dSDavid du Colombier 
1896b1707c5dSDavid du Colombier 		if(task & (Efatal<<8) ||
1897b1707c5dSDavid du Colombier 		    task & (ASbsy|ASdrq) && d->state == Dready){
1898014ad43fSDavid du Colombier 			d->port->ci = 0;
1899b1707c5dSDavid du Colombier 			ahcirecover(&d->portc);
1900b1707c5dSDavid du Colombier 			task = d->port->task;
1901b1707c5dSDavid du Colombier 		}
1902b1707c5dSDavid du Colombier 		qunlock(&d->portm);
1903b1707c5dSDavid du Colombier 		if(flag == 0){
1904b1707c5dSDavid du Colombier 			if(++try == 10){
1905b1707c5dSDavid du Colombier 				print("%s: bad disk\n", name);
1906b1707c5dSDavid du Colombier 				r->status = SDeio;
1907b1707c5dSDavid du Colombier 				return SDeio;
1908b1707c5dSDavid du Colombier 			}
19099413350cSDavid du Colombier 			print("%s: retry blk %lld\n", name, lba);
1910b1707c5dSDavid du Colombier 			goto retry;
1911b1707c5dSDavid du Colombier 		}
1912b1707c5dSDavid du Colombier 		if(flag & Ferror){
19139413350cSDavid du Colombier 			print("%s: i/o error task=%#ux @%,lld\n",
1914f0499952SDavid du Colombier 				name, task, lba);
1915b1707c5dSDavid du Colombier 			r->status = SDeio;
1916b1707c5dSDavid du Colombier 			return SDeio;
1917b1707c5dSDavid du Colombier 		}
1918b1707c5dSDavid du Colombier 
1919b1707c5dSDavid du Colombier 		count -= n;
1920b1707c5dSDavid du Colombier 		lba   += n;
1921b1707c5dSDavid du Colombier 		data += n * unit->secsize;
1922b1707c5dSDavid du Colombier 	}
1923b1707c5dSDavid du Colombier 	r->rlen = data - (uchar*)r->data;
1924b1707c5dSDavid du Colombier 	r->status = SDok;
1925b1707c5dSDavid du Colombier 	return SDok;
1926b1707c5dSDavid du Colombier }
1927b1707c5dSDavid du Colombier 
1928b1707c5dSDavid du Colombier /*
1929c09c4750SDavid du Colombier  * configure drives 0-5 as ahci sata (c.f. errata).
1930c09c4750SDavid du Colombier  * what about 6 & 7, as claimed by marvell 0x9123?
1931b1707c5dSDavid du Colombier  */
1932b1707c5dSDavid du Colombier static int
iaahcimode(Pcidev * p)1933b1707c5dSDavid du Colombier iaahcimode(Pcidev *p)
1934b1707c5dSDavid du Colombier {
1935f0499952SDavid du Colombier 	dprint("iaahcimode: %#ux %#ux %#ux\n", pcicfgr8(p, 0x91), pcicfgr8(p, 92),
1936b1707c5dSDavid du Colombier 		pcicfgr8(p, 93));
1937eac5056eSDavid du Colombier 	pcicfgw16(p, 0x92, pcicfgr16(p, 0x92) | 0x3f);	/* ports 0-5 */
1938b1707c5dSDavid du Colombier 	return 0;
1939b1707c5dSDavid du Colombier }
1940b1707c5dSDavid du Colombier 
1941b1707c5dSDavid du Colombier static void
iasetupahci(Ctlr * c)1942b1707c5dSDavid du Colombier iasetupahci(Ctlr *c)
1943b1707c5dSDavid du Colombier {
1944b1707c5dSDavid du Colombier 	/* disable cmd block decoding. */
1945b1707c5dSDavid du Colombier 	pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~(1<<15));
1946b1707c5dSDavid du Colombier 	pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~(1<<15));
1947b1707c5dSDavid du Colombier 
1948b1707c5dSDavid du Colombier 	c->lmmio[0x4/4] |= 1 << 31;	/* enable ahci mode (ghc register) */
1949b1707c5dSDavid du Colombier 	c->lmmio[0xc/4] = (1 << 6) - 1;	/* 5 ports. (supposedly ro pi reg.) */
1950b1707c5dSDavid du Colombier 
1951eac5056eSDavid du Colombier 	/* enable ahci mode and 6 ports; from ich9 datasheet */
1952014ad43fSDavid du Colombier 	pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5);
1953014ad43fSDavid du Colombier }
1954014ad43fSDavid du Colombier 
1955014ad43fSDavid du Colombier static int
didtype(Pcidev * p)1956014ad43fSDavid du Colombier didtype(Pcidev *p)
1957014ad43fSDavid du Colombier {
1958014ad43fSDavid du Colombier 	switch(p->vid){
195975cb58dbSDavid du Colombier 	case Vintel:
1960014ad43fSDavid du Colombier 		if((p->did & 0xfffc) == 0x2680)
1961014ad43fSDavid du Colombier 			return Tesb;
196291157df7SDavid du Colombier 		/*
196391157df7SDavid du Colombier 		 * 0x27c4 is the intel 82801 in compatibility (not sata) mode.
196491157df7SDavid du Colombier 		 */
196582726826SDavid du Colombier 		if (p->did == 0x1e02 ||			/* c210 */
196682726826SDavid du Colombier 		    p->did == 0x24d1 ||			/* 82801eb/er */
1967500d234bSDavid du Colombier 		    (p->did & 0xfffb) == 0x27c1 ||	/* 82801g[bh]m ich7 */
196891157df7SDavid du Colombier 		    p->did == 0x2821 ||			/* 82801h[roh] */
196991157df7SDavid du Colombier 		    (p->did & 0xfffe) == 0x2824 ||	/* 82801h[b] */
197091157df7SDavid du Colombier 		    (p->did & 0xfeff) == 0x2829 ||	/* ich8/9m */
1971014ad43fSDavid du Colombier 		    (p->did & 0xfffe) == 0x2922 ||	/* ich9 */
197291157df7SDavid du Colombier 		    p->did == 0x3a02 ||			/* 82801jd/do */
197391157df7SDavid du Colombier 		    (p->did & 0xfefe) == 0x3a22 ||	/* ich10, pch */
1974d8cd2beaSDavid du Colombier 		    (p->did & 0xfff8) == 0x3b28)	/* pchm */
1975014ad43fSDavid du Colombier 			return Tich;
1976014ad43fSDavid du Colombier 		break;
197775cb58dbSDavid du Colombier 	case Vatiamd:
197823173ec1SDavid du Colombier 		if(p->did == 0x4380 || p->did == 0x4390 || p->did == 0x4391){
1979f0499952SDavid du Colombier 			print("detected sb600 vid %#ux did %#ux\n", p->vid, p->did);
1980014ad43fSDavid du Colombier 			return Tsb600;
198123173ec1SDavid du Colombier 		}
1982014ad43fSDavid du Colombier 		break;
198375cb58dbSDavid du Colombier 	case Vmarvell:
1984c09c4750SDavid du Colombier 		if (p->did == 0x9123)
1985c09c4750SDavid du Colombier 			print("ahci: marvell sata 3 controller has delusions "
1986c09c4750SDavid du Colombier 				"of something on unit 7\n");
198775cb58dbSDavid du Colombier 		break;
198875cb58dbSDavid du Colombier 	}
1989c02f0a41SDavid du Colombier 	if(p->ccrb == Pcibcstore && p->ccru == Pciscsata && p->ccrp == 1){
1990c09c4750SDavid du Colombier 		print("ahci: Tunk: vid %#4.4ux did %#4.4ux\n", p->vid, p->did);
1991014ad43fSDavid du Colombier 		return Tunk;
1992c02f0a41SDavid du Colombier 	}
1993014ad43fSDavid du Colombier 	return -1;
1994b1707c5dSDavid du Colombier }
1995b1707c5dSDavid du Colombier 
199676f70593SDavid du Colombier static int
newctlr(Ctlr * ctlr,SDev * sdev,int nunit)199776f70593SDavid du Colombier newctlr(Ctlr *ctlr, SDev *sdev, int nunit)
199876f70593SDavid du Colombier {
199976f70593SDavid du Colombier 	int i, n;
200076f70593SDavid du Colombier 	Drive *drive;
200176f70593SDavid du Colombier 
200276f70593SDavid du Colombier 	ctlr->ndrive = sdev->nunit = nunit;
200376f70593SDavid du Colombier 	ctlr->mport = ctlr->hba->cap & ((1<<5)-1);
200476f70593SDavid du Colombier 
200576f70593SDavid du Colombier 	i = (ctlr->hba->cap >> 20) & ((1<<4)-1);		/* iss */
200676f70593SDavid du Colombier 	print("#S/sd%c: %s: %#p %s, %d ports, irq %d\n", sdev->idno,
200776f70593SDavid du Colombier 		Tname(ctlr), ctlr->physio, descmode[i], nunit, ctlr->pci->intl);
200876f70593SDavid du Colombier 	/* map the drives -- they don't all need to be enabled. */
200976f70593SDavid du Colombier 	n = 0;
201076f70593SDavid du Colombier 	ctlr->rawdrive = malloc(NCtlrdrv * sizeof(Drive));
201176f70593SDavid du Colombier 	if(ctlr->rawdrive == nil) {
201276f70593SDavid du Colombier 		print("ahci: out of memory\n");
201376f70593SDavid du Colombier 		return -1;
201476f70593SDavid du Colombier 	}
201576f70593SDavid du Colombier 	for(i = 0; i < NCtlrdrv; i++) {
201676f70593SDavid du Colombier 		drive = ctlr->rawdrive + i;
201776f70593SDavid du Colombier 		drive->portno = i;
201876f70593SDavid du Colombier 		drive->driveno = -1;
201976f70593SDavid du Colombier 		drive->sectors = 0;
202076f70593SDavid du Colombier 		drive->serial[0] = ' ';
202176f70593SDavid du Colombier 		drive->ctlr = ctlr;
202276f70593SDavid du Colombier 		if((ctlr->hba->pi & (1<<i)) == 0)
202376f70593SDavid du Colombier 			continue;
202476f70593SDavid du Colombier 		drive->port = (Aport*)(ctlr->mmio + 0x80*i + 0x100);
202576f70593SDavid du Colombier 		drive->portc.p = drive->port;
2026*58da3067SDavid du Colombier 		drive->portc.pm = &drive->portm;
202776f70593SDavid du Colombier 		drive->driveno = n++;
202876f70593SDavid du Colombier 		ctlr->drive[drive->driveno] = drive;
202976f70593SDavid du Colombier 		iadrive[niadrive + drive->driveno] = drive;
203076f70593SDavid du Colombier 	}
203176f70593SDavid du Colombier 	for(i = 0; i < n; i++)
203276f70593SDavid du Colombier 		if(ahciidle(ctlr->drive[i]->port) == -1){
203376f70593SDavid du Colombier 			dprint("ahci: %s: port %d wedged; abort\n",
203476f70593SDavid du Colombier 				Tname(ctlr), i);
203576f70593SDavid du Colombier 			return -1;
203676f70593SDavid du Colombier 		}
203776f70593SDavid du Colombier 	for(i = 0; i < n; i++){
203876f70593SDavid du Colombier 		ctlr->drive[i]->mode = DMsatai;
203976f70593SDavid du Colombier 		configdrive(ctlr->drive[i]);
204076f70593SDavid du Colombier 	}
204176f70593SDavid du Colombier 	return n;
204276f70593SDavid du Colombier }
204376f70593SDavid du Colombier 
2044b1707c5dSDavid du Colombier static SDev*
iapnp(void)2045b1707c5dSDavid du Colombier iapnp(void)
2046b1707c5dSDavid du Colombier {
204776f70593SDavid du Colombier 	int n, nunit, type;
2048b1707c5dSDavid du Colombier 	ulong io;
2049b1707c5dSDavid du Colombier 	Ctlr *c;
2050b1707c5dSDavid du Colombier 	Pcidev *p;
2051b1707c5dSDavid du Colombier 	SDev *head, *tail, *s;
2052b1707c5dSDavid du Colombier 	static int done;
2053b1707c5dSDavid du Colombier 
2054b1707c5dSDavid du Colombier 	if(done++)
2055b1707c5dSDavid du Colombier 		return nil;
2056b1707c5dSDavid du Colombier 
2057014ad43fSDavid du Colombier 	memset(olds, 0xff, sizeof olds);
2058b1707c5dSDavid du Colombier 	p = nil;
2059b1707c5dSDavid du Colombier 	head = tail = nil;
2060b1707c5dSDavid du Colombier 	while((p = pcimatch(p, 0, 0)) != nil){
2061014ad43fSDavid du Colombier 		type = didtype(p);
2062014ad43fSDavid du Colombier 		if (type == -1 || p->mem[Abar].bar == 0)
2063b1707c5dSDavid du Colombier 			continue;
2064b1707c5dSDavid du Colombier 		if(niactlr == NCtlr){
206591157df7SDavid du Colombier 			print("ahci: iapnp: %s: too many controllers\n",
20663d56950aSDavid du Colombier 				tname[type]);
2067b1707c5dSDavid du Colombier 			break;
2068b1707c5dSDavid du Colombier 		}
2069b1707c5dSDavid du Colombier 		c = iactlr + niactlr;
2070b1707c5dSDavid du Colombier 		s = sdevs  + niactlr;
2071b1707c5dSDavid du Colombier 		memset(c, 0, sizeof *c);
2072b1707c5dSDavid du Colombier 		memset(s, 0, sizeof *s);
2073b1707c5dSDavid du Colombier 		io = p->mem[Abar].bar & ~0xf;
207475cb58dbSDavid du Colombier 		c->physio = (uchar *)io;
2075b1707c5dSDavid du Colombier 		c->mmio = vmap(io, p->mem[Abar].size);
2076b1707c5dSDavid du Colombier 		if(c->mmio == 0){
2077f0499952SDavid du Colombier 			print("ahci: %s: address %#luX in use did=%#x\n",
2078b1707c5dSDavid du Colombier 				Tname(c), io, p->did);
2079b1707c5dSDavid du Colombier 			continue;
2080b1707c5dSDavid du Colombier 		}
2081b1707c5dSDavid du Colombier 		c->lmmio = (ulong*)c->mmio;
2082b1707c5dSDavid du Colombier 		c->pci = p;
2083b1707c5dSDavid du Colombier 		c->type = type;
2084b1707c5dSDavid du Colombier 
2085b1707c5dSDavid du Colombier 		s->ifc = &sdiahciifc;
2086b1707c5dSDavid du Colombier 		s->idno = 'E' + niactlr;
2087b1707c5dSDavid du Colombier 		s->ctlr = c;
2088b1707c5dSDavid du Colombier 		c->sdev = s;
2089b1707c5dSDavid du Colombier 
2090014ad43fSDavid du Colombier 		if(Intel(c) && p->did != 0x2681)
2091b1707c5dSDavid du Colombier 			iasetupahci(c);
2092b1707c5dSDavid du Colombier 		nunit = ahciconf(c);
2093b1707c5dSDavid du Colombier //		ahcihbareset((Ahba*)c->mmio);
2094014ad43fSDavid du Colombier 		if(Intel(c) && iaahcimode(p) == -1)
2095b1707c5dSDavid du Colombier 			break;
2096b1707c5dSDavid du Colombier 		if(nunit < 1){
2097b1707c5dSDavid du Colombier 			vunmap(c->mmio, p->mem[Abar].size);
2098b1707c5dSDavid du Colombier 			continue;
2099b1707c5dSDavid du Colombier 		}
210076f70593SDavid du Colombier 		n = newctlr(c, s, nunit);
210176f70593SDavid du Colombier 		if(n < 0)
2102eac5056eSDavid du Colombier 			continue;
2103014ad43fSDavid du Colombier 		niadrive += n;
2104b1707c5dSDavid du Colombier 		niactlr++;
2105b1707c5dSDavid du Colombier 		if(head)
2106b1707c5dSDavid du Colombier 			tail->next = s;
2107b1707c5dSDavid du Colombier 		else
2108b1707c5dSDavid du Colombier 			head = s;
2109b1707c5dSDavid du Colombier 		tail = s;
2110b1707c5dSDavid du Colombier 	}
2111b1707c5dSDavid du Colombier 	return head;
2112b1707c5dSDavid du Colombier }
2113b1707c5dSDavid du Colombier 
2114b1707c5dSDavid du Colombier static char* smarttab[] = {
2115b1707c5dSDavid du Colombier 	"unset",
2116b1707c5dSDavid du Colombier 	"error",
2117b1707c5dSDavid du Colombier 	"threshold exceeded",
2118b1707c5dSDavid du Colombier 	"normal"
2119b1707c5dSDavid du Colombier };
2120b1707c5dSDavid du Colombier 
2121b1707c5dSDavid du Colombier static char *
pflag(char * s,char * e,uchar f)2122b1707c5dSDavid du Colombier pflag(char *s, char *e, uchar f)
2123b1707c5dSDavid du Colombier {
2124b1707c5dSDavid du Colombier 	uchar i;
2125b1707c5dSDavid du Colombier 
2126b1707c5dSDavid du Colombier 	for(i = 0; i < 8; i++)
2127b1707c5dSDavid du Colombier 		if(f & (1 << i))
2128b1707c5dSDavid du Colombier 			s = seprint(s, e, "%s ", flagname[i]);
2129b1707c5dSDavid du Colombier 	return seprint(s, e, "\n");
2130b1707c5dSDavid du Colombier }
2131b1707c5dSDavid du Colombier 
2132b1707c5dSDavid du Colombier static int
iarctl(SDunit * u,char * p,int l)2133b1707c5dSDavid du Colombier iarctl(SDunit *u, char *p, int l)
2134b1707c5dSDavid du Colombier {
2135b1707c5dSDavid du Colombier 	char buf[32];
2136b1707c5dSDavid du Colombier 	char *e, *op;
2137b1707c5dSDavid du Colombier 	Aport *o;
2138b1707c5dSDavid du Colombier 	Ctlr *c;
2139b1707c5dSDavid du Colombier 	Drive *d;
2140b1707c5dSDavid du Colombier 
214158db92f4SDavid du Colombier 	c = u->dev->ctlr;
214258db92f4SDavid du Colombier 	if(c == nil) {
214358db92f4SDavid du Colombier print("iarctl: nil u->dev->ctlr\n");
2144b1707c5dSDavid du Colombier 		return 0;
214558db92f4SDavid du Colombier 	}
2146b1707c5dSDavid du Colombier 	d = c->drive[u->subno];
2147b1707c5dSDavid du Colombier 	o = d->port;
2148b1707c5dSDavid du Colombier 
2149b1707c5dSDavid du Colombier 	e = p+l;
2150b1707c5dSDavid du Colombier 	op = p;
2151b1707c5dSDavid du Colombier 	if(d->state == Dready){
2152b1707c5dSDavid du Colombier 		p = seprint(p, e, "model\t%s\n", d->model);
2153b1707c5dSDavid du Colombier 		p = seprint(p, e, "serial\t%s\n", d->serial);
2154b1707c5dSDavid du Colombier 		p = seprint(p, e, "firm\t%s\n", d->firmware);
2155b1707c5dSDavid du Colombier 		if(d->smartrs == 0xff)
2156b1707c5dSDavid du Colombier 			p = seprint(p, e, "smart\tenable error\n");
2157b1707c5dSDavid du Colombier 		else if(d->smartrs == 0)
2158b1707c5dSDavid du Colombier 			p = seprint(p, e, "smart\tdisabled\n");
2159b1707c5dSDavid du Colombier 		else
2160b1707c5dSDavid du Colombier 			p = seprint(p, e, "smart\t%s\n",
2161b1707c5dSDavid du Colombier 				smarttab[d->portm.smart]);
2162b1707c5dSDavid du Colombier 		p = seprint(p, e, "flag\t");
2163b1707c5dSDavid du Colombier 		p = pflag(p, e, d->portm.feat);
2164b1707c5dSDavid du Colombier 	}else
2165b1707c5dSDavid du Colombier 		p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]);
2166b1707c5dSDavid du Colombier 	serrstr(o->serror, buf, buf + sizeof buf - 1);
2167f0499952SDavid du Colombier 	p = seprint(p, e, "reg\ttask %#lux cmd %#lux serr %#lux %s ci %#lux "
216839b91b2bSDavid du Colombier 		"is %#lux; sig %#lux sstatus %06#lux\n",
2169f0499952SDavid du Colombier 		o->task, o->cmd, o->serror, buf,
2170b1707c5dSDavid du Colombier 		o->ci, o->isr, o->sig, o->sstatus);
217158db92f4SDavid du Colombier 	if(d->unit == nil)
217258db92f4SDavid du Colombier 		panic("iarctl: nil d->unit");
217358db92f4SDavid du Colombier 	p = seprint(p, e, "geometry %llud %lud\n", d->sectors, d->unit->secsize);
2174b1707c5dSDavid du Colombier 	return p - op;
2175b1707c5dSDavid du Colombier }
2176b1707c5dSDavid du Colombier 
2177b1707c5dSDavid du Colombier static void
runflushcache(Drive * d)2178b1707c5dSDavid du Colombier runflushcache(Drive *d)
2179b1707c5dSDavid du Colombier {
2180b1707c5dSDavid du Colombier 	long t0;
2181b1707c5dSDavid du Colombier 
2182b1707c5dSDavid du Colombier 	t0 = MACHP(0)->ticks;
218381ede731SDavid du Colombier 	if(flushcache(d) != 0)
218481ede731SDavid du Colombier 		error(Eio);
21853d56950aSDavid du Colombier 	dprint("ahci: flush in %ld ms\n", MACHP(0)->ticks - t0);
2186b1707c5dSDavid du Colombier }
2187b1707c5dSDavid du Colombier 
2188b1707c5dSDavid du Colombier static void
forcemode(Drive * d,char * mode)2189b1707c5dSDavid du Colombier forcemode(Drive *d, char *mode)
2190b1707c5dSDavid du Colombier {
2191b1707c5dSDavid du Colombier 	int i;
2192b1707c5dSDavid du Colombier 
2193b1707c5dSDavid du Colombier 	for(i = 0; i < nelem(modename); i++)
2194b1707c5dSDavid du Colombier 		if(strcmp(mode, modename[i]) == 0)
2195b1707c5dSDavid du Colombier 			break;
2196b1707c5dSDavid du Colombier 	if(i == nelem(modename))
2197b1707c5dSDavid du Colombier 		i = 0;
2198aec4d1fcSDavid du Colombier 	ilock(d);
2199b1707c5dSDavid du Colombier 	d->mode = i;
2200aec4d1fcSDavid du Colombier 	iunlock(d);
2201b1707c5dSDavid du Colombier }
2202b1707c5dSDavid du Colombier 
2203b1707c5dSDavid du Colombier static void
runsmartable(Drive * d,int i)2204b1707c5dSDavid du Colombier runsmartable(Drive *d, int i)
2205b1707c5dSDavid du Colombier {
2206b1707c5dSDavid du Colombier 	if(waserror()){
2207b1707c5dSDavid du Colombier 		qunlock(&d->portm);
2208b1707c5dSDavid du Colombier 		d->smartrs = 0;
2209b1707c5dSDavid du Colombier 		nexterror();
2210b1707c5dSDavid du Colombier 	}
221181ede731SDavid du Colombier 	if(lockready(d) == -1)
221281ede731SDavid du Colombier 		error(Eio);
2213b1707c5dSDavid du Colombier 	d->smartrs = smart(&d->portc, i);
2214b1707c5dSDavid du Colombier 	d->portm.smart = 0;
2215b1707c5dSDavid du Colombier 	qunlock(&d->portm);
2216b1707c5dSDavid du Colombier 	poperror();
2217b1707c5dSDavid du Colombier }
2218b1707c5dSDavid du Colombier 
2219b1707c5dSDavid du Colombier static void
forcestate(Drive * d,char * state)2220b1707c5dSDavid du Colombier forcestate(Drive *d, char *state)
2221b1707c5dSDavid du Colombier {
2222b1707c5dSDavid du Colombier 	int i;
2223b1707c5dSDavid du Colombier 
2224b1707c5dSDavid du Colombier 	for(i = 0; i < nelem(diskstates); i++)
2225b1707c5dSDavid du Colombier 		if(strcmp(state, diskstates[i]) == 0)
2226b1707c5dSDavid du Colombier 			break;
2227b1707c5dSDavid du Colombier 	if(i == nelem(diskstates))
2228014ad43fSDavid du Colombier 		error(Ebadctl);
2229aec4d1fcSDavid du Colombier 	setstate(d, i);
2230b1707c5dSDavid du Colombier }
2231b1707c5dSDavid du Colombier 
2232fb579fa2SDavid du Colombier /*
2233fb579fa2SDavid du Colombier  * force this driver to notice a change of medium if the hardware doesn't
2234fb579fa2SDavid du Colombier  * report it.
2235fb579fa2SDavid du Colombier  */
2236fb579fa2SDavid du Colombier static void
changemedia(SDunit * u)2237fb579fa2SDavid du Colombier changemedia(SDunit *u)
2238fb579fa2SDavid du Colombier {
2239fb579fa2SDavid du Colombier 	Ctlr *c;
2240fb579fa2SDavid du Colombier 	Drive *d;
2241fb579fa2SDavid du Colombier 
2242fb579fa2SDavid du Colombier 	c = u->dev->ctlr;
2243fb579fa2SDavid du Colombier 	d = c->drive[u->subno];
2244fb579fa2SDavid du Colombier 	ilock(d);
2245fb579fa2SDavid du Colombier 	d->mediachange = 1;
2246fb579fa2SDavid du Colombier 	u->sectors = 0;
2247fb579fa2SDavid du Colombier 	iunlock(d);
2248fb579fa2SDavid du Colombier }
2249b1707c5dSDavid du Colombier 
2250b1707c5dSDavid du Colombier static int
iawctl(SDunit * u,Cmdbuf * cmd)2251b1707c5dSDavid du Colombier iawctl(SDunit *u, Cmdbuf *cmd)
2252b1707c5dSDavid du Colombier {
2253b1707c5dSDavid du Colombier 	char **f;
2254b1707c5dSDavid du Colombier 	Ctlr *c;
2255b1707c5dSDavid du Colombier 	Drive *d;
225681ede731SDavid du Colombier 	uint i;
2257b1707c5dSDavid du Colombier 
2258b1707c5dSDavid du Colombier 	c = u->dev->ctlr;
2259b1707c5dSDavid du Colombier 	d = c->drive[u->subno];
2260b1707c5dSDavid du Colombier 	f = cmd->f;
2261b1707c5dSDavid du Colombier 
2262fb579fa2SDavid du Colombier 	if(strcmp(f[0], "change") == 0)
2263fb579fa2SDavid du Colombier 		changemedia(u);
2264fb579fa2SDavid du Colombier 	else if(strcmp(f[0], "flushcache") == 0)
2265b1707c5dSDavid du Colombier 		runflushcache(d);
2266b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "identify") ==  0){
2267b1707c5dSDavid du Colombier 		i = strtoul(f[1]? f[1]: "0", 0, 0);
2268b1707c5dSDavid du Colombier 		if(i > 0xff)
2269b1707c5dSDavid du Colombier 			i = 0;
2270f0499952SDavid du Colombier 		dprint("ahci: %04d %#ux\n", i, d->info[i]);
2271b1707c5dSDavid du Colombier 	}else if(strcmp(f[0], "mode") == 0)
2272b1707c5dSDavid du Colombier 		forcemode(d, f[1]? f[1]: "satai");
2273b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "nop") == 0){
2274b1707c5dSDavid du Colombier 		if((d->portm.feat & Dnop) == 0){
227581ede731SDavid du Colombier 			cmderror(cmd, "no drive support");
2276b1707c5dSDavid du Colombier 			return -1;
2277b1707c5dSDavid du Colombier 		}
2278b1707c5dSDavid du Colombier 		if(waserror()){
2279b1707c5dSDavid du Colombier 			qunlock(&d->portm);
2280b1707c5dSDavid du Colombier 			nexterror();
2281b1707c5dSDavid du Colombier 		}
228281ede731SDavid du Colombier 		if(lockready(d) == -1)
228381ede731SDavid du Colombier 			error(Eio);
2284b1707c5dSDavid du Colombier 		nop(&d->portc);
2285b1707c5dSDavid du Colombier 		qunlock(&d->portm);
2286b1707c5dSDavid du Colombier 		poperror();
2287b1707c5dSDavid du Colombier 	}else if(strcmp(f[0], "reset") == 0)
2288b1707c5dSDavid du Colombier 		forcestate(d, "reset");
2289b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "smart") == 0){
2290b1707c5dSDavid du Colombier 		if(d->smartrs == 0){
2291b1707c5dSDavid du Colombier 			cmderror(cmd, "smart not enabled");
2292b1707c5dSDavid du Colombier 			return -1;
2293b1707c5dSDavid du Colombier 		}
2294b1707c5dSDavid du Colombier 		if(waserror()){
2295b1707c5dSDavid du Colombier 			qunlock(&d->portm);
2296b1707c5dSDavid du Colombier 			d->smartrs = 0;
2297b1707c5dSDavid du Colombier 			nexterror();
2298b1707c5dSDavid du Colombier 		}
229981ede731SDavid du Colombier 		if(lockready(d) == -1)
230081ede731SDavid du Colombier 			error(Eio);
2301b1707c5dSDavid du Colombier 		d->portm.smart = 2 + smartrs(&d->portc);
2302b1707c5dSDavid du Colombier 		qunlock(&d->portm);
2303b1707c5dSDavid du Colombier 		poperror();
2304b1707c5dSDavid du Colombier 	}else if(strcmp(f[0], "smartdisable") == 0)
2305b1707c5dSDavid du Colombier 		runsmartable(d, 1);
2306b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "smartenable") == 0)
2307b1707c5dSDavid du Colombier 		runsmartable(d, 0);
2308b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "state") == 0)
2309b1707c5dSDavid du Colombier 		forcestate(d, f[1]? f[1]: "null");
2310b1707c5dSDavid du Colombier 	else{
2311b1707c5dSDavid du Colombier 		cmderror(cmd, Ebadctl);
2312b1707c5dSDavid du Colombier 		return -1;
2313b1707c5dSDavid du Colombier 	}
2314b1707c5dSDavid du Colombier 	return 0;
2315b1707c5dSDavid du Colombier }
2316b1707c5dSDavid du Colombier 
2317b1707c5dSDavid du Colombier static char *
portr(char * p,char * e,uint x)2318b1707c5dSDavid du Colombier portr(char *p, char *e, uint x)
2319b1707c5dSDavid du Colombier {
2320b1707c5dSDavid du Colombier 	int i, a;
2321b1707c5dSDavid du Colombier 
2322b1707c5dSDavid du Colombier 	p[0] = 0;
2323b1707c5dSDavid du Colombier 	a = -1;
2324b1707c5dSDavid du Colombier 	for(i = 0; i < 32; i++){
2325b1707c5dSDavid du Colombier 		if((x & (1<<i)) == 0){
2326b1707c5dSDavid du Colombier 			if(a != -1 && i - 1 != a)
2327b1707c5dSDavid du Colombier 				p = seprint(p, e, "-%d", i - 1);
2328b1707c5dSDavid du Colombier 			a = -1;
2329b1707c5dSDavid du Colombier 			continue;
2330b1707c5dSDavid du Colombier 		}
2331b1707c5dSDavid du Colombier 		if(a == -1){
2332b1707c5dSDavid du Colombier 			if(i > 0)
2333b1707c5dSDavid du Colombier 				p = seprint(p, e, ", ");
2334b1707c5dSDavid du Colombier 			p = seprint(p, e, "%d", a = i);
2335b1707c5dSDavid du Colombier 		}
2336b1707c5dSDavid du Colombier 	}
2337b1707c5dSDavid du Colombier 	if(a != -1 && i - 1 != a)
2338b1707c5dSDavid du Colombier 		p = seprint(p, e, "-%d", i - 1);
2339b1707c5dSDavid du Colombier 	return p;
2340b1707c5dSDavid du Colombier }
2341b1707c5dSDavid du Colombier 
2342b1707c5dSDavid du Colombier /* must emit exactly one line per controller (sd(3)) */
2343b1707c5dSDavid du Colombier static char*
iartopctl(SDev * sdev,char * p,char * e)2344b1707c5dSDavid du Colombier iartopctl(SDev *sdev, char *p, char *e)
2345b1707c5dSDavid du Colombier {
234691b330d9SDavid du Colombier 	ulong cap;
2347b1707c5dSDavid du Colombier 	char pr[25];
2348b1707c5dSDavid du Colombier 	Ahba *hba;
2349b1707c5dSDavid du Colombier 	Ctlr *ctlr;
2350b1707c5dSDavid du Colombier 
2351b1707c5dSDavid du Colombier #define has(x, str) if(cap & (x)) p = seprint(p, e, "%s ", (str))
2352b1707c5dSDavid du Colombier 
2353b1707c5dSDavid du Colombier 	ctlr = sdev->ctlr;
2354b1707c5dSDavid du Colombier 	hba = ctlr->hba;
235575cb58dbSDavid du Colombier 	p = seprint(p, e, "sd%c ahci port %#p: ", sdev->idno, ctlr->physio);
2356b1707c5dSDavid du Colombier 	cap = hba->cap;
2357b1707c5dSDavid du Colombier 	has(Hs64a, "64a");
2358b1707c5dSDavid du Colombier 	has(Hsalp, "alp");
2359b1707c5dSDavid du Colombier 	has(Hsam, "am");
2360b1707c5dSDavid du Colombier 	has(Hsclo, "clo");
2361b1707c5dSDavid du Colombier 	has(Hcccs, "coal");
2362b1707c5dSDavid du Colombier 	has(Hems, "ems");
2363b1707c5dSDavid du Colombier 	has(Hsal, "led");
2364b1707c5dSDavid du Colombier 	has(Hsmps, "mps");
2365b1707c5dSDavid du Colombier 	has(Hsncq, "ncq");
2366b1707c5dSDavid du Colombier 	has(Hssntf, "ntf");
2367b1707c5dSDavid du Colombier 	has(Hspm, "pm");
2368b1707c5dSDavid du Colombier 	has(Hpsc, "pslum");
2369b1707c5dSDavid du Colombier 	has(Hssc, "slum");
2370b1707c5dSDavid du Colombier 	has(Hsss, "ss");
2371b1707c5dSDavid du Colombier 	has(Hsxs, "sxs");
2372b1707c5dSDavid du Colombier 	portr(pr, pr + sizeof pr, hba->pi);
2373b1707c5dSDavid du Colombier 	return seprint(p, e,
2374f0499952SDavid du Colombier 		"iss %ld ncs %ld np %ld; ghc %#lux isr %#lux pi %#lux %s ver %#lux\n",
2375b1707c5dSDavid du Colombier 		(cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f),
2376b1707c5dSDavid du Colombier 		hba->ghc, hba->isr, hba->pi, pr, hba->ver);
2377b1707c5dSDavid du Colombier #undef has
2378b1707c5dSDavid du Colombier }
2379b1707c5dSDavid du Colombier 
2380b1707c5dSDavid du Colombier static int
iawtopctl(SDev *,Cmdbuf * cmd)2381b1707c5dSDavid du Colombier iawtopctl(SDev *, Cmdbuf *cmd)
2382b1707c5dSDavid du Colombier {
2383b1707c5dSDavid du Colombier 	int *v;
2384b1707c5dSDavid du Colombier 	char **f;
2385b1707c5dSDavid du Colombier 
2386b1707c5dSDavid du Colombier 	f = cmd->f;
2387b1707c5dSDavid du Colombier 	v = 0;
2388b1707c5dSDavid du Colombier 
23899e8a50a9SDavid du Colombier 	if (f[0] == nil)
23909e8a50a9SDavid du Colombier 		return 0;
2391b1707c5dSDavid du Colombier 	if(strcmp(f[0], "debug") == 0)
2392b1707c5dSDavid du Colombier 		v = &debug;
2393b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "idprint") == 0)
2394b1707c5dSDavid du Colombier 		v = &prid;
2395b1707c5dSDavid du Colombier 	else if(strcmp(f[0], "aprint") == 0)
2396b1707c5dSDavid du Colombier 		v = &datapi;
2397b1707c5dSDavid du Colombier 	else
2398b1707c5dSDavid du Colombier 		cmderror(cmd, Ebadctl);
2399b1707c5dSDavid du Colombier 
2400b1707c5dSDavid du Colombier 	switch(cmd->nf){
2401b1707c5dSDavid du Colombier 	default:
2402b1707c5dSDavid du Colombier 		cmderror(cmd, Ebadarg);
2403b1707c5dSDavid du Colombier 	case 1:
2404b1707c5dSDavid du Colombier 		*v ^= 1;
2405b1707c5dSDavid du Colombier 		break;
2406b1707c5dSDavid du Colombier 	case 2:
24079e8a50a9SDavid du Colombier 		if(f[1])
24089e8a50a9SDavid du Colombier 			*v = strcmp(f[1], "on") == 0;
24099e8a50a9SDavid du Colombier 		else
24109e8a50a9SDavid du Colombier 			*v ^= 1;
2411b1707c5dSDavid du Colombier 		break;
2412b1707c5dSDavid du Colombier 	}
2413b1707c5dSDavid du Colombier 	return 0;
2414b1707c5dSDavid du Colombier }
2415b1707c5dSDavid du Colombier 
2416b1707c5dSDavid du Colombier SDifc sdiahciifc = {
2417b1707c5dSDavid du Colombier 	"iahci",
2418b1707c5dSDavid du Colombier 
2419b1707c5dSDavid du Colombier 	iapnp,
2420b1707c5dSDavid du Colombier 	nil,		/* legacy */
2421b1707c5dSDavid du Colombier 	iaenable,
2422b1707c5dSDavid du Colombier 	iadisable,
2423b1707c5dSDavid du Colombier 
2424b1707c5dSDavid du Colombier 	iaverify,
2425b1707c5dSDavid du Colombier 	iaonline,
2426b1707c5dSDavid du Colombier 	iario,
2427b1707c5dSDavid du Colombier 	iarctl,
2428b1707c5dSDavid du Colombier 	iawctl,
2429b1707c5dSDavid du Colombier 
2430b1707c5dSDavid du Colombier 	scsibio,
2431b1707c5dSDavid du Colombier 	nil,		/* probe */
2432b1707c5dSDavid du Colombier 	nil,		/* clear */
2433b1707c5dSDavid du Colombier 	iartopctl,
2434b1707c5dSDavid du Colombier 	iawtopctl,
2435b1707c5dSDavid du Colombier };
2436