[U-Boot] [PATCH] mmc: new legacy MMC/SPI driver

Wilfried Busalski w.busalski at lancier-monitoring.de
Thu Oct 22 15:02:32 CEST 2009


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 at gentoo.org> schrieb im Newsbeitrag 
news:1255537669-1285-1-git-send-email-vapier at gentoo.org...
> From: Cliff Cai <cliff.cai at analog.com>
>
> Signed-off-by: Cliff Cai <cliff.cai at analog.com>
> Signed-off-by: Mike Frysinger <vapier at gentoo.org>
> CC: Wolfgang Wegner <wolfgang at 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 at rubico.se>
> + * Hans Eklund, <hans at 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 





More information about the U-Boot mailing list