From patchwork Fri Apr 23 03:28:27 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mike Frysinger X-Patchwork-Id: 71774 Return-Path: X-Original-To: wd@gemini.denx.de Delivered-To: wd@gemini.denx.de Received: from diddl.denx.de (diddl.denx.de [10.0.0.6]) by gemini.denx.de (Postfix) with ESMTP id 2E04B1A09F for ; Fri, 23 Apr 2010 05:28:53 +0200 (CEST) Received: from diddl.denx.de (localhost.localdomain [127.0.0.1]) by diddl.denx.de (Postfix) with ESMTP id 149B5C913130 for ; Fri, 23 Apr 2010 05:28:53 +0200 (CEST) Received: from pop.mnet-online.de by diddl.denx.de with POP3 (fetchmail-6.3.9) for (single-drop); Fri, 23 Apr 2010 05:28:53 +0200 (CEST) Received: from murder (svr19.m-online.net [192.168.3.147]) by backend2 (Cyrus v2.2.12) with LMTPA; Fri, 23 Apr 2010 05:28:47 +0200 X-Sieve: CMU Sieve 2.2 Received: from mail.m-online.net (localhost [127.0.0.1]) by frontend3.pop.m-online.net (Cyrus v2.2.13) with LMTPA; Fri, 23 Apr 2010 05:28:47 +0200 Received: from scanner-3.m-online.net (scanner-3.m-online.net [192.168.1.20]) by mail.m-online.net (Postfix) with ESMTP id 6AA2E200099; Fri, 23 Apr 2010 05:28:47 +0200 (CEST) Received: from mxin-1.m-online.net ([192.168.6.164]) by scanner-3.m-online.net (scanner-3.m-online.net [192.168.1.20]) (amavisd-new, port 10026) with ESMTP id 11371-06; Fri, 23 Apr 2010 05:28:45 +0200 (CEST) Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by mxin-1.m-online.net (Postfix) with ESMTP id 3491046C0AA; Fri, 23 Apr 2010 05:28:45 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C952928278; Fri, 23 Apr 2010 05:28:40 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hPOFlpQU3P1t; Fri, 23 Apr 2010 05:28:40 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 6AE882827A; Fri, 23 Apr 2010 05:28:36 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 0D8E728272 for ; Fri, 23 Apr 2010 05:28:32 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id kadxfiMuHDaK for ; Fri, 23 Apr 2010 05:28:30 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) by theia.denx.de (Postfix) with ESMTPS id 054272826D for ; Fri, 23 Apr 2010 05:28:26 +0200 (CEST) Received: from localhost.localdomain (localhost [127.0.0.1]) by smtp.gentoo.org (Postfix) with ESMTP id 7B9841B40EC; Fri, 23 Apr 2010 03:28:21 +0000 (UTC) From: Mike Frysinger To: u-boot@lists.denx.de Date: Thu, 22 Apr 2010 23:28:27 -0400 Message-Id: <1271993307-10361-1-git-send-email-vapier@gentoo.org> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1271913394-12958-1-git-send-email-thomas@wytron.com.tw> References: <1271913394-12958-1-git-send-email-thomas@wytron.com.tw> Subject: [U-Boot] [PATCH] mmc: new legacy MMC/SPI driver X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de X-Virus-Scanned: by amavisd-new at m-online.net From: Hans Eklund Needs converting to generic MMC framework. Signed-off-by: Hans Eklund Signed-off-by: Cliff Cai Signed-off-by: Mike Frysinger --- 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, + * Hans Eklund, + * + * 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 +#include +#include +#include + +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_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 at %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 "); + init_part(&mmc_block_dev_desc); + return 0; +}