Message ID | 1479103248-9491-9-git-send-email-riteshh@codeaurora.org (mailing list archive) |
---|---|
State | Superseded, archived |
Delegated to: | Stephen Boyd |
Headers | show |
On 11/14, Ritesh Harjani wrote: > @@ -577,6 +578,90 @@ 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: > + * Implement MSM version of sdhci_set_clock. > + * This is required since 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; > + unsigned long timeout; > + > + /* > + * 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); > + > + clk |= SDHCI_CLOCK_INT_EN; > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); > + > + /* Wait max 20 ms */ > + timeout = 20; > + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) > + & SDHCI_CLOCK_INT_STABLE)) { > + if (timeout == 0) { > + pr_err("%s: Internal clock never stabilised\n", > + mmc_hostname(host->mmc)); > + return; > + } > + timeout--; > + mdelay(1); > + } > + > + clk |= SDHCI_CLOCK_CARD_EN; > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); This is almost a copy/paste of sdhci_set_clock(). Can we make sdhci_set_clock() call a __sdhci_set_clock() function that takes unsigned int clock, and also a flag indicating if we want to set the internal clock divider or not? Then we can call __sdhci_set_clock() from sdhci_set_clock() with (clock, true) as arguments and (clock, false). > +} > + > +/* 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); > + if (clock != msm_host->clk_rate) { Why do we need to check here? Can't we call clk_set_rate() Unconditionally? > + 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); > + spin_lock_irq(&host->lock); > + goto out; Or replace the above two lines with goto err; > + } > + msm_host->clk_rate = clock; > + pr_debug("%s: Setting clock at rate %lu\n", > + mmc_hostname(host->mmc), clk_get_rate(msm_host->clk)); > + } And put an err label here. > + 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" }, > {},
Hi Stephen/Adrian, On 11/15/2016 1:07 AM, Stephen Boyd wrote: > On 11/14, Ritesh Harjani wrote: >> @@ -577,6 +578,90 @@ 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: >> + * Implement MSM version of sdhci_set_clock. >> + * This is required since 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; >> + unsigned long timeout; >> + >> + /* >> + * 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); >> + >> + clk |= SDHCI_CLOCK_INT_EN; >> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); >> + >> + /* Wait max 20 ms */ >> + timeout = 20; >> + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) >> + & SDHCI_CLOCK_INT_STABLE)) { >> + if (timeout == 0) { >> + pr_err("%s: Internal clock never stabilised\n", >> + mmc_hostname(host->mmc)); >> + return; >> + } >> + timeout--; >> + mdelay(1); >> + } >> + >> + clk |= SDHCI_CLOCK_CARD_EN; >> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); > > This is almost a copy/paste of sdhci_set_clock(). Can we make > sdhci_set_clock() call a __sdhci_set_clock() function that takes > unsigned int clock, and also a flag indicating if we want to set > the internal clock divider or not? Then we can call > __sdhci_set_clock() from sdhci_set_clock() with (clock, true) as > arguments and (clock, false). Adrian, Could you please comment here ? > >> +} >> + >> +/* 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); >> + if (clock != msm_host->clk_rate) { > > Why do we need to check here? Can't we call clk_set_rate() > Unconditionally? Since it may so happen that above layers may call for ->set_clock function with same requested clock more than once, hence we cache the host->clock here. Also, since requested clock (host->clock) can be say 400Mhz but the actual pltfm supported clock would be say 384MHz. > >> + 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); >> + spin_lock_irq(&host->lock); >> + goto out; > > Or replace the above two lines with goto err; Ok, I will have another label out_lock instead of err. > >> + } >> + msm_host->clk_rate = clock; >> + pr_debug("%s: Setting clock at rate %lu\n", >> + mmc_hostname(host->mmc), clk_get_rate(msm_host->clk)); >> + } > > And put an err label here. will put the label here as 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" }, >> {}, >
On 11/15, Ritesh Harjani wrote: > On 11/15/2016 1:07 AM, Stephen Boyd wrote: > >On 11/14, Ritesh Harjani wrote: > > > >>+} > >>+ > >>+/* 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); > >>+ if (clock != msm_host->clk_rate) { > > > >Why do we need to check here? Can't we call clk_set_rate() > >Unconditionally? > Since it may so happen that above layers may call for ->set_clock > function with same requested clock more than once, hence we cache > the host->clock here. > Also, since requested clock (host->clock) can be say 400Mhz but the > actual pltfm supported clock would be say 384MHz. clk_set_rate() detects the same rate being set even after it internally rounds the rate. We're not going to touch the clk hardware if 400 is requested once but 384 is what's set and then 400 is requested again. Caching the rate here in the driver can lead to problems too if the driver is out of sync with the clk hardware state, so it's best to avoid doing anything fancy here and just let the framework handle duplicates.
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 268424c..b96a4a7 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -82,6 +82,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; }; @@ -577,6 +578,90 @@ 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: + * Implement MSM version of sdhci_set_clock. + * This is required since 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; + unsigned long timeout; + + /* + * 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); + + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + /* Wait max 20 ms */ + timeout = 20; + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { + pr_err("%s: Internal clock never stabilised\n", + mmc_hostname(host->mmc)); + return; + } + timeout--; + mdelay(1); + } + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +} + +/* 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); + if (clock != msm_host->clk_rate) { + 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); + spin_lock_irq(&host->lock); + goto out; + } + msm_host->clk_rate = clock; + pr_debug("%s: Setting clock at rate %lu\n", + mmc_hostname(host->mmc), clk_get_rate(msm_host->clk)); + } + 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" }, {}, @@ -587,7 +672,7 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) 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,