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