123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- /*
- * mmc / sd memory card
- *
- * Copyright © 2012 Richard Miller <r.miller@acm.org>
- *
- * Assumes only one card on the bus
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "../port/error.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "../port/sd.h"
- #define CSD(end, start) rbits(csd, start, (end)-(start)+1)
- typedef struct Ctlr Ctlr;
- enum {
- Inittimeout = 15,
- Multiblock = 1,
- /* Commands */
- GO_IDLE_STATE = 0,
- ALL_SEND_CID = 2,
- SEND_RELATIVE_ADDR= 3,
- SELECT_CARD = 7,
- SD_SEND_IF_COND = 8,
- SEND_CSD = 9,
- STOP_TRANSMISSION= 12,
- SEND_STATUS = 13,
- SET_BLOCKLEN = 16,
- READ_SINGLE_BLOCK= 17,
- READ_MULTIPLE_BLOCK= 18,
- WRITE_BLOCK = 24,
- WRITE_MULTIPLE_BLOCK= 25,
- APP_CMD = 55, /* prefix for following app-specific commands */
- SET_BUS_WIDTH = 6,
- SD_SEND_OP_COND = 41,
- /* Command arguments */
- /* SD_SEND_IF_COND */
- Voltage = 1<<8,
- Checkpattern = 0x42,
- /* SELECT_CARD */
- Rcashift = 16,
- /* SD_SEND_OP_COND */
- Hcs = 1<<30, /* host supports SDHC & SDXC */
- Ccs = 1<<30, /* card is SDHC or SDXC */
- V3_3 = 3<<20, /* 3.2-3.4 volts */
- /* SET_BUS_WIDTH */
- Width1 = 0<<0,
- Width4 = 2<<0,
- /* OCR (operating conditions register) */
- Powerup = 1<<31,
- };
- struct Ctlr {
- SDev *dev;
- SDio *io;
- /* SD card registers */
- u16int rca;
- u32int ocr;
- u32int cid[4];
- u32int csd[4];
- };
- extern SDifc sdmmcifc;
- extern SDio sdio;
- static uint
- rbits(u32int *p, uint start, uint len)
- {
- uint w, off, v;
- w = start / 32;
- off = start % 32;
- if(off == 0)
- v = p[w];
- else
- v = p[w] >> off | p[w+1] << (32-off);
- if(len < 32)
- return v & ((1<<len) - 1);
- else
- return v;
- }
- static void
- identify(SDunit *unit, u32int *csd)
- {
- uint csize, mult;
- unit->secsize = 1 << CSD(83, 80);
- switch(CSD(127, 126)){
- case 0: /* CSD version 1 */
- csize = CSD(73, 62);
- mult = CSD(49, 47);
- unit->sectors = (csize+1) * (1<<(mult+2));
- break;
- case 1: /* CSD version 2 */
- csize = CSD(69, 48);
- unit->sectors = (csize+1) * 512LL*KiB / unit->secsize;
- break;
- }
- if(unit->secsize == 1024){
- unit->sectors <<= 1;
- unit->secsize = 512;
- }
- }
- static SDev*
- mmcpnp(void)
- {
- SDev *sdev;
- Ctlr *ctl;
- if(sdio.init() < 0)
- return nil;
- sdev = malloc(sizeof(SDev));
- if(sdev == nil)
- return nil;
- ctl = malloc(sizeof(Ctlr));
- if(ctl == nil){
- free(sdev);
- return nil;
- }
- sdev->idno = 'M';
- sdev->ifc = &sdmmcifc;
- sdev->nunit = 1;
- sdev->ctlr = ctl;
- ctl->dev = sdev;
- ctl->io = &sdio;
- return sdev;
- }
- static int
- mmcverify(SDunit *unit)
- {
- int n;
- Ctlr *ctl;
- ctl = unit->dev->ctlr;
- n = ctl->io->inquiry((char*)&unit->inquiry[8], sizeof(unit->inquiry)-8);
- if(n < 0)
- return 0;
- unit->inquiry[0] = SDperdisk;
- unit->inquiry[1] = SDinq1removable;
- unit->inquiry[4] = sizeof(unit->inquiry)-4;
- return 1;
- }
- static int
- mmcenable(SDev* dev)
- {
- Ctlr *ctl;
- ctl = dev->ctlr;
- ctl->io->enable();
- return 1;
- }
- static int
- mmconline(SDunit *unit)
- {
- int hcs, i;
- u32int r[4];
- Ctlr *ctl;
- SDio *io;
- ctl = unit->dev->ctlr;
- io = ctl->io;
- assert(unit->subno == 0);
- if(waserror()){
- unit->sectors = 0;
- return 0;
- }
- if(unit->sectors != 0){
- io->cmd(SEND_STATUS, ctl->rca<<Rcashift, r);
- poperror();
- return 1;
- }
- io->cmd(GO_IDLE_STATE, 0, r);
- hcs = 0;
- if(!waserror()){
- io->cmd(SD_SEND_IF_COND, Voltage|Checkpattern, r);
- if(r[0] == (Voltage|Checkpattern)) /* SD 2.0 or above */
- hcs = Hcs;
- poperror();
- }
- for(i = 0; i < Inittimeout; i++){
- delay(100);
- io->cmd(APP_CMD, 0, r);
- io->cmd(SD_SEND_OP_COND, hcs|V3_3, r);
- if(r[0] & Powerup)
- break;
- }
- if(i == Inittimeout){
- print("sdmmc: card won't power up\n");
- poperror();
- return 2;
- }
- ctl->ocr = r[0];
- io->cmd(ALL_SEND_CID, 0, r);
- memmove(ctl->cid, r, sizeof ctl->cid);
- io->cmd(SEND_RELATIVE_ADDR, 0, r);
- ctl->rca = r[0]>>16;
- io->cmd(SEND_CSD, ctl->rca<<Rcashift, r);
- memmove(ctl->csd, r, sizeof ctl->csd);
- identify(unit, ctl->csd);
- io->cmd(SELECT_CARD, ctl->rca<<Rcashift, r);
- io->cmd(SET_BLOCKLEN, unit->secsize, r);
- io->cmd(APP_CMD, ctl->rca<<Rcashift, r);
- io->cmd(SET_BUS_WIDTH, Width4, r);
- poperror();
- return 1;
- }
- static int
- mmcrctl(SDunit *unit, char *p, int l)
- {
- Ctlr *ctl;
- int i, n;
- ctl = unit->dev->ctlr;
- assert(unit->subno == 0);
- if(unit->sectors == 0){
- mmconline(unit);
- if(unit->sectors == 0)
- return 0;
- }
- n = snprint(p, l, "rca %4.4ux ocr %8.8ux\ncid ", ctl->rca, ctl->ocr);
- for(i = nelem(ctl->cid)-1; i >= 0; i--)
- n += snprint(p+n, l-n, "%8.8ux", ctl->cid[i]);
- n += snprint(p+n, l-n, " csd ");
- for(i = nelem(ctl->csd)-1; i >= 0; i--)
- n += snprint(p+n, l-n, "%8.8ux", ctl->csd[i]);
- n += snprint(p+n, l-n, "\ngeometry %llud %ld\n",
- unit->sectors, unit->secsize);
- return n;
- }
- static long
- mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno)
- {
- int len, tries;
- ulong b;
- u32int r[4];
- uchar *buf;
- Ctlr *ctl;
- SDio *io;
- USED(lun);
- ctl = unit->dev->ctlr;
- io = ctl->io;
- assert(unit->subno == 0);
- if(unit->sectors == 0)
- error("media change");
- buf = data;
- len = unit->secsize;
- if(Multiblock){
- b = bno;
- tries = 0;
- while(waserror())
- if(++tries == 3)
- nexterror();
- io->iosetup(write, buf, len, nb);
- if(waserror()){
- io->cmd(STOP_TRANSMISSION, 0, r);
- nexterror();
- }
- io->cmd(write? WRITE_MULTIPLE_BLOCK: READ_MULTIPLE_BLOCK,
- ctl->ocr & Ccs? b: b * len, r);
- io->io(write, buf, nb * len);
- poperror();
- io->cmd(STOP_TRANSMISSION, 0, r);
- poperror();
- b += nb;
- }else{
- for(b = bno; b < bno + nb; b++){
- io->iosetup(write, buf, len, 1);
- io->cmd(write? WRITE_BLOCK : READ_SINGLE_BLOCK,
- ctl->ocr & Ccs? b: b * len, r);
- io->io(write, buf, len);
- buf += len;
- }
- }
- return (b - bno) * len;
- }
- static int
- mmcrio(SDreq*)
- {
- return -1;
- }
- SDifc sdmmcifc = {
- .name = "mmc",
- .pnp = mmcpnp,
- .enable = mmcenable,
- .verify = mmcverify,
- .online = mmconline,
- .rctl = mmcrctl,
- .bio = mmcbio,
- .rio = mmcrio,
- };
|