* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
@ 2009-10-14 16:27 Mike Frysinger
2009-10-14 23:44 ` Mike Frysinger
` (2 more replies)
0 siblings, 3 replies; 19+ messages in thread
From: Mike Frysinger @ 2009-10-14 16:27 UTC (permalink / raw)
To: u-boot
From: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
CC: Wolfgang Wegner <wolfgang@leila.ping.de>
---
note: this isnt really suitable for merging. it needs cleaning up,
porting to the generic mmc framework, fixing of license, etc...
posting it in case someone feels like running with it and/or might
find it useful ;).
drivers/mmc/Makefile | 3 +
drivers/mmc/spi_mmc.c | 1108 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1111 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..cf685b5
--- /dev/null
+++ b/drivers/mmc/spi_mmc.c
@@ -0,0 +1,1108 @@
+/*
+ * Copyright (C) 2007, Rubico AB (www.rubico.se). All Rights Reserve.
+ *
+ * Developed as a part the CDT project C4 (www.cdt.ltu.se).
+ *
+ * Robert Selberg, <robert@rubico.se>
+ * Hans Eklund, <hans@rubico.se>
+ *
+ * Licensed under the GPL-2.
+ */
+
+/*
+ * Copyright (C) 2005, Rubico AB. All Rights Reserve.
+ *
+ * Developed as a part the CDT project C4(www.cdt.ltu.se).
+ *
+ * FILE mmc_spi_mode.c
+ *
+ * PROGRAMMER: Hans Eklund (hans [at] rubico [dot] se) (Rubico AB)
+ *
+ * DATE OF CREATION: April, 2006.
+ *
+ * SYNOPSIS:
+ *
+ * DESCRIPTION: SPI-MMC/SD Protocol.
+ *
+ * DEPENDENCIES: Independent.
+ * (well, one, for printing debug text on the target, (kernel.h for linux))
+ *
+ * TODO: Correct Multiple block read and write functions. Didnt have time
+ * to make them all failsafe. Will be done soon.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#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@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@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
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2009-10-14 16:27 [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver Mike Frysinger
@ 2009-10-14 23:44 ` Mike Frysinger
2009-10-15 8:01 ` [U-Boot] [PATCH v2] " Mike Frysinger
2009-10-22 13:02 ` [U-Boot] [PATCH] " Wilfried Busalski
2 siblings, 0 replies; 19+ messages in thread
From: Mike Frysinger @ 2009-10-14 23:44 UTC (permalink / raw)
To: u-boot
On Wednesday 14 October 2009 12:27:49 Mike Frysinger wrote:
> From: Cliff Cai <cliff.cai@analog.com>
>
> Signed-off-by: Cliff Cai <cliff.cai@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> CC: Wolfgang Wegner <wolfgang@leila.ping.de>
> ---
> note: this isnt really suitable for merging. it needs cleaning up,
> porting to the generic mmc framework, fixing of license, etc...
> posting it in case someone feels like running with it and/or might
> find it useful ;).
i looked into converting it to the generic mmc framework (as it should allow
for shrinking quite a bit of code), but i really dont know enough about mmc to
figure it out. i have plenty of experience with the common SPI framework, so
that was easy for me.
in other words, i most likely wont do any more development on the code, nor
will any one else on my side (unless someone complains something isnt
working). feel free to send patches as i'd be happy to merge them ;).
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20091014/ebd2ecf1/attachment.pgp
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH v2] mmc: new legacy MMC/SPI driver
2009-10-14 16:27 [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver Mike Frysinger
2009-10-14 23:44 ` Mike Frysinger
@ 2009-10-15 8:01 ` Mike Frysinger
2009-10-22 13:02 ` [U-Boot] [PATCH] " Wilfried Busalski
2 siblings, 0 replies; 19+ messages in thread
From: Mike Frysinger @ 2009-10-15 8:01 UTC (permalink / raw)
To: u-boot
From: Hans Eklund <hans@rubico.se>
Needs converting to generic MMC framework.
Signed-off-by: Hans Eklund <hans@rubico.se>
Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Mike Frysinger <vapier@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@rubico.se>
+ * Hans Eklund, <hans@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@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@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
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2009-10-14 16:27 [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver Mike Frysinger
2009-10-14 23:44 ` Mike Frysinger
2009-10-15 8:01 ` [U-Boot] [PATCH v2] " Mike Frysinger
@ 2009-10-22 13:02 ` Wilfried Busalski
2009-10-22 13:42 ` Mike Frysinger
2 siblings, 1 reply; 19+ messages in thread
From: Wilfried Busalski @ 2009-10-22 13:02 UTC (permalink / raw)
To: u-boot
Hi Mike
I've included your source "mmc-spi.c" into my u-boot tree.
What do I have to do to activate your driver ?
Is it enough to call the function "mmc_legacy_init(int verbose)" ?
and set the new config_ ?
regards
Wilfried
"Mike Frysinger" <vapier@gentoo.org> schrieb im Newsbeitrag
news:1255537669-1285-1-git-send-email-vapier at gentoo.org...
> From: Cliff Cai <cliff.cai@analog.com>
>
> Signed-off-by: Cliff Cai <cliff.cai@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> CC: Wolfgang Wegner <wolfgang@leila.ping.de>
> ---
> note: this isnt really suitable for merging. it needs cleaning up,
> porting to the generic mmc framework, fixing of license, etc...
> posting it in case someone feels like running with it and/or might
> find it useful ;).
>
> drivers/mmc/Makefile | 3 +
> drivers/mmc/spi_mmc.c | 1108
> +++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 1111 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..cf685b5
> --- /dev/null
> +++ b/drivers/mmc/spi_mmc.c
> @@ -0,0 +1,1108 @@
> +/*
> + * Copyright (C) 2007, Rubico AB (www.rubico.se). All Rights Reserve.
> + *
> + * Developed as a part the CDT project C4 (www.cdt.ltu.se).
> + *
> + * Robert Selberg, <robert@rubico.se>
> + * Hans Eklund, <hans@rubico.se>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +/*
> + * Copyright (C) 2005, Rubico AB. All Rights Reserve.
> + *
> + * Developed as a part the CDT project C4(www.cdt.ltu.se).
> + *
> + * FILE mmc_spi_mode.c
> + *
> + * PROGRAMMER: Hans Eklund (hans [at] rubico [dot] se) (Rubico AB)
> + *
> + * DATE OF CREATION: April, 2006.
> + *
> + * SYNOPSIS:
> + *
> + * DESCRIPTION: SPI-MMC/SD Protocol.
> + *
> + * DEPENDENCIES: Independent.
> + * (well, one, for printing debug text on the target, (kernel.h for
> linux))
> + *
> + * TODO: Correct Multiple block read and write functions. Didnt have time
> + * to make them all failsafe. Will be done soon.
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#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
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2009-10-22 13:02 ` [U-Boot] [PATCH] " Wilfried Busalski
@ 2009-10-22 13:42 ` Mike Frysinger
2009-10-22 14:31 ` Wilfried Busalski
0 siblings, 1 reply; 19+ messages in thread
From: Mike Frysinger @ 2009-10-22 13:42 UTC (permalink / raw)
To: u-boot
On Thursday 22 October 2009 09:02:32 Wilfried Busalski wrote:
> Hi Mike
please dont top post
> I've included your source "mmc-spi.c" into my u-boot tree.
>
> What do I have to do to activate your driver ?
> Is it enough to call the function "mmc_legacy_init(int verbose)" ?
> and set the new config_ ?
the driver is like any other legacy mmc driver. there is nothing special
about it. there are no board-specific functions for you to call.
you can control the SPI settings via the CONFIG_SPI_MMC_DEFAULT_* defines.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20091022/2d695d91/attachment.pgp
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2009-10-22 13:42 ` Mike Frysinger
@ 2009-10-22 14:31 ` Wilfried Busalski
2009-10-22 20:37 ` Mike Frysinger
0 siblings, 1 reply; 19+ messages in thread
From: Wilfried Busalski @ 2009-10-22 14:31 UTC (permalink / raw)
To: u-boot
Hi Mike
thaks for your responds
I'm very new to u-boot and my problem is on with hook the mmc interface will
be activated and therefor I did not understand what to do ?
regards
wilfried
>On Thursday 22 October 2009 09:02:32 Wilfried Busalski wrote:
>> Hi Mike
>
>please dont top post
>
>> I've included your source "mmc-spi.c" into my u-boot tree.
>>
>> What do I have to do to activate your driver ?
>> Is it enough to call the function "mmc_legacy_init(int verbose)" ?
>> and set the new config_ ?
>
>the driver is like any other legacy mmc driver. there is nothing special
>about it. there are no board-specific functions for you to call.
>
>you can control the SPI settings via the CONFIG_SPI_MMC_DEFAULT_* defines.
>-mike
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2009-10-22 14:31 ` Wilfried Busalski
@ 2009-10-22 20:37 ` Mike Frysinger
2009-10-22 22:50 ` Ivan Llopard
0 siblings, 1 reply; 19+ messages in thread
From: Mike Frysinger @ 2009-10-22 20:37 UTC (permalink / raw)
To: u-boot
On Thursday 22 October 2009 10:31:23 Wilfried Busalski wrote:
> Hi Mike
you're still top posting
> I'm very new to u-boot and my problem is on with hook the mmc interface
> will be activated and therefor I did not understand what to do ?
i dont know what you mean. there is no hook for you to call with mmc legacy.
all the common code takes care of calling things.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20091022/e1701599/attachment.pgp
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2009-10-22 20:37 ` Mike Frysinger
@ 2009-10-22 22:50 ` Ivan Llopard
2009-10-23 1:21 ` Mike Frysinger
0 siblings, 1 reply; 19+ messages in thread
From: Ivan Llopard @ 2009-10-22 22:50 UTC (permalink / raw)
To: u-boot
Awesome, great code!. I think it MUST be added to the u-boot tree but
i'm not the all powerful Wolfgang ;-).
I think he doesn't know how to include this driver and the spi one.
You should take a look at the configuration file for your board and
add the corresponding macros. CONFIG_SPI_MMC is a new one if you want
to add this driver, isn't it?.
Regards,
Ivan Llopard.
2009/10/22 Mike Frysinger <vapier@gentoo.org>:
> On Thursday 22 October 2009 10:31:23 Wilfried Busalski wrote:
>> Hi Mike
>
> you're still top posting
>
>> I'm very new to u-boot and my problem is on with hook the mmc interface
>> ?will be activated and therefor I did not understand what to do ?
>
> i dont know what you mean. ?there is no hook for you to call with mmc legacy.
> all the common code takes care of calling things.
> -mike
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
>
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2009-10-22 22:50 ` Ivan Llopard
@ 2009-10-23 1:21 ` Mike Frysinger
0 siblings, 0 replies; 19+ messages in thread
From: Mike Frysinger @ 2009-10-23 1:21 UTC (permalink / raw)
To: u-boot
On Thursday 22 October 2009 18:50:16 Ivan Llopard wrote:
> Awesome, great code!. I think it MUST be added to the u-boot tree but
> i'm not the all powerful Wolfgang ;-).
> I think he doesn't know how to include this driver and the spi one.
> You should take a look at the configuration file for your board and
> add the corresponding macros. CONFIG_SPI_MMC is a new one if you want
> to add this driver, isn't it?.
yes, you have to have a SPI controller driver and enable the appropriate
CONFIG options, but that isnt an issue specific to this driver. any time you
want to enable a new driver you have to enable the related config option.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20091022/90a6c163/attachment.pgp
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2010-04-22 5:16 [U-Boot] [PATCH] spi_mmc: set default spi bus Thomas Chou
@ 2010-04-23 3:28 ` Mike Frysinger
2010-04-23 8:20 ` Wolfgang Wegner
0 siblings, 1 reply; 19+ messages in thread
From: Mike Frysinger @ 2010-04-23 3:28 UTC (permalink / raw)
To: u-boot
From: Hans Eklund <hans@rubico.se>
Needs converting to generic MMC framework.
Signed-off-by: Hans Eklund <hans@rubico.se>
Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
drivers/mmc/Makefile | 3 +
drivers/mmc/spi_mmc.c | 1119 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1122 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..7ce9ce1
--- /dev/null
+++ b/drivers/mmc/spi_mmc.c
@@ -0,0 +1,1119 @@
+/*
+ * 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@rubico.se>
+ * Hans Eklund, <hans@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@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@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_BUS
+# define CONFIG_SPI_MMC_DEFAULT_BUS 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)
+{
+ char *s;
+ int bus, cs, hz, mode;
+
+ if (slave) {
+ spi_release_bus(slave);
+ spi_free_slave(slave);
+ }
+
+ if ((s = getenv("mmc_bus")))
+ bus = simple_strtoul(s, NULL, 10);
+ else
+ bus = CONFIG_SPI_MMC_DEFAULT_BUS;
+
+ if ((s = getenv("mmc_cs")))
+ cs = simple_strtoul(s, NULL, 10);
+ else
+ cs = CONFIG_SPI_MMC_DEFAULT_CS;
+
+ if ((s = getenv("mmc_hz")))
+ hz = simple_strtoul(s, NULL, 10);
+ else
+ hz = CONFIG_SPI_MMC_DEFAULT_SPEED;
+
+ if ((s = getenv("mmc_mode")))
+ mode = simple_strtoul(s, NULL, 10);
+ else
+ mode = CONFIG_SPI_MMC_DEFAULT_MODE;
+
+ printf("using spi0.%i@%i hz with mode %x\n", cs, hz, mode);
+ slave = spi_setup_slave(bus, cs, hz, mode);
+ if (!slave)
+ return -1;
+ spi_claim_bus(slave);
+
+ return 0;
+}
+
+int mmc_legacy_init(int verbose)
+{
+ int ret;
+
+ ret = spi_mmc_init();
+ if (ret)
+ return ret;
+ 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.7.0.4
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2010-04-23 3:28 ` [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver Mike Frysinger
@ 2010-04-23 8:20 ` Wolfgang Wegner
2010-04-23 14:55 ` Mike Frysinger
0 siblings, 1 reply; 19+ messages in thread
From: Wolfgang Wegner @ 2010-04-23 8:20 UTC (permalink / raw)
To: u-boot
Hi Mike,
On Thu, Apr 22, 2010 at 11:28:27PM -0400, Mike Frysinger wrote:
> +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();
[...]
Is there any reason to use doassert() and deassert() except to generate
the "dummy" cycles needed to initialize the card? Which driver does
_need_ an explicit cs_activate() and cs_deactivate() outside the
write() and read() functions?
CS control is done automatically by most drivers. According to
include/spi.h, the _board_code_ is supposed to supply spi_cs_[de]activate()
to be used by the driver in case the hardware can not control the chip
selects automatically. The documentation is not clear about it, but
from my point of view spi_cs_[de]activate() are not part of the
SPI API.
I think we should not clutter client drivers with such implementation
specific things.
I have to clean up my code and try to migrate to your newer code base,
but I did implement a modified version using SPI_XFER_BEGIN,
spi_setup_slave() (to clear the CS) and SPI_CS_HIGH to achieve the
CS switching in a way compliant with e.g. the coldfire SPI driver.
(Although IIRC I had to add SPI_CS_HIGH there, too)
Regards,
Wolfgang
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2010-04-23 8:20 ` Wolfgang Wegner
@ 2010-04-23 14:55 ` Mike Frysinger
0 siblings, 0 replies; 19+ messages in thread
From: Mike Frysinger @ 2010-04-23 14:55 UTC (permalink / raw)
To: u-boot
On Friday 23 April 2010 04:20:34 Wolfgang Wegner wrote:
> On Thu, Apr 22, 2010 at 11:28:27PM -0400, Mike Frysinger wrote:
> > +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();
>
> [...]
>
> Is there any reason to use doassert() and deassert() except to generate
> the "dummy" cycles needed to initialize the card? Which driver does
> _need_ an explicit cs_activate() and cs_deactivate() outside the
> write() and read() functions?
i have no idea. i didnt write the driver.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100423/117039a5/attachment.pgp
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
@ 2010-10-28 1:23 Mike Frysinger
2010-10-28 1:30 ` Timur Tabi
2010-12-12 17:00 ` Mike Frysinger
0 siblings, 2 replies; 19+ messages in thread
From: Mike Frysinger @ 2010-10-28 1:23 UTC (permalink / raw)
To: u-boot
From: Hans Eklund <hans@rubico.se>
Signed-off-by: Hans Eklund <hans@rubico.se>
Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
i'd like this considered for merging even though this is using the legacy
framework. there have been attempts to rewrite the driver on top of the
new framework, but all attempts thus far have failed. this driver however
is continued to be used by many people/boards in production setups. thus
i'd like this merged until we have a new working driver to replace it.
drivers/mmc/Makefile | 3 +
drivers/mmc/spi_mmc.c | 1111 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1114 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/spi_mmc.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 2ead634..5d8f05b 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_ATMEL_MCI) += atmel_mci.o
diff --git a/drivers/mmc/spi_mmc.c b/drivers/mmc/spi_mmc.c
new file mode 100644
index 0000000..c106fcc
--- /dev/null
+++ b/drivers/mmc/spi_mmc.c
@@ -0,0 +1,1111 @@
+/*
+ * 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@rubico.se>
+ * Hans Eklund, <hans@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@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@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)
+{
+ char *s;
+ int cs, hz, mode;
+
+ if (slave) {
+ spi_release_bus(slave);
+ spi_free_slave(slave);
+ }
+
+ if ((s = getenv("mmc_cs")))
+ cs = simple_strtoul(s, NULL, 10);
+ else
+ cs = CONFIG_SPI_MMC_DEFAULT_CS;
+
+ if ((s = getenv("mmc_hz")))
+ hz = simple_strtoul(s, NULL, 10);
+ else
+ hz = CONFIG_SPI_MMC_DEFAULT_SPEED;
+
+ if ((s = getenv("mmc_mode")))
+ mode = simple_strtoul(s, NULL, 10);
+ else
+ mode = CONFIG_SPI_MMC_DEFAULT_MODE;
+
+ printf("using spi0.%i@%i hz with mode %x\n", cs, hz, mode);
+ slave = spi_setup_slave(0, cs, hz, mode);
+ if (!slave)
+ return -1;
+ spi_claim_bus(slave);
+
+ return 0;
+}
+
+int mmc_legacy_init(int verbose)
+{
+ int ret;
+
+ ret = spi_mmc_init();
+ if (ret)
+ return ret;
+ 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.7.3.2
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2010-10-28 1:23 Mike Frysinger
@ 2010-10-28 1:30 ` Timur Tabi
2010-10-28 1:38 ` Mike Frysinger
2010-12-12 17:00 ` Mike Frysinger
1 sibling, 1 reply; 19+ messages in thread
From: Timur Tabi @ 2010-10-28 1:30 UTC (permalink / raw)
To: u-boot
On Wed, Oct 27, 2010 at 8:23 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> + * Licensed under the GPL-2 or later.
Could you include the normal GPL license text instead? We have
in-house tools that scan for the strings in that text to determine the
license of the file.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2010-10-28 1:30 ` Timur Tabi
@ 2010-10-28 1:38 ` Mike Frysinger
2010-10-28 1:53 ` Timur Tabi
0 siblings, 1 reply; 19+ messages in thread
From: Mike Frysinger @ 2010-10-28 1:38 UTC (permalink / raw)
To: u-boot
On Wednesday, October 27, 2010 21:30:37 Timur Tabi wrote:
> On Wed, Oct 27, 2010 at 8:23 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> > + * Licensed under the GPL-2 or later.
>
> Could you include the normal GPL license text instead? We have
> in-house tools that scan for the strings in that text to determine the
> license of the file.
update your tools then
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20101027/17ed9ae9/attachment.pgp
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2010-10-28 1:38 ` Mike Frysinger
@ 2010-10-28 1:53 ` Timur Tabi
2010-10-28 3:47 ` Mike Frysinger
0 siblings, 1 reply; 19+ messages in thread
From: Timur Tabi @ 2010-10-28 1:53 UTC (permalink / raw)
To: u-boot
On Wed, Oct 27, 2010 at 8:38 PM, Mike Frysinger <vapier@gentoo.org> wrote:
>> Could you include the normal GPL license text instead? ?We have
>> in-house tools that scan for the strings in that text to determine the
>> license of the file.
>
> update your tools then
Come on, Mike, that's not fair. I'm asking for a simple change that
doesn't inconvenience you, and this is how you respond? Are you
having a bad day or something?
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2010-10-28 1:53 ` Timur Tabi
@ 2010-10-28 3:47 ` Mike Frysinger
[not found] ` <5610599F537DD74A8D1F5CC946A7507303479410@az33exm25.fsl.freescale.net>
0 siblings, 1 reply; 19+ messages in thread
From: Mike Frysinger @ 2010-10-28 3:47 UTC (permalink / raw)
To: u-boot
On Wednesday, October 27, 2010 21:53:22 Timur Tabi wrote:
> On Wed, Oct 27, 2010 at 8:38 PM, Mike Frysinger wrote:
> >> Could you include the normal GPL license text instead? We have
> >> in-house tools that scan for the strings in that text to determine the
> >> license of the file.
> >
> > update your tools then
>
> Come on, Mike, that's not fair. I'm asking for a simple change that
> doesn't inconvenience you, and this is how you respond? Are you
> having a bad day or something?
i'm not about to go through the u-boot/linux/uClibc/whatever tree to satisfy a
random licensing tool. it'd make a lot more sense to fix your tool.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20101027/b05fcfe0/attachment.pgp
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
[not found] ` <5610599F537DD74A8D1F5CC946A7507303479410@az33exm25.fsl.freescale.net>
@ 2010-10-28 17:50 ` Mike Frysinger
0 siblings, 0 replies; 19+ messages in thread
From: Mike Frysinger @ 2010-10-28 17:50 UTC (permalink / raw)
To: u-boot
On Thu, Oct 28, 2010 at 8:06 AM, Tabi Timur-B04825 wrote:
> Mike Frysinger wrote:
>> i'm not about to go through the u-boot/linux/uClibc/whatever tree to satisfy a
>> random licensing tool. ?it'd make a lot more sense to fix your tool.
>
> I'm not asking you to go through existing code and make changes. ?You're
> posting a patch for a new file. ?Why can't this particular new file use the
> convention that most of U-Boot uses?
i'm not going to change my coding style to suite some random tool when
it makes a lot more sense to fix the tool. i constantly merge code
using this style and will continue to do so in the future.
-mike
^ permalink raw reply [flat|nested] 19+ messages in thread
* [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver
2010-10-28 1:23 Mike Frysinger
2010-10-28 1:30 ` Timur Tabi
@ 2010-12-12 17:00 ` Mike Frysinger
1 sibling, 0 replies; 19+ messages in thread
From: Mike Frysinger @ 2010-12-12 17:00 UTC (permalink / raw)
To: u-boot
On Wednesday, October 27, 2010 21:23:17 Mike Frysinger wrote:
> i'd like this considered for merging even though this is using the legacy
> framework. there have been attempts to rewrite the driver on top of the
> new framework, but all attempts thus far have failed. this driver however
> is continued to be used by many people/boards in production setups. thus
> i'd like this merged until we have a new working driver to replace it.
can we get this into next ? or at least some indication that it's going to be
merged post 2010.12 ?
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20101212/89dbcd25/attachment.pgp
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2010-12-12 17:00 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-10-14 16:27 [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver Mike Frysinger
2009-10-14 23:44 ` Mike Frysinger
2009-10-15 8:01 ` [U-Boot] [PATCH v2] " Mike Frysinger
2009-10-22 13:02 ` [U-Boot] [PATCH] " Wilfried Busalski
2009-10-22 13:42 ` Mike Frysinger
2009-10-22 14:31 ` Wilfried Busalski
2009-10-22 20:37 ` Mike Frysinger
2009-10-22 22:50 ` Ivan Llopard
2009-10-23 1:21 ` Mike Frysinger
-- strict thread matches above, loose matches on Subject: below --
2010-04-22 5:16 [U-Boot] [PATCH] spi_mmc: set default spi bus Thomas Chou
2010-04-23 3:28 ` [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver Mike Frysinger
2010-04-23 8:20 ` Wolfgang Wegner
2010-04-23 14:55 ` Mike Frysinger
2010-10-28 1:23 Mike Frysinger
2010-10-28 1:30 ` Timur Tabi
2010-10-28 1:38 ` Mike Frysinger
2010-10-28 1:53 ` Timur Tabi
2010-10-28 3:47 ` Mike Frysinger
[not found] ` <5610599F537DD74A8D1F5CC946A7507303479410@az33exm25.fsl.freescale.net>
2010-10-28 17:50 ` Mike Frysinger
2010-12-12 17:00 ` Mike Frysinger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox