X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fmmc%2Fsdhci.c;h=1eaea04ad108ac27fe5e4c954eed7fb1221384d2;hb=f97d7e8be96888b622309d9563da0ab3fba0534b;hp=9ebd33d90e6a37a8db7e88cb1ca280cfd2b66a6a;hpb=1902692aa0b2dcbb9351172be03c57d1e82447e4;p=u-boot diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 9ebd33d90e..1eaea04ad1 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -81,8 +81,16 @@ static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data) static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, unsigned int start_addr) { - unsigned int stat, rdy, mask, block = 0; + unsigned int stat, rdy, mask, timeout, block = 0; +#ifdef CONFIG_MMC_SDMA + unsigned char ctrl; + ctrl = sdhci_readl(host, SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_DMA_MASK; + ctrl |= SDHCI_CTRL_SDMA; + sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL); +#endif + timeout = 1000000; rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL; mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE; do { @@ -103,11 +111,17 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, #ifdef CONFIG_MMC_SDMA if (stat & SDHCI_INT_DMA_END) { sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS); - start_addr &= SDHCI_DEFAULT_BOUNDARY_SIZE - 1; + start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1); start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE; sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); } #endif + if (timeout-- > 0) + udelay(10); + else { + printf("Transfer data timeout\n"); + return -1; + } } while (!(stat & SDHCI_INT_DATA_END)); return 0; } @@ -121,6 +135,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; @@ -196,15 +211,26 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); #ifdef CONFIG_MMC_SDMA - flush_cache(0, ~0); + flush_cache(start_addr, trans_bytes); #endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); do { 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); @@ -214,6 +240,9 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if (!ret && data) ret = sdhci_transfer_data(host, data, start_addr); + if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) + udelay(1000); + stat = sdhci_readl(host, SDHCI_INT_STATUS); sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); if (!ret) { @@ -241,7 +270,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) if (clock == 0) return 0; - if (host->version >= SDHCI_SPEC_300) { + if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { /* Version 3.00 divisors must be a multiple of 2. */ if (mmc->f_max <= clock) div = 1; @@ -260,6 +289,9 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) } div >>= 1; + if (host->set_clock) + host->set_clock(host->index, div); + clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; @@ -308,6 +340,9 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) return; } + if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + pwr |= SDHCI_POWER_ON; sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); @@ -318,6 +353,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); @@ -325,10 +363,10 @@ void sdhci_set_ios(struct mmc *mmc) ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); if (mmc->bus_width == 8) { ctrl &= ~SDHCI_CTRL_4BITBUS; - if (host->version >= SDHCI_SPEC_300) + if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) ctrl |= SDHCI_CTRL_8BITBUS; } else { - if (host->version >= SDHCI_SPEC_300) + if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) ctrl &= ~SDHCI_CTRL_8BITBUS; if (mmc->bus_width == 4) ctrl |= SDHCI_CTRL_4BITBUS; @@ -341,6 +379,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); } @@ -356,12 +397,27 @@ int sdhci_init(struct mmc *mmc) } } - /* Eable all state */ - sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_ENABLE); - sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_SIGNAL_ENABLE); - sdhci_set_power(host, fls(mmc->voltages) - 1); + if (host->quirks & SDHCI_QUIRK_NO_CD) { + unsigned int status; + + sdhci_writel(host, SDHCI_CTRL_CD_TEST_INS | SDHCI_CTRL_CD_TEST, + SDHCI_HOST_CONTROL); + + status = sdhci_readl(host, SDHCI_PRESENT_STATE); + while ((!(status & SDHCI_CARD_PRESENT)) || + (!(status & SDHCI_CARD_STATE_STABLE)) || + (!(status & SDHCI_CARD_DETECT_PIN_LEVEL))) + status = sdhci_readl(host, SDHCI_PRESENT_STATE); + } + + /* Enable only interrupts served by the SD controller */ + sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK + , SDHCI_INT_ENABLE); + /* Mask all sdhci interrupt sources */ + sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE); + return 0; } @@ -377,11 +433,14 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) } mmc->priv = host; + host->mmc = mmc; sprintf(mmc->name, "%s", host->name); mmc->send_cmd = sdhci_send_command; mmc->set_ios = sdhci_set_ios; mmc->init = sdhci_init; + mmc->getcd = NULL; + mmc->getwp = NULL; caps = sdhci_readl(host, SDHCI_CAPABILITIES); #ifdef CONFIG_MMC_SDMA @@ -394,7 +453,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) if (max_clk) mmc->f_max = max_clk; else { - if (host->version >= SDHCI_SPEC_300) + if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) mmc->f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; else @@ -409,7 +468,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) if (min_clk) mmc->f_min = min_clk; else { - if (host->version >= SDHCI_SPEC_300) + if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_300; else mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_200; @@ -422,9 +481,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);