ref: aa671181e86bc0560dbabf347a6480d39881f52b
parent: deaa290f857425c0dacb0ac65312925490da8bbe
author: cinap_lenrek <[email protected]>
date: Sat Jul 13 21:33:07 EDT 2013
add ricoh pci sdmmc host controller driver for X230 the driver should work for standard sdhc (see http://www.sdcard.org/) controllers, but matches for the ricoh controller only as it was the only one i have for testing.
--- a/sys/src/9/pc/pccpuf
+++ b/sys/src/9/pc/pccpuf
@@ -97,6 +97,7 @@
sdiahci pci sdscsi led
sdodin pci sdscsi led
sdvirtio pci sdscsi
+ sdmmc pci pmmc
sdloop
vga3dfx +cur
--- a/sys/src/9/pc/pcf
+++ b/sys/src/9/pc/pcf
@@ -95,6 +95,7 @@
sdiahci pci sdscsi led
sdodin pci sdscsi led
sdvirtio pci sdscsi
+ sdmmc pci pmmc
sdloop
uarti8250
--- /dev/null
+++ b/sys/src/9/pc/pmmc.c
@@ -1,0 +1,549 @@
+/*
+ * pci mmc controller.
+ *
+ * initially written for X230 Ricoh MMC controller.
+ * cmdinfo[] table stolen from bcm/emmc.c, thanks richard.
+ *
+ * for sdhc documentation see: https://www.sdcard.org/
+ */
+#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"
+
+/* registers */
+enum {
+ Rsdma = 0x00,
+ Rbsize = 0x04,
+ Rbcount = 0x06,
+ Rarg = 0x08,
+ Rmode = 0x0C,
+ Rcmd = 0x0E,
+ Rresp0 = 0x10,
+ Rresp1 = 0x14,
+ Rresp2 = 0x18,
+ Rresp3 = 0x1C,
+ Rdat0 = 0x20,
+ Rdat1 = 0x22,
+ Rpres = 0x24,
+ Rhc = 0x28,
+ Rpwr = 0x29,
+ Rbgc = 0x2A,
+ Rwkc = 0x2B,
+ Rclc = 0x2C,
+ Rtmc = 0x2E,
+ Rsrst = 0x2F,
+ Rnis = 0x30,
+ Reis = 0x32,
+ Rnie = 0x34,
+ Reie = 0x36,
+ Rnise = 0x38,
+ Reise = 0x3A,
+ Ra12 = 0x3C,
+ Rcap = 0x40,
+ Rrcap = 0x44,
+ Rxcap = 0x48,
+ Rrxcap = 0x4C,
+ Rfea12 = 0x50,
+ Rfeei = 0x52,
+ Radmasr = 0x54,
+ Radmaba = 0x58,
+ Rsists = 0xFC,
+ Rhcver = 0xFE,
+};
+
+/* sts bits */
+enum {
+ Seint = 1<<15,
+ Snint = 1<<8,
+ Srem = 1<<7,
+ Sins = 1<<6,
+ Srrdy = 1<<5,
+ Swrdy = 1<<4,
+ Sdint = 1<<3,
+ Sbge = 1<<2,
+ Strac = 1<<1,
+ Scmdc = 1<<0,
+
+ Smask = 0x81ff,
+};
+
+/* err bits */
+enum {
+ Ea12 = 1<<8,
+ Elimit = 1<<7,
+ Edebit = 1<<6,
+ Edcrc = 1<<5,
+ Edtmo = 1<<4,
+ Ecidx = 1<<3,
+ Ecebit = 1<<2,
+ Eccrc = 1<<1,
+ Ectmo = 1<<0,
+
+ Emask = 0x1ff,
+};
+
+/* present bits */
+enum {
+ Plsig = 1<<24,
+ Pldat3 = 1<<23,
+ Pldat2 = 1<<22,
+ Pldat1 = 1<<21,
+ Pldat0 = 1<<20,
+
+ Ppswit = 1<<19,
+ Pcrddt = 1<<18,
+ Pcrdst = 1<<17,
+ Pcrdin = 1<<16,
+
+ Pbufrd = 1<<11,
+ Pbufwr = 1<<10,
+ Ptrard = 1<<9,
+ Ptrawr = 1<<8,
+ Pdat = 1<<2,
+
+ Pinhbc = 1<<1,
+ Pinhbd = 1<<0,
+};
+
+/* Rmode bits */
+enum {
+ Mblk = 1<<5,
+ Mrd = 1<<4,
+ Mwr = 0<<4,
+ Ma12 = 1<<2,
+ Mcnt = 1<<1,
+ Mdma = 1<<0,
+};
+
+/* command bits */
+enum {
+ Abort = 3<<6,
+ Isdata = 1<<5,
+ Ixchken = 1<<4,
+ Crcchken = 1<<3,
+
+ Respmask = 3,
+ Respnone = 0,
+ Resp48busy = 3,
+ Resp48 = 2,
+ Resp136 = 1,
+};
+
+/* fake for cmdinfo */
+enum {
+ Blkcnten = Mblk << 8,
+ Multiblock = Mcnt << 8,
+ Card2host = Mrd << 8,
+ Host2card = Mwr << 8,
+};
+
+int cmdinfo[64] = {
+[0] Ixchken,
+[2] Resp136,
+[3] Resp48 | Ixchken | Crcchken,
+[6] Resp48 | Ixchken | Crcchken,
+[7] Resp48busy | Ixchken | Crcchken,
+[8] Resp48 | Ixchken | Crcchken,
+[9] Resp136,
+[12] Resp48busy | Ixchken | Crcchken,
+[13] Resp48 | Ixchken | Crcchken,
+[16] Resp48,
+[17] Resp48 | Isdata | Card2host | Ixchken | Crcchken,
+[18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken,
+[24] Resp48 | Isdata | Host2card | Ixchken | Crcchken,
+[25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken,
+[41] Resp48,
+[55] Resp48 | Ixchken | Crcchken,
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ Lock;
+
+ Pcidev *pdev;
+ u8int *mmio;
+
+ int intfreq; /* khz */
+ int change;
+
+ u32int waitsts;
+ u32int waitmsk;
+ Rendez r;
+
+ struct {
+ int bcount;
+ int bsize;
+ } io;
+};
+
+static Ctlr pmmc[1];
+
+#define CR8(c, off) *((u8int*)(c->mmio + off))
+#define CR16(c, off) *((u16int*)(c->mmio + off))
+#define CR32(c, off) *((u32int*)(c->mmio + off))
+
+static void
+mmcinterrupt(Ureg*, void *arg)
+{
+ u16int nis, eis;
+ Ctlr *c;
+
+ c = arg;
+ nis = CR16(c, Rnis);
+ if((nis & Smask) == 0)
+ return; /* not for us */
+
+ CR16(c, Rnis) = nis; /* ack */
+ ilock(c);
+ eis = 0;
+ if((nis & Seint) != 0){
+ eis = CR16(c, Reis);
+ CR16(c, Reis) = eis; /* ack */
+ }
+ if((nis & Snint) != 0)
+ CR16(c, Rnie) |= Snint; /* ack */
+ if((nis & (Srem|Sins)) != 0)
+ c->change = 1;
+ c->waitsts |= nis | (eis << 16);
+ if((c->waitsts & c->waitmsk) != 0)
+ wakeup(&c->r);
+ iunlock(c);
+}
+
+static int
+pmmcinit(void)
+{
+ Pcidev *p;
+
+ p = nil;
+ while((p = pcimatch(p, 0, 0)) != nil){
+ if(p->vid == 0x1180 && p->did == 0xe823) /* Ricoh */
+ break;
+ }
+
+ if(p == nil || p->mem[0].size < 256)
+ return -1;
+
+ if(p->did == 0x1180 && p->vid == 0xe823){ /* Ricoh */
+ /* Enable SD2.0 mode. */
+ pcicfgw8(p, 0xf9, 0xfc);
+ pcicfgw8(p, 0x150, 0x10);
+ pcicfgw8(p, 0xf9, 0x00);
+
+ /*
+ * Some SD/MMC cards don't work with the default base
+ * clock frequency of 200MHz. Lower it to 50Hz.
+ */
+ pcicfgw8(p, 0xfc, 0x01);
+ pcicfgw8(p, 0xe1, 50);
+ pcicfgw8(p, 0xfc, 0x00);
+ }
+
+ pmmc->mmio = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
+ if(pmmc->mmio == nil)
+ return -1;
+ pmmc->pdev = p;
+ return 0;
+}
+
+static int
+pmmcinquiry(char *inquiry, int inqlen)
+{
+ return snprint(inquiry, inqlen, "MMC Host Controller");
+}
+
+static void
+softreset(Ctlr *c, int all)
+{
+ int i, m;
+
+ m = all ? 1 : 6;
+ CR8(c, Rsrst) = m;
+ for(i=10; i>=0; i--){
+ if((CR8(c, Rsrst) & m) == 0)
+ break;
+ delay(10);
+ CR8(c, Rsrst) = 0;
+ }
+ if(i < 0) iprint("mmc: didnt reset\n");
+}
+
+static void
+setpower(Ctlr *c, int on)
+{
+ enum {
+ Vcap18 = 1<<26, Vset18 = 0x05,
+ Vcap30 = 1<<25, Vset30 = 0x06,
+ Vcap33 = 1<<24, Vset33 = 0x07,
+ };
+ u32int cap, v;
+
+ cap = CR32(c, Rcap);
+ v = Vset18;
+ if(cap & Vcap30)
+ v = Vset30;
+ if(cap & Vcap33)
+ v = Vset33;
+ CR8(c, Rpwr) = on ? ((v<<1) | 1) : 0;
+}
+
+static void
+setclkfreq(Ctlr *c, int khz)
+{
+ u32int caps, intfreq;
+ int i, div;
+
+ if(khz == 0){
+ CR16(c, Rclc) |= ~4; /* sd clock disable */
+ return;
+ }
+
+ caps = CR32(c, Rcap);
+ intfreq = 1000*((caps >> 8) & 0x3f);
+ for(div = 1; div <= 256; div <<= 1){
+ if((intfreq / div) <= khz){
+ div >>= 1;
+ break;
+ }
+ }
+ CR16(c, Rclc) = 0;
+ CR16(c, Rclc) = div<<8;
+ CR16(c, Rclc) |= 1; /* int clock enable */
+ for(i=1000; i>=0; i--){
+ if(CR16(c, Rclc) & 2) /* int clock stable */
+ break;
+ delay(10);
+ }
+ if(i < 0) iprint("mmc: clock didnt stabilize\n");
+ CR16(c, Rclc) |= 4; /* sd clock enable */
+}
+
+static void
+resetctlr(Ctlr *c)
+{
+ u32int m;
+
+ ilock(c);
+ CR16(c, Rnise) = 0; /* interrupts off */
+
+ c->change = 0;
+ c->waitmsk = c->waitsts = 0;
+ softreset(c, 1);
+
+ /* set timeout */
+ CR8(c, Rtmc) = 0x0e;
+
+ m = Srem | Sins | Srrdy | Swrdy | Sdint | Sbge | Strac | Scmdc;
+ CR16(c, Rnie) = m;
+ CR16(c, Reie) = Emask;
+ CR16(c, Rnise) = m;
+ CR16(c, Reise) = Emask;
+
+ setpower(c, 1);
+ setclkfreq(c, 400);
+ iunlock(c);
+}
+
+static int
+waitcond(void *arg)
+{
+ Ctlr *c;
+
+ c = arg;
+ return (c->waitsts & c->waitmsk) != 0;
+}
+
+static u32int
+intrwait(Ctlr *c, u32int mask, int tmo)
+{
+ u32int status;
+
+ c->waitmsk = Seint | mask;
+ do {
+ if(!waserror()){
+ tsleep(&c->r, waitcond, c, 100);
+ poperror();
+ }
+ mmcinterrupt(nil, c);
+ if(waitcond(c))
+ break;
+ tmo -= 100;
+ } while(tmo > 0);
+
+ ilock(c);
+ status = c->waitsts;
+ c->waitsts &= ~(status & mask);
+ c->waitmsk = 0;
+ if((status & mask) == 0 || (status & Seint) != 0){
+ /* abort command on timeout/error interrupt */
+ softreset(c, 0);
+ c->waitsts = 0;
+ status = 0;
+ }
+ iunlock(c);
+
+ return status;
+}
+
+
+static void
+pmmcenable(void)
+{
+ Pcidev *p;
+ Ctlr *c;
+
+ c = pmmc;
+ p = c->pdev;
+ resetctlr(c);
+ intrenable(p->intl, mmcinterrupt, c, p->tbdf, "mmc");
+}
+
+static int
+pmmccmd(u32int cmd, u32int arg, u32int *resp)
+{
+ u32int status;
+ int i, mode;
+ Ctlr *c;
+
+ if(cmd >= nelem(cmdinfo) || cmdinfo[cmd] == 0)
+ error(Egreg);
+ mode = cmdinfo[cmd] >> 8;
+ cmd = (cmd << 8) | (cmdinfo[cmd] & 0xFF);
+
+ c = pmmc;
+
+ if(c->change)
+ resetctlr(c);
+ if((CR32(c, Rpres) & Pcrdin) == 0)
+ error("no card");
+
+ status = Pinhbc;
+ if((cmd & Isdata) != 0 || (cmd & Respmask) == Resp48busy)
+ status |= Pinhbd;
+ for(i=1000; (CR32(c, Rpres) & status) != 0 && i>=0; i--)
+ delay(1);
+ if(i < 0)
+ error(Eio);
+
+ ilock(c);
+ if((mode & (Mcnt|Mblk)) == (Mcnt|Mblk)){
+ CR16(c, Rbsize) = c->io.bsize;
+ CR16(c, Rbcount) = c->io.bcount;
+ }
+ CR32(c, Rarg) = arg;
+ CR16(c, Rmode) = mode;
+ CR16(c, Rcmd) = cmd;
+ iunlock(c);
+
+ if(!intrwait(c, Scmdc, 1000))
+ error(Eio);
+
+ switch(cmd & Respmask){
+ case Resp48busy:
+ resp[0] = CR32(c, Rresp0);
+ if(!intrwait(c, Strac, 3000))
+ error(Eio);
+ break;
+ case Resp48:
+ resp[0] = CR32(c, Rresp0);
+ break;
+ case Resp136:
+ resp[0] = CR32(c, Rresp0)<<8;
+ resp[1] = CR32(c, Rresp0)>>24 | CR32(c, Rresp1)<<8;
+ resp[2] = CR32(c, Rresp1)>>24 | CR32(c, Rresp2)<<8;
+ resp[3] = CR32(c, Rresp2)>>24 | CR32(c, Rresp3)<<8;
+ break;
+ case Respnone:
+ resp[0] = 0;
+ break;
+ }
+
+ cmd >>= 8;
+ if(cmd == 0x06){ /* buswidth */
+ switch(arg){
+ case 0:
+ CR8(c, Rhc) &= ~2;
+ break;
+ case 2:
+ CR8(c, Rhc) |= 2;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void
+pmmciosetup(int write, void *buf, int bsize, int bcount)
+{
+ Ctlr *c;
+
+ USED(write);
+ USED(buf);
+
+ if(bsize == 0 || (bsize & 3) != 0)
+ error(Egreg);
+
+ c = pmmc;
+ c->io.bsize = bsize;
+ c->io.bcount = bcount;
+}
+
+static void
+readblock(Ctlr *c, uchar *buf, int len)
+{
+ for(len >>= 2; len > 0; len--){
+ *((u32int*)buf) = CR32(c, Rdat0);
+ buf += 4;
+ }
+}
+
+static void
+writeblock(Ctlr *c, uchar *buf, int len)
+{
+ for(len >>= 2; len > 0; len--){
+ CR32(c, Rdat0) = *((u32int*)buf);
+ buf += 4;
+ }
+}
+
+static void
+pmmcio(int write, uchar *buf, int len)
+{
+ Ctlr *c;
+ int n;
+
+ c = pmmc;
+ if(len != c->io.bsize*c->io.bcount)
+ error(Egreg);
+ while(len > 0){
+ if(!intrwait(c, write ? Swrdy : Srrdy, 3000))
+ error(Eio);
+ n = len;
+ if(n > c->io.bsize)
+ n = c->io.bsize;
+ if(write)
+ writeblock(c, buf, n);
+ else
+ readblock(c, buf, n);
+ len -= n;
+ buf += n;
+ }
+ if(!intrwait(c, Strac, 1000))
+ error(Eio);
+}
+
+SDio sdio = {
+ "pmmc",
+ pmmcinit,
+ pmmcenable,
+ pmmcinquiry,
+ pmmccmd,
+ pmmciosetup,
+ pmmcio,
+};
--- a/sys/src/9/port/sdmmc.c
+++ b/sys/src/9/port/sdmmc.c
@@ -107,7 +107,7 @@
break;
case 1: /* CSD version 2 */
csize = CSD(69, 48);
- unit->sectors = (csize+1) * 512LL*KiB / unit->secsize;
+ unit->sectors = (csize+1) * 0x80000LL / unit->secsize;
break;
}
if(unit->secsize == 1024){
@@ -263,7 +263,7 @@
io = ctl->io;
assert(unit->subno == 0);
if(unit->sectors == 0)
- error("media change");
+ error(Echange);
buf = data;
len = unit->secsize;
if(Multiblock){