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