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