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