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