xref: /plan9-contrib/sys/src/9/bcm/sdhost.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1*5c47fe09SDavid du Colombier /*
2*5c47fe09SDavid du Colombier  * bcm2835 sdhost controller
3*5c47fe09SDavid du Colombier  *
4*5c47fe09SDavid du Colombier  * Copyright © 2016 Richard Miller <r.miller@acm.org>
5*5c47fe09SDavid du Colombier  */
6*5c47fe09SDavid du Colombier 
7*5c47fe09SDavid du Colombier #include "u.h"
8*5c47fe09SDavid du Colombier #include "../port/lib.h"
9*5c47fe09SDavid du Colombier #include "../port/error.h"
10*5c47fe09SDavid du Colombier #include "mem.h"
11*5c47fe09SDavid du Colombier #include "dat.h"
12*5c47fe09SDavid du Colombier #include "fns.h"
13*5c47fe09SDavid du Colombier #include "io.h"
14*5c47fe09SDavid du Colombier #include "../port/sd.h"
15*5c47fe09SDavid du Colombier 
16*5c47fe09SDavid du Colombier extern SDio *sdcardlink;
17*5c47fe09SDavid du Colombier 
18*5c47fe09SDavid du Colombier #define SDHOSTREGS	(VIRTIO+0x202000)
19*5c47fe09SDavid du Colombier 
20*5c47fe09SDavid du Colombier enum {
21*5c47fe09SDavid du Colombier 	Extfreq		= 250*Mhz,	/* guess external clock frequency if vcore doesn't say */
22*5c47fe09SDavid du Colombier 	Initfreq	= 400000,	/* initialisation frequency for MMC */
23*5c47fe09SDavid du Colombier 	SDfreq		= 25*Mhz,	/* standard SD frequency */
24*5c47fe09SDavid du Colombier 	SDfreqhs	= 50*Mhz,	/* SD high speed frequency */
25*5c47fe09SDavid du Colombier 	FifoDepth	= 4,		/* "Limit fifo usage due to silicon bug" (linux driver) */
26*5c47fe09SDavid du Colombier 
27*5c47fe09SDavid du Colombier 	GoIdle		= 0,		/* mmc/sdio go idle state */
28*5c47fe09SDavid du Colombier 	MMCSelect	= 7,		/* mmc/sd card select command */
29*5c47fe09SDavid du Colombier 	Setbuswidth	= 6,		/* mmc/sd set bus width command */
30*5c47fe09SDavid du Colombier 	Switchfunc	= 6,		/* mmc/sd switch function command */
31*5c47fe09SDavid du Colombier 	Stoptransmission = 12,	/* mmc/sd stop transmission command */
32*5c47fe09SDavid du Colombier 	Appcmd = 55,			/* mmc/sd application command prefix */
33*5c47fe09SDavid du Colombier };
34*5c47fe09SDavid du Colombier 
35*5c47fe09SDavid du Colombier enum {
36*5c47fe09SDavid du Colombier 	/* Controller registers */
37*5c47fe09SDavid du Colombier 	Cmd		= 0x00>>2,
38*5c47fe09SDavid du Colombier 	Arg		= 0x04>>2,
39*5c47fe09SDavid du Colombier 	Timeout		= 0x08>>2,
40*5c47fe09SDavid du Colombier 	Clkdiv		= 0x0c>>2,
41*5c47fe09SDavid du Colombier 
42*5c47fe09SDavid du Colombier 	Resp0		= 0x10>>2,
43*5c47fe09SDavid du Colombier 	Resp1		= 0x14>>2,
44*5c47fe09SDavid du Colombier 	Resp2		= 0x18>>2,
45*5c47fe09SDavid du Colombier 	Resp3		= 0x1c>>2,
46*5c47fe09SDavid du Colombier 
47*5c47fe09SDavid du Colombier 	Status		= 0x20>>2,
48*5c47fe09SDavid du Colombier 	Poweron		= 0x30>>2,
49*5c47fe09SDavid du Colombier 	Dbgmode		= 0x34>>2,
50*5c47fe09SDavid du Colombier 	Hconfig		= 0x38>>2,
51*5c47fe09SDavid du Colombier 	Blksize		= 0x3c>>2,
52*5c47fe09SDavid du Colombier 	Data		= 0x40>>2,
53*5c47fe09SDavid du Colombier 	Blkcount	= 0x50>>2,
54*5c47fe09SDavid du Colombier 
55*5c47fe09SDavid du Colombier 	/* Cmd */
56*5c47fe09SDavid du Colombier 	Start		= 1<<15,
57*5c47fe09SDavid du Colombier 	Failed		= 1<<14,
58*5c47fe09SDavid du Colombier 	Respmask	= 7<<9,
59*5c47fe09SDavid du Colombier 	Resp48busy	= 4<<9,
60*5c47fe09SDavid du Colombier 	Respnone	= 2<<9,
61*5c47fe09SDavid du Colombier 	Resp136		= 1<<9,
62*5c47fe09SDavid du Colombier 	Resp48		= 0<<9,
63*5c47fe09SDavid du Colombier 	Host2card	= 1<<7,
64*5c47fe09SDavid du Colombier 	Card2host	= 1<<6,
65*5c47fe09SDavid du Colombier 
66*5c47fe09SDavid du Colombier 	/* Status */
67*5c47fe09SDavid du Colombier 	Busyint		= 1<<10,
68*5c47fe09SDavid du Colombier 	Blkint		= 1<<9,
69*5c47fe09SDavid du Colombier 	Sdioint		= 1<<8,
70*5c47fe09SDavid du Colombier 	Rewtimeout	= 1<<7,
71*5c47fe09SDavid du Colombier 	Cmdtimeout	= 1<<6,
72*5c47fe09SDavid du Colombier 	Crcerror	= 3<<4,
73*5c47fe09SDavid du Colombier 	Fifoerror	= 1<<3,
74*5c47fe09SDavid du Colombier 	Dataflag	= 1<<0,
75*5c47fe09SDavid du Colombier 	Intstatus	= (Busyint|Blkint|Sdioint|Dataflag),
76*5c47fe09SDavid du Colombier 	Errstatus	= (Rewtimeout|Cmdtimeout|Crcerror|Fifoerror),
77*5c47fe09SDavid du Colombier 
78*5c47fe09SDavid du Colombier 	/* Hconfig */
79*5c47fe09SDavid du Colombier 	BusyintEn	= 1<<10,
80*5c47fe09SDavid du Colombier 	BlkintEn	= 1<<8,
81*5c47fe09SDavid du Colombier 	SdiointEn	= 1<<5,
82*5c47fe09SDavid du Colombier 	DataintEn	= 1<<4,
83*5c47fe09SDavid du Colombier 	Slowcard	= 1<<3,
84*5c47fe09SDavid du Colombier 	Extbus4		= 1<<2,
85*5c47fe09SDavid du Colombier 	Intbuswide	= 1<<1,
86*5c47fe09SDavid du Colombier 	Cmdrelease	= 1<<0,
87*5c47fe09SDavid du Colombier };
88*5c47fe09SDavid du Colombier 
89*5c47fe09SDavid du Colombier static int cmdinfo[64] = {
90*5c47fe09SDavid du Colombier [0]  Start | Respnone,
91*5c47fe09SDavid du Colombier [2]  Start | Resp136,
92*5c47fe09SDavid du Colombier [3]  Start | Resp48,
93*5c47fe09SDavid du Colombier [5]  Start | Resp48,
94*5c47fe09SDavid du Colombier [6]  Start | Resp48,
95*5c47fe09SDavid du Colombier [63]  Start | Resp48 | Card2host,
96*5c47fe09SDavid du Colombier [7]  Start | Resp48busy,
97*5c47fe09SDavid du Colombier [8]  Start | Resp48,
98*5c47fe09SDavid du Colombier [9]  Start | Resp136,
99*5c47fe09SDavid du Colombier [11] Start | Resp48,
100*5c47fe09SDavid du Colombier [12] Start | Resp48busy,
101*5c47fe09SDavid du Colombier [13] Start | Resp48,
102*5c47fe09SDavid du Colombier [16] Start | Resp48,
103*5c47fe09SDavid du Colombier [17] Start | Resp48 | Card2host,
104*5c47fe09SDavid du Colombier [18] Start | Resp48 | Card2host,
105*5c47fe09SDavid du Colombier [24] Start | Resp48 | Host2card,
106*5c47fe09SDavid du Colombier [25] Start | Resp48 | Host2card,
107*5c47fe09SDavid du Colombier [41] Start | Resp48,
108*5c47fe09SDavid du Colombier [52] Start | Resp48,
109*5c47fe09SDavid du Colombier [53] Start | Resp48,
110*5c47fe09SDavid du Colombier [55] Start | Resp48,
111*5c47fe09SDavid du Colombier };
112*5c47fe09SDavid du Colombier 
113*5c47fe09SDavid du Colombier typedef struct Ctlr Ctlr;
114*5c47fe09SDavid du Colombier 
115*5c47fe09SDavid du Colombier struct Ctlr {
116*5c47fe09SDavid du Colombier 	Rendez	r;
117*5c47fe09SDavid du Colombier 	int	bcount;
118*5c47fe09SDavid du Colombier 	int	done;
119*5c47fe09SDavid du Colombier 	ulong	extclk;
120*5c47fe09SDavid du Colombier 	int	appcmd;
121*5c47fe09SDavid du Colombier };
122*5c47fe09SDavid du Colombier 
123*5c47fe09SDavid du Colombier static Ctlr sdhost;
124*5c47fe09SDavid du Colombier 
125*5c47fe09SDavid du Colombier static void sdhostinterrupt(Ureg*, void*);
126*5c47fe09SDavid du Colombier 
127*5c47fe09SDavid du Colombier static void
WR(int reg,u32int val)128*5c47fe09SDavid du Colombier WR(int reg, u32int val)
129*5c47fe09SDavid du Colombier {
130*5c47fe09SDavid du Colombier 	u32int *r = (u32int*)SDHOSTREGS;
131*5c47fe09SDavid du Colombier 
132*5c47fe09SDavid du Colombier 	if(0)print("WR %2.2ux %ux\n", reg<<2, val);
133*5c47fe09SDavid du Colombier 	r[reg] = val;
134*5c47fe09SDavid du Colombier }
135*5c47fe09SDavid du Colombier 
136*5c47fe09SDavid du Colombier static int
datadone(void *)137*5c47fe09SDavid du Colombier datadone(void*)
138*5c47fe09SDavid du Colombier {
139*5c47fe09SDavid du Colombier 	return sdhost.done;
140*5c47fe09SDavid du Colombier }
141*5c47fe09SDavid du Colombier 
142*5c47fe09SDavid du Colombier static void
sdhostclock(uint freq)143*5c47fe09SDavid du Colombier sdhostclock(uint freq)
144*5c47fe09SDavid du Colombier {
145*5c47fe09SDavid du Colombier 	uint div;
146*5c47fe09SDavid du Colombier 
147*5c47fe09SDavid du Colombier 	div = sdhost.extclk / freq;
148*5c47fe09SDavid du Colombier 	if(sdhost.extclk / freq > freq)
149*5c47fe09SDavid du Colombier 		div++;
150*5c47fe09SDavid du Colombier 	if(div < 2)
151*5c47fe09SDavid du Colombier 		div = 2;
152*5c47fe09SDavid du Colombier 	WR(Clkdiv, div - 2);
153*5c47fe09SDavid du Colombier }
154*5c47fe09SDavid du Colombier 
155*5c47fe09SDavid du Colombier static int
sdhostinit(void)156*5c47fe09SDavid du Colombier sdhostinit(void)
157*5c47fe09SDavid du Colombier {
158*5c47fe09SDavid du Colombier 	u32int *r;
159*5c47fe09SDavid du Colombier 	ulong clk;
160*5c47fe09SDavid du Colombier 	int i;
161*5c47fe09SDavid du Colombier 
162*5c47fe09SDavid du Colombier 	/* disconnect emmc and connect sdhost to SD card gpio pins */
163*5c47fe09SDavid du Colombier 	for(i = 48; i <= 53; i++)
164*5c47fe09SDavid du Colombier 		gpiosel(i, Alt0);
165*5c47fe09SDavid du Colombier 	clk = getclkrate(ClkCore);
166*5c47fe09SDavid du Colombier 	if(clk == 0){
167*5c47fe09SDavid du Colombier 		clk = Extfreq;
168*5c47fe09SDavid du Colombier 		print("sdhost: assuming external clock %lud Mhz\n", clk/1000000);
169*5c47fe09SDavid du Colombier 	}
170*5c47fe09SDavid du Colombier 	sdhost.extclk = clk;
171*5c47fe09SDavid du Colombier 	sdhostclock(Initfreq);
172*5c47fe09SDavid du Colombier 	r = (u32int*)SDHOSTREGS;
173*5c47fe09SDavid du Colombier 	WR(Poweron, 0);
174*5c47fe09SDavid du Colombier 	WR(Timeout, 0xF00000);
175*5c47fe09SDavid du Colombier 	WR(Dbgmode, FINS(r[Dbgmode], 9, 10, (FifoDepth | FifoDepth<<5)));
176*5c47fe09SDavid du Colombier 	return 0;
177*5c47fe09SDavid du Colombier }
178*5c47fe09SDavid du Colombier 
179*5c47fe09SDavid du Colombier static int
sdhostinquiry(char * inquiry,int inqlen)180*5c47fe09SDavid du Colombier sdhostinquiry(char *inquiry, int inqlen)
181*5c47fe09SDavid du Colombier {
182*5c47fe09SDavid du Colombier 	return snprint(inquiry, inqlen, "BCM SDHost Controller");
183*5c47fe09SDavid du Colombier }
184*5c47fe09SDavid du Colombier 
185*5c47fe09SDavid du Colombier static void
sdhostenable(void)186*5c47fe09SDavid du Colombier sdhostenable(void)
187*5c47fe09SDavid du Colombier {
188*5c47fe09SDavid du Colombier 	u32int *r;
189*5c47fe09SDavid du Colombier 
190*5c47fe09SDavid du Colombier 	r = (u32int*)SDHOSTREGS;
191*5c47fe09SDavid du Colombier 	USED(r);
192*5c47fe09SDavid du Colombier 	WR(Poweron, 1);
193*5c47fe09SDavid du Colombier 	delay(10);
194*5c47fe09SDavid du Colombier 	WR(Hconfig, Intbuswide | Slowcard | BusyintEn);
195*5c47fe09SDavid du Colombier 	WR(Clkdiv, 0x7FF);
196*5c47fe09SDavid du Colombier 	intrenable(IRQsdhost, sdhostinterrupt, nil, 0, "sdhost");
197*5c47fe09SDavid du Colombier }
198*5c47fe09SDavid du Colombier 
199*5c47fe09SDavid du Colombier static int
sdhostcmd(u32int cmd,u32int arg,u32int * resp)200*5c47fe09SDavid du Colombier sdhostcmd(u32int cmd, u32int arg, u32int *resp)
201*5c47fe09SDavid du Colombier {
202*5c47fe09SDavid du Colombier 	u32int *r;
203*5c47fe09SDavid du Colombier 	u32int c;
204*5c47fe09SDavid du Colombier 	int i;
205*5c47fe09SDavid du Colombier 	ulong now;
206*5c47fe09SDavid du Colombier 
207*5c47fe09SDavid du Colombier 	r = (u32int*)SDHOSTREGS;
208*5c47fe09SDavid du Colombier 	assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0);
209*5c47fe09SDavid du Colombier 	c = cmd | cmdinfo[cmd];
210*5c47fe09SDavid du Colombier 	/*
211*5c47fe09SDavid du Colombier 	 * CMD6 may be Setbuswidth or Switchfunc depending on Appcmd prefix
212*5c47fe09SDavid du Colombier 	 */
213*5c47fe09SDavid du Colombier 	if(cmd == Switchfunc && !sdhost.appcmd)
214*5c47fe09SDavid du Colombier 		c |= Card2host;
215*5c47fe09SDavid du Colombier 	if(cmd != Stoptransmission && (i = (r[Dbgmode] & 0xF)) > 2){
216*5c47fe09SDavid du Colombier 		print("sdhost: previous command stuck: Dbg=%d Cmd=%ux\n", i, r[Cmd]);
217*5c47fe09SDavid du Colombier 		error(Eio);
218*5c47fe09SDavid du Colombier 	}
219*5c47fe09SDavid du Colombier 	/*
220*5c47fe09SDavid du Colombier 	 * GoIdle indicates new card insertion: reset bus width & speed
221*5c47fe09SDavid du Colombier 	 */
222*5c47fe09SDavid du Colombier 	if(cmd == GoIdle){
223*5c47fe09SDavid du Colombier 		WR(Hconfig, r[Hconfig] & ~Extbus4);
224*5c47fe09SDavid du Colombier 		sdhostclock(Initfreq);
225*5c47fe09SDavid du Colombier 	}
226*5c47fe09SDavid du Colombier 
227*5c47fe09SDavid du Colombier 	if(r[Status] & (Errstatus|Dataflag))
228*5c47fe09SDavid du Colombier 		WR(Status, Errstatus|Dataflag);
229*5c47fe09SDavid du Colombier 	sdhost.done = 0;
230*5c47fe09SDavid du Colombier 	WR(Arg, arg);
231*5c47fe09SDavid du Colombier 	WR(Cmd, c);
232*5c47fe09SDavid du Colombier 	now = m->ticks;
233*5c47fe09SDavid du Colombier 	while(((i=r[Cmd])&(Start|Failed)) == Start)
234*5c47fe09SDavid du Colombier 		if(m->ticks-now > HZ)
235*5c47fe09SDavid du Colombier 			break;
236*5c47fe09SDavid du Colombier 	if((i&(Start|Failed)) != 0){
237*5c47fe09SDavid du Colombier 		if(r[Status] != Cmdtimeout)
238*5c47fe09SDavid du Colombier 			print("sdhost: cmd %ux arg %ux error stat %ux\n", i, arg, r[Status]);
239*5c47fe09SDavid du Colombier 		i = r[Status];
240*5c47fe09SDavid du Colombier 		WR(Status, i);
241*5c47fe09SDavid du Colombier 		error(Eio);
242*5c47fe09SDavid du Colombier 	}
243*5c47fe09SDavid du Colombier 	switch(c & Respmask){
244*5c47fe09SDavid du Colombier 	case Resp136:
245*5c47fe09SDavid du Colombier 		resp[0] = r[Resp0];
246*5c47fe09SDavid du Colombier 		resp[1] = r[Resp1];
247*5c47fe09SDavid du Colombier 		resp[2] = r[Resp2];
248*5c47fe09SDavid du Colombier 		resp[3] = r[Resp3];
249*5c47fe09SDavid du Colombier 		break;
250*5c47fe09SDavid du Colombier 	case Resp48:
251*5c47fe09SDavid du Colombier 	case Resp48busy:
252*5c47fe09SDavid du Colombier 		resp[0] = r[Resp0];
253*5c47fe09SDavid du Colombier 		break;
254*5c47fe09SDavid du Colombier 	case Respnone:
255*5c47fe09SDavid du Colombier 		resp[0] = 0;
256*5c47fe09SDavid du Colombier 		break;
257*5c47fe09SDavid du Colombier 	}
258*5c47fe09SDavid du Colombier 	if((c & Respmask) == Resp48busy){
259*5c47fe09SDavid du Colombier 		tsleep(&sdhost.r, datadone, 0, 3000);
260*5c47fe09SDavid du Colombier 	}
261*5c47fe09SDavid du Colombier 	switch(cmd) {
262*5c47fe09SDavid du Colombier 	case MMCSelect:
263*5c47fe09SDavid du Colombier 		/*
264*5c47fe09SDavid du Colombier 		 * Once card is selected, use faster clock
265*5c47fe09SDavid du Colombier 		 */
266*5c47fe09SDavid du Colombier 		delay(1);
267*5c47fe09SDavid du Colombier 		sdhostclock(SDfreq);
268*5c47fe09SDavid du Colombier 		delay(1);
269*5c47fe09SDavid du Colombier 		break;
270*5c47fe09SDavid du Colombier 	case Setbuswidth:
271*5c47fe09SDavid du Colombier 		if(sdhost.appcmd){
272*5c47fe09SDavid du Colombier 			/*
273*5c47fe09SDavid du Colombier 			 * If card bus width changes, change host bus width
274*5c47fe09SDavid du Colombier 			 */
275*5c47fe09SDavid du Colombier 			switch(arg){
276*5c47fe09SDavid du Colombier 			case 0:
277*5c47fe09SDavid du Colombier 				WR(Hconfig, r[Hconfig] & ~Extbus4);
278*5c47fe09SDavid du Colombier 				break;
279*5c47fe09SDavid du Colombier 			case 2:
280*5c47fe09SDavid du Colombier 				WR(Hconfig, r[Hconfig] | Extbus4);
281*5c47fe09SDavid du Colombier 				break;
282*5c47fe09SDavid du Colombier 			}
283*5c47fe09SDavid du Colombier 		}else{
284*5c47fe09SDavid du Colombier 			/*
285*5c47fe09SDavid du Colombier 			 * If card switched into high speed mode, increase clock speed
286*5c47fe09SDavid du Colombier 			 */
287*5c47fe09SDavid du Colombier 			if((arg&0x8000000F) == 0x80000001){
288*5c47fe09SDavid du Colombier 				delay(1);
289*5c47fe09SDavid du Colombier 				sdhostclock(SDfreqhs);
290*5c47fe09SDavid du Colombier 				delay(1);
291*5c47fe09SDavid du Colombier 			}
292*5c47fe09SDavid du Colombier 		}
293*5c47fe09SDavid du Colombier 		break;
294*5c47fe09SDavid du Colombier 	}
295*5c47fe09SDavid du Colombier 	sdhost.appcmd = (cmd == Appcmd);
296*5c47fe09SDavid du Colombier 	return 0;
297*5c47fe09SDavid du Colombier }
298*5c47fe09SDavid du Colombier 
299*5c47fe09SDavid du Colombier void
sdhostiosetup(int write,void * buf,int bsize,int bcount)300*5c47fe09SDavid du Colombier sdhostiosetup(int write, void *buf, int bsize, int bcount)
301*5c47fe09SDavid du Colombier {
302*5c47fe09SDavid du Colombier 	USED(write);
303*5c47fe09SDavid du Colombier 	USED(buf);
304*5c47fe09SDavid du Colombier 
305*5c47fe09SDavid du Colombier 	sdhost.bcount = bcount;
306*5c47fe09SDavid du Colombier 	WR(Blksize, bsize);
307*5c47fe09SDavid du Colombier 	WR(Blkcount, bcount);
308*5c47fe09SDavid du Colombier }
309*5c47fe09SDavid du Colombier 
310*5c47fe09SDavid du Colombier static void
sdhostio(int write,uchar * buf,int len)311*5c47fe09SDavid du Colombier sdhostio(int write, uchar *buf, int len)
312*5c47fe09SDavid du Colombier {
313*5c47fe09SDavid du Colombier 	u32int *r;
314*5c47fe09SDavid du Colombier 	int piolen;
315*5c47fe09SDavid du Colombier 	u32int w;
316*5c47fe09SDavid du Colombier 
317*5c47fe09SDavid du Colombier 	r = (u32int*)SDHOSTREGS;
318*5c47fe09SDavid du Colombier 	assert((len&3) == 0);
319*5c47fe09SDavid du Colombier 	assert((PTR2UINT(buf)&3) == 0);
320*5c47fe09SDavid du Colombier 	okay(1);
321*5c47fe09SDavid du Colombier 	if(waserror()){
322*5c47fe09SDavid du Colombier 		okay(0);
323*5c47fe09SDavid du Colombier 		nexterror();
324*5c47fe09SDavid du Colombier 	}
325*5c47fe09SDavid du Colombier 	/*
326*5c47fe09SDavid du Colombier 	 * According to comments in the linux driver, the hardware "doesn't
327*5c47fe09SDavid du Colombier 	 * manage the FIFO DREQs properly for multi-block transfers" on input,
328*5c47fe09SDavid du Colombier 	 * so the dma must be stopped early and the last 3 words fetched with pio
329*5c47fe09SDavid du Colombier 	 */
330*5c47fe09SDavid du Colombier 	piolen = 0;
331*5c47fe09SDavid du Colombier 	if(!write && sdhost.bcount > 1){
332*5c47fe09SDavid du Colombier 		piolen = (FifoDepth-1) * sizeof(u32int);
333*5c47fe09SDavid du Colombier 		len -= piolen;
334*5c47fe09SDavid du Colombier 	}
335*5c47fe09SDavid du Colombier 	if(write)
336*5c47fe09SDavid du Colombier 		dmastart(DmaChanSdhost, DmaDevSdhost, DmaM2D,
337*5c47fe09SDavid du Colombier 			buf, &r[Data], len);
338*5c47fe09SDavid du Colombier 	else
339*5c47fe09SDavid du Colombier 		dmastart(DmaChanSdhost, DmaDevSdhost, DmaD2M,
340*5c47fe09SDavid du Colombier 			&r[Data], buf, len);
341*5c47fe09SDavid du Colombier 	if(dmawait(DmaChanSdhost) < 0)
342*5c47fe09SDavid du Colombier 		error(Eio);
343*5c47fe09SDavid du Colombier 	if(!write){
344*5c47fe09SDavid du Colombier 		cachedinvse(buf, len);
345*5c47fe09SDavid du Colombier 		buf += len;
346*5c47fe09SDavid du Colombier 		for(; piolen > 0; piolen -= sizeof(u32int)){
347*5c47fe09SDavid du Colombier 			if((r[Dbgmode] & 0x1F00) == 0){
348*5c47fe09SDavid du Colombier 				print("sdhost: FIFO empty after short dma read\n");
349*5c47fe09SDavid du Colombier 				error(Eio);
350*5c47fe09SDavid du Colombier 			}
351*5c47fe09SDavid du Colombier 			w = r[Data];
352*5c47fe09SDavid du Colombier 			*((u32int*)buf) = w;
353*5c47fe09SDavid du Colombier 			buf += sizeof(u32int);
354*5c47fe09SDavid du Colombier 		}
355*5c47fe09SDavid du Colombier 	}
356*5c47fe09SDavid du Colombier 	poperror();
357*5c47fe09SDavid du Colombier 	okay(0);
358*5c47fe09SDavid du Colombier }
359*5c47fe09SDavid du Colombier 
360*5c47fe09SDavid du Colombier static void
sdhostinterrupt(Ureg *,void *)361*5c47fe09SDavid du Colombier sdhostinterrupt(Ureg*, void*)
362*5c47fe09SDavid du Colombier {
363*5c47fe09SDavid du Colombier 	u32int *r;
364*5c47fe09SDavid du Colombier 	int i;
365*5c47fe09SDavid du Colombier 
366*5c47fe09SDavid du Colombier 	r = (u32int*)SDHOSTREGS;
367*5c47fe09SDavid du Colombier 	i = r[Status];
368*5c47fe09SDavid du Colombier 	WR(Status, i);
369*5c47fe09SDavid du Colombier 	if(i & Busyint){
370*5c47fe09SDavid du Colombier 		sdhost.done = 1;
371*5c47fe09SDavid du Colombier 		wakeup(&sdhost.r);
372*5c47fe09SDavid du Colombier 	}
373*5c47fe09SDavid du Colombier }
374*5c47fe09SDavid du Colombier 
375*5c47fe09SDavid du Colombier SDio sdiohost = {
376*5c47fe09SDavid du Colombier 	"sdhost",
377*5c47fe09SDavid du Colombier 	sdhostinit,
378*5c47fe09SDavid du Colombier 	sdhostenable,
379*5c47fe09SDavid du Colombier 	sdhostinquiry,
380*5c47fe09SDavid du Colombier 	sdhostcmd,
381*5c47fe09SDavid du Colombier 	sdhostiosetup,
382*5c47fe09SDavid du Colombier 	sdhostio,
383*5c47fe09SDavid du Colombier };
384*5c47fe09SDavid du Colombier 
385*5c47fe09SDavid du Colombier void
sdhostlink(void)386*5c47fe09SDavid du Colombier sdhostlink(void)
387*5c47fe09SDavid du Colombier {
388*5c47fe09SDavid du Colombier 	sdcardlink = &sdiohost;
389*5c47fe09SDavid du Colombier }
390