[U-Boot] [PATCH v2] mmc: new legacy MMC/SPI driver
Mike Frysinger
vapier at gentoo.org
Thu Oct 15 10:01:47 CEST 2009
From: Hans Eklund <hans at rubico.se>
Needs converting to generic MMC framework.
Signed-off-by: Hans Eklund <hans at rubico.se>
Signed-off-by: Cliff Cai <cliff.cai at analog.com>
Signed-off-by: Mike Frysinger <vapier at gentoo.org>
---
v2
- Rubico OK-ed the licensing changes
i dont know if there's a requirement for new mmc drivers to be ported
to the generic framework before merging ? looks like only 2 drivers
have been ported so far ...
drivers/mmc/Makefile | 3 +
drivers/mmc/spi_mmc.c | 1091 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1094 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/spi_mmc.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..1b8f5bd 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -23,6 +23,9 @@
include $(TOPDIR)/config.mk
+# stick it up here to avoid conflicts
+COBJS-$(CONFIG_SPI_MMC) += spi_mmc.o
+
LIB := $(obj)libmmc.a
COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
diff --git a/drivers/mmc/spi_mmc.c b/drivers/mmc/spi_mmc.c
new file mode 100644
index 0000000..b25d3dc
--- /dev/null
+++ b/drivers/mmc/spi_mmc.c
@@ -0,0 +1,1091 @@
+/*
+ * SPI-MMC/SD Protocol.
+ *
+ * Copyright (C) 2005-2007, Rubico AB (www.rubico.se)
+ *
+ * Developed as a part the CDT project C4 (www.cdt.ltu.se).
+ *
+ * Robert Selberg, <robert at rubico.se>
+ * Hans Eklund, <hans at rubico.se>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+/*
+ * TODO: Correct Multiple block read and write functions. Didnt have time
+ * to make them all failsafe. Will be done soon.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+#include <mmc.h>
+
+enum {
+ MMC_INIT_TIMEOUT = 30000,
+ MMC_COMMAND_TIMEOUT = 5000,
+ MMC_PROG_TIMEOUT = 500000,
+ BUSY_BLOCK_LEN = 1,
+ BUSY_BLOCK_LEN_SHORT = 16,
+ MMC_SECTOR_SIZE = 512,
+ SD_PRE_CMD_ZEROS = 4,
+ SD_CLK_CNTRL = 2,
+ LOG_LEN = 16,
+ WRB_LEN = 256,
+
+/* Card command classes */
+
+/* Internal error codes */
+ ERR_SPI_TIMEOUT = 0xF1,
+ ERR_MMC_TIMEOUT = 0xF2,
+ ERR_MMC_PROG_TIMEOUT = 0xF3,
+ ERR_UNKNOWN_TOK = 0xF4,
+
+/* return values from functions */
+ RVAL_OK = 0,
+ RVAL_ERROR = 1,
+ RVAL_CRITICAL = 2,
+
+/* Format R1(b) response tokens (1 byte long) */
+ BUSY_TOKEN = 0x00,
+ R1_OK = 0x00,
+ R1_IDLE_STATE = 0x01,
+ R1_ERASE_STATE = 0x02,
+ R1_ILLEGAL_CMD = 0x04,
+ R1_COM_CRC_ERROR = 0x08,
+ R1_ERASE_SEQ_ERROR = 0x10,
+ R1_ADDRESS_ERROR = 0x20,
+ R1_PARAMETER_ERROR = 0x40,
+
+/* Format R2 response tokens (2 bytes long, first is same as R1 responses) */
+ R2_OK = 0x00,
+ R2_CARD_LOCKED = 0x01,
+ R2_WP_ERASE_SKIP = 0x02,
+ R2_LOCK_UNLOCK_CMD_FAIL = 0x02,
+ R2_ERROR = 0x04,
+ R2_CC_ERROR = 0x08,
+ R2_CARD_ECC_FAILED = 0x10,
+ R2_WP_VIOLATION = 0x20,
+ R2_ERASE_PARAM = 0x40,
+ R2_OUT_OF_RANGE = 0x80,
+ R2_CSD_OVERWRITE = 0x80,
+/* TODO: Format R3 response tokens */
+
+/* Data response tokens */
+ DR_MASK = 0x0F,
+ DR_ACCEPTED = 0x05,
+ DR_CRC_ERROR = 0x0B,
+ DR_WRITE_ERROR = 0x0D,
+
+/*
+ Data tokens (4 bytes to (N+3) bytes long), N is data block len
+ format of the Start Data Block Token
+*/
+ SBT_S_BLOCK_READ = 0xFE,
+ SBT_M_BLOCK_READ = 0xFE,
+ SBT_S_BLOCK_WRITE = 0xFE,
+ SBT_M_BLOCK_WRITE = 0xFC,
+ STT_M_BLOCK_WRITE = 0xFD,
+
+/* Data error tokens (1 byte long) */
+ DE_ERROR = 0x01,
+ DE_CC_ERROR = 0x02,
+ DE_CARD_ECC_FAILED = 0x04,
+ DE_OUT_OF_RANGE = 0x08,
+ DE_CARD_IS_LOCKED = 0x10,
+
+/* MMC/SD SPI mode commands */
+ GO_IDLE_STATE = 0,
+ SEND_OP_COND = 1,
+ SEND_CSD = 9,
+ SEND_CID = 10,
+ STOP_TRANSMISSION = 12,
+ SEND_STATUS = 13,
+ SET_BLOCKLEN = 16,
+ READ_SINGLE_BLOCK = 17,
+ READ_MULTIPLE_BLOCK = 18,
+ WRITE_BLOCK = 24,
+ WRITE_MULTIPLE_BLOCK = 25,
+ SD_SEND_OP_COND = 41,
+ APP_CMD = 55,
+};
+
+/* minimal local versions of CSD/CID structures,
+ somewhat ripped from linux MMC layer, the entire
+ CSD struct is larger and is not completley parsed
+*/
+struct cid_str {
+ unsigned int manfid;
+ char prod_name[8];
+ unsigned int serial;
+ unsigned short oemid;
+ unsigned short year;
+ unsigned char hwrev;
+ unsigned char fwrev;
+ unsigned char month;
+};
+
+struct csd_str { /* __csd field name__*/
+ unsigned char mmca_vsn; /* CSD_STRUCTURE */
+ unsigned short cmdclass; /* CCC */
+ unsigned short tacc_clks; /* TAAC */
+ unsigned int tacc_ns; /* NSAC */
+ unsigned int max_dtr; /* TRANS_SPEED */
+ unsigned int read_blkbits; /* READ_BL_LEN */
+ unsigned int capacity;
+};
+
+/*
+ mmc_spi_dev - Implementation need to configure this struct
+ with callback functions to read and write data that the
+ mmc_spi function can use for its operations.
+ NOTE: Every function defined here expect exclusive access to
+ any MMC/SD card it is operating on. Functions should be considered
+ critical sections. Also note that the read/write callbacks may a mutex
+ if they can be executed by another context.
+*/
+struct mmc_spi_dev {
+ int (*read)(unsigned char *buf, unsigned int nbytes, void *priv_data);
+ int (*write)(unsigned char *buf, unsigned int nbytes, void *priv_data);
+ void (*doassert)(void);
+ void (*deassert)(void);
+ void *priv_data; /* incomming pointer to private data for callbacks */
+ unsigned char raw_csd[18]; /* raw csd data to use with external parser */
+ unsigned char raw_cid[18]; /* raw cid data to use with external parser */
+ struct cid_str cid; /* internal represent. of cid data */
+ struct csd_str csd; /* internal represent. of csd data */
+ int sd; /* true if SD card found */
+ int log_len;
+ unsigned int est_write_lat; /* [bytes] */
+ unsigned short force_cs_high; /* true if write/read callbacks should ask for CS high */
+ unsigned int errors; /* total amount of errors recorded since card insertion */
+ unsigned char cmd_log[LOG_LEN];
+ unsigned short error_log[LOG_LEN];
+ unsigned short status_log[LOG_LEN]; /* Status is not checked anymore since some cards will cry */
+};
+
+
+static unsigned char mmc_cmd[6] = {0x40, 0x00, 0x00, 0x00, 0x00, 0x95};
+static unsigned char Null_Word[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF};
+static unsigned char latest_cmd;
+
+static int init_mode = 1;
+
+
+#if 0
+/**********************************************************************\
+*
+* MMC CSD/CID related, could be somewhat trimmed and cleaned
+*
+\**********************************************************************/
+static unsigned char getbit(void *ptr, unsigned int n)
+{
+ unsigned int byte_nr;
+ unsigned int bit_nr;
+
+ byte_nr = n/8;
+ bit_nr = n % 8;
+
+ return (unsigned char)(((unsigned char *)ptr)[byte_nr] >> bit_nr) & 1;
+}
+
+static unsigned int getvalue(void *ptr, unsigned int n, unsigned int len)
+{
+ unsigned int value = 0;
+ int i = 0;
+
+ for (i = 0; i < len; i++)
+ value += ((unsigned int)getbit(ptr, n+i)) << i;
+ return value;
+}
+#endif
+
+static unsigned char mmc_wait_response(struct mmc_spi_dev *pdev, unsigned int timeout)
+{
+ unsigned char card_resp = 0xFF;
+ unsigned int n = 0;
+ /* reset time and set to timeout ms */
+ while (1) {
+
+ if (pdev->read(&card_resp, 1, pdev->priv_data) < 0) {
+ debug("error: mmc_wait_response read error\n");
+ return ERR_SPI_TIMEOUT;
+ }
+ if (card_resp != 0xFF)
+ return card_resp;
+ /*
+ NOTE: "timeout" in seconds may not be a good idea after all
+ (by doing pdev->elapsed_time() )
+ wait for a specific amount of polls for now.
+ */
+ if ((n++ >= timeout)) {
+ debug("hey! timed out after %d since %d bytes was maximum(latest_cmd=%d)\n", n,
+ timeout, latest_cmd);
+ return ERR_MMC_TIMEOUT;
+ }
+ }
+}
+
+static short mmc_spi_read_status(struct mmc_spi_dev *pdev)
+{
+ unsigned char b1 = 0;
+ unsigned char b2 = 0;
+ unsigned short r2 = 0xffff;
+ static unsigned char status_cmd[6] = {0x4D, 0x00, 0x00, 0x00, 0x00, 0x95};
+
+ pdev->doassert();
+
+ if (pdev->sd) {
+ if (pdev->write(Null_Word, SD_PRE_CMD_ZEROS, pdev->priv_data) < 0) {
+ debug("sending SD_PRE_CMD_ZEROS failed\n");
+ pdev->deassert();
+ return ERR_SPI_TIMEOUT;
+ }
+ }
+ if (pdev->write(status_cmd, 6, pdev->priv_data) < 0) {
+ debug("sending of SEND_STATUS command failed\n");
+ pdev->deassert();
+ return ERR_SPI_TIMEOUT;
+ }
+ b1 = mmc_wait_response(pdev, MMC_COMMAND_TIMEOUT);
+ b2 = mmc_wait_response(pdev, MMC_COMMAND_TIMEOUT);
+
+ if (b1 == ERR_MMC_TIMEOUT || b2 == ERR_MMC_TIMEOUT) {
+ debug("No status received !\n");
+ pdev->deassert();
+ return ERR_MMC_TIMEOUT;
+ }
+
+ r2 = b2 + (b1 << 8);
+
+ if (r2)
+ debug("STATUS r2: 0x%04x\n", r2);
+ pdev->deassert();
+
+ return r2;
+
+ /* TODO: Implement in a finer way */
+ switch (b1) {
+ case R1_OK:
+ break;
+ case R1_IDLE_STATE:
+ printf("R1_IDLE_STATE\n");
+ break;
+ case R1_ERASE_STATE:
+ printf("R1_ERASE_STATE\n");
+ break;
+ case R1_ILLEGAL_CMD:
+ printf("R1_ILLEGAL_COMMAND\n");
+ break;
+ case R1_COM_CRC_ERROR:
+ printf("R1_COM_CRC_ERROR\n");
+ break;
+ case R1_ERASE_SEQ_ERROR:
+ printf("R1_ERASE_SEQ_ERROR\n");
+ break;
+ case R1_ADDRESS_ERROR:
+ printf("R1_ADDRESS_ERROR\n");
+ break;
+ case R1_PARAMETER_ERROR:
+ printf("R1_PARAMETER_ERROR\n");
+ break;
+ case 0xFF:
+ printf("b1: STATUS RESPONSE TIMEOUT\n");
+ break;
+ default:
+ printf("b1: INVALID STATUS RESPONSE(0x%02x)\n", b1);
+ break;
+ }
+
+ switch (b2) {
+ case R2_OK:
+ break;
+ case R2_CARD_LOCKED:
+ printf("R2_CARD_LOCKED\n");
+ break;
+ case R2_WP_ERASE_SKIP:
+ printf("R2_WP_ERASE_SKIP/Unlock command failed\n");
+ break;
+ case R2_ERROR:
+ printf("R2_ERROR\n");
+ break;
+ case R2_CC_ERROR:
+ printf("R2_CC_ERROR\n");
+ break;
+ case R2_CARD_ECC_FAILED:
+ printf("R2_CARD_ECC_FAILED\n");
+ break;
+ case R2_WP_VIOLATION:
+ printf("R2_WP_VIOLATION\n");
+ break;
+ case R2_ERASE_PARAM:
+ printf("R2_ERASE_PARAM\n");
+ break;
+ case R2_OUT_OF_RANGE:
+ printf("R2_OUT_OF_RANGE, CSD_Overwrite\n");
+ break;
+ case 0xFF:
+ printf("b2: STATUS RESPONSE TIMEOUT\n");
+ break;
+ default:
+ printf("b2: INVALID STATUS RESPONSE(0x%02x)\n", b2);
+ break;
+ }
+
+ return r2;
+}
+
+#if 0
+static void mmc_spi_fill_card_struct(struct mmc_spi_dev *pdev)
+{
+ unsigned short c_size_mult = 0;
+ unsigned short c_size = 0;
+ unsigned char *raw_csd;
+ unsigned char *raw_cid;
+
+ /* local, shorter names, just to keep lines below shorter */
+ raw_csd = pdev->raw_csd;
+ raw_cid = pdev->raw_cid;
+ pdev->csd.mmca_vsn = (raw_csd[0] & 0x3c) >> 2;
+ pdev->csd.cmdclass = (((__u16)raw_csd[4]) << 4) | ((raw_csd[5] & 0xf0) >> 4);
+ pdev->csd.tacc_clks = raw_csd[1];
+ pdev->csd.tacc_ns = raw_csd[2];
+ pdev->csd.max_dtr = raw_csd[3];
+ pdev->csd.read_blkbits = raw_csd[5] & 0x0f;
+ /* for calculating capacity(in blocks) */
+ c_size = ((((__u16)raw_csd[6]) & 0x03) << 10) | (((__u16)raw_csd[7]) << 2) |
+ (((__u16)raw_csd[8]) & 0xc0) >> 6;
+ c_size_mult = ((raw_csd[9] & 0x03) << 1) | ((raw_csd[10] & 0x80) >> 7);
+ pdev->csd.capacity = (c_size+1) * (1 << (c_size_mult + 2));
+ pdev->cid.manfid = getvalue(raw_cid, 127-127, 8);
+ memcpy(pdev->cid.prod_name, raw_cid+3, 7);
+ pdev->cid.serial = getvalue(raw_cid, 127-47, 32);
+ pdev->cid.oemid = getvalue(raw_cid, 127-119, 16);
+ pdev->cid.year = 1997 + (getvalue(raw_cid, 127-15, 8) & 0x0F);
+ pdev->cid.hwrev = (getvalue(raw_cid, 127-55, 8) & 0xF0) >> 4;
+ pdev->cid.fwrev = getvalue(raw_cid, 127-55, 8) & 0x0F;
+ pdev->cid.month = (getvalue(raw_cid, 127-15, 8) & 0xF0) >> 4;
+}
+#endif
+
+static short mmc_spi_dummy_clocks(struct mmc_spi_dev *pdev, unsigned short nbytes)
+{
+ int i;
+
+ pdev->force_cs_high = 1;
+ for (i = 0; i < nbytes; i++) {
+ if (pdev->write(Null_Word, 1, pdev->priv_data) < 0) {
+ pdev->force_cs_high = 0;
+ return 1;
+ }
+ }
+ pdev->force_cs_high = 0;
+
+ return 0;
+}
+
+static short send_cmd_and_wait(struct mmc_spi_dev *pdev,
+ unsigned char command,
+ unsigned int argument,
+ unsigned short cmd_resp,
+ unsigned int timeout)
+{
+ unsigned short resp = 0xff;
+ unsigned short rval = 0;
+ /* Build command string */
+ mmc_cmd[0] = 0x40 + command;
+ mmc_cmd[1] = (unsigned char)(argument >> 24 & 0xff);
+ mmc_cmd[2] = (unsigned char)(argument >> 16 & 0xff);
+ mmc_cmd[3] = (unsigned char)(argument >> 8 & 0xff);
+ mmc_cmd[4] = (unsigned char)(argument & 0xff);
+ mmc_cmd[5] = 0x95;
+
+ /* record last command if not in init mode */
+ if (!init_mode)
+ latest_cmd = command;
+ if (init_mode || pdev->sd) {
+ /* Send a few zeros since SDs may be sleeping */
+ if (pdev->write(Null_Word, SD_PRE_CMD_ZEROS, pdev->priv_data) < 0) {
+ debug("sending SD_PRE_CMD_ZEROS failed\n");
+ rval = ERR_SPI_TIMEOUT;
+ goto out;
+ }
+ }
+
+ if (pdev->write(mmc_cmd, 6, pdev->priv_data) < 0) {
+ debug("sending command %d failed\n", command);
+ rval = ERR_SPI_TIMEOUT;
+ goto out;
+ }
+ resp = mmc_wait_response(pdev, timeout);
+ if (resp != cmd_resp) {
+ /* NOTE: ignore "illegal command" responses */
+ if (resp == 4) {
+ rval = 0;
+ goto out;
+ }
+ /* Will only be active during init, seems to be needed by some SDs */
+ if (init_mode) {
+ /* This delay is somewhat picky for some SDs. Dont change it */
+ udelay(10000);
+ } else {
+ debug("unexpected response to command %d, wanted 0x%x, got 0x%x)\n",
+ command, cmd_resp, resp);
+ }
+ rval = ERR_MMC_TIMEOUT;
+ goto out;
+ }
+out:
+ /*
+ if (pdev->sd) {
+ mmc_spi_dummy_clocks(pdev, SD_CLK_CNTRL);
+ }
+ */
+ return rval;
+}
+
+static short mmc_spi_error_handler(struct mmc_spi_dev *pdev, short rval)
+{
+ /* Handle log index, wrap if necessary */
+ unsigned short status = 0;
+
+ /* If error, check status and log */
+ if (rval) {
+ /* shift old log entries down */
+ memcpy(pdev->error_log+1, pdev->error_log, LOG_LEN-1);
+ memcpy(pdev->status_log+1, pdev->status_log, LOG_LEN-1);
+ memcpy(pdev->cmd_log+1, pdev->cmd_log, LOG_LEN-1);
+ pdev->cmd_log[0] = latest_cmd;
+ pdev->error_log[0] = rval;
+
+ /*
+ NOTE: status may be zero even on errors.
+ since data lines may be left low(if card pulled from socket for ex.)
+ */
+ status = mmc_spi_read_status(pdev);
+ pdev->status_log[0] = status;
+ pdev->errors++;
+
+ debug("Latest command was: %d\n", latest_cmd);
+ }
+ switch (rval) {
+ case ERR_SPI_TIMEOUT:
+ debug("ERR_SPI_TIMEOUT\n");
+ return RVAL_CRITICAL;
+ case ERR_MMC_TIMEOUT:
+ debug("ERR_MMC_TIMEOUT\n");
+ return RVAL_ERROR;
+ case ERR_MMC_PROG_TIMEOUT:
+ case ERR_UNKNOWN_TOK:
+ case DR_CRC_ERROR:
+ case DR_WRITE_ERROR:
+ default:
+ if (status) {
+ return RVAL_ERROR;
+ } else {
+ /* NOTE: could use status to determine what to do more accurately */
+ return RVAL_OK;
+ }
+ }
+ return 0;
+}
+
+#if 0
+/**
+* read_mmc_reg - reads the 128 bit CSD or CID register data + 2 byte CRC
+*
+*/
+static short read_mmc_reg(struct mmc_spi_dev *pdev, short csd)
+{
+ unsigned char resp = 0xff;
+ unsigned char *buf;
+ unsigned short rval = 0;
+
+ pdev->doassert();
+ if (csd) {
+ rval = send_cmd_and_wait(pdev, SEND_CSD, 0, R1_OK, MMC_COMMAND_TIMEOUT);
+ if (rval)
+ goto out;
+ buf = pdev->raw_csd;
+ } else {
+ rval = send_cmd_and_wait(pdev, SEND_CID, 0, R1_OK, MMC_COMMAND_TIMEOUT);
+ if (rval)
+ goto out;
+ buf = pdev->raw_cid;
+ }
+
+ /* start block token */
+ resp = mmc_wait_response(pdev, MMC_COMMAND_TIMEOUT);
+ if (resp != SBT_S_BLOCK_READ) {
+ debug("mmc did not send 0xFE(got 0x%x)\n", resp);
+ rval = resp;
+ goto out;
+ }
+ if (pdev->read(buf, 18, pdev->priv_data) < 18) {
+ debug("reading 18 bytes of data failed\n");
+ rval = ERR_SPI_TIMEOUT;
+ goto out;
+ }
+out:
+ /* send clocks for SD cards */
+ if (pdev->sd)
+ mmc_spi_dummy_clocks(pdev, SD_CLK_CNTRL);
+
+ pdev->deassert();
+
+ /* check for errors, but dont change rval */
+ mmc_spi_error_handler(pdev, rval);
+
+ return rval;
+}
+
+static short mmc_spi_get_card(struct mmc_spi_dev *pdev)
+{
+ if (read_mmc_reg(pdev, 1)) {
+ debug("CSD register read failed.\n");
+ return 1;
+ }
+ if (read_mmc_reg(pdev, 0)) {
+ debug("CID register read failed.\n");
+ return 1;
+ }
+
+ /* Parse CSD and CID data */
+ mmc_spi_fill_card_struct(pdev);
+
+ return 0;
+}
+#endif
+
+static short mmc_spi_read_mmc_block(struct mmc_spi_dev *pdev, unsigned char *buf, unsigned long address)
+{
+ unsigned char resp = 0xff;
+ unsigned short rval = 0;
+
+ pdev->doassert();
+ rval = send_cmd_and_wait(pdev, READ_SINGLE_BLOCK, address, R1_OK, MMC_COMMAND_TIMEOUT);
+ if (rval)
+ goto out;
+
+ /* Poll for start block token */
+ resp = mmc_wait_response(pdev, MMC_COMMAND_TIMEOUT);
+ if (resp != SBT_S_BLOCK_READ) {
+ debug("mmc did not send 0xFE(got 0x%x)\n", resp);
+ rval = resp;
+ goto out;
+ }
+ /* Read data */
+ if (pdev->read(buf, 512, pdev->priv_data) < 512) {
+ debug("reading 512 bytes of data failed\n");
+ rval = ERR_SPI_TIMEOUT;
+ goto out;
+ }
+ /* TODO: read CRC */
+out:;
+
+ /* send 8 clocks for SD cards */
+ if (pdev->sd)
+ mmc_spi_dummy_clocks(pdev, SD_CLK_CNTRL);
+ pdev->deassert();
+
+ return mmc_spi_error_handler(pdev, rval);
+}
+
+/*
+ Not implemented on Blackfin since DMA reads are a bit troublesome(512 bytes
+ requested could be 514 bytes read.. this could be solved with some hacks though)
+*/
+#ifdef USE_MULT_BLOCK_READS
+static short mmc_spi_read_mult_mmc_block(struct mmc_spi_dev *pdev, unsigned char *buf,
+ unsigned int address, int nblocks)
+{
+ unsigned char resp = 0xff;
+ int rval = 0;
+ int i = 0;
+
+ rval = send_cmd_and_wait(pdev, READ_MULTIPLE_BLOCK, address, R1_OK, MMC_COMMAND_TIMEOUT)
+ if (rval)
+ goto out;
+
+ /* idea: read n blocks in one swoop, Data, Garbage and Tokens
+ * GGGGGTDDD..512..DDDGGGGTDDDD..512..DDDGGGGT - - -
+ *-------'''''''''''''.....''''''''''''''
+ * Then memcpy data to the real buffer, may need a few pages of memory for this
+ */
+ for (i = 0; i < nblocks; i++) {
+ /* Poll for start block token */
+ resp = mmc_wait_response(pdev, MMC_COMMAND_TIMEOUT)
+ if (resp != SBT_M_BLOCK_READ) {
+ debug("mmc did not send 0xFE(got 0x%x)\n", resp);
+ rval = resp;
+ goto out;
+ }
+ if (pdev->read(buf+i*MMC_SECTOR_SIZE, MMC_SECTOR_SIZE, pdev->priv_data) < MMC_SECTOR_SIZE) {
+ debug("reading 512 bytes of data failed\n");
+ rval = 1;
+ goto out;
+ }
+ }
+ rval = 0;
+out:
+
+ /* send 8 clocks for SD cards */
+ if (pdev->sd)
+ mmc_spi_dummy_clocks(pdev, SD_CLK_CNTRL);
+
+ /* send stop command */
+ rval = send_cmd_and_wait(pdev, STOP_TRANSMISSION, address, R1_OK, MMC_COMMAND_TIMEOUT))) {
+
+ return mmc_spi_error_handler(pdev, rval);
+}
+
+static short mmc_spi_write_mmc_block(struct mmc_spi_dev *pdev, unsigned char *buf, unsigned int address)
+{
+ unsigned short rval = 0;
+ unsigned char resp = 0xff;
+ unsigned char token;
+ unsigned int n_polls = 0;
+
+ pdev->doassert();
+ rval = send_cmd_and_wait(pdev, WRITE_BLOCK, address, R1_OK, MMC_COMMAND_TIMEOUT);
+ if (rval) {
+ debug("write error at %08x \n", address);
+ goto out;
+ }
+
+ /* send start block token */
+ token = SBT_S_BLOCK_WRITE;
+ if (pdev->write(&token, 1, pdev->priv_data) < 0) {
+ debug("sending START_BLOCK_TOKEN failed\n");
+ rval = ERR_SPI_TIMEOUT;
+ goto out;
+
+ }
+ /* transmit data block */
+ if (pdev->write(buf, MMC_SECTOR_SIZE, pdev->priv_data) < MMC_SECTOR_SIZE) {
+ debug("transmission of 512 bytes failed\n");
+ rval = ERR_SPI_TIMEOUT;
+ goto out;
+
+ }
+ resp = mmc_wait_response(pdev, MMC_COMMAND_TIMEOUT) & DR_MASK;
+ /* wait for data response token */
+ if (resp != DR_ACCEPTED) {
+ /*
+ some card seem to send 0 or 1 at this point,
+ accet that even though not according to MMC spec.
+ */
+ if (resp != 0 && resp != 1 && resp != 4) {
+ debug("mmc did not send DR_ACCEPTED token(got R1=0x%x)\n", resp);
+ rval = ERR_MMC_TIMEOUT;
+ goto out;
+ }
+ }
+
+ while (1) {
+ /*
+ NOTE: could read response block-wise(effecive if DMA is utilized) to buffer
+ and check for tokens.
+ */
+ if (pdev->read(&resp, 1, pdev->priv_data) < 0) {
+ debug("busy token read polling failed\n");
+ rval = resp;
+ goto out;
+ }
+ switch (resp & DR_MASK) {
+ case BUSY_TOKEN:
+ break;
+ case DR_ACCEPTED:
+ goto out;
+ case DR_CRC_ERROR:
+ rval = DR_CRC_ERROR;
+ goto out;
+ case DR_WRITE_ERROR:
+ rval = DR_WRITE_ERROR;
+ goto out;
+ default:
+ goto out;
+ }
+ if (n_polls++ >= MMC_PROG_TIMEOUT) {
+ rval = ERR_MMC_PROG_TIMEOUT;
+ goto out;
+ }
+ }
+out:
+ /* send 8 clocks for SD cards */
+ if (pdev->sd)
+ mmc_spi_dummy_clocks(pdev, SD_CLK_CNTRL);
+
+ pdev->deassert();
+
+ return mmc_spi_error_handler(pdev, rval);
+}
+
+static unsigned char wrb[WRB_LEN];
+
+static short mmc_spi_write_mult_mmc_block(struct mmc_spi_dev *pdev,
+ unsigned char *buf, unsigned int address, int nblocks)
+{
+ unsigned short rval = 0;
+ unsigned char resp = 0xff;
+ unsigned char resp_last = 0xff;
+ unsigned char resp_oldest = 0xff;
+ unsigned int tc = 0;
+ int i = 0;
+ unsigned char token;
+ unsigned int n_polls = 0;
+
+
+ debug("adr(r): %08x\n", address);
+ pdev->doassert();
+ rval = send_cmd_and_wait(pdev, WRITE_MULTIPLE_BLOCK, address, R1_OK, MMC_COMMAND_TIMEOUT);
+ if (rval) {
+ debug("NO MBW!!!\n");
+ goto out;
+ }
+
+ for (i = 0; i < nblocks; i++) {
+ /* send start block token */
+ token = SBT_M_BLOCK_WRITE;
+ if (pdev->write(&token, 1, pdev->priv_data) < 0) {
+ debug("sending START_BLOCK_TOKEN failed\n");
+ rval = ERR_SPI_TIMEOUT;
+ goto stop;
+
+ }
+ /* transmit data block */
+ if (pdev->write(buf+i*MMC_SECTOR_SIZE, MMC_SECTOR_SIZE, pdev->priv_data) < MMC_SECTOR_SIZE) {
+ debug("transmission of 512 bytes failed\n");
+ rval = ERR_SPI_TIMEOUT;
+ goto stop;
+
+ }
+ /* wait for data response token */
+ resp = mmc_wait_response(pdev, MMC_COMMAND_TIMEOUT) & DR_MASK;
+ if (resp != DR_ACCEPTED) {
+ if (resp != 0 && resp != 1 && resp != 2 && resp != 4) {
+ debug("mmc did not send DR_ACCEPTED token(got R1=0x%x)\n", resp);
+ rval = ERR_MMC_TIMEOUT;
+ goto stop;
+ }
+ }
+ /* send 8 clocks for SD cards */
+ if (pdev->sd)
+ mmc_spi_dummy_clocks(pdev, SD_CLK_CNTRL);
+ /* wait on busy/error token while MMC is programming new data */
+ tc = 0;
+ n_polls = 0;
+
+ while (1) {
+ /* read response byte-wise(take one or two reads only) */
+ if (pdev->read(&resp, 1, pdev->priv_data) < 0) {
+ debug("busy token read polling failed\n");
+ rval = ERR_SPI_TIMEOUT;
+ goto stop;
+ }
+ switch (resp & DR_MASK) {
+ case BUSY_TOKEN:
+ break;
+ case DR_ACCEPTED:
+ goto next;
+ case DR_CRC_ERROR:
+ rval = DR_CRC_ERROR;
+ goto stop;
+ case DR_WRITE_ERROR:
+ rval = DR_WRITE_ERROR;
+ goto stop;
+ default:
+ goto next;
+ }
+ if (n_polls++ >= MMC_PROG_TIMEOUT) {
+ rval = ERR_MMC_PROG_TIMEOUT;
+ goto stop;
+ }
+ }
+next:;
+ }
+
+stop:
+ /* send stop tran token (STT_M_BLOCK_WRITE) */
+ token = STT_M_BLOCK_WRITE;
+ if (pdev->write(&token, 1, pdev->priv_data) < 0) {
+ debug("sending STT_M_BLOCK_WRITE failed\n");
+ rval = ERR_SPI_TIMEOUT;
+ goto out;
+ }
+
+ n_polls = 0;
+ /*
+ wait on final busy/error token while MMC is programming new data.
+ This is done in blocks of length WRB_LEN instead of 1-byte poll
+ (takes several 100 bytes to do at 20Mhz spi clock). Could decrease burst
+ preformance on very fast cards. But improves over-all system performance
+ immensley when using this driver.
+ */
+ while (1 && !rval) {
+ /* read response block wise */
+ if (pdev->read(wrb, WRB_LEN, pdev->priv_data) < 0) {
+ debug("busy token read polling failed");
+ rval = ERR_SPI_TIMEOUT;
+ goto out;
+ }
+ if (n_polls++ >= MMC_PROG_TIMEOUT) {
+ debug("POLL for last token timeout!!(resp=%x, last_resp=%x, resp_oldest=%x)\n",
+ resp, resp_last, resp_oldest);
+ rval = ERR_MMC_TIMEOUT;
+ goto out;
+ }
+
+ /*
+ Exit when card raises the data line(busy to done token transition)
+ NOTE: transition is often(allways?) 0x00, 0x00, 0x??, 0xFF, 0xFF,
+ where ?? could be anything != 0xFF for some card brands. Nothing
+ to do but ignore this last "token". This was a beast and caused trouble
+ with some off-brands. Either my interpretations of MMC/SD spec was bad.
+ Or some cards are just sloppy made.
+ */
+ if (wrb[WRB_LEN-1] == 0xFF) {
+ debug("Got final MBW busy wait done(as 0x%x after %d reads @ %08x.. "
+ "last_resp=%x, resp_oldest=%x\n", wrb[WRB_LEN-1], n_polls, address,
+ resp_last, resp_oldest);
+ goto out;
+ }
+ resp_oldest = resp_last;
+ resp_last = resp;
+ }
+out:
+
+ /* send 8 clocks for SD cards */
+ if (pdev->sd)
+ mmc_spi_dummy_clocks(pdev, SD_CLK_CNTRL);
+
+ pdev->deassert();
+
+ /* Reading status breaks compatibility with some cards, skip it */
+ return mmc_spi_error_handler(pdev, rval);
+}
+#endif
+
+static short mmc_spi_init_card(struct mmc_spi_dev *pdev)
+{
+ unsigned short cntr = 0;
+
+ /* for making init process beeing silent */
+ init_mode = 1;
+ /* save length of log for external usage */
+ pdev->log_len = LOG_LEN;
+
+ /* 10 bytes(80 cycles) with CS de-asserted */
+ mmc_spi_dummy_clocks(pdev, 10);
+ pdev->doassert();
+ if (send_cmd_and_wait(pdev, GO_IDLE_STATE, 0, R1_IDLE_STATE, MMC_INIT_TIMEOUT))
+ return 1;
+ pdev->deassert();
+ /* Send One Byte Delay */
+ if (pdev->write(Null_Word, 1, pdev->priv_data) < 0)
+ return 1;
+ pdev->doassert();
+ /* Look for SD card */
+ for (cntr = 0; cntr < 60; cntr++) {
+ /* Send One Byte Delay */
+ if (pdev->write(Null_Word, 1, pdev->priv_data) < 0)
+ return 1;
+ if (send_cmd_and_wait(pdev, APP_CMD, 0, R1_OK, MMC_INIT_TIMEOUT) == 0)
+ goto next;
+ if (send_cmd_and_wait(pdev, APP_CMD, 0, R1_IDLE_STATE, MMC_INIT_TIMEOUT))
+ continue;
+next:
+ if (send_cmd_and_wait(pdev, SD_SEND_OP_COND, 0, R1_OK, MMC_INIT_TIMEOUT) == 0) {
+ /* Send One Byte Delay and return */
+ if (pdev->write(Null_Word, 4, pdev->priv_data) < 0) {
+ pdev->deassert();
+ return 1;
+ }
+ pdev->sd = 1;
+ init_mode = 0;
+ debug("SD card found!\n");
+ pdev->deassert();
+ return 0;
+ }
+ }
+
+ /* poll card by sending CMD1 and wait for card initialization complete */
+ for (cntr = 0; cntr < 60; cntr++) {
+ if (send_cmd_and_wait(pdev, SEND_OP_COND, 0, R1_OK, MMC_INIT_TIMEOUT) == 0) {
+ if (pdev->write(Null_Word, 1, pdev->priv_data) < 0) {
+ pdev->deassert();
+ return 1;
+ }
+ pdev->sd = 0;
+ init_mode = 0;
+ debug("MMC card found!\n");
+ pdev->deassert();
+ return 0;
+ }
+ if (pdev->write(Null_Word, 1, pdev->priv_data) < 0) {
+ pdev->deassert();
+ return 1;
+ }
+ }
+ debug("doh!\n\n\n");
+ pdev->deassert();
+ return 1;
+}
+
+#ifdef DEBUG_REGS
+static short mmc_spi_mmc_spi_get_card_old(struct mmc_spi_dev *pdev)
+{
+ int i;
+ struct mmc_card *card = pdev->private_data->card;
+ unsigned char raw_csd[18];
+ unsigned char raw_cid[18];
+ unsigned short c_size_mult = 0;
+ unsigned short c_size = 0;
+ unsigned short read_bl_len = 0;
+ unsigned int cap = 0;
+
+ memset(raw_cid, 0, 18);
+ memset(raw_csd, 0, 18);
+
+ if (read_mmc_reg(pdev, raw_cid, 0)) {
+ debug("CSD register read failed.\n");
+ return 1;
+ };
+ if (read_mmc_reg(pdev, raw_csd, 1)) {
+ debug("CID register read failed.\n");
+ return 1;
+ }
+
+ /********** NO DEBUG CODE FROM HERE *********************/
+ card->csd.mmca_vsn = (raw_csd[0] & 0x3c) >> 2;
+ card->csd.cmdclass = (((__u16)raw_csd[4]) << 4) | ((raw_csd[5] & 0xf0) >> 4);
+ card->csd.tacc_clks = raw_csd[1];
+ card->csd.tacc_ns = raw_csd[2];
+ card->csd.max_dtr = raw_csd[3];
+ card->csd.read_blkbits = raw_csd[5] & 0x0f;
+
+ /* for calculating capacity(in blocks) */
+ c_size = ((((__u16)raw_csd[6]) & 0x03) << 10) | (((__u16)raw_csd[7]) << 2) | (((__u16)raw_csd[8]) & 0xc0) >> 6;
+ c_size_mult = ((raw_csd[9] & 0x03) << 1) | ((raw_csd[10] & 0x80) >> 7);
+ read_bl_len = raw_csd[5] & 0x0f;
+ card->csd.capacity = (c_size+1) * (1 << (c_size_mult + 2));
+
+ /* for printing capacity in bytes */
+ cap = (c_size+1) * (1 << (c_size_mult + 2)) * (1 << read_bl_len);
+
+ card->cid.manfid = getvalue(raw_cid, 127-127, 8);
+ memcpy(card.cid.prod_name, raw_cid+3, 7);
+ card->cid.serial = getvalue(raw_cid, 127-47, 32);
+ card->cid.oemid = getvalue(raw_cid, 127-119, 16);
+ card->cid.year = 1997 + (getvalue(raw_cid, 127-15, 8) & 0x0F);
+ card->cid.hwrev = (getvalue(raw_cid, 127-55, 8) & 0xF0) >> 4;
+ card->cid.fwrev = getvalue(raw_cid, 127-55, 8) & 0x0F;
+ card->cid.month = (getvalue(raw_cid, 127-15, 8) & 0xF0) >> 4;
+
+ printf("MMC found:\n\t Capacity: %dM\n\t Name: %s \n\t Rev: %d.%d \n\t"
+ "Date: %d/%d \n\t Serial: 0x%x (%u)\n", cap/(1024*1024),
+ card.cid.prod_name, card.cid.hwrev, card.cid.fwrev,
+ card.cid.year, card.cid.month, card.cid.serial, card.cid.serial);
+ return 0;
+}
+#endif
+
+
+#ifndef CONFIG_SPI_MMC_DEFAULT_CS
+# define CONFIG_SPI_MMC_DEFAULT_CS 1
+#endif
+#ifndef CONFIG_SPI_MMC_DEFAULT_SPEED
+# define CONFIG_SPI_MMC_DEFAULT_SPEED 30000000
+#endif
+#ifndef CONFIG_SPI_MMC_DEFAULT_MODE
+# define CONFIG_SPI_MMC_DEFAULT_MODE SPI_MODE_3
+#endif
+
+#define MMC_BLOCK_SIZE 512
+
+static block_dev_desc_t mmc_block_dev_desc;
+static struct mmc_spi_dev msdev;
+
+block_dev_desc_t *mmc_get_dev(int dev)
+{
+ debug("mmc_get_dev\n");
+ return (block_dev_desc_t *)&mmc_block_dev_desc;
+}
+
+static int r;
+unsigned long mmc_block_read(int dev, unsigned long blk_start, lbaint_t blkcnt, void *dst2)
+{
+ int i;
+ unsigned char *dst = dst2;
+
+ for (i = 0; i < blkcnt; i++, blk_start++, dst += MMC_BLOCK_SIZE) {
+ r += MMC_BLOCK_SIZE;
+ if (mmc_spi_read_mmc_block(&msdev, dst, blk_start * MMC_BLOCK_SIZE) != RVAL_OK)
+ printf("error in mmc_block_read\n");;
+ }
+ debug("mmc_block_read: %d bytes\n", r);
+ return blkcnt;
+}
+
+static struct spi_slave *slave;
+
+static void spi_assert(void)
+{
+ spi_cs_activate(slave);
+}
+
+static void spi_deassert(void)
+{
+ spi_cs_deactivate(slave);
+}
+
+static int spi_wait_write(unsigned char *buffer, unsigned int count, void *dummy)
+{
+ spi_xfer(slave, count * 8, buffer, NULL, 0);
+ return count;
+}
+
+static int spi_wait_read(unsigned char *buffer, unsigned int count, void *dummy)
+{
+ spi_xfer(slave, count * 8, NULL, buffer, 0);
+ return count;
+}
+
+static int spi_mmc_init(void)
+{
+ if (slave) {
+ spi_release_bus(slave);
+ spi_free_slave(slave);
+ }
+
+ slave = spi_setup_slave(0, CONFIG_SPI_MMC_DEFAULT_CS,
+ CONFIG_SPI_MMC_DEFAULT_SPEED, CONFIG_SPI_MMC_DEFAULT_MODE);
+ if (!slave)
+ return -1;
+ spi_claim_bus(slave);
+
+ return 0;
+}
+
+int mmc_legacy_init(int verbose)
+{
+ int ret;
+
+ spi_mmc_init();
+ msdev.read = &spi_wait_read;
+ msdev.write = &spi_wait_write;
+ msdev.doassert = &spi_assert;
+ msdev.deassert = &spi_deassert;
+ ret = mmc_spi_init_card(&msdev);
+ if (ret)
+ return ret;
+ mmc_block_dev_desc.if_type = IF_TYPE_MMC;
+ mmc_block_dev_desc.part_type = PART_TYPE_DOS;
+ mmc_block_dev_desc.dev = 0;
+ mmc_block_dev_desc.blksz = MMC_BLOCK_SIZE;
+ mmc_block_dev_desc.block_read = mmc_block_read;
+ sprintf(mmc_block_dev_desc.vendor, "Rubico AB <www.rubico.se>");
+ init_part(&mmc_block_dev_desc);
+ return 0;
+}
--
1.6.5
More information about the U-Boot
mailing list