]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/sdmmc.c
pc kernel: fix wrong simd exception mask (fixes go bootstrap)
[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         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
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
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) * 0x80000LL / unit->secsize;
111                 break;
112         }
113         if(unit->secsize == 1024){
114                 unit->sectors <<= 1;
115                 unit->secsize = 512;
116         }
117 }
118
119 static SDev*
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
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] = 0x00;        /* direct access (disk) */
155         unit->inquiry[1] = 0x80;        /* removable */
156         unit->inquiry[4] = sizeof(unit->inquiry)-4;
157         return 1;
158 }
159
160 static int
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
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
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
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(Echange);
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
300 mmcrio(SDreq *r)
301 {
302         int i, rw, count;
303         uvlong lba;
304
305         if((i = sdfakescsi(r)) != SDnostatus)
306                 return r->status = i;
307         if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
308                 return i;
309         r->rlen = mmcbio(r->unit, r->lun, rw == SDwrite, r->data, count, lba);
310         return r->status = SDok;
311 }
312
313 SDifc sdmmcifc = {
314         .name   = "mmc",
315         .pnp    = mmcpnp,
316         .enable = mmcenable,
317         .verify = mmcverify,
318         .online = mmconline,
319         .rctl   = mmcrctl,
320         .bio    = mmcbio,
321         .rio    = mmcrio,
322 };