diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/arm_pl180_mmci.c | 26 | ||||
-rw-r--r-- | drivers/mmc/mxsmmc.c | 351 | ||||
-rw-r--r-- | drivers/mmc/omap_hsmmc.c | 26 | ||||
-rw-r--r-- | drivers/mmc/tegra2_mmc.c | 130 | ||||
-rw-r--r-- | drivers/mmc/tegra2_mmc.h | 49 |
6 files changed, 500 insertions, 83 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9f9db75..506f1d6 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -36,6 +36,7 @@ COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o COBJS-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o COBJS-$(CONFIG_MV_SDHCI) += mv_sdhci.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o +COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index ed296ee..e6467a2 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -111,7 +111,6 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd) static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) { u32 *tempbuff = dest; - int i; u64 xfercount = blkcount * blksize; struct mmc_host *host = dev->priv; u32 status, status_err; @@ -121,31 +120,6 @@ static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) status = readl(&host->base->status); status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_RXOVERR); - while (!status_err && - (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32))) { - if (status & SDI_STA_RXFIFOBR) { - for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) - *(tempbuff + i) = readl(&host->base->fifo); - tempbuff += SDI_FIFO_BURST_SIZE; - xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); - } - status = readl(&host->base->status); - status_err = status & - (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_RXOVERR); - } - - if (status & SDI_STA_DTIMEOUT) { - printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", - xfercount, status); - return -ETIMEDOUT; - } else if (status & SDI_STA_DCRCFAIL) { - printf("Read data blk CRC error: 0x%x\n", status); - return -EILSEQ; - } else if (status & SDI_STA_RXOVERR) { - printf("Read data RX overflow error\n"); - return -EIO; - } - while ((!status_err) && (xfercount >= sizeof(u32))) { if (status & SDI_STA_RXDAVL) { *(tempbuff) = readl(&host->base->fifo); diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c new file mode 100644 index 0000000..2a9949e --- /dev/null +++ b/drivers/mmc/mxsmmc.c @@ -0,0 +1,351 @@ +/* + * Freescale i.MX28 SSP MMC driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * (C) Copyright 2008-2010 Freescale Semiconductor, Inc. + * Terry Lv + * + * Copyright 2007, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the pxa mmc code: + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <malloc.h> +#include <mmc.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> + +struct mxsmmc_priv { + int id; + struct mx28_ssp_regs *regs; + uint32_t clkseq_bypass; + uint32_t *clkctrl_ssp; + uint32_t buswidth; + int (*mmc_is_wp)(int); +}; + +#define MXSMMC_MAX_TIMEOUT 10000 + +/* + * Sends a command out on the bus. Takes the mmc pointer, + * a command pointer, and an optional data pointer. + */ +static int +mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv; + struct mx28_ssp_regs *ssp_regs = priv->regs; + uint32_t reg; + int timeout; + uint32_t data_count; + uint32_t *data_ptr; + uint32_t ctrl0; + + debug("MMC%d: CMD%d\n", mmc->block_dev.dev, cmd->cmdidx); + + /* Check bus busy */ + timeout = MXSMMC_MAX_TIMEOUT; + while (--timeout) { + udelay(1000); + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & + (SSP_STATUS_BUSY | SSP_STATUS_DATA_BUSY | + SSP_STATUS_CMD_BUSY))) { + break; + } + } + + if (!timeout) { + printf("MMC%d: Bus busy timeout!\n", mmc->block_dev.dev); + return TIMEOUT; + } + + /* See if card is present */ + if (readl(&ssp_regs->hw_ssp_status) & SSP_STATUS_CARD_DETECT) { + printf("MMC%d: No card detected!\n", mmc->block_dev.dev); + return NO_CARD_ERR; + } + + /* Start building CTRL0 contents */ + ctrl0 = priv->buswidth; + + /* Set up command */ + if (!(cmd->resp_type & MMC_RSP_CRC)) + ctrl0 |= SSP_CTRL0_IGNORE_CRC; + if (cmd->resp_type & MMC_RSP_PRESENT) /* Need to get response */ + ctrl0 |= SSP_CTRL0_GET_RESP; + if (cmd->resp_type & MMC_RSP_136) /* It's a 136 bits response */ + ctrl0 |= SSP_CTRL0_LONG_RESP; + + /* Command index */ + reg = readl(&ssp_regs->hw_ssp_cmd0); + reg &= ~(SSP_CMD0_CMD_MASK | SSP_CMD0_APPEND_8CYC); + reg |= cmd->cmdidx << SSP_CMD0_CMD_OFFSET; + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + reg |= SSP_CMD0_APPEND_8CYC; + writel(reg, &ssp_regs->hw_ssp_cmd0); + + /* Command argument */ + writel(cmd->cmdarg, &ssp_regs->hw_ssp_cmd1); + + /* Set up data */ + if (data) { + /* READ or WRITE */ + if (data->flags & MMC_DATA_READ) { + ctrl0 |= SSP_CTRL0_READ; + } else if (priv->mmc_is_wp(mmc->block_dev.dev)) { + printf("MMC%d: Can not write a locked card!\n", + mmc->block_dev.dev); + return UNUSABLE_ERR; + } + + ctrl0 |= SSP_CTRL0_DATA_XFER; + reg = ((data->blocks - 1) << + SSP_BLOCK_SIZE_BLOCK_COUNT_OFFSET) | + ((ffs(data->blocksize) - 1) << + SSP_BLOCK_SIZE_BLOCK_SIZE_OFFSET); + writel(reg, &ssp_regs->hw_ssp_block_size); + + reg = data->blocksize * data->blocks; + writel(reg, &ssp_regs->hw_ssp_xfer_size); + } + + /* Kick off the command */ + ctrl0 |= SSP_CTRL0_WAIT_FOR_IRQ | SSP_CTRL0_ENABLE | SSP_CTRL0_RUN; + writel(ctrl0, &ssp_regs->hw_ssp_ctrl0); + + /* Wait for the command to complete */ + timeout = MXSMMC_MAX_TIMEOUT; + while (--timeout) { + udelay(1000); + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & SSP_STATUS_CMD_BUSY)) + break; + } + + if (!timeout) { + printf("MMC%d: Command %d busy\n", + mmc->block_dev.dev, cmd->cmdidx); + return TIMEOUT; + } + + /* Check command timeout */ + if (reg & SSP_STATUS_RESP_TIMEOUT) { + printf("MMC%d: Command %d timeout (status 0x%08x)\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return TIMEOUT; + } + + /* Check command errors */ + if (reg & (SSP_STATUS_RESP_CRC_ERR | SSP_STATUS_RESP_ERR)) { + printf("MMC%d: Command %d error (status 0x%08x)!\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return COMM_ERR; + } + + /* Copy response to response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[3] = readl(&ssp_regs->hw_ssp_sdresp0); + cmd->response[2] = readl(&ssp_regs->hw_ssp_sdresp1); + cmd->response[1] = readl(&ssp_regs->hw_ssp_sdresp2); + cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp3); + } else + cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp0); + + /* Return if no data to process */ + if (!data) + return 0; + + /* Process the data */ + data_count = data->blocksize * data->blocks; + timeout = MXSMMC_MAX_TIMEOUT; + if (data->flags & MMC_DATA_READ) { + data_ptr = (uint32_t *)data->dest; + while (data_count && --timeout) { + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & SSP_STATUS_FIFO_EMPTY)) { + *data_ptr++ = readl(&ssp_regs->hw_ssp_data); + data_count -= 4; + timeout = MXSMMC_MAX_TIMEOUT; + } else + udelay(1000); + } + } else { + data_ptr = (uint32_t *)data->src; + timeout *= 100; + while (data_count && --timeout) { + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & SSP_STATUS_FIFO_FULL)) { + writel(*data_ptr++, &ssp_regs->hw_ssp_data); + data_count -= 4; + timeout = MXSMMC_MAX_TIMEOUT; + } else + udelay(1000); + } + } + + if (!timeout) { + printf("MMC%d: Data timeout with command %d (status 0x%08x)!\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return COMM_ERR; + } + + /* Check data errors */ + reg = readl(&ssp_regs->hw_ssp_status); + if (reg & + (SSP_STATUS_TIMEOUT | SSP_STATUS_DATA_CRC_ERR | + SSP_STATUS_FIFO_OVRFLW | SSP_STATUS_FIFO_UNDRFLW)) { + printf("MMC%d: Data error with command %d (status 0x%08x)!\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return COMM_ERR; + } + + return 0; +} + +static void mxsmmc_set_ios(struct mmc *mmc) +{ + struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv; + struct mx28_ssp_regs *ssp_regs = priv->regs; + + /* Set the clock speed */ + if (mmc->clock) + mx28_set_ssp_busclock(priv->id, mmc->clock / 1000); + + switch (mmc->bus_width) { + case 1: + priv->buswidth = SSP_CTRL0_BUS_WIDTH_ONE_BIT; + break; + case 4: + priv->buswidth = SSP_CTRL0_BUS_WIDTH_FOUR_BIT; + break; + case 8: + priv->buswidth = SSP_CTRL0_BUS_WIDTH_EIGHT_BIT; + break; + } + + /* Set the bus width */ + clrsetbits_le32(&ssp_regs->hw_ssp_ctrl0, + SSP_CTRL0_BUS_WIDTH_MASK, priv->buswidth); + + debug("MMC%d: Set %d bits bus width\n", + mmc->block_dev.dev, mmc->bus_width); +} + +static int mxsmmc_init(struct mmc *mmc) +{ + struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv; + struct mx28_ssp_regs *ssp_regs = priv->regs; + + /* Reset SSP */ + mx28_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); + + /* 8 bits word length in MMC mode */ + clrsetbits_le32(&ssp_regs->hw_ssp_ctrl1, + SSP_CTRL1_SSP_MODE_MASK | SSP_CTRL1_WORD_LENGTH_MASK, + SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS); + + /* Set initial bit clock 400 KHz */ + mx28_set_ssp_busclock(priv->id, 400); + + /* Send initial 74 clock cycles (185 us @ 400 KHz)*/ + writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_set); + udelay(200); + writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_clr); + + return 0; +} + +int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int)) +{ + struct mx28_clkctrl_regs *clkctrl_regs = + (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; + struct mmc *mmc = NULL; + struct mxsmmc_priv *priv = NULL; + + mmc = malloc(sizeof(struct mmc)); + if (!mmc) + return -ENOMEM; + + priv = malloc(sizeof(struct mxsmmc_priv)); + if (!priv) { + free(mmc); + return -ENOMEM; + } + + priv->mmc_is_wp = wp; + priv->id = id; + switch (id) { + case 0: + priv->regs = (struct mx28_ssp_regs *)MXS_SSP0_BASE; + priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP0; + priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp0; + break; + case 1: + priv->regs = (struct mx28_ssp_regs *)MXS_SSP1_BASE; + priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP1; + priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp1; + break; + case 2: + priv->regs = (struct mx28_ssp_regs *)MXS_SSP2_BASE; + priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP2; + priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp2; + break; + case 3: + priv->regs = (struct mx28_ssp_regs *)MXS_SSP3_BASE; + priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP3; + priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp3; + break; + } + + sprintf(mmc->name, "MXS MMC"); + mmc->send_cmd = mxsmmc_send_cmd; + mmc->set_ios = mxsmmc_set_ios; + mmc->init = mxsmmc_init; + mmc->priv = priv; + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + + mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | + MMC_MODE_HS_52MHz | MMC_MODE_HS; + + /* + * SSPCLK = 480 * 18 / 29 / 1 = 297.731 MHz + * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), + * CLOCK_DIVIDE has to be an even value from 2 to 254, and + * CLOCK_RATE could be any integer from 0 to 255. + */ + mmc->f_min = 400000; + mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2; + mmc->b_max = 0; + + mmc_register(mmc); + return 0; +} diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index ebda980..c38b9e6 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -36,8 +36,9 @@ /* If we fail after 1 second wait, something is really bad */ #define MAX_RETRY_MS 1000 -static int mmc_read_data(hsmmc_t *mmc_base, char *buf, unsigned int size); -static int mmc_write_data(hsmmc_t *mmc_base, const char *buf, unsigned int siz); +static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); +static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, + unsigned int siz); static struct mmc hsmmc_dev[2]; #if defined(CONFIG_OMAP44XX) && defined(CONFIG_TWL6030_POWER) @@ -97,7 +98,7 @@ unsigned char mmc_board_init(struct mmc *mmc) return 0; } -void mmc_init_stream(hsmmc_t *mmc_base) +void mmc_init_stream(struct hsmmc *mmc_base) { ulong start; @@ -128,7 +129,7 @@ void mmc_init_stream(hsmmc_t *mmc_base) static int mmc_init_setup(struct mmc *mmc) { - hsmmc_t *mmc_base = (hsmmc_t *)mmc->priv; + struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; unsigned int reg_val; unsigned int dsor; ulong start; @@ -192,7 +193,7 @@ static int mmc_init_setup(struct mmc *mmc) static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { - hsmmc_t *mmc_base = (hsmmc_t *)mmc->priv; + struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; unsigned int flags, mmc_stat; ulong start; @@ -305,7 +306,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return 0; } -static int mmc_read_data(hsmmc_t *mmc_base, char *buf, unsigned int size) +static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size) { unsigned int *output_buf = (unsigned int *)buf; unsigned int mmc_stat; @@ -356,7 +357,8 @@ static int mmc_read_data(hsmmc_t *mmc_base, char *buf, unsigned int size) return 0; } -static int mmc_write_data(hsmmc_t *mmc_base, const char *buf, unsigned int size) +static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, + unsigned int size) { unsigned int *input_buf = (unsigned int *)buf; unsigned int mmc_stat; @@ -409,7 +411,7 @@ static int mmc_write_data(hsmmc_t *mmc_base, const char *buf, unsigned int size) static void mmc_set_ios(struct mmc *mmc) { - hsmmc_t *mmc_base = (hsmmc_t *)mmc->priv; + struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; unsigned int dsor = 0; ulong start; @@ -473,20 +475,20 @@ int omap_mmc_init(int dev_index) switch (dev_index) { case 0: - mmc->priv = (hsmmc_t *)OMAP_HSMMC1_BASE; + mmc->priv = (struct hsmmc *)OMAP_HSMMC1_BASE; break; #ifdef OMAP_HSMMC2_BASE case 1: - mmc->priv = (hsmmc_t *)OMAP_HSMMC2_BASE; + mmc->priv = (struct hsmmc *)OMAP_HSMMC2_BASE; break; #endif #ifdef OMAP_HSMMC3_BASE case 2: - mmc->priv = (hsmmc_t *)OMAP_HSMMC3_BASE; + mmc->priv = (struct hsmmc *)OMAP_HSMMC3_BASE; break; #endif default: - mmc->priv = (hsmmc_t *)OMAP_HSMMC1_BASE; + mmc->priv = (struct hsmmc *)OMAP_HSMMC1_BASE; return 1; } mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; diff --git a/drivers/mmc/tegra2_mmc.c b/drivers/mmc/tegra2_mmc.c index 78b1190..ccf48bb 100644 --- a/drivers/mmc/tegra2_mmc.c +++ b/drivers/mmc/tegra2_mmc.c @@ -81,7 +81,8 @@ static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) * 11 = Selects 64-bit Address ADMA2 */ ctrl = readb(&host->reg->hostctl); - ctrl &= ~(3 << 3); /* SDMA */ + ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; + ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; writeb(ctrl, &host->reg->hostctl); /* We do not handle DMA boundaries, so set it to max (512 KiB) */ @@ -103,43 +104,36 @@ static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) * ENBLKCNT[1] : Block Count Enable * ENDMA[0] : DMA Enable */ - mode = (1 << 1) | (1 << 0); + mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | + TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); + if (data->blocks > 1) - mode |= (1 << 5); + mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; + if (data->flags & MMC_DATA_READ) - mode |= (1 << 4); + mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; writew(mode, &host->reg->trnmod); } -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) +static int mmc_wait_inhibit(struct mmc_host *host, + struct mmc_cmd *cmd, + struct mmc_data *data, + unsigned int timeout) { - struct mmc_host *host = (struct mmc_host *)mmc->priv; - int flags, i; - unsigned int timeout; - unsigned int mask; - unsigned int retry = 0x100000; - debug(" mmc_send_cmd called\n"); - - /* Wait max 10 ms */ - timeout = 10; - /* * PRNSTS - * CMDINHDAT[1] : Command Inhibit (DAT) - * CMDINHCMD[0] : Command Inhibit (CMD) + * CMDINHDAT[1] : Command Inhibit (DAT) + * CMDINHCMD[0] : Command Inhibit (CMD) */ - mask = (1 << 0); - if ((data != NULL) || (cmd->resp_type & MMC_RSP_BUSY)) - mask |= (1 << 1); + unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; /* * We shouldn't wait for data inhibit for stop commands, even * though they might use busy signaling */ - if (data) - mask &= ~(1 << 1); + if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) + mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; while (readl(&host->reg->prnsts) & mask) { if (timeout == 0) { @@ -150,6 +144,24 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, udelay(1000); } + return 0; +} + +static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct mmc_host *host = (struct mmc_host *)mmc->priv; + int flags, i; + int result; + unsigned int mask; + unsigned int retry = 0x100000; + debug(" mmc_send_cmd called\n"); + + result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); + + if (result < 0) + return result; + if (data) mmc_prepare_data(host, data); @@ -175,20 +187,20 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, * 11 = Length 48 Check busy after response */ if (!(cmd->resp_type & MMC_RSP_PRESENT)) - flags = 0; + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; else if (cmd->resp_type & MMC_RSP_136) - flags = (1 << 0); + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; else if (cmd->resp_type & MMC_RSP_BUSY) - flags = (3 << 0); + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; else - flags = (2 << 0); + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; if (cmd->resp_type & MMC_RSP_CRC) - flags |= (1 << 3); + flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; if (cmd->resp_type & MMC_RSP_OPCODE) - flags |= (1 << 4); + flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; if (data) - flags |= (1 << 5); + flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; debug("cmd: %d\n", cmd->cmdidx); @@ -197,7 +209,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, for (i = 0; i < retry; i++) { mask = readl(&host->reg->norintsts); /* Command Complete */ - if (mask & (1 << 0)) { + if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { if (!data) writel(mask, &host->reg->norintsts); break; @@ -209,11 +221,11 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return TIMEOUT; } - if (mask & (1 << 16)) { + if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { /* Timeout Error */ debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); return TIMEOUT; - } else if (mask & (1 << 15)) { + } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { /* Error Interrupt */ debug("error: %08x cmd %d\n", mask, cmd->cmdidx); return -1; @@ -256,23 +268,44 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, } if (data) { + unsigned long start = get_timer(0); + while (1) { mask = readl(&host->reg->norintsts); - if (mask & (1 << 15)) { + if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { /* Error Interrupt */ writel(mask, &host->reg->norintsts); printf("%s: error during transfer: 0x%08x\n", __func__, mask); return -1; - } else if (mask & (1 << 3)) { - /* DMA Interrupt */ + } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { + /* + * DMA Interrupt, restart the transfer where + * it was interrupted. + */ + unsigned int address = readl(&host->reg->sysad); + debug("DMA end\n"); - break; - } else if (mask & (1 << 1)) { + writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, + &host->reg->norintsts); + writel(address, &host->reg->sysad); + } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { /* Transfer Complete */ debug("r/w is done\n"); break; + } else if (get_timer(start) > 2000UL) { + writel(mask, &host->reg->norintsts); + printf("%s: MMC Timeout\n" + " Interrupt status 0x%08x\n" + " Interrupt status enable 0x%08x\n" + " Interrupt signal enable 0x%08x\n" + " Present status 0x%08x\n", + __func__, mask, + readl(&host->reg->norintstsen), + readl(&host->reg->norintsigen), + readl(&host->reg->prnsts)); + return -1; } } writel(mask, &host->reg->norintsts); @@ -310,12 +343,14 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) * ENINTCLK[0] : Internal Clock Enable */ div >>= 1; - clk = (div << 8) | (1 << 0); + clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | + TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); writew(clk, &host->reg->clkcon); /* Wait max 10 ms */ timeout = 10; - while (!(readw(&host->reg->clkcon) & (1 << 1))) { + while (!(readw(&host->reg->clkcon) & + TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { if (timeout == 0) { printf("%s: timeout error\n", __func__); return; @@ -324,7 +359,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) udelay(1000); } - clk |= (1 << 2); + clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; writew(clk, &host->reg->clkcon); debug("mmc_change_clock: clkcon = %08X\n", clk); @@ -375,7 +410,7 @@ static void mmc_reset(struct mmc_host *host) * 1 = reset * 0 = work */ - writeb((1 << 0), &host->reg->swrst); + writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); host->clock = 0; @@ -383,7 +418,7 @@ static void mmc_reset(struct mmc_host *host) timeout = 100; /* hw clears the bit when it's done */ - while (readb(&host->reg->swrst) & (1 << 0)) { + while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { if (timeout == 0) { printf("%s: timeout error\n", __func__); return; @@ -413,12 +448,17 @@ static int mmc_core_init(struct mmc *mmc) * NORMAL Interrupt Status Enable Register init * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable + * [3] ENSTADMAINT : DMA boundary interrupt * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable * [0] ENSTACMDCMPLT : Command Complete Status Enable */ mask = readl(&host->reg->norintstsen); mask &= ~(0xffff); - mask |= (1 << 5) | (1 << 4) | (1 << 1) | (1 << 0); + mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | + TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | + TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | + TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | + TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); writel(mask, &host->reg->norintstsen); /* @@ -427,7 +467,7 @@ static int mmc_core_init(struct mmc *mmc) */ mask = readl(&host->reg->norintsigen); mask &= ~(0xffff); - mask |= (1 << 1); + mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; writel(mask, &host->reg->norintsigen); return 0; diff --git a/drivers/mmc/tegra2_mmc.h b/drivers/mmc/tegra2_mmc.h index 28698e0..671583c 100644 --- a/drivers/mmc/tegra2_mmc.h +++ b/drivers/mmc/tegra2_mmc.h @@ -68,6 +68,55 @@ struct tegra2_mmc { unsigned char res6[0x100]; /* RESERVED, offset 100h-1FFh */ }; +#define TEGRA_MMC_HOSTCTL_DMASEL_MASK (3 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_SDMA (0 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_32BIT (2 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_64BIT (3 << 3) + +#define TEGRA_MMC_TRNMOD_DMA_ENABLE (1 << 0) +#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE (1 << 1) +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE (0 << 4) +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ (1 << 4) +#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT (1 << 5) + +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK (3 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE (0 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 (1 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 (2 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY (3 << 0) + +#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK (1 << 3) +#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK (1 << 4) +#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER (1 << 5) + +#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD (1 << 0) +#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT (1 << 1) + +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE (1 << 0) +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE (1 << 1) +#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE (1 << 2) + +#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT 8 +#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_MASK (0xff << 8) + +#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL (1 << 0) +#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE (1 << 1) +#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE (1 << 2) + +#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE (1 << 0) +#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE (1 << 1) +#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT (1 << 3) +#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT (1 << 15) +#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT (1 << 16) + +#define TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE (1 << 0) +#define TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE (1 << 1) +#define TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT (1 << 3) +#define TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY (1 << 4) +#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY (1 << 5) + +#define TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE (1 << 1) + struct mmc_host { struct tegra2_mmc *reg; unsigned int version; /* SDHCI spec. version */ |