This patch supports mmc/sd card with spi interface. I have tested with sd and mmc cards. But there is still ocr issue with SDHC.
Signed-off-by: Thomas Chou <tho...@wytron.com.tw> --- drivers/mmc/Makefile | 1 + drivers/mmc/mmc_spi.c | 252 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 0 deletions(-) create mode 100644 drivers/mmc/mmc_spi.c diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 1b8f5bd..d03eb47 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -31,6 +31,7 @@ LIB := $(obj)libmmc.a COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o +COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c new file mode 100644 index 0000000..76c5977 --- /dev/null +++ b/drivers/mmc/mmc_spi.c @@ -0,0 +1,252 @@ +/* + * generic mmc spi driver + * + * Copyright (C) 2010 Thomas Chou <tho...@wytron.com.tw> + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <part.h> +#include <mmc.h> +#include <spi.h> +#include <asm/errno.h> + +#define CTOUT 0x10 +#define RTOUT 0x10000 +#define WTOUT 0x10000 + +static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg) +{ + u8 cmdo[6]; + u8 r1; + int i; + cmdo[0] = 0x40 + cmdidx; + cmdo[1] = cmdarg >> 24; + cmdo[2] = cmdarg >> 16; + cmdo[3] = cmdarg >> 8; + cmdo[4] = cmdarg; + cmdo[5] = 0x95; /* crc valid only for cmd00 */ + spi_xfer(mmc->priv, 6 * 8, cmdo, NULL, SPI_XFER_BEGIN); + for (i = 0; i < CTOUT; i++) { + spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0); + if ((r1 & 0x80) == 0) + break; + } + debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1); + return r1; +} + +static uint mmc_spi_readdata(struct mmc *mmc, char *buf, + u32 bcnt, u32 bsize) +{ + u8 r1; + u8 crc[2]; + int i; + while (bcnt--) { + for (i = 0; i < RTOUT; i++) { + spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0); + if (r1 != 0xff) + break; + } + debug("%s:tok%d %x\n", __func__, i, r1); + if (r1 == 0xfe) { + spi_xfer(mmc->priv, bsize * 8, NULL, buf, 0); + buf += bsize; + spi_xfer(mmc->priv, 2 * 8, NULL, crc, 0); + r1 = 0; + } else + break; + } + return r1; +} + +static uint mmc_spi_writedata(struct mmc *mmc, const char *buf, + u32 bcnt, u32 bsize) +{ + u8 r1; + u8 tok[2] = { 0xff, 0xfe }; + u8 crc[2]; + int i; + while (bcnt--) { + spi_xfer(mmc->priv, 2 * 8, tok, NULL, 0); + spi_xfer(mmc->priv, bsize * 8, buf, NULL, 0); + buf += bsize; + spi_xfer(mmc->priv, 2 * 8, crc, NULL, 0); + spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0); + if (r1 == 0x05) { + for (i = 0; i < WTOUT; i++) { + spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0); + if (r1 == 0xff) { + r1 = 0; + break; + } + } + if (i == WTOUT) { + debug("%s:wtout %x\n", __func__, r1); + r1 = 0x04; + break; + } + } else + break; + } + return r1; +} + +static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf, + u32 bcnt, u32 bsize) +{ + u8 r1; + u8 tok[2] = { 0xff, 0xfc }; + u8 stop[2] = { 0xff, 0xfd }; + u8 crc[2]; + int i; + while (bcnt--) { + spi_xfer(mmc->priv, 2 * 8, tok, NULL, 0); + spi_xfer(mmc->priv, bsize * 8, buf, NULL, 0); + buf += bsize; + spi_xfer(mmc->priv, 2 * 8, crc, NULL, 0); + spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0); + if (r1 == 0x05) { + for (i = 0; i < WTOUT; i++) { + spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0); + if (r1 == 0xff) { + r1 = 0; + break; + } + } + if (i == WTOUT) { + debug("%s:wtout %x\n", __func__, r1); + r1 = 0x04; + break; + } + } + } + if (bcnt == 0 && r1 == 0) { + spi_xfer(mmc->priv, 2 * 8, stop, NULL, 0); + for (i = 0; i < WTOUT; i++) { + spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0); + if (r1 == 0xff) { + r1 = 0; + break; + } + } + if (i == WTOUT) { + debug("%s:wtout %x\n", __func__, r1); + r1 = 0x04; + } + } + return r1; +} + +static inline uint rswab(u8 *d) +{ + return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; +} + +static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + u8 r1; + u8 resp[16]; + int i; + debug("%s:cmd%d %x %x %x\n", __func__, + cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags); + if (cmd->cmdidx == MMC_CMD_ALL_SEND_CID) + cmd->cmdidx = MMC_CMD_SEND_CID; + r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg); + if (cmd->cmdidx == 55 && (r1 & 0x04)) + return TIMEOUT; + if ((cmd->cmdidx == MMC_CMD_SEND_OP_COND || + cmd->cmdidx == SD_CMD_APP_SEND_OP_COND) && + cmd->resp_type == MMC_RSP_R3) { + r1 = mmc_spi_sendcmd(mmc, 58, 0); + spi_xfer(mmc->priv, 4 * 8, NULL, resp, 0); + cmd->response[0] = rswab(resp); + debug("ocr %x\n", cmd->response[0]); + } else if (cmd->resp_type == MMC_RSP_R2) { + r1 = mmc_spi_readdata(mmc, resp, 1, 16); + for (i = 0; i < 4; i++) + cmd->response[i] = rswab(resp + i * 4); + debug("r136 %x %x %x %x\n", cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + } else if (cmd->resp_type == MMC_RSP_R1) { + cmd->response[0] = 0; + if (r1 & 0x7e) + cmd->response[0] |= (1 << 19); + if (r1 & 0x40) + cmd->response[0] |= (1 << 31); + if (r1 & 0x20) + cmd->response[0] |= (1 << 30); + if (r1 & 0x10) + cmd->response[0] |= (1 << 28); + if (r1 & 0x08) + cmd->response[0] |= (1 << 23); + if (r1 & 0x04) + cmd->response[0] |= (1 << 22); + if (r1 & 0x02) + cmd->response[0] |= (1 << 13); + } + if (data) { + debug("%s:data %x %x %x\n", __func__, + data->flags, data->blocks, data->blocksize); + if (data->flags == MMC_DATA_READ) { + r1 = mmc_spi_readdata(mmc, data->dest, + data->blocks, data->blocksize); + } else if (data->flags == MMC_DATA_WRITE) { + if (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK) + r1 = mmc_spi_writeblock(mmc, data->src, + data->blocks, data->blocksize); + else + r1 = mmc_spi_writedata(mmc, data->src, + data->blocks, data->blocksize); + } + } + return 0; +} + +static void mmc_spi_set_ios(struct mmc *mmc) +{ + debug("%s:\n", __func__); +} + +static int mmc_spi_init_p(struct mmc *mmc) +{ + u8 d = 0xff; + int i; + debug("%s:\n", __func__); + spi_release_bus(mmc->priv); + for (i = 0; i < 16; i++) /* power on initialization */ + spi_xfer(mmc->priv, 1 * 8, &d, NULL, 0); + spi_claim_bus(mmc->priv); + return 0; +} + +int mmc_spi_init(uint bus, uint cs, uint speed, uint mode) +{ + struct mmc *mmc = NULL; + + mmc = malloc(sizeof(struct mmc)); + if (!mmc) + return -ENOMEM; + sprintf(mmc->name, "mmc_spi"); + mmc->priv = spi_setup_slave(bus, cs, speed, mode); + if (!mmc->priv) { + free(mmc); + return -ENOMEM; + } + mmc->send_cmd = mmc_spi_request; + mmc->set_ios = mmc_spi_set_ios; + mmc->init = mmc_spi_init_p; + mmc->host_caps = 0; + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->f_max = speed; + mmc->f_min = mmc->f_max >> 9; + mmc->block_dev.part_type = PART_TYPE_DOS; + + mmc_register(mmc); + + return 0; +} -- 1.6.6.1 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot