From: Hans Eklund <h...@rubico.se>

Needs converting to generic MMC framework.

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

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to