xref: /plan9-contrib/sys/src/9/kw/sdio.c (revision 0cc6832d7c845a6250c7353daab06d4af2dfe5cb)
1*0cc6832dSDavid du Colombier /*
2*0cc6832dSDavid du Colombier  * kirkwood SDIO / SDMem / MMC host interface
3*0cc6832dSDavid du Colombier  */
4*0cc6832dSDavid du Colombier 
5*0cc6832dSDavid du Colombier #include "u.h"
6*0cc6832dSDavid du Colombier #include "../port/lib.h"
7*0cc6832dSDavid du Colombier #include "../port/error.h"
8*0cc6832dSDavid du Colombier #include "mem.h"
9*0cc6832dSDavid du Colombier #include "dat.h"
10*0cc6832dSDavid du Colombier #include "fns.h"
11*0cc6832dSDavid du Colombier #include "io.h"
12*0cc6832dSDavid du Colombier #include "../port/sd.h"
13*0cc6832dSDavid du Colombier 
14*0cc6832dSDavid du Colombier #define TM(bits)	((bits)<<16)
15*0cc6832dSDavid du Colombier #define	GETTM(bits)	(((bits)>>16)&0xFFFF)
16*0cc6832dSDavid du Colombier #define GETCMD(bits)	((bits)&0xFFFF)
17*0cc6832dSDavid du Colombier 
18*0cc6832dSDavid du Colombier typedef struct Ctlr Ctlr;
19*0cc6832dSDavid du Colombier 
20*0cc6832dSDavid du Colombier enum {
21*0cc6832dSDavid du Colombier 	Clkfreq	= 100000000,	/* external clk frequency */
22*0cc6832dSDavid du Colombier 	Initfreq= 400000,	/* initialisation frequency for MMC */
23*0cc6832dSDavid du Colombier 	SDfreq	= 25000000,	/* standard SD frequency */
24*0cc6832dSDavid du Colombier 	PIOread	= 0,		/* use programmed i/o (not dma) for reading */
25*0cc6832dSDavid du Colombier 	PIOwrite= 0,		/* use programmed i/o (not dma) writing */
26*0cc6832dSDavid du Colombier 	Polldone= 0,		/* poll for Datadone status, don't use interrupt */
27*0cc6832dSDavid du Colombier 	Pollread= 1,		/* poll for reading blocks */
28*0cc6832dSDavid du Colombier 	Pollwrite= 1,		/* poll for writing blocks */
29*0cc6832dSDavid du Colombier 
30*0cc6832dSDavid du Colombier 	MMCSelect= 7,		/* mmc/sd card select command */
31*0cc6832dSDavid du Colombier 	Setbuswidth= 6,		/* mmc/sd set bus width command */
32*0cc6832dSDavid du Colombier };
33*0cc6832dSDavid du Colombier 
34*0cc6832dSDavid du Colombier enum {
35*0cc6832dSDavid du Colombier 	/* Controller registers */
36*0cc6832dSDavid du Colombier 	DmaLSB		= 0x0>>2,
37*0cc6832dSDavid du Colombier 	DmaMSB		= 0x4>>2,
38*0cc6832dSDavid du Colombier 	Blksize		= 0x8>>2,
39*0cc6832dSDavid du Colombier 	Blkcount	= 0xc>>2,
40*0cc6832dSDavid du Colombier 	ArgLSB		= 0x10>>2,
41*0cc6832dSDavid du Colombier 	ArgMSB		= 0x14>>2,
42*0cc6832dSDavid du Colombier 	Tm		= 0x18>>2,
43*0cc6832dSDavid du Colombier 	Cmd		= 0x1c>>2,
44*0cc6832dSDavid du Colombier 	Resp0		= 0x20>>2,
45*0cc6832dSDavid du Colombier 	Resp1		= 0x24>>2,
46*0cc6832dSDavid du Colombier 	Resp2		= 0x28>>2,
47*0cc6832dSDavid du Colombier 	Resp3		= 0x2c>>2,
48*0cc6832dSDavid du Colombier 	Resp4		= 0x30>>2,
49*0cc6832dSDavid du Colombier 	Resp5		= 0x34>>2,
50*0cc6832dSDavid du Colombier 	Resp6		= 0x38>>2,
51*0cc6832dSDavid du Colombier 	Resp7		= 0x3c>>2,
52*0cc6832dSDavid du Colombier 	Data		= 0x40>>2,
53*0cc6832dSDavid du Colombier 	Hoststat	= 0x48>>2,
54*0cc6832dSDavid du Colombier 	Hostctl		= 0x50>>2,
55*0cc6832dSDavid du Colombier 	Clockctl	= 0x58>>2,
56*0cc6832dSDavid du Colombier 	Softreset	= 0x5C>>2,
57*0cc6832dSDavid du Colombier 	Interrupt	= 0x60>>2,
58*0cc6832dSDavid du Colombier 	ErrIntr		= 0x64>>2,
59*0cc6832dSDavid du Colombier 	Irptmask	= 0x68>>2,
60*0cc6832dSDavid du Colombier 	ErrIrptmask	= 0x6C>>2,
61*0cc6832dSDavid du Colombier 	Irpten		= 0x70>>2,
62*0cc6832dSDavid du Colombier 	ErrIrpten	= 0x74>>2,
63*0cc6832dSDavid du Colombier 	Mbuslo		= 0x100>>2,
64*0cc6832dSDavid du Colombier 	Mbushi		= 0x104>>2,
65*0cc6832dSDavid du Colombier 	Win0ctl		= 0x108>>2,
66*0cc6832dSDavid du Colombier 	Win0base	= 0x10c>>2,
67*0cc6832dSDavid du Colombier 	Win1ctl		= 0x110>>2,
68*0cc6832dSDavid du Colombier 	Win1base	= 0x114>>2,
69*0cc6832dSDavid du Colombier 	Win2ctl		= 0x118>>2,
70*0cc6832dSDavid du Colombier 	Win2base	= 0x11c>>2,
71*0cc6832dSDavid du Colombier 	Win3ctl		= 0x120>>2,
72*0cc6832dSDavid du Colombier 	Win3base	= 0x124>>2,
73*0cc6832dSDavid du Colombier 	Clockdiv	= 0x128>>2,
74*0cc6832dSDavid du Colombier 
75*0cc6832dSDavid du Colombier 	/* Hostctl */
76*0cc6832dSDavid du Colombier 	Timeouten	= 1<<15,
77*0cc6832dSDavid du Colombier 	Datatoshift	= 11,
78*0cc6832dSDavid du Colombier 	Datatomask	= 0x7800,
79*0cc6832dSDavid du Colombier 	Hispeed		= 1<<10,
80*0cc6832dSDavid du Colombier 	Dwidth4		= 1<<9,
81*0cc6832dSDavid du Colombier 	Dwidth1		= 0<<9,
82*0cc6832dSDavid du Colombier 	Bigendian	= 1<<3,
83*0cc6832dSDavid du Colombier 	LSBfirst	= 1<<4,
84*0cc6832dSDavid du Colombier 	Cardtypemask	= 3<<1,
85*0cc6832dSDavid du Colombier 	Cardtypemem	= 0<<1,
86*0cc6832dSDavid du Colombier 	Cardtypeio	= 1<<1,
87*0cc6832dSDavid du Colombier 	Cardtypeiomem	= 2<<1,
88*0cc6832dSDavid du Colombier 	Cardtypsdio	= 3<<1,
89*0cc6832dSDavid du Colombier 	Pushpullen	= 1<<0,
90*0cc6832dSDavid du Colombier 
91*0cc6832dSDavid du Colombier 	/* Clockctl */
92*0cc6832dSDavid du Colombier 	Sdclken		= 1<<0,
93*0cc6832dSDavid du Colombier 
94*0cc6832dSDavid du Colombier 	/* Softreset */
95*0cc6832dSDavid du Colombier 	Swreset		= 1<<8,
96*0cc6832dSDavid du Colombier 
97*0cc6832dSDavid du Colombier 	/* Cmd */
98*0cc6832dSDavid du Colombier 	Indexshift	= 8,
99*0cc6832dSDavid du Colombier 	Isdata		= 1<<5,
100*0cc6832dSDavid du Colombier 	Ixchken		= 1<<4,
101*0cc6832dSDavid du Colombier 	Crcchken	= 3<<2,
102*0cc6832dSDavid du Colombier 	Respmask	= 3<<0,
103*0cc6832dSDavid du Colombier 	Respnone	= 0<<0,
104*0cc6832dSDavid du Colombier 	Resp136		= 1<<0,
105*0cc6832dSDavid du Colombier 	Resp48		= 2<<0,
106*0cc6832dSDavid du Colombier 	Resp48busy	= 3<<0,
107*0cc6832dSDavid du Colombier 
108*0cc6832dSDavid du Colombier 	/* Tm */
109*0cc6832dSDavid du Colombier 	Hostdma		= 0<<6,
110*0cc6832dSDavid du Colombier 	Hostpio		= 1<<6,
111*0cc6832dSDavid du Colombier 	Stopclken	= 1<<5,
112*0cc6832dSDavid du Colombier 	Host2card	= 0<<4,
113*0cc6832dSDavid du Colombier 	Card2host	= 1<<4,
114*0cc6832dSDavid du Colombier 	Autocmd12	= 1<<2,
115*0cc6832dSDavid du Colombier 	Hwwrdata	= 1<<1,
116*0cc6832dSDavid du Colombier 	Swwrdata	= 1<<0,
117*0cc6832dSDavid du Colombier 
118*0cc6832dSDavid du Colombier 	/* ErrIntr */
119*0cc6832dSDavid du Colombier 	Crcstaterr	= 1<<14,
120*0cc6832dSDavid du Colombier 	Crcstartbiterr	= 1<<13,
121*0cc6832dSDavid du Colombier 	Crcendbiterr	= 1<<12,
122*0cc6832dSDavid du Colombier 	Resptbiterr	= 1<<11,
123*0cc6832dSDavid du Colombier 	Xfersizeerr	= 1<<10,
124*0cc6832dSDavid du Colombier 	Cmdstarterr	= 1<<9,
125*0cc6832dSDavid du Colombier 	Acmderr		= 1<<8,
126*0cc6832dSDavid du Colombier 	Denderr		= 1<<6,
127*0cc6832dSDavid du Colombier 	Dcrcerr		= 1<<5,
128*0cc6832dSDavid du Colombier 	Dtoerr		= 1<<4,
129*0cc6832dSDavid du Colombier 	Cbaderr		= 1<<3,
130*0cc6832dSDavid du Colombier 	Cenderr		= 1<<2,
131*0cc6832dSDavid du Colombier 	Ccrcerr		= 1<<1,
132*0cc6832dSDavid du Colombier 	Ctoerr		= 1<<0,
133*0cc6832dSDavid du Colombier 
134*0cc6832dSDavid du Colombier 	/* Interrupt */
135*0cc6832dSDavid du Colombier 	Err		= 1<<15,
136*0cc6832dSDavid du Colombier 	Write8ready	= 1<<11,
137*0cc6832dSDavid du Colombier 	Read8wready	= 1<<10,
138*0cc6832dSDavid du Colombier 	Cardintr	= 1<<8,
139*0cc6832dSDavid du Colombier 	Readrdy		= 1<<5,
140*0cc6832dSDavid du Colombier 	Writerdy	= 1<<4,
141*0cc6832dSDavid du Colombier 	Dmadone		= 1<<3,
142*0cc6832dSDavid du Colombier 	Blockgap	= 1<<2,
143*0cc6832dSDavid du Colombier 	Datadone	= 1<<1,
144*0cc6832dSDavid du Colombier 	Cmddone		= 1<<0,
145*0cc6832dSDavid du Colombier 
146*0cc6832dSDavid du Colombier 	/* Hoststat */
147*0cc6832dSDavid du Colombier 	Fifoempty	= 1<<13,
148*0cc6832dSDavid du Colombier 	Fifofull	= 1<<12,
149*0cc6832dSDavid du Colombier 	Rxactive	= 1<<9,
150*0cc6832dSDavid du Colombier 	Txactive	= 1<<8,
151*0cc6832dSDavid du Colombier 	Cardbusy	= 1<<1,
152*0cc6832dSDavid du Colombier 	Cmdinhibit	= 1<<0,
153*0cc6832dSDavid du Colombier };
154*0cc6832dSDavid du Colombier 
155*0cc6832dSDavid du Colombier int cmdinfo[64] = {
156*0cc6832dSDavid du Colombier [0]	Ixchken,
157*0cc6832dSDavid du Colombier [2]	Resp136,
158*0cc6832dSDavid du Colombier [3]	Resp48 | Ixchken | Crcchken,
159*0cc6832dSDavid du Colombier [6]	Resp48 | Ixchken | Crcchken,
160*0cc6832dSDavid du Colombier [7]	Resp48busy | Ixchken | Crcchken,
161*0cc6832dSDavid du Colombier [8]	Resp48 | Ixchken | Crcchken,
162*0cc6832dSDavid du Colombier [9]	Resp136,
163*0cc6832dSDavid du Colombier [12]	Resp48busy | Ixchken | Crcchken,
164*0cc6832dSDavid du Colombier [13]	Resp48 | Ixchken | Crcchken,
165*0cc6832dSDavid du Colombier [16]	Resp48,
166*0cc6832dSDavid du Colombier [17]	Resp48 | Isdata | TM(Card2host) | Ixchken | Crcchken,
167*0cc6832dSDavid du Colombier [18]	Resp48 | Isdata | TM(Card2host) | Ixchken | Crcchken,
168*0cc6832dSDavid du Colombier [24]	Resp48 | Isdata | TM(Host2card | Hwwrdata) | Ixchken | Crcchken,
169*0cc6832dSDavid du Colombier [25]	Resp48 | Isdata | TM(Host2card | Hwwrdata) | Ixchken | Crcchken,
170*0cc6832dSDavid du Colombier [41]	Resp48,
171*0cc6832dSDavid du Colombier [55]	Resp48 | Ixchken | Crcchken,
172*0cc6832dSDavid du Colombier };
173*0cc6832dSDavid du Colombier 
174*0cc6832dSDavid du Colombier struct Ctlr {
175*0cc6832dSDavid du Colombier 	Rendez	r;
176*0cc6832dSDavid du Colombier 	int	datadone;
177*0cc6832dSDavid du Colombier 	int	fastclock;
178*0cc6832dSDavid du Colombier };
179*0cc6832dSDavid du Colombier 
180*0cc6832dSDavid du Colombier static Ctlr ctlr;
181*0cc6832dSDavid du Colombier 
182*0cc6832dSDavid du Colombier static void sdiointerrupt(Ureg*, void*);
183*0cc6832dSDavid du Colombier 
184*0cc6832dSDavid du Colombier void
WR(int reg,u32int val)185*0cc6832dSDavid du Colombier WR(int reg, u32int val)
186*0cc6832dSDavid du Colombier {
187*0cc6832dSDavid du Colombier 	u32int *r;
188*0cc6832dSDavid du Colombier 
189*0cc6832dSDavid du Colombier 	r = (u32int*)AddrSdio;
190*0cc6832dSDavid du Colombier 	val &= 0xFFFF;
191*0cc6832dSDavid du Colombier 	if(0)iprint("WR %#4.4ux %#ux\n", reg<<2, val);
192*0cc6832dSDavid du Colombier 	r[reg] = val;
193*0cc6832dSDavid du Colombier }
194*0cc6832dSDavid du Colombier 
195*0cc6832dSDavid du Colombier static uint
clkdiv(uint d)196*0cc6832dSDavid du Colombier clkdiv(uint d)
197*0cc6832dSDavid du Colombier {
198*0cc6832dSDavid du Colombier 	assert(d < 1<<11);
199*0cc6832dSDavid du Colombier 	return d;
200*0cc6832dSDavid du Colombier }
201*0cc6832dSDavid du Colombier 
202*0cc6832dSDavid du Colombier static int
datadone(void *)203*0cc6832dSDavid du Colombier datadone(void*)
204*0cc6832dSDavid du Colombier {
205*0cc6832dSDavid du Colombier 	return ctlr.datadone;
206*0cc6832dSDavid du Colombier }
207*0cc6832dSDavid du Colombier 
208*0cc6832dSDavid du Colombier static int
sdioinit(void)209*0cc6832dSDavid du Colombier sdioinit(void)
210*0cc6832dSDavid du Colombier {
211*0cc6832dSDavid du Colombier 	u32int *r;
212*0cc6832dSDavid du Colombier 
213*0cc6832dSDavid du Colombier 	r = (u32int*)AddrSdio;
214*0cc6832dSDavid du Colombier 	WR(Softreset, Swreset);
215*0cc6832dSDavid du Colombier 	while(r[Softreset] & Swreset)
216*0cc6832dSDavid du Colombier 		;
217*0cc6832dSDavid du Colombier 	delay(10);
218*0cc6832dSDavid du Colombier 	return 0;
219*0cc6832dSDavid du Colombier }
220*0cc6832dSDavid du Colombier 
221*0cc6832dSDavid du Colombier static int
sdioinquiry(char * inquiry,int inqlen)222*0cc6832dSDavid du Colombier sdioinquiry(char *inquiry, int inqlen)
223*0cc6832dSDavid du Colombier {
224*0cc6832dSDavid du Colombier 	return snprint(inquiry, inqlen, "SDIO Host Controller");
225*0cc6832dSDavid du Colombier }
226*0cc6832dSDavid du Colombier 
227*0cc6832dSDavid du Colombier static void
sdioenable(void)228*0cc6832dSDavid du Colombier sdioenable(void)
229*0cc6832dSDavid du Colombier {
230*0cc6832dSDavid du Colombier 	u32int *r;
231*0cc6832dSDavid du Colombier 
232*0cc6832dSDavid du Colombier 	r = (u32int*)AddrSdio;
233*0cc6832dSDavid du Colombier 	WR(Clockdiv, clkdiv(Clkfreq/Initfreq - 1));
234*0cc6832dSDavid du Colombier 	delay(10);
235*0cc6832dSDavid du Colombier 	WR(Clockctl, r[Clockctl] & ~Sdclken);
236*0cc6832dSDavid du Colombier 	WR(Hostctl, Pushpullen|Bigendian|Cardtypemem);
237*0cc6832dSDavid du Colombier 	WR(Irpten, 0);
238*0cc6832dSDavid du Colombier 	WR(Interrupt, ~0);
239*0cc6832dSDavid du Colombier 	WR(ErrIntr, ~0);
240*0cc6832dSDavid du Colombier 	WR(Irptmask, ~0);
241*0cc6832dSDavid du Colombier 	WR(ErrIrptmask, ~Dtoerr);
242*0cc6832dSDavid du Colombier 	intrenable(Irqlo, IRQ0sdio, sdiointerrupt, &ctlr, "sdio");
243*0cc6832dSDavid du Colombier }
244*0cc6832dSDavid du Colombier 
245*0cc6832dSDavid du Colombier static int
awaitdone(u32int * r,int bits,int ticks)246*0cc6832dSDavid du Colombier awaitdone(u32int *r, int bits, int ticks)
247*0cc6832dSDavid du Colombier {
248*0cc6832dSDavid du Colombier 	int i;
249*0cc6832dSDavid du Colombier 	ulong start;
250*0cc6832dSDavid du Colombier 
251*0cc6832dSDavid du Colombier 	start = m->ticks;
252*0cc6832dSDavid du Colombier 	while(((i = r[Interrupt]) & (bits|Err)) == 0)
253*0cc6832dSDavid du Colombier 		if(m->ticks - start > ticks)
254*0cc6832dSDavid du Colombier 			break;
255*0cc6832dSDavid du Colombier 	return i;
256*0cc6832dSDavid du Colombier }
257*0cc6832dSDavid du Colombier 
258*0cc6832dSDavid du Colombier static void
ckerr(u32int * r,int i,int len,char * op)259*0cc6832dSDavid du Colombier ckerr(u32int *r, int i, int len, char *op)
260*0cc6832dSDavid du Colombier {
261*0cc6832dSDavid du Colombier 	int err;
262*0cc6832dSDavid du Colombier 
263*0cc6832dSDavid du Colombier 	if(i & Err){
264*0cc6832dSDavid du Colombier 		err = r[ErrIntr];
265*0cc6832dSDavid du Colombier 		iprint("sdioio: (%d) %s error intr %#ux err %#ux stat %#ux\n",
266*0cc6832dSDavid du Colombier 			len, op, i, err, r[Hoststat]);
267*0cc6832dSDavid du Colombier 		WR(ErrIntr, err);
268*0cc6832dSDavid du Colombier 		WR(Interrupt, i);
269*0cc6832dSDavid du Colombier 		error(Eio);
270*0cc6832dSDavid du Colombier 	}
271*0cc6832dSDavid du Colombier }
272*0cc6832dSDavid du Colombier 
273*0cc6832dSDavid du Colombier static void
ckdmadone(u32int * r,int i,char * msg)274*0cc6832dSDavid du Colombier ckdmadone(u32int *r, int i, char *msg)
275*0cc6832dSDavid du Colombier {
276*0cc6832dSDavid du Colombier 	if((i & Dmadone) == 0){
277*0cc6832dSDavid du Colombier 		iprint("sdioio: %s intr %#ux stat %#ux\n", msg, i, r[Hoststat]);
278*0cc6832dSDavid du Colombier 		WR(Interrupt, i);
279*0cc6832dSDavid du Colombier 		error(Eio);
280*0cc6832dSDavid du Colombier 	}
281*0cc6832dSDavid du Colombier }
282*0cc6832dSDavid du Colombier 
283*0cc6832dSDavid du Colombier static void
getresp(u32int * r,u32int * resp,int resptype)284*0cc6832dSDavid du Colombier getresp(u32int *r, u32int *resp, int resptype)
285*0cc6832dSDavid du Colombier {
286*0cc6832dSDavid du Colombier 	switch(resptype){
287*0cc6832dSDavid du Colombier 	case Resp136:
288*0cc6832dSDavid du Colombier 		resp[0] = r[Resp7]<<8  | r[Resp6]<<22;
289*0cc6832dSDavid du Colombier 		resp[1] = r[Resp6]>>10 | r[Resp5]<<6 | r[Resp4]<<22;
290*0cc6832dSDavid du Colombier 		resp[2] = r[Resp4]>>10 | r[Resp3]<<6 | r[Resp2]<<22;
291*0cc6832dSDavid du Colombier 		resp[3] = r[Resp2]>>10 | r[Resp1]<<6 | r[Resp0]<<22;
292*0cc6832dSDavid du Colombier 		break;
293*0cc6832dSDavid du Colombier 	case Resp48:
294*0cc6832dSDavid du Colombier 	case Resp48busy:
295*0cc6832dSDavid du Colombier 		resp[0] = r[Resp2] | r[Resp1]<<6 | r[Resp0]<<22;
296*0cc6832dSDavid du Colombier 		break;
297*0cc6832dSDavid du Colombier 	case Respnone:
298*0cc6832dSDavid du Colombier 		resp[0] = 0;
299*0cc6832dSDavid du Colombier 		break;
300*0cc6832dSDavid du Colombier 	}
301*0cc6832dSDavid du Colombier }
302*0cc6832dSDavid du Colombier 
303*0cc6832dSDavid du Colombier static void
awaitresp48data(u32int * r,u32int cmd)304*0cc6832dSDavid du Colombier awaitresp48data(u32int *r, u32int cmd)
305*0cc6832dSDavid du Colombier {
306*0cc6832dSDavid du Colombier 	int i;
307*0cc6832dSDavid du Colombier 
308*0cc6832dSDavid du Colombier 	if(Polldone)
309*0cc6832dSDavid du Colombier 		i = awaitdone(r, Datadone, 3*HZ);
310*0cc6832dSDavid du Colombier 	else{
311*0cc6832dSDavid du Colombier 		WR(Irpten, Datadone|Err);
312*0cc6832dSDavid du Colombier 		tsleep(&ctlr.r, datadone, 0, 3000);
313*0cc6832dSDavid du Colombier 		i = ctlr.datadone;
314*0cc6832dSDavid du Colombier 		ctlr.datadone = 0;
315*0cc6832dSDavid du Colombier 		WR(Irpten, 0);
316*0cc6832dSDavid du Colombier 	}
317*0cc6832dSDavid du Colombier 	if((i & Datadone) == 0)
318*0cc6832dSDavid du Colombier 		iprint("sdioio: no Datadone after CMD%d\n", cmd);
319*0cc6832dSDavid du Colombier 	if(i & Err)
320*0cc6832dSDavid du Colombier 		iprint("sdioio: CMD%d error interrupt %#ux %#ux\n",
321*0cc6832dSDavid du Colombier 			cmd, r[Interrupt], r[ErrIntr]);
322*0cc6832dSDavid du Colombier 	WR(Interrupt, i);
323*0cc6832dSDavid du Colombier }
324*0cc6832dSDavid du Colombier 
325*0cc6832dSDavid du Colombier static void
finishcmd(u32int cmd,u32int arg)326*0cc6832dSDavid du Colombier finishcmd(u32int cmd, u32int arg)
327*0cc6832dSDavid du Colombier {
328*0cc6832dSDavid du Colombier 	u32int *r;
329*0cc6832dSDavid du Colombier 
330*0cc6832dSDavid du Colombier 	/*
331*0cc6832dSDavid du Colombier 	 * Once card is selected, use faster clock.
332*0cc6832dSDavid du Colombier 	 * If card bus width changes, change host bus width.
333*0cc6832dSDavid du Colombier 	 */
334*0cc6832dSDavid du Colombier 	r = (u32int*)AddrSdio;
335*0cc6832dSDavid du Colombier 	if(cmd == MMCSelect){
336*0cc6832dSDavid du Colombier 		delay(10);
337*0cc6832dSDavid du Colombier 		WR(Clockdiv, clkdiv(Clkfreq/SDfreq - 1));
338*0cc6832dSDavid du Colombier 		delay(10);
339*0cc6832dSDavid du Colombier 		ctlr.fastclock = 1;
340*0cc6832dSDavid du Colombier 	} else if(cmd == Setbuswidth)
341*0cc6832dSDavid du Colombier 		switch(arg){
342*0cc6832dSDavid du Colombier 		case 0:
343*0cc6832dSDavid du Colombier 			WR(Hostctl, r[Hostctl] & ~Dwidth4);
344*0cc6832dSDavid du Colombier 			break;
345*0cc6832dSDavid du Colombier 		case 2:
346*0cc6832dSDavid du Colombier 			WR(Hostctl, r[Hostctl] | Dwidth4);
347*0cc6832dSDavid du Colombier 			break;
348*0cc6832dSDavid du Colombier 		}
349*0cc6832dSDavid du Colombier }
350*0cc6832dSDavid du Colombier 
351*0cc6832dSDavid du Colombier static int
sdiocmd(u32int cmd,u32int arg,u32int * resp)352*0cc6832dSDavid du Colombier sdiocmd(u32int cmd, u32int arg, u32int *resp)
353*0cc6832dSDavid du Colombier {
354*0cc6832dSDavid du Colombier 	int i, err;
355*0cc6832dSDavid du Colombier 	u32int c;
356*0cc6832dSDavid du Colombier 	u32int *r;
357*0cc6832dSDavid du Colombier 
358*0cc6832dSDavid du Colombier 	assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0);
359*0cc6832dSDavid du Colombier 	i = GETTM(cmdinfo[cmd]);
360*0cc6832dSDavid du Colombier 	c = cmd<<Indexshift | GETCMD(cmdinfo[cmd]);
361*0cc6832dSDavid du Colombier 	if(c & Isdata)
362*0cc6832dSDavid du Colombier 		if(i & Card2host)
363*0cc6832dSDavid du Colombier 			i |= PIOread?  Hostpio: Hostdma;
364*0cc6832dSDavid du Colombier 		else
365*0cc6832dSDavid du Colombier 			i |= PIOwrite? Hostpio: Hostdma;
366*0cc6832dSDavid du Colombier 	WR(Tm, i);
367*0cc6832dSDavid du Colombier 	WR(ArgLSB, arg);
368*0cc6832dSDavid du Colombier 	WR(ArgMSB, arg>>16);
369*0cc6832dSDavid du Colombier 	WR(ErrIntr, ~0);
370*0cc6832dSDavid du Colombier 	WR(Cmd, c);
371*0cc6832dSDavid du Colombier 
372*0cc6832dSDavid du Colombier 	r = (u32int*)AddrSdio;
373*0cc6832dSDavid du Colombier 	i = awaitdone(r, Cmddone, HZ);
374*0cc6832dSDavid du Colombier 	if((i & (Cmddone|Err)) != Cmddone){
375*0cc6832dSDavid du Colombier 		if((err = r[ErrIntr]) != Ctoerr)
376*0cc6832dSDavid du Colombier 			iprint("sdio: cmd %#ux error intr %#ux %#ux stat %#ux\n",
377*0cc6832dSDavid du Colombier 				c, i, err, r[Hoststat]);
378*0cc6832dSDavid du Colombier 		WR(ErrIntr, err);
379*0cc6832dSDavid du Colombier 		WR(Interrupt, i);
380*0cc6832dSDavid du Colombier 		error(Eio);
381*0cc6832dSDavid du Colombier 	}
382*0cc6832dSDavid du Colombier 	WR(Interrupt, i & ~Datadone);
383*0cc6832dSDavid du Colombier 
384*0cc6832dSDavid du Colombier 	c &= Respmask;
385*0cc6832dSDavid du Colombier 	getresp(r, resp, c);
386*0cc6832dSDavid du Colombier 	if(c == Resp48busy)
387*0cc6832dSDavid du Colombier 		awaitresp48data(r, cmd);
388*0cc6832dSDavid du Colombier 
389*0cc6832dSDavid du Colombier 	finishcmd(cmd, arg);
390*0cc6832dSDavid du Colombier 	return 0;
391*0cc6832dSDavid du Colombier }
392*0cc6832dSDavid du Colombier 
393*0cc6832dSDavid du Colombier static void
sdioiosetup(int write,void * buf,int bsize,int bcount)394*0cc6832dSDavid du Colombier sdioiosetup(int write, void *buf, int bsize, int bcount)
395*0cc6832dSDavid du Colombier {
396*0cc6832dSDavid du Colombier 	int len;
397*0cc6832dSDavid du Colombier 	uintptr pa;
398*0cc6832dSDavid du Colombier 
399*0cc6832dSDavid du Colombier 	pa = PADDR(buf);
400*0cc6832dSDavid du Colombier 	if(write && !PIOwrite){
401*0cc6832dSDavid du Colombier 		WR(DmaLSB, pa);
402*0cc6832dSDavid du Colombier 		WR(DmaMSB, pa>>16);
403*0cc6832dSDavid du Colombier 		len = bsize * bcount;
404*0cc6832dSDavid du Colombier 		cachedwbse(buf, len);
405*0cc6832dSDavid du Colombier 		l2cacheuwbse(buf, len);
406*0cc6832dSDavid du Colombier 	}else if(!write && !PIOread){
407*0cc6832dSDavid du Colombier 		WR(DmaLSB, pa);
408*0cc6832dSDavid du Colombier 		WR(DmaMSB, pa>>16);
409*0cc6832dSDavid du Colombier 		len = bsize * bcount;
410*0cc6832dSDavid du Colombier 		cachedwbinvse(buf, len);
411*0cc6832dSDavid du Colombier 		l2cacheuwbinvse(buf, len);
412*0cc6832dSDavid du Colombier 	}
413*0cc6832dSDavid du Colombier 	WR(Blksize, bsize);
414*0cc6832dSDavid du Colombier 	WR(Blkcount, bcount);
415*0cc6832dSDavid du Colombier }
416*0cc6832dSDavid du Colombier 
417*0cc6832dSDavid du Colombier static uchar *
getdatas(u32int * r,uchar * buf)418*0cc6832dSDavid du Colombier getdatas(u32int *r, uchar *buf)
419*0cc6832dSDavid du Colombier {
420*0cc6832dSDavid du Colombier 	ushort d;
421*0cc6832dSDavid du Colombier 
422*0cc6832dSDavid du Colombier 	d = r[Data];
423*0cc6832dSDavid du Colombier 	*buf++ = d;
424*0cc6832dSDavid du Colombier 	*buf++ = d>>8;
425*0cc6832dSDavid du Colombier 	return buf;
426*0cc6832dSDavid du Colombier }
427*0cc6832dSDavid du Colombier 
428*0cc6832dSDavid du Colombier static int
sdioread(uchar * buf,int * lenp)429*0cc6832dSDavid du Colombier sdioread(uchar *buf, int *lenp)
430*0cc6832dSDavid du Colombier {
431*0cc6832dSDavid du Colombier 	int i, now, len;
432*0cc6832dSDavid du Colombier 	u32int *r;
433*0cc6832dSDavid du Colombier 
434*0cc6832dSDavid du Colombier 	r = (u32int*)AddrSdio;
435*0cc6832dSDavid du Colombier 	i = 0;
436*0cc6832dSDavid du Colombier 	len = *lenp;
437*0cc6832dSDavid du Colombier 	while(len > 0){
438*0cc6832dSDavid du Colombier 		if(Pollread){
439*0cc6832dSDavid du Colombier 			now = m->ticks;
440*0cc6832dSDavid du Colombier 			i = awaitdone(r, Read8wready|Readrdy, 3*HZ);
441*0cc6832dSDavid du Colombier 			if(m->ticks - now > 3*HZ){
442*0cc6832dSDavid du Colombier 				print("sdioio: (%d) no Readrdy intr %#ux stat %#ux\n",
443*0cc6832dSDavid du Colombier 					len, i, r[Hoststat]);
444*0cc6832dSDavid du Colombier 				error(Eio);
445*0cc6832dSDavid du Colombier 			}
446*0cc6832dSDavid du Colombier 		}else{
447*0cc6832dSDavid du Colombier 			i = r[Interrupt];
448*0cc6832dSDavid du Colombier 			if((i & (Read8wready|Readrdy|Err)) == 0){
449*0cc6832dSDavid du Colombier 				WR(Irpten, (len > 8*4? Read8wready:
450*0cc6832dSDavid du Colombier 					Readrdy) | Err);
451*0cc6832dSDavid du Colombier 				tsleep(&ctlr.r, datadone, 0, 3000);
452*0cc6832dSDavid du Colombier 				WR(Irpten, 0);
453*0cc6832dSDavid du Colombier 				i = ctlr.datadone;
454*0cc6832dSDavid du Colombier 				ctlr.datadone = 0;
455*0cc6832dSDavid du Colombier 				if((i & (Read8wready|Readrdy|Err)) == 0){
456*0cc6832dSDavid du Colombier 					print("sdioio: (%d) no Readrdy intr %#ux stat %#ux\n",
457*0cc6832dSDavid du Colombier 						len, i, r[Hoststat]);
458*0cc6832dSDavid du Colombier 					error(Eio);
459*0cc6832dSDavid du Colombier 				}
460*0cc6832dSDavid du Colombier 			}
461*0cc6832dSDavid du Colombier 		}
462*0cc6832dSDavid du Colombier 
463*0cc6832dSDavid du Colombier 		if((i & Read8wready) && len >= 8*2*2){
464*0cc6832dSDavid du Colombier 			for(i = 0; i < 8*2; i++)
465*0cc6832dSDavid du Colombier 				buf = getdatas(r, buf);
466*0cc6832dSDavid du Colombier 			len -= 8*2*2;
467*0cc6832dSDavid du Colombier 		}else if(i & Readrdy){
468*0cc6832dSDavid du Colombier 			buf = getdatas(r, buf);
469*0cc6832dSDavid du Colombier 			buf = getdatas(r, buf);
470*0cc6832dSDavid du Colombier 			len -= 2*2;
471*0cc6832dSDavid du Colombier 		} else
472*0cc6832dSDavid du Colombier 			ckerr(r, i, len, "read");
473*0cc6832dSDavid du Colombier 	}
474*0cc6832dSDavid du Colombier 	*lenp = len;
475*0cc6832dSDavid du Colombier 	return i;
476*0cc6832dSDavid du Colombier }
477*0cc6832dSDavid du Colombier 
478*0cc6832dSDavid du Colombier static int
sdiowrite(uchar * buf,int * lenp)479*0cc6832dSDavid du Colombier sdiowrite(uchar *buf, int *lenp)
480*0cc6832dSDavid du Colombier {
481*0cc6832dSDavid du Colombier 	int i, now, len;
482*0cc6832dSDavid du Colombier 	u32int *r;
483*0cc6832dSDavid du Colombier 
484*0cc6832dSDavid du Colombier 	r = (u32int*)AddrSdio;
485*0cc6832dSDavid du Colombier 	i = 0;
486*0cc6832dSDavid du Colombier 	len = *lenp;
487*0cc6832dSDavid du Colombier 	while(len > 0){
488*0cc6832dSDavid du Colombier 		if(Pollwrite){
489*0cc6832dSDavid du Colombier 			now = m->ticks;
490*0cc6832dSDavid du Colombier 			i = awaitdone(r, Writerdy, 8*HZ);
491*0cc6832dSDavid du Colombier 			if(m->ticks - now > 8*HZ){
492*0cc6832dSDavid du Colombier 				print("sdioio: (%d) no Writerdy intr %#ux stat %#ux\n",
493*0cc6832dSDavid du Colombier 					len, i, r[Hoststat]);
494*0cc6832dSDavid du Colombier 				error(Eio);
495*0cc6832dSDavid du Colombier 			}
496*0cc6832dSDavid du Colombier 		}else{
497*0cc6832dSDavid du Colombier 			i = r[Interrupt];
498*0cc6832dSDavid du Colombier 			if((i & (Writerdy|Err)) == 0){
499*0cc6832dSDavid du Colombier 				WR(Irpten, Writerdy | Err);
500*0cc6832dSDavid du Colombier 				tsleep(&ctlr.r, datadone, 0, 8000);
501*0cc6832dSDavid du Colombier 				WR(Irpten, 0);
502*0cc6832dSDavid du Colombier 				i = ctlr.datadone;
503*0cc6832dSDavid du Colombier 				ctlr.datadone = 0;
504*0cc6832dSDavid du Colombier 				if((i & (Writerdy|Err)) == 0){
505*0cc6832dSDavid du Colombier 					print("sdioio: (%d) no Writerdy intr %#ux stat %#ux\n",
506*0cc6832dSDavid du Colombier 						len, i, r[Hoststat]);
507*0cc6832dSDavid du Colombier 					error(Eio);
508*0cc6832dSDavid du Colombier 				}
509*0cc6832dSDavid du Colombier 			}
510*0cc6832dSDavid du Colombier 		}
511*0cc6832dSDavid du Colombier 		if(i & Writerdy){
512*0cc6832dSDavid du Colombier 			r[Data] = buf[0] | buf[1]<<8;
513*0cc6832dSDavid du Colombier 			r[Data] = buf[2] | buf[3]<<8;
514*0cc6832dSDavid du Colombier 			buf += 4;
515*0cc6832dSDavid du Colombier 			len -= 4;
516*0cc6832dSDavid du Colombier 		} else
517*0cc6832dSDavid du Colombier 			ckerr(r, i, len, "write");
518*0cc6832dSDavid du Colombier 	}
519*0cc6832dSDavid du Colombier 	*lenp = len;
520*0cc6832dSDavid du Colombier 	return i;
521*0cc6832dSDavid du Colombier }
522*0cc6832dSDavid du Colombier 
523*0cc6832dSDavid du Colombier static void
sdioio(int write,uchar * buf,int len)524*0cc6832dSDavid du Colombier sdioio(int write, uchar *buf, int len)
525*0cc6832dSDavid du Colombier {
526*0cc6832dSDavid du Colombier 	int i;
527*0cc6832dSDavid du Colombier 	u32int *r;
528*0cc6832dSDavid du Colombier 
529*0cc6832dSDavid du Colombier 	assert((len & 3) == 0);
530*0cc6832dSDavid du Colombier 	r = (u32int*)AddrSdio;
531*0cc6832dSDavid du Colombier 	if(write && PIOwrite)
532*0cc6832dSDavid du Colombier 		i = sdiowrite(buf, &len);
533*0cc6832dSDavid du Colombier 	else if(!write && PIOread)
534*0cc6832dSDavid du Colombier 		i = sdioread(buf, &len);
535*0cc6832dSDavid du Colombier 	else{
536*0cc6832dSDavid du Colombier 		WR(Irpten, Dmadone|Err);
537*0cc6832dSDavid du Colombier 		tsleep(&ctlr.r, datadone, 0, 3000);
538*0cc6832dSDavid du Colombier 		WR(Irpten, 0);
539*0cc6832dSDavid du Colombier 		i = ctlr.datadone;
540*0cc6832dSDavid du Colombier 		ctlr.datadone = 0;
541*0cc6832dSDavid du Colombier 		ckerr(r, i, len, "dma");
542*0cc6832dSDavid du Colombier 		ckdmadone(r, i, "no dma done");
543*0cc6832dSDavid du Colombier 		WR(Interrupt, Dmadone);
544*0cc6832dSDavid du Colombier 	}
545*0cc6832dSDavid du Colombier 
546*0cc6832dSDavid du Colombier 	if(Polldone)
547*0cc6832dSDavid du Colombier 		i = awaitdone(r, Datadone, 3*HZ);
548*0cc6832dSDavid du Colombier 	else if((i & Datadone) == 0){
549*0cc6832dSDavid du Colombier 		WR(Irpten, Datadone|Err);
550*0cc6832dSDavid du Colombier 		tsleep(&ctlr.r, datadone, 0, 3000);
551*0cc6832dSDavid du Colombier 		i = ctlr.datadone;
552*0cc6832dSDavid du Colombier 		ctlr.datadone = 0;
553*0cc6832dSDavid du Colombier 		WR(Irpten, 0);
554*0cc6832dSDavid du Colombier 	}
555*0cc6832dSDavid du Colombier 	ckerr(r, i, len, "IO");
556*0cc6832dSDavid du Colombier 	ckdmadone(r, i, "IO timeout");
557*0cc6832dSDavid du Colombier 	if(i)
558*0cc6832dSDavid du Colombier 		WR(Interrupt, i);
559*0cc6832dSDavid du Colombier }
560*0cc6832dSDavid du Colombier 
561*0cc6832dSDavid du Colombier static void
sdiointerrupt(Ureg *,void *)562*0cc6832dSDavid du Colombier sdiointerrupt(Ureg*, void*)
563*0cc6832dSDavid du Colombier {
564*0cc6832dSDavid du Colombier 	u32int *r;
565*0cc6832dSDavid du Colombier 
566*0cc6832dSDavid du Colombier 	r = (u32int*)AddrSdio;
567*0cc6832dSDavid du Colombier 	ctlr.datadone = r[Interrupt];
568*0cc6832dSDavid du Colombier 	WR(Irpten, 0);
569*0cc6832dSDavid du Colombier 	wakeup(&ctlr.r);
570*0cc6832dSDavid du Colombier }
571*0cc6832dSDavid du Colombier 
572*0cc6832dSDavid du Colombier SDio sdio = {
573*0cc6832dSDavid du Colombier 	"sdio",
574*0cc6832dSDavid du Colombier 	sdioinit,
575*0cc6832dSDavid du Colombier 	sdioenable,
576*0cc6832dSDavid du Colombier 	sdioinquiry,
577*0cc6832dSDavid du Colombier 	sdiocmd,
578*0cc6832dSDavid du Colombier 	sdioiosetup,
579*0cc6832dSDavid du Colombier 	sdioio,
580*0cc6832dSDavid du Colombier };
581