]>
Commit | Line | Data |
---|---|---|
1d80c142 MR |
1 | /* |
2 | * Copyright 2016 Maxime Ripard | |
3 | * | |
4 | * Maxime Ripard <maxime.ripard@free-electrons.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
02ae2bc6 | 17 | #include <linux/clk.h> |
1d80c142 MR |
18 | #include <linux/clk-provider.h> |
19 | #include <linux/iopoll.h> | |
20 | #include <linux/slab.h> | |
21 | ||
22 | #include "ccu_common.h" | |
02ae2bc6 | 23 | #include "ccu_gate.h" |
1d80c142 MR |
24 | #include "ccu_reset.h" |
25 | ||
26 | static DEFINE_SPINLOCK(ccu_lock); | |
27 | ||
28 | void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) | |
29 | { | |
3de64bf1 | 30 | void __iomem *addr; |
1d80c142 MR |
31 | u32 reg; |
32 | ||
33 | if (!lock) | |
34 | return; | |
35 | ||
3de64bf1 CYT |
36 | if (common->features & CCU_FEATURE_LOCK_REG) |
37 | addr = common->base + common->lock_reg; | |
38 | else | |
39 | addr = common->base + common->reg; | |
40 | ||
41 | WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000)); | |
1d80c142 MR |
42 | } |
43 | ||
02ae2bc6 CYT |
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 | ||
1d80c142 MR |
91 | int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, |
92 | const struct sunxi_ccu_desc *desc) | |
93 | { | |
94 | struct ccu_reset *reset; | |
95 | int i, ret; | |
96 | ||
97 | for (i = 0; i < desc->num_ccu_clks; i++) { | |
98 | struct ccu_common *cclk = desc->ccu_clks[i]; | |
99 | ||
100 | if (!cclk) | |
101 | continue; | |
102 | ||
103 | cclk->base = reg; | |
104 | cclk->lock = &ccu_lock; | |
105 | } | |
106 | ||
107 | for (i = 0; i < desc->hw_clks->num ; i++) { | |
108 | struct clk_hw *hw = desc->hw_clks->hws[i]; | |
109 | ||
110 | if (!hw) | |
111 | continue; | |
112 | ||
113 | ret = clk_hw_register(NULL, hw); | |
114 | if (ret) { | |
115 | pr_err("Couldn't register clock %s\n", | |
116 | clk_hw_get_name(hw)); | |
117 | goto err_clk_unreg; | |
118 | } | |
119 | } | |
120 | ||
121 | ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, | |
122 | desc->hw_clks); | |
123 | if (ret) | |
124 | goto err_clk_unreg; | |
125 | ||
126 | reset = kzalloc(sizeof(*reset), GFP_KERNEL); | |
5d806f9f SB |
127 | if (!reset) { |
128 | ret = -ENOMEM; | |
129 | goto err_alloc_reset; | |
130 | } | |
131 | ||
1d80c142 MR |
132 | reset->rcdev.of_node = node; |
133 | reset->rcdev.ops = &ccu_reset_ops; | |
134 | reset->rcdev.owner = THIS_MODULE; | |
135 | reset->rcdev.nr_resets = desc->num_resets; | |
136 | reset->base = reg; | |
137 | reset->lock = &ccu_lock; | |
138 | reset->reset_map = desc->resets; | |
139 | ||
140 | ret = reset_controller_register(&reset->rcdev); | |
141 | if (ret) | |
142 | goto err_of_clk_unreg; | |
143 | ||
144 | return 0; | |
145 | ||
146 | err_of_clk_unreg: | |
5d806f9f SB |
147 | kfree(reset); |
148 | err_alloc_reset: | |
149 | of_clk_del_provider(node); | |
1d80c142 | 150 | err_clk_unreg: |
5d806f9f SB |
151 | while (--i >= 0) { |
152 | struct clk_hw *hw = desc->hw_clks->hws[i]; | |
153 | ||
154 | if (!hw) | |
155 | continue; | |
156 | clk_hw_unregister(hw); | |
157 | } | |
1d80c142 MR |
158 | return ret; |
159 | } |