From 960e1c4d93be86d3b118fe22d4edc69e401b28b5 Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Mon, 11 Dec 2017 17:55:33 +0100 Subject: clk: at91: pmc: Wait for clocks when resuming Wait for the syncronization of all clocks when resuming, not only the UPLL clock. Do not use regmap_read_poll_timeout, as it will call BUG() when interrupts are masked, which is the case in here. Signed-off-by: Romain Izard Acked-by: Ludovic Desroches Acked-by: Nicolas Ferre Acked-by: Alexandre Belloni Signed-off-by: Stephen Boyd --- drivers/clk/at91/pmc.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 775af473fe11..5c2b26de303e 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -107,10 +107,20 @@ static int pmc_suspend(void) return 0; } +static bool pmc_ready(unsigned int mask) +{ + unsigned int status; + + regmap_read(pmcreg, AT91_PMC_SR, &status); + + return ((status & mask) == mask) ? 1 : 0; +} + static void pmc_resume(void) { - int i, ret = 0; + int i; u32 tmp; + u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA; regmap_read(pmcreg, AT91_PMC_MCKR, &tmp); if (pmc_cache.mckr != tmp) @@ -134,13 +144,11 @@ static void pmc_resume(void) AT91_PMC_PCR_CMD); } - if (pmc_cache.uckr & AT91_PMC_UPLLEN) { - ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp, - !(tmp & AT91_PMC_LOCKU), - 10, 5000); - if (ret) - pr_crit("USB PLL didn't lock when resuming\n"); - } + if (pmc_cache.uckr & AT91_PMC_UPLLEN) + mask |= AT91_PMC_LOCKU; + + while (!pmc_ready(mask)) + cpu_relax(); } static struct syscore_ops pmc_syscore_ops = { -- cgit v1.2.3 From 3c6fad2593d75a1674c5c2b19c78552c48ef46b5 Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Mon, 11 Dec 2017 17:55:34 +0100 Subject: clk: at91: pmc: Save SCSR during suspend The contents of the System Clock Status Register (SCSR) needs to be restored into the System Clock Enable Register (SCER). As the bootloader will restore some clocks by itself, the issue can be missed as only the USB controller, the LCD controller, the Image Sensor controller and the programmable clocks will be impacted. Fix the obvious typo in the suspend/resume code, as the IMR register does not need to be saved twice. Signed-off-by: Romain Izard Acked-by: Nicolas Ferre Acked-by: Alexandre Belloni Signed-off-by: Stephen Boyd --- drivers/clk/at91/pmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 5c2b26de303e..07dc2861ad3f 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -86,7 +86,7 @@ static int pmc_suspend(void) { int i; - regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.scsr); + regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr); regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0); regmap_read(pmcreg, AT91_CKGR_UCKR, &pmc_cache.uckr); regmap_read(pmcreg, AT91_CKGR_MOR, &pmc_cache.mor); @@ -129,7 +129,7 @@ static void pmc_resume(void) if (pmc_cache.pllar != tmp) pr_warn("PLLAR was not configured properly by the firmware\n"); - regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.scsr); + regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr); regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0); regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr); regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor); -- cgit v1.2.3 From 13967bea0bdb194b8674b4102fcdd383a8a18baa Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Mon, 11 Dec 2017 17:55:35 +0100 Subject: clk: at91: pmc: Support backup for programmable clocks When an AT91 programmable clock is declared in the device tree, register it into the Power Management Controller driver. On entering suspend mode, the driver saves and restores the Programmable Clock registers to support the backup mode for these clocks. Signed-off-by: Romain Izard Acked-by: Nicolas Ferre Acked-by: Alexandre Belloni Signed-off-by: Stephen Boyd --- drivers/clk/at91/clk-programmable.c | 2 ++ drivers/clk/at91/pmc.c | 35 +++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 2 ++ 3 files changed, 39 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 85a449cf61e3..0e6aab1252fc 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -204,6 +204,8 @@ at91_clk_register_programmable(struct regmap *regmap, if (ret) { kfree(prog); hw = ERR_PTR(ret); + } else { + pmc_register_pck(id); } return hw; diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 07dc2861ad3f..1fa27f4ea538 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -22,6 +22,7 @@ #include "pmc.h" #define PMC_MAX_IDS 128 +#define PMC_MAX_PCKS 8 int of_at91_get_clk_range(struct device_node *np, const char *propname, struct clk_range *range) @@ -50,6 +51,7 @@ EXPORT_SYMBOL_GPL(of_at91_get_clk_range); static struct regmap *pmcreg; static u8 registered_ids[PMC_MAX_IDS]; +static u8 registered_pcks[PMC_MAX_PCKS]; static struct { @@ -66,8 +68,13 @@ static struct u32 pcr[PMC_MAX_IDS]; u32 audio_pll0; u32 audio_pll1; + u32 pckr[PMC_MAX_PCKS]; } pmc_cache; +/* + * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored + * without alteration in the table, and 0 is for unused clocks. + */ void pmc_register_id(u8 id) { int i; @@ -82,9 +89,28 @@ void pmc_register_id(u8 id) } } +/* + * As Programmable Clock 0 is valid on AT91 chips, there is an offset + * of 1 between the stored value and the real clock ID. + */ +void pmc_register_pck(u8 pck) +{ + int i; + + for (i = 0; i < PMC_MAX_PCKS; i++) { + if (registered_pcks[i] == 0) { + registered_pcks[i] = pck + 1; + break; + } + if (registered_pcks[i] == (pck + 1)) + break; + } +} + static int pmc_suspend(void) { int i; + u8 num; regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr); regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0); @@ -103,6 +129,10 @@ static int pmc_suspend(void) regmap_read(pmcreg, AT91_PMC_PCR, &pmc_cache.pcr[registered_ids[i]]); } + for (i = 0; registered_pcks[i]; i++) { + num = registered_pcks[i] - 1; + regmap_read(pmcreg, AT91_PMC_PCKR(num), &pmc_cache.pckr[num]); + } return 0; } @@ -119,6 +149,7 @@ static bool pmc_ready(unsigned int mask) static void pmc_resume(void) { int i; + u8 num; u32 tmp; u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA; @@ -143,6 +174,10 @@ static void pmc_resume(void) pmc_cache.pcr[registered_ids[i]] | AT91_PMC_PCR_CMD); } + for (i = 0; registered_pcks[i]; i++) { + num = registered_pcks[i] - 1; + regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]); + } if (pmc_cache.uckr & AT91_PMC_UPLLEN) mask |= AT91_PMC_LOCKU; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 858e8ef7e8db..d22b1fa9ecdc 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -31,8 +31,10 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname, #ifdef CONFIG_PM void pmc_register_id(u8 id); +void pmc_register_pck(u8 pck); #else static inline void pmc_register_id(u8 id) {} +static inline void pmc_register_pck(u8 pck) {} #endif #endif /* __PMC_H_ */ -- cgit v1.2.3 From f8f8f1d04494d3a6546bee3f0618c4dba31d7b72 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 2 Nov 2017 00:36:09 -0700 Subject: clk: Don't touch hardware when reparenting during registration The orphan clocks reparent operation shouldn't touch the hardware if clocks are enabled, otherwise it may get a chance to disable a newly registered critical clock which triggers the warning below. Assuming we have two clocks: A and B, B is the parent of A. Clock A has flag: CLK_OPS_PARENT_ENABLE Clock B has flag: CLK_IS_CRITICAL Step 1: Clock A is registered, then it becomes orphan. Step 2: Clock B is registered. Before clock B reach the critical clock enable operation, orphan A will find the newly registered parent B and do reparent operation, then parent B will be finally disabled in __clk_set_parent_after() due to CLK_OPS_PARENT_ENABLE flag as there's still no users of B which will then trigger the following warning. WARNING: CPU: 0 PID: 0 at drivers/clk/clk.c:597 clk_core_disable+0xb4/0xe0 Modules linked in: CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.11.0-rc1-00056-gdff1f66-dirty #1373 Hardware name: Generic DT based system Backtrace: [] (dump_backtrace) from [] (show_stack+0x18/0x1c) r6:600000d3 r5:00000000 r4:c0e26358 r3:00000000 [] (show_stack) from [] (dump_stack+0xb4/0xe8) [] (dump_stack) from [] (__warn+0xd8/0x104) r10:c0c21cd0 r9:c048aa78 r8:00000255 r7:00000009 r6:c0c1cd90 r5:00000000 r4:00000000 r3:c0e01d34 [] (__warn) from [] (warn_slowpath_null+0x28/0x30) r9:00000000 r8:ef00bf80 r7:c165ac4c r6:ef00bf80 r5:ef00bf80 r4:ef00bf80 [] (warn_slowpath_null) from [] (clk_core_disable+0xb4/0xe0) [] (clk_core_disable) from [] (clk_core_disable_lock+0x20/0x2c) r4:000000d3 r3:c0e0af00 [] (clk_core_disable_lock) from [] (clk_core_disable_unprepare+0x14/0x28) r5:00000000 r4:ef00bf80 [] (clk_core_disable_unprepare) from [] (__clk_set_parent_after+0x38/0x54) r4:ef00bd80 r3:000010a0 [] (__clk_set_parent_after) from [] (clk_register+0x4d0/0x648) r6:ef00d500 r5:ef00bf80 r4:ef00bd80 r3:ef00bfd4 [] (clk_register) from [] (clk_hw_register+0x10/0x1c) r9:00000000 r8:00000003 r7:00000000 r6:00000824 r5:00000001 r4:ef00d500 [] (clk_hw_register) from [] (_register_divider+0xcc/0x120) [] (_register_divider) from [] (clk_register_divider+0x44/0x54) r10:00000004 r9:00000003 r8:00000001 r7:00000000 r6:00000003 r5:00000001 r4:f0810030 [] (clk_register_divider) from [] (imx7ulp_clocks_init+0x558/0xe98) r7:c0e296f8 r6:c165c808 r5:00000000 r4:c165c808 [] (imx7ulp_clocks_init) from [] (of_clk_init+0x118/0x1e0) r10:00000001 r9:c0e01f68 r8:00000000 r7:c0e01f60 r6:ef7f8974 r5:ef0035c0 r4:00000006 [] (of_clk_init) from [] (time_init+0x2c/0x38) r10:efffed40 r9:c0d61a48 r8:c0e78000 r7:c0e07900 r6:ffffffff r5:c0e78000 r4:00000000 [] (time_init) from [] (start_kernel+0x218/0x394) [] (start_kernel) from [<6000807c>] (0x6000807c) r10:00000000 r9:410fc075 r8:6000406a r7:c0e0c930 r6:c0d61a44 r5:c0e07918 r4:c0e78294 We know that the clk isn't enabled with any sort of prepare_count here so we don't need to enable anything to prevent a race. And we're holding the prepare mutex so set_rate/set_parent can't race here either. Based on an earlier patch by Dong Aisheng. Fixes: fc8726a2c021 ("clk: core: support clocks which requires parents enable (part 2)") Cc: Michael Turquette Cc: Shawn Guo Reported-by: Dong Aisheng Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 647d056df88c..999777a66505 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2570,14 +2570,17 @@ static int __clk_core_init(struct clk_core *core) */ hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { struct clk_core *parent = __clk_init_parent(orphan); + unsigned long flags; /* * we could call __clk_set_parent, but that would result in a * redundant call to the .set_rate op, if it exists */ if (parent) { - __clk_set_parent_before(orphan, parent); - __clk_set_parent_after(orphan, parent, NULL); + /* update the clk tree topology */ + flags = clk_enable_lock(); + clk_reparent(orphan, parent); + clk_enable_unlock(flags); __clk_recalc_accuracies(orphan); __clk_recalc_rates(orphan, 0); } -- cgit v1.2.3 From 063578dc5f407f67d149133818efabe457daafda Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 5 Sep 2017 11:32:40 +0200 Subject: clk: axi-clkgen: Correctly handle nocount bit in recalc_rate() If the nocount bit is set the divider is bypassed and the settings for the divider count should be ignored and a divider value of 1 should be assumed. Handle this correctly in the driver recalc_rate() callback. While the driver sets up the part so that the read back dividers values yield the correct result the power-on reset settings of the part might not reflect this and hence calling e.g. clk_get_rate() without prior calls to clk_set_rate() will yield the wrong result. Signed-off-by: Lars-Peter Clausen Signed-off-by: Stephen Boyd --- drivers/clk/clk-axi-clkgen.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 5e918e7afaba..95a6e9834392 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -40,6 +40,10 @@ #define MMCM_REG_FILTER1 0x4e #define MMCM_REG_FILTER2 0x4f +#define MMCM_CLKOUT_NOCOUNT BIT(6) + +#define MMCM_CLK_DIV_NOCOUNT BIT(12) + struct axi_clkgen { void __iomem *base; struct clk_hw clk_hw; @@ -315,12 +319,27 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, unsigned int reg; unsigned long long tmp; - axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®); - dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_2, ®); + if (reg & MMCM_CLKOUT_NOCOUNT) { + dout = 1; + } else { + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®); + dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); + } + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®); - d = (reg & 0x3f) + ((reg >> 6) & 0x3f); - axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®); - m = (reg & 0x3f) + ((reg >> 6) & 0x3f); + if (reg & MMCM_CLK_DIV_NOCOUNT) + d = 1; + else + d = (reg & 0x3f) + ((reg >> 6) & 0x3f); + + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB2, ®); + if (reg & MMCM_CLKOUT_NOCOUNT) { + m = 1; + } else { + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®); + m = (reg & 0x3f) + ((reg >> 6) & 0x3f); + } if (d == 0 || dout == 0) return 0; -- cgit v1.2.3 From 448c3c057a1dbb6ecf6e507a3f1a58f5eab21560 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 5 Sep 2017 11:32:41 +0200 Subject: clk: axi-clkgen: Round closest in round_rate() and recalc_rate() To minimize the rounding error round to the closest integer when calculating the result in the recalc_rate() and set_rate() callbacks. Also in order to improve precision multiply first and then divide. Signed-off-by: Lars-Peter Clausen Signed-off-by: Stephen Boyd --- drivers/clk/clk-axi-clkgen.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 95a6e9834392..48d11f2598e8 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -302,13 +302,17 @@ static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { unsigned int d, m, dout; + unsigned long long tmp; axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout); if (d == 0 || dout == 0 || m == 0) return -EINVAL; - return *parent_rate / d * m / dout; + tmp = (unsigned long long)*parent_rate * m; + tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d); + + return min_t(unsigned long long, tmp, LONG_MAX); } static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, @@ -344,8 +348,8 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, if (d == 0 || dout == 0) return 0; - tmp = (unsigned long long)(parent_rate / d) * m; - do_div(tmp, dout); + tmp = (unsigned long long)parent_rate * m; + tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d); return min_t(unsigned long long, tmp, ULONG_MAX); } -- cgit v1.2.3 From 758231d5a80a784d60ce7c96b27f8771ca4c682b Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 16 Sep 2017 01:30:30 +0300 Subject: clk: si5351: implement remove handler The driver has no remove function, so it does not cleanup resources that are not under devm management. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5351.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 20d90769cced..36a15f161dfd 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -1619,6 +1619,18 @@ err_clk: return ret; } +static int si5351_i2c_remove(struct i2c_client *client) +{ + struct si5351_driver_data *drvdata = i2c_get_clientdata(client); + + of_clk_del_provider(client->dev.of_node); + if (!IS_ERR(drvdata->pxtal)) + clk_disable_unprepare(drvdata->pxtal); + if (!IS_ERR(drvdata->pclkin)) + clk_disable_unprepare(drvdata->pclkin); + return 0; +} + static const struct i2c_device_id si5351_i2c_ids[] = { { "si5351a", SI5351_VARIANT_A }, { "si5351a-msop", SI5351_VARIANT_A3 }, @@ -1634,6 +1646,7 @@ static struct i2c_driver si5351_driver = { .of_match_table = of_match_ptr(si5351_dt_ids), }, .probe = si5351_i2c_probe, + .remove = si5351_i2c_remove, .id_table = si5351_i2c_ids, }; module_i2c_driver(si5351_driver); -- cgit v1.2.3 From 51279ef9f64cf7eb8b3f891a2b60fa1aa4938afc Mon Sep 17 00:00:00 2001 From: Sergej Sawazki Date: Sat, 16 Sep 2017 13:44:41 +0200 Subject: clk: si5351: Add DT property to enable PLL reset Add optional output clock DT property to enable PLL reset when a clock output is enabled. Cc: Sebastian Hesselbarth Cc: Rabeeh Khoury Cc: Russell King Signed-off-by: Sergej Sawazki Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/clock/silabs,si5351.txt | 1 + drivers/clk/clk-si5351.c | 3 +++ include/linux/platform_data/si5351.h | 2 ++ 3 files changed, 6 insertions(+) (limited to 'drivers/clk') diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt index a6c4ef343b44..f00191cad8cd 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si5351.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt @@ -49,6 +49,7 @@ Optional child node properties: - silabs,multisynth-source: source pll A(0) or B(1) of corresponding multisynth divider. - silabs,pll-master: boolean, multisynth can change pll frequency. +- silabs,pll-reset: boolean, clock output can reset its pll. - silabs,disable-state : clock output disable state, shall be 0 = clock output is driven LOW when disabled 1 = clock output is driven HIGH when disabled diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 36a15f161dfd..f63fcc0d8cf5 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -1297,6 +1297,9 @@ static int si5351_dt_parse(struct i2c_client *client, pdata->clkout[num].pll_master = of_property_read_bool(child, "silabs,pll-master"); + + pdata->clkout[num].pll_reset = + of_property_read_bool(child, "silabs,pll-reset"); } client->dev.platform_data = pdata; diff --git a/include/linux/platform_data/si5351.h b/include/linux/platform_data/si5351.h index 818c5c6e203f..c71a2dd66143 100644 --- a/include/linux/platform_data/si5351.h +++ b/include/linux/platform_data/si5351.h @@ -86,6 +86,7 @@ enum si5351_disable_state { * @multisynth_src: multisynth source clock * @clkout_src: clkout source clock * @pll_master: if true, clkout can also change pll rate + * @pll_reset: if true, clkout can reset its pll * @drive: output drive strength * @rate: initial clkout rate, or default if 0 */ @@ -95,6 +96,7 @@ struct si5351_clkout_config { enum si5351_drive_strength drive; enum si5351_disable_state disable_state; bool pll_master; + bool pll_reset; unsigned long rate; }; -- cgit v1.2.3 From b26ff127c52c005ac4eb99ebff7bd17c240c2e89 Mon Sep 17 00:00:00 2001 From: Sergej Sawazki Date: Sat, 16 Sep 2017 13:44:42 +0200 Subject: clk: si5351: Apply PLL soft reset before enabling the outputs The "Si5351A/B/C Data Sheet" states to apply a PLL soft reset before enabling the output clocks [1]. This is required to get a deterministic phase relationship between the output clocks. Without resetting the PLL, the phase relationship between the clocks is unpredictable. Fix this by resetting the PLL in si5351_clkout_prepare(). References: [1] https://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351-B.pdf Figure 12 ("I2C Programming Procedure") Cc: Sebastian Hesselbarth Cc: Rabeeh Khoury Cc: Russell King Signed-off-by: Sergej Sawazki Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5351.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index f63fcc0d8cf5..79b770fabe05 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -903,13 +903,42 @@ static int _si5351_clkout_set_disable_state( return 0; } +void _si5351_clkout_reset_pll(struct si5351_driver_data *drvdata, int num) +{ + u8 val = si5351_reg_read(drvdata, SI5351_CLK0_CTRL + num); + + switch (val & SI5351_CLK_INPUT_MASK) { + case SI5351_CLK_INPUT_XTAL: + case SI5351_CLK_INPUT_CLKIN: + return; /* pll not used, no need to reset */ + } + + si5351_reg_write(drvdata, SI5351_PLL_RESET, + val & SI5351_CLK_PLL_SELECT ? SI5351_PLL_RESET_B : + SI5351_PLL_RESET_A); + + dev_dbg(&drvdata->client->dev, "%s - %s: pll = %d\n", + __func__, clk_hw_get_name(&drvdata->clkout[num].hw), + (val & SI5351_CLK_PLL_SELECT) ? 1 : 0); +} + static int si5351_clkout_prepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); + struct si5351_platform_data *pdata = + hwdata->drvdata->client->dev.platform_data; si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, SI5351_CLK_POWERDOWN, 0); + + /* + * Do a pll soft reset on the parent pll -- needed to get a + * deterministic phase relationship between the output clocks. + */ + if (pdata->clkout[hwdata->num].pll_reset) + _si5351_clkout_reset_pll(hwdata->drvdata, hwdata->num); + si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, (1 << hwdata->num), 0); return 0; -- cgit v1.2.3 From cdba9a4fb0b53703959ac861e415816cb61aded4 Mon Sep 17 00:00:00 2001 From: Sergej Sawazki Date: Tue, 25 Jul 2017 23:21:02 +0200 Subject: clk: si5351: Rename internal plls to avoid name collisions This drivers probe fails due to a clock name collision if a clock named 'plla' or 'pllb' is already registered when registering this drivers internal plls. Fix it by renaming internal plls to avoid name collisions. Cc: Sebastian Hesselbarth Cc: Rabeeh Khoury Signed-off-by: Sergej Sawazki Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5351.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 79b770fabe05..841d9fe9bfea 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -72,7 +72,7 @@ static const char * const si5351_input_names[] = { "xtal", "clkin" }; static const char * const si5351_pll_names[] = { - "plla", "pllb", "vxco" + "si5351_plla", "si5351_pllb", "si5351_vxco" }; static const char * const si5351_msynth_names[] = { "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7" -- cgit v1.2.3 From 1fffaf6aed88671b458c66891f66c914d5bd148d Mon Sep 17 00:00:00 2001 From: Sergej Sawazki Date: Tue, 25 Jul 2017 20:32:11 +0200 Subject: clk: si5351: Do not enable parent clocks on probe The si5351 driver should not prepare or enable other clocks in the tree on probe. Let the clients decide when to prepare or enable the clocks. Cc: Sebastian Hesselbarth Cc: Rabeeh Khoury Signed-off-by: Sergej Sawazki [sboyd@codeaurora.org: Remove most of the .remove function too] Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5351.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 841d9fe9bfea..10f99400ca61 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -1469,11 +1469,6 @@ static int si5351_i2c_probe(struct i2c_client *client, } } - if (!IS_ERR(drvdata->pxtal)) - clk_prepare_enable(drvdata->pxtal); - if (!IS_ERR(drvdata->pclkin)) - clk_prepare_enable(drvdata->pclkin); - /* register xtal input clock gate */ memset(&init, 0, sizeof(init)); init.name = si5351_input_names[0]; @@ -1488,7 +1483,7 @@ static int si5351_i2c_probe(struct i2c_client *client, ret = devm_clk_hw_register(&client->dev, &drvdata->xtal); if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } /* register clkin input clock gate */ @@ -1506,7 +1501,7 @@ static int si5351_i2c_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } } @@ -1528,7 +1523,7 @@ static int si5351_i2c_probe(struct i2c_client *client, ret = devm_clk_hw_register(&client->dev, &drvdata->pll[0].hw); if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } /* register PLLB or VXCO (Si5351B) */ @@ -1552,7 +1547,7 @@ static int si5351_i2c_probe(struct i2c_client *client, ret = devm_clk_hw_register(&client->dev, &drvdata->pll[1].hw); if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } /* register clk multisync and clk out divider */ @@ -1571,7 +1566,7 @@ static int si5351_i2c_probe(struct i2c_client *client, if (WARN_ON(!drvdata->msynth || !drvdata->clkout)) { ret = -ENOMEM; - goto err_clk; + return ret; } for (n = 0; n < num_clocks; n++) { @@ -1591,7 +1586,7 @@ static int si5351_i2c_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } } @@ -1619,7 +1614,7 @@ static int si5351_i2c_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } /* set initial clkout rate */ @@ -1638,28 +1633,16 @@ static int si5351_i2c_probe(struct i2c_client *client, drvdata); if (ret) { dev_err(&client->dev, "unable to add clk provider\n"); - goto err_clk; + return ret; } return 0; - -err_clk: - if (!IS_ERR(drvdata->pxtal)) - clk_disable_unprepare(drvdata->pxtal); - if (!IS_ERR(drvdata->pclkin)) - clk_disable_unprepare(drvdata->pclkin); - return ret; } static int si5351_i2c_remove(struct i2c_client *client) { - struct si5351_driver_data *drvdata = i2c_get_clientdata(client); - of_clk_del_provider(client->dev.of_node); - if (!IS_ERR(drvdata->pxtal)) - clk_disable_unprepare(drvdata->pxtal); - if (!IS_ERR(drvdata->pclkin)) - clk_disable_unprepare(drvdata->pclkin); + return 0; } -- cgit v1.2.3 From 869de5cf96cf3952e8f998581cd368e38cebf8d2 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Tue, 26 Dec 2017 15:30:36 +0200 Subject: clk: pxa: unbreak lookup of CLK_POUT Since switching to clk drivers, the CLK_POUT cannot be searched for by clk_get() API and thus it returns with ENOENT. Register it with the clk_lookup and thus unbreak the users of it. Signed-off-by: Igor Grinberg Acked-by: Robert Jarzmik Signed-off-by: Stephen Boyd --- drivers/clk/pxa/clk-pxa3xx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/pxa/clk-pxa3xx.c b/drivers/clk/pxa/clk-pxa3xx.c index 42bdaa772be0..2d126df2bccd 100644 --- a/drivers/clk/pxa/clk-pxa3xx.c +++ b/drivers/clk/pxa/clk-pxa3xx.c @@ -329,12 +329,16 @@ static void __init pxa3xx_dummy_clocks_init(void) static void __init pxa3xx_base_clocks_init(void) { + struct clk *clk; + pxa3xx_register_plls(); pxa3xx_register_core(); clk_register_clk_pxa3xx_system_bus(); clk_register_clk_pxa3xx_ac97(); clk_register_clk_pxa3xx_smemc(); - clk_register_gate(NULL, "CLK_POUT", "osc_13mhz", 0, OSCC, 11, 0, NULL); + clk = clk_register_gate(NULL, "CLK_POUT", + "osc_13mhz", 0, OSCC, 11, 0, NULL); + clk_register_clkdev(clk, "CLK_POUT", NULL); clkdev_pxa_register(CLK_OSTIMER, "OSTIMER0", NULL, clk_register_fixed_factor(NULL, "os-timer0", "osc_13mhz", 0, 1, 4)); -- cgit v1.2.3 From 0136f852be24fd9de35f0d0c1a1d2b2dcb5eb612 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 27 Dec 2017 12:25:13 +0800 Subject: clk: si5351: _si5351_clkout_reset_pll() can be static Fixes: b26ff127c52c ("clk: si5351: Apply PLL soft reset before enabling the outputs") Signed-off-by: Fengguang Wu Signed-off-by: Stephen Boyd --- drivers/clk/clk-si5351.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 10f99400ca61..50e7c341e97e 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -903,7 +903,7 @@ static int _si5351_clkout_set_disable_state( return 0; } -void _si5351_clkout_reset_pll(struct si5351_driver_data *drvdata, int num) +static void _si5351_clkout_reset_pll(struct si5351_driver_data *drvdata, int num) { u8 val = si5351_reg_read(drvdata, SI5351_CLK0_CTRL + num); -- cgit v1.2.3