Plan 9 from Bell Labs’s /usr/web/sources/patch/applied/sdmmc-driver/sdmmc.c.new

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#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"

/*
 * mmc / sd memory card
 *
 *
 * Copyright © 2012 Richard Miller <[email protected]>
 *
 * Assumes only one card on the bus
 */

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

typedef struct Ctlr Ctlr;

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

#define CSD(end,start)	rbits(csd, start, end-start+1)

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)
{
	Ctlr *ctl;
	int n;

	ctl = unit->dev->ctlr;
	n = ctl->io->inquiry((char*)&unit->inquiry[8], sizeof(unit->inquiry)-8);
	if(n < 0)
		return 0;
	unit->inquiry[0] = SDperdisk;
	unit->inquiry[1] = SDinq1removable;
	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)
{
	Ctlr *ctl;
	SDio *io;
	u32int r[4];
	int hcs, i;

	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)
{
	Ctlr *ctl;
	SDio *io;
	uchar *buf;
	u32int r[4];
	ulong b;
	int len, tries;

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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].