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