diff options
author | Stephen Boyd <sboyd@codeaurora.org> | 2017-04-17 14:29:18 -0400 |
---|---|---|
committer | Stephen Boyd <sboyd@codeaurora.org> | 2017-04-17 14:29:18 -0400 |
commit | e609f9f23e57f6af1c8f0b5759a87f9db3521eb1 (patch) | |
tree | fb270968e6d49b2cf7fb70929d8cf7fd598a56c0 /drivers/clk | |
parent | 83dd720da666a2af6debbb2d8aab289e3b6cb8f5 (diff) | |
parent | e7590308d17e578e47f298cc3fec359108341cb6 (diff) |
Merge branch 'clk-fixes' into clk-next
* clk-fixes:
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
Diffstat (limited to 'drivers/clk')
-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 |
4 files changed, 74 insertions, 0 deletions
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 | ||