]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/sdmmc.c
devproc: return process id when reading /proc/n/ctl file
[plan9front.git] / sys / src / 9 / port / sdmmc.c
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 extern SDifc sdmmcifc;
85 extern SDio sdio;
86
87 static uint
88 rbits(u32int *p, uint start, uint len)
89 {
90         uint w, off, v;
91
92         w   = start / 32;
93         off = start % 32;
94         if(off == 0)
95                 v = p[w];
96         else
97                 v = p[w] >> off | p[w+1] << (32-off);
98         if(len < 32)
99                 return v & ((1<<len) - 1);
100         else
101                 return v;
102 }
103
104 static void
105 identify(SDunit *unit, u32int *csd)
106 {
107         uint csize, mult;
108
109         unit->secsize = 1 << CSD(83, 80);
110         switch(CSD(127, 126)){
111         case 0:                         /* CSD version 1 */
112                 csize = CSD(73, 62);
113                 mult = CSD(49, 47);
114                 unit->sectors = (csize+1) * (1<<(mult+2));
115                 break;
116         case 1:                         /* CSD version 2 */
117                 csize = CSD(69, 48);
118                 unit->sectors = (csize+1) * 0x80000LL / unit->secsize;
119                 break;
120         }
121         if(unit->secsize == 1024){
122                 unit->sectors <<= 1;
123                 unit->secsize = 512;
124         }
125 }
126
127 static SDev*
128 mmcpnp(void)
129 {
130         SDev *sdev;
131         Ctlr *ctl;
132
133         if(sdio.init() < 0)
134                 return nil;
135         sdev = malloc(sizeof(SDev));
136         if(sdev == nil)
137                 return nil;
138         ctl = malloc(sizeof(Ctlr));
139         if(ctl == nil){
140                 free(sdev);
141                 return nil;
142         }
143         sdev->idno = 'M';
144         sdev->ifc = &sdmmcifc;
145         sdev->nunit = 1;
146         sdev->ctlr = ctl;
147         ctl->dev = sdev;
148         ctl->io = &sdio;
149         return sdev;
150 }
151
152 static int
153 mmcverify(SDunit *unit)
154 {
155         int n;
156         Ctlr *ctl;
157
158         ctl = unit->dev->ctlr;
159         n = ctl->io->inquiry((char*)&unit->inquiry[8], sizeof(unit->inquiry)-8);
160         if(n < 0)
161                 return 0;
162         unit->inquiry[0] = 0x00;        /* direct access (disk) */
163         unit->inquiry[1] = 0x80;        /* removable */
164         unit->inquiry[4] = sizeof(unit->inquiry)-4;
165         return 1;
166 }
167
168 static int
169 mmcenable(SDev* dev)
170 {
171         Ctlr *ctl;
172
173         ctl = dev->ctlr;
174         ctl->io->enable();
175         return 1;
176 }
177
178 static void
179 mmcswitchfunc(SDio *io, int arg)
180 {
181         uchar *buf;
182         int n;
183         u32int r[4];
184
185         n = Funcbytes;
186         buf = sdmalloc(n);
187         if(waserror()){
188                 print("mmcswitchfunc error\n");
189                 sdfree(buf);
190                 nexterror();
191         }
192         io->iosetup(0, buf, n, 1);
193         io->cmd(SWITCH_FUNC, arg, r);
194         io->io(0, buf, n);
195         sdfree(buf);
196         poperror();
197 }
198
199 static int
200 mmconline(SDunit *unit)
201 {
202         int hcs, i;
203         u32int r[4];
204         Ctlr *ctl;
205         SDio *io;
206
207         ctl = unit->dev->ctlr;
208         io = ctl->io;
209         assert(unit->subno == 0);
210
211         if(waserror()){
212                 unit->sectors = 0;
213                 return 0;
214         }
215         if(unit->sectors != 0){
216                 io->cmd(SEND_STATUS, ctl->rca<<Rcashift, r);
217                 poperror();
218                 return 1;
219         }
220         io->cmd(GO_IDLE_STATE, 0, r);
221         hcs = 0;
222         if(!waserror()){
223                 io->cmd(SD_SEND_IF_COND, Voltage|Checkpattern, r);
224                 if(r[0] == (Voltage|Checkpattern))      /* SD 2.0 or above */
225                         hcs = Hcs;
226                 poperror();
227         }
228         for(i = 0; i < Inittimeout; i++){
229                 delay(100);
230                 io->cmd(APP_CMD, 0, r);
231                 io->cmd(SD_SEND_OP_COND, hcs|V3_3, r);
232                 if(r[0] & Powerup)
233                         break;
234         }
235         if(i == Inittimeout){
236                 print("sdmmc: card won't power up\n");
237                 poperror();
238                 return 2;
239         }
240         ctl->ocr = r[0];
241         io->cmd(ALL_SEND_CID, 0, r);
242         memmove(ctl->cid, r, sizeof ctl->cid);
243         io->cmd(SEND_RELATIVE_ADDR, 0, r);
244         ctl->rca = r[0]>>16;
245         io->cmd(SEND_CSD, ctl->rca<<Rcashift, r);
246         memmove(ctl->csd, r, sizeof ctl->csd);
247         identify(unit, ctl->csd);
248         io->cmd(SELECT_CARD, ctl->rca<<Rcashift, r);
249         io->cmd(SET_BLOCKLEN, unit->secsize, r);
250         io->cmd(APP_CMD, ctl->rca<<Rcashift, r);
251         io->cmd(SET_BUS_WIDTH, Width4, r);
252         if(io->highspeed){
253                 if(!waserror()){
254                         mmcswitchfunc(io, Hispeed|Setfunc);
255                         poperror();
256                 }
257         }
258         poperror();
259         return 1;
260 }
261
262 static int
263 mmcrctl(SDunit *unit, char *p, int l)
264 {
265         Ctlr *ctl;
266         int i, n;
267
268         ctl = unit->dev->ctlr;
269         assert(unit->subno == 0);
270         if(unit->sectors == 0){
271                 mmconline(unit);
272                 if(unit->sectors == 0)
273                         return 0;
274         }
275         n = snprint(p, l, "rca %4.4ux ocr %8.8ux\ncid ", ctl->rca, ctl->ocr);
276         for(i = nelem(ctl->cid)-1; i >= 0; i--)
277                 n += snprint(p+n, l-n, "%8.8ux", ctl->cid[i]);
278         n += snprint(p+n, l-n, " csd ");
279         for(i = nelem(ctl->csd)-1; i >= 0; i--)
280                 n += snprint(p+n, l-n, "%8.8ux", ctl->csd[i]);
281         n += snprint(p+n, l-n, "\ngeometry %llud %ld\n",
282                 unit->sectors, unit->secsize);
283         return n;
284 }
285
286 static long
287 mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno)
288 {
289         int len, tries;
290         ulong b;
291         u32int r[4];
292         uchar *buf;
293         Ctlr *ctl;
294         SDio *io;
295
296         USED(lun);
297         ctl = unit->dev->ctlr;
298         io = ctl->io;
299         assert(unit->subno == 0);
300         if(unit->sectors == 0)
301                 error(Echange);
302         buf = data;
303         len = unit->secsize;
304         if(Multiblock){
305                 b = bno;
306                 tries = 0;
307                 while(waserror())
308                         if(++tries == 3)
309                                 nexterror();
310                 io->iosetup(write, buf, len, nb);
311                 if(waserror()){
312                         io->cmd(STOP_TRANSMISSION, 0, r);
313                         nexterror();
314                 }
315                 io->cmd(write? WRITE_MULTIPLE_BLOCK: READ_MULTIPLE_BLOCK,
316                         ctl->ocr & Ccs? b: b * len, r);
317                 io->io(write, buf, nb * len);
318                 poperror();
319                 io->cmd(STOP_TRANSMISSION, 0, r);
320                 poperror();
321                 b += nb;
322         }else{
323                 for(b = bno; b < bno + nb; b++){
324                         io->iosetup(write, buf, len, 1);
325                         io->cmd(write? WRITE_BLOCK : READ_SINGLE_BLOCK,
326                                 ctl->ocr & Ccs? b: b * len, r);
327                         io->io(write, buf, len);
328                         buf += len;
329                 }
330         }
331         return (b - bno) * len;
332 }
333
334 static int
335 mmcrio(SDreq *r)
336 {
337         int i, rw, count;
338         uvlong lba;
339
340         if((i = sdfakescsi(r)) != SDnostatus)
341                 return r->status = i;
342         if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
343                 return i;
344         r->rlen = mmcbio(r->unit, r->lun, rw == SDwrite, r->data, count, lba);
345         return r->status = SDok;
346 }
347
348 SDifc sdmmcifc = {
349         .name   = "mmc",
350         .pnp    = mmcpnp,
351         .enable = mmcenable,
352         .verify = mmcverify,
353         .online = mmconline,
354         .rctl   = mmcrctl,
355         .bio    = mmcbio,
356         .rio    = mmcrio,
357 };