X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fmmc%2Fsdhci.c;h=837c53842b3802700ca192692c9ead0e2d220590;hb=2cb5d67c1aceb758033954cc06382367ac89e6ac;hp=1d6d2fde0d8489dba3490f4e1714b4b65631cb89;hpb=915ffa5213756568f6185d05cda2cb2f6050f974;p=u-boot diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 1d6d2fde0d..837c53842b 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -87,7 +87,7 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, if (stat & SDHCI_INT_ERROR) { printf("%s: Error detected in status(0x%X)!\n", __func__, stat); - return -1; + return -EIO; } if (stat & rdy) { if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)) @@ -110,7 +110,7 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, udelay(10); else { printf("%s: Transfer data timeout\n", __func__); - return -1; + return -ETIMEDOUT; } } while (!(stat & SDHCI_INT_DATA_END)); return 0; @@ -121,13 +121,10 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, * for card ready state. * Every time when card is busy after timeout then (last) timeout value will be * increased twice but only if it doesn't exceed global defined maximum. - * Each function call will use last timeout value. Max timeout can be redefined - * in board config file. + * Each function call will use last timeout value. */ -#ifndef CONFIG_SDHCI_CMD_MAX_TIMEOUT -#define CONFIG_SDHCI_CMD_MAX_TIMEOUT 3200 -#endif -#define CONFIG_SDHCI_CMD_DEFAULT_TIMEOUT 100 +#define SDHCI_CMD_MAX_TIMEOUT 3200 +#define SDHCI_CMD_DEFAULT_TIMEOUT 100 #define SDHCI_READ_STATUS_TIMEOUT 1000 #ifdef CONFIG_DM_MMC_OPS @@ -151,7 +148,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, unsigned start = get_timer(0); /* Timeout unit - ms */ - static unsigned int cmd_timeout = CONFIG_SDHCI_CMD_DEFAULT_TIMEOUT; + static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; @@ -164,7 +161,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { if (time >= cmd_timeout) { printf("%s: MMC: %d busy ", __func__, mmc_dev); - if (2 * cmd_timeout <= CONFIG_SDHCI_CMD_MAX_TIMEOUT) { + if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) { cmd_timeout += cmd_timeout; printf("timeout increasing to: %u ms.\n", cmd_timeout); @@ -297,7 +294,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) { struct sdhci_host *host = mmc->priv; - unsigned int div, clk, timeout, reg; + unsigned int div, clk = 0, timeout, reg; /* Wait max 20 ms */ timeout = 200; @@ -306,7 +303,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) if (timeout == 0) { printf("%s: Timeout to wait cmd & data inhibit\n", __func__); - return -1; + return -EBUSY; } timeout--; @@ -321,14 +318,36 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) return 0; if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { - /* Version 3.00 divisors must be a multiple of 2. */ - if (mmc->cfg->f_max <= clock) - div = 1; - else { - for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { - if ((mmc->cfg->f_max / div) <= clock) + /* + * Check if the Host Controller supports Programmable Clock + * Mode. + */ + if (host->clk_mul) { + for (div = 1; div <= 1024; div++) { + if ((mmc->cfg->f_max * host->clk_mul / div) + <= clock) break; } + + /* + * Set Programmable Clock Mode in the Clock + * Control register. + */ + clk = SDHCI_PROG_CLOCK_MODE; + div--; + } else { + /* Version 3.00 divisors must be a multiple of 2. */ + if (mmc->cfg->f_max <= clock) { + div = 1; + } else { + for (div = 2; + div < SDHCI_MAX_DIV_SPEC_300; + div += 2) { + if ((mmc->cfg->f_max / div) <= clock) + break; + } + } + div >>= 1; } } else { /* Version 2.00 divisors must be a power of 2. */ @@ -336,13 +355,13 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) if ((mmc->cfg->f_max / div) <= clock) break; } + div >>= 1; } - div >>= 1; if (host->set_clock) host->set_clock(host->index, div); - clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; clk |= SDHCI_CLOCK_INT_EN; @@ -355,7 +374,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) if (timeout == 0) { printf("%s: Internal clock never stabilised.\n", __func__); - return -1; + return -EBUSY; } timeout--; udelay(1000); @@ -451,12 +470,14 @@ static int sdhci_init(struct mmc *mmc) { struct sdhci_host *host = mmc->priv; + sdhci_reset(host, SDHCI_RESET_ALL); + if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !aligned_buffer) { aligned_buffer = memalign(8, 512*1024); if (!aligned_buffer) { printf("%s: Aligned buffer alloc failed!!!\n", __func__); - return -1; + return -ENOMEM; } } @@ -511,18 +532,34 @@ static const struct mmc_ops sdhci_ops = { }; #endif -int sdhci_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth, - uint caps, u32 max_clk, u32 min_clk, uint version, - uint quirks, uint host_caps) +int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, + u32 max_clk, u32 min_clk) { - cfg->name = name; + u32 caps, caps_1; + + caps = sdhci_readl(host, SDHCI_CAPABILITIES); + +#ifdef CONFIG_MMC_SDMA + if (!(caps & SDHCI_CAN_DO_SDMA)) { + printf("%s: Your controller doesn't support SDMA!!\n", + __func__); + return -EINVAL; + } +#endif + if (host->quirks & SDHCI_QUIRK_REG32_RW) + host->version = + sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; + else + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + cfg->name = host->name; #ifndef CONFIG_DM_MMC_OPS cfg->ops = &sdhci_ops; #endif if (max_clk) cfg->f_max = max_clk; else { - if (version >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) cfg->f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; else @@ -530,12 +567,15 @@ int sdhci_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth, SDHCI_CLOCK_BASE_SHIFT; cfg->f_max *= 1000000; } - if (cfg->f_max == 0) + if (cfg->f_max == 0) { + printf("%s: Hardware doesn't specify base clock frequency\n", + __func__); return -EINVAL; + } if (min_clk) cfg->f_min = min_clk; else { - if (version >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) cfg->f_min = cfg->f_max / SDHCI_MAX_DIV_SPEC_300; else cfg->f_min = cfg->f_max / SDHCI_MAX_DIV_SPEC_200; @@ -548,21 +588,29 @@ int sdhci_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth, if (caps & SDHCI_CAN_VDD_180) cfg->voltages |= MMC_VDD_165_195; + if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE) + cfg->voltages |= host->voltages; + cfg->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; - if (version >= SDHCI_SPEC_300) { + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { if (caps & SDHCI_CAN_DO_8BIT) cfg->host_caps |= MMC_MODE_8BIT; } - if (quirks & SDHCI_QUIRK_NO_HISPD_BIT) - cfg->host_caps &= ~(MMC_MODE_HS | MMC_MODE_HS_52MHz); - - if (host_caps) - cfg->host_caps |= host_caps; + if (host->host_caps) + cfg->host_caps |= host->host_caps; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + /* + * In case of Host Controller v3.00, find out whether clock + * multiplier is supported. + */ + caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >> + SDHCI_CLOCK_MUL_SHIFT; + return 0; } @@ -574,34 +622,16 @@ int sdhci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg) #else int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) { - unsigned int caps; - - caps = sdhci_readl(host, SDHCI_CAPABILITIES); -#ifdef CONFIG_MMC_SDMA - if (!(caps & SDHCI_CAN_DO_SDMA)) { - printf("%s: Your controller doesn't support SDMA!!\n", - __func__); - return -1; - } -#endif + int ret; - if (sdhci_setup_cfg(&host->cfg, host->name, host->bus_width, caps, - max_clk, min_clk, SDHCI_GET_VERSION(host), - host->quirks, host->host_caps)) { - printf("%s: Hardware doesn't specify base clock frequency\n", - __func__); - return -EINVAL; - } - - if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE) - host->cfg.voltages |= host->voltages; - - sdhci_reset(host, SDHCI_RESET_ALL); + ret = sdhci_setup_cfg(&host->cfg, host, max_clk, min_clk); + if (ret) + return ret; host->mmc = mmc_create(&host->cfg, host); if (host->mmc == NULL) { printf("%s: mmc create fail!\n", __func__); - return -1; + return -ENOMEM; } return 0;