All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype
@ 2016-08-12 16:38 Geert Uytterhoeven
  2016-08-12 16:38 ` [PATCH/PROTO 1/9 common] spi: sh-msiof: Add support for R-Car H3 Geert Uytterhoeven
                   ` (9 more replies)
  0 siblings, 10 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

	Hi all,

On R-Car H3, the various MSIOF instances (used for SPI) have a common
parent clock. This mso clock is a programmable "DIV6" clock (divider
1 - 64). Its rate lies in the range 12.5 to 800 MHz, or 6.25 to 400 MHz
(depending on the main crystal).
After boot up, the default configuration is the lowest possible rate,
which leads to bad performance with devices desiring a higher transfer
rate.

MSIOF clock tree:

    pll1/4 (800 or 400 MHz)
      mso clock
	msiof0 clock (type MSTP = gate + status)
	  msiof0 internal divider (divider range 1 - 1024, with holes)
	msiof1 clock (type MSTP)
	  msiof1 internal divider
	msiof2 clock (type MSTP)
	  msiof2 internal divider
	msiof3 clock (type MSTP)
	  msiof3 internal divider

To optimize performance, we want to control the mso parent clock.

  - Higher parent clock means:
      + Better performance for devices wanting high target rates,
      + More accurate target rates in MSIOF driver,
      - More power consumption (doesn't matter that much with R-Car),
      - Cannot support devices needing real low target rates.
  - Lower parent clock means:
      + Less power consumption (doesn't matter that much with R-Car),
      + Can support all devices,
      - Worse performance for devices wanting high target rates,
      - Less accurate target rates, decreasing performance.

Configuring the mso parent clock rate can either be done statically, or
dynamically. In both cases, this must be based on individual device
requirements. Note that this is not specific to SPI.

SPI specific notes:
  - The SPI master knows about SPI slave device maximum frequencies only
    when spi_master.setup() is called,
  - SPI slave device maximum frequencies may change at runtime (through
    calling spi_setup(), esp. for spidev),
  - Currently the MSIOF driver does not set
    spi_master.{min,max}_speed_hz. However, setting it means the SPI
    core will never call spi_master.setup() with a rate outside this
    range, and thus we will never be informed of such rates.  Hence if
    we decide to set this, and we want dynamic configuration, we should
    fill in the real minimum and maximum supported by the hardware,
    regardless of the current parent clock rate.

In this prototype, I'm offering 3 solutions to control the mso parent
clock rate.

  - Option 1: Static configuration through the "assigned-clocks" and
    assigned-clock-rates DT properties.

  - Option 2: Dynamic configuration, by changing the mso clock through
    the msiofX module clocks from the MSIOF driver.

  - Option 2: Dynamic configuration, by changing a dummy clk-divider,
    which mimics the capabilities of the MSIOF internal divider.

More information can be found in the individual patches for the various
options.

For your convenience, these patches are also available in the following
branches of my renesas-drivers git repository at
https://git.kernel.org/cgit/linux/kernel/git/geert/renesas-drivers.git:
  - topic/r8a7795-msiof-parent-clock-prototype-core
  - topic/r8a7795-msiof-parent-clock-prototype-dt
  - topic/r8a7795-msiof-parent-clock-prototype-option-1
  - topic/r8a7795-msiof-parent-clock-prototype-option-2
  - topic/r8a7795-msiof-parent-clock-prototype-option-3

Warning:
  - None of these patches is intended to be applied as-is!
  - R-Car H3 ES1.0 has a hardware bug preventing MSIOF from being used
    with real SPI devices. However, this does not affect the generation
    of clock signals on the SPI bus.

Conclusion, based on the observations for the various options:
  The mso clock frequency cannot be changed while the mso clock is
  enabled. However, Runtime PM may not have disabled it yet due its
  asynchronous nature, leading to subtle failures. Add to this the added
  complexity for options 2 and 3, and their still needed improvements, I
  think static configuration in DT (option 1) is the best solution.

Thanks for your comments!

Geert Uytterhoeven (9):
  [common] spi: sh-msiof: Add support for R-Car H3
  [common] spi: sh-msiof: Print max and transfer frequency
  [common] arm64: dts: r8a7795: Add all MSIOF nodes
  [common] arm64: dts: salvator-x: Add dummy MSIOF SPI slave devices
  [option 1] arm64: dts: salvator-x: Configure MSIOF parent clock
  [option 2/3] spi: sh-msiof: Add clock notifier to enforce valid parent
    clock
  [option 2] spi: sh-msiof: Configure MSIOF parent clock
  [option 3] clk: divider: Add hack to support dummy clocks
  [option 3] spi: sh-msiof: Configure MSIOF parent clock

 Documentation/devicetree/bindings/spi/sh-msiof.txt |   1 +
 arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts |  79 ++++
 arch/arm64/boot/dts/renesas/r8a7795.dtsi           |  52 +++
 drivers/clk/clk-divider.c                          |  33 ++-
 drivers/spi/spi-sh-msiof.c                         | 355 +++++++++++++++++++++
 include/linux/clk-provider.h                       |   1 +
 4 files changed, 508 insertions(+), 13 deletions(-)

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

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 1/9 common] spi: sh-msiof: Add support for R-Car H3
  2016-08-12 16:38 [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Geert Uytterhoeven
@ 2016-08-12 16:38 ` Geert Uytterhoeven
  2016-08-12 16:38 ` [PATCH/PROTO 2/9 common] spi: sh-msiof: Print max and transfer frequency Geert Uytterhoeven
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

Add support for MSIOF in r8a7795 (R-Car H3).

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Not intended for upstream merge.

R-Car H3 ES1.0 has a hardware bug preventing MSIOF from being used with
real SPI devices. However, this does not affect the generation of clock
signals on the SPI bus.
---
 Documentation/devicetree/bindings/spi/sh-msiof.txt | 1 +
 drivers/spi/spi-sh-msiof.c                         | 1 +
 2 files changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt
index aa005c1d10d95756..e4ba96cb45ffff31 100644
--- a/Documentation/devicetree/bindings/spi/sh-msiof.txt
+++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt
@@ -10,6 +10,7 @@ Required properties:
 			 "renesas,msiof-r8a7792" (R-Car V2H)
 			 "renesas,msiof-r8a7793" (R-Car M2-N)
 			 "renesas,msiof-r8a7794" (R-Car E2)
+			 "renesas,msiof-r8a7795" (R-Car H3)
 			 "renesas,msiof-sh73a0" (SH-Mobile AG5)
 - reg                  : A list of offsets and lengths of the register sets for
 			 the device.
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 6852552a32ef7457..ffa5efdf266a8276 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -996,6 +996,7 @@ static const struct of_device_id sh_msiof_match[] = {
 	{ .compatible = "renesas,msiof-r8a7792",   .data = &r8a779x_data },
 	{ .compatible = "renesas,msiof-r8a7793",   .data = &r8a779x_data },
 	{ .compatible = "renesas,msiof-r8a7794",   .data = &r8a779x_data },
+	{ .compatible = "renesas,msiof-r8a7795",   .data = &r8a779x_data },
 	{},
 };
 MODULE_DEVICE_TABLE(of, sh_msiof_match);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 2/9 common] spi: sh-msiof: Print max and transfer frequency
  2016-08-12 16:38 [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Geert Uytterhoeven
  2016-08-12 16:38 ` [PATCH/PROTO 1/9 common] spi: sh-msiof: Add support for R-Car H3 Geert Uytterhoeven
@ 2016-08-12 16:38 ` Geert Uytterhoeven
  2016-08-12 16:38 ` [PATCH/PROTO 3/9 common] arm64: dts: r8a7795: Add all MSIOF nodes Geert Uytterhoeven
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Not intended for upstream merge.
---
 drivers/spi/spi-sh-msiof.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index ffa5efdf266a8276..72f80a088cddd395 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -272,6 +272,13 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
 	k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_div_table) - 1);
 
 	scr = sh_msiof_spi_div_table[k].brdv | SCR_BRPS(brps);
+	dev_info(&p->pdev->dev, "clk at %lu Hz, brps = %u, brdv = %u%s\n",
+		 parent_rate, brps, sh_msiof_spi_div_table[k].div,
+		 ((sh_msiof_spi_div_table[k].div == 1 && brps > 2) ||
+		  (brps > 32)) ? " INVALID!" : "");
+	dev_info(&p->pdev->dev, "wanted: %u Hz, actual: %lu Hz\n", spi_hz,
+		 parent_rate / sh_msiof_spi_div_table[k].div / brps);
+
 	sh_msiof_write(p, TSCR, scr);
 	if (!(p->master->flags & SPI_MASTER_MUST_TX))
 		sh_msiof_write(p, RSCR, scr);
@@ -523,6 +530,16 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 {
 	struct device_node	*np = spi->master->dev.of_node;
 	struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
+	u32 max_speed_hz, min_speed_hz;
+	unsigned long rate;
+
+	rate = clk_get_rate(p->clk);
+	max_speed_hz = rate;
+	min_speed_hz = rate / 1024;
+
+	dev_info(&p->pdev->dev,
+		 "%s: master speed min %u max %u, device speed max = %u\n",
+		 __func__, min_speed_hz, max_speed_hz, spi->max_speed_hz);
 
 	pm_runtime_get_sync(&p->pdev->dev);
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 3/9 common] arm64: dts: r8a7795: Add all MSIOF nodes
  2016-08-12 16:38 [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Geert Uytterhoeven
  2016-08-12 16:38 ` [PATCH/PROTO 1/9 common] spi: sh-msiof: Add support for R-Car H3 Geert Uytterhoeven
  2016-08-12 16:38 ` [PATCH/PROTO 2/9 common] spi: sh-msiof: Print max and transfer frequency Geert Uytterhoeven
@ 2016-08-12 16:38 ` Geert Uytterhoeven
  2016-08-12 16:38   ` Geert Uytterhoeven
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

Add the device nodes for all MSIOF SPI controllers, incl. clocks, clock
domain, and dma properties.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Not intended for upstream merge.

R-Car H3 ES1.0 has a hardware bug preventing MSIOF from being used with
real SPI devices. However, this does not affect the generation of clock
signals on the SPI bus.

v2:
  - Rebased,
  - Switch to final CPG/MSSR bindings,
  - Change one-line summary prefix to match current arm-soc practices.
---
 arch/arm64/boot/dts/renesas/r8a7795.dtsi | 52 ++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
index 80af0ba68750f346..41549faec07bae3d 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
@@ -838,6 +838,58 @@
 			status = "disabled";
 		};
 
+		msiof0: spi@e6e90000 {
+			compatible = "renesas,msiof-r8a7795";
+			reg = <0 0xe6e90000 0 0x0064>;
+			interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cpg CPG_MOD 211>;
+			dmas = <&dmac1 0x41>, <&dmac1 0x40>;
+			dma-names = "tx", "rx";
+			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+
+		msiof1: spi@e6ea0000 {
+			compatible = "renesas,msiof-r8a7795";
+			reg = <0 0xe6ea0000 0 0x0064>;
+			interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cpg CPG_MOD 210>;
+			dmas = <&dmac1 0x43>, <&dmac1 0x42>;
+			dma-names = "tx", "rx";
+			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+
+		msiof2: spi@e6c00000 {
+			compatible = "renesas,msiof-r8a7795";
+			reg = <0 0xe6c00000 0 0x0064>;
+			interrupts = <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cpg CPG_MOD 209>;
+			dmas = <&dmac0 0x45>, <&dmac0 0x44>;
+			dma-names = "tx", "rx";
+			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+
+		msiof3: spi@e6c10000 {
+			compatible = "renesas,msiof-r8a7795";
+			reg = <0 0xe6c10000 0 0x0064>;
+			interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cpg CPG_MOD 208>;
+			dmas = <&dmac0 0x47>, <&dmac0 0x46>;
+			dma-names = "tx", "rx";
+			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+
 		scif0: serial@e6e60000 {
 			compatible = "renesas,scif-r8a7795",
 				     "renesas,rcar-gen3-scif", "renesas,scif";
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 4/9 common] arm64: dts: salvator-x: Add dummy MSIOF SPI slave devices
@ 2016-08-12 16:38   ` Geert Uytterhoeven
  0 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

Declares three SPI slave devices on three different MSIOF instances:
  - high-speed (30 MHz),
  - medium-speed (1 MHz),
  - low-speed (20 kHz).

The maximum frequencies are chosen such that the optimal parent clock
frequency for the high-speed device is outside of the allowed range for
the low-speed device.

Reorder the devices by changing the "&msiofX" references to investigate
different probe order.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Not intended for upstream merge.
---
 arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
index 4ef4a712671b2579..9040a691b8b61887 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
+++ b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
@@ -45,6 +45,10 @@
 		serial2 = &scif3;
 		serial3 = &hscif3;
 		ethernet0 = &avb;
+		spi0 = &msiof0;
+		spi1 = &msiof1;
+		spi2 = &msiof2;
+		spi3 = &msiof3;
 	};
 
 	chosen {
@@ -182,6 +186,24 @@
 		function = "avb";
 	};
 
+	msiof0_pins: msiof0 {
+		groups = "msiof0_clk", "msiof0_sync", "msiof0_rxd",
+			 "msiof0_txd";
+		function = "msiof0";
+	};
+
+	msiof2_pins: msiof2 {
+		groups = "msiof2_clk_b", "msiof2_sync_b", "msiof2_rxd_b",
+			 "msiof2_txd_b";
+		function = "msiof2";
+	};
+
+	msiof3_pins: msiof3 {
+		groups = "msiof3_clk_a", "msiof3_sync_a", "msiof3_rxd_a",
+			 "msiof3_txd_a";
+		function = "msiof3";
+	};
+
 	sdhi0_pins: sd0 {
 		groups = "sdhi0_data4", "sdhi0_ctrl";
 		function = "sdhi0";
@@ -455,3 +477,55 @@
 &pciec1 {
 	status = "okay";
 };
+
+/* Dummy high-speed SPI slave device */
+&msiof0 {
+	pinctrl-0 = <&msiof0_pins>;
+	pinctrl-names = "default";
+
+	status = "okay";
+
+	flash@0 {
+		compatible = "jedec,spi-nor";
+		//compatible = "spidev";
+		reg = <0>;
+
+		spi-max-frequency = <30000000>;
+	};
+};
+
+/* Dummy medium-speed SPI slave device */
+&msiof2 {
+	pinctrl-0 = <&msiof2_pins>;
+	pinctrl-names = "default";
+
+	status = "okay";
+
+	spidev@0 {
+		//compatible = "jedec,spi-nor";
+		compatible = "spidev";
+		reg = <0>;
+
+		spi-max-frequency = <1000000>;
+	};
+};
+
+/* Dummy low-speed SPI slave device */
+&msiof3 {
+	pinctrl-0 = <&msiof3_pins>;
+	pinctrl-names = "default";
+
+	status = "okay";
+
+	gpio@0 {
+		compatible = "fairchild,74hc595";
+		//compatible = "jedec,spi-nor";
+		//compatible = "spidev";
+		reg = <0>;
+
+		gpio-controller;
+		#gpio-cells = <2>;
+		registers-number = <2>;
+		spi-max-frequency = <20000>;
+	};
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 4/9 common] arm64: dts: salvator-x: Add dummy MSIOF SPI slave devices
@ 2016-08-12 16:38   ` Geert Uytterhoeven
  0 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven

Declares three SPI slave devices on three different MSIOF instances:
  - high-speed (30 MHz),
  - medium-speed (1 MHz),
  - low-speed (20 kHz).

The maximum frequencies are chosen such that the optimal parent clock
frequency for the high-speed device is outside of the allowed range for
the low-speed device.

Reorder the devices by changing the "&msiofX" references to investigate
different probe order.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
Not intended for upstream merge.
---
 arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
index 4ef4a712671b2579..9040a691b8b61887 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
+++ b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
@@ -45,6 +45,10 @@
 		serial2 = &scif3;
 		serial3 = &hscif3;
 		ethernet0 = &avb;
+		spi0 = &msiof0;
+		spi1 = &msiof1;
+		spi2 = &msiof2;
+		spi3 = &msiof3;
 	};
 
 	chosen {
@@ -182,6 +186,24 @@
 		function = "avb";
 	};
 
+	msiof0_pins: msiof0 {
+		groups = "msiof0_clk", "msiof0_sync", "msiof0_rxd",
+			 "msiof0_txd";
+		function = "msiof0";
+	};
+
+	msiof2_pins: msiof2 {
+		groups = "msiof2_clk_b", "msiof2_sync_b", "msiof2_rxd_b",
+			 "msiof2_txd_b";
+		function = "msiof2";
+	};
+
+	msiof3_pins: msiof3 {
+		groups = "msiof3_clk_a", "msiof3_sync_a", "msiof3_rxd_a",
+			 "msiof3_txd_a";
+		function = "msiof3";
+	};
+
 	sdhi0_pins: sd0 {
 		groups = "sdhi0_data4", "sdhi0_ctrl";
 		function = "sdhi0";
@@ -455,3 +477,55 @@
 &pciec1 {
 	status = "okay";
 };
+
+/* Dummy high-speed SPI slave device */
+&msiof0 {
+	pinctrl-0 = <&msiof0_pins>;
+	pinctrl-names = "default";
+
+	status = "okay";
+
+	flash@0 {
+		compatible = "jedec,spi-nor";
+		//compatible = "spidev";
+		reg = <0>;
+
+		spi-max-frequency = <30000000>;
+	};
+};
+
+/* Dummy medium-speed SPI slave device */
+&msiof2 {
+	pinctrl-0 = <&msiof2_pins>;
+	pinctrl-names = "default";
+
+	status = "okay";
+
+	spidev@0 {
+		//compatible = "jedec,spi-nor";
+		compatible = "spidev";
+		reg = <0>;
+
+		spi-max-frequency = <1000000>;
+	};
+};
+
+/* Dummy low-speed SPI slave device */
+&msiof3 {
+	pinctrl-0 = <&msiof3_pins>;
+	pinctrl-names = "default";
+
+	status = "okay";
+
+	gpio@0 {
+		compatible = "fairchild,74hc595";
+		//compatible = "jedec,spi-nor";
+		//compatible = "spidev";
+		reg = <0>;
+
+		gpio-controller;
+		#gpio-cells = <2>;
+		registers-number = <2>;
+		spi-max-frequency = <20000>;
+	};
+};
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 5/9 option 1] arm64: dts: salvator-x: Configure MSIOF parent clock
  2016-08-12 16:38 [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Geert Uytterhoeven
                   ` (3 preceding siblings ...)
  2016-08-12 16:38   ` Geert Uytterhoeven
@ 2016-08-12 16:38 ` Geert Uytterhoeven
  2016-08-12 16:38 ` [PATCH/PROTO 6/9 option 2/3] spi: sh-msiof: Add clock notifier to enforce valid " Geert Uytterhoeven
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

Add assigned-clocks and assigned-clock-rates DT properties to the cpg
device node, to configure the MSIOF parent clock for 20 MHz.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Not intended for upstream merge.

Note that these properties could also be added to an individual MSIOF
device node. As all MSIOF instances share the same parent clock, this
is not a good idea:
  - MSIOF instances initialized before the one with the properties will
    use the old (default) parent clock rate.
    When the MSIOF instances with the properties is initialized, the
    parent clock rate will change. In the absence of a clock notifier,
    no one will notice (at this point).
    All subsequent operations will use the new parent clock rate.
    As the MSIOF driver use clk_get_rate() before setting up each
    transfer, it will automatically adapt.
    However, spi_master.{min,max}_speed_hz are not updated (note that
    currently they are not set by the MSIOF driver anyway!).
  - If you add conflicting properties to multiple msiof nodes, they will
    still be used, but override each other, determined by probe order.

Cfr. Documentation/devicetree/bindings/clock/clock-bindings.txt

   "Configuring a clock's parent and rate through the device node that
    consumes the clock can be done only for clocks that have a single
    user. Specifying conflicting parent or rate configuration in
    multiple consumer nodes for a shared clock is forbidden."

and

   "Configuration of common clocks, which affect multiple consumer
    devices can be similarly specified in the clock provider node."
---
 arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
index 9040a691b8b61887..61c9f651adbdfcad 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
+++ b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
@@ -143,6 +143,11 @@
 	};
 };
 
+&cpg {
+	assigned-clocks = <&cpg CPG_CORE R8A7795_CLK_MSO>;
+	assigned-clock-rates = <20000000>;
+};
+
 &extal_clk {
 	clock-frequency = <16666666>;
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 6/9 option 2/3] spi: sh-msiof: Add clock notifier to enforce valid parent clock
  2016-08-12 16:38 [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Geert Uytterhoeven
                   ` (4 preceding siblings ...)
  2016-08-12 16:38 ` [PATCH/PROTO 5/9 option 1] arm64: dts: salvator-x: Configure MSIOF parent clock Geert Uytterhoeven
@ 2016-08-12 16:38 ` Geert Uytterhoeven
  2016-08-12 16:38   ` Geert Uytterhoeven
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

Use clock notifiers to avoid changing the clock rate to an out-of-range
setting for already configured devices. This will allow dynamic
configuration of the MSIOF parent clock.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Not intended for upstream merge.
---
 drivers/spi/spi-sh-msiof.c | 65 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 72f80a088cddd395..656eaa4d03ed497b 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -53,6 +53,8 @@ struct sh_msiof_spi_priv {
 	void *rx_dma_page;
 	dma_addr_t tx_dma_addr;
 	dma_addr_t rx_dma_addr;
+	struct notifier_block clk_rate_change_nb;
+	u32 dev_max_speed_hz;
 };
 
 #define TMDR1	0x00	/* Transmit Mode Register 1 */
@@ -180,6 +182,10 @@ struct sh_msiof_spi_priv {
 #define IER_RFOVFE	0x00000008 /* Receive FIFO Overflow Enable */
 
 
+/* Maximum internal divider */
+#define MAX_DIV		1024
+
+
 static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
 {
 	switch (reg_offs) {
@@ -253,7 +259,7 @@ static struct {
 static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
 				      unsigned long parent_rate, u32 spi_hz)
 {
-	unsigned long div = 1024;
+	unsigned long div = MAX_DIV;
 	u32 brps, scr;
 	size_t k;
 
@@ -526,6 +532,53 @@ static void sh_msiof_spi_read_fifo_s32u(struct sh_msiof_spi_priv *p,
 		put_unaligned(swab32(sh_msiof_read(p, RFDR) >> fs), &buf_32[k]);
 }
 
+static int sh_msiof_clk_notifier_cb(struct notifier_block *nb,
+				    unsigned long event, void *data)
+{
+	struct clk_notifier_data *ndata = data;
+	struct sh_msiof_spi_priv *p =
+		container_of(nb, struct sh_msiof_spi_priv, clk_rate_change_nb);
+
+	// FIXME locking
+
+	if (!p->dev_max_speed_hz)
+		return NOTIFY_OK;
+
+	switch (event) {
+	case PRE_RATE_CHANGE:
+		dev_info(&p->pdev->dev, "%s: %pC old_rate %lu new_rate %lu\n",
+			 "PRE_RATE_CHANGE", ndata->clk, ndata->old_rate,
+			 ndata->new_rate);
+		if (p->dev_max_speed_hz < ndata->new_rate / MAX_DIV) {
+			dev_err(&p->pdev->dev,
+				"New parent clock rate too high for %u\n",
+				p->dev_max_speed_hz);
+			return NOTIFY_STOP;
+		}
+		if (p->dev_max_speed_hz > ndata->new_rate) {
+			dev_warn(&p->pdev->dev,
+				 "New parent clock rate too low for %u, ignoring\n",
+				 p->dev_max_speed_hz);
+		}
+		return NOTIFY_OK;
+
+	case POST_RATE_CHANGE:
+		dev_info(&p->pdev->dev, "%s: %pC old_rate %lu new_rate %lu\n",
+			 "POST_RATE_CHANGE", ndata->clk, ndata->old_rate,
+			 ndata->new_rate);
+		return NOTIFY_OK;
+
+	case ABORT_RATE_CHANGE:
+		return NOTIFY_OK;
+
+	default:
+		dev_info(&p->pdev->dev,
+			 "0x%lx: %pC old_rate %lu new_rate %lu\n", event,
+			 ndata->clk, ndata->old_rate, ndata->new_rate);
+		return NOTIFY_DONE;
+	}
+}
+
 static int sh_msiof_spi_setup(struct spi_device *spi)
 {
 	struct device_node	*np = spi->master->dev.of_node;
@@ -535,12 +588,14 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 
 	rate = clk_get_rate(p->clk);
 	max_speed_hz = rate;
-	min_speed_hz = rate / 1024;
+	min_speed_hz = rate / MAX_DIV;
 
 	dev_info(&p->pdev->dev,
 		 "%s: master speed min %u max %u, device speed max = %u\n",
 		 __func__, min_speed_hz, max_speed_hz, spi->max_speed_hz);
 
+	p->dev_max_speed_hz = spi->max_speed_hz;
+
 	pm_runtime_get_sync(&p->pdev->dev);
 
 	if (!np) {
@@ -1263,6 +1318,10 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
 	p->pdev = pdev;
 	pm_runtime_enable(&pdev->dev);
 
+	p->clk_rate_change_nb.notifier_call = sh_msiof_clk_notifier_cb;
+	if (clk_notifier_register(p->clk, &p->clk_rate_change_nb))
+		dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
+
 	/* Platform data may override FIFO sizes */
 	p->tx_fifo_size = chipdata->tx_fifo_size;
 	p->rx_fifo_size = chipdata->rx_fifo_size;
@@ -1299,6 +1358,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
 	return 0;
 
  err2:
+	clk_notifier_unregister(p->clk, &p->clk_rate_change_nb);
 	sh_msiof_release_dma(p);
 	pm_runtime_disable(&pdev->dev);
  err1:
@@ -1310,6 +1370,7 @@ static int sh_msiof_spi_remove(struct platform_device *pdev)
 {
 	struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
 
+	clk_notifier_unregister(p->clk, &p->clk_rate_change_nb);
 	sh_msiof_release_dma(p);
 	pm_runtime_disable(&pdev->dev);
 	return 0;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 7/9 option 2] spi: sh-msiof: Configure MSIOF parent clock
@ 2016-08-12 16:38   ` Geert Uytterhoeven
  0 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

Change the clock rate in spi_master.setup() to accomodate the desired
maximum clock rate for the device being set up:
  - Change the clock rate if the msiofX (and thus mso) clock rate is too
    high.  Failure to do so is considered an error.
  - Try to change the clock rate if the msiofX (and thus mso) clock rate
    is too low to achieve the desired performance. Failure to do so is
    not considered an error, as the device will still work, but slower.

Results (sequential operations during probing):
  1. msiof0 (and mso) set to 30.8 MHz, 15.4 MHz after internal
     divider,
  2. msiof2 kept at 30.8 MHz, 993 kHz after internal divider,
  3. msiof3 (and mso) set to 20 MHz, 19.5 kHz after internal divider.

Observations:
  - As the requested rate of 30 MHz is rounded to 30.8 MHz, which is
    higher than 30 MHz, an mso internal divider of 2 is used, leading to
    a much lower rate of 15.4 MHz, and thus lower performance than
    expected.
  - The parent clock frequency cannot be changed while the mso clock is
    enabled. This is tricky, as the MSIOF modules are part of a Clock
    Domain, hence their clocks (and its parent clock) are under control
    of Runtime PM.
    So the parent clock may still be enabled due to asynchronous
    runtime-suspend not having disabled it yet, causing clk_set_rate()
    to fail sometimes with -EBUSY.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Not intended for upstream merge.
---
 drivers/spi/spi-sh-msiof.c | 74 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 73 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 656eaa4d03ed497b..0b69643936cdb941 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -584,7 +584,8 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 	struct device_node	*np = spi->master->dev.of_node;
 	struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
 	u32 max_speed_hz, min_speed_hz;
-	unsigned long rate;
+	unsigned long rate, req_rate;
+	int error;
 
 	rate = clk_get_rate(p->clk);
 	max_speed_hz = rate;
@@ -594,6 +595,77 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 		 "%s: master speed min %u max %u, device speed max = %u\n",
 		 __func__, min_speed_hz, max_speed_hz, spi->max_speed_hz);
 
+	if (spi->max_speed_hz < min_speed_hz) {
+		dev_err(&p->pdev->dev,
+			"Parent clock rate %lu too high for %u!\n", rate,
+			spi->max_speed_hz);
+
+		req_rate = spi->max_speed_hz * MAX_DIV;
+
+		error = clk_set_rate(p->clk, req_rate);
+		if (error) {
+			dev_err(&p->pdev->dev,
+				"Failed to set parent clock rate to %lu: %d\n",
+				req_rate, error);
+			return error;
+		}
+
+		rate = clk_get_rate(p->clk);
+		dev_info(&p->pdev->dev,
+			 "Changed parent clock rate to %lu actual %lu\n",
+			 req_rate, rate);
+
+		max_speed_hz = rate;
+		min_speed_hz = rate / MAX_DIV;
+
+		dev_info(&p->pdev->dev,
+			 "%s: new master speed min %u max %u, device speed max = %u\n",
+			 __func__, min_speed_hz, max_speed_hz,
+			 spi->max_speed_hz);
+
+		if (spi->max_speed_hz < min_speed_hz) {
+			dev_err(&p->pdev->dev,
+				"New parent clock rate %lu too high for %u!\n",
+				rate, spi->max_speed_hz);
+			return -EINVAL;
+		}
+	} else if (spi->max_speed_hz * 4 > max_speed_hz * 5) {
+		/* More than 20% lower than desired */
+		dev_warn(&p->pdev->dev,
+			 "Parent clock rate %lu too low for %u\n", rate,
+			 spi->max_speed_hz);
+
+		req_rate = spi->max_speed_hz;
+
+		error = clk_set_rate(p->clk, req_rate);
+		if (error) {
+			dev_warn(&p->pdev->dev,
+				"Failed to set parent clock rate to %lu: %d, ignoring\n",
+				req_rate, error);
+			goto done;
+		}
+
+		rate = clk_get_rate(p->clk);
+		dev_info(&p->pdev->dev,
+			 "Changed parent clock rate to %lu actual %lu\n",
+			 req_rate, rate);
+
+		max_speed_hz = rate;
+		min_speed_hz = rate / MAX_DIV;
+
+		dev_info(&p->pdev->dev,
+			 "%s: new master speed min %u max %u, device speed max = %u\n",
+			 __func__, min_speed_hz, max_speed_hz,
+			 spi->max_speed_hz);
+
+		if (spi->max_speed_hz * 4 > max_speed_hz * 5) {
+			dev_warn(&p->pdev->dev,
+				 "New parent clock rate %lu too high for %u, ignoring\n",
+				 rate, spi->max_speed_hz);
+		}
+	}
+
+done:
 	p->dev_max_speed_hz = spi->max_speed_hz;
 
 	pm_runtime_get_sync(&p->pdev->dev);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 7/9 option 2] spi: sh-msiof: Configure MSIOF parent clock
@ 2016-08-12 16:38   ` Geert Uytterhoeven
  0 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven

Change the clock rate in spi_master.setup() to accomodate the desired
maximum clock rate for the device being set up:
  - Change the clock rate if the msiofX (and thus mso) clock rate is too
    high.  Failure to do so is considered an error.
  - Try to change the clock rate if the msiofX (and thus mso) clock rate
    is too low to achieve the desired performance. Failure to do so is
    not considered an error, as the device will still work, but slower.

Results (sequential operations during probing):
  1. msiof0 (and mso) set to 30.8 MHz, 15.4 MHz after internal
     divider,
  2. msiof2 kept at 30.8 MHz, 993 kHz after internal divider,
  3. msiof3 (and mso) set to 20 MHz, 19.5 kHz after internal divider.

Observations:
  - As the requested rate of 30 MHz is rounded to 30.8 MHz, which is
    higher than 30 MHz, an mso internal divider of 2 is used, leading to
    a much lower rate of 15.4 MHz, and thus lower performance than
    expected.
  - The parent clock frequency cannot be changed while the mso clock is
    enabled. This is tricky, as the MSIOF modules are part of a Clock
    Domain, hence their clocks (and its parent clock) are under control
    of Runtime PM.
    So the parent clock may still be enabled due to asynchronous
    runtime-suspend not having disabled it yet, causing clk_set_rate()
    to fail sometimes with -EBUSY.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
Not intended for upstream merge.
---
 drivers/spi/spi-sh-msiof.c | 74 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 73 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 656eaa4d03ed497b..0b69643936cdb941 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -584,7 +584,8 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 	struct device_node	*np = spi->master->dev.of_node;
 	struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
 	u32 max_speed_hz, min_speed_hz;
-	unsigned long rate;
+	unsigned long rate, req_rate;
+	int error;
 
 	rate = clk_get_rate(p->clk);
 	max_speed_hz = rate;
@@ -594,6 +595,77 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 		 "%s: master speed min %u max %u, device speed max = %u\n",
 		 __func__, min_speed_hz, max_speed_hz, spi->max_speed_hz);
 
+	if (spi->max_speed_hz < min_speed_hz) {
+		dev_err(&p->pdev->dev,
+			"Parent clock rate %lu too high for %u!\n", rate,
+			spi->max_speed_hz);
+
+		req_rate = spi->max_speed_hz * MAX_DIV;
+
+		error = clk_set_rate(p->clk, req_rate);
+		if (error) {
+			dev_err(&p->pdev->dev,
+				"Failed to set parent clock rate to %lu: %d\n",
+				req_rate, error);
+			return error;
+		}
+
+		rate = clk_get_rate(p->clk);
+		dev_info(&p->pdev->dev,
+			 "Changed parent clock rate to %lu actual %lu\n",
+			 req_rate, rate);
+
+		max_speed_hz = rate;
+		min_speed_hz = rate / MAX_DIV;
+
+		dev_info(&p->pdev->dev,
+			 "%s: new master speed min %u max %u, device speed max = %u\n",
+			 __func__, min_speed_hz, max_speed_hz,
+			 spi->max_speed_hz);
+
+		if (spi->max_speed_hz < min_speed_hz) {
+			dev_err(&p->pdev->dev,
+				"New parent clock rate %lu too high for %u!\n",
+				rate, spi->max_speed_hz);
+			return -EINVAL;
+		}
+	} else if (spi->max_speed_hz * 4 > max_speed_hz * 5) {
+		/* More than 20% lower than desired */
+		dev_warn(&p->pdev->dev,
+			 "Parent clock rate %lu too low for %u\n", rate,
+			 spi->max_speed_hz);
+
+		req_rate = spi->max_speed_hz;
+
+		error = clk_set_rate(p->clk, req_rate);
+		if (error) {
+			dev_warn(&p->pdev->dev,
+				"Failed to set parent clock rate to %lu: %d, ignoring\n",
+				req_rate, error);
+			goto done;
+		}
+
+		rate = clk_get_rate(p->clk);
+		dev_info(&p->pdev->dev,
+			 "Changed parent clock rate to %lu actual %lu\n",
+			 req_rate, rate);
+
+		max_speed_hz = rate;
+		min_speed_hz = rate / MAX_DIV;
+
+		dev_info(&p->pdev->dev,
+			 "%s: new master speed min %u max %u, device speed max = %u\n",
+			 __func__, min_speed_hz, max_speed_hz,
+			 spi->max_speed_hz);
+
+		if (spi->max_speed_hz * 4 > max_speed_hz * 5) {
+			dev_warn(&p->pdev->dev,
+				 "New parent clock rate %lu too high for %u, ignoring\n",
+				 rate, spi->max_speed_hz);
+		}
+	}
+
+done:
 	p->dev_max_speed_hz = spi->max_speed_hz;
 
 	pm_runtime_get_sync(&p->pdev->dev);
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 8/9 option 3] clk: divider: Add hack to support dummy clocks
  2016-08-12 16:38 [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Geert Uytterhoeven
                   ` (6 preceding siblings ...)
  2016-08-12 16:38   ` Geert Uytterhoeven
@ 2016-08-12 16:38 ` Geert Uytterhoeven
  2016-08-12 16:38   ` Geert Uytterhoeven
  2016-08-25  5:34 ` [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Magnus Damm
  9 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

Allow registering dummy divider clocks that do not operate on an actual
hardware register (reg = NULL), for testing clock algorithms.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Not intended for upstream merge.
---
 drivers/clk/clk-divider.c    | 33 +++++++++++++++++++++++----------
 include/linux/clk-provider.h |  1 +
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 96386ffc84835f12..cd1caea76d6243ec 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -141,8 +141,12 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
 	struct clk_divider *divider = to_clk_divider(hw);
 	unsigned int val;
 
-	val = clk_readl(divider->reg) >> divider->shift;
-	val &= div_mask(divider->width);
+	if (divider->reg) {
+		val = clk_readl(divider->reg) >> divider->shift;
+		val &= div_mask(divider->width);
+	} else {
+		val = divider->reg_val;
+	}
 
 	return divider_recalc_rate(hw, parent_rate, val, divider->table,
 				   divider->flags);
@@ -352,8 +356,12 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
 
 	/* if read only, just return current value */
 	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
-		bestdiv = clk_readl(divider->reg) >> divider->shift;
-		bestdiv &= div_mask(divider->width);
+		if (divider->reg) {
+			bestdiv = clk_readl(divider->reg) >> divider->shift;
+			bestdiv &= div_mask(divider->width);
+		} else {
+			bestdiv = divider->reg_val;
+		}
 		bestdiv = _get_div(divider->table, bestdiv, divider->flags,
 			divider->width);
 		return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
@@ -396,14 +404,19 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
 	else
 		__acquire(divider->lock);
 
-	if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
-		val = div_mask(divider->width) << (divider->shift + 16);
+	if (divider->reg) {
+		if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
+			val = div_mask(divider->width) << (divider->shift + 16);
+		} else {
+			val = clk_readl(divider->reg);
+			val &= ~(div_mask(divider->width) << divider->shift);
+		}
+		val |= value << divider->shift;
+		clk_writel(val, divider->reg);
 	} else {
-		val = clk_readl(divider->reg);
-		val &= ~(div_mask(divider->width) << divider->shift);
+		pr_info("Setting %pC to value 0x%x\n", hw->clk, value);
+		divider->reg_val = value;
 	}
-	val |= value << divider->shift;
-	clk_writel(val, divider->reg);
 
 	if (divider->lock)
 		spin_unlock_irqrestore(divider->lock, flags);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index a39c0c530778251b..8aea584750f17a74 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -389,6 +389,7 @@ struct clk_div_table {
 struct clk_divider {
 	struct clk_hw	hw;
 	void __iomem	*reg;
+	unsigned int reg_val;	// FIXME if reg is NULL
 	u8		shift;
 	u8		width;
 	u8		flags;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 9/9 option 3] spi: sh-msiof: Configure MSIOF parent clock
@ 2016-08-12 16:38   ` Geert Uytterhoeven
  0 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: linux-spi, linux-clk, Geert Uytterhoeven

Change the clock rate in spi_master.setup() to accomodate the desired
maximum clock rate for the device being set up.
Use clk-fixed on a fake clock behaving like the internal MSIOF divider.
Setting the rate of the fake clock will make clk-fixed find an optimal
rate for the mso clock, with an optimal internal msiof divider.

Results (sequential operations during probing):
  1. msiof0 (and mso) set to 400 MHz, msiof0-div at 28.6 MHz
  2. msiof2 (and mso) set to 8 MHz, msiof2-div at 1 MHz
  3. msiof3 kept at 8 MHz, msiof3-div at 20 kHz

Observations:
  - The algorithm used by clk-fixed does find a better clock, closer
    to the target rate,
  - However, mso clock rates are really high,
  - Despite calling clk_set_rate_range() on each msiofX clock,
    clk_set_rate() on another msiofX clock may reprogram mso to
    violate the set constraints.  Hence at the end the mso clock runs
    at only 8 MHz, which is suboptimal.
  - The parent clock frequency cannot be changed while the mso clock
    is enabled. This is tricky, as the MSIOF modules are part of a
    Clock Domain, hence their clocks (and its parent clock) are under
    control of Runtime PM.
    So the parent clock may still be enabled due to asynchronous
    runtime-suspend not having disabled it yet, causing clk_set_rate()
    to fail sometimes with -EBUSY.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Not intended for upstream merge.
---
 drivers/spi/spi-sh-msiof.c | 198 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 198 insertions(+)

diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 656eaa4d03ed497b..c13420888f86371f 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -13,6 +13,7 @@
 
 #include <linux/bitmap.h>
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
@@ -44,6 +45,7 @@ struct sh_msiof_spi_priv {
 	struct spi_master *master;
 	void __iomem *mapbase;
 	struct clk *clk;
+	struct clk *div_clk;
 	struct platform_device *pdev;
 	struct sh_msiof_spi_info *info;
 	struct completion done;
@@ -579,6 +581,194 @@ static int sh_msiof_clk_notifier_cb(struct notifier_block *nb,
 	}
 }
 
+static const struct clk_div_table sh_misof_div_clk_table[] = {
+	{ .div =  1 *  1,	.val = SCR_BRDV_DIV_1  | SCR_BRPS( 1) },
+	{ .div =  1 *  2,	.val = SCR_BRDV_DIV_1  | SCR_BRPS( 2) },
+
+	{ .div =  2 *  1,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 1) },
+	{ .div =  2 *  2,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 2) },
+	{ .div =  2 *  3,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 3) },
+	{ .div =  2 *  4,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 4) },
+	{ .div =  2 *  5,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 5) },
+	{ .div =  2 *  6,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 6) },
+	{ .div =  2 *  7,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 7) },
+	{ .div =  2 *  8,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 8) },
+	{ .div =  2 *  9,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 9) },
+	{ .div =  2 * 10,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(10) },
+	{ .div =  2 * 11,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(11) },
+	{ .div =  2 * 12,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(12) },
+	{ .div =  2 * 13,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(13) },
+	{ .div =  2 * 14,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(14) },
+	{ .div =  2 * 15,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(15) },
+	{ .div =  2 * 16,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(16) },
+	{ .div =  2 * 17,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(17) },
+	{ .div =  2 * 18,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(18) },
+	{ .div =  2 * 19,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(19) },
+	{ .div =  2 * 20,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(20) },
+	{ .div =  2 * 21,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(21) },
+	{ .div =  2 * 22,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(22) },
+	{ .div =  2 * 23,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(23) },
+	{ .div =  2 * 24,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(24) },
+	{ .div =  2 * 25,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(25) },
+	{ .div =  2 * 26,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(26) },
+	{ .div =  2 * 27,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(27) },
+	{ .div =  2 * 28,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(28) },
+	{ .div =  2 * 29,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(29) },
+	{ .div =  2 * 30,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(30) },
+	{ .div =  2 * 31,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(31) },
+	{ .div =  2 * 32,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(32) },
+
+	{ .div =  4 *  1,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 1) },
+	{ .div =  4 *  2,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 2) },
+	{ .div =  4 *  3,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 3) },
+	{ .div =  4 *  4,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 4) },
+	{ .div =  4 *  5,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 5) },
+	{ .div =  4 *  6,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 6) },
+	{ .div =  4 *  7,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 7) },
+	{ .div =  4 *  8,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 8) },
+	{ .div =  4 *  9,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 9) },
+	{ .div =  4 * 10,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(10) },
+	{ .div =  4 * 11,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(11) },
+	{ .div =  4 * 12,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(12) },
+	{ .div =  4 * 13,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(13) },
+	{ .div =  4 * 14,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(14) },
+	{ .div =  4 * 15,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(15) },
+	{ .div =  4 * 16,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(16) },
+	{ .div =  4 * 17,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(17) },
+	{ .div =  4 * 18,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(18) },
+	{ .div =  4 * 19,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(19) },
+	{ .div =  4 * 20,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(20) },
+	{ .div =  4 * 21,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(21) },
+	{ .div =  4 * 22,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(22) },
+	{ .div =  4 * 23,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(23) },
+	{ .div =  4 * 24,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(24) },
+	{ .div =  4 * 25,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(25) },
+	{ .div =  4 * 26,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(26) },
+	{ .div =  4 * 27,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(27) },
+	{ .div =  4 * 28,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(28) },
+	{ .div =  4 * 29,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(29) },
+	{ .div =  4 * 30,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(30) },
+	{ .div =  4 * 31,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(31) },
+	{ .div =  4 * 32,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(32) },
+
+	{ .div =  8 *  1,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 1) },
+	{ .div =  8 *  2,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 2) },
+	{ .div =  8 *  3,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 3) },
+	{ .div =  8 *  4,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 4) },
+	{ .div =  8 *  5,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 5) },
+	{ .div =  8 *  6,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 6) },
+	{ .div =  8 *  7,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 7) },
+	{ .div =  8 *  8,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 8) },
+	{ .div =  8 *  9,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 9) },
+	{ .div =  8 * 10,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(10) },
+	{ .div =  8 * 11,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(11) },
+	{ .div =  8 * 12,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(12) },
+	{ .div =  8 * 13,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(13) },
+	{ .div =  8 * 14,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(14) },
+	{ .div =  8 * 15,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(15) },
+	{ .div =  8 * 16,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(16) },
+	{ .div =  8 * 17,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(17) },
+	{ .div =  8 * 18,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(18) },
+	{ .div =  8 * 19,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(19) },
+	{ .div =  8 * 20,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(20) },
+	{ .div =  8 * 21,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(21) },
+	{ .div =  8 * 22,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(22) },
+	{ .div =  8 * 23,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(23) },
+	{ .div =  8 * 24,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(24) },
+	{ .div =  8 * 25,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(25) },
+	{ .div =  8 * 26,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(26) },
+	{ .div =  8 * 27,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(27) },
+	{ .div =  8 * 28,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(28) },
+	{ .div =  8 * 29,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(29) },
+	{ .div =  8 * 30,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(30) },
+	{ .div =  8 * 31,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(31) },
+	{ .div =  8 * 32,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(32) },
+
+	{ .div = 16 *  1,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 1) },
+	{ .div = 16 *  2,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 2) },
+	{ .div = 16 *  3,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 3) },
+	{ .div = 16 *  4,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 4) },
+	{ .div = 16 *  5,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 5) },
+	{ .div = 16 *  6,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 6) },
+	{ .div = 16 *  7,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 7) },
+	{ .div = 16 *  8,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 8) },
+	{ .div = 16 *  9,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 9) },
+	{ .div = 16 * 10,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(10) },
+	{ .div = 16 * 11,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(11) },
+	{ .div = 16 * 12,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(12) },
+	{ .div = 16 * 13,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(13) },
+	{ .div = 16 * 14,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(14) },
+	{ .div = 16 * 15,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(15) },
+	{ .div = 16 * 16,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(16) },
+	{ .div = 16 * 17,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(17) },
+	{ .div = 16 * 18,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(18) },
+	{ .div = 16 * 19,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(19) },
+	{ .div = 16 * 20,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(20) },
+	{ .div = 16 * 21,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(21) },
+	{ .div = 16 * 22,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(22) },
+	{ .div = 16 * 23,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(23) },
+	{ .div = 16 * 24,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(24) },
+	{ .div = 16 * 25,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(25) },
+	{ .div = 16 * 26,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(26) },
+	{ .div = 16 * 27,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(27) },
+	{ .div = 16 * 28,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(28) },
+	{ .div = 16 * 29,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(29) },
+	{ .div = 16 * 30,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(30) },
+	{ .div = 16 * 31,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(31) },
+	{ .div = 16 * 32,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(32) },
+
+	{ .div = 32 *  1,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 1) },
+	{ .div = 32 *  2,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 2) },
+	{ .div = 32 *  3,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 3) },
+	{ .div = 32 *  4,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 4) },
+	{ .div = 32 *  5,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 5) },
+	{ .div = 32 *  6,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 6) },
+	{ .div = 32 *  7,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 7) },
+	{ .div = 32 *  8,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 8) },
+	{ .div = 32 *  9,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 9) },
+	{ .div = 32 * 10,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(10) },
+	{ .div = 32 * 11,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(11) },
+	{ .div = 32 * 12,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(12) },
+	{ .div = 32 * 13,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(13) },
+	{ .div = 32 * 14,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(14) },
+	{ .div = 32 * 15,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(15) },
+	{ .div = 32 * 16,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(16) },
+	{ .div = 32 * 17,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(17) },
+	{ .div = 32 * 18,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(18) },
+	{ .div = 32 * 19,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(19) },
+	{ .div = 32 * 20,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(20) },
+	{ .div = 32 * 21,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(21) },
+	{ .div = 32 * 22,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(22) },
+	{ .div = 32 * 23,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(23) },
+	{ .div = 32 * 24,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(24) },
+	{ .div = 32 * 25,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(25) },
+	{ .div = 32 * 26,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(26) },
+	{ .div = 32 * 27,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(27) },
+	{ .div = 32 * 28,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(28) },
+	{ .div = 32 * 29,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(29) },
+	{ .div = 32 * 30,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(30) },
+	{ .div = 32 * 31,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(31) },
+	{ .div = 32 * 32,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(32) },
+
+	{ .div = 0,	.val = 0 },
+};
+
+static void sh_msiof_register_div_clk(struct sh_msiof_spi_priv *p)
+{
+	char parent_name[16], name[16];
+
+	snprintf(parent_name, sizeof(parent_name), "%pC", p->clk);
+	snprintf(name, sizeof(name), "%pC-div", p->clk);
+
+	p->div_clk = clk_register_divider_table(NULL, name, parent_name,
+						CLK_SET_RATE_PARENT, NULL, 0,
+						16, 0, sh_misof_div_clk_table,
+						NULL);
+	if (IS_ERR(p->div_clk))
+		pr_err("clk_register_divider_table %s failed: %ld\n", name,
+		       PTR_ERR(p->div_clk));
+}
+
 static int sh_msiof_spi_setup(struct spi_device *spi)
 {
 	struct device_node	*np = spi->master->dev.of_node;
@@ -594,6 +784,12 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 		 "%s: master speed min %u max %u, device speed max = %u\n",
 		 __func__, min_speed_hz, max_speed_hz, spi->max_speed_hz);
 
+	clk_set_rate(p->div_clk, spi->max_speed_hz);
+	/* Set lower bound to 80% of desired rate */
+	clk_set_rate_range(p->div_clk, spi->max_speed_hz * 4 / 5,
+			   spi->max_speed_hz);
+	pr_info("%pC at %pCr, %pC at %pCr\n", p->clk, p->clk, p->div_clk, p->div_clk);
+
 	p->dev_max_speed_hz = spi->max_speed_hz;
 
 	pm_runtime_get_sync(&p->pdev->dev);
@@ -1322,6 +1518,8 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
 	if (clk_notifier_register(p->clk, &p->clk_rate_change_nb))
 		dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
 
+	sh_msiof_register_div_clk(p);
+
 	/* Platform data may override FIFO sizes */
 	p->tx_fifo_size = chipdata->tx_fifo_size;
 	p->rx_fifo_size = chipdata->rx_fifo_size;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH/PROTO 9/9 option 3] spi: sh-msiof: Configure MSIOF parent clock
@ 2016-08-12 16:38   ` Geert Uytterhoeven
  0 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-12 16:38 UTC (permalink / raw)
  To: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven

Change the clock rate in spi_master.setup() to accomodate the desired
maximum clock rate for the device being set up.
Use clk-fixed on a fake clock behaving like the internal MSIOF divider.
Setting the rate of the fake clock will make clk-fixed find an optimal
rate for the mso clock, with an optimal internal msiof divider.

Results (sequential operations during probing):
  1. msiof0 (and mso) set to 400 MHz, msiof0-div at 28.6 MHz
  2. msiof2 (and mso) set to 8 MHz, msiof2-div at 1 MHz
  3. msiof3 kept at 8 MHz, msiof3-div at 20 kHz

Observations:
  - The algorithm used by clk-fixed does find a better clock, closer
    to the target rate,
  - However, mso clock rates are really high,
  - Despite calling clk_set_rate_range() on each msiofX clock,
    clk_set_rate() on another msiofX clock may reprogram mso to
    violate the set constraints.  Hence at the end the mso clock runs
    at only 8 MHz, which is suboptimal.
  - The parent clock frequency cannot be changed while the mso clock
    is enabled. This is tricky, as the MSIOF modules are part of a
    Clock Domain, hence their clocks (and its parent clock) are under
    control of Runtime PM.
    So the parent clock may still be enabled due to asynchronous
    runtime-suspend not having disabled it yet, causing clk_set_rate()
    to fail sometimes with -EBUSY.

Not-Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
Not intended for upstream merge.
---
 drivers/spi/spi-sh-msiof.c | 198 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 198 insertions(+)

diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 656eaa4d03ed497b..c13420888f86371f 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -13,6 +13,7 @@
 
 #include <linux/bitmap.h>
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
@@ -44,6 +45,7 @@ struct sh_msiof_spi_priv {
 	struct spi_master *master;
 	void __iomem *mapbase;
 	struct clk *clk;
+	struct clk *div_clk;
 	struct platform_device *pdev;
 	struct sh_msiof_spi_info *info;
 	struct completion done;
@@ -579,6 +581,194 @@ static int sh_msiof_clk_notifier_cb(struct notifier_block *nb,
 	}
 }
 
+static const struct clk_div_table sh_misof_div_clk_table[] = {
+	{ .div =  1 *  1,	.val = SCR_BRDV_DIV_1  | SCR_BRPS( 1) },
+	{ .div =  1 *  2,	.val = SCR_BRDV_DIV_1  | SCR_BRPS( 2) },
+
+	{ .div =  2 *  1,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 1) },
+	{ .div =  2 *  2,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 2) },
+	{ .div =  2 *  3,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 3) },
+	{ .div =  2 *  4,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 4) },
+	{ .div =  2 *  5,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 5) },
+	{ .div =  2 *  6,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 6) },
+	{ .div =  2 *  7,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 7) },
+	{ .div =  2 *  8,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 8) },
+	{ .div =  2 *  9,	.val = SCR_BRDV_DIV_2  | SCR_BRPS( 9) },
+	{ .div =  2 * 10,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(10) },
+	{ .div =  2 * 11,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(11) },
+	{ .div =  2 * 12,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(12) },
+	{ .div =  2 * 13,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(13) },
+	{ .div =  2 * 14,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(14) },
+	{ .div =  2 * 15,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(15) },
+	{ .div =  2 * 16,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(16) },
+	{ .div =  2 * 17,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(17) },
+	{ .div =  2 * 18,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(18) },
+	{ .div =  2 * 19,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(19) },
+	{ .div =  2 * 20,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(20) },
+	{ .div =  2 * 21,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(21) },
+	{ .div =  2 * 22,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(22) },
+	{ .div =  2 * 23,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(23) },
+	{ .div =  2 * 24,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(24) },
+	{ .div =  2 * 25,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(25) },
+	{ .div =  2 * 26,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(26) },
+	{ .div =  2 * 27,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(27) },
+	{ .div =  2 * 28,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(28) },
+	{ .div =  2 * 29,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(29) },
+	{ .div =  2 * 30,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(30) },
+	{ .div =  2 * 31,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(31) },
+	{ .div =  2 * 32,	.val = SCR_BRDV_DIV_2  | SCR_BRPS(32) },
+
+	{ .div =  4 *  1,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 1) },
+	{ .div =  4 *  2,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 2) },
+	{ .div =  4 *  3,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 3) },
+	{ .div =  4 *  4,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 4) },
+	{ .div =  4 *  5,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 5) },
+	{ .div =  4 *  6,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 6) },
+	{ .div =  4 *  7,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 7) },
+	{ .div =  4 *  8,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 8) },
+	{ .div =  4 *  9,	.val = SCR_BRDV_DIV_4  | SCR_BRPS( 9) },
+	{ .div =  4 * 10,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(10) },
+	{ .div =  4 * 11,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(11) },
+	{ .div =  4 * 12,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(12) },
+	{ .div =  4 * 13,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(13) },
+	{ .div =  4 * 14,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(14) },
+	{ .div =  4 * 15,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(15) },
+	{ .div =  4 * 16,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(16) },
+	{ .div =  4 * 17,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(17) },
+	{ .div =  4 * 18,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(18) },
+	{ .div =  4 * 19,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(19) },
+	{ .div =  4 * 20,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(20) },
+	{ .div =  4 * 21,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(21) },
+	{ .div =  4 * 22,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(22) },
+	{ .div =  4 * 23,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(23) },
+	{ .div =  4 * 24,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(24) },
+	{ .div =  4 * 25,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(25) },
+	{ .div =  4 * 26,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(26) },
+	{ .div =  4 * 27,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(27) },
+	{ .div =  4 * 28,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(28) },
+	{ .div =  4 * 29,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(29) },
+	{ .div =  4 * 30,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(30) },
+	{ .div =  4 * 31,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(31) },
+	{ .div =  4 * 32,	.val = SCR_BRDV_DIV_4  | SCR_BRPS(32) },
+
+	{ .div =  8 *  1,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 1) },
+	{ .div =  8 *  2,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 2) },
+	{ .div =  8 *  3,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 3) },
+	{ .div =  8 *  4,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 4) },
+	{ .div =  8 *  5,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 5) },
+	{ .div =  8 *  6,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 6) },
+	{ .div =  8 *  7,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 7) },
+	{ .div =  8 *  8,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 8) },
+	{ .div =  8 *  9,	.val = SCR_BRDV_DIV_8  | SCR_BRPS( 9) },
+	{ .div =  8 * 10,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(10) },
+	{ .div =  8 * 11,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(11) },
+	{ .div =  8 * 12,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(12) },
+	{ .div =  8 * 13,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(13) },
+	{ .div =  8 * 14,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(14) },
+	{ .div =  8 * 15,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(15) },
+	{ .div =  8 * 16,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(16) },
+	{ .div =  8 * 17,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(17) },
+	{ .div =  8 * 18,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(18) },
+	{ .div =  8 * 19,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(19) },
+	{ .div =  8 * 20,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(20) },
+	{ .div =  8 * 21,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(21) },
+	{ .div =  8 * 22,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(22) },
+	{ .div =  8 * 23,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(23) },
+	{ .div =  8 * 24,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(24) },
+	{ .div =  8 * 25,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(25) },
+	{ .div =  8 * 26,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(26) },
+	{ .div =  8 * 27,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(27) },
+	{ .div =  8 * 28,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(28) },
+	{ .div =  8 * 29,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(29) },
+	{ .div =  8 * 30,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(30) },
+	{ .div =  8 * 31,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(31) },
+	{ .div =  8 * 32,	.val = SCR_BRDV_DIV_8  | SCR_BRPS(32) },
+
+	{ .div = 16 *  1,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 1) },
+	{ .div = 16 *  2,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 2) },
+	{ .div = 16 *  3,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 3) },
+	{ .div = 16 *  4,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 4) },
+	{ .div = 16 *  5,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 5) },
+	{ .div = 16 *  6,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 6) },
+	{ .div = 16 *  7,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 7) },
+	{ .div = 16 *  8,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 8) },
+	{ .div = 16 *  9,	.val = SCR_BRDV_DIV_16 | SCR_BRPS( 9) },
+	{ .div = 16 * 10,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(10) },
+	{ .div = 16 * 11,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(11) },
+	{ .div = 16 * 12,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(12) },
+	{ .div = 16 * 13,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(13) },
+	{ .div = 16 * 14,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(14) },
+	{ .div = 16 * 15,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(15) },
+	{ .div = 16 * 16,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(16) },
+	{ .div = 16 * 17,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(17) },
+	{ .div = 16 * 18,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(18) },
+	{ .div = 16 * 19,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(19) },
+	{ .div = 16 * 20,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(20) },
+	{ .div = 16 * 21,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(21) },
+	{ .div = 16 * 22,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(22) },
+	{ .div = 16 * 23,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(23) },
+	{ .div = 16 * 24,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(24) },
+	{ .div = 16 * 25,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(25) },
+	{ .div = 16 * 26,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(26) },
+	{ .div = 16 * 27,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(27) },
+	{ .div = 16 * 28,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(28) },
+	{ .div = 16 * 29,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(29) },
+	{ .div = 16 * 30,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(30) },
+	{ .div = 16 * 31,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(31) },
+	{ .div = 16 * 32,	.val = SCR_BRDV_DIV_16 | SCR_BRPS(32) },
+
+	{ .div = 32 *  1,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 1) },
+	{ .div = 32 *  2,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 2) },
+	{ .div = 32 *  3,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 3) },
+	{ .div = 32 *  4,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 4) },
+	{ .div = 32 *  5,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 5) },
+	{ .div = 32 *  6,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 6) },
+	{ .div = 32 *  7,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 7) },
+	{ .div = 32 *  8,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 8) },
+	{ .div = 32 *  9,	.val = SCR_BRDV_DIV_32 | SCR_BRPS( 9) },
+	{ .div = 32 * 10,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(10) },
+	{ .div = 32 * 11,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(11) },
+	{ .div = 32 * 12,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(12) },
+	{ .div = 32 * 13,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(13) },
+	{ .div = 32 * 14,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(14) },
+	{ .div = 32 * 15,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(15) },
+	{ .div = 32 * 16,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(16) },
+	{ .div = 32 * 17,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(17) },
+	{ .div = 32 * 18,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(18) },
+	{ .div = 32 * 19,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(19) },
+	{ .div = 32 * 20,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(20) },
+	{ .div = 32 * 21,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(21) },
+	{ .div = 32 * 22,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(22) },
+	{ .div = 32 * 23,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(23) },
+	{ .div = 32 * 24,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(24) },
+	{ .div = 32 * 25,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(25) },
+	{ .div = 32 * 26,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(26) },
+	{ .div = 32 * 27,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(27) },
+	{ .div = 32 * 28,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(28) },
+	{ .div = 32 * 29,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(29) },
+	{ .div = 32 * 30,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(30) },
+	{ .div = 32 * 31,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(31) },
+	{ .div = 32 * 32,	.val = SCR_BRDV_DIV_32 | SCR_BRPS(32) },
+
+	{ .div = 0,	.val = 0 },
+};
+
+static void sh_msiof_register_div_clk(struct sh_msiof_spi_priv *p)
+{
+	char parent_name[16], name[16];
+
+	snprintf(parent_name, sizeof(parent_name), "%pC", p->clk);
+	snprintf(name, sizeof(name), "%pC-div", p->clk);
+
+	p->div_clk = clk_register_divider_table(NULL, name, parent_name,
+						CLK_SET_RATE_PARENT, NULL, 0,
+						16, 0, sh_misof_div_clk_table,
+						NULL);
+	if (IS_ERR(p->div_clk))
+		pr_err("clk_register_divider_table %s failed: %ld\n", name,
+		       PTR_ERR(p->div_clk));
+}
+
 static int sh_msiof_spi_setup(struct spi_device *spi)
 {
 	struct device_node	*np = spi->master->dev.of_node;
@@ -594,6 +784,12 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
 		 "%s: master speed min %u max %u, device speed max = %u\n",
 		 __func__, min_speed_hz, max_speed_hz, spi->max_speed_hz);
 
+	clk_set_rate(p->div_clk, spi->max_speed_hz);
+	/* Set lower bound to 80% of desired rate */
+	clk_set_rate_range(p->div_clk, spi->max_speed_hz * 4 / 5,
+			   spi->max_speed_hz);
+	pr_info("%pC at %pCr, %pC at %pCr\n", p->clk, p->clk, p->div_clk, p->div_clk);
+
 	p->dev_max_speed_hz = spi->max_speed_hz;
 
 	pm_runtime_get_sync(&p->pdev->dev);
@@ -1322,6 +1518,8 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
 	if (clk_notifier_register(p->clk, &p->clk_rate_change_nb))
 		dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
 
+	sh_msiof_register_div_clk(p);
+
 	/* Platform data may override FIFO sizes */
 	p->tx_fifo_size = chipdata->tx_fifo_size;
 	p->rx_fifo_size = chipdata->rx_fifo_size;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype
  2016-08-12 16:38 [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Geert Uytterhoeven
                   ` (8 preceding siblings ...)
  2016-08-12 16:38   ` Geert Uytterhoeven
@ 2016-08-25  5:34 ` Magnus Damm
  2016-08-29 12:38   ` Geert Uytterhoeven
  9 siblings, 1 reply; 15+ messages in thread
From: Magnus Damm @ 2016-08-25  5:34 UTC (permalink / raw)
  To: Geert Uytterhoeven; +Cc: Linux-Renesas, linux-spi, linux-clk

Hi Geert,

On Sat, Aug 13, 2016 at 1:38 AM, Geert Uytterhoeven
<geert+renesas@glider.be> wrote:
>         Hi all,
>
> On R-Car H3, the various MSIOF instances (used for SPI) have a common
> parent clock. This mso clock is a programmable "DIV6" clock (divider
> 1 - 64). Its rate lies in the range 12.5 to 800 MHz, or 6.25 to 400 MHz
> (depending on the main crystal).
> After boot up, the default configuration is the lowest possible rate,
> which leads to bad performance with devices desiring a higher transfer
> rate.
>
> MSIOF clock tree:
>
>     pll1/4 (800 or 400 MHz)
>       mso clock
>         msiof0 clock (type MSTP = gate + status)
>           msiof0 internal divider (divider range 1 - 1024, with holes)
>         msiof1 clock (type MSTP)
>           msiof1 internal divider
>         msiof2 clock (type MSTP)
>           msiof2 internal divider
>         msiof3 clock (type MSTP)
>           msiof3 internal divider
>
> To optimize performance, we want to control the mso parent clock.
>
>   - Higher parent clock means:
>       + Better performance for devices wanting high target rates,
>       + More accurate target rates in MSIOF driver,
>       - More power consumption (doesn't matter that much with R-Car),
>       - Cannot support devices needing real low target rates.
>   - Lower parent clock means:
>       + Less power consumption (doesn't matter that much with R-Car),
>       + Can support all devices,
>       - Worse performance for devices wanting high target rates,
>       - Less accurate target rates, decreasing performance.
>
> Configuring the mso parent clock rate can either be done statically, or
> dynamically. In both cases, this must be based on individual device
> requirements. Note that this is not specific to SPI.
>
> SPI specific notes:
>   - The SPI master knows about SPI slave device maximum frequencies only
>     when spi_master.setup() is called,
>   - SPI slave device maximum frequencies may change at runtime (through
>     calling spi_setup(), esp. for spidev),
>   - Currently the MSIOF driver does not set
>     spi_master.{min,max}_speed_hz. However, setting it means the SPI
>     core will never call spi_master.setup() with a rate outside this
>     range, and thus we will never be informed of such rates.  Hence if
>     we decide to set this, and we want dynamic configuration, we should
>     fill in the real minimum and maximum supported by the hardware,
>     regardless of the current parent clock rate.
>
> In this prototype, I'm offering 3 solutions to control the mso parent
> clock rate.
>
>   - Option 1: Static configuration through the "assigned-clocks" and
>     assigned-clock-rates DT properties.
>
>   - Option 2: Dynamic configuration, by changing the mso clock through
>     the msiofX module clocks from the MSIOF driver.
>
>   - Option 2: Dynamic configuration, by changing a dummy clk-divider,
>     which mimics the capabilities of the MSIOF internal divider.

Thanks for cooking up these solutions. I dislike putting software
policy into DT, but at least Option 1 is simple. Option 2 and 3 seem a
bit overly complex to me.

May I ask if you considered solving the common case and special case
independently?

Regarding the common case, I think it is sane to assume that everyone
wants a high performing solution if possible. So using highest clock
rate possible must be a good default to as long as we can be sure to
also be able to handle devices wanting low clock rates. In my mind
this would mean that a standard unmodified DT should result in highest
performance possible. This probably involves programming the CPG/MSSR
hardware during boot-up based on some table with maximum supported
frequency. The high performing setting may or may not be the default
from reset (or boot loader) today, but perhaps it makes sense to rely
less on hardware settings performed during boot? At least this seems
like a good long term aim to me.

As for short term special case handling, the only issue we are having
is when more than one MSIOF instance want to use frequencies that are
incompatible. The first MSIOF parent clock user should be able to
select whichever rate it wants in theory, but with more than one MSIOF
instance then if one MSIOF instance wants to use the highest frequency
possible and the other the lowest possible then the 1024 divider is
not enough to cover both. From my side I would say it makes sense to
cover this special case with "Option 1" above. To avoid probe order
issues the CPG/MSSR driver handling the parent clock could for
instance early during boot look for DT frequency properties in the DTB
and adjust the shared default rate based on that.

For more long term special case handling, can't each MSIOF instance
request the frequency independently and the parent clock CPG/MSSR
driver can take all the child frequency requests into consideration
and set something that satisfies everyone? clk_set_rate_range() and/or
struct clk_rate_request seem to provide some way to pass on this
information. During probe of the MSIOF driver perhaps the devices need
to be queued up early on and once all the devices are deemed to be
loaded then the devices are started in order with the lowest requested
frequency first. Of course, if we need to load a new slow speed device
later on then the existing ones may need to be ripped out and the
whole bunch reinstalled. I believe this is similar to "Option 2"
above.

Thanks,

/ magnus

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype
  2016-08-25  5:34 ` [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Magnus Damm
@ 2016-08-29 12:38   ` Geert Uytterhoeven
  0 siblings, 0 replies; 15+ messages in thread
From: Geert Uytterhoeven @ 2016-08-29 12:38 UTC (permalink / raw)
  To: Magnus Damm; +Cc: Geert Uytterhoeven, Linux-Renesas, linux-spi, linux-clk

Hi Magnus,

On Thu, Aug 25, 2016 at 7:34 AM, Magnus Damm <magnus.damm@gmail.com> wrote:
> On Sat, Aug 13, 2016 at 1:38 AM, Geert Uytterhoeven
> <geert+renesas@glider.be> wrote:
>> On R-Car H3, the various MSIOF instances (used for SPI) have a common
>> parent clock. This mso clock is a programmable "DIV6" clock (divider
>> 1 - 64). Its rate lies in the range 12.5 to 800 MHz, or 6.25 to 400 MHz
>> (depending on the main crystal).
>> After boot up, the default configuration is the lowest possible rate,
>> which leads to bad performance with devices desiring a higher transfer
>> rate.
>>
>> MSIOF clock tree:
>>
>>     pll1/4 (800 or 400 MHz)
>>       mso clock
>>         msiof0 clock (type MSTP = gate + status)
>>           msiof0 internal divider (divider range 1 - 1024, with holes)
>>         msiof1 clock (type MSTP)
>>           msiof1 internal divider
>>         msiof2 clock (type MSTP)
>>           msiof2 internal divider
>>         msiof3 clock (type MSTP)
>>           msiof3 internal divider
>>
>> To optimize performance, we want to control the mso parent clock.
>>
>>   - Higher parent clock means:
>>       + Better performance for devices wanting high target rates,
>>       + More accurate target rates in MSIOF driver,
>>       - More power consumption (doesn't matter that much with R-Car),
>>       - Cannot support devices needing real low target rates.
>>   - Lower parent clock means:
>>       + Less power consumption (doesn't matter that much with R-Car),
>>       + Can support all devices,
>>       - Worse performance for devices wanting high target rates,
>>       - Less accurate target rates, decreasing performance.
>>
>> Configuring the mso parent clock rate can either be done statically, or
>> dynamically. In both cases, this must be based on individual device
>> requirements. Note that this is not specific to SPI.
>>
>> SPI specific notes:
>>   - The SPI master knows about SPI slave device maximum frequencies only
>>     when spi_master.setup() is called,
>>   - SPI slave device maximum frequencies may change at runtime (through
>>     calling spi_setup(), esp. for spidev),
>>   - Currently the MSIOF driver does not set
>>     spi_master.{min,max}_speed_hz. However, setting it means the SPI
>>     core will never call spi_master.setup() with a rate outside this
>>     range, and thus we will never be informed of such rates.  Hence if
>>     we decide to set this, and we want dynamic configuration, we should
>>     fill in the real minimum and maximum supported by the hardware,
>>     regardless of the current parent clock rate.
>>
>> In this prototype, I'm offering 3 solutions to control the mso parent
>> clock rate.
>>
>>   - Option 1: Static configuration through the "assigned-clocks" and
>>     assigned-clock-rates DT properties.
>>
>>   - Option 2: Dynamic configuration, by changing the mso clock through
>>     the msiofX module clocks from the MSIOF driver.
>>
>>   - Option 2: Dynamic configuration, by changing a dummy clk-divider,
>>     which mimics the capabilities of the MSIOF internal divider.
>
> Thanks for cooking up these solutions. I dislike putting software
> policy into DT, but at least Option 1 is simple. Option 2 and 3 seem a
> bit overly complex to me.
>
> May I ask if you considered solving the common case and special case
> independently?

I think all of this is sufficiently generic to apply to other parent clocks and
devices, too.

> Regarding the common case, I think it is sane to assume that everyone
> wants a high performing solution if possible. So using highest clock
> rate possible must be a good default to as long as we can be sure to
> also be able to handle devices wanting low clock rates. In my mind
> this would mean that a standard unmodified DT should result in highest
> performance possible. This probably involves programming the CPG/MSSR
> hardware during boot-up based on some table with maximum supported
> frequency. The high performing setting may or may not be the default
> from reset (or boot loader) today, but perhaps it makes sense to rely
> less on hardware settings performed during boot? At least this seems
> like a good long term aim to me.

We can easily add default rates to the clock tables in the CPG/MSSR driver,
and program them during clock driver initialization. This can still be
overridden from DT using "assigned-clock-rates".

Whether this is something we want to do now is another question:
MSIOF is unusable for SPI on (early) H3, and the Salvator-X development board
doesn't have any on-board SPI devices connected to MSIOF SPI masters, so we
don't have a use case for which we can define defaults.
Hence changes are high the user who develops his own (expansion) board will
have to configure a different value using "assigned-clock-rates" anyway.

Perhaps it's a better idea to add a comment to the r8a7795.dtsi or
r8a7795-salvator-x.dts file with an example "assigned-clock-rates" property,
serving as documentation for board designers, cfr. the clock-frequency values
to be overridden by the board DTS?

For other parent clocks related to devices which are used on Salvator-X, we can
provide sensible defaults in the CPG/MSSR driver.

> As for short term special case handling, the only issue we are having
> is when more than one MSIOF instance want to use frequencies that are
> incompatible. The first MSIOF parent clock user should be able to

IMHO there are two issues:
  1. Incompatible parent clock frequencies, due to the 1-1024 divider,
  2. Suboptimal clock frequencies, due to granularity of the clock rate.
      E.g. if the parent clock runs at 31 MHz and the device has an upper limit
      of 30 MHz, you can not use it at speeds higher than 15.5 MHz.
      Lowering the parent clock will increase performance here.

Of course the first is worse, as it totally prohibits using the device.

> select whichever rate it wants in theory, but with more than one MSIOF
> instance then if one MSIOF instance wants to use the highest frequency
> possible and the other the lowest possible then the 1024 divider is
> not enough to cover both. From my side I would say it makes sense to
> cover this special case with "Option 1" above. To avoid probe order

I agree "Option 1" is the best solution to make sure the parent clock rate
is usable for all SPI slave devices.

> issues the CPG/MSSR driver handling the parent clock could for
> instance early during boot look for DT frequency properties in the DTB
> and adjust the shared default rate based on that.

That's an alternative run-time way of determining a suitable parent clock rate.
This does mean the CPG/MSSR driver must be made aware of consumer devices,
their divider limitations, and device-specific properties ("spi-max-frequency");
has to scan for those in DT, and find a suitable parent clock rate.
It won't work when using DT overlays, while the system integrator can take
 those into account when specifying the global "assigned-clock-rates" property.

> For more long term special case handling, can't each MSIOF instance
> request the frequency independently and the parent clock CPG/MSSR
> driver can take all the child frequency requests into consideration
> and set something that satisfies everyone? clk_set_rate_range() and/or
> struct clk_rate_request seem to provide some way to pass on this
> information. During probe of the MSIOF driver perhaps the devices need
> to be queued up early on and once all the devices are deemed to be
> loaded then the devices are started in order with the lowest requested
> frequency first. Of course, if we need to load a new slow speed device
> later on then the existing ones may need to be ripped out and the
> whole bunch reinstalled. I believe this is similar to "Option 2"
> above.

Unfortunately spi_master.setup() is called sequentially for all SPI devices,
while we need to coordinate them to find a suitable parent clock frequency.

So again I think "Option 1" is the best solution.

Note that if we let software decide the optimal parent clock rate, the CPG/MSSR
driver should have frequency limits in its tables, to avoid programming
out-of-range frequencies. The DIV6 clock register layout allows for dividing
the 400 or 800 MHz clock by 1..64, while the MSO clock itself is limited to 66
MHz, according to the documentation.

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

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2016-08-29 12:38 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-12 16:38 [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Geert Uytterhoeven
2016-08-12 16:38 ` [PATCH/PROTO 1/9 common] spi: sh-msiof: Add support for R-Car H3 Geert Uytterhoeven
2016-08-12 16:38 ` [PATCH/PROTO 2/9 common] spi: sh-msiof: Print max and transfer frequency Geert Uytterhoeven
2016-08-12 16:38 ` [PATCH/PROTO 3/9 common] arm64: dts: r8a7795: Add all MSIOF nodes Geert Uytterhoeven
2016-08-12 16:38 ` [PATCH/PROTO 4/9 common] arm64: dts: salvator-x: Add dummy MSIOF SPI slave devices Geert Uytterhoeven
2016-08-12 16:38   ` Geert Uytterhoeven
2016-08-12 16:38 ` [PATCH/PROTO 5/9 option 1] arm64: dts: salvator-x: Configure MSIOF parent clock Geert Uytterhoeven
2016-08-12 16:38 ` [PATCH/PROTO 6/9 option 2/3] spi: sh-msiof: Add clock notifier to enforce valid " Geert Uytterhoeven
2016-08-12 16:38 ` [PATCH/PROTO 7/9 option 2] spi: sh-msiof: Configure MSIOF " Geert Uytterhoeven
2016-08-12 16:38   ` Geert Uytterhoeven
2016-08-12 16:38 ` [PATCH/PROTO 8/9 option 3] clk: divider: Add hack to support dummy clocks Geert Uytterhoeven
2016-08-12 16:38 ` [PATCH/PROTO 9/9 option 3] spi: sh-msiof: Configure MSIOF parent clock Geert Uytterhoeven
2016-08-12 16:38   ` Geert Uytterhoeven
2016-08-25  5:34 ` [PATCH/PROTO 0/9] R-Car H3 MSIOF Parent Clock Control Prototype Magnus Damm
2016-08-29 12:38   ` Geert Uytterhoeven

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.