diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-04-19 20:16:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-04-19 20:16:18 -0400 |
commit | f61143c45077df4fa78e2f1ba455a00bbe1d5b8c (patch) | |
tree | 3dc9bbd23deacbf21b70e742f75d54f1f496c8e9 | |
parent | 4988f7a40f45929588d4b8f09c71eb785f55a19c (diff) | |
parent | e7590308d17e578e47f298cc3fec359108341cb6 (diff) |
Merge tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk fixes from Stephen Boyd"
- one stm32f4 fix for a change that introduced the PLL_I2S and PLL_SAI
boards
- two Allwinner clk driver build fixes
- two Allwinner CPU clk driver fixes where we see random CPUFreq
crashes because the CPU's PLL locks up sometimes when we change the
rate
* tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux:
clk: sunxi-ng: a33: gate then ungate PLL CPU clk after rate change
clk: sunxi-ng: Add clk notifier to gate then ungate PLL clocks
clk: sunxi-ng: fix build failure in ccu-sun9i-a80 driver
clk: sunxi-ng: fix build error without CONFIG_RESET_CONTROLLER
clk: stm32f4: fix: exclude values 0 and 1 for PLLQ
-rw-r--r-- | drivers/clk/clk-stm32f4.c | 13 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/Kconfig | 2 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/ccu-sun8i-a33.c | 11 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/ccu_common.c | 49 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/ccu_common.h | 12 |
5 files changed, 84 insertions, 3 deletions
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c index ab609a76706f..cf9449b3dbd9 100644 --- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c | |||
@@ -429,6 +429,13 @@ static const struct clk_div_table pll_divp_table[] = { | |||
429 | { 0, 2 }, { 1, 4 }, { 2, 6 }, { 3, 8 }, { 0 } | 429 | { 0, 2 }, { 1, 4 }, { 2, 6 }, { 3, 8 }, { 0 } |
430 | }; | 430 | }; |
431 | 431 | ||
432 | static const struct clk_div_table pll_divq_table[] = { | ||
433 | { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, | ||
434 | { 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 }, { 12, 12 }, { 13, 13 }, | ||
435 | { 14, 14 }, { 15, 15 }, | ||
436 | { 0 } | ||
437 | }; | ||
438 | |||
432 | static const struct clk_div_table pll_divr_table[] = { | 439 | static const struct clk_div_table pll_divr_table[] = { |
433 | { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 0 } | 440 | { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 0 } |
434 | }; | 441 | }; |
@@ -496,9 +503,9 @@ struct stm32f4_div_data { | |||
496 | 503 | ||
497 | #define MAX_PLL_DIV 3 | 504 | #define MAX_PLL_DIV 3 |
498 | static const struct stm32f4_div_data div_data[MAX_PLL_DIV] = { | 505 | static const struct stm32f4_div_data div_data[MAX_PLL_DIV] = { |
499 | { 16, 2, 0, pll_divp_table }, | 506 | { 16, 2, 0, pll_divp_table }, |
500 | { 24, 4, CLK_DIVIDER_ONE_BASED, NULL }, | 507 | { 24, 4, 0, pll_divq_table }, |
501 | { 28, 3, 0, pll_divr_table }, | 508 | { 28, 3, 0, pll_divr_table }, |
502 | }; | 509 | }; |
503 | 510 | ||
504 | struct stm32f4_pll_data { | 511 | struct stm32f4_pll_data { |
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index 72109d2cf41b..1c2357301017 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig | |||
@@ -1,6 +1,7 @@ | |||
1 | config SUNXI_CCU | 1 | config SUNXI_CCU |
2 | bool "Clock support for Allwinner SoCs" | 2 | bool "Clock support for Allwinner SoCs" |
3 | depends on ARCH_SUNXI || COMPILE_TEST | 3 | depends on ARCH_SUNXI || COMPILE_TEST |
4 | select RESET_CONTROLLER | ||
4 | default ARCH_SUNXI | 5 | default ARCH_SUNXI |
5 | 6 | ||
6 | if SUNXI_CCU | 7 | if SUNXI_CCU |
@@ -135,6 +136,7 @@ config SUN8I_V3S_CCU | |||
135 | config SUN9I_A80_CCU | 136 | config SUN9I_A80_CCU |
136 | bool "Support for the Allwinner A80 CCU" | 137 | bool "Support for the Allwinner A80 CCU" |
137 | select SUNXI_CCU_DIV | 138 | select SUNXI_CCU_DIV |
139 | select SUNXI_CCU_MULT | ||
138 | select SUNXI_CCU_GATE | 140 | select SUNXI_CCU_GATE |
139 | select SUNXI_CCU_NKMP | 141 | select SUNXI_CCU_NKMP |
140 | select SUNXI_CCU_NM | 142 | select SUNXI_CCU_NM |
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c index a7b3c08ed0e2..2c69b631967a 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c | |||
@@ -752,6 +752,13 @@ static const struct sunxi_ccu_desc sun8i_a33_ccu_desc = { | |||
752 | .num_resets = ARRAY_SIZE(sun8i_a33_ccu_resets), | 752 | .num_resets = ARRAY_SIZE(sun8i_a33_ccu_resets), |
753 | }; | 753 | }; |
754 | 754 | ||
755 | static struct ccu_pll_nb sun8i_a33_pll_cpu_nb = { | ||
756 | .common = &pll_cpux_clk.common, | ||
757 | /* copy from pll_cpux_clk */ | ||
758 | .enable = BIT(31), | ||
759 | .lock = BIT(28), | ||
760 | }; | ||
761 | |||
755 | static struct ccu_mux_nb sun8i_a33_cpu_nb = { | 762 | static struct ccu_mux_nb sun8i_a33_cpu_nb = { |
756 | .common = &cpux_clk.common, | 763 | .common = &cpux_clk.common, |
757 | .cm = &cpux_clk.mux, | 764 | .cm = &cpux_clk.mux, |
@@ -783,6 +790,10 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node) | |||
783 | 790 | ||
784 | sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc); | 791 | sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc); |
785 | 792 | ||
793 | /* Gate then ungate PLL CPU after any rate changes */ | ||
794 | ccu_pll_notifier_register(&sun8i_a33_pll_cpu_nb); | ||
795 | |||
796 | /* Reparent CPU during PLL CPU rate changes */ | ||
786 | ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, | 797 | ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, |
787 | &sun8i_a33_cpu_nb); | 798 | &sun8i_a33_cpu_nb); |
788 | } | 799 | } |
diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c index 8a47bafd7890..9d8724715a43 100644 --- a/drivers/clk/sunxi-ng/ccu_common.c +++ b/drivers/clk/sunxi-ng/ccu_common.c | |||
@@ -14,11 +14,13 @@ | |||
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <linux/clk.h> | ||
17 | #include <linux/clk-provider.h> | 18 | #include <linux/clk-provider.h> |
18 | #include <linux/iopoll.h> | 19 | #include <linux/iopoll.h> |
19 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
20 | 21 | ||
21 | #include "ccu_common.h" | 22 | #include "ccu_common.h" |
23 | #include "ccu_gate.h" | ||
22 | #include "ccu_reset.h" | 24 | #include "ccu_reset.h" |
23 | 25 | ||
24 | static DEFINE_SPINLOCK(ccu_lock); | 26 | static DEFINE_SPINLOCK(ccu_lock); |
@@ -39,6 +41,53 @@ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) | |||
39 | WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000)); | 41 | WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000)); |
40 | } | 42 | } |
41 | 43 | ||
44 | /* | ||
45 | * This clock notifier is called when the frequency of a PLL clock is | ||
46 | * changed. In common PLL designs, changes to the dividers take effect | ||
47 | * almost immediately, while changes to the multipliers (implemented | ||
48 | * as dividers in the feedback loop) take a few cycles to work into | ||
49 | * the feedback loop for the PLL to stablize. | ||
50 | * | ||
51 | * Sometimes when the PLL clock rate is changed, the decrease in the | ||
52 | * divider is too much for the decrease in the multiplier to catch up. | ||
53 | * The PLL clock rate will spike, and in some cases, might lock up | ||
54 | * completely. | ||
55 | * | ||
56 | * This notifier callback will gate and then ungate the clock, | ||
57 | * effectively resetting it, so it proceeds to work. Care must be | ||
58 | * taken to reparent consumers to other temporary clocks during the | ||
59 | * rate change, and that this notifier callback must be the first | ||
60 | * to be registered. | ||
61 | */ | ||
62 | static int ccu_pll_notifier_cb(struct notifier_block *nb, | ||
63 | unsigned long event, void *data) | ||
64 | { | ||
65 | struct ccu_pll_nb *pll = to_ccu_pll_nb(nb); | ||
66 | int ret = 0; | ||
67 | |||
68 | if (event != POST_RATE_CHANGE) | ||
69 | goto out; | ||
70 | |||
71 | ccu_gate_helper_disable(pll->common, pll->enable); | ||
72 | |||
73 | ret = ccu_gate_helper_enable(pll->common, pll->enable); | ||
74 | if (ret) | ||
75 | goto out; | ||
76 | |||
77 | ccu_helper_wait_for_lock(pll->common, pll->lock); | ||
78 | |||
79 | out: | ||
80 | return notifier_from_errno(ret); | ||
81 | } | ||
82 | |||
83 | int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb) | ||
84 | { | ||
85 | pll_nb->clk_nb.notifier_call = ccu_pll_notifier_cb; | ||
86 | |||
87 | return clk_notifier_register(pll_nb->common->hw.clk, | ||
88 | &pll_nb->clk_nb); | ||
89 | } | ||
90 | |||
42 | int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, | 91 | int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, |
43 | const struct sunxi_ccu_desc *desc) | 92 | const struct sunxi_ccu_desc *desc) |
44 | { | 93 | { |
diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h index 73d81dc58fc5..d6fdd7a789aa 100644 --- a/drivers/clk/sunxi-ng/ccu_common.h +++ b/drivers/clk/sunxi-ng/ccu_common.h | |||
@@ -83,6 +83,18 @@ struct sunxi_ccu_desc { | |||
83 | 83 | ||
84 | void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock); | 84 | void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock); |
85 | 85 | ||
86 | struct ccu_pll_nb { | ||
87 | struct notifier_block clk_nb; | ||
88 | struct ccu_common *common; | ||
89 | |||
90 | u32 enable; | ||
91 | u32 lock; | ||
92 | }; | ||
93 | |||
94 | #define to_ccu_pll_nb(_nb) container_of(_nb, struct ccu_pll_nb, clk_nb) | ||
95 | |||
96 | int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb); | ||
97 | |||
86 | int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, | 98 | int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, |
87 | const struct sunxi_ccu_desc *desc); | 99 | const struct sunxi_ccu_desc *desc); |
88 | 100 | ||