xref: /plan9/sys/src/9/port/sdmmc.c (revision b2b7afb3c4fa78bd9fd7789ae95bbeebaf9be085)
1*0cc6832dSDavid du Colombier /*
2*0cc6832dSDavid du Colombier  * mmc / sd memory card
3*0cc6832dSDavid du Colombier  *
4*0cc6832dSDavid du Colombier  * Copyright © 2012 Richard Miller <r.miller@acm.org>
5*0cc6832dSDavid du Colombier  *
6*0cc6832dSDavid du Colombier  * Assumes only one card on the bus
7*0cc6832dSDavid du Colombier  */
8*0cc6832dSDavid du Colombier 
9*0cc6832dSDavid du Colombier #include "u.h"
10*0cc6832dSDavid du Colombier #include "../port/lib.h"
11*0cc6832dSDavid du Colombier #include "../port/error.h"
12*0cc6832dSDavid du Colombier #include "mem.h"
13*0cc6832dSDavid du Colombier #include "dat.h"
14*0cc6832dSDavid du Colombier #include "fns.h"
15*0cc6832dSDavid du Colombier #include "io.h"
16*0cc6832dSDavid du Colombier 
17*0cc6832dSDavid du Colombier #include "../port/sd.h"
18*0cc6832dSDavid du Colombier 
19*0cc6832dSDavid du Colombier #define CSD(end, start)	rbits(csd, start, (end)-(start)+1)
20*0cc6832dSDavid du Colombier 
21*0cc6832dSDavid du Colombier typedef struct Ctlr Ctlr;
22*0cc6832dSDavid du Colombier 
23*0cc6832dSDavid du Colombier enum {
24*0cc6832dSDavid du Colombier 	Inittimeout	= 15,
25*0cc6832dSDavid du Colombier 	Multiblock	= 1,
26*0cc6832dSDavid du Colombier 
27*0cc6832dSDavid du Colombier 	/* Commands */
28*0cc6832dSDavid du Colombier 	GO_IDLE_STATE	= 0,
29*0cc6832dSDavid du Colombier 	ALL_SEND_CID	= 2,
30*0cc6832dSDavid du Colombier 	SEND_RELATIVE_ADDR= 3,
31*0cc6832dSDavid du Colombier 	SELECT_CARD	= 7,
32*0cc6832dSDavid du Colombier 	SD_SEND_IF_COND	= 8,
33*0cc6832dSDavid du Colombier 	SEND_CSD	= 9,
34*0cc6832dSDavid du Colombier 	STOP_TRANSMISSION= 12,
35*0cc6832dSDavid du Colombier 	SEND_STATUS	= 13,
36*0cc6832dSDavid du Colombier 	SET_BLOCKLEN	= 16,
37*0cc6832dSDavid du Colombier 	READ_SINGLE_BLOCK= 17,
38*0cc6832dSDavid du Colombier 	READ_MULTIPLE_BLOCK= 18,
39*0cc6832dSDavid du Colombier 	WRITE_BLOCK	= 24,
40*0cc6832dSDavid du Colombier 	WRITE_MULTIPLE_BLOCK= 25,
41*0cc6832dSDavid du Colombier 	APP_CMD		= 55,	/* prefix for following app-specific commands */
42*0cc6832dSDavid du Colombier 	SET_BUS_WIDTH	= 6,
43*0cc6832dSDavid du Colombier 	SD_SEND_OP_COND	= 41,
44*0cc6832dSDavid du Colombier 
45*0cc6832dSDavid du Colombier 	/* Command arguments */
46*0cc6832dSDavid du Colombier 	/* SD_SEND_IF_COND */
47*0cc6832dSDavid du Colombier 	Voltage		= 1<<8,
48*0cc6832dSDavid du Colombier 	Checkpattern	= 0x42,
49*0cc6832dSDavid du Colombier 
50*0cc6832dSDavid du Colombier 	/* SELECT_CARD */
51*0cc6832dSDavid du Colombier 	Rcashift	= 16,
52*0cc6832dSDavid du Colombier 
53*0cc6832dSDavid du Colombier 	/* SD_SEND_OP_COND */
54*0cc6832dSDavid du Colombier 	Hcs	= 1<<30,	/* host supports SDHC & SDXC */
55*0cc6832dSDavid du Colombier 	Ccs	= 1<<30,	/* card is SDHC or SDXC */
56*0cc6832dSDavid du Colombier 	V3_3	= 3<<20,	/* 3.2-3.4 volts */
57*0cc6832dSDavid du Colombier 
58*0cc6832dSDavid du Colombier 	/* SET_BUS_WIDTH */
59*0cc6832dSDavid du Colombier 	Width1	= 0<<0,
60*0cc6832dSDavid du Colombier 	Width4	= 2<<0,
61*0cc6832dSDavid du Colombier 
62*0cc6832dSDavid du Colombier 	/* OCR (operating conditions register) */
63*0cc6832dSDavid du Colombier 	Powerup	= 1<<31,
64*0cc6832dSDavid du Colombier };
65*0cc6832dSDavid du Colombier 
66*0cc6832dSDavid du Colombier struct Ctlr {
67*0cc6832dSDavid du Colombier 	SDev	*dev;
68*0cc6832dSDavid du Colombier 	SDio	*io;
69*0cc6832dSDavid du Colombier 	/* SD card registers */
70*0cc6832dSDavid du Colombier 	u16int	rca;
71*0cc6832dSDavid du Colombier 	u32int	ocr;
72*0cc6832dSDavid du Colombier 	u32int	cid[4];
73*0cc6832dSDavid du Colombier 	u32int	csd[4];
74*0cc6832dSDavid du Colombier };
75*0cc6832dSDavid du Colombier 
76*0cc6832dSDavid du Colombier extern SDifc sdmmcifc;
77*0cc6832dSDavid du Colombier extern SDio sdio;
78*0cc6832dSDavid du Colombier 
79*0cc6832dSDavid du Colombier static uint
rbits(u32int * p,uint start,uint len)80*0cc6832dSDavid du Colombier rbits(u32int *p, uint start, uint len)
81*0cc6832dSDavid du Colombier {
82*0cc6832dSDavid du Colombier 	uint w, off, v;
83*0cc6832dSDavid du Colombier 
84*0cc6832dSDavid du Colombier 	w   = start / 32;
85*0cc6832dSDavid du Colombier 	off = start % 32;
86*0cc6832dSDavid du Colombier 	if(off == 0)
87*0cc6832dSDavid du Colombier 		v = p[w];
88*0cc6832dSDavid du Colombier 	else
89*0cc6832dSDavid du Colombier 		v = p[w] >> off | p[w+1] << (32-off);
90*0cc6832dSDavid du Colombier 	if(len < 32)
91*0cc6832dSDavid du Colombier 		return v & ((1<<len) - 1);
92*0cc6832dSDavid du Colombier 	else
93*0cc6832dSDavid du Colombier 		return v;
94*0cc6832dSDavid du Colombier }
95*0cc6832dSDavid du Colombier 
96*0cc6832dSDavid du Colombier static void
identify(SDunit * unit,u32int * csd)97*0cc6832dSDavid du Colombier identify(SDunit *unit, u32int *csd)
98*0cc6832dSDavid du Colombier {
99*0cc6832dSDavid du Colombier 	uint csize, mult;
100*0cc6832dSDavid du Colombier 
101*0cc6832dSDavid du Colombier 	unit->secsize = 1 << CSD(83, 80);
102*0cc6832dSDavid du Colombier 	switch(CSD(127, 126)){
103*0cc6832dSDavid du Colombier 	case 0:				/* CSD version 1 */
104*0cc6832dSDavid du Colombier 		csize = CSD(73, 62);
105*0cc6832dSDavid du Colombier 		mult = CSD(49, 47);
106*0cc6832dSDavid du Colombier 		unit->sectors = (csize+1) * (1<<(mult+2));
107*0cc6832dSDavid du Colombier 		break;
108*0cc6832dSDavid du Colombier 	case 1:				/* CSD version 2 */
109*0cc6832dSDavid du Colombier 		csize = CSD(69, 48);
110*0cc6832dSDavid du Colombier 		unit->sectors = (csize+1) * 512LL*KiB / unit->secsize;
111*0cc6832dSDavid du Colombier 		break;
112*0cc6832dSDavid du Colombier 	}
113*0cc6832dSDavid du Colombier 	if(unit->secsize == 1024){
114*0cc6832dSDavid du Colombier 		unit->sectors <<= 1;
115*0cc6832dSDavid du Colombier 		unit->secsize = 512;
116*0cc6832dSDavid du Colombier 	}
117*0cc6832dSDavid du Colombier }
118*0cc6832dSDavid du Colombier 
119*0cc6832dSDavid du Colombier static SDev*
mmcpnp(void)120*0cc6832dSDavid du Colombier mmcpnp(void)
121*0cc6832dSDavid du Colombier {
122*0cc6832dSDavid du Colombier 	SDev *sdev;
123*0cc6832dSDavid du Colombier 	Ctlr *ctl;
124*0cc6832dSDavid du Colombier 
125*0cc6832dSDavid du Colombier 	if(sdio.init() < 0)
126*0cc6832dSDavid du Colombier 		return nil;
127*0cc6832dSDavid du Colombier 	sdev = malloc(sizeof(SDev));
128*0cc6832dSDavid du Colombier 	if(sdev == nil)
129*0cc6832dSDavid du Colombier 		return nil;
130*0cc6832dSDavid du Colombier 	ctl = malloc(sizeof(Ctlr));
131*0cc6832dSDavid du Colombier 	if(ctl == nil){
132*0cc6832dSDavid du Colombier 		free(sdev);
133*0cc6832dSDavid du Colombier 		return nil;
134*0cc6832dSDavid du Colombier 	}
135*0cc6832dSDavid du Colombier 	sdev->idno = 'M';
136*0cc6832dSDavid du Colombier 	sdev->ifc = &sdmmcifc;
137*0cc6832dSDavid du Colombier 	sdev->nunit = 1;
138*0cc6832dSDavid du Colombier 	sdev->ctlr = ctl;
139*0cc6832dSDavid du Colombier 	ctl->dev = sdev;
140*0cc6832dSDavid du Colombier 	ctl->io = &sdio;
141*0cc6832dSDavid du Colombier 	return sdev;
142*0cc6832dSDavid du Colombier }
143*0cc6832dSDavid du Colombier 
144*0cc6832dSDavid du Colombier static int
mmcverify(SDunit * unit)145*0cc6832dSDavid du Colombier mmcverify(SDunit *unit)
146*0cc6832dSDavid du Colombier {
147*0cc6832dSDavid du Colombier 	int n;
148*0cc6832dSDavid du Colombier 	Ctlr *ctl;
149*0cc6832dSDavid du Colombier 
150*0cc6832dSDavid du Colombier 	ctl = unit->dev->ctlr;
151*0cc6832dSDavid du Colombier 	n = ctl->io->inquiry((char*)&unit->inquiry[8], sizeof(unit->inquiry)-8);
152*0cc6832dSDavid du Colombier 	if(n < 0)
153*0cc6832dSDavid du Colombier 		return 0;
154*0cc6832dSDavid du Colombier 	unit->inquiry[0] = SDperdisk;
155*0cc6832dSDavid du Colombier 	unit->inquiry[1] = SDinq1removable;
156*0cc6832dSDavid du Colombier 	unit->inquiry[4] = sizeof(unit->inquiry)-4;
157*0cc6832dSDavid du Colombier 	return 1;
158*0cc6832dSDavid du Colombier }
159*0cc6832dSDavid du Colombier 
160*0cc6832dSDavid du Colombier static int
mmcenable(SDev * dev)161*0cc6832dSDavid du Colombier mmcenable(SDev* dev)
162*0cc6832dSDavid du Colombier {
163*0cc6832dSDavid du Colombier 	Ctlr *ctl;
164*0cc6832dSDavid du Colombier 
165*0cc6832dSDavid du Colombier 	ctl = dev->ctlr;
166*0cc6832dSDavid du Colombier 	ctl->io->enable();
167*0cc6832dSDavid du Colombier 	return 1;
168*0cc6832dSDavid du Colombier }
169*0cc6832dSDavid du Colombier 
170*0cc6832dSDavid du Colombier static int
mmconline(SDunit * unit)171*0cc6832dSDavid du Colombier mmconline(SDunit *unit)
172*0cc6832dSDavid du Colombier {
173*0cc6832dSDavid du Colombier 	int hcs, i;
174*0cc6832dSDavid du Colombier 	u32int r[4];
175*0cc6832dSDavid du Colombier 	Ctlr *ctl;
176*0cc6832dSDavid du Colombier 	SDio *io;
177*0cc6832dSDavid du Colombier 
178*0cc6832dSDavid du Colombier 	ctl = unit->dev->ctlr;
179*0cc6832dSDavid du Colombier 	io = ctl->io;
180*0cc6832dSDavid du Colombier 	assert(unit->subno == 0);
181*0cc6832dSDavid du Colombier 
182*0cc6832dSDavid du Colombier 	if(waserror()){
183*0cc6832dSDavid du Colombier 		unit->sectors = 0;
184*0cc6832dSDavid du Colombier 		return 0;
185*0cc6832dSDavid du Colombier 	}
186*0cc6832dSDavid du Colombier 	if(unit->sectors != 0){
187*0cc6832dSDavid du Colombier 		io->cmd(SEND_STATUS, ctl->rca<<Rcashift, r);
188*0cc6832dSDavid du Colombier 		poperror();
189*0cc6832dSDavid du Colombier 		return 1;
190*0cc6832dSDavid du Colombier 	}
191*0cc6832dSDavid du Colombier 	io->cmd(GO_IDLE_STATE, 0, r);
192*0cc6832dSDavid du Colombier 	hcs = 0;
193*0cc6832dSDavid du Colombier 	if(!waserror()){
194*0cc6832dSDavid du Colombier 		io->cmd(SD_SEND_IF_COND, Voltage|Checkpattern, r);
195*0cc6832dSDavid du Colombier 		if(r[0] == (Voltage|Checkpattern))	/* SD 2.0 or above */
196*0cc6832dSDavid du Colombier 			hcs = Hcs;
197*0cc6832dSDavid du Colombier 		poperror();
198*0cc6832dSDavid du Colombier 	}
199*0cc6832dSDavid du Colombier 	for(i = 0; i < Inittimeout; i++){
200*0cc6832dSDavid du Colombier 		delay(100);
201*0cc6832dSDavid du Colombier 		io->cmd(APP_CMD, 0, r);
202*0cc6832dSDavid du Colombier 		io->cmd(SD_SEND_OP_COND, hcs|V3_3, r);
203*0cc6832dSDavid du Colombier 		if(r[0] & Powerup)
204*0cc6832dSDavid du Colombier 			break;
205*0cc6832dSDavid du Colombier 	}
206*0cc6832dSDavid du Colombier 	if(i == Inittimeout){
207*0cc6832dSDavid du Colombier 		print("sdmmc: card won't power up\n");
208*0cc6832dSDavid du Colombier 		poperror();
209*0cc6832dSDavid du Colombier 		return 2;
210*0cc6832dSDavid du Colombier 	}
211*0cc6832dSDavid du Colombier 	ctl->ocr = r[0];
212*0cc6832dSDavid du Colombier 	io->cmd(ALL_SEND_CID, 0, r);
213*0cc6832dSDavid du Colombier 	memmove(ctl->cid, r, sizeof ctl->cid);
214*0cc6832dSDavid du Colombier 	io->cmd(SEND_RELATIVE_ADDR, 0, r);
215*0cc6832dSDavid du Colombier 	ctl->rca = r[0]>>16;
216*0cc6832dSDavid du Colombier 	io->cmd(SEND_CSD, ctl->rca<<Rcashift, r);
217*0cc6832dSDavid du Colombier 	memmove(ctl->csd, r, sizeof ctl->csd);
218*0cc6832dSDavid du Colombier 	identify(unit, ctl->csd);
219*0cc6832dSDavid du Colombier 	io->cmd(SELECT_CARD, ctl->rca<<Rcashift, r);
220*0cc6832dSDavid du Colombier 	io->cmd(SET_BLOCKLEN, unit->secsize, r);
221*0cc6832dSDavid du Colombier 	io->cmd(APP_CMD, ctl->rca<<Rcashift, r);
222*0cc6832dSDavid du Colombier 	io->cmd(SET_BUS_WIDTH, Width4, r);
223*0cc6832dSDavid du Colombier 	poperror();
224*0cc6832dSDavid du Colombier 	return 1;
225*0cc6832dSDavid du Colombier }
226*0cc6832dSDavid du Colombier 
227*0cc6832dSDavid du Colombier static int
mmcrctl(SDunit * unit,char * p,int l)228*0cc6832dSDavid du Colombier mmcrctl(SDunit *unit, char *p, int l)
229*0cc6832dSDavid du Colombier {
230*0cc6832dSDavid du Colombier 	Ctlr *ctl;
231*0cc6832dSDavid du Colombier 	int i, n;
232*0cc6832dSDavid du Colombier 
233*0cc6832dSDavid du Colombier 	ctl = unit->dev->ctlr;
234*0cc6832dSDavid du Colombier 	assert(unit->subno == 0);
235*0cc6832dSDavid du Colombier 	if(unit->sectors == 0){
236*0cc6832dSDavid du Colombier 		mmconline(unit);
237*0cc6832dSDavid du Colombier 		if(unit->sectors == 0)
238*0cc6832dSDavid du Colombier 			return 0;
239*0cc6832dSDavid du Colombier 	}
240*0cc6832dSDavid du Colombier 	n = snprint(p, l, "rca %4.4ux ocr %8.8ux\ncid ", ctl->rca, ctl->ocr);
241*0cc6832dSDavid du Colombier 	for(i = nelem(ctl->cid)-1; i >= 0; i--)
242*0cc6832dSDavid du Colombier 		n += snprint(p+n, l-n, "%8.8ux", ctl->cid[i]);
243*0cc6832dSDavid du Colombier 	n += snprint(p+n, l-n, " csd ");
244*0cc6832dSDavid du Colombier 	for(i = nelem(ctl->csd)-1; i >= 0; i--)
245*0cc6832dSDavid du Colombier 		n += snprint(p+n, l-n, "%8.8ux", ctl->csd[i]);
246*0cc6832dSDavid du Colombier 	n += snprint(p+n, l-n, "\ngeometry %llud %ld\n",
247*0cc6832dSDavid du Colombier 		unit->sectors, unit->secsize);
248*0cc6832dSDavid du Colombier 	return n;
249*0cc6832dSDavid du Colombier }
250*0cc6832dSDavid du Colombier 
251*0cc6832dSDavid du Colombier static long
mmcbio(SDunit * unit,int lun,int write,void * data,long nb,uvlong bno)252*0cc6832dSDavid du Colombier mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno)
253*0cc6832dSDavid du Colombier {
254*0cc6832dSDavid du Colombier 	int len, tries;
255*0cc6832dSDavid du Colombier 	ulong b;
256*0cc6832dSDavid du Colombier 	u32int r[4];
257*0cc6832dSDavid du Colombier 	uchar *buf;
258*0cc6832dSDavid du Colombier 	Ctlr *ctl;
259*0cc6832dSDavid du Colombier 	SDio *io;
260*0cc6832dSDavid du Colombier 
261*0cc6832dSDavid du Colombier 	USED(lun);
262*0cc6832dSDavid du Colombier 	ctl = unit->dev->ctlr;
263*0cc6832dSDavid du Colombier 	io = ctl->io;
264*0cc6832dSDavid du Colombier 	assert(unit->subno == 0);
265*0cc6832dSDavid du Colombier 	if(unit->sectors == 0)
266*0cc6832dSDavid du Colombier 		error("media change");
267*0cc6832dSDavid du Colombier 	buf = data;
268*0cc6832dSDavid du Colombier 	len = unit->secsize;
269*0cc6832dSDavid du Colombier 	if(Multiblock){
270*0cc6832dSDavid du Colombier 		b = bno;
271*0cc6832dSDavid du Colombier 		tries = 0;
272*0cc6832dSDavid du Colombier 		while(waserror())
273*0cc6832dSDavid du Colombier 			if(++tries == 3)
274*0cc6832dSDavid du Colombier 				nexterror();
275*0cc6832dSDavid du Colombier 		io->iosetup(write, buf, len, nb);
276*0cc6832dSDavid du Colombier 		if(waserror()){
277*0cc6832dSDavid du Colombier 			io->cmd(STOP_TRANSMISSION, 0, r);
278*0cc6832dSDavid du Colombier 			nexterror();
279*0cc6832dSDavid du Colombier 		}
280*0cc6832dSDavid du Colombier 		io->cmd(write? WRITE_MULTIPLE_BLOCK: READ_MULTIPLE_BLOCK,
281*0cc6832dSDavid du Colombier 			ctl->ocr & Ccs? b: b * len, r);
282*0cc6832dSDavid du Colombier 		io->io(write, buf, nb * len);
283*0cc6832dSDavid du Colombier 		poperror();
284*0cc6832dSDavid du Colombier 		io->cmd(STOP_TRANSMISSION, 0, r);
285*0cc6832dSDavid du Colombier 		poperror();
286*0cc6832dSDavid du Colombier 		b += nb;
287*0cc6832dSDavid du Colombier 	}else{
288*0cc6832dSDavid du Colombier 		for(b = bno; b < bno + nb; b++){
289*0cc6832dSDavid du Colombier 			io->iosetup(write, buf, len, 1);
290*0cc6832dSDavid du Colombier 			io->cmd(write? WRITE_BLOCK : READ_SINGLE_BLOCK,
291*0cc6832dSDavid du Colombier 				ctl->ocr & Ccs? b: b * len, r);
292*0cc6832dSDavid du Colombier 			io->io(write, buf, len);
293*0cc6832dSDavid du Colombier 			buf += len;
294*0cc6832dSDavid du Colombier 		}
295*0cc6832dSDavid du Colombier 	}
296*0cc6832dSDavid du Colombier 	return (b - bno) * len;
297*0cc6832dSDavid du Colombier }
298*0cc6832dSDavid du Colombier 
299*0cc6832dSDavid du Colombier static int
mmcrio(SDreq *)300*0cc6832dSDavid du Colombier mmcrio(SDreq*)
301*0cc6832dSDavid du Colombier {
302*0cc6832dSDavid du Colombier 	return -1;
303*0cc6832dSDavid du Colombier }
304*0cc6832dSDavid du Colombier 
305*0cc6832dSDavid du Colombier SDifc sdmmcifc = {
306*0cc6832dSDavid du Colombier 	.name	= "mmc",
307*0cc6832dSDavid du Colombier 	.pnp	= mmcpnp,
308*0cc6832dSDavid du Colombier 	.enable	= mmcenable,
309*0cc6832dSDavid du Colombier 	.verify	= mmcverify,
310*0cc6832dSDavid du Colombier 	.online	= mmconline,
311*0cc6832dSDavid du Colombier 	.rctl	= mmcrctl,
312*0cc6832dSDavid du Colombier 	.bio	= mmcbio,
313*0cc6832dSDavid du Colombier 	.rio	= mmcrio,
314*0cc6832dSDavid du Colombier };
315