xref: /plan9-contrib/sys/src/9/bcm/emmc.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier  * bcm2835 external mass media controller (mmc / sd host interface)
35d9de2d3SDavid du Colombier  *
45d9de2d3SDavid du Colombier  * Copyright © 2012 Richard Miller <r.miller@acm.org>
55d9de2d3SDavid du Colombier  */
65d9de2d3SDavid du Colombier 
7*5c47fe09SDavid du Colombier /*
8*5c47fe09SDavid du Colombier 	Not officially documented: emmc can be connected to different gpio pins
9*5c47fe09SDavid du Colombier 		48-53 (SD card)
10*5c47fe09SDavid du Colombier 		22-27 (P1 header)
11*5c47fe09SDavid du Colombier 		34-39 (wifi - pi3 only)
12*5c47fe09SDavid du Colombier 	using ALT3 function to activate the required routing
13*5c47fe09SDavid du Colombier  */
14*5c47fe09SDavid du Colombier 
155d9de2d3SDavid du Colombier #include "u.h"
165d9de2d3SDavid du Colombier #include "../port/lib.h"
175d9de2d3SDavid du Colombier #include "../port/error.h"
185d9de2d3SDavid du Colombier #include "mem.h"
195d9de2d3SDavid du Colombier #include "dat.h"
205d9de2d3SDavid du Colombier #include "fns.h"
215d9de2d3SDavid du Colombier #include "io.h"
225d9de2d3SDavid du Colombier #include "../port/sd.h"
235d9de2d3SDavid du Colombier 
245d9de2d3SDavid du Colombier #define EMMCREGS	(VIRTIO+0x300000)
255d9de2d3SDavid du Colombier 
265d9de2d3SDavid du Colombier enum {
275d9de2d3SDavid du Colombier 	Extfreq		= 100*Mhz,	/* guess external clock frequency if */
285d9de2d3SDavid du Colombier 					/* not available from vcore */
295d9de2d3SDavid du Colombier 	Initfreq	= 400000,	/* initialisation frequency for MMC */
305d9de2d3SDavid du Colombier 	SDfreq		= 25*Mhz,	/* standard SD frequency */
31*5c47fe09SDavid du Colombier 	SDfreqhs	= 50*Mhz,	/* high speed frequency */
325d9de2d3SDavid du Colombier 	DTO		= 14,		/* data timeout exponent (guesswork) */
335d9de2d3SDavid du Colombier 
34*5c47fe09SDavid du Colombier 	GoIdle		= 0,		/* mmc/sdio go idle state */
355d9de2d3SDavid du Colombier 	MMCSelect	= 7,		/* mmc/sd card select command */
365d9de2d3SDavid du Colombier 	Setbuswidth	= 6,		/* mmc/sd set bus width command */
37*5c47fe09SDavid du Colombier 	Switchfunc	= 6,		/* mmc/sd switch function command */
38*5c47fe09SDavid du Colombier 	Voltageswitch = 11,		/* md/sdio switch to 1.8V */
39*5c47fe09SDavid du Colombier 	IORWdirect = 52,		/* sdio read/write direct command */
40*5c47fe09SDavid du Colombier 	IORWextended = 53,		/* sdio read/write extended command */
41*5c47fe09SDavid du Colombier 	Appcmd = 55,			/* mmc/sd application command prefix */
425d9de2d3SDavid du Colombier };
435d9de2d3SDavid du Colombier 
445d9de2d3SDavid du Colombier enum {
455d9de2d3SDavid du Colombier 	/* Controller registers */
465d9de2d3SDavid du Colombier 	Arg2			= 0x00>>2,
475d9de2d3SDavid du Colombier 	Blksizecnt		= 0x04>>2,
485d9de2d3SDavid du Colombier 	Arg1			= 0x08>>2,
495d9de2d3SDavid du Colombier 	Cmdtm			= 0x0c>>2,
505d9de2d3SDavid du Colombier 	Resp0			= 0x10>>2,
515d9de2d3SDavid du Colombier 	Resp1			= 0x14>>2,
525d9de2d3SDavid du Colombier 	Resp2			= 0x18>>2,
535d9de2d3SDavid du Colombier 	Resp3			= 0x1c>>2,
545d9de2d3SDavid du Colombier 	Data			= 0x20>>2,
555d9de2d3SDavid du Colombier 	Status			= 0x24>>2,
565d9de2d3SDavid du Colombier 	Control0		= 0x28>>2,
575d9de2d3SDavid du Colombier 	Control1		= 0x2c>>2,
585d9de2d3SDavid du Colombier 	Interrupt		= 0x30>>2,
595d9de2d3SDavid du Colombier 	Irptmask		= 0x34>>2,
605d9de2d3SDavid du Colombier 	Irpten			= 0x38>>2,
615d9de2d3SDavid du Colombier 	Control2		= 0x3c>>2,
625d9de2d3SDavid du Colombier 	Forceirpt		= 0x50>>2,
635d9de2d3SDavid du Colombier 	Boottimeout		= 0x70>>2,
645d9de2d3SDavid du Colombier 	Dbgsel			= 0x74>>2,
655d9de2d3SDavid du Colombier 	Exrdfifocfg		= 0x80>>2,
665d9de2d3SDavid du Colombier 	Exrdfifoen		= 0x84>>2,
675d9de2d3SDavid du Colombier 	Tunestep		= 0x88>>2,
685d9de2d3SDavid du Colombier 	Tunestepsstd		= 0x8c>>2,
695d9de2d3SDavid du Colombier 	Tunestepsddr		= 0x90>>2,
705d9de2d3SDavid du Colombier 	Spiintspt		= 0xf0>>2,
715d9de2d3SDavid du Colombier 	Slotisrver		= 0xfc>>2,
725d9de2d3SDavid du Colombier 
735d9de2d3SDavid du Colombier 	/* Control0 */
74*5c47fe09SDavid du Colombier 	Hispeed			= 1<<2,
755d9de2d3SDavid du Colombier 	Dwidth4			= 1<<1,
765d9de2d3SDavid du Colombier 	Dwidth1			= 0<<1,
775d9de2d3SDavid du Colombier 
785d9de2d3SDavid du Colombier 	/* Control1 */
795d9de2d3SDavid du Colombier 	Srstdata		= 1<<26,	/* reset data circuit */
805d9de2d3SDavid du Colombier 	Srstcmd			= 1<<25,	/* reset command circuit */
815d9de2d3SDavid du Colombier 	Srsthc			= 1<<24,	/* reset complete host controller */
825d9de2d3SDavid du Colombier 	Datatoshift		= 16,		/* data timeout unit exponent */
835d9de2d3SDavid du Colombier 	Datatomask		= 0xF0000,
845d9de2d3SDavid du Colombier 	Clkfreq8shift		= 8,		/* SD clock base divider LSBs */
855d9de2d3SDavid du Colombier 	Clkfreq8mask		= 0xFF00,
865d9de2d3SDavid du Colombier 	Clkfreqms2shift		= 6,		/* SD clock base divider MSBs */
875d9de2d3SDavid du Colombier 	Clkfreqms2mask		= 0xC0,
885d9de2d3SDavid du Colombier 	Clkgendiv		= 0<<5,		/* SD clock divided */
895d9de2d3SDavid du Colombier 	Clkgenprog		= 1<<5,		/* SD clock programmable */
905d9de2d3SDavid du Colombier 	Clken			= 1<<2,		/* SD clock enable */
915d9de2d3SDavid du Colombier 	Clkstable		= 1<<1,
925d9de2d3SDavid du Colombier 	Clkintlen		= 1<<0,		/* enable internal EMMC clocks */
935d9de2d3SDavid du Colombier 
945d9de2d3SDavid du Colombier 	/* Cmdtm */
955d9de2d3SDavid du Colombier 	Indexshift		= 24,
965d9de2d3SDavid du Colombier 	Suspend			= 1<<22,
975d9de2d3SDavid du Colombier 	Resume			= 2<<22,
985d9de2d3SDavid du Colombier 	Abort			= 3<<22,
995d9de2d3SDavid du Colombier 	Isdata			= 1<<21,
1005d9de2d3SDavid du Colombier 	Ixchken			= 1<<20,
1015d9de2d3SDavid du Colombier 	Crcchken		= 1<<19,
1025d9de2d3SDavid du Colombier 	Respmask		= 3<<16,
1035d9de2d3SDavid du Colombier 	Respnone		= 0<<16,
1045d9de2d3SDavid du Colombier 	Resp136			= 1<<16,
1055d9de2d3SDavid du Colombier 	Resp48			= 2<<16,
1065d9de2d3SDavid du Colombier 	Resp48busy		= 3<<16,
1075d9de2d3SDavid du Colombier 	Multiblock		= 1<<5,
1085d9de2d3SDavid du Colombier 	Host2card		= 0<<4,
1095d9de2d3SDavid du Colombier 	Card2host		= 1<<4,
1105d9de2d3SDavid du Colombier 	Autocmd12		= 1<<2,
1115d9de2d3SDavid du Colombier 	Autocmd23		= 2<<2,
1125d9de2d3SDavid du Colombier 	Blkcnten		= 1<<1,
1135d9de2d3SDavid du Colombier 
1145d9de2d3SDavid du Colombier 	/* Interrupt */
1155d9de2d3SDavid du Colombier 	Acmderr		= 1<<24,
1165d9de2d3SDavid du Colombier 	Denderr		= 1<<22,
1175d9de2d3SDavid du Colombier 	Dcrcerr		= 1<<21,
1185d9de2d3SDavid du Colombier 	Dtoerr		= 1<<20,
1195d9de2d3SDavid du Colombier 	Cbaderr		= 1<<19,
1205d9de2d3SDavid du Colombier 	Cenderr		= 1<<18,
1215d9de2d3SDavid du Colombier 	Ccrcerr		= 1<<17,
1225d9de2d3SDavid du Colombier 	Ctoerr		= 1<<16,
1235d9de2d3SDavid du Colombier 	Err		= 1<<15,
124*5c47fe09SDavid du Colombier 	Cardintr	= 1<<8,
1255d9de2d3SDavid du Colombier 	Cardinsert	= 1<<6,		/* not in Broadcom datasheet */
1265d9de2d3SDavid du Colombier 	Readrdy		= 1<<5,
1275d9de2d3SDavid du Colombier 	Writerdy	= 1<<4,
1285d9de2d3SDavid du Colombier 	Datadone	= 1<<1,
1295d9de2d3SDavid du Colombier 	Cmddone		= 1<<0,
1305d9de2d3SDavid du Colombier 
1315d9de2d3SDavid du Colombier 	/* Status */
1325d9de2d3SDavid du Colombier 	Bufread		= 1<<11,	/* not in Broadcom datasheet */
1335d9de2d3SDavid du Colombier 	Bufwrite	= 1<<10,	/* not in Broadcom datasheet */
1345d9de2d3SDavid du Colombier 	Readtrans	= 1<<9,
1355d9de2d3SDavid du Colombier 	Writetrans	= 1<<8,
1365d9de2d3SDavid du Colombier 	Datactive	= 1<<2,
1375d9de2d3SDavid du Colombier 	Datinhibit	= 1<<1,
1385d9de2d3SDavid du Colombier 	Cmdinhibit	= 1<<0,
1395d9de2d3SDavid du Colombier };
1405d9de2d3SDavid du Colombier 
141*5c47fe09SDavid du Colombier static int cmdinfo[64] = {
1425d9de2d3SDavid du Colombier [0]  Ixchken,
1435d9de2d3SDavid du Colombier [2]  Resp136,
1445d9de2d3SDavid du Colombier [3]  Resp48 | Ixchken | Crcchken,
145*5c47fe09SDavid du Colombier [5]  Resp48,
1465d9de2d3SDavid du Colombier [6]  Resp48 | Ixchken | Crcchken,
1475d9de2d3SDavid du Colombier [7]  Resp48busy | Ixchken | Crcchken,
1485d9de2d3SDavid du Colombier [8]  Resp48 | Ixchken | Crcchken,
1495d9de2d3SDavid du Colombier [9]  Resp136,
150*5c47fe09SDavid du Colombier [11] Resp48 | Ixchken | Crcchken,
1515d9de2d3SDavid du Colombier [12] Resp48busy | Ixchken | Crcchken,
1525d9de2d3SDavid du Colombier [13] Resp48 | Ixchken | Crcchken,
1535d9de2d3SDavid du Colombier [16] Resp48,
1545d9de2d3SDavid du Colombier [17] Resp48 | Isdata | Card2host | Ixchken | Crcchken,
1555d9de2d3SDavid du Colombier [18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken,
1565d9de2d3SDavid du Colombier [24] Resp48 | Isdata | Host2card | Ixchken | Crcchken,
1575d9de2d3SDavid du Colombier [25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken,
1585d9de2d3SDavid du Colombier [41] Resp48,
159*5c47fe09SDavid du Colombier [52] Resp48 | Ixchken | Crcchken,
160*5c47fe09SDavid du Colombier [53] Resp48	| Ixchken | Crcchken | Isdata,
1615d9de2d3SDavid du Colombier [55] Resp48 | Ixchken | Crcchken,
1625d9de2d3SDavid du Colombier };
1635d9de2d3SDavid du Colombier 
1645d9de2d3SDavid du Colombier typedef struct Ctlr Ctlr;
1655d9de2d3SDavid du Colombier 
1665d9de2d3SDavid du Colombier struct Ctlr {
1675d9de2d3SDavid du Colombier 	Rendez	r;
168*5c47fe09SDavid du Colombier 	Rendez	cardr;
1695d9de2d3SDavid du Colombier 	int	fastclock;
1705d9de2d3SDavid du Colombier 	ulong	extclk;
171*5c47fe09SDavid du Colombier 	int	appcmd;
1725d9de2d3SDavid du Colombier };
1735d9de2d3SDavid du Colombier 
1745d9de2d3SDavid du Colombier static Ctlr emmc;
1755d9de2d3SDavid du Colombier 
1765d9de2d3SDavid du Colombier static void mmcinterrupt(Ureg*, void*);
1775d9de2d3SDavid du Colombier 
1785d9de2d3SDavid du Colombier static void
WR(int reg,u32int val)1795d9de2d3SDavid du Colombier WR(int reg, u32int val)
1805d9de2d3SDavid du Colombier {
1815d9de2d3SDavid du Colombier 	u32int *r = (u32int*)EMMCREGS;
1825d9de2d3SDavid du Colombier 
1835d9de2d3SDavid du Colombier 	if(0)print("WR %2.2ux %ux\n", reg<<2, val);
1845d9de2d3SDavid du Colombier 	microdelay(emmc.fastclock? 2 : 20);
185*5c47fe09SDavid du Colombier 	coherence();
1865d9de2d3SDavid du Colombier 	r[reg] = val;
1875d9de2d3SDavid du Colombier }
1885d9de2d3SDavid du Colombier 
1895d9de2d3SDavid du Colombier static uint
clkdiv(uint d)1905d9de2d3SDavid du Colombier clkdiv(uint d)
1915d9de2d3SDavid du Colombier {
1925d9de2d3SDavid du Colombier 	uint v;
1935d9de2d3SDavid du Colombier 
1945d9de2d3SDavid du Colombier 	assert(d < 1<<10);
1955d9de2d3SDavid du Colombier 	v = (d << Clkfreq8shift) & Clkfreq8mask;
1965d9de2d3SDavid du Colombier 	v |= ((d >> 8) << Clkfreqms2shift) & Clkfreqms2mask;
1975d9de2d3SDavid du Colombier 	return v;
1985d9de2d3SDavid du Colombier }
1995d9de2d3SDavid du Colombier 
200*5c47fe09SDavid du Colombier static void
emmcclk(uint freq)201*5c47fe09SDavid du Colombier emmcclk(uint freq)
202*5c47fe09SDavid du Colombier {
203*5c47fe09SDavid du Colombier 	u32int *r;
204*5c47fe09SDavid du Colombier 	uint div;
205*5c47fe09SDavid du Colombier 	int i;
206*5c47fe09SDavid du Colombier 
207*5c47fe09SDavid du Colombier 	r = (u32int*)EMMCREGS;
208*5c47fe09SDavid du Colombier 	div = emmc.extclk / (freq<<1);
209*5c47fe09SDavid du Colombier 	if(emmc.extclk / (div<<1) > freq)
210*5c47fe09SDavid du Colombier 		div++;
211*5c47fe09SDavid du Colombier 	WR(Control1, clkdiv(div) |
212*5c47fe09SDavid du Colombier 		DTO<<Datatoshift | Clkgendiv | Clken | Clkintlen);
213*5c47fe09SDavid du Colombier 	for(i = 0; i < 1000; i++){
214*5c47fe09SDavid du Colombier 		delay(1);
215*5c47fe09SDavid du Colombier 		if(r[Control1] & Clkstable)
216*5c47fe09SDavid du Colombier 			break;
217*5c47fe09SDavid du Colombier 	}
218*5c47fe09SDavid du Colombier 	if(i == 1000)
219*5c47fe09SDavid du Colombier 		print("emmc: can't set clock to %ud\n", freq);
220*5c47fe09SDavid du Colombier }
221*5c47fe09SDavid du Colombier 
2225d9de2d3SDavid du Colombier static int
datadone(void *)2235d9de2d3SDavid du Colombier datadone(void*)
2245d9de2d3SDavid du Colombier {
225*5c47fe09SDavid du Colombier 	int i;
226*5c47fe09SDavid du Colombier 
227*5c47fe09SDavid du Colombier 	u32int *r = (u32int*)EMMCREGS;
228*5c47fe09SDavid du Colombier 	i = r[Interrupt];
229*5c47fe09SDavid du Colombier 	return i & (Datadone|Err);
230*5c47fe09SDavid du Colombier }
231*5c47fe09SDavid du Colombier 
232*5c47fe09SDavid du Colombier static int
cardintready(void *)233*5c47fe09SDavid du Colombier cardintready(void*)
234*5c47fe09SDavid du Colombier {
235*5c47fe09SDavid du Colombier 	int i;
236*5c47fe09SDavid du Colombier 
237*5c47fe09SDavid du Colombier 	u32int *r = (u32int*)EMMCREGS;
238*5c47fe09SDavid du Colombier 	i = r[Interrupt];
239*5c47fe09SDavid du Colombier 	return i & Cardintr;
2405d9de2d3SDavid du Colombier }
2415d9de2d3SDavid du Colombier 
2425d9de2d3SDavid du Colombier static int
emmcinit(void)2435d9de2d3SDavid du Colombier emmcinit(void)
2445d9de2d3SDavid du Colombier {
2455d9de2d3SDavid du Colombier 	u32int *r;
2465d9de2d3SDavid du Colombier 	ulong clk;
2475d9de2d3SDavid du Colombier 
2485d9de2d3SDavid du Colombier 	clk = getclkrate(ClkEmmc);
2495d9de2d3SDavid du Colombier 	if(clk == 0){
2505d9de2d3SDavid du Colombier 		clk = Extfreq;
251*5c47fe09SDavid du Colombier 		print("emmc: assuming external clock %lud Mhz\n", clk/1000000);
2525d9de2d3SDavid du Colombier 	}
2535d9de2d3SDavid du Colombier 	emmc.extclk = clk;
2545d9de2d3SDavid du Colombier 	r = (u32int*)EMMCREGS;
2555d9de2d3SDavid du Colombier 	if(0)print("emmc control %8.8ux %8.8ux %8.8ux\n",
2565d9de2d3SDavid du Colombier 		r[Control0], r[Control1], r[Control2]);
2575d9de2d3SDavid du Colombier 	WR(Control1, Srsthc);
2585d9de2d3SDavid du Colombier 	delay(10);
2595d9de2d3SDavid du Colombier 	while(r[Control1] & Srsthc)
2605d9de2d3SDavid du Colombier 		;
261*5c47fe09SDavid du Colombier 	WR(Control1, Srstdata);
262*5c47fe09SDavid du Colombier 	delay(10);
263*5c47fe09SDavid du Colombier 	WR(Control1, 0);
2645d9de2d3SDavid du Colombier 	return 0;
2655d9de2d3SDavid du Colombier }
2665d9de2d3SDavid du Colombier 
2675d9de2d3SDavid du Colombier static int
emmcinquiry(char * inquiry,int inqlen)2685d9de2d3SDavid du Colombier emmcinquiry(char *inquiry, int inqlen)
2695d9de2d3SDavid du Colombier {
2705d9de2d3SDavid du Colombier 	u32int *r;
2715d9de2d3SDavid du Colombier 	uint ver;
2725d9de2d3SDavid du Colombier 
2735d9de2d3SDavid du Colombier 	r = (u32int*)EMMCREGS;
2745d9de2d3SDavid du Colombier 	ver = r[Slotisrver] >> 16;
2755d9de2d3SDavid du Colombier 	return snprint(inquiry, inqlen,
2765d9de2d3SDavid du Colombier 		"Arasan eMMC SD Host Controller %2.2x Version %2.2x",
2775d9de2d3SDavid du Colombier 		ver&0xFF, ver>>8);
2785d9de2d3SDavid du Colombier }
2795d9de2d3SDavid du Colombier 
2805d9de2d3SDavid du Colombier static void
emmcenable(void)2815d9de2d3SDavid du Colombier emmcenable(void)
2825d9de2d3SDavid du Colombier {
283*5c47fe09SDavid du Colombier 	emmcclk(Initfreq);
284*5c47fe09SDavid du Colombier 	WR(Irpten, 0);
285*5c47fe09SDavid du Colombier 	WR(Irptmask, ~0);
286*5c47fe09SDavid du Colombier 	WR(Interrupt, ~0);
287*5c47fe09SDavid du Colombier 	intrenable(IRQmmc, mmcinterrupt, nil, 0, "mmc");
288*5c47fe09SDavid du Colombier }
289*5c47fe09SDavid du Colombier 
290*5c47fe09SDavid du Colombier int
sdiocardintr(int wait)291*5c47fe09SDavid du Colombier sdiocardintr(int wait)
292*5c47fe09SDavid du Colombier {
2935d9de2d3SDavid du Colombier 	u32int *r;
2945d9de2d3SDavid du Colombier 	int i;
2955d9de2d3SDavid du Colombier 
2965d9de2d3SDavid du Colombier 	r = (u32int*)EMMCREGS;
297*5c47fe09SDavid du Colombier 	WR(Interrupt, Cardintr);
298*5c47fe09SDavid du Colombier 	while(((i = r[Interrupt]) & Cardintr) == 0){
299*5c47fe09SDavid du Colombier 		if(!wait)
300*5c47fe09SDavid du Colombier 			return 0;
301*5c47fe09SDavid du Colombier 		WR(Irpten, r[Irpten] | Cardintr);
302*5c47fe09SDavid du Colombier 		sleep(&emmc.cardr, cardintready, 0);
3035d9de2d3SDavid du Colombier 	}
304*5c47fe09SDavid du Colombier 	WR(Interrupt, Cardintr);
305*5c47fe09SDavid du Colombier 	return i;
3065d9de2d3SDavid du Colombier }
3075d9de2d3SDavid du Colombier 
3085d9de2d3SDavid du Colombier static int
emmccmd(u32int cmd,u32int arg,u32int * resp)3095d9de2d3SDavid du Colombier emmccmd(u32int cmd, u32int arg, u32int *resp)
3105d9de2d3SDavid du Colombier {
3115d9de2d3SDavid du Colombier 	u32int *r;
3125d9de2d3SDavid du Colombier 	u32int c;
3135d9de2d3SDavid du Colombier 	int i;
3145d9de2d3SDavid du Colombier 	ulong now;
3155d9de2d3SDavid du Colombier 
3165d9de2d3SDavid du Colombier 	r = (u32int*)EMMCREGS;
3175d9de2d3SDavid du Colombier 	assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0);
3185d9de2d3SDavid du Colombier 	c = (cmd << Indexshift) | cmdinfo[cmd];
319*5c47fe09SDavid du Colombier 	/*
320*5c47fe09SDavid du Colombier 	 * CMD6 may be Setbuswidth or Switchfunc depending on Appcmd prefix
321*5c47fe09SDavid du Colombier 	 */
322*5c47fe09SDavid du Colombier 	if(cmd == Switchfunc && !emmc.appcmd)
323*5c47fe09SDavid du Colombier 		c |= Isdata|Card2host;
324*5c47fe09SDavid du Colombier 	if(cmd == IORWextended){
325*5c47fe09SDavid du Colombier 		if(arg & (1<<31))
326*5c47fe09SDavid du Colombier 			c |= Host2card;
327*5c47fe09SDavid du Colombier 		else
328*5c47fe09SDavid du Colombier 			c |= Card2host;
329*5c47fe09SDavid du Colombier 		if((r[Blksizecnt]&0xFFFF0000) != 0x10000)
330*5c47fe09SDavid du Colombier 			c |= Multiblock | Blkcnten;
331*5c47fe09SDavid du Colombier 	}
332*5c47fe09SDavid du Colombier 	/*
333*5c47fe09SDavid du Colombier 	 * GoIdle indicates new card insertion: reset bus width & speed
334*5c47fe09SDavid du Colombier 	 */
335*5c47fe09SDavid du Colombier 	if(cmd == GoIdle){
336*5c47fe09SDavid du Colombier 		WR(Control0, r[Control0] & ~(Dwidth4|Hispeed));
337*5c47fe09SDavid du Colombier 		emmcclk(Initfreq);
338*5c47fe09SDavid du Colombier 	}
3395d9de2d3SDavid du Colombier 	if(r[Status] & Cmdinhibit){
3405d9de2d3SDavid du Colombier 		print("emmccmd: need to reset Cmdinhibit intr %ux stat %ux\n",
3415d9de2d3SDavid du Colombier 			r[Interrupt], r[Status]);
3425d9de2d3SDavid du Colombier 		WR(Control1, r[Control1] | Srstcmd);
3435d9de2d3SDavid du Colombier 		while(r[Control1] & Srstcmd)
3445d9de2d3SDavid du Colombier 			;
3455d9de2d3SDavid du Colombier 		while(r[Status] & Cmdinhibit)
3465d9de2d3SDavid du Colombier 			;
3475d9de2d3SDavid du Colombier 	}
348b4d1cf41SDavid du Colombier 	if((r[Status] & Datinhibit) &&
349b4d1cf41SDavid du Colombier 	   ((c & Isdata) || (c & Respmask) == Resp48busy)){
3505d9de2d3SDavid du Colombier 		print("emmccmd: need to reset Datinhibit intr %ux stat %ux\n",
3515d9de2d3SDavid du Colombier 			r[Interrupt], r[Status]);
3525d9de2d3SDavid du Colombier 		WR(Control1, r[Control1] | Srstdata);
3535d9de2d3SDavid du Colombier 		while(r[Control1] & Srstdata)
3545d9de2d3SDavid du Colombier 			;
3555d9de2d3SDavid du Colombier 		while(r[Status] & Datinhibit)
3565d9de2d3SDavid du Colombier 			;
3575d9de2d3SDavid du Colombier 	}
3585d9de2d3SDavid du Colombier 	WR(Arg1, arg);
359*5c47fe09SDavid du Colombier 	if((i = (r[Interrupt] & ~Cardintr)) != 0){
3605d9de2d3SDavid du Colombier 		if(i != Cardinsert)
3615d9de2d3SDavid du Colombier 			print("emmc: before command, intr was %ux\n", i);
3625d9de2d3SDavid du Colombier 		WR(Interrupt, i);
3635d9de2d3SDavid du Colombier 	}
3645d9de2d3SDavid du Colombier 	WR(Cmdtm, c);
3655d9de2d3SDavid du Colombier 	now = m->ticks;
3665d9de2d3SDavid du Colombier 	while(((i=r[Interrupt])&(Cmddone|Err)) == 0)
3675d9de2d3SDavid du Colombier 		if(m->ticks-now > HZ)
3685d9de2d3SDavid du Colombier 			break;
3695d9de2d3SDavid du Colombier 	if((i&(Cmddone|Err)) != Cmddone){
370*5c47fe09SDavid du Colombier 		if((i&~(Err|Cardintr)) != Ctoerr)
371*5c47fe09SDavid du Colombier 			print("emmc: cmd %ux arg %ux error intr %ux stat %ux\n", c, arg, i, r[Status]);
3725d9de2d3SDavid du Colombier 		WR(Interrupt, i);
3735d9de2d3SDavid du Colombier 		if(r[Status]&Cmdinhibit){
3745d9de2d3SDavid du Colombier 			WR(Control1, r[Control1]|Srstcmd);
3755d9de2d3SDavid du Colombier 			while(r[Control1]&Srstcmd)
3765d9de2d3SDavid du Colombier 				;
3775d9de2d3SDavid du Colombier 		}
3785d9de2d3SDavid du Colombier 		error(Eio);
3795d9de2d3SDavid du Colombier 	}
3805d9de2d3SDavid du Colombier 	WR(Interrupt, i & ~(Datadone|Readrdy|Writerdy));
3815d9de2d3SDavid du Colombier 	switch(c & Respmask){
3825d9de2d3SDavid du Colombier 	case Resp136:
3835d9de2d3SDavid du Colombier 		resp[0] = r[Resp0]<<8;
3845d9de2d3SDavid du Colombier 		resp[1] = r[Resp0]>>24 | r[Resp1]<<8;
3855d9de2d3SDavid du Colombier 		resp[2] = r[Resp1]>>24 | r[Resp2]<<8;
3865d9de2d3SDavid du Colombier 		resp[3] = r[Resp2]>>24 | r[Resp3]<<8;
3875d9de2d3SDavid du Colombier 		break;
3885d9de2d3SDavid du Colombier 	case Resp48:
3895d9de2d3SDavid du Colombier 	case Resp48busy:
3905d9de2d3SDavid du Colombier 		resp[0] = r[Resp0];
3915d9de2d3SDavid du Colombier 		break;
3925d9de2d3SDavid du Colombier 	case Respnone:
3935d9de2d3SDavid du Colombier 		resp[0] = 0;
3945d9de2d3SDavid du Colombier 		break;
3955d9de2d3SDavid du Colombier 	}
3965d9de2d3SDavid du Colombier 	if((c & Respmask) == Resp48busy){
397*5c47fe09SDavid du Colombier 		WR(Irpten, r[Irpten]|Datadone|Err);
3985d9de2d3SDavid du Colombier 		tsleep(&emmc.r, datadone, 0, 3000);
399*5c47fe09SDavid du Colombier 		i = r[Interrupt];
4005d9de2d3SDavid du Colombier 		if((i & Datadone) == 0)
4015d9de2d3SDavid du Colombier 			print("emmcio: no Datadone after CMD%d\n", cmd);
4025d9de2d3SDavid du Colombier 		if(i & Err)
4035d9de2d3SDavid du Colombier 			print("emmcio: CMD%d error interrupt %ux\n",
4045d9de2d3SDavid du Colombier 				cmd, r[Interrupt]);
4055d9de2d3SDavid du Colombier 		WR(Interrupt, i);
4065d9de2d3SDavid du Colombier 	}
4075d9de2d3SDavid du Colombier 	/*
4085d9de2d3SDavid du Colombier 	 * Once card is selected, use faster clock
4095d9de2d3SDavid du Colombier 	 */
4105d9de2d3SDavid du Colombier 	if(cmd == MMCSelect){
4115d9de2d3SDavid du Colombier 		delay(1);
412*5c47fe09SDavid du Colombier 		emmcclk(SDfreq);
413*5c47fe09SDavid du Colombier 		delay(1);
4145d9de2d3SDavid du Colombier 		emmc.fastclock = 1;
4155d9de2d3SDavid du Colombier 	}
416*5c47fe09SDavid du Colombier 	if(cmd == Setbuswidth){
417*5c47fe09SDavid du Colombier 		if(emmc.appcmd){
4185d9de2d3SDavid du Colombier 			/*
4195d9de2d3SDavid du Colombier 			 * If card bus width changes, change host bus width
4205d9de2d3SDavid du Colombier 			 */
4215d9de2d3SDavid du Colombier 			switch(arg){
4225d9de2d3SDavid du Colombier 			case 0:
4235d9de2d3SDavid du Colombier 				WR(Control0, r[Control0] & ~Dwidth4);
4245d9de2d3SDavid du Colombier 				break;
4255d9de2d3SDavid du Colombier 			case 2:
4265d9de2d3SDavid du Colombier 				WR(Control0, r[Control0] | Dwidth4);
4275d9de2d3SDavid du Colombier 				break;
4285d9de2d3SDavid du Colombier 			}
429*5c47fe09SDavid du Colombier 		}else{
430*5c47fe09SDavid du Colombier 			/*
431*5c47fe09SDavid du Colombier 			 * If card switched into high speed mode, increase clock speed
432*5c47fe09SDavid du Colombier 			 */
433*5c47fe09SDavid du Colombier 			if((arg&0x8000000F) == 0x80000001){
434*5c47fe09SDavid du Colombier 				delay(1);
435*5c47fe09SDavid du Colombier 				emmcclk(SDfreqhs);
436*5c47fe09SDavid du Colombier 				delay(1);
437b4d1cf41SDavid du Colombier 			}
438*5c47fe09SDavid du Colombier 		}
439*5c47fe09SDavid du Colombier 	}else if(cmd == IORWdirect && (arg & ~0xFF) == (1<<31|0<<28|7<<9)){
440*5c47fe09SDavid du Colombier 		switch(arg & 0x3){
441*5c47fe09SDavid du Colombier 		case 0:
442*5c47fe09SDavid du Colombier 			WR(Control0, r[Control0] & ~Dwidth4);
443*5c47fe09SDavid du Colombier 			break;
444*5c47fe09SDavid du Colombier 		case 2:
445*5c47fe09SDavid du Colombier 			WR(Control0, r[Control0] | Dwidth4);
446*5c47fe09SDavid du Colombier 			//WR(Control0, r[Control0] | Hispeed);
447*5c47fe09SDavid du Colombier 			break;
448*5c47fe09SDavid du Colombier 		}
449*5c47fe09SDavid du Colombier 	}
450*5c47fe09SDavid du Colombier 	emmc.appcmd = (cmd == Appcmd);
4515d9de2d3SDavid du Colombier 	return 0;
4525d9de2d3SDavid du Colombier }
4535d9de2d3SDavid du Colombier 
4545d9de2d3SDavid du Colombier void
emmciosetup(int write,void * buf,int bsize,int bcount)4555d9de2d3SDavid du Colombier emmciosetup(int write, void *buf, int bsize, int bcount)
4565d9de2d3SDavid du Colombier {
4575d9de2d3SDavid du Colombier 	USED(write);
4585d9de2d3SDavid du Colombier 	USED(buf);
4595d9de2d3SDavid du Colombier 	WR(Blksizecnt, bcount<<16 | bsize);
4605d9de2d3SDavid du Colombier }
4615d9de2d3SDavid du Colombier 
4625d9de2d3SDavid du Colombier static void
emmcio(int write,uchar * buf,int len)4635d9de2d3SDavid du Colombier emmcio(int write, uchar *buf, int len)
4645d9de2d3SDavid du Colombier {
4655d9de2d3SDavid du Colombier 	u32int *r;
4665d9de2d3SDavid du Colombier 	int i;
4675d9de2d3SDavid du Colombier 
4685d9de2d3SDavid du Colombier 	r = (u32int*)EMMCREGS;
4695d9de2d3SDavid du Colombier 	assert((len&3) == 0);
4705d9de2d3SDavid du Colombier 	okay(1);
4715d9de2d3SDavid du Colombier 	if(waserror()){
4725d9de2d3SDavid du Colombier 		okay(0);
4735d9de2d3SDavid du Colombier 		nexterror();
4745d9de2d3SDavid du Colombier 	}
4755d9de2d3SDavid du Colombier 	if(write)
4765d9de2d3SDavid du Colombier 		dmastart(DmaChanEmmc, DmaDevEmmc, DmaM2D,
4775d9de2d3SDavid du Colombier 			buf, &r[Data], len);
4785d9de2d3SDavid du Colombier 	else
4795d9de2d3SDavid du Colombier 		dmastart(DmaChanEmmc, DmaDevEmmc, DmaD2M,
4805d9de2d3SDavid du Colombier 			&r[Data], buf, len);
4815d9de2d3SDavid du Colombier 	if(dmawait(DmaChanEmmc) < 0)
4825d9de2d3SDavid du Colombier 		error(Eio);
483*5c47fe09SDavid du Colombier 	if(!write)
484*5c47fe09SDavid du Colombier 		cachedinvse(buf, len);
485*5c47fe09SDavid du Colombier 	WR(Irpten, r[Irpten]|Datadone|Err);
4865d9de2d3SDavid du Colombier 	tsleep(&emmc.r, datadone, 0, 3000);
487*5c47fe09SDavid du Colombier 	i = r[Interrupt]&~Cardintr;
4885d9de2d3SDavid du Colombier 	if((i & Datadone) == 0){
4895d9de2d3SDavid du Colombier 		print("emmcio: %d timeout intr %ux stat %ux\n",
4905d9de2d3SDavid du Colombier 			write, i, r[Status]);
4915d9de2d3SDavid du Colombier 		WR(Interrupt, i);
4925d9de2d3SDavid du Colombier 		error(Eio);
4935d9de2d3SDavid du Colombier 	}
4945d9de2d3SDavid du Colombier 	if(i & Err){
4955d9de2d3SDavid du Colombier 		print("emmcio: %d error intr %ux stat %ux\n",
4965d9de2d3SDavid du Colombier 			write, r[Interrupt], r[Status]);
4975d9de2d3SDavid du Colombier 		WR(Interrupt, i);
4985d9de2d3SDavid du Colombier 		error(Eio);
4995d9de2d3SDavid du Colombier 	}
5005d9de2d3SDavid du Colombier 	if(i)
5015d9de2d3SDavid du Colombier 		WR(Interrupt, i);
5025d9de2d3SDavid du Colombier 	poperror();
5035d9de2d3SDavid du Colombier 	okay(0);
5045d9de2d3SDavid du Colombier }
5055d9de2d3SDavid du Colombier 
5065d9de2d3SDavid du Colombier static void
mmcinterrupt(Ureg *,void *)5075d9de2d3SDavid du Colombier mmcinterrupt(Ureg*, void*)
5085d9de2d3SDavid du Colombier {
5095d9de2d3SDavid du Colombier 	u32int *r;
5105d9de2d3SDavid du Colombier 	int i;
5115d9de2d3SDavid du Colombier 
5125d9de2d3SDavid du Colombier 	r = (u32int*)EMMCREGS;
5135d9de2d3SDavid du Colombier 	i = r[Interrupt];
514*5c47fe09SDavid du Colombier 	if(i&(Datadone|Err))
5155d9de2d3SDavid du Colombier 		wakeup(&emmc.r);
516*5c47fe09SDavid du Colombier 	if(i&Cardintr)
517*5c47fe09SDavid du Colombier 		wakeup(&emmc.cardr);
518*5c47fe09SDavid du Colombier 	WR(Irpten, r[Irpten] & ~i);
5195d9de2d3SDavid du Colombier }
5205d9de2d3SDavid du Colombier 
5215d9de2d3SDavid du Colombier SDio sdio = {
5225d9de2d3SDavid du Colombier 	"emmc",
5235d9de2d3SDavid du Colombier 	emmcinit,
5245d9de2d3SDavid du Colombier 	emmcenable,
5255d9de2d3SDavid du Colombier 	emmcinquiry,
5265d9de2d3SDavid du Colombier 	emmccmd,
5275d9de2d3SDavid du Colombier 	emmciosetup,
5285d9de2d3SDavid du Colombier 	emmcio,
5295d9de2d3SDavid du Colombier };
530