diff mbox

[v2,2/9] mmc: tmio, sh_mobile_sdhi: Add support for variable input clock frequency

Message ID 1459525479-20842-3-git-send-email-wsa@the-dreams.de (mailing list archive)
State New, archived
Headers show

Commit Message

Wolfram Sang April 1, 2016, 3:44 p.m. UTC
From: Ben Hutchings <ben.hutchings@codethink.co.uk>

Currently tmio_mmc assumes that the input clock frequency is fixed and
only its own clock divider can be changed.  This is not true in the
case of sh_mobile_sdhi; we can use the clock API to change it.

In tmio_mmc:
- Delegate setting of f_min from tmio to the clk_enable operation (if
  implemented), as it can be smaller than f_max / 512
- Add an optional clk_update operation called from tmio_mmc_set_clock()
  that updates the input clock frequency
- Rename tmio_mmc_clk_update() to tmio_mmc_clk_enable(), to avoid
  confusion with the clk_update operation

In sh_mobile_sdhi:
- Make the setting of f_max conditional; it should be set through the
  max-frequency property in the device tree in future
- Set f_min based on the input clock's minimum frequency
- Implement the clk_update operation, selecting the best input clock
  frequency for the bus frequency that's wanted

sh_mobile_sdhi_clk_update() is loosely based on Kuninori Morimoto's work
in sh_mmcif.

Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 drivers/mmc/host/sh_mobile_sdhi.c | 56 +++++++++++++++++++++++++++++++++++++--
 drivers/mmc/host/tmio_mmc.h       |  2 ++
 drivers/mmc/host/tmio_mmc_pio.c   | 24 +++++++----------
 3 files changed, 66 insertions(+), 16 deletions(-)

Comments

Geert Uytterhoeven April 12, 2016, 12:55 p.m. UTC | #1
On Fri, Apr 1, 2016 at 5:44 PM, Wolfram Sang <wsa@the-dreams.de> wrote:
> From: Ben Hutchings <ben.hutchings@codethink.co.uk>
>
> Currently tmio_mmc assumes that the input clock frequency is fixed and
> only its own clock divider can be changed.  This is not true in the
> case of sh_mobile_sdhi; we can use the clock API to change it.
>
> In tmio_mmc:
> - Delegate setting of f_min from tmio to the clk_enable operation (if
>   implemented), as it can be smaller than f_max / 512
> - Add an optional clk_update operation called from tmio_mmc_set_clock()
>   that updates the input clock frequency
> - Rename tmio_mmc_clk_update() to tmio_mmc_clk_enable(), to avoid
>   confusion with the clk_update operation
>
> In sh_mobile_sdhi:
> - Make the setting of f_max conditional; it should be set through the
>   max-frequency property in the device tree in future
> - Set f_min based on the input clock's minimum frequency
> - Implement the clk_update operation, selecting the best input clock
>   frequency for the bus frequency that's wanted
>
> sh_mobile_sdhi_clk_update() is loosely based on Kuninori Morimoto's work
> in sh_mmcif.
>
> Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>

This is now commit 2e21101df4fe8bdc ("mmc: tmio, sh_mobile_sdhi: Add support
for variable input clock frequency") in the next branch of Ulf's mmc.git.

  1. The SDHI/MMC clocks now run much slower than before. Perhaps this is
     intentional, and a consequence of finding the best way to drive the SD
     card at the target frequency?
  2. On r8a7740, the situation is worse: the HP ("High-speed Peripheral")
     clock is also scaled down from 99 MHz to 12.375 MHz.
     As the HP clock is the parent of lots of on-chip devices, this may affect
     performance for all of them.

On r8a73a4, r8a7791, and sh73a0, the SDHI clocks are children of the pll1_div2
clocks, which are fixed.
On r8a7740, the SDHI and MMC clocks are children of the HP clock,
which is also scaled down, affecting all other siblings.

dmesg and /sys/kernel/debug/clk/clk_summary differences below.


r8a73a4/ape6evm
---------------

-sh_mobile_sdhi ee100000.sd: mmc0 base at 0xee100000 clock rate 88 MHz
+sh_mobile_sdhi ee100000.sd: mmc0 base at 0xee100000 max clock rate 88 MHz

-sh_mobile_sdhi ee120000.sd: mmc1 base at 0xee120000 clock rate 12 MHz
+sh_mobile_sdhi ee120000.sd: mmc1 base at 0xee120000 max clock rate 12 MHz

   clock           enable_cnt  prepare_cnt        rate   accuracy   phase
-------------------------------------------------------------------------

-     sdhi1ck               1            1    12500000          0 0
-        sdhi1              2            2    12500000          0 0
-     sdhi0ck               1            1    88888888          0 0
-        sdhi0              1            2    88888888          0 0
+     sdhi1ck               1            1    12698412          0 0
+        sdhi1              2            2    12698412          0 0
+     sdhi0ck               1            1    12698412          0 0
+        sdhi0              1            2    12698412          0 0


r8a7791/koelsch
---------------

-sh_mobile_sdhi ee100000.sd: mmc0 base at 0xee100000 clock rate 97 MHz
+sh_mobile_sdhi ee100000.sd: mmc0 base at 0xee100000 max clock rate 97 MHz

-sh_mobile_sdhi ee140000.sd: mmc1 base at 0xee140000 clock rate 48 MHz
+sh_mobile_sdhi ee140000.sd: mmc1 base at 0xee140000 max clock rate 48 MHz

-sh_mobile_sdhi ee160000.sd: mmc2 base at 0xee160000 clock rate 48 MHz
+sh_mobile_sdhi ee160000.sd: mmc2 base at 0xee160000 max clock rate 48 MHz


      mmc0                  0            0    12187500          0 0
         mmcif0             0            0    12187500          0 0
-     sd3                   1            1    48750000          0 0
-        sdhi2              1            2    48750000          0 0
-     sd2                   1            1    48750000          0 0
-        sdhi1              1            2    48750000          0 0
+     sd3                   1            1    12786885          0 0
+        sdhi2              1            2    12786885          0 0
+     sd2                   1            1    12786885          0 0
+        sdhi1              1            2    12786885          0 0


sh73a0/kzm9g
------------

-sh_mobile_sdhi ee100000.sd: mmc0 base at 0xee100000 clock rate 69 MHz
+sh_mobile_sdhi ee100000.sd: mmc0 base at 0xee100000 max clock rate 69 MHz

-sh_mobile_sdhi ee140000.sd: mmc1 base at 0xee140000 clock rate 69 MHz
+sh_mobile_sdhi ee140000.sd: mmc1 base at 0xee140000 max clock rate 69 MHz


-    sdhi2ck                1            1    69333333          0 0
-       sdhi2               2            2    69333333          0 0
+    sdhi2ck                1            1    12734693          0 0
+       sdhi2               2            2    12734693          0 0
     sdhi1ck                0            0    69333333          0 0
        sdhi1               0            0    69333333          0 0
-    sdhi0ck                1            1    69333333          0 0
-       sdhi0               1            2    69333333          0 0
+    sdhi0ck                1            1    12734693          0 0
+       sdhi0               1            2    12734693          0 0


r8a7740/armadillo
-----------------

-sh_mobile_sdhi e6850000.sd: mmc0 base at 0xe6850000 clock rate 99 MHz
+sh_mobile_sdhi e6850000.sd: mmc0 base at 0xe6850000 max clock rate 99 MHz

-sh_mmcif e6bd0000.mmc: Chip version 0x0003, clock rate 99MHz
+sh_mmcif e6bd0000.mmc: Chip version 0x0003, clock rate 12MHz

-   hp                      4            6    99000000          0 0
-      tpu0                 0            1    99000000          0 0
-      gether               1            1    99000000          0 0
-      mmc                  2            2    99000000          0 0
-      sdhi1                0            0    99000000          0 0
-      sdhi0                1            2    99000000          0 0
-      usbf                 0            0    99000000          0 0
-      fsi                  0            1    99000000          0 0
-      usbdmac              0            0    99000000          0 0
-      dmac3                0            0    99000000          0 0
-      dmac2                0            0    99000000          0 0
-      dmac1                0            0    99000000          0 0
-      intca                4            4    99000000          0 0
-      usphy                0            0    99000000          0 0
-      usbfunc              0            0    99000000          0 0
-      sdhi2                0            0    99000000          0 0
-      usbhost              0            0    99000000          0 0
+   hp                      4            6    12375000          0 0
+      tpu0                 0            1    12375000          0 0
+      gether               1            1    12375000          0 0
+      mmc                  2            2    12375000          0 0
+      sdhi1                0            0    12375000          0 0
+      sdhi0                1            2    12375000          0 0
+      usbf                 0            0    12375000          0 0
+      fsi                  0            1    12375000          0 0
+      usbdmac              0            0    12375000          0 0
+      dmac3                0            0    12375000          0 0
+      dmac2                0            0    12375000          0 0
+      dmac1                0            0    12375000          0 0
+      intca                4            4    12375000          0 0
+      usphy                0            0    12375000          0 0
+      usbfunc              0            0    12375000          0 0
+      sdhi2                0            0    12375000          0 0
+      usbhost              0            0    12375000          0 0

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings April 12, 2016, 4:19 p.m. UTC | #2
On Tue, 2016-04-12 at 14:55 +0200, Geert Uytterhoeven wrote:
> On Fri, Apr 1, 2016 at 5:44 PM, Wolfram Sang <wsa@the-dreams.de> wrote:
> > From: Ben Hutchings <ben.hutchings@codethink.co.uk>
> >
> > Currently tmio_mmc assumes that the input clock frequency is fixed and
> > only its own clock divider can be changed.  This is not true in the
> > case of sh_mobile_sdhi; we can use the clock API to change it.
> >
> > In tmio_mmc:
> > - Delegate setting of f_min from tmio to the clk_enable operation (if
> >   implemented), as it can be smaller than f_max / 512
> > - Add an optional clk_update operation called from tmio_mmc_set_clock()
> >   that updates the input clock frequency
> > - Rename tmio_mmc_clk_update() to tmio_mmc_clk_enable(), to avoid
> >   confusion with the clk_update operation
> >
> > In sh_mobile_sdhi:
> > - Make the setting of f_max conditional; it should be set through the
> >   max-frequency property in the device tree in future
> > - Set f_min based on the input clock's minimum frequency
> > - Implement the clk_update operation, selecting the best input clock
> >   frequency for the bus frequency that's wanted
> >
> > sh_mobile_sdhi_clk_update() is loosely based on Kuninori Morimoto's work
> > in sh_mmcif.
> >
> > Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
> > Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> 
> This is now commit 2e21101df4fe8bdc ("mmc: tmio, sh_mobile_sdhi: Add support
> for variable input clock frequency") in the next branch of Ulf's mmc.git.
> 
>   1. The SDHI/MMC clocks now run much slower than before. Perhaps this is
>      intentional, and a consequence of finding the best way to drive the SD
>      card at the target frequency?

I don't think is generally a problem.  Probably even saves a little
power.

>   2. On r8a7740, the situation is worse: the HP ("High-speed Peripheral")
>      clock is also scaled down from 99 MHz to 12.375 MHz.
>      As the HP clock is the parent of lots of on-chip devices, this may affect
>      performance for all of them.
>
> On r8a73a4, r8a7791, and sh73a0, the SDHI clocks are children of the pll1_div2
> clocks, which are fixed.
> On r8a7740, the SDHI and MMC clocks are children of the HP clock,
> which is also scaled down, affecting all other siblings.
[...]

That seems like a bug in the clock driver.  If it doesn't have
independent dividers for each clock client then it shouldn't allow any
client to change the frequency.

Ben.
diff mbox

Patch

diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 55849350202b7d..8fd1d6b29190b6 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -139,7 +139,20 @@  static int sh_mobile_sdhi_clk_enable(struct tmio_mmc_host *host)
 	if (ret < 0)
 		return ret;
 
-	mmc->f_max = clk_get_rate(priv->clk);
+	/*
+	 * The clock driver may not know what maximum frequency
+	 * actually works, so it should be set with the max-frequency
+	 * property which will already have been read to f_max.  If it
+	 * was missing, assume the current frequency is the maximum.
+	 */
+	if (!mmc->f_max)
+		mmc->f_max = clk_get_rate(priv->clk);
+
+	/*
+	 * Minimum frequency is the minimum input clock frequency
+	 * divided by our maximum divider.
+	 */
+	mmc->f_min = max(clk_round_rate(priv->clk, 1) / 512, 1L);
 
 	/* enable 16bit data access on SDBUF as default */
 	sh_mobile_sdhi_sdbuf_width(host, 16);
@@ -147,6 +160,44 @@  static int sh_mobile_sdhi_clk_enable(struct tmio_mmc_host *host)
 	return 0;
 }
 
+static unsigned int sh_mobile_sdhi_clk_update(struct tmio_mmc_host *host,
+					      unsigned int new_clock)
+{
+	struct sh_mobile_sdhi *priv = host_to_priv(host);
+	unsigned int freq, best_freq, diff_min, diff;
+	int i;
+
+	diff_min = ~0;
+	best_freq = 0;
+
+	/*
+	 * We want the bus clock to be as close as possible to, but no
+	 * greater than, new_clock.  As we can divide by 1 << i for
+	 * any i in [0, 9] we want the input clock to be as close as
+	 * possible, but no greater than, new_clock << i.
+	 */
+	for (i = min(9, ilog2(UINT_MAX / new_clock)); i >= 0; i--) {
+		freq = clk_round_rate(priv->clk, new_clock << i);
+		if (freq > (new_clock << i)) {
+			/* Too fast; look for a slightly slower option */
+			freq = clk_round_rate(priv->clk,
+					      (new_clock << i) / 4 * 3);
+			if (freq > (new_clock << i))
+				continue;
+		}
+
+		diff = new_clock - (freq >> i);
+		if (diff <= diff_min) {
+			best_freq = freq;
+			diff_min = diff;
+		}
+	}
+
+	clk_set_rate(priv->clk, best_freq);
+
+	return best_freq;
+}
+
 static void sh_mobile_sdhi_clk_disable(struct tmio_mmc_host *host)
 {
 	struct sh_mobile_sdhi *priv = host_to_priv(host);
@@ -265,6 +316,7 @@  static int sh_mobile_sdhi_probe(struct platform_device *pdev)
 	host->dma		= dma_priv;
 	host->write16_hook	= sh_mobile_sdhi_write16_hook;
 	host->clk_enable	= sh_mobile_sdhi_clk_enable;
+	host->clk_update	= sh_mobile_sdhi_clk_update;
 	host->clk_disable	= sh_mobile_sdhi_clk_disable;
 	host->multi_io_quirk	= sh_mobile_sdhi_multi_io_quirk;
 
@@ -362,7 +414,7 @@  static int sh_mobile_sdhi_probe(struct platform_device *pdev)
 		}
 	}
 
-	dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
+	dev_info(&pdev->dev, "%s base at 0x%08lx max clock rate %u MHz\n",
 		 mmc_hostname(host->mmc), (unsigned long)
 		 (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
 		 host->mmc->f_max / 1000000);
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 68fd8d791358c1..b44b5890290622 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -96,6 +96,8 @@  struct tmio_mmc_host {
 
 	int (*write16_hook)(struct tmio_mmc_host *host, int addr);
 	int (*clk_enable)(struct tmio_mmc_host *host);
+	unsigned int (*clk_update)(struct tmio_mmc_host *host,
+				   unsigned int new_clock);
 	void (*clk_disable)(struct tmio_mmc_host *host);
 	int (*multi_io_quirk)(struct mmc_card *card,
 			      unsigned int direction, int blk_size);
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index d1160156678ade..ae81b34f17a5a5 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -160,9 +160,12 @@  static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
 	u32 clk = 0, clock;
 
 	if (new_clock) {
-		for (clock = host->mmc->f_min, clk = 0x80000080;
-		     new_clock >= (clock << 1);
-		     clk >>= 1)
+		if (host->clk_update)
+			clock = host->clk_update(host, new_clock) / 512;
+		else
+			clock = host->mmc->f_min;
+
+		for (clk = 0x80000080; new_clock >= (clock << 1); clk >>= 1)
 			clock <<= 1;
 
 		/* 1/1 clock is option */
@@ -837,19 +840,12 @@  fail:
 	pm_runtime_put_autosuspend(mmc_dev(mmc));
 }
 
-static int tmio_mmc_clk_update(struct tmio_mmc_host *host)
+static int tmio_mmc_clk_enable(struct tmio_mmc_host *host)
 {
-	struct mmc_host *mmc = host->mmc;
-	int ret;
-
 	if (!host->clk_enable)
 		return -ENOTSUPP;
 
-	ret = host->clk_enable(host);
-	if (!ret)
-		mmc->f_min = mmc->f_max / 512;
-
-	return ret;
+	return host->clk_enable(host);
 }
 
 static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
@@ -1135,7 +1131,7 @@  int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
 				  mmc->caps & MMC_CAP_NONREMOVABLE ||
 				  mmc->slot.cd_irq >= 0);
 
-	if (tmio_mmc_clk_update(_host) < 0) {
+	if (tmio_mmc_clk_enable(_host) < 0) {
 		mmc->f_max = pdata->hclk;
 		mmc->f_min = mmc->f_max / 512;
 	}
@@ -1263,7 +1259,7 @@  int tmio_mmc_host_runtime_resume(struct device *dev)
 	struct tmio_mmc_host *host = mmc_priv(mmc);
 
 	tmio_mmc_reset(host);
-	tmio_mmc_clk_update(host);
+	tmio_mmc_clk_enable(host);
 
 	if (host->clk_cache) {
 		tmio_mmc_set_clock(host, host->clk_cache);