diff options
author | Michael Turquette <mturquette@baylibre.com> | 2015-12-22 15:34:39 -0500 |
---|---|---|
committer | Michael Turquette <mturquette@baylibre.com> | 2015-12-22 15:34:39 -0500 |
commit | 471a9abeb18f43cf437f58d3a13ed8f43b7dede7 (patch) | |
tree | dd7420745f698f1888ea1bab1352cd8c41598b07 | |
parent | 1807b34f951b5d79da172fee238a56ff0143157f (diff) | |
parent | 63b8d92c793f8622227f70b0685a356d47178cfa (diff) |
Merge branch 'clk-dove' into clk-next
-rw-r--r-- | drivers/clk/mvebu/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/mvebu/dove-divider.c | 262 | ||||
-rw-r--r-- | drivers/clk/mvebu/dove-divider.h | 6 | ||||
-rw-r--r-- | drivers/clk/mvebu/dove.c | 6 |
4 files changed, 275 insertions, 1 deletions
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 645ac7ea3565..8866115486f7 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile | |||
@@ -7,6 +7,6 @@ obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o | |||
7 | obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o | 7 | obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o |
8 | obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o | 8 | obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o |
9 | obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o | 9 | obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o |
10 | obj-$(CONFIG_DOVE_CLK) += dove.o | 10 | obj-$(CONFIG_DOVE_CLK) += dove.o dove-divider.o |
11 | obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o | 11 | obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o |
12 | obj-$(CONFIG_ORION_CLK) += orion.o | 12 | obj-$(CONFIG_ORION_CLK) += orion.o |
diff --git a/drivers/clk/mvebu/dove-divider.c b/drivers/clk/mvebu/dove-divider.c new file mode 100644 index 000000000000..d5c5bfa35a5a --- /dev/null +++ b/drivers/clk/mvebu/dove-divider.c | |||
@@ -0,0 +1,262 @@ | |||
1 | /* | ||
2 | * Marvell Dove PMU Core PLL divider driver | ||
3 | * | ||
4 | * Cleaned up by substantially rewriting, and converted to DT by | ||
5 | * Russell King. Origin is not known. | ||
6 | */ | ||
7 | #include <linux/clk-provider.h> | ||
8 | #include <linux/delay.h> | ||
9 | #include <linux/io.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/of.h> | ||
12 | #include <linux/of_address.h> | ||
13 | |||
14 | #include "dove-divider.h" | ||
15 | |||
16 | struct dove_clk { | ||
17 | const char *name; | ||
18 | struct clk_hw hw; | ||
19 | void __iomem *base; | ||
20 | spinlock_t *lock; | ||
21 | u8 div_bit_start; | ||
22 | u8 div_bit_end; | ||
23 | u8 div_bit_load; | ||
24 | u8 div_bit_size; | ||
25 | u32 *divider_table; | ||
26 | }; | ||
27 | |||
28 | enum { | ||
29 | DIV_CTRL0 = 0, | ||
30 | DIV_CTRL1 = 4, | ||
31 | DIV_CTRL1_N_RESET_MASK = BIT(10), | ||
32 | }; | ||
33 | |||
34 | #define to_dove_clk(hw) container_of(hw, struct dove_clk, hw) | ||
35 | |||
36 | static void dove_load_divider(void __iomem *base, u32 val, u32 mask, u32 load) | ||
37 | { | ||
38 | u32 v; | ||
39 | |||
40 | v = readl_relaxed(base + DIV_CTRL1) | DIV_CTRL1_N_RESET_MASK; | ||
41 | writel_relaxed(v, base + DIV_CTRL1); | ||
42 | |||
43 | v = (readl_relaxed(base + DIV_CTRL0) & ~(mask | load)) | val; | ||
44 | writel_relaxed(v, base + DIV_CTRL0); | ||
45 | writel_relaxed(v | load, base + DIV_CTRL0); | ||
46 | ndelay(250); | ||
47 | writel_relaxed(v, base + DIV_CTRL0); | ||
48 | } | ||
49 | |||
50 | static unsigned int dove_get_divider(struct dove_clk *dc) | ||
51 | { | ||
52 | unsigned int divider; | ||
53 | u32 val; | ||
54 | |||
55 | val = readl_relaxed(dc->base + DIV_CTRL0); | ||
56 | val >>= dc->div_bit_start; | ||
57 | |||
58 | divider = val & ~(~0 << dc->div_bit_size); | ||
59 | |||
60 | if (dc->divider_table) | ||
61 | divider = dc->divider_table[divider]; | ||
62 | |||
63 | return divider; | ||
64 | } | ||
65 | |||
66 | static int dove_calc_divider(const struct dove_clk *dc, unsigned long rate, | ||
67 | unsigned long parent_rate, bool set) | ||
68 | { | ||
69 | unsigned int divider, max; | ||
70 | |||
71 | divider = DIV_ROUND_CLOSEST(parent_rate, rate); | ||
72 | |||
73 | if (dc->divider_table) { | ||
74 | unsigned int i; | ||
75 | |||
76 | for (i = 0; dc->divider_table[i]; i++) | ||
77 | if (divider == dc->divider_table[i]) { | ||
78 | divider = i; | ||
79 | break; | ||
80 | } | ||
81 | |||
82 | if (!dc->divider_table[i]) | ||
83 | return -EINVAL; | ||
84 | } else { | ||
85 | max = 1 << dc->div_bit_size; | ||
86 | |||
87 | if (set && (divider == 0 || divider >= max)) | ||
88 | return -EINVAL; | ||
89 | if (divider >= max) | ||
90 | divider = max - 1; | ||
91 | else if (divider == 0) | ||
92 | divider = 1; | ||
93 | } | ||
94 | |||
95 | return divider; | ||
96 | } | ||
97 | |||
98 | static unsigned long dove_recalc_rate(struct clk_hw *hw, unsigned long parent) | ||
99 | { | ||
100 | struct dove_clk *dc = to_dove_clk(hw); | ||
101 | unsigned int divider = dove_get_divider(dc); | ||
102 | unsigned long rate = DIV_ROUND_CLOSEST(parent, divider); | ||
103 | |||
104 | pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n", | ||
105 | __func__, dc->name, divider, parent, rate); | ||
106 | |||
107 | return rate; | ||
108 | } | ||
109 | |||
110 | static long dove_round_rate(struct clk_hw *hw, unsigned long rate, | ||
111 | unsigned long *parent) | ||
112 | { | ||
113 | struct dove_clk *dc = to_dove_clk(hw); | ||
114 | unsigned long parent_rate = *parent; | ||
115 | int divider; | ||
116 | |||
117 | divider = dove_calc_divider(dc, rate, parent_rate, false); | ||
118 | if (divider < 0) | ||
119 | return divider; | ||
120 | |||
121 | rate = DIV_ROUND_CLOSEST(parent_rate, divider); | ||
122 | |||
123 | pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n", | ||
124 | __func__, dc->name, divider, parent_rate, rate); | ||
125 | |||
126 | return rate; | ||
127 | } | ||
128 | |||
129 | static int dove_set_clock(struct clk_hw *hw, unsigned long rate, | ||
130 | unsigned long parent_rate) | ||
131 | { | ||
132 | struct dove_clk *dc = to_dove_clk(hw); | ||
133 | u32 mask, load, div; | ||
134 | int divider; | ||
135 | |||
136 | divider = dove_calc_divider(dc, rate, parent_rate, true); | ||
137 | if (divider < 0) | ||
138 | return divider; | ||
139 | |||
140 | pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n", | ||
141 | __func__, dc->name, divider, parent_rate, rate); | ||
142 | |||
143 | div = (u32)divider << dc->div_bit_start; | ||
144 | mask = ~(~0 << dc->div_bit_size) << dc->div_bit_start; | ||
145 | load = BIT(dc->div_bit_load); | ||
146 | |||
147 | spin_lock(dc->lock); | ||
148 | dove_load_divider(dc->base, div, mask, load); | ||
149 | spin_unlock(dc->lock); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static const struct clk_ops dove_divider_ops = { | ||
155 | .set_rate = dove_set_clock, | ||
156 | .round_rate = dove_round_rate, | ||
157 | .recalc_rate = dove_recalc_rate, | ||
158 | }; | ||
159 | |||
160 | static struct clk *clk_register_dove_divider(struct device *dev, | ||
161 | struct dove_clk *dc, const char **parent_names, size_t num_parents, | ||
162 | void __iomem *base) | ||
163 | { | ||
164 | char name[32]; | ||
165 | struct clk_init_data init = { | ||
166 | .name = name, | ||
167 | .ops = &dove_divider_ops, | ||
168 | .parent_names = parent_names, | ||
169 | .num_parents = num_parents, | ||
170 | }; | ||
171 | |||
172 | strlcpy(name, dc->name, sizeof(name)); | ||
173 | |||
174 | dc->hw.init = &init; | ||
175 | dc->base = base; | ||
176 | dc->div_bit_size = dc->div_bit_end - dc->div_bit_start + 1; | ||
177 | |||
178 | return clk_register(dev, &dc->hw); | ||
179 | } | ||
180 | |||
181 | static DEFINE_SPINLOCK(dove_divider_lock); | ||
182 | |||
183 | static u32 axi_divider[] = {-1, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 0}; | ||
184 | |||
185 | static struct dove_clk dove_hw_clocks[4] = { | ||
186 | { | ||
187 | .name = "axi", | ||
188 | .lock = &dove_divider_lock, | ||
189 | .div_bit_start = 1, | ||
190 | .div_bit_end = 6, | ||
191 | .div_bit_load = 7, | ||
192 | .divider_table = axi_divider, | ||
193 | }, { | ||
194 | .name = "gpu", | ||
195 | .lock = &dove_divider_lock, | ||
196 | .div_bit_start = 8, | ||
197 | .div_bit_end = 13, | ||
198 | .div_bit_load = 14, | ||
199 | }, { | ||
200 | .name = "vmeta", | ||
201 | .lock = &dove_divider_lock, | ||
202 | .div_bit_start = 15, | ||
203 | .div_bit_end = 20, | ||
204 | .div_bit_load = 21, | ||
205 | }, { | ||
206 | .name = "lcd", | ||
207 | .lock = &dove_divider_lock, | ||
208 | .div_bit_start = 22, | ||
209 | .div_bit_end = 27, | ||
210 | .div_bit_load = 28, | ||
211 | }, | ||
212 | }; | ||
213 | |||
214 | static const char *core_pll[] = { | ||
215 | "core-pll", | ||
216 | }; | ||
217 | |||
218 | static int dove_divider_init(struct device *dev, void __iomem *base, | ||
219 | struct clk **clks) | ||
220 | { | ||
221 | struct clk *clk; | ||
222 | int i; | ||
223 | |||
224 | /* | ||
225 | * Create the core PLL clock. We treat this as a fixed rate | ||
226 | * clock as we don't know any better, and documentation is sparse. | ||
227 | */ | ||
228 | clk = clk_register_fixed_rate(dev, core_pll[0], NULL, CLK_IS_ROOT, | ||
229 | 2000000000UL); | ||
230 | if (IS_ERR(clk)) | ||
231 | return PTR_ERR(clk); | ||
232 | |||
233 | for (i = 0; i < ARRAY_SIZE(dove_hw_clocks); i++) | ||
234 | clks[i] = clk_register_dove_divider(dev, &dove_hw_clocks[i], | ||
235 | core_pll, | ||
236 | ARRAY_SIZE(core_pll), base); | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static struct clk *dove_divider_clocks[4]; | ||
242 | |||
243 | static struct clk_onecell_data dove_divider_data = { | ||
244 | .clks = dove_divider_clocks, | ||
245 | .clk_num = ARRAY_SIZE(dove_divider_clocks), | ||
246 | }; | ||
247 | |||
248 | void __init dove_divider_clk_init(struct device_node *np) | ||
249 | { | ||
250 | void *base; | ||
251 | |||
252 | base = of_iomap(np, 0); | ||
253 | if (WARN_ON(!base)) | ||
254 | return; | ||
255 | |||
256 | if (WARN_ON(dove_divider_init(NULL, base, dove_divider_clocks))) { | ||
257 | iounmap(base); | ||
258 | return; | ||
259 | } | ||
260 | |||
261 | of_clk_add_provider(np, of_clk_src_onecell_get, &dove_divider_data); | ||
262 | } | ||
diff --git a/drivers/clk/mvebu/dove-divider.h b/drivers/clk/mvebu/dove-divider.h new file mode 100644 index 000000000000..4f2f718deb8e --- /dev/null +++ b/drivers/clk/mvebu/dove-divider.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef DOVE_DIVIDER_H | ||
2 | #define DOVE_DIVIDER_H | ||
3 | |||
4 | void __init dove_divider_clk_init(struct device_node *np); | ||
5 | |||
6 | #endif | ||
diff --git a/drivers/clk/mvebu/dove.c b/drivers/clk/mvebu/dove.c index b8c2424ac926..59fad9546c84 100644 --- a/drivers/clk/mvebu/dove.c +++ b/drivers/clk/mvebu/dove.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/io.h> | 17 | #include <linux/io.h> |
18 | #include <linux/of.h> | 18 | #include <linux/of.h> |
19 | #include "common.h" | 19 | #include "common.h" |
20 | #include "dove-divider.h" | ||
20 | 21 | ||
21 | /* | 22 | /* |
22 | * Core Clocks | 23 | * Core Clocks |
@@ -184,9 +185,14 @@ static void __init dove_clk_init(struct device_node *np) | |||
184 | { | 185 | { |
185 | struct device_node *cgnp = | 186 | struct device_node *cgnp = |
186 | of_find_compatible_node(NULL, NULL, "marvell,dove-gating-clock"); | 187 | of_find_compatible_node(NULL, NULL, "marvell,dove-gating-clock"); |
188 | struct device_node *ddnp = | ||
189 | of_find_compatible_node(NULL, NULL, "marvell,dove-divider-clock"); | ||
187 | 190 | ||
188 | mvebu_coreclk_setup(np, &dove_coreclks); | 191 | mvebu_coreclk_setup(np, &dove_coreclks); |
189 | 192 | ||
193 | if (ddnp) | ||
194 | dove_divider_clk_init(ddnp); | ||
195 | |||
190 | if (cgnp) | 196 | if (cgnp) |
191 | mvebu_clk_gating_setup(cgnp, dove_gating_desc); | 197 | mvebu_clk_gating_setup(cgnp, dove_gating_desc); |
192 | } | 198 | } |