diff mbox

[PATCH/PROTO,9/9,option,3] spi: sh-msiof: Configure MSIOF parent clock

Message ID 1471019925-29083-10-git-send-email-geert+renesas@glider.be (mailing list archive)
State RFC
Delegated to: Geert Uytterhoeven
Headers show

Commit Message

Geert Uytterhoeven Aug. 12, 2016, 4:38 p.m. UTC
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 mbox

Patch

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;