From 97bdc9cac7be74f770c8a7da5f2491e551a85194 Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Thu, 28 Feb 2013 19:50:51 +0530 Subject: mmc: sdhci: Enable clock scaling feature Add support for enabling clock scaling feature and indicate the same to MMC core layer by setting the capability MMC_CAP2_CLK_SCALE. Change-Id: I24f144d3f727160c302966888fb439b3a39a0dde Signed-off-by: Sahitya Tummala [venkatg@codeaurora.org: sdhci_set_clock is now a library function thats called from platform clock handler, make changes to address that] Signed-off-by: Venkat Gopalakrishnan [subhashj@codeaurora.org: fixed compilation error] Signed-off-by: Subhash Jadavani --- drivers/mmc/host/sdhci-msm.c | 175 ++++++++++++++++++++++++++++++++++++------- drivers/mmc/host/sdhci.c | 4 + drivers/mmc/host/sdhci.h | 2 + 3 files changed, 155 insertions(+), 26 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 217f7c34dfa8..a21dde2d694e 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -197,13 +197,14 @@ struct sdhci_msm_pltfm_data { u32 caps2; unsigned long mmc_bus_width; - u32 max_clk; struct sdhci_msm_slot_reg_data *vreg_data; bool nonremovable; struct sdhci_msm_pin_data *pin_data; u32 cpu_dma_latency_us; int status_gpio; /* card detection GPIO that is configured as IRQ */ struct sdhci_msm_bus_voting_data *voting_data; + u32 *sup_clk_table; + unsigned char sup_clk_cnt; }; struct sdhci_msm_bus_vote { @@ -228,6 +229,7 @@ struct sdhci_msm_host { struct sdhci_pltfm_data sdhci_msm_pdata; wait_queue_head_t pwr_irq_wait; struct sdhci_msm_bus_vote msm_bus_vote; + u32 clk_rate; /* Keeps track of current clock rate that is set */ }; enum vdd_io_level { @@ -547,9 +549,18 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) int size = sizeof(tuning_block_64); /* Tuning pattern size in bytes */ int rc; struct mmc_host *mmc = host->mmc; + struct mmc_ios ios = host->mmc->ios; + + /* + * Tuning is required for SDR104 and HS200 cards and if clock frequency + * is greater than 100MHz in these modes. + */ + if (host->clock <= (100 * 1000 * 1000) || + !(ios.timing == MMC_TIMING_MMC_HS200 || + ios.timing == MMC_TIMING_UHS_SDR104)) + return 0; pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__); - /* Tuning is only required for SDR104 modes */ spin_lock_irqsave(&host->lock, flags); if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) && @@ -1071,6 +1082,8 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev) u32 bus_width = 0; u32 cpu_dma_latency; int len, i; + int clk_table_len; + u32 *clk_table = NULL; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { @@ -1094,6 +1107,18 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev) &cpu_dma_latency)) pdata->cpu_dma_latency_us = cpu_dma_latency; + if (sdhci_msm_dt_get_array(dev, "qcom,clk-rates", + &clk_table, &clk_table_len, 0)) { + dev_err(dev, "failed parsing supported clock rates\n"); + goto out; + } + if (!clk_table || !clk_table_len) { + dev_err(dev, "Invalid clock table\n"); + goto out; + } + pdata->sup_clk_table = clk_table; + pdata->sup_clk_cnt = clk_table_len; + pdata->vreg_data = devm_kzalloc(dev, sizeof(struct sdhci_msm_slot_reg_data), GFP_KERNEL); @@ -1119,8 +1144,6 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev) goto out; } - of_property_read_u32(np, "qcom,max-clk-rate", &pdata->max_clk); - len = of_property_count_strings(np, "qcom,bus-speed-mode"); for (i = 0; i < len; i++) { @@ -1781,16 +1804,58 @@ static unsigned int sdhci_msm_max_segs(void) return SDHCI_MSM_MAX_SEGMENTS; } -void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) +static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) { - int rc; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; - unsigned long flags; - if (clock && !atomic_read(&msm_host->clks_on)) { - pr_debug("%s: request to enable clock at rate %u\n", - mmc_hostname(host->mmc), clock); + return msm_host->pdata->sup_clk_table[0]; +} + +static unsigned int sdhci_msm_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + int max_clk_index = msm_host->pdata->sup_clk_cnt; + + return msm_host->pdata->sup_clk_table[max_clk_index - 1]; +} + +static unsigned int sdhci_msm_get_sup_clk_rate(struct sdhci_host *host, + u32 req_clk) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + unsigned int sel_clk = -1; + unsigned char cnt; + + if (req_clk < sdhci_msm_get_min_clock(host)) { + sel_clk = sdhci_msm_get_min_clock(host); + return sel_clk; + } + + for (cnt = 0; cnt < msm_host->pdata->sup_clk_cnt; cnt++) { + if (msm_host->pdata->sup_clk_table[cnt] > req_clk) { + break; + } else if (msm_host->pdata->sup_clk_table[cnt] == req_clk) { + sel_clk = msm_host->pdata->sup_clk_table[cnt]; + break; + } else { + sel_clk = msm_host->pdata->sup_clk_table[cnt]; + } + } + return sel_clk; +} + +static int sdhci_msm_prepare_clocks(struct sdhci_host *host, bool enable) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + int rc = 0; + + if (enable && !atomic_read(&msm_host->clks_on)) { + pr_debug("%s: request to enable clocks\n", + mmc_hostname(host->mmc)); if (!IS_ERR_OR_NULL(msm_host->bus_clk)) { rc = clk_prepare_enable(msm_host->bus_clk); if (rc) { @@ -1814,9 +1879,8 @@ void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) goto disable_pclk; } mb(); - atomic_set(&msm_host->clks_on, 1); - } else if (!clock && atomic_read(&msm_host->clks_on)) { + } else if (!enable && atomic_read(&msm_host->clks_on)) { pr_debug("%s: request to disable clocks\n", mmc_hostname(host->mmc)); sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); @@ -1826,11 +1890,8 @@ void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) clk_disable_unprepare(msm_host->pclk); if (!IS_ERR_OR_NULL(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); - atomic_set(&msm_host->clks_on, 0); } - spin_lock_irqsave(&host->lock, flags); - host->clock = clock; - spin_unlock_irqrestore(&host->lock, flags); + atomic_set(&msm_host->clks_on, enable); goto out; disable_pclk: if (!IS_ERR_OR_NULL(msm_host->pclk)) @@ -1839,7 +1900,54 @@ disable_bus_clk: if (!IS_ERR_OR_NULL(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); out: - return; + return rc; +} + +static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) +{ + int rc; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + struct mmc_ios curr_ios = host->mmc->ios; + u32 sup_clock, ddr_clock; + + if (!clock) { + sdhci_msm_prepare_clocks(host, false); + host->clock = clock; + goto out; + } + + rc = sdhci_msm_prepare_clocks(host, true); + if (rc) + goto out; + + sup_clock = sdhci_msm_get_sup_clk_rate(host, clock); + if (curr_ios.timing == MMC_TIMING_UHS_DDR50) { + /* + * The SDHC requires internal clock frequency to be double the + * actual clock that will be set for DDR mode. The controller + * uses the faster clock(100MHz) for some of its parts and send + * the actual required clock (50MHz) to the card. + */ + ddr_clock = clock * 2; + sup_clock = sdhci_msm_get_sup_clk_rate(host, + ddr_clock); + } + if (sup_clock != msm_host->clk_rate) { + pr_debug("%s: %s: setting clk rate to %u\n", + mmc_hostname(host->mmc), __func__, sup_clock); + rc = clk_set_rate(msm_host->clk, sup_clock); + if (rc) { + pr_err("%s: %s: Failed to set rate %u for host-clk : %d\n", + mmc_hostname(host->mmc), __func__, + sup_clock, rc); + goto out; + } + msm_host->clk_rate = sup_clock; + host->clock = clock; + } +out: + sdhci_set_clock(host, clock); } static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, @@ -1862,6 +1970,17 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, ctrl_2 |= SDHCI_CTRL_UHS_SDR104; else if (uhs == MMC_TIMING_UHS_DDR50) ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + /* + * When clock frquency is less than 100MHz, the feedback clock must be + * provided and DLL must not be used so that tuning can be skipped. To + * provide feedback clock, the mode selection can be any value less + * than 3'b011 in bits [2:0] of HOST CONTROL2 register. + */ + if (host->clock <= (100 * 1000 * 1000) && + (uhs == MMC_TIMING_MMC_HS200 || + uhs == MMC_TIMING_UHS_SDR104)) + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } @@ -1874,6 +1993,8 @@ static struct sdhci_ops sdhci_msm_ops = { .get_max_segments = sdhci_msm_max_segs, .set_clock = sdhci_msm_set_clock, .platform_bus_voting = sdhci_msm_bus_voting, + .get_min_clock = sdhci_msm_get_min_clock, + .get_max_clock = sdhci_msm_get_max_clock, }; static int sdhci_msm_probe(struct platform_device *pdev) @@ -1951,7 +2072,15 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (ret) goto pclk_disable; + /* Set to the minimum supported clock frequency */ + ret = clk_set_rate(msm_host->clk, sdhci_msm_get_min_clock(host)); + if (ret) { + dev_err(&pdev->dev, "MClk rate set failed (%d)\n", ret); + goto clk_disable; + } + msm_host->clk_rate = sdhci_msm_get_min_clock(host); atomic_set(&msm_host->clks_on, 1); + /* Setup regulators */ ret = sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, true); if (ret) { @@ -1983,6 +2112,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) */ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE; + host->quirks |= SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; + host->quirks2 |= SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK; host->quirks2 |= SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING; host_version = readl_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); @@ -2037,6 +2168,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->mmc->caps2 |= MMC_CAP2_CACHE_CTRL; msm_host->mmc->caps2 |= MMC_CAP2_INIT_BKOPS; msm_host->mmc->caps2 |= MMC_CAP2_POWEROFF_NOTIFY; + msm_host->mmc->caps2 |= MMC_CAP2_CLK_SCALE; if (msm_host->pdata->nonremovable) msm_host->mmc->caps |= MMC_CAP_NONREMOVABLE; @@ -2074,15 +2206,6 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto free_cd_gpio; } - /* Set core clk rate, optionally override from dts */ - if (msm_host->pdata->max_clk) - host->max_clk = msm_host->pdata->max_clk; - ret = clk_set_rate(msm_host->clk, host->max_clk); - if (ret) { - dev_err(&pdev->dev, "MClk rate set failed (%d)\n", ret); - goto remove_host; - } - msm_host->msm_bus_vote.max_bus_bw.show = show_sdhci_max_bus_bw; msm_host->msm_bus_vote.max_bus_bw.store = store_sdhci_max_bus_bw; sysfs_attr_init(&msm_host->msm_bus_vote.max_bus_bw.attr); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2cc859cdffcf..5adddc4b30ce 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1253,6 +1253,10 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) clock_set: if (real_div) host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; + + if (host->quirks2 & SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK) + div = 0; + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 9f1a25241220..1329531767ec 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -434,6 +434,8 @@ struct sdhci_host { /* Ignore CMD CRC errors for tuning commands */ #define SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING (1<<6) +#define SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK (1<<19) + int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ -- cgit v1.2.3