diff mbox

[v7,08/14] mmc: sdhci-msm: Implement set_clock callback for sdhci-msm

Message ID 1479103248-9491-9-git-send-email-riteshh@codeaurora.org (mailing list archive)
State New, archived
Headers show

Commit Message

Ritesh Harjani Nov. 14, 2016, 6 a.m. UTC
sdhci-msm controller may have different clk-rates for each
bus speed mode. Thus implement set_clock callback for
sdhci-msm driver.

Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/host/sdhci-msm.c | 87 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 86 insertions(+), 1 deletion(-)

Comments

Stephen Boyd Nov. 14, 2016, 7:37 p.m. UTC | #1
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" },
>  	{},
Ritesh Harjani Nov. 15, 2016, 5:10 a.m. UTC | #2
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" },
>>  	{},
>
Stephen Boyd Nov. 15, 2016, 7:27 p.m. UTC | #3
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 mbox

Patch

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,