shithub: riscv

Download patch

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,
+};