From a44a54bf2f64b69b2289d658fd3c205491c7ac5d Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Tue, 27 Dec 2022 19:34:58 +0000 Subject: sdmmc: new interface for SDio We want to support the internal emmc device on the reform, which has a different initialization sequence from SDcard. The problem is, all mmc controller drivers where using the command index to derive the command properties. The command index's interpretation depends on the command set. And MMC has different commands then SD. Also, there are the APP_CMD escaped commands which drivers then tried to recover the state... All of this is total nonsense... The controller drivers should not care and the command properties should be maintained by port/sdmmc.c. So we pass the command as a struct SDiocmd, which has all the properties (command index, response type, data transfer mode... and even a string for debugging). The controller just converts this into register values and just executes the commands. Next, the controller drivers shouldnt snoop on the commands and then try to apply bus and frequency switching on their own. This is now made explicit by having SDio.bus(io, width, speed) function. --- sys/src/9/port/sdmmc.c | 243 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 175 insertions(+), 68 deletions(-) (limited to 'sys/src/9/port/sdmmc.c') diff --git a/sys/src/9/port/sdmmc.c b/sys/src/9/port/sdmmc.c index d8a9258fe..fd5b04497 100644 --- a/sys/src/9/port/sdmmc.c +++ b/sys/src/9/port/sdmmc.c @@ -15,34 +15,31 @@ #include "../port/sd.h" -#define CSD(end, start) rbits(csd, start, (end)-(start)+1) - -typedef struct Ctlr Ctlr; - +/* Commands */ +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 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" }; +SDiocmd SD_SEND_IF_COND = { 8, 1, 0, 0, "SD_SEND_IF_COND" }; +SDiocmd SEND_CSD = { 9, 2, 0, 0, "SEND_CSD" }; +SDiocmd STOP_TRANSMISSION = {12, 1, 1, 0, "STOP_TRANSMISSION" }; +SDiocmd SEND_STATUS = {13, 1, 0, 0, "SEND_STATUS" }; +SDiocmd SET_BLOCKLEN = {16, 1, 0, 0, "SET_BLOCKLEN" }; +SDiocmd READ_SINGLE_BLOCK = {17, 1, 0, 1, "READ_SINGLE_BLOCK" }; +SDiocmd READ_MULTIPLE_BLOCK = {18, 1, 0, 3, "READ_MULTIPLE_BLOCK" }; +SDiocmd WRITE_SINGLE_BLOCK = {24, 1, 0, 2, "WRITE_SINGLE_BLOCK" }; +SDiocmd WRITE_MULTIPLE_BLOCK = {25, 1, 0, 4, "WRITE_MULTIPLE_BLOCK" }; + +/* prefix for following app-specific commands */ +SDiocmd APP_CMD = {55, 1, 0, 0, "APP_CMD" }; +SDiocmd SD_SET_BUS_WIDTH = { 6, 1, 0, 0, "SD_SET_BUS_WIDTH" }; +SDiocmd SD_SEND_OP_COND = {41, 3, 0, 0, "SD_SEND_OP_COND" }; + +/* Command arguments */ enum { - Inittimeout = 15, - Multiblock = 1, - - /* Commands */ - GO_IDLE_STATE = 0, - ALL_SEND_CID = 2, - SEND_RELATIVE_ADDR= 3, - SWITCH_FUNC = 6, - 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, @@ -55,7 +52,7 @@ enum { Ccs = 1<<30, /* card is SDHC or SDXC */ V3_3 = 3<<20, /* 3.2-3.4 volts */ - /* SET_BUS_WIDTH */ + /* SD_SET_BUS_WIDTH */ Width1 = 0<<0, Width4 = 2<<0, @@ -70,16 +67,33 @@ enum { Powerup = 1<<31, }; +enum { + Multiblock = 1, + Inittimeout = 15, + + Initfreq = 400000, /* initialisation frequency for MMC */ + SDfreq = 25000000, /* standard SD frequency */ + SDfreqhs = 50000000, /* highspeed frequency */ +}; + +typedef struct Ctlr Ctlr; struct Ctlr { SDev *dev; SDio *io; + int ismmc; + + int buswidth; + int busspeed; + /* SD card registers */ - u16int rca; + u32int rca; u32int ocr; u32int cid[4]; u32int csd[4]; + u8int ext_csd[512]; + int retry; }; @@ -157,11 +171,31 @@ mmcpnp(void) return list; } +static void +readextcsd(Ctlr *ctlr) +{ + SDio *io = ctlr->io; + u32int r[4]; + uchar *buf; + + buf = sdmalloc(512); + if(waserror()){ + sdfree(buf); + nexterror(); + } + (*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); + sdfree(buf); + poperror(); +} + static uint rbits(u32int *p, uint start, uint len) { uint w, off, v; - + w = start / 32; off = start % 32; if(off == 0) @@ -174,11 +208,25 @@ rbits(u32int *p, uint start, uint len) return v; } +static uvlong +rbytes(uchar *p, uint start, uint len) +{ + uvlong v = 0; + uint i; + + p += start; + for(i = 0; i < len; i++) + v |= (uvlong)p[i] << 8*i; + return v; +} + static void -identify(SDunit *unit, u32int *csd) +identify(SDunit *unit) { + Ctlr *ctlr = unit->dev->ctlr; uint csize, mult; +#define CSD(end, start) rbits(ctlr->csd, start, (end)-(start)+1) unit->secsize = 1 << CSD(83, 80); switch(CSD(127, 126)){ case 0: /* CSD version 1 */ @@ -190,6 +238,11 @@ identify(SDunit *unit, u32int *csd) 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; } if(unit->secsize == 1024){ unit->sectors <<= 1; @@ -223,8 +276,9 @@ mmcenable(SDev* dev) } static void -mmcswitchfunc(SDio *io, int arg) +switchfunc(Ctlr *ctlr, int arg) { + SDio *io = ctlr->io; u32int r[4]; uchar *buf; int n; @@ -232,12 +286,19 @@ mmcswitchfunc(SDio *io, int arg) n = Funcbytes; buf = sdmalloc(n); if(waserror()){ - print("%s: mmcswitchfunc error\n", io->name); sdfree(buf); nexterror(); } (*io->iosetup)(io, 0, buf, n, 1); - (*io->cmd)(io, SWITCH_FUNC, arg, r); + (*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); + } + } (*io->io)(io, 0, buf, n); sdfree(buf); poperror(); @@ -248,31 +309,59 @@ cardinit(Ctlr *ctlr) { SDio *io = ctlr->io; u32int r[4]; - int hcs, i; - - (*io->cmd)(io, GO_IDLE_STATE, 0, r); - hcs = 0; - if(!waserror()){ - (*io->cmd)(io, SD_SEND_IF_COND, Voltage|Checkpattern, r); - if(r[0] == (Voltage|Checkpattern)) /* SD 2.0 or above */ - hcs = Hcs; + int i, hcs; + + ctlr->buswidth = 1; + ctlr->busspeed = Initfreq; + if(io->bus != nil) + (*io->bus)(io, ctlr->buswidth, ctlr->busspeed); + + (*io->cmd)(io, &GO_IDLE_STATE, 0, r); + + /* card type unknown */ + ctlr->ismmc = -1; + + if(!waserror()){ /* try SD card first */ + hcs = 0; + 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 */ + 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); + if(r[0] & Powerup) + break; + } + ctlr->ismmc = 0; /* this is SD card */ poperror(); + if(i == Inittimeout) + return 2; + } else if(ctlr->ismmc) { /* try MMC if not ruled out */ + (*io->cmd)(io, &GO_IDLE_STATE, 0, r); + + for(i = 0; i < Inittimeout; i++){ + tsleep(&up->sleep, return0, nil, 100); + (*io->cmd)(io, &SEND_OP_COND, 0x80ff8080, r); + if(r[0] & Powerup) + break; + } + ctlr->ismmc = 1; /* this is MMC */ + if(i == Inittimeout) + return 2; } - 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); - if(r[0] & Powerup) - break; - } - if(i == Inittimeout) - return 2; + ctlr->ocr = r[0]; - (*io->cmd)(io, ALL_SEND_CID, 0, r); + (*io->cmd)(io, &ALL_SEND_CID, 0, r); memmove(ctlr->cid, r, sizeof ctlr->cid); - (*io->cmd)(io, SEND_RELATIVE_ADDR, 0, r); + (*io->cmd)(io, &SET_RELATIVE_ADDR, 0, r); ctlr->rca = r[0]>>16; - (*io->cmd)(io, SEND_CSD, ctlr->rca<cmd)(io, &SEND_CSD, ctlr->rca<csd, r, sizeof ctlr->csd); return 1; } @@ -309,7 +398,7 @@ mmconline(SDunit *unit) return 0; } if(unit->sectors != 0){ - (*io->cmd)(io, SEND_STATUS, ctlr->rca<cmd)(io, &SEND_STATUS, ctlr->rca<csd); - (*io->cmd)(io, SELECT_CARD, ctlr->rca<cmd)(io, SET_BLOCKLEN, unit->secsize, r); - (*io->cmd)(io, APP_CMD, ctlr->rca<cmd)(io, SET_BUS_WIDTH, Width4, r); - if(io->highspeed){ + (*io->cmd)(io, &SELECT_CARD, ctlr->rca<busspeed = SDfreq; + if(io->bus != nil){ + tsleep(&up->sleep, return0, nil, 10); + (*io->bus)(io, 0, ctlr->busspeed); + 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<cmd)(io, &SD_SET_BUS_WIDTH, Width4, r); + ctlr->buswidth = 4; + if(io->bus != nil) + (*io->bus)(io, ctlr->buswidth, 0); + if(!waserror()){ - mmcswitchfunc(io, Hispeed|Setfunc); + switchfunc(ctlr, Hispeed|Setfunc); poperror(); } } @@ -344,12 +447,16 @@ mmcrctl(SDunit *unit, char *p, int l) if(unit->sectors == 0) return 0; } - n = snprint(p, l, "rca %4.4ux ocr %8.8ux\ncid ", ctlr->rca, ctlr->ocr); + n = snprint(p, l, "rca %4.4ux ocr %8.8ux", ctlr->rca, ctlr->ocr); + + 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]); - n += snprint(p+n, l-n, " csd "); + + 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]); + n += snprint(p+n, l-n, "\ngeometry %llud %ld\n", unit->sectors, unit->secsize); return n; @@ -379,20 +486,20 @@ mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno) nexterror(); (*io->iosetup)(io, write, buf, len, nb); if(waserror()){ - (*io->cmd)(io, STOP_TRANSMISSION, 0, r); + (*io->cmd)(io, &STOP_TRANSMISSION, 0, r); nexterror(); } - (*io->cmd)(io, write? WRITE_MULTIPLE_BLOCK: READ_MULTIPLE_BLOCK, + (*io->cmd)(io, write? &WRITE_MULTIPLE_BLOCK: &READ_MULTIPLE_BLOCK, ctlr->ocr & Ccs? b: b * len, r); (*io->io)(io, write, buf, nb * len); poperror(); - (*io->cmd)(io, STOP_TRANSMISSION, 0, r); + (*io->cmd)(io, &STOP_TRANSMISSION, 0, r); poperror(); b += nb; }else{ for(b = bno; b < bno + nb; b++){ (*io->iosetup)(io, write, buf, len, 1); - (*io->cmd)(io, write? WRITE_BLOCK : READ_SINGLE_BLOCK, + (*io->cmd)(io, write? &WRITE_SINGLE_BLOCK: &READ_SINGLE_BLOCK, ctlr->ocr & Ccs? b: b * len, r); (*io->io)(io, write, buf, len); buf += len; -- cgit v1.2.3