|
@@ -84,6 +84,7 @@ struct sdhci_msm_host {
|
|
|
struct clk *pclk; /* SDHC peripheral bus clock */
|
|
|
struct clk *bus_clk; /* SDHC bus voter clock */
|
|
|
struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/
|
|
|
+ unsigned long clk_rate;
|
|
|
struct mmc_host *mmc;
|
|
|
bool use_14lpp_dll_reset;
|
|
|
};
|
|
@@ -571,6 +572,69 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
|
|
|
return SDHCI_MSM_MIN_CLOCK;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * __sdhci_msm_set_clock - sdhci_msm clock control.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * MSM controller does not use internal divider and
|
|
|
+ * instead directly control the GCC clock as per
|
|
|
+ * HW recommendation.
|
|
|
+ **/
|
|
|
+void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
|
|
+{
|
|
|
+ u16 clk;
|
|
|
+ /*
|
|
|
+ * Keep actual_clock as zero -
|
|
|
+ * - since there is no divider used so no need of having actual_clock.
|
|
|
+ * - MSM controller uses SDCLK for data timeout calculation. If
|
|
|
+ * actual_clock is zero, host->clock is taken for calculation.
|
|
|
+ */
|
|
|
+ host->mmc->actual_clock = 0;
|
|
|
+
|
|
|
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
|
|
+
|
|
|
+ if (clock == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * MSM controller do not use clock divider.
|
|
|
+ * Thus read SDHCI_CLOCK_CONTROL and only enable
|
|
|
+ * clock with no divider value programmed.
|
|
|
+ */
|
|
|
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
|
+ sdhci_enable_clk(host, clk);
|
|
|
+}
|
|
|
+
|
|
|
+/* sdhci_msm_set_clock - Called with (host->lock) spinlock held. */
|
|
|
+static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
|
|
+{
|
|
|
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
|
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ if (!clock) {
|
|
|
+ msm_host->clk_rate = clock;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_unlock_irq(&host->lock);
|
|
|
+
|
|
|
+ rc = clk_set_rate(msm_host->clk, clock);
|
|
|
+ if (rc) {
|
|
|
+ pr_err("%s: Failed to set clock at rate %u\n",
|
|
|
+ mmc_hostname(host->mmc), clock);
|
|
|
+ goto out_lock;
|
|
|
+ }
|
|
|
+ msm_host->clk_rate = clock;
|
|
|
+ pr_debug("%s: Setting clock at rate %lu\n",
|
|
|
+ mmc_hostname(host->mmc), clk_get_rate(msm_host->clk));
|
|
|
+
|
|
|
+out_lock:
|
|
|
+ spin_lock_irq(&host->lock);
|
|
|
+out:
|
|
|
+ __sdhci_msm_set_clock(host, clock);
|
|
|
+}
|
|
|
+
|
|
|
static const struct of_device_id sdhci_msm_dt_match[] = {
|
|
|
{ .compatible = "qcom,sdhci-msm-v4" },
|
|
|
{},
|
|
@@ -581,7 +645,7 @@ MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
|
|
|
static const struct sdhci_ops sdhci_msm_ops = {
|
|
|
.platform_execute_tuning = sdhci_msm_execute_tuning,
|
|
|
.reset = sdhci_reset,
|
|
|
- .set_clock = sdhci_set_clock,
|
|
|
+ .set_clock = sdhci_msm_set_clock,
|
|
|
.get_min_clock = sdhci_msm_get_min_clock,
|
|
|
.get_max_clock = sdhci_msm_get_max_clock,
|
|
|
.set_bus_width = sdhci_set_bus_width,
|