ref: f8ddcb23b9dd969875dd4e961591700c79b7d5f7
parent: d57aad453176b4de9e46cc543777f642efdacf30
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Thu Dec 29 13:16:28 EST 2022
sdmmc: actually implement mmc support - pass the correct ocr value for SEND_OP_COND (advertise Hcs and 3.3v) - disambiguate SEND_RELATIVE_ADDRESS/SET_RELATIVE_ADDRESS - detect the MMC version from CSD structure - detect the capacity according to spec for MMC - implement SWITCH command to set highspeed frequency and bus width - rename Ctlr to Card (thats really what we keep track of here)
--- a/sys/src/9/port/sdmmc.c
+++ b/sys/src/9/port/sdmmc.c
@@ -16,10 +16,12 @@
#include "../port/sd.h"
/* Commands */
-SDiocmd GO_IDLE_STATE = { 0, 0, 0, 0, "GO_IDLE_STATE" };
+SDiocmd GO_IDLE_STATE = { 0, 0, 0, 0, "GO_IDLE_STATE" };
SDiocmd SEND_OP_COND = { 1, 3, 0, 0, "SEND_OP_COND" };
SDiocmd ALL_SEND_CID = { 2, 2, 0, 0, "ALL_SEND_CID" };
SDiocmd SET_RELATIVE_ADDR = { 3, 1, 0, 0, "SET_RELATIVE_ADDR" };
+SDiocmd SEND_RELATIVE_ADDR = { 3, 6, 0, 0, "SEND_RELATIVE_ADDR" };
+SDiocmd SWITCH = { 6, 1, 1, 0, "SWITCH" };
SDiocmd SWITCH_FUNC = { 6, 1, 0, 1, "SWITCH_FUNC" };
SDiocmd SELECT_CARD = { 7, 1, 1, 0, "SELECT_CARD" };
SDiocmd SEND_EXT_CSD = { 8, 1, 0, 1, "SEND_EXT_CSD" };
@@ -41,17 +43,12 @@
/* Command arguments */
enum {
/* SD_SEND_IF_COND */
- Voltage = 1<<8,
+ Voltage = 1<<8, /* Host supplied voltage range 2.7-3.6 volts*/
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 */
-
/* SD_SET_BUS_WIDTH */
Width1 = 0<<0,
Width4 = 2<<0,
@@ -63,8 +60,18 @@
Setfunc = 0x80FFFFF0,
Funcbytes = 64,
+ /* SWITCH */
+ MMCSetSDTiming = 0x3B90000,
+ MMCSetHSTiming = 0x3B90100,
+ MMCSetBusWidth1 = 0x3B70000,
+ MMCSetBusWidth4 = 0x3B70100,
+ MMCSetBusWidth8 = 0x3B70200,
+
/* OCR (operating conditions register) */
Powerup = 1<<31,
+ Hcs = 1<<30, /* Host capacity support */
+ Ccs = 1<<30, /* Card capacity status */
+ V3_3 = 3<<20, /* 3.2-3.4 volts */
};
enum {
@@ -76,12 +83,13 @@
SDfreqhs = 50000000, /* highspeed frequency */
};
-typedef struct Ctlr Ctlr;
-struct Ctlr {
+typedef struct Card Card;
+struct Card {
SDev *dev;
SDio *io;
int ismmc;
+ int specver;
int buswidth;
int busspeed;
@@ -118,7 +126,7 @@
init1(void)
{
SDev *sdev;
- Ctlr *ctlr;
+ Card *card;
SDio *io;
int more;
@@ -126,12 +134,12 @@
return nil;
if((sdev = malloc(sizeof(SDev))) == nil)
return nil;
- if((ctlr = malloc(sizeof(Ctlr))) == nil){
+ if((card = malloc(sizeof(Card))) == nil){
free(sdev);
return nil;
}
if((io = malloc(sizeof(SDio))) == nil){
- free(ctlr);
+ free(card);
free(sdev);
return nil;
}
@@ -144,7 +152,7 @@
goto Next;
free(io);
- free(ctlr);
+ free(card);
free(sdev);
return nil;
}
@@ -154,9 +162,9 @@
sdev->idno = 'M';
sdev->ifc = &sdmmcifc;
sdev->nunit = 1;
- sdev->ctlr = ctlr;
- ctlr->dev = sdev;
- ctlr->io = io;
+ sdev->ctlr = card;
+ card->dev = sdev;
+ card->io = io;
return sdev;
}
@@ -172,9 +180,9 @@
}
static void
-readextcsd(Ctlr *ctlr)
+readextcsd(Card *card)
{
- SDio *io = ctlr->io;
+ SDio *io = card->io;
u32int r[4];
uchar *buf;
@@ -186,7 +194,7 @@
(*io->iosetup)(io, 0, buf, 512, 1);
(*io->cmd)(io, &SEND_EXT_CSD, 0, r);
(*io->io)(io, 0, buf, 512);
- memmove(ctlr->ext_csd, buf, sizeof ctlr->ext_csd);
+ memmove(card->ext_csd, buf, sizeof card->ext_csd);
sdfree(buf);
poperror();
}
@@ -223,27 +231,90 @@
static void
identify(SDunit *unit)
{
- Ctlr *ctlr = unit->dev->ctlr;
+ Card *card = unit->dev->ctlr;
uint csize, mult;
+ uvlong capacity;
-#define CSD(end, start) rbits(ctlr->csd, start, (end)-(start)+1)
+#define CSD(end, start) rbits(card->csd, start, (end)-(start)+1)
+ mult = CSD(49, 47);
+ csize = CSD(73, 62);
+ unit->sectors = (csize+1) * (1<<(mult+2));
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) * 0x80000LL / unit->secsize;
- break;
- default:
- readextcsd(ctlr);
-#define EXT_CSD(end, start) rbytes(ctlr->ext_csd, start, (end)-(start)+1)
- unit->sectors = EXT_CSD(215, 212);
- unit->secsize = 512;
+
+ card->specver = 0;
+
+ if(card->ismmc){
+ switch(CSD(125, 122)){
+ case 0:
+ default:
+ card->specver = 120; /* 1.2 */
+ break;
+ case 1:
+ card->specver = 140; /* 1.4 */
+ break;
+ case 2:
+ card->specver = 122; /* 1.22 */
+ break;
+ case 3:
+ card->specver = 300; /* 3.0 */
+ break;
+ case 4:
+ card->specver = 400; /* 4.0 */
+ break;
+ }
+ switch(CSD(127, 126)){
+ case 2: /* MMC CSD Version 1.2 */
+ case 3: /* MMC Version coded in EXT_CSD */
+ if(card->specver < 400)
+ break;
+ readextcsd(card);
+#define EXT_CSD(end, start) rbytes(card->ext_csd, start, (end)-(start)+1)
+ switch(EXT_CSD(192, 192)){
+ case 8:
+ card->specver = 510; /* 5.1 */
+ break;
+ case 7:
+ card->specver = 500; /* 5.0 */
+ break;
+ case 6:
+ card->specver = 450; /* 4.5/4.51 */
+ break;
+ case 5:
+ card->specver = 441; /* 4.41 */
+ break;
+ case 3:
+ card->specver = 430; /* 4.3 */
+ break;
+ case 2:
+ card->specver = 420; /* 4.2 */
+ break;
+ case 1:
+ card->specver = 410; /* 4.1 */
+ break;
+ case 0:
+ card->specver = 400; /* 4.0 */
+ break;
+ }
+ }
+ if(card->specver >= 420) {
+ capacity = EXT_CSD(215, 212) * 512ULL;
+ if(capacity > 0x80000000ULL)
+ unit->sectors = capacity / unit->secsize;
+ }
+ } else {
+ switch(CSD(127, 126)){
+ case 0: /* SD Version 1.0 */
+ card->specver = 100;
+ break;
+ case 1: /* SD Version 2.0 */
+ card->specver = 200;
+ csize = CSD(69, 48);
+ capacity = (csize+1) * 0x80000ULL;
+ unit->sectors = capacity / unit->secsize;
+ break;
+ }
}
+
if(unit->secsize == 1024){
unit->sectors <<= 1;
unit->secsize = 512;
@@ -253,8 +324,8 @@
static int
mmcverify(SDunit *unit)
{
- Ctlr *ctlr = unit->dev->ctlr;
- SDio *io = ctlr->io;
+ Card *card = unit->dev->ctlr;
+ SDio *io = card->io;
int n;
n = (*io->inquiry)(io, (char*)&unit->inquiry[8], sizeof(unit->inquiry)-8);
@@ -269,17 +340,38 @@
static int
mmcenable(SDev* dev)
{
- Ctlr *ctlr = dev->ctlr;
- SDio *io = ctlr->io;
+ Card *card = dev->ctlr;
+ SDio *io = card->io;
(*io->enable)(io);
return 1;
}
static void
-switchfunc(Ctlr *ctlr, int arg)
+mmcswitch(Card *card, u32int arg)
{
- SDio *io = ctlr->io;
+ SDio *io = card->io;
u32int r[4];
+ int i;
+
+ (*io->cmd)(io, &SWITCH, arg, r);
+
+ for(i=0; i<10;i++){
+ tsleep(&up->sleep, return0, nil, 100);
+
+ (*io->cmd)(io, &SEND_STATUS, card->rca<<Rcashift, r);
+ if(r[0] & (1<<7))
+ error(Eio);
+ if(r[0] & (1<<8))
+ return;
+ }
+ error(Eio);
+}
+
+static void
+sdswitchfunc(Card *card, int arg)
+{
+ SDio *io = card->io;
+ u32int r[4];
uchar *buf;
int n;
@@ -292,12 +384,10 @@
(*io->iosetup)(io, 0, buf, n, 1);
(*io->cmd)(io, &SWITCH_FUNC, arg, r);
if((arg & 0xFFFFFFF0) == Setfunc){
- ctlr->busspeed = (arg & Hispeed) != 0? SDfreqhs: SDfreq;
- if(io->bus != nil){
- tsleep(&up->sleep, return0, nil, 10);
- (*io->bus)(io, 0, ctlr->busspeed);
- tsleep(&up->sleep, return0, nil, 10);
- }
+ card->busspeed = (arg & Hispeed) != 0? SDfreqhs: SDfreq;
+ tsleep(&up->sleep, return0, nil, 10);
+ (*io->bus)(io, 0, card->busspeed);
+ tsleep(&up->sleep, return0, nil, 10);
}
(*io->io)(io, 0, buf, n);
sdfree(buf);
@@ -305,64 +395,73 @@
}
static int
-cardinit(Ctlr *ctlr)
+cardinit(Card *card)
{
- SDio *io = ctlr->io;
- u32int r[4];
- int i, hcs;
+ SDio *io = card->io;
+ u32int r[4], ocr;
+ int i;
- ctlr->buswidth = 1;
- ctlr->busspeed = Initfreq;
- if(io->bus != nil)
- (*io->bus)(io, ctlr->buswidth, ctlr->busspeed);
+ card->buswidth = 1;
+ card->busspeed = Initfreq;
+ (*io->bus)(io, card->buswidth, card->busspeed);
+ tsleep(&up->sleep, return0, nil, 10);
(*io->cmd)(io, &GO_IDLE_STATE, 0, r);
/* card type unknown */
- ctlr->ismmc = -1;
+ card->ismmc = -1;
if(!waserror()){ /* try SD card first */
- hcs = 0;
+ ocr = V3_3;
if(!waserror()){
(*io->cmd)(io, &SD_SEND_IF_COND, Voltage|Checkpattern, r);
- if(r[0] == (Voltage|Checkpattern)) /* SD 2.0 or above */
- hcs = Hcs;
-
- ctlr->ismmc = 0; /* this is SD card */
+ if(r[0] == (Voltage|Checkpattern)){ /* SD 2.0 or above */
+ ocr |= Hcs;
+ card->ismmc = 0; /* this is SD card */
+ }
poperror();
}
for(i = 0; i < Inittimeout; i++){
tsleep(&up->sleep, return0, nil, 100);
(*io->cmd)(io, &APP_CMD, 0, r);
- (*io->cmd)(io, &SD_SEND_OP_COND, hcs|V3_3, r);
+ (*io->cmd)(io, &SD_SEND_OP_COND, ocr, r);
if(r[0] & Powerup)
break;
}
- ctlr->ismmc = 0; /* this is SD card */
+ card->ismmc = 0; /* this is SD card */
poperror();
if(i == Inittimeout)
return 2;
- } else if(ctlr->ismmc) { /* try MMC if not ruled out */
+ } else if(card->ismmc) { /* try MMC if not ruled out */
(*io->cmd)(io, &GO_IDLE_STATE, 0, r);
+ ocr = Hcs|V3_3;
for(i = 0; i < Inittimeout; i++){
tsleep(&up->sleep, return0, nil, 100);
- (*io->cmd)(io, &SEND_OP_COND, 0x80ff8080, r);
+ (*io->cmd)(io, &SEND_OP_COND, ocr, r);
if(r[0] & Powerup)
break;
}
- ctlr->ismmc = 1; /* this is MMC */
+ card->ismmc = 1; /* this is MMC */
if(i == Inittimeout)
return 2;
}
- ctlr->ocr = r[0];
+ card->ocr = r[0];
(*io->cmd)(io, &ALL_SEND_CID, 0, r);
- memmove(ctlr->cid, r, sizeof ctlr->cid);
- (*io->cmd)(io, &SET_RELATIVE_ADDR, 0, r);
- ctlr->rca = r[0]>>16;
- (*io->cmd)(io, &SEND_CSD, ctlr->rca<<Rcashift, r);
- memmove(ctlr->csd, r, sizeof ctlr->csd);
+ memmove(card->cid, r, sizeof card->cid);
+
+ if(card->ismmc){
+ card->rca = 0;
+ (*io->cmd)(io, &SET_RELATIVE_ADDR, card->rca<<16, r);
+ } else {
+ (*io->cmd)(io, &SEND_RELATIVE_ADDR, 0, r);
+ card->rca = r[0]>>16;
+ }
+
+ (*io->cmd)(io, &SEND_CSD, card->rca<<Rcashift, r);
+ memmove(card->csd, r, sizeof card->csd);
+
return 1;
}
@@ -369,15 +468,15 @@
static void
retryproc(void *arg)
{
- Ctlr *ctlr = arg;
+ Card *card = arg;
int i = 0;
while(waserror())
;
- if(i++ < ctlr->retry)
- cardinit(ctlr);
+ if(i++ < card->retry)
+ cardinit(card);
USED(i);
- ctlr->retry = 0;
+ card->retry = 0;
pexit("", 1);
}
@@ -384,50 +483,71 @@
static int
mmconline(SDunit *unit)
{
- Ctlr *ctlr = unit->dev->ctlr;
- SDio *io = ctlr->io;
+ Card *card = unit->dev->ctlr;
+ SDio *io = card->io;
u32int r[4];
assert(unit->subno == 0);
- if(ctlr->retry)
+ if(card->retry)
return 0;
if(waserror()){
unit->sectors = 0;
- if(ctlr->retry++ == 0)
- kproc(unit->name, retryproc, ctlr);
+ if(card->retry++ == 0)
+ kproc(unit->name, retryproc, card);
return 0;
}
if(unit->sectors != 0){
- (*io->cmd)(io, &SEND_STATUS, ctlr->rca<<Rcashift, r);
+ (*io->cmd)(io, &SEND_STATUS, card->rca<<Rcashift, r);
poperror();
return 1;
}
- if(cardinit(ctlr) != 1){
+ if(cardinit(card) != 1){
poperror();
return 2;
}
- (*io->cmd)(io, &SELECT_CARD, ctlr->rca<<Rcashift, r);
+ (*io->cmd)(io, &SELECT_CARD, card->rca<<Rcashift, r);
+ tsleep(&up->sleep, return0, nil, 10);
- ctlr->busspeed = SDfreq;
- if(io->bus != nil){
- tsleep(&up->sleep, return0, nil, 10);
- (*io->bus)(io, 0, ctlr->busspeed);
- tsleep(&up->sleep, return0, nil, 10);
- }
+ (*io->bus)(io, 0, card->busspeed = SDfreq);
+ tsleep(&up->sleep, return0, nil, 10);
identify(unit);
-
(*io->cmd)(io, &SET_BLOCKLEN, unit->secsize, r);
- if(!ctlr->ismmc){
- (*io->cmd)(io, &APP_CMD, ctlr->rca<<Rcashift, r);
+ if(card->ismmc && card->specver >= 400){
+ if(!waserror()){
+ mmcswitch(card, MMCSetHSTiming);
+ (*io->bus)(io, 0, card->busspeed = SDfreqhs);
+ tsleep(&up->sleep, return0, nil, 10);
+ readextcsd(card);
+ poperror();
+ } else {
+ mmcswitch(card, MMCSetSDTiming);
+ (*io->bus)(io, 0, card->busspeed = SDfreq);
+ tsleep(&up->sleep, return0, nil, 10);
+ readextcsd(card);
+ }
+ if(!waserror()){
+ mmcswitch(card, MMCSetBusWidth8);
+ (*io->bus)(io, card->buswidth = 8, 0);
+ readextcsd(card);
+ poperror();
+ } else if(!waserror()){
+ mmcswitch(card, MMCSetBusWidth4);
+ (*io->bus)(io, card->buswidth = 4, 0);
+ readextcsd(card);
+ poperror();
+ } else {
+ mmcswitch(card, MMCSetBusWidth1);
+ (*io->bus)(io, card->buswidth = 1, 0);
+ readextcsd(card);
+ }
+ } else if(!card->ismmc) {
+ (*io->cmd)(io, &APP_CMD, card->rca<<Rcashift, r);
(*io->cmd)(io, &SD_SET_BUS_WIDTH, Width4, r);
- ctlr->buswidth = 4;
- if(io->bus != nil)
- (*io->bus)(io, ctlr->buswidth, 0);
-
+ (*io->bus)(io, card->buswidth = 4, 0);
if(!waserror()){
- switchfunc(ctlr, Hispeed|Setfunc);
+ sdswitchfunc(card, Hispeed|Setfunc);
poperror();
}
}
@@ -438,8 +558,9 @@
static int
mmcrctl(SDunit *unit, char *p, int l)
{
- Ctlr *ctlr = unit->dev->ctlr;
- int i, n;
+ Card *card = unit->dev->ctlr;
+ char *s = p, *e = s + l;
+ int i;
assert(unit->subno == 0);
if(unit->sectors == 0){
@@ -447,26 +568,29 @@
if(unit->sectors == 0)
return 0;
}
- n = snprint(p, l, "rca %4.4ux ocr %8.8ux", ctlr->rca, ctlr->ocr);
+ p = seprint(p, e, "version %s %d.%2.2d\n", card->ismmc? "MMC": "SD",
+ card->specver/100, card->specver%100);
- n += snprint(p+n, l-n, "\ncid ");
- for(i = nelem(ctlr->cid)-1; i >= 0; i--)
- n += snprint(p+n, l-n, "%8.8ux", ctlr->cid[i]);
+ p = seprint(p, e, "rca %4.4ux ocr %8.8ux", card->rca, card->ocr);
- n += snprint(p+n, l-n, "\ncsd ");
- for(i = nelem(ctlr->csd)-1; i >= 0; i--)
- n += snprint(p+n, l-n, "%8.8ux", ctlr->csd[i]);
+ p = seprint(p, e, "\ncid ");
+ for(i = nelem(card->cid)-1; i >= 0; i--)
+ p = seprint(p, e, "%8.8ux", card->cid[i]);
- n += snprint(p+n, l-n, "\ngeometry %llud %ld\n",
+ p = seprint(p, e, "\ncsd ");
+ for(i = nelem(card->csd)-1; i >= 0; i--)
+ p = seprint(p, e, "%8.8ux", card->csd[i]);
+
+ p = seprint(p, e, "\ngeometry %llud %ld\n",
unit->sectors, unit->secsize);
- return n;
+ return p - s;
}
static long
mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno)
{
- Ctlr *ctlr = unit->dev->ctlr;
- SDio *io = ctlr->io;
+ Card *card = unit->dev->ctlr;
+ SDio *io = card->io;
int len, tries;
u32int r[4];
uchar *buf;
@@ -490,7 +614,7 @@
nexterror();
}
(*io->cmd)(io, write? &WRITE_MULTIPLE_BLOCK: &READ_MULTIPLE_BLOCK,
- ctlr->ocr & Ccs? b: b * len, r);
+ card->ocr & Ccs? b: b * len, r);
(*io->io)(io, write, buf, nb * len);
poperror();
(*io->cmd)(io, &STOP_TRANSMISSION, 0, r);
@@ -500,7 +624,7 @@
for(b = bno; b < bno + nb; b++){
(*io->iosetup)(io, write, buf, len, 1);
(*io->cmd)(io, write? &WRITE_SINGLE_BLOCK: &READ_SINGLE_BLOCK,
- ctlr->ocr & Ccs? b: b * len, r);
+ card->ocr & Ccs? b: b * len, r);
(*io->io)(io, write, buf, len);
buf += len;
}