ref: 14d663b1695df1144ee19b6481f9c68bb8be21b2
parent: ae116c94460e3b3e0eab82f06bf1c92c425ee1ac
author: cinap_lenrek <[email protected]>
date: Sat Jan 26 12:06:28 EST 2013
kernel: add portable sd mmc interface (from sources)
--- a/sys/src/9/port/sd.h
+++ b/sys/src/9/port/sd.h
@@ -4,6 +4,7 @@
typedef struct SDev SDev;
typedef struct SDfile SDfile;
typedef struct SDifc SDifc;
+typedef struct SDio SDio;
typedef struct SDpart SDpart;
typedef struct SDperm SDperm;
typedef struct SDreq SDreq;
@@ -143,8 +144,32 @@
SDcdb = 2,
};
+/*
+ * Allow the default #defines for sdmalloc & sdfree to be overridden by
+ * system-specific versions. This can be used to avoid extra copying
+ * by making sure sd buffers are cache-aligned (some ARM systems) or
+ * page-aligned (xen) for DMA.
+ */
+#ifndef sdmalloc
#define sdmalloc(n) malloc(n)
#define sdfree(p) free(p)
+#endif
+
+/*
+ * mmc/sd/sdio host controller interface
+ */
+
+struct SDio {
+ char *name;
+ int (*init)(void);
+ void (*enable)(void);
+ int (*inquiry)(char*, int);
+ int (*cmd)(u32int, u32int, u32int*);
+ void (*iosetup)(int, void*, int, int);
+ void (*io)(int, uchar*, int);
+};
+
+extern SDio sdio;
/* devsd.c */
extern void sdadddevs(SDev*);
--- /dev/null
+++ b/sys/src/9/port/sdmmc.c
@@ -1,0 +1,314 @@
+/*
+ * mmc / sd memory card
+ *
+ * Copyright © 2012 Richard Miller <[email protected]>
+ *
+ * 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] = 0x00; /* direct access (disk) */
+ unit->inquiry[1] = 0x80; /* removable */
+ 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,
+};