diff options
author | Wolfgang Denk <wd@denx.de> | 2012-05-20 21:20:50 +0200 |
---|---|---|
committer | Wolfgang Denk <wd@denx.de> | 2012-05-20 21:20:50 +0200 |
commit | 6bc337fb13003a9a949dfb2713e308fb97faae8a (patch) | |
tree | 3ce9b22609e6c1d3e140fd2fb96af0c26efa948d /drivers | |
parent | f50bf50d7f6f99c5ad4666d63a7eef43d3940500 (diff) | |
parent | 7d2d58b4e63a8307f713e6b17fdb9b2413548574 (diff) | |
download | u-boot-imx-6bc337fb13003a9a949dfb2713e308fb97faae8a.zip u-boot-imx-6bc337fb13003a9a949dfb2713e308fb97faae8a.tar.gz u-boot-imx-6bc337fb13003a9a949dfb2713e308fb97faae8a.tar.bz2 |
Merge branch 'master' of git://git.denx.de/u-boot-mmc
* 'master' of git://git.denx.de/u-boot-mmc:
ARM: SAMSUNG: support sdhci controller
mmc: support the sdhci instead of s5p_mmc for samsung-soc
mmc: add the quirk to use the sdhci for samsung-soc
mmc: sdhci: add the quirk for broken r1b response
i.MX28: Lower the amount of blocks transfered in one DMA cycle
mmc: fsl_esdhc: Poll until card is not busy anymore
include/mmc.h: remove struct mmc_csd
mmc: omap: handle controller errors properly
mmc: omap: improve stat wait message
mmc: omap: follow TRM procedure to power on cards
mmc:fix: Set mmc width according to MMC host capabilities
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/Makefile | 2 | ||||
-rw-r--r-- | drivers/mmc/fsl_esdhc.c | 39 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 4 | ||||
-rw-r--r-- | drivers/mmc/mxsmmc.c | 2 | ||||
-rw-r--r-- | drivers/mmc/omap_hsmmc.c | 55 | ||||
-rw-r--r-- | drivers/mmc/s5p_mmc.c | 490 | ||||
-rw-r--r-- | drivers/mmc/s5p_sdhci.c | 98 | ||||
-rw-r--r-- | drivers/mmc/sdhci.c | 24 |
8 files changed, 212 insertions, 502 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index c245352..a8e681c 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -39,8 +39,8 @@ 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 -COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o COBJS-$(CONFIG_SDHCI) += sdhci.o +COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index a2f35e3..07370b5 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -307,19 +307,56 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) #else esdhc_write32(®s->xfertyp, xfertyp); #endif + + /* Mask all irqs */ + esdhc_write32(®s->irqsigen, 0); + /* Wait for the command to complete */ - while (!(esdhc_read32(®s->irqstat) & IRQSTAT_CC)) + while (!(esdhc_read32(®s->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE))) ; irqstat = esdhc_read32(®s->irqstat); esdhc_write32(®s->irqstat, irqstat); + /* Reset CMD and DATA portions on error */ + if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) { + esdhc_write32(®s->sysctl, esdhc_read32(®s->sysctl) | + SYSCTL_RSTC); + while (esdhc_read32(®s->sysctl) & SYSCTL_RSTC) + ; + + if (data) { + esdhc_write32(®s->sysctl, + esdhc_read32(®s->sysctl) | + SYSCTL_RSTD); + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTD)) + ; + } + } + if (irqstat & CMD_ERR) return COMM_ERR; if (irqstat & IRQSTAT_CTOE) return TIMEOUT; + /* Workaround for ESDHC errata ENGcm03648 */ + if (!data && (cmd->resp_type & MMC_RSP_BUSY)) { + int timeout = 2500; + + /* Poll on DATA0 line for cmd with busy signal for 250 ms */ + while (timeout > 0 && !(esdhc_read32(®s->prsstat) & + PRSSTAT_DAT0)) { + udelay(100); + timeout--; + } + + if (timeout <= 0) { + printf("Timeout waiting for DAT0 to go high!\n"); + return TIMEOUT; + } + } + /* Copy the response to the response buffer */ if (cmd->resp_type & MMC_RSP_136) { u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 596732e..aebe578 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1199,7 +1199,9 @@ int mmc_startup(struct mmc *mmc) else mmc_set_clock(mmc, 25000000); } else { - for (width = EXT_CSD_BUS_WIDTH_8; width >= 0; width--) { + width = ((mmc->host_caps & MMC_MODE_MASK_WIDTH_BITS) >> + MMC_MODE_WIDTH_BITS_SHIFT); + for (; width >= 0; width--) { /* Set the card to use 4 bit*/ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, width); diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index 35c6bda..6572e95 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -406,7 +406,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int)) */ mmc->f_min = 400000; mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2; - mmc->b_max = 0x40; + mmc->b_max = 0x20; mmc_register(mmc); return 0; diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2400db2..f2a7a78 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -33,6 +33,10 @@ #include <asm/arch/mmc_host_def.h> #include <asm/arch/sys_proto.h> +/* common definitions for all OMAPs */ +#define SYSCTL_SRC (1 << 25) +#define SYSCTL_SRD (1 << 26) + /* If we fail after 1 second wait, something is really bad */ #define MAX_RETRY_MS 1000 @@ -62,15 +66,21 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc) unsigned char mmc_board_init(struct mmc *mmc) { -#if defined(CONFIG_TWL4030_POWER) - twl4030_power_mmc_init(); -#endif - #if defined(CONFIG_OMAP34XX) t2_t *t2_base = (t2_t *)T2_BASE; struct prcm *prcm_base = (struct prcm *)PRCM_BASE; + u32 pbias_lite; - writel(readl(&t2_base->pbias_lite) | PBIASLITEPWRDNZ1 | + pbias_lite = readl(&t2_base->pbias_lite); + pbias_lite &= ~(PBIASLITEPWRDNZ1 | PBIASLITEPWRDNZ0); + writel(pbias_lite, &t2_base->pbias_lite); +#endif +#if defined(CONFIG_TWL4030_POWER) + twl4030_power_mmc_init(); + mdelay(100); /* ramp-up delay from Linux code */ +#endif +#if defined(CONFIG_OMAP34XX) + writel(pbias_lite | PBIASLITEPWRDNZ1 | PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0, &t2_base->pbias_lite); @@ -189,6 +199,27 @@ static int mmc_init_setup(struct mmc *mmc) return 0; } +/* + * MMC controller internal finite state machine reset + * + * Used to reset command or data internal state machines, using respectively + * SRC or SRD bit of SYSCTL register + */ +static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) +{ + ulong start; + + mmc_reg_out(&mmc_base->sysctl, bit, bit); + + start = get_timer(0); + while ((readl(&mmc_base->sysctl) & bit) != 0) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for sysctl %x to clear\n", + __func__, bit); + return; + } + } +} static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) @@ -209,7 +240,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, start = get_timer(0); while (readl(&mmc_base->stat)) { if (get_timer(0) - start > MAX_RETRY_MS) { - printf("%s: timedout waiting for stat!\n", __func__); + printf("%s: timedout waiting for STAT (%x) to clear\n", + __func__, readl(&mmc_base->stat)); return TIMEOUT; } } @@ -277,9 +309,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, } } while (!mmc_stat); - if ((mmc_stat & IE_CTO) != 0) + if ((mmc_stat & IE_CTO) != 0) { + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); return TIMEOUT; - else if ((mmc_stat & ERRI_MASK) != 0) + } else if ((mmc_stat & ERRI_MASK) != 0) return -1; if (mmc_stat & CC_MASK) { @@ -330,6 +363,9 @@ static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size) } } while (mmc_stat == 0); + if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + if ((mmc_stat & ERRI_MASK) != 0) return 1; @@ -382,6 +418,9 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, } } while (mmc_stat == 0); + if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + if ((mmc_stat & ERRI_MASK) != 0) return 1; diff --git a/drivers/mmc/s5p_mmc.c b/drivers/mmc/s5p_mmc.c deleted file mode 100644 index 4ae3aaf..0000000 --- a/drivers/mmc/s5p_mmc.c +++ /dev/null @@ -1,490 +0,0 @@ -/* - * (C) Copyright 2009 SAMSUNG Electronics - * Minkyu Kang <mk7.kang@samsung.com> - * Jaehoon Chung <jh80.chung@samsung.com> - * - * 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 <mmc.h> -#include <asm/io.h> -#include <asm/arch/mmc.h> -#include <asm/arch/clk.h> - -/* support 4 mmc hosts */ -struct mmc mmc_dev[4]; -struct mmc_host mmc_host[4]; - -static inline struct s5p_mmc *s5p_get_base_mmc(int dev_index) -{ - unsigned long offset = dev_index * sizeof(struct s5p_mmc); - return (struct s5p_mmc *)(samsung_get_base_mmc() + offset); -} - -static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) -{ - unsigned char ctrl; - - debug("data->dest: %08x\n", (u32)data->dest); - writel((u32)data->dest, &host->reg->sysad); - /* - * DMASEL[4:3] - * 00 = Selects SDMA - * 01 = Reserved - * 10 = Selects 32-bit Address ADMA2 - * 11 = Selects 64-bit Address ADMA2 - */ - ctrl = readb(&host->reg->hostctl); - ctrl &= ~(3 << 3); - writeb(ctrl, &host->reg->hostctl); - - /* We do not handle DMA boundaries, so set it to max (512 KiB) */ - writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); - writew(data->blocks, &host->reg->blkcnt); -} - -static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) -{ - unsigned short mode; - - /* - * TRNMOD - * MUL1SIN0[5] : Multi/Single Block Select - * RD1WT0[4] : Data Transfer Direction Select - * 1 = read - * 0 = write - * ENACMD12[2] : Auto CMD12 Enable - * ENBLKCNT[1] : Block Count Enable - * ENDMA[0] : DMA Enable - */ - mode = (1 << 1) | (1 << 0); - if (data->blocks > 1) - mode |= (1 << 5); - if (data->flags & MMC_DATA_READ) - mode |= (1 << 4); - - writew(mode, &host->reg->trnmod); -} - -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; - unsigned int timeout; - unsigned int mask; - unsigned int retry = 0x100000; - - /* Wait max 10 ms */ - timeout = 10; - - /* - * PRNSTS - * 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); - - /* - * We shouldn't wait for data inihibit for stop commands, even - * though they might use busy signaling - */ - if (data) - mask &= ~(1 << 1); - - while (readl(&host->reg->prnsts) & mask) { - if (timeout == 0) { - printf("%s: timeout error\n", __func__); - return -1; - } - timeout--; - udelay(1000); - } - - if (data) - mmc_prepare_data(host, data); - - debug("cmd->arg: %08x\n", cmd->cmdarg); - writel(cmd->cmdarg, &host->reg->argument); - - if (data) - mmc_set_transfer_mode(host, data); - - if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) - return -1; - - /* - * CMDREG - * CMDIDX[13:8] : Command index - * DATAPRNT[5] : Data Present Select - * ENCMDIDX[4] : Command Index Check Enable - * ENCMDCRC[3] : Command CRC Check Enable - * RSPTYP[1:0] - * 00 = No Response - * 01 = Length 136 - * 10 = Length 48 - * 11 = Length 48 Check busy after response - */ - if (!(cmd->resp_type & MMC_RSP_PRESENT)) - flags = 0; - else if (cmd->resp_type & MMC_RSP_136) - flags = (1 << 0); - else if (cmd->resp_type & MMC_RSP_BUSY) - flags = (3 << 0); - else - flags = (2 << 0); - - if (cmd->resp_type & MMC_RSP_CRC) - flags |= (1 << 3); - if (cmd->resp_type & MMC_RSP_OPCODE) - flags |= (1 << 4); - if (data) - flags |= (1 << 5); - - debug("cmd: %d\n", cmd->cmdidx); - - writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); - - for (i = 0; i < retry; i++) { - mask = readl(&host->reg->norintsts); - /* Command Complete */ - if (mask & (1 << 0)) { - if (!data) - writel(mask, &host->reg->norintsts); - break; - } - } - - if (i == retry) { - printf("%s: waiting for status update\n", __func__); - return TIMEOUT; - } - - if (mask & (1 << 16)) { - /* Timeout Error */ - debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); - return TIMEOUT; - } else if (mask & (1 << 15)) { - /* Error Interrupt */ - debug("error: %08x cmd %d\n", mask, cmd->cmdidx); - return -1; - } - - if (cmd->resp_type & MMC_RSP_PRESENT) { - if (cmd->resp_type & MMC_RSP_136) { - /* CRC is stripped so we need to do some shifting. */ - for (i = 0; i < 4; i++) { - unsigned int offset = - (unsigned int)(&host->reg->rspreg3 - i); - cmd->response[i] = readl(offset) << 8; - - if (i != 3) { - cmd->response[i] |= - readb(offset - 1); - } - debug("cmd->resp[%d]: %08x\n", - i, cmd->response[i]); - } - } else if (cmd->resp_type & MMC_RSP_BUSY) { - for (i = 0; i < retry; i++) { - /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ - if (readl(&host->reg->prnsts) - & (1 << 20)) /* DAT[0] */ - break; - } - - if (i == retry) { - printf("%s: card is still busy\n", __func__); - return TIMEOUT; - } - - cmd->response[0] = readl(&host->reg->rspreg0); - debug("cmd->resp[0]: %08x\n", cmd->response[0]); - } else { - cmd->response[0] = readl(&host->reg->rspreg0); - debug("cmd->resp[0]: %08x\n", cmd->response[0]); - } - } - - if (data) { - while (1) { - mask = readl(&host->reg->norintsts); - - if (mask & (1 << 15)) { - /* 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, restart the transfer where - * it was interrupted. - */ - unsigned int address = readl(&host->reg->sysad); - - debug("DMA end\n"); - writel((1 << 3), &host->reg->norintsts); - writel(address, &host->reg->sysad); - } else if (mask & (1 << 1)) { - /* Transfer Complete */ - debug("r/w is done\n"); - break; - } - } - writel(mask, &host->reg->norintsts); - } - - udelay(1000); - return 0; -} - -static void mmc_change_clock(struct mmc_host *host, uint clock) -{ - int div; - unsigned short clk; - unsigned long timeout; - unsigned long ctrl2; - - /* - * SELBASECLK[5:4] - * 00/01 = HCLK - * 10 = EPLL - * 11 = XTI or XEXTCLK - */ - ctrl2 = readl(&host->reg->control2); - ctrl2 &= ~(3 << 4); - ctrl2 |= (2 << 4); - writel(ctrl2, &host->reg->control2); - - writew(0, &host->reg->clkcon); - - /* XXX: we assume that clock is between 40MHz and 50MHz */ - if (clock == 0) - goto out; - else if (clock <= 400000) - div = 0x100; - else if (clock <= 20000000) - div = 4; - else if (clock <= 26000000) - div = 2; - else - div = 1; - debug("div: %d\n", div); - - div >>= 1; - /* - * CLKCON - * SELFREQ[15:8] : base clock divied by value - * ENSDCLK[2] : SD Clock Enable - * STBLINTCLK[1] : Internal Clock Stable - * ENINTCLK[0] : Internal Clock Enable - */ - clk = (div << 8) | (1 << 0); - writew(clk, &host->reg->clkcon); - - set_mmc_clk(host->dev_index, div); - - /* Wait max 10 ms */ - timeout = 10; - while (!(readw(&host->reg->clkcon) & (1 << 1))) { - if (timeout == 0) { - printf("%s: timeout error\n", __func__); - return; - } - timeout--; - udelay(1000); - } - - clk |= (1 << 2); - writew(clk, &host->reg->clkcon); - -out: - host->clock = clock; -} - -static void mmc_set_ios(struct mmc *mmc) -{ - struct mmc_host *host = mmc->priv; - unsigned char ctrl; - unsigned long val; - - debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); - - /* - * SELCLKPADDS[17:16] - * 00 = 2mA - * 01 = 4mA - * 10 = 7mA - * 11 = 9mA - */ - writel(0x3 << 16, &host->reg->control4); - - val = readl(&host->reg->control2); - val &= (0x3 << 4); - - val |= (1 << 31) | /* write status clear async mode enable */ - (1 << 30) | /* command conflict mask enable */ - (1 << 14) | /* Feedback Clock Enable for Rx Clock */ - (1 << 8); /* SDCLK hold enable */ - - writel(val, &host->reg->control2); - - /* - * FCSEL1[15] FCSEL0[7] - * FCSel[1:0] : Rx Feedback Clock Delay Control - * Inverter delay means10ns delay if SDCLK 50MHz setting - * 01 = Delay1 (basic delay) - * 11 = Delay2 (basic delay + 2ns) - * 00 = Delay3 (inverter delay) - * 10 = Delay4 (inverter delay + 2ns) - */ - writel(0x8080, &host->reg->control3); - - mmc_change_clock(host, mmc->clock); - - ctrl = readb(&host->reg->hostctl); - - /* - * WIDE8[5] - * 0 = Depend on WIDE4 - * 1 = 8-bit mode - * WIDE4[1] - * 1 = 4-bit mode - * 0 = 1-bit mode - */ - if (mmc->bus_width == 8) - ctrl |= (1 << 5); - else if (mmc->bus_width == 4) - ctrl |= (1 << 1); - else - ctrl &= ~(1 << 1); - - /* - * OUTEDGEINV[2] - * 1 = Riging edge output - * 0 = Falling edge output - */ - ctrl &= ~(1 << 2); - - writeb(ctrl, &host->reg->hostctl); -} - -static void mmc_reset(struct mmc_host *host) -{ - unsigned int timeout; - - /* - * RSTALL[0] : Software reset for all - * 1 = reset - * 0 = work - */ - writeb((1 << 0), &host->reg->swrst); - - host->clock = 0; - - /* Wait max 100 ms */ - timeout = 100; - - /* hw clears the bit when it's done */ - while (readb(&host->reg->swrst) & (1 << 0)) { - if (timeout == 0) { - printf("%s: timeout error\n", __func__); - return; - } - timeout--; - udelay(1000); - } -} - -static int mmc_core_init(struct mmc *mmc) -{ - struct mmc_host *host = (struct mmc_host *)mmc->priv; - unsigned int mask; - - mmc_reset(host); - - host->version = readw(&host->reg->hcver); - - /* mask all */ - writel(0xffffffff, &host->reg->norintstsen); - writel(0xffffffff, &host->reg->norintsigen); - - writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ - - /* - * NORMAL Interrupt Status Enable Register init - * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable - * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable - * [3] ENSTADMAINT : DMA Interrupt Status Enable - * [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 << 3) | (1 << 1) | (1 << 0); - writel(mask, &host->reg->norintstsen); - - /* - * NORMAL Interrupt Signal Enable Register init - * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable - */ - mask = readl(&host->reg->norintsigen); - mask &= ~(0xffff); - mask |= (1 << 1); - writel(mask, &host->reg->norintsigen); - - return 0; -} - -static int s5p_mmc_initialize(int dev_index, int bus_width) -{ - struct mmc *mmc; - - mmc = &mmc_dev[dev_index]; - - sprintf(mmc->name, "SAMSUNG SD/MMC"); - mmc->priv = &mmc_host[dev_index]; - mmc->send_cmd = mmc_send_cmd; - mmc->set_ios = mmc_set_ios; - mmc->init = mmc_core_init; - mmc->getcd = NULL; - - mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; - if (bus_width == 8) - mmc->host_caps = MMC_MODE_8BIT; - else - mmc->host_caps = MMC_MODE_4BIT; - mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; - - mmc->f_min = 400000; - mmc->f_max = 52000000; - - mmc_host[dev_index].dev_index = dev_index; - mmc_host[dev_index].clock = 0; - mmc_host[dev_index].reg = s5p_get_base_mmc(dev_index); - mmc->b_max = 0; - mmc_register(mmc); - - return 0; -} - -int s5p_mmc_init(int dev_index, int bus_width) -{ - return s5p_mmc_initialize(dev_index, bus_width); -} diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c new file mode 100644 index 0000000..1d4481b --- /dev/null +++ b/drivers/mmc/s5p_sdhci.c @@ -0,0 +1,98 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Jaehoon Chung <jh80.chung@samsung.com> + * + * 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 <sdhci.h> +#include <asm/arch/mmc.h> + +static char *S5P_NAME = "SAMSUNG SDHCI"; +static void s5p_sdhci_set_control_reg(struct sdhci_host *host) +{ + unsigned long val, ctrl; + /* + * SELCLKPADDS[17:16] + * 00 = 2mA + * 01 = 4mA + * 10 = 7mA + * 11 = 9mA + */ + sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4); + + val = sdhci_readl(host, SDHCI_CONTROL2); + val &= SDHCI_CTRL2_SELBASECLK_SHIFT; + + val |= SDHCI_CTRL2_ENSTAASYNCCLR | + SDHCI_CTRL2_ENCMDCNFMSK | + SDHCI_CTRL2_ENFBCLKRX | + SDHCI_CTRL2_ENCLKOUTHOLD; + + sdhci_writel(host, val, SDHCI_CONTROL2); + + /* + * FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7] + * FCSel[1:0] : Rx Feedback Clock Delay Control + * Inverter delay means10ns delay if SDCLK 50MHz setting + * 01 = Delay1 (basic delay) + * 11 = Delay2 (basic delay + 2ns) + * 00 = Delay3 (inverter delay) + * 10 = Delay4 (inverter delay + 2ns) + */ + val = SDHCI_CTRL3_FCSEL3 | SDHCI_CTRL3_FCSEL1; + sdhci_writel(host, val, SDHCI_CONTROL3); + + /* + * SELBASECLK[5:4] + * 00/01 = HCLK + * 10 = EPLL + * 11 = XTI or XEXTCLK + */ + ctrl = sdhci_readl(host, SDHCI_CONTROL2); + ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3); + ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2); + sdhci_writel(host, ctrl, SDHCI_CONTROL2); +} + +int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) +{ + struct sdhci_host *host = NULL; + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("sdhci__host malloc fail!\n"); + return 1; + } + + host->name = S5P_NAME; + host->ioaddr = (void *)regbase; + host->quirks = quirks; + + host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE; + host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + if (quirks & SDHCI_QUIRK_REG32_RW) + host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; + else + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + host->set_control_reg = &s5p_sdhci_set_control_reg; + + host->host_caps = MMC_MODE_HC; + + add_sdhci(host, max_clk, min_clk); + return 0; +} diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index fc904b5..1709643 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -128,6 +128,7 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; unsigned int timeout, start_addr = 0; + unsigned int retry = 10000; /* Wait max 10 ms */ timeout = 10; @@ -210,8 +211,19 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) break; + if (--retry == 0) + break; } while ((stat & mask) != mask); + if (retry == 0) { + if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) + return 0; + else { + printf("Timeout for status update!\n"); + return TIMEOUT; + } + } + if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { sdhci_cmd_done(host, cmd); sdhci_writel(host, mask, SDHCI_INT_STATUS); @@ -325,6 +337,9 @@ void sdhci_set_ios(struct mmc *mmc) u32 ctrl; struct sdhci_host *host = (struct sdhci_host *)mmc->priv; + if (host->set_control_reg) + host->set_control_reg(host); + if (mmc->clock != host->clock) sdhci_set_clock(mmc, mmc->clock); @@ -348,6 +363,9 @@ void sdhci_set_ios(struct mmc *mmc) else ctrl &= ~SDHCI_CTRL_HISPD; + if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT) + ctrl &= ~SDHCI_CTRL_HISPD; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } @@ -431,9 +449,15 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; if (caps & SDHCI_CAN_VDD_180) mmc->voltages |= MMC_VDD_165_195; + + if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE) + mmc->voltages |= host->voltages; + mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; if (caps & SDHCI_CAN_DO_8BIT) mmc->host_caps |= MMC_MODE_8BIT; + if (host->host_caps) + mmc->host_caps |= host->host_caps; sdhci_reset(host, SDHCI_RESET_ALL); mmc_register(mmc); |