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