diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/ingenic/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/ingenic/cgu.c | 711 | ||||
-rw-r--r-- | drivers/clk/ingenic/cgu.h | 223 | ||||
-rw-r--r-- | drivers/clk/ingenic/jz4740-cgu.c | 303 | ||||
-rw-r--r-- | drivers/clk/ingenic/jz4780-cgu.c | 733 |
6 files changed, 1975 insertions, 1 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 5b6af6a9319f..8732e4c5bf3c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile | |||
@@ -24,7 +24,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o | |||
24 | obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o | 24 | obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o |
25 | obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o | 25 | obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o |
26 | obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o | 26 | obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o |
27 | obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o | 27 | obj-$(CONFIG_MACH_LOONGSON32) += clk-ls1x.o |
28 | obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o | 28 | obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o |
29 | obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o | 29 | obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o |
30 | obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o | 30 | obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o |
@@ -51,6 +51,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ | |||
51 | obj-$(CONFIG_ARCH_HIP04) += hisilicon/ | 51 | obj-$(CONFIG_ARCH_HIP04) += hisilicon/ |
52 | obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ | 52 | obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ |
53 | obj-$(CONFIG_ARCH_MXC) += imx/ | 53 | obj-$(CONFIG_ARCH_MXC) += imx/ |
54 | obj-$(CONFIG_MACH_INGENIC) += ingenic/ | ||
54 | obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ | 55 | obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ |
55 | ifeq ($(CONFIG_COMMON_CLK), y) | 56 | ifeq ($(CONFIG_COMMON_CLK), y) |
56 | obj-$(CONFIG_ARCH_MMP) += mmp/ | 57 | obj-$(CONFIG_ARCH_MMP) += mmp/ |
diff --git a/drivers/clk/ingenic/Makefile b/drivers/clk/ingenic/Makefile new file mode 100644 index 000000000000..cd47b0664c2b --- /dev/null +++ b/drivers/clk/ingenic/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | obj-y += cgu.o | ||
2 | obj-$(CONFIG_MACH_JZ4740) += jz4740-cgu.o | ||
3 | obj-$(CONFIG_MACH_JZ4780) += jz4780-cgu.o | ||
diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c new file mode 100644 index 000000000000..b936cdd1a13c --- /dev/null +++ b/drivers/clk/ingenic/cgu.c | |||
@@ -0,0 +1,711 @@ | |||
1 | /* | ||
2 | * Ingenic SoC CGU driver | ||
3 | * | ||
4 | * Copyright (c) 2013-2015 Imagination Technologies | ||
5 | * Author: Paul Burton <paul.burton@imgtec.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/bitops.h> | ||
19 | #include <linux/clk-provider.h> | ||
20 | #include <linux/clkdev.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/math64.h> | ||
23 | #include <linux/of.h> | ||
24 | #include <linux/of_address.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | #include "cgu.h" | ||
28 | |||
29 | #define MHZ (1000 * 1000) | ||
30 | |||
31 | /** | ||
32 | * ingenic_cgu_gate_get() - get the value of clock gate register bit | ||
33 | * @cgu: reference to the CGU whose registers should be read | ||
34 | * @info: info struct describing the gate bit | ||
35 | * | ||
36 | * Retrieves the state of the clock gate bit described by info. The | ||
37 | * caller must hold cgu->lock. | ||
38 | * | ||
39 | * Return: true if the gate bit is set, else false. | ||
40 | */ | ||
41 | static inline bool | ||
42 | ingenic_cgu_gate_get(struct ingenic_cgu *cgu, | ||
43 | const struct ingenic_cgu_gate_info *info) | ||
44 | { | ||
45 | return readl(cgu->base + info->reg) & BIT(info->bit); | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * ingenic_cgu_gate_set() - set the value of clock gate register bit | ||
50 | * @cgu: reference to the CGU whose registers should be modified | ||
51 | * @info: info struct describing the gate bit | ||
52 | * @val: non-zero to gate a clock, otherwise zero | ||
53 | * | ||
54 | * Sets the given gate bit in order to gate or ungate a clock. | ||
55 | * | ||
56 | * The caller must hold cgu->lock. | ||
57 | */ | ||
58 | static inline void | ||
59 | ingenic_cgu_gate_set(struct ingenic_cgu *cgu, | ||
60 | const struct ingenic_cgu_gate_info *info, bool val) | ||
61 | { | ||
62 | u32 clkgr = readl(cgu->base + info->reg); | ||
63 | |||
64 | if (val) | ||
65 | clkgr |= BIT(info->bit); | ||
66 | else | ||
67 | clkgr &= ~BIT(info->bit); | ||
68 | |||
69 | writel(clkgr, cgu->base + info->reg); | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * PLL operations | ||
74 | */ | ||
75 | |||
76 | static unsigned long | ||
77 | ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | ||
78 | { | ||
79 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
80 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
81 | const struct ingenic_cgu_clk_info *clk_info; | ||
82 | const struct ingenic_cgu_pll_info *pll_info; | ||
83 | unsigned m, n, od_enc, od; | ||
84 | bool bypass, enable; | ||
85 | unsigned long flags; | ||
86 | u32 ctl; | ||
87 | |||
88 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
89 | BUG_ON(clk_info->type != CGU_CLK_PLL); | ||
90 | pll_info = &clk_info->pll; | ||
91 | |||
92 | spin_lock_irqsave(&cgu->lock, flags); | ||
93 | ctl = readl(cgu->base + pll_info->reg); | ||
94 | spin_unlock_irqrestore(&cgu->lock, flags); | ||
95 | |||
96 | m = (ctl >> pll_info->m_shift) & GENMASK(pll_info->m_bits - 1, 0); | ||
97 | m += pll_info->m_offset; | ||
98 | n = (ctl >> pll_info->n_shift) & GENMASK(pll_info->n_bits - 1, 0); | ||
99 | n += pll_info->n_offset; | ||
100 | od_enc = ctl >> pll_info->od_shift; | ||
101 | od_enc &= GENMASK(pll_info->od_bits - 1, 0); | ||
102 | bypass = !!(ctl & BIT(pll_info->bypass_bit)); | ||
103 | enable = !!(ctl & BIT(pll_info->enable_bit)); | ||
104 | |||
105 | if (bypass) | ||
106 | return parent_rate; | ||
107 | |||
108 | if (!enable) | ||
109 | return 0; | ||
110 | |||
111 | for (od = 0; od < pll_info->od_max; od++) { | ||
112 | if (pll_info->od_encoding[od] == od_enc) | ||
113 | break; | ||
114 | } | ||
115 | BUG_ON(od == pll_info->od_max); | ||
116 | od++; | ||
117 | |||
118 | return div_u64((u64)parent_rate * m, n * od); | ||
119 | } | ||
120 | |||
121 | static unsigned long | ||
122 | ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info, | ||
123 | unsigned long rate, unsigned long parent_rate, | ||
124 | unsigned *pm, unsigned *pn, unsigned *pod) | ||
125 | { | ||
126 | const struct ingenic_cgu_pll_info *pll_info; | ||
127 | unsigned m, n, od; | ||
128 | |||
129 | pll_info = &clk_info->pll; | ||
130 | od = 1; | ||
131 | |||
132 | /* | ||
133 | * The frequency after the input divider must be between 10 and 50 MHz. | ||
134 | * The highest divider yields the best resolution. | ||
135 | */ | ||
136 | n = parent_rate / (10 * MHZ); | ||
137 | n = min_t(unsigned, n, 1 << clk_info->pll.n_bits); | ||
138 | n = max_t(unsigned, n, pll_info->n_offset); | ||
139 | |||
140 | m = (rate / MHZ) * od * n / (parent_rate / MHZ); | ||
141 | m = min_t(unsigned, m, 1 << clk_info->pll.m_bits); | ||
142 | m = max_t(unsigned, m, pll_info->m_offset); | ||
143 | |||
144 | if (pm) | ||
145 | *pm = m; | ||
146 | if (pn) | ||
147 | *pn = n; | ||
148 | if (pod) | ||
149 | *pod = od; | ||
150 | |||
151 | return div_u64((u64)parent_rate * m, n * od); | ||
152 | } | ||
153 | |||
154 | static long | ||
155 | ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate, | ||
156 | unsigned long *prate) | ||
157 | { | ||
158 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
159 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
160 | const struct ingenic_cgu_clk_info *clk_info; | ||
161 | |||
162 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
163 | BUG_ON(clk_info->type != CGU_CLK_PLL); | ||
164 | |||
165 | return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL); | ||
166 | } | ||
167 | |||
168 | static int | ||
169 | ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, | ||
170 | unsigned long parent_rate) | ||
171 | { | ||
172 | const unsigned timeout = 100; | ||
173 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
174 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
175 | const struct ingenic_cgu_clk_info *clk_info; | ||
176 | const struct ingenic_cgu_pll_info *pll_info; | ||
177 | unsigned long rate, flags; | ||
178 | unsigned m, n, od, i; | ||
179 | u32 ctl; | ||
180 | |||
181 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
182 | BUG_ON(clk_info->type != CGU_CLK_PLL); | ||
183 | pll_info = &clk_info->pll; | ||
184 | |||
185 | rate = ingenic_pll_calc(clk_info, req_rate, parent_rate, | ||
186 | &m, &n, &od); | ||
187 | if (rate != req_rate) | ||
188 | pr_info("ingenic-cgu: request '%s' rate %luHz, actual %luHz\n", | ||
189 | clk_info->name, req_rate, rate); | ||
190 | |||
191 | spin_lock_irqsave(&cgu->lock, flags); | ||
192 | ctl = readl(cgu->base + pll_info->reg); | ||
193 | |||
194 | ctl &= ~(GENMASK(pll_info->m_bits - 1, 0) << pll_info->m_shift); | ||
195 | ctl |= (m - pll_info->m_offset) << pll_info->m_shift; | ||
196 | |||
197 | ctl &= ~(GENMASK(pll_info->n_bits - 1, 0) << pll_info->n_shift); | ||
198 | ctl |= (n - pll_info->n_offset) << pll_info->n_shift; | ||
199 | |||
200 | ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift); | ||
201 | ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift; | ||
202 | |||
203 | ctl &= ~BIT(pll_info->bypass_bit); | ||
204 | ctl |= BIT(pll_info->enable_bit); | ||
205 | |||
206 | writel(ctl, cgu->base + pll_info->reg); | ||
207 | |||
208 | /* wait for the PLL to stabilise */ | ||
209 | for (i = 0; i < timeout; i++) { | ||
210 | ctl = readl(cgu->base + pll_info->reg); | ||
211 | if (ctl & BIT(pll_info->stable_bit)) | ||
212 | break; | ||
213 | mdelay(1); | ||
214 | } | ||
215 | |||
216 | spin_unlock_irqrestore(&cgu->lock, flags); | ||
217 | |||
218 | if (i == timeout) | ||
219 | return -EBUSY; | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static const struct clk_ops ingenic_pll_ops = { | ||
225 | .recalc_rate = ingenic_pll_recalc_rate, | ||
226 | .round_rate = ingenic_pll_round_rate, | ||
227 | .set_rate = ingenic_pll_set_rate, | ||
228 | }; | ||
229 | |||
230 | /* | ||
231 | * Operations for all non-PLL clocks | ||
232 | */ | ||
233 | |||
234 | static u8 ingenic_clk_get_parent(struct clk_hw *hw) | ||
235 | { | ||
236 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
237 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
238 | const struct ingenic_cgu_clk_info *clk_info; | ||
239 | u32 reg; | ||
240 | u8 i, hw_idx, idx = 0; | ||
241 | |||
242 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
243 | |||
244 | if (clk_info->type & CGU_CLK_MUX) { | ||
245 | reg = readl(cgu->base + clk_info->mux.reg); | ||
246 | hw_idx = (reg >> clk_info->mux.shift) & | ||
247 | GENMASK(clk_info->mux.bits - 1, 0); | ||
248 | |||
249 | /* | ||
250 | * Convert the hardware index to the parent index by skipping | ||
251 | * over any -1's in the parents array. | ||
252 | */ | ||
253 | for (i = 0; i < hw_idx; i++) { | ||
254 | if (clk_info->parents[i] != -1) | ||
255 | idx++; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | return idx; | ||
260 | } | ||
261 | |||
262 | static int ingenic_clk_set_parent(struct clk_hw *hw, u8 idx) | ||
263 | { | ||
264 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
265 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
266 | const struct ingenic_cgu_clk_info *clk_info; | ||
267 | unsigned long flags; | ||
268 | u8 curr_idx, hw_idx, num_poss; | ||
269 | u32 reg, mask; | ||
270 | |||
271 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
272 | |||
273 | if (clk_info->type & CGU_CLK_MUX) { | ||
274 | /* | ||
275 | * Convert the parent index to the hardware index by adding | ||
276 | * 1 for any -1 in the parents array preceding the given | ||
277 | * index. That is, we want the index of idx'th entry in | ||
278 | * clk_info->parents which does not equal -1. | ||
279 | */ | ||
280 | hw_idx = curr_idx = 0; | ||
281 | num_poss = 1 << clk_info->mux.bits; | ||
282 | for (; hw_idx < num_poss; hw_idx++) { | ||
283 | if (clk_info->parents[hw_idx] == -1) | ||
284 | continue; | ||
285 | if (curr_idx == idx) | ||
286 | break; | ||
287 | curr_idx++; | ||
288 | } | ||
289 | |||
290 | /* idx should always be a valid parent */ | ||
291 | BUG_ON(curr_idx != idx); | ||
292 | |||
293 | mask = GENMASK(clk_info->mux.bits - 1, 0); | ||
294 | mask <<= clk_info->mux.shift; | ||
295 | |||
296 | spin_lock_irqsave(&cgu->lock, flags); | ||
297 | |||
298 | /* write the register */ | ||
299 | reg = readl(cgu->base + clk_info->mux.reg); | ||
300 | reg &= ~mask; | ||
301 | reg |= hw_idx << clk_info->mux.shift; | ||
302 | writel(reg, cgu->base + clk_info->mux.reg); | ||
303 | |||
304 | spin_unlock_irqrestore(&cgu->lock, flags); | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | return idx ? -EINVAL : 0; | ||
309 | } | ||
310 | |||
311 | static unsigned long | ||
312 | ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | ||
313 | { | ||
314 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
315 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
316 | const struct ingenic_cgu_clk_info *clk_info; | ||
317 | unsigned long rate = parent_rate; | ||
318 | u32 div_reg, div; | ||
319 | |||
320 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
321 | |||
322 | if (clk_info->type & CGU_CLK_DIV) { | ||
323 | div_reg = readl(cgu->base + clk_info->div.reg); | ||
324 | div = (div_reg >> clk_info->div.shift) & | ||
325 | GENMASK(clk_info->div.bits - 1, 0); | ||
326 | div += 1; | ||
327 | |||
328 | rate /= div; | ||
329 | } | ||
330 | |||
331 | return rate; | ||
332 | } | ||
333 | |||
334 | static unsigned | ||
335 | ingenic_clk_calc_div(const struct ingenic_cgu_clk_info *clk_info, | ||
336 | unsigned long parent_rate, unsigned long req_rate) | ||
337 | { | ||
338 | unsigned div; | ||
339 | |||
340 | /* calculate the divide */ | ||
341 | div = DIV_ROUND_UP(parent_rate, req_rate); | ||
342 | |||
343 | /* and impose hardware constraints */ | ||
344 | div = min_t(unsigned, div, 1 << clk_info->div.bits); | ||
345 | div = max_t(unsigned, div, 1); | ||
346 | |||
347 | return div; | ||
348 | } | ||
349 | |||
350 | static long | ||
351 | ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate, | ||
352 | unsigned long *parent_rate) | ||
353 | { | ||
354 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
355 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
356 | const struct ingenic_cgu_clk_info *clk_info; | ||
357 | long rate = *parent_rate; | ||
358 | |||
359 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
360 | |||
361 | if (clk_info->type & CGU_CLK_DIV) | ||
362 | rate /= ingenic_clk_calc_div(clk_info, *parent_rate, req_rate); | ||
363 | else if (clk_info->type & CGU_CLK_FIXDIV) | ||
364 | rate /= clk_info->fixdiv.div; | ||
365 | |||
366 | return rate; | ||
367 | } | ||
368 | |||
369 | static int | ||
370 | ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, | ||
371 | unsigned long parent_rate) | ||
372 | { | ||
373 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
374 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
375 | const struct ingenic_cgu_clk_info *clk_info; | ||
376 | const unsigned timeout = 100; | ||
377 | unsigned long rate, flags; | ||
378 | unsigned div, i; | ||
379 | u32 reg, mask; | ||
380 | int ret = 0; | ||
381 | |||
382 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
383 | |||
384 | if (clk_info->type & CGU_CLK_DIV) { | ||
385 | div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate); | ||
386 | rate = parent_rate / div; | ||
387 | |||
388 | if (rate != req_rate) | ||
389 | return -EINVAL; | ||
390 | |||
391 | spin_lock_irqsave(&cgu->lock, flags); | ||
392 | reg = readl(cgu->base + clk_info->div.reg); | ||
393 | |||
394 | /* update the divide */ | ||
395 | mask = GENMASK(clk_info->div.bits - 1, 0); | ||
396 | reg &= ~(mask << clk_info->div.shift); | ||
397 | reg |= (div - 1) << clk_info->div.shift; | ||
398 | |||
399 | /* clear the stop bit */ | ||
400 | if (clk_info->div.stop_bit != -1) | ||
401 | reg &= ~BIT(clk_info->div.stop_bit); | ||
402 | |||
403 | /* set the change enable bit */ | ||
404 | if (clk_info->div.ce_bit != -1) | ||
405 | reg |= BIT(clk_info->div.ce_bit); | ||
406 | |||
407 | /* update the hardware */ | ||
408 | writel(reg, cgu->base + clk_info->div.reg); | ||
409 | |||
410 | /* wait for the change to take effect */ | ||
411 | if (clk_info->div.busy_bit != -1) { | ||
412 | for (i = 0; i < timeout; i++) { | ||
413 | reg = readl(cgu->base + clk_info->div.reg); | ||
414 | if (!(reg & BIT(clk_info->div.busy_bit))) | ||
415 | break; | ||
416 | mdelay(1); | ||
417 | } | ||
418 | if (i == timeout) | ||
419 | ret = -EBUSY; | ||
420 | } | ||
421 | |||
422 | spin_unlock_irqrestore(&cgu->lock, flags); | ||
423 | return ret; | ||
424 | } | ||
425 | |||
426 | return -EINVAL; | ||
427 | } | ||
428 | |||
429 | static int ingenic_clk_enable(struct clk_hw *hw) | ||
430 | { | ||
431 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
432 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
433 | const struct ingenic_cgu_clk_info *clk_info; | ||
434 | unsigned long flags; | ||
435 | |||
436 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
437 | |||
438 | if (clk_info->type & CGU_CLK_GATE) { | ||
439 | /* ungate the clock */ | ||
440 | spin_lock_irqsave(&cgu->lock, flags); | ||
441 | ingenic_cgu_gate_set(cgu, &clk_info->gate, false); | ||
442 | spin_unlock_irqrestore(&cgu->lock, flags); | ||
443 | } | ||
444 | |||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | static void ingenic_clk_disable(struct clk_hw *hw) | ||
449 | { | ||
450 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
451 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
452 | const struct ingenic_cgu_clk_info *clk_info; | ||
453 | unsigned long flags; | ||
454 | |||
455 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
456 | |||
457 | if (clk_info->type & CGU_CLK_GATE) { | ||
458 | /* gate the clock */ | ||
459 | spin_lock_irqsave(&cgu->lock, flags); | ||
460 | ingenic_cgu_gate_set(cgu, &clk_info->gate, true); | ||
461 | spin_unlock_irqrestore(&cgu->lock, flags); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | static int ingenic_clk_is_enabled(struct clk_hw *hw) | ||
466 | { | ||
467 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); | ||
468 | struct ingenic_cgu *cgu = ingenic_clk->cgu; | ||
469 | const struct ingenic_cgu_clk_info *clk_info; | ||
470 | unsigned long flags; | ||
471 | int enabled = 1; | ||
472 | |||
473 | clk_info = &cgu->clock_info[ingenic_clk->idx]; | ||
474 | |||
475 | if (clk_info->type & CGU_CLK_GATE) { | ||
476 | spin_lock_irqsave(&cgu->lock, flags); | ||
477 | enabled = !ingenic_cgu_gate_get(cgu, &clk_info->gate); | ||
478 | spin_unlock_irqrestore(&cgu->lock, flags); | ||
479 | } | ||
480 | |||
481 | return enabled; | ||
482 | } | ||
483 | |||
484 | static const struct clk_ops ingenic_clk_ops = { | ||
485 | .get_parent = ingenic_clk_get_parent, | ||
486 | .set_parent = ingenic_clk_set_parent, | ||
487 | |||
488 | .recalc_rate = ingenic_clk_recalc_rate, | ||
489 | .round_rate = ingenic_clk_round_rate, | ||
490 | .set_rate = ingenic_clk_set_rate, | ||
491 | |||
492 | .enable = ingenic_clk_enable, | ||
493 | .disable = ingenic_clk_disable, | ||
494 | .is_enabled = ingenic_clk_is_enabled, | ||
495 | }; | ||
496 | |||
497 | /* | ||
498 | * Setup functions. | ||
499 | */ | ||
500 | |||
501 | static int ingenic_register_clock(struct ingenic_cgu *cgu, unsigned idx) | ||
502 | { | ||
503 | const struct ingenic_cgu_clk_info *clk_info = &cgu->clock_info[idx]; | ||
504 | struct clk_init_data clk_init; | ||
505 | struct ingenic_clk *ingenic_clk = NULL; | ||
506 | struct clk *clk, *parent; | ||
507 | const char *parent_names[4]; | ||
508 | unsigned caps, i, num_possible; | ||
509 | int err = -EINVAL; | ||
510 | |||
511 | BUILD_BUG_ON(ARRAY_SIZE(clk_info->parents) > ARRAY_SIZE(parent_names)); | ||
512 | |||
513 | if (clk_info->type == CGU_CLK_EXT) { | ||
514 | clk = of_clk_get_by_name(cgu->np, clk_info->name); | ||
515 | if (IS_ERR(clk)) { | ||
516 | pr_err("%s: no external clock '%s' provided\n", | ||
517 | __func__, clk_info->name); | ||
518 | err = -ENODEV; | ||
519 | goto out; | ||
520 | } | ||
521 | err = clk_register_clkdev(clk, clk_info->name, NULL); | ||
522 | if (err) { | ||
523 | clk_put(clk); | ||
524 | goto out; | ||
525 | } | ||
526 | cgu->clocks.clks[idx] = clk; | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | if (!clk_info->type) { | ||
531 | pr_err("%s: no clock type specified for '%s'\n", __func__, | ||
532 | clk_info->name); | ||
533 | goto out; | ||
534 | } | ||
535 | |||
536 | ingenic_clk = kzalloc(sizeof(*ingenic_clk), GFP_KERNEL); | ||
537 | if (!ingenic_clk) { | ||
538 | err = -ENOMEM; | ||
539 | goto out; | ||
540 | } | ||
541 | |||
542 | ingenic_clk->hw.init = &clk_init; | ||
543 | ingenic_clk->cgu = cgu; | ||
544 | ingenic_clk->idx = idx; | ||
545 | |||
546 | clk_init.name = clk_info->name; | ||
547 | clk_init.flags = 0; | ||
548 | clk_init.parent_names = parent_names; | ||
549 | |||
550 | caps = clk_info->type; | ||
551 | |||
552 | if (caps & (CGU_CLK_MUX | CGU_CLK_CUSTOM)) { | ||
553 | clk_init.num_parents = 0; | ||
554 | |||
555 | if (caps & CGU_CLK_MUX) | ||
556 | num_possible = 1 << clk_info->mux.bits; | ||
557 | else | ||
558 | num_possible = ARRAY_SIZE(clk_info->parents); | ||
559 | |||
560 | for (i = 0; i < num_possible; i++) { | ||
561 | if (clk_info->parents[i] == -1) | ||
562 | continue; | ||
563 | |||
564 | parent = cgu->clocks.clks[clk_info->parents[i]]; | ||
565 | parent_names[clk_init.num_parents] = | ||
566 | __clk_get_name(parent); | ||
567 | clk_init.num_parents++; | ||
568 | } | ||
569 | |||
570 | BUG_ON(!clk_init.num_parents); | ||
571 | BUG_ON(clk_init.num_parents > ARRAY_SIZE(parent_names)); | ||
572 | } else { | ||
573 | BUG_ON(clk_info->parents[0] == -1); | ||
574 | clk_init.num_parents = 1; | ||
575 | parent = cgu->clocks.clks[clk_info->parents[0]]; | ||
576 | parent_names[0] = __clk_get_name(parent); | ||
577 | } | ||
578 | |||
579 | if (caps & CGU_CLK_CUSTOM) { | ||
580 | clk_init.ops = clk_info->custom.clk_ops; | ||
581 | |||
582 | caps &= ~CGU_CLK_CUSTOM; | ||
583 | |||
584 | if (caps) { | ||
585 | pr_err("%s: custom clock may not be combined with type 0x%x\n", | ||
586 | __func__, caps); | ||
587 | goto out; | ||
588 | } | ||
589 | } else if (caps & CGU_CLK_PLL) { | ||
590 | clk_init.ops = &ingenic_pll_ops; | ||
591 | |||
592 | caps &= ~CGU_CLK_PLL; | ||
593 | |||
594 | if (caps) { | ||
595 | pr_err("%s: PLL may not be combined with type 0x%x\n", | ||
596 | __func__, caps); | ||
597 | goto out; | ||
598 | } | ||
599 | } else { | ||
600 | clk_init.ops = &ingenic_clk_ops; | ||
601 | } | ||
602 | |||
603 | /* nothing to do for gates or fixed dividers */ | ||
604 | caps &= ~(CGU_CLK_GATE | CGU_CLK_FIXDIV); | ||
605 | |||
606 | if (caps & CGU_CLK_MUX) { | ||
607 | if (!(caps & CGU_CLK_MUX_GLITCHFREE)) | ||
608 | clk_init.flags |= CLK_SET_PARENT_GATE; | ||
609 | |||
610 | caps &= ~(CGU_CLK_MUX | CGU_CLK_MUX_GLITCHFREE); | ||
611 | } | ||
612 | |||
613 | if (caps & CGU_CLK_DIV) { | ||
614 | caps &= ~CGU_CLK_DIV; | ||
615 | } else { | ||
616 | /* pass rate changes to the parent clock */ | ||
617 | clk_init.flags |= CLK_SET_RATE_PARENT; | ||
618 | } | ||
619 | |||
620 | if (caps) { | ||
621 | pr_err("%s: unknown clock type 0x%x\n", __func__, caps); | ||
622 | goto out; | ||
623 | } | ||
624 | |||
625 | clk = clk_register(NULL, &ingenic_clk->hw); | ||
626 | if (IS_ERR(clk)) { | ||
627 | pr_err("%s: failed to register clock '%s'\n", __func__, | ||
628 | clk_info->name); | ||
629 | err = PTR_ERR(clk); | ||
630 | goto out; | ||
631 | } | ||
632 | |||
633 | err = clk_register_clkdev(clk, clk_info->name, NULL); | ||
634 | if (err) | ||
635 | goto out; | ||
636 | |||
637 | cgu->clocks.clks[idx] = clk; | ||
638 | out: | ||
639 | if (err) | ||
640 | kfree(ingenic_clk); | ||
641 | return err; | ||
642 | } | ||
643 | |||
644 | struct ingenic_cgu * | ||
645 | ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info, | ||
646 | unsigned num_clocks, struct device_node *np) | ||
647 | { | ||
648 | struct ingenic_cgu *cgu; | ||
649 | |||
650 | cgu = kzalloc(sizeof(*cgu), GFP_KERNEL); | ||
651 | if (!cgu) | ||
652 | goto err_out; | ||
653 | |||
654 | cgu->base = of_iomap(np, 0); | ||
655 | if (!cgu->base) { | ||
656 | pr_err("%s: failed to map CGU registers\n", __func__); | ||
657 | goto err_out_free; | ||
658 | } | ||
659 | |||
660 | cgu->np = np; | ||
661 | cgu->clock_info = clock_info; | ||
662 | cgu->clocks.clk_num = num_clocks; | ||
663 | |||
664 | spin_lock_init(&cgu->lock); | ||
665 | |||
666 | return cgu; | ||
667 | |||
668 | err_out_free: | ||
669 | kfree(cgu); | ||
670 | err_out: | ||
671 | return NULL; | ||
672 | } | ||
673 | |||
674 | int ingenic_cgu_register_clocks(struct ingenic_cgu *cgu) | ||
675 | { | ||
676 | unsigned i; | ||
677 | int err; | ||
678 | |||
679 | cgu->clocks.clks = kcalloc(cgu->clocks.clk_num, sizeof(struct clk *), | ||
680 | GFP_KERNEL); | ||
681 | if (!cgu->clocks.clks) { | ||
682 | err = -ENOMEM; | ||
683 | goto err_out; | ||
684 | } | ||
685 | |||
686 | for (i = 0; i < cgu->clocks.clk_num; i++) { | ||
687 | err = ingenic_register_clock(cgu, i); | ||
688 | if (err) | ||
689 | goto err_out_unregister; | ||
690 | } | ||
691 | |||
692 | err = of_clk_add_provider(cgu->np, of_clk_src_onecell_get, | ||
693 | &cgu->clocks); | ||
694 | if (err) | ||
695 | goto err_out_unregister; | ||
696 | |||
697 | return 0; | ||
698 | |||
699 | err_out_unregister: | ||
700 | for (i = 0; i < cgu->clocks.clk_num; i++) { | ||
701 | if (!cgu->clocks.clks[i]) | ||
702 | continue; | ||
703 | if (cgu->clock_info[i].type & CGU_CLK_EXT) | ||
704 | clk_put(cgu->clocks.clks[i]); | ||
705 | else | ||
706 | clk_unregister(cgu->clocks.clks[i]); | ||
707 | } | ||
708 | kfree(cgu->clocks.clks); | ||
709 | err_out: | ||
710 | return err; | ||
711 | } | ||
diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h new file mode 100644 index 000000000000..99347e2b97e8 --- /dev/null +++ b/drivers/clk/ingenic/cgu.h | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * Ingenic SoC CGU driver | ||
3 | * | ||
4 | * Copyright (c) 2013-2015 Imagination Technologies | ||
5 | * Author: Paul Burton <paul.burton@imgtec.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #ifndef __DRIVERS_CLK_INGENIC_CGU_H__ | ||
19 | #define __DRIVERS_CLK_INGENIC_CGU_H__ | ||
20 | |||
21 | #include <linux/bitops.h> | ||
22 | #include <linux/of.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | |||
25 | /** | ||
26 | * struct ingenic_cgu_pll_info - information about a PLL | ||
27 | * @reg: the offset of the PLL's control register within the CGU | ||
28 | * @m_shift: the number of bits to shift the multiplier value by (ie. the | ||
29 | * index of the lowest bit of the multiplier value in the PLL's | ||
30 | * control register) | ||
31 | * @m_bits: the size of the multiplier field in bits | ||
32 | * @m_offset: the multiplier value which encodes to 0 in the PLL's control | ||
33 | * register | ||
34 | * @n_shift: the number of bits to shift the divider value by (ie. the | ||
35 | * index of the lowest bit of the divider value in the PLL's | ||
36 | * control register) | ||
37 | * @n_bits: the size of the divider field in bits | ||
38 | * @n_offset: the divider value which encodes to 0 in the PLL's control | ||
39 | * register | ||
40 | * @od_shift: the number of bits to shift the post-VCO divider value by (ie. | ||
41 | * the index of the lowest bit of the post-VCO divider value in | ||
42 | * the PLL's control register) | ||
43 | * @od_bits: the size of the post-VCO divider field in bits | ||
44 | * @od_max: the maximum post-VCO divider value | ||
45 | * @od_encoding: a pointer to an array mapping post-VCO divider values to | ||
46 | * their encoded values in the PLL control register, or -1 for | ||
47 | * unsupported values | ||
48 | * @bypass_bit: the index of the bypass bit in the PLL control register | ||
49 | * @enable_bit: the index of the enable bit in the PLL control register | ||
50 | * @stable_bit: the index of the stable bit in the PLL control register | ||
51 | */ | ||
52 | struct ingenic_cgu_pll_info { | ||
53 | unsigned reg; | ||
54 | const s8 *od_encoding; | ||
55 | u8 m_shift, m_bits, m_offset; | ||
56 | u8 n_shift, n_bits, n_offset; | ||
57 | u8 od_shift, od_bits, od_max; | ||
58 | u8 bypass_bit; | ||
59 | u8 enable_bit; | ||
60 | u8 stable_bit; | ||
61 | }; | ||
62 | |||
63 | /** | ||
64 | * struct ingenic_cgu_mux_info - information about a clock mux | ||
65 | * @reg: offset of the mux control register within the CGU | ||
66 | * @shift: number of bits to shift the mux value by (ie. the index of | ||
67 | * the lowest bit of the mux value within its control register) | ||
68 | * @bits: the size of the mux value in bits | ||
69 | */ | ||
70 | struct ingenic_cgu_mux_info { | ||
71 | unsigned reg; | ||
72 | u8 shift; | ||
73 | u8 bits; | ||
74 | }; | ||
75 | |||
76 | /** | ||
77 | * struct ingenic_cgu_div_info - information about a divider | ||
78 | * @reg: offset of the divider control register within the CGU | ||
79 | * @shift: number of bits to shift the divide value by (ie. the index of | ||
80 | * the lowest bit of the divide value within its control register) | ||
81 | * @bits: the size of the divide value in bits | ||
82 | * @ce_bit: the index of the change enable bit within reg, or -1 if there | ||
83 | * isn't one | ||
84 | * @busy_bit: the index of the busy bit within reg, or -1 if there isn't one | ||
85 | * @stop_bit: the index of the stop bit within reg, or -1 if there isn't one | ||
86 | */ | ||
87 | struct ingenic_cgu_div_info { | ||
88 | unsigned reg; | ||
89 | u8 shift; | ||
90 | u8 bits; | ||
91 | s8 ce_bit; | ||
92 | s8 busy_bit; | ||
93 | s8 stop_bit; | ||
94 | }; | ||
95 | |||
96 | /** | ||
97 | * struct ingenic_cgu_fixdiv_info - information about a fixed divider | ||
98 | * @div: the divider applied to the parent clock | ||
99 | */ | ||
100 | struct ingenic_cgu_fixdiv_info { | ||
101 | unsigned div; | ||
102 | }; | ||
103 | |||
104 | /** | ||
105 | * struct ingenic_cgu_gate_info - information about a clock gate | ||
106 | * @reg: offset of the gate control register within the CGU | ||
107 | * @bit: offset of the bit in the register that controls the gate | ||
108 | */ | ||
109 | struct ingenic_cgu_gate_info { | ||
110 | unsigned reg; | ||
111 | u8 bit; | ||
112 | }; | ||
113 | |||
114 | /** | ||
115 | * struct ingenic_cgu_custom_info - information about a custom (SoC) clock | ||
116 | * @clk_ops: custom clock operation callbacks | ||
117 | */ | ||
118 | struct ingenic_cgu_custom_info { | ||
119 | struct clk_ops *clk_ops; | ||
120 | }; | ||
121 | |||
122 | /** | ||
123 | * struct ingenic_cgu_clk_info - information about a clock | ||
124 | * @name: name of the clock | ||
125 | * @type: a bitmask formed from CGU_CLK_* values | ||
126 | * @parents: an array of the indices of potential parents of this clock | ||
127 | * within the clock_info array of the CGU, or -1 in entries | ||
128 | * which correspond to no valid parent | ||
129 | * @pll: information valid if type includes CGU_CLK_PLL | ||
130 | * @gate: information valid if type includes CGU_CLK_GATE | ||
131 | * @mux: information valid if type includes CGU_CLK_MUX | ||
132 | * @div: information valid if type includes CGU_CLK_DIV | ||
133 | * @fixdiv: information valid if type includes CGU_CLK_FIXDIV | ||
134 | * @custom: information valid if type includes CGU_CLK_CUSTOM | ||
135 | */ | ||
136 | struct ingenic_cgu_clk_info { | ||
137 | const char *name; | ||
138 | |||
139 | enum { | ||
140 | CGU_CLK_NONE = 0, | ||
141 | CGU_CLK_EXT = BIT(0), | ||
142 | CGU_CLK_PLL = BIT(1), | ||
143 | CGU_CLK_GATE = BIT(2), | ||
144 | CGU_CLK_MUX = BIT(3), | ||
145 | CGU_CLK_MUX_GLITCHFREE = BIT(4), | ||
146 | CGU_CLK_DIV = BIT(5), | ||
147 | CGU_CLK_FIXDIV = BIT(6), | ||
148 | CGU_CLK_CUSTOM = BIT(7), | ||
149 | } type; | ||
150 | |||
151 | int parents[4]; | ||
152 | |||
153 | union { | ||
154 | struct ingenic_cgu_pll_info pll; | ||
155 | |||
156 | struct { | ||
157 | struct ingenic_cgu_gate_info gate; | ||
158 | struct ingenic_cgu_mux_info mux; | ||
159 | struct ingenic_cgu_div_info div; | ||
160 | struct ingenic_cgu_fixdiv_info fixdiv; | ||
161 | }; | ||
162 | |||
163 | struct ingenic_cgu_custom_info custom; | ||
164 | }; | ||
165 | }; | ||
166 | |||
167 | /** | ||
168 | * struct ingenic_cgu - data about the CGU | ||
169 | * @np: the device tree node that caused the CGU to be probed | ||
170 | * @base: the ioremap'ed base address of the CGU registers | ||
171 | * @clock_info: an array containing information about implemented clocks | ||
172 | * @clocks: used to provide clocks to DT, allows lookup of struct clk* | ||
173 | * @lock: lock to be held whilst manipulating CGU registers | ||
174 | */ | ||
175 | struct ingenic_cgu { | ||
176 | struct device_node *np; | ||
177 | void __iomem *base; | ||
178 | |||
179 | const struct ingenic_cgu_clk_info *clock_info; | ||
180 | struct clk_onecell_data clocks; | ||
181 | |||
182 | spinlock_t lock; | ||
183 | }; | ||
184 | |||
185 | /** | ||
186 | * struct ingenic_clk - private data for a clock | ||
187 | * @hw: see Documentation/clk.txt | ||
188 | * @cgu: a pointer to the CGU data | ||
189 | * @idx: the index of this clock in cgu->clock_info | ||
190 | */ | ||
191 | struct ingenic_clk { | ||
192 | struct clk_hw hw; | ||
193 | struct ingenic_cgu *cgu; | ||
194 | unsigned idx; | ||
195 | }; | ||
196 | |||
197 | #define to_ingenic_clk(_hw) container_of(_hw, struct ingenic_clk, hw) | ||
198 | |||
199 | /** | ||
200 | * ingenic_cgu_new() - create a new CGU instance | ||
201 | * @clock_info: an array of clock information structures describing the clocks | ||
202 | * which are implemented by the CGU | ||
203 | * @num_clocks: the number of entries in clock_info | ||
204 | * @np: the device tree node which causes this CGU to be probed | ||
205 | * | ||
206 | * Return: a pointer to the CGU instance if initialisation is successful, | ||
207 | * otherwise NULL. | ||
208 | */ | ||
209 | struct ingenic_cgu * | ||
210 | ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info, | ||
211 | unsigned num_clocks, struct device_node *np); | ||
212 | |||
213 | /** | ||
214 | * ingenic_cgu_register_clocks() - Registers the clocks | ||
215 | * @cgu: pointer to cgu data | ||
216 | * | ||
217 | * Register the clocks described by the CGU with the common clock framework. | ||
218 | * | ||
219 | * Return: 0 on success or -errno if unsuccesful. | ||
220 | */ | ||
221 | int ingenic_cgu_register_clocks(struct ingenic_cgu *cgu); | ||
222 | |||
223 | #endif /* __DRIVERS_CLK_INGENIC_CGU_H__ */ | ||
diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c new file mode 100644 index 000000000000..305a26c2a800 --- /dev/null +++ b/drivers/clk/ingenic/jz4740-cgu.c | |||
@@ -0,0 +1,303 @@ | |||
1 | /* | ||
2 | * Ingenic JZ4740 SoC CGU driver | ||
3 | * | ||
4 | * Copyright (c) 2015 Imagination Technologies | ||
5 | * Author: Paul Burton <paul.burton@imgtec.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/clk-provider.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <dt-bindings/clock/jz4740-cgu.h> | ||
22 | #include <asm/mach-jz4740/clock.h> | ||
23 | #include "cgu.h" | ||
24 | |||
25 | /* CGU register offsets */ | ||
26 | #define CGU_REG_CPCCR 0x00 | ||
27 | #define CGU_REG_LCR 0x04 | ||
28 | #define CGU_REG_CPPCR 0x10 | ||
29 | #define CGU_REG_CLKGR 0x20 | ||
30 | #define CGU_REG_SCR 0x24 | ||
31 | #define CGU_REG_I2SCDR 0x60 | ||
32 | #define CGU_REG_LPCDR 0x64 | ||
33 | #define CGU_REG_MSCCDR 0x68 | ||
34 | #define CGU_REG_UHCCDR 0x6c | ||
35 | #define CGU_REG_SSICDR 0x74 | ||
36 | |||
37 | /* bits within a PLL control register */ | ||
38 | #define PLLCTL_M_SHIFT 23 | ||
39 | #define PLLCTL_M_MASK (0x1ff << PLLCTL_M_SHIFT) | ||
40 | #define PLLCTL_N_SHIFT 18 | ||
41 | #define PLLCTL_N_MASK (0x1f << PLLCTL_N_SHIFT) | ||
42 | #define PLLCTL_OD_SHIFT 16 | ||
43 | #define PLLCTL_OD_MASK (0x3 << PLLCTL_OD_SHIFT) | ||
44 | #define PLLCTL_STABLE (1 << 10) | ||
45 | #define PLLCTL_BYPASS (1 << 9) | ||
46 | #define PLLCTL_ENABLE (1 << 8) | ||
47 | |||
48 | /* bits within the LCR register */ | ||
49 | #define LCR_SLEEP (1 << 0) | ||
50 | |||
51 | /* bits within the CLKGR register */ | ||
52 | #define CLKGR_UDC (1 << 11) | ||
53 | |||
54 | static struct ingenic_cgu *cgu; | ||
55 | |||
56 | static const s8 pll_od_encoding[4] = { | ||
57 | 0x0, 0x1, -1, 0x3, | ||
58 | }; | ||
59 | |||
60 | static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { | ||
61 | |||
62 | /* External clocks */ | ||
63 | |||
64 | [JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT }, | ||
65 | [JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT }, | ||
66 | |||
67 | [JZ4740_CLK_PLL] = { | ||
68 | "pll", CGU_CLK_PLL, | ||
69 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | ||
70 | .pll = { | ||
71 | .reg = CGU_REG_CPPCR, | ||
72 | .m_shift = 23, | ||
73 | .m_bits = 9, | ||
74 | .m_offset = 2, | ||
75 | .n_shift = 18, | ||
76 | .n_bits = 5, | ||
77 | .n_offset = 2, | ||
78 | .od_shift = 16, | ||
79 | .od_bits = 2, | ||
80 | .od_max = 4, | ||
81 | .od_encoding = pll_od_encoding, | ||
82 | .stable_bit = 10, | ||
83 | .bypass_bit = 9, | ||
84 | .enable_bit = 8, | ||
85 | }, | ||
86 | }, | ||
87 | |||
88 | /* Muxes & dividers */ | ||
89 | |||
90 | [JZ4740_CLK_PLL_HALF] = { | ||
91 | "pll half", CGU_CLK_DIV, | ||
92 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, | ||
93 | .div = { CGU_REG_CPCCR, 21, 1, -1, -1, -1 }, | ||
94 | }, | ||
95 | |||
96 | [JZ4740_CLK_CCLK] = { | ||
97 | "cclk", CGU_CLK_DIV, | ||
98 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, | ||
99 | .div = { CGU_REG_CPCCR, 0, 4, 22, -1, -1 }, | ||
100 | }, | ||
101 | |||
102 | [JZ4740_CLK_HCLK] = { | ||
103 | "hclk", CGU_CLK_DIV, | ||
104 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, | ||
105 | .div = { CGU_REG_CPCCR, 4, 4, 22, -1, -1 }, | ||
106 | }, | ||
107 | |||
108 | [JZ4740_CLK_PCLK] = { | ||
109 | "pclk", CGU_CLK_DIV, | ||
110 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, | ||
111 | .div = { CGU_REG_CPCCR, 8, 4, 22, -1, -1 }, | ||
112 | }, | ||
113 | |||
114 | [JZ4740_CLK_MCLK] = { | ||
115 | "mclk", CGU_CLK_DIV, | ||
116 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, | ||
117 | .div = { CGU_REG_CPCCR, 12, 4, 22, -1, -1 }, | ||
118 | }, | ||
119 | |||
120 | [JZ4740_CLK_LCD] = { | ||
121 | "lcd", CGU_CLK_DIV | CGU_CLK_GATE, | ||
122 | .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, | ||
123 | .div = { CGU_REG_CPCCR, 16, 5, 22, -1, -1 }, | ||
124 | .gate = { CGU_REG_CLKGR, 10 }, | ||
125 | }, | ||
126 | |||
127 | [JZ4740_CLK_LCD_PCLK] = { | ||
128 | "lcd_pclk", CGU_CLK_DIV, | ||
129 | .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, | ||
130 | .div = { CGU_REG_LPCDR, 0, 11, -1, -1, -1 }, | ||
131 | }, | ||
132 | |||
133 | [JZ4740_CLK_I2S] = { | ||
134 | "i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, | ||
135 | .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, | ||
136 | .mux = { CGU_REG_CPCCR, 31, 1 }, | ||
137 | .div = { CGU_REG_I2SCDR, 0, 8, -1, -1, -1 }, | ||
138 | .gate = { CGU_REG_CLKGR, 6 }, | ||
139 | }, | ||
140 | |||
141 | [JZ4740_CLK_SPI] = { | ||
142 | "spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, | ||
143 | .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 }, | ||
144 | .mux = { CGU_REG_SSICDR, 31, 1 }, | ||
145 | .div = { CGU_REG_SSICDR, 0, 4, -1, -1, -1 }, | ||
146 | .gate = { CGU_REG_CLKGR, 4 }, | ||
147 | }, | ||
148 | |||
149 | [JZ4740_CLK_MMC] = { | ||
150 | "mmc", CGU_CLK_DIV | CGU_CLK_GATE, | ||
151 | .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, | ||
152 | .div = { CGU_REG_MSCCDR, 0, 5, -1, -1, -1 }, | ||
153 | .gate = { CGU_REG_CLKGR, 7 }, | ||
154 | }, | ||
155 | |||
156 | [JZ4740_CLK_UHC] = { | ||
157 | "uhc", CGU_CLK_DIV | CGU_CLK_GATE, | ||
158 | .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, | ||
159 | .div = { CGU_REG_UHCCDR, 0, 4, -1, -1, -1 }, | ||
160 | .gate = { CGU_REG_CLKGR, 14 }, | ||
161 | }, | ||
162 | |||
163 | [JZ4740_CLK_UDC] = { | ||
164 | "udc", CGU_CLK_MUX | CGU_CLK_DIV, | ||
165 | .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, | ||
166 | .mux = { CGU_REG_CPCCR, 29, 1 }, | ||
167 | .div = { CGU_REG_CPCCR, 23, 6, -1, -1, -1 }, | ||
168 | .gate = { CGU_REG_SCR, 6 }, | ||
169 | }, | ||
170 | |||
171 | /* Gate-only clocks */ | ||
172 | |||
173 | [JZ4740_CLK_UART0] = { | ||
174 | "uart0", CGU_CLK_GATE, | ||
175 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | ||
176 | .gate = { CGU_REG_CLKGR, 0 }, | ||
177 | }, | ||
178 | |||
179 | [JZ4740_CLK_UART1] = { | ||
180 | "uart1", CGU_CLK_GATE, | ||
181 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | ||
182 | .gate = { CGU_REG_CLKGR, 15 }, | ||
183 | }, | ||
184 | |||
185 | [JZ4740_CLK_DMA] = { | ||
186 | "dma", CGU_CLK_GATE, | ||
187 | .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, | ||
188 | .gate = { CGU_REG_CLKGR, 12 }, | ||
189 | }, | ||
190 | |||
191 | [JZ4740_CLK_IPU] = { | ||
192 | "ipu", CGU_CLK_GATE, | ||
193 | .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, | ||
194 | .gate = { CGU_REG_CLKGR, 13 }, | ||
195 | }, | ||
196 | |||
197 | [JZ4740_CLK_ADC] = { | ||
198 | "adc", CGU_CLK_GATE, | ||
199 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | ||
200 | .gate = { CGU_REG_CLKGR, 8 }, | ||
201 | }, | ||
202 | |||
203 | [JZ4740_CLK_I2C] = { | ||
204 | "i2c", CGU_CLK_GATE, | ||
205 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | ||
206 | .gate = { CGU_REG_CLKGR, 3 }, | ||
207 | }, | ||
208 | |||
209 | [JZ4740_CLK_AIC] = { | ||
210 | "aic", CGU_CLK_GATE, | ||
211 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | ||
212 | .gate = { CGU_REG_CLKGR, 5 }, | ||
213 | }, | ||
214 | }; | ||
215 | |||
216 | static void __init jz4740_cgu_init(struct device_node *np) | ||
217 | { | ||
218 | int retval; | ||
219 | |||
220 | cgu = ingenic_cgu_new(jz4740_cgu_clocks, | ||
221 | ARRAY_SIZE(jz4740_cgu_clocks), np); | ||
222 | if (!cgu) { | ||
223 | pr_err("%s: failed to initialise CGU\n", __func__); | ||
224 | return; | ||
225 | } | ||
226 | |||
227 | retval = ingenic_cgu_register_clocks(cgu); | ||
228 | if (retval) | ||
229 | pr_err("%s: failed to register CGU Clocks\n", __func__); | ||
230 | } | ||
231 | CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init); | ||
232 | |||
233 | void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode) | ||
234 | { | ||
235 | uint32_t lcr = readl(cgu->base + CGU_REG_LCR); | ||
236 | |||
237 | switch (mode) { | ||
238 | case JZ4740_WAIT_MODE_IDLE: | ||
239 | lcr &= ~LCR_SLEEP; | ||
240 | break; | ||
241 | |||
242 | case JZ4740_WAIT_MODE_SLEEP: | ||
243 | lcr |= LCR_SLEEP; | ||
244 | break; | ||
245 | } | ||
246 | |||
247 | writel(lcr, cgu->base + CGU_REG_LCR); | ||
248 | } | ||
249 | |||
250 | void jz4740_clock_udc_disable_auto_suspend(void) | ||
251 | { | ||
252 | uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); | ||
253 | |||
254 | clkgr &= ~CLKGR_UDC; | ||
255 | writel(clkgr, cgu->base + CGU_REG_CLKGR); | ||
256 | } | ||
257 | EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend); | ||
258 | |||
259 | void jz4740_clock_udc_enable_auto_suspend(void) | ||
260 | { | ||
261 | uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); | ||
262 | |||
263 | clkgr |= CLKGR_UDC; | ||
264 | writel(clkgr, cgu->base + CGU_REG_CLKGR); | ||
265 | } | ||
266 | EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend); | ||
267 | |||
268 | #define JZ_CLOCK_GATE_UART0 BIT(0) | ||
269 | #define JZ_CLOCK_GATE_TCU BIT(1) | ||
270 | #define JZ_CLOCK_GATE_DMAC BIT(12) | ||
271 | |||
272 | void jz4740_clock_suspend(void) | ||
273 | { | ||
274 | uint32_t clkgr, cppcr; | ||
275 | |||
276 | clkgr = readl(cgu->base + CGU_REG_CLKGR); | ||
277 | clkgr |= JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0; | ||
278 | writel(clkgr, cgu->base + CGU_REG_CLKGR); | ||
279 | |||
280 | cppcr = readl(cgu->base + CGU_REG_CPPCR); | ||
281 | cppcr &= ~BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); | ||
282 | writel(cppcr, cgu->base + CGU_REG_CPPCR); | ||
283 | } | ||
284 | |||
285 | void jz4740_clock_resume(void) | ||
286 | { | ||
287 | uint32_t clkgr, cppcr, stable; | ||
288 | |||
289 | cppcr = readl(cgu->base + CGU_REG_CPPCR); | ||
290 | cppcr |= BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); | ||
291 | writel(cppcr, cgu->base + CGU_REG_CPPCR); | ||
292 | |||
293 | stable = BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.stable_bit); | ||
294 | do { | ||
295 | cppcr = readl(cgu->base + CGU_REG_CPPCR); | ||
296 | } while (!(cppcr & stable)); | ||
297 | |||
298 | clkgr = readl(cgu->base + CGU_REG_CLKGR); | ||
299 | clkgr &= ~JZ_CLOCK_GATE_TCU; | ||
300 | clkgr &= ~JZ_CLOCK_GATE_DMAC; | ||
301 | clkgr &= ~JZ_CLOCK_GATE_UART0; | ||
302 | writel(clkgr, cgu->base + CGU_REG_CLKGR); | ||
303 | } | ||
diff --git a/drivers/clk/ingenic/jz4780-cgu.c b/drivers/clk/ingenic/jz4780-cgu.c new file mode 100644 index 000000000000..431f962300b6 --- /dev/null +++ b/drivers/clk/ingenic/jz4780-cgu.c | |||
@@ -0,0 +1,733 @@ | |||
1 | /* | ||
2 | * Ingenic JZ4780 SoC CGU driver | ||
3 | * | ||
4 | * Copyright (c) 2013-2015 Imagination Technologies | ||
5 | * Author: Paul Burton <paul.burton@imgtec.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/clk-provider.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <dt-bindings/clock/jz4780-cgu.h> | ||
22 | #include "cgu.h" | ||
23 | |||
24 | /* CGU register offsets */ | ||
25 | #define CGU_REG_CLOCKCONTROL 0x00 | ||
26 | #define CGU_REG_PLLCONTROL 0x0c | ||
27 | #define CGU_REG_APLL 0x10 | ||
28 | #define CGU_REG_MPLL 0x14 | ||
29 | #define CGU_REG_EPLL 0x18 | ||
30 | #define CGU_REG_VPLL 0x1c | ||
31 | #define CGU_REG_CLKGR0 0x20 | ||
32 | #define CGU_REG_OPCR 0x24 | ||
33 | #define CGU_REG_CLKGR1 0x28 | ||
34 | #define CGU_REG_DDRCDR 0x2c | ||
35 | #define CGU_REG_VPUCDR 0x30 | ||
36 | #define CGU_REG_USBPCR 0x3c | ||
37 | #define CGU_REG_USBRDT 0x40 | ||
38 | #define CGU_REG_USBVBFIL 0x44 | ||
39 | #define CGU_REG_USBPCR1 0x48 | ||
40 | #define CGU_REG_LP0CDR 0x54 | ||
41 | #define CGU_REG_I2SCDR 0x60 | ||
42 | #define CGU_REG_LP1CDR 0x64 | ||
43 | #define CGU_REG_MSC0CDR 0x68 | ||
44 | #define CGU_REG_UHCCDR 0x6c | ||
45 | #define CGU_REG_SSICDR 0x74 | ||
46 | #define CGU_REG_CIMCDR 0x7c | ||
47 | #define CGU_REG_PCMCDR 0x84 | ||
48 | #define CGU_REG_GPUCDR 0x88 | ||
49 | #define CGU_REG_HDMICDR 0x8c | ||
50 | #define CGU_REG_MSC1CDR 0xa4 | ||
51 | #define CGU_REG_MSC2CDR 0xa8 | ||
52 | #define CGU_REG_BCHCDR 0xac | ||
53 | #define CGU_REG_CLOCKSTATUS 0xd4 | ||
54 | |||
55 | /* bits within the OPCR register */ | ||
56 | #define OPCR_SPENDN0 (1 << 7) | ||
57 | #define OPCR_SPENDN1 (1 << 6) | ||
58 | |||
59 | /* bits within the USBPCR register */ | ||
60 | #define USBPCR_USB_MODE BIT(31) | ||
61 | #define USBPCR_IDPULLUP_MASK (0x3 << 28) | ||
62 | #define USBPCR_COMMONONN BIT(25) | ||
63 | #define USBPCR_VBUSVLDEXT BIT(24) | ||
64 | #define USBPCR_VBUSVLDEXTSEL BIT(23) | ||
65 | #define USBPCR_POR BIT(22) | ||
66 | #define USBPCR_OTG_DISABLE BIT(20) | ||
67 | #define USBPCR_COMPDISTUNE_MASK (0x7 << 17) | ||
68 | #define USBPCR_OTGTUNE_MASK (0x7 << 14) | ||
69 | #define USBPCR_SQRXTUNE_MASK (0x7 << 11) | ||
70 | #define USBPCR_TXFSLSTUNE_MASK (0xf << 7) | ||
71 | #define USBPCR_TXPREEMPHTUNE BIT(6) | ||
72 | #define USBPCR_TXHSXVTUNE_MASK (0x3 << 4) | ||
73 | #define USBPCR_TXVREFTUNE_MASK 0xf | ||
74 | |||
75 | /* bits within the USBPCR1 register */ | ||
76 | #define USBPCR1_REFCLKSEL_SHIFT 26 | ||
77 | #define USBPCR1_REFCLKSEL_MASK (0x3 << USBPCR1_REFCLKSEL_SHIFT) | ||
78 | #define USBPCR1_REFCLKSEL_CORE (0x2 << USBPCR1_REFCLKSEL_SHIFT) | ||
79 | #define USBPCR1_REFCLKDIV_SHIFT 24 | ||
80 | #define USBPCR1_REFCLKDIV_MASK (0x3 << USBPCR1_REFCLKDIV_SHIFT) | ||
81 | #define USBPCR1_REFCLKDIV_19_2 (0x3 << USBPCR1_REFCLKDIV_SHIFT) | ||
82 | #define USBPCR1_REFCLKDIV_48 (0x2 << USBPCR1_REFCLKDIV_SHIFT) | ||
83 | #define USBPCR1_REFCLKDIV_24 (0x1 << USBPCR1_REFCLKDIV_SHIFT) | ||
84 | #define USBPCR1_REFCLKDIV_12 (0x0 << USBPCR1_REFCLKDIV_SHIFT) | ||
85 | #define USBPCR1_USB_SEL BIT(28) | ||
86 | #define USBPCR1_WORD_IF0 BIT(19) | ||
87 | #define USBPCR1_WORD_IF1 BIT(18) | ||
88 | |||
89 | /* bits within the USBRDT register */ | ||
90 | #define USBRDT_VBFIL_LD_EN BIT(25) | ||
91 | #define USBRDT_USBRDT_MASK 0x7fffff | ||
92 | |||
93 | /* bits within the USBVBFIL register */ | ||
94 | #define USBVBFIL_IDDIGFIL_SHIFT 16 | ||
95 | #define USBVBFIL_IDDIGFIL_MASK (0xffff << USBVBFIL_IDDIGFIL_SHIFT) | ||
96 | #define USBVBFIL_USBVBFIL_MASK (0xffff) | ||
97 | |||
98 | static struct ingenic_cgu *cgu; | ||
99 | |||
100 | static u8 jz4780_otg_phy_get_parent(struct clk_hw *hw) | ||
101 | { | ||
102 | /* we only use CLKCORE, revisit if that ever changes */ | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static int jz4780_otg_phy_set_parent(struct clk_hw *hw, u8 idx) | ||
107 | { | ||
108 | unsigned long flags; | ||
109 | u32 usbpcr1; | ||
110 | |||
111 | if (idx > 0) | ||
112 | return -EINVAL; | ||
113 | |||
114 | spin_lock_irqsave(&cgu->lock, flags); | ||
115 | |||
116 | usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1); | ||
117 | usbpcr1 &= ~USBPCR1_REFCLKSEL_MASK; | ||
118 | /* we only use CLKCORE */ | ||
119 | usbpcr1 |= USBPCR1_REFCLKSEL_CORE; | ||
120 | writel(usbpcr1, cgu->base + CGU_REG_USBPCR1); | ||
121 | |||
122 | spin_unlock_irqrestore(&cgu->lock, flags); | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static unsigned long jz4780_otg_phy_recalc_rate(struct clk_hw *hw, | ||
127 | unsigned long parent_rate) | ||
128 | { | ||
129 | u32 usbpcr1; | ||
130 | unsigned refclk_div; | ||
131 | |||
132 | usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1); | ||
133 | refclk_div = usbpcr1 & USBPCR1_REFCLKDIV_MASK; | ||
134 | |||
135 | switch (refclk_div) { | ||
136 | case USBPCR1_REFCLKDIV_12: | ||
137 | return 12000000; | ||
138 | |||
139 | case USBPCR1_REFCLKDIV_24: | ||
140 | return 24000000; | ||
141 | |||
142 | case USBPCR1_REFCLKDIV_48: | ||
143 | return 48000000; | ||
144 | |||
145 | case USBPCR1_REFCLKDIV_19_2: | ||
146 | return 19200000; | ||
147 | } | ||
148 | |||
149 | BUG(); | ||
150 | return parent_rate; | ||
151 | } | ||
152 | |||
153 | static long jz4780_otg_phy_round_rate(struct clk_hw *hw, unsigned long req_rate, | ||
154 | unsigned long *parent_rate) | ||
155 | { | ||
156 | if (req_rate < 15600000) | ||
157 | return 12000000; | ||
158 | |||
159 | if (req_rate < 21600000) | ||
160 | return 19200000; | ||
161 | |||
162 | if (req_rate < 36000000) | ||
163 | return 24000000; | ||
164 | |||
165 | return 48000000; | ||
166 | } | ||
167 | |||
168 | static int jz4780_otg_phy_set_rate(struct clk_hw *hw, unsigned long req_rate, | ||
169 | unsigned long parent_rate) | ||
170 | { | ||
171 | unsigned long flags; | ||
172 | u32 usbpcr1, div_bits; | ||
173 | |||
174 | switch (req_rate) { | ||
175 | case 12000000: | ||
176 | div_bits = USBPCR1_REFCLKDIV_12; | ||
177 | break; | ||
178 | |||
179 | case 19200000: | ||
180 | div_bits = USBPCR1_REFCLKDIV_19_2; | ||
181 | break; | ||
182 | |||
183 | case 24000000: | ||
184 | div_bits = USBPCR1_REFCLKDIV_24; | ||
185 | break; | ||
186 | |||
187 | case 48000000: | ||
188 | div_bits = USBPCR1_REFCLKDIV_48; | ||
189 | break; | ||
190 | |||
191 | default: | ||
192 | return -EINVAL; | ||
193 | } | ||
194 | |||
195 | spin_lock_irqsave(&cgu->lock, flags); | ||
196 | |||
197 | usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1); | ||
198 | usbpcr1 &= ~USBPCR1_REFCLKDIV_MASK; | ||
199 | usbpcr1 |= div_bits; | ||
200 | writel(usbpcr1, cgu->base + CGU_REG_USBPCR1); | ||
201 | |||
202 | spin_unlock_irqrestore(&cgu->lock, flags); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static struct clk_ops jz4780_otg_phy_ops = { | ||
207 | .get_parent = jz4780_otg_phy_get_parent, | ||
208 | .set_parent = jz4780_otg_phy_set_parent, | ||
209 | |||
210 | .recalc_rate = jz4780_otg_phy_recalc_rate, | ||
211 | .round_rate = jz4780_otg_phy_round_rate, | ||
212 | .set_rate = jz4780_otg_phy_set_rate, | ||
213 | }; | ||
214 | |||
215 | static const s8 pll_od_encoding[16] = { | ||
216 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, | ||
217 | 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, | ||
218 | }; | ||
219 | |||
220 | static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = { | ||
221 | |||
222 | /* External clocks */ | ||
223 | |||
224 | [JZ4780_CLK_EXCLK] = { "ext", CGU_CLK_EXT }, | ||
225 | [JZ4780_CLK_RTCLK] = { "rtc", CGU_CLK_EXT }, | ||
226 | |||
227 | /* PLLs */ | ||
228 | |||
229 | #define DEF_PLL(name) { \ | ||
230 | .reg = CGU_REG_ ## name, \ | ||
231 | .m_shift = 19, \ | ||
232 | .m_bits = 13, \ | ||
233 | .m_offset = 1, \ | ||
234 | .n_shift = 13, \ | ||
235 | .n_bits = 6, \ | ||
236 | .n_offset = 1, \ | ||
237 | .od_shift = 9, \ | ||
238 | .od_bits = 4, \ | ||
239 | .od_max = 16, \ | ||
240 | .od_encoding = pll_od_encoding, \ | ||
241 | .stable_bit = 6, \ | ||
242 | .bypass_bit = 1, \ | ||
243 | .enable_bit = 0, \ | ||
244 | } | ||
245 | |||
246 | [JZ4780_CLK_APLL] = { | ||
247 | "apll", CGU_CLK_PLL, | ||
248 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
249 | .pll = DEF_PLL(APLL), | ||
250 | }, | ||
251 | |||
252 | [JZ4780_CLK_MPLL] = { | ||
253 | "mpll", CGU_CLK_PLL, | ||
254 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
255 | .pll = DEF_PLL(MPLL), | ||
256 | }, | ||
257 | |||
258 | [JZ4780_CLK_EPLL] = { | ||
259 | "epll", CGU_CLK_PLL, | ||
260 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
261 | .pll = DEF_PLL(EPLL), | ||
262 | }, | ||
263 | |||
264 | [JZ4780_CLK_VPLL] = { | ||
265 | "vpll", CGU_CLK_PLL, | ||
266 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
267 | .pll = DEF_PLL(VPLL), | ||
268 | }, | ||
269 | |||
270 | #undef DEF_PLL | ||
271 | |||
272 | /* Custom (SoC-specific) OTG PHY */ | ||
273 | |||
274 | [JZ4780_CLK_OTGPHY] = { | ||
275 | "otg_phy", CGU_CLK_CUSTOM, | ||
276 | .parents = { -1, -1, JZ4780_CLK_EXCLK, -1 }, | ||
277 | .custom = { &jz4780_otg_phy_ops }, | ||
278 | }, | ||
279 | |||
280 | /* Muxes & dividers */ | ||
281 | |||
282 | [JZ4780_CLK_SCLKA] = { | ||
283 | "sclk_a", CGU_CLK_MUX, | ||
284 | .parents = { -1, JZ4780_CLK_APLL, JZ4780_CLK_EXCLK, | ||
285 | JZ4780_CLK_RTCLK }, | ||
286 | .mux = { CGU_REG_CLOCKCONTROL, 30, 2 }, | ||
287 | }, | ||
288 | |||
289 | [JZ4780_CLK_CPUMUX] = { | ||
290 | "cpumux", CGU_CLK_MUX, | ||
291 | .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
292 | JZ4780_CLK_EPLL }, | ||
293 | .mux = { CGU_REG_CLOCKCONTROL, 28, 2 }, | ||
294 | }, | ||
295 | |||
296 | [JZ4780_CLK_CPU] = { | ||
297 | "cpu", CGU_CLK_DIV, | ||
298 | .parents = { JZ4780_CLK_CPUMUX, -1, -1, -1 }, | ||
299 | .div = { CGU_REG_CLOCKCONTROL, 0, 4, 22, -1, -1 }, | ||
300 | }, | ||
301 | |||
302 | [JZ4780_CLK_L2CACHE] = { | ||
303 | "l2cache", CGU_CLK_DIV, | ||
304 | .parents = { JZ4780_CLK_CPUMUX, -1, -1, -1 }, | ||
305 | .div = { CGU_REG_CLOCKCONTROL, 4, 4, -1, -1, -1 }, | ||
306 | }, | ||
307 | |||
308 | [JZ4780_CLK_AHB0] = { | ||
309 | "ahb0", CGU_CLK_MUX | CGU_CLK_DIV, | ||
310 | .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
311 | JZ4780_CLK_EPLL }, | ||
312 | .mux = { CGU_REG_CLOCKCONTROL, 26, 2 }, | ||
313 | .div = { CGU_REG_CLOCKCONTROL, 8, 4, 21, -1, -1 }, | ||
314 | }, | ||
315 | |||
316 | [JZ4780_CLK_AHB2PMUX] = { | ||
317 | "ahb2_apb_mux", CGU_CLK_MUX, | ||
318 | .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
319 | JZ4780_CLK_RTCLK }, | ||
320 | .mux = { CGU_REG_CLOCKCONTROL, 24, 2 }, | ||
321 | }, | ||
322 | |||
323 | [JZ4780_CLK_AHB2] = { | ||
324 | "ahb2", CGU_CLK_DIV, | ||
325 | .parents = { JZ4780_CLK_AHB2PMUX, -1, -1, -1 }, | ||
326 | .div = { CGU_REG_CLOCKCONTROL, 12, 4, 20, -1, -1 }, | ||
327 | }, | ||
328 | |||
329 | [JZ4780_CLK_PCLK] = { | ||
330 | "pclk", CGU_CLK_DIV, | ||
331 | .parents = { JZ4780_CLK_AHB2PMUX, -1, -1, -1 }, | ||
332 | .div = { CGU_REG_CLOCKCONTROL, 16, 4, 20, -1, -1 }, | ||
333 | }, | ||
334 | |||
335 | [JZ4780_CLK_DDR] = { | ||
336 | "ddr", CGU_CLK_MUX | CGU_CLK_DIV, | ||
337 | .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1 }, | ||
338 | .mux = { CGU_REG_DDRCDR, 30, 2 }, | ||
339 | .div = { CGU_REG_DDRCDR, 0, 4, 29, 28, 27 }, | ||
340 | }, | ||
341 | |||
342 | [JZ4780_CLK_VPU] = { | ||
343 | "vpu", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, | ||
344 | .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
345 | JZ4780_CLK_EPLL, -1 }, | ||
346 | .mux = { CGU_REG_VPUCDR, 30, 2 }, | ||
347 | .div = { CGU_REG_VPUCDR, 0, 4, 29, 28, 27 }, | ||
348 | .gate = { CGU_REG_CLKGR1, 2 }, | ||
349 | }, | ||
350 | |||
351 | [JZ4780_CLK_I2SPLL] = { | ||
352 | "i2s_pll", CGU_CLK_MUX | CGU_CLK_DIV, | ||
353 | .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_EPLL, -1, -1 }, | ||
354 | .mux = { CGU_REG_I2SCDR, 30, 1 }, | ||
355 | .div = { CGU_REG_I2SCDR, 0, 8, 29, 28, 27 }, | ||
356 | }, | ||
357 | |||
358 | [JZ4780_CLK_I2S] = { | ||
359 | "i2s", CGU_CLK_MUX, | ||
360 | .parents = { JZ4780_CLK_EXCLK, JZ4780_CLK_I2SPLL, -1, -1 }, | ||
361 | .mux = { CGU_REG_I2SCDR, 31, 1 }, | ||
362 | }, | ||
363 | |||
364 | [JZ4780_CLK_LCD0PIXCLK] = { | ||
365 | "lcd0pixclk", CGU_CLK_MUX | CGU_CLK_DIV, | ||
366 | .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
367 | JZ4780_CLK_VPLL, -1 }, | ||
368 | .mux = { CGU_REG_LP0CDR, 30, 2 }, | ||
369 | .div = { CGU_REG_LP0CDR, 0, 8, 28, 27, 26 }, | ||
370 | }, | ||
371 | |||
372 | [JZ4780_CLK_LCD1PIXCLK] = { | ||
373 | "lcd1pixclk", CGU_CLK_MUX | CGU_CLK_DIV, | ||
374 | .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
375 | JZ4780_CLK_VPLL, -1 }, | ||
376 | .mux = { CGU_REG_LP1CDR, 30, 2 }, | ||
377 | .div = { CGU_REG_LP1CDR, 0, 8, 28, 27, 26 }, | ||
378 | }, | ||
379 | |||
380 | [JZ4780_CLK_MSCMUX] = { | ||
381 | "msc_mux", CGU_CLK_MUX, | ||
382 | .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1 }, | ||
383 | .mux = { CGU_REG_MSC0CDR, 30, 2 }, | ||
384 | }, | ||
385 | |||
386 | [JZ4780_CLK_MSC0] = { | ||
387 | "msc0", CGU_CLK_DIV | CGU_CLK_GATE, | ||
388 | .parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 }, | ||
389 | .div = { CGU_REG_MSC0CDR, 0, 8, 29, 28, 27 }, | ||
390 | .gate = { CGU_REG_CLKGR0, 3 }, | ||
391 | }, | ||
392 | |||
393 | [JZ4780_CLK_MSC1] = { | ||
394 | "msc1", CGU_CLK_DIV | CGU_CLK_GATE, | ||
395 | .parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 }, | ||
396 | .div = { CGU_REG_MSC1CDR, 0, 8, 29, 28, 27 }, | ||
397 | .gate = { CGU_REG_CLKGR0, 11 }, | ||
398 | }, | ||
399 | |||
400 | [JZ4780_CLK_MSC2] = { | ||
401 | "msc2", CGU_CLK_DIV | CGU_CLK_GATE, | ||
402 | .parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 }, | ||
403 | .div = { CGU_REG_MSC2CDR, 0, 8, 29, 28, 27 }, | ||
404 | .gate = { CGU_REG_CLKGR0, 12 }, | ||
405 | }, | ||
406 | |||
407 | [JZ4780_CLK_UHC] = { | ||
408 | "uhc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, | ||
409 | .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
410 | JZ4780_CLK_EPLL, JZ4780_CLK_OTGPHY }, | ||
411 | .mux = { CGU_REG_UHCCDR, 30, 2 }, | ||
412 | .div = { CGU_REG_UHCCDR, 0, 8, 29, 28, 27 }, | ||
413 | .gate = { CGU_REG_CLKGR0, 24 }, | ||
414 | }, | ||
415 | |||
416 | [JZ4780_CLK_SSIPLL] = { | ||
417 | "ssi_pll", CGU_CLK_MUX | CGU_CLK_DIV, | ||
418 | .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1, -1 }, | ||
419 | .mux = { CGU_REG_SSICDR, 30, 1 }, | ||
420 | .div = { CGU_REG_SSICDR, 0, 8, 29, 28, 27 }, | ||
421 | }, | ||
422 | |||
423 | [JZ4780_CLK_SSI] = { | ||
424 | "ssi", CGU_CLK_MUX, | ||
425 | .parents = { JZ4780_CLK_EXCLK, JZ4780_CLK_SSIPLL, -1, -1 }, | ||
426 | .mux = { CGU_REG_SSICDR, 31, 1 }, | ||
427 | }, | ||
428 | |||
429 | [JZ4780_CLK_CIMMCLK] = { | ||
430 | "cim_mclk", CGU_CLK_MUX | CGU_CLK_DIV, | ||
431 | .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1, -1 }, | ||
432 | .mux = { CGU_REG_CIMCDR, 31, 1 }, | ||
433 | .div = { CGU_REG_CIMCDR, 0, 8, 30, 29, 28 }, | ||
434 | }, | ||
435 | |||
436 | [JZ4780_CLK_PCMPLL] = { | ||
437 | "pcm_pll", CGU_CLK_MUX | CGU_CLK_DIV, | ||
438 | .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
439 | JZ4780_CLK_EPLL, JZ4780_CLK_VPLL }, | ||
440 | .mux = { CGU_REG_PCMCDR, 29, 2 }, | ||
441 | .div = { CGU_REG_PCMCDR, 0, 8, 28, 27, 26 }, | ||
442 | }, | ||
443 | |||
444 | [JZ4780_CLK_PCM] = { | ||
445 | "pcm", CGU_CLK_MUX | CGU_CLK_GATE, | ||
446 | .parents = { JZ4780_CLK_EXCLK, JZ4780_CLK_PCMPLL, -1, -1 }, | ||
447 | .mux = { CGU_REG_PCMCDR, 31, 1 }, | ||
448 | .gate = { CGU_REG_CLKGR1, 3 }, | ||
449 | }, | ||
450 | |||
451 | [JZ4780_CLK_GPU] = { | ||
452 | "gpu", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, | ||
453 | .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
454 | JZ4780_CLK_EPLL }, | ||
455 | .mux = { CGU_REG_GPUCDR, 30, 2 }, | ||
456 | .div = { CGU_REG_GPUCDR, 0, 4, 29, 28, 27 }, | ||
457 | .gate = { CGU_REG_CLKGR1, 4 }, | ||
458 | }, | ||
459 | |||
460 | [JZ4780_CLK_HDMI] = { | ||
461 | "hdmi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, | ||
462 | .parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
463 | JZ4780_CLK_VPLL, -1 }, | ||
464 | .mux = { CGU_REG_HDMICDR, 30, 2 }, | ||
465 | .div = { CGU_REG_HDMICDR, 0, 8, 29, 28, 26 }, | ||
466 | .gate = { CGU_REG_CLKGR1, 9 }, | ||
467 | }, | ||
468 | |||
469 | [JZ4780_CLK_BCH] = { | ||
470 | "bch", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, | ||
471 | .parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, | ||
472 | JZ4780_CLK_EPLL }, | ||
473 | .mux = { CGU_REG_BCHCDR, 30, 2 }, | ||
474 | .div = { CGU_REG_BCHCDR, 0, 4, 29, 28, 27 }, | ||
475 | .gate = { CGU_REG_CLKGR0, 1 }, | ||
476 | }, | ||
477 | |||
478 | /* Gate-only clocks */ | ||
479 | |||
480 | [JZ4780_CLK_NEMC] = { | ||
481 | "nemc", CGU_CLK_GATE, | ||
482 | .parents = { JZ4780_CLK_AHB2, -1, -1, -1 }, | ||
483 | .gate = { CGU_REG_CLKGR0, 0 }, | ||
484 | }, | ||
485 | |||
486 | [JZ4780_CLK_OTG0] = { | ||
487 | "otg0", CGU_CLK_GATE, | ||
488 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
489 | .gate = { CGU_REG_CLKGR0, 2 }, | ||
490 | }, | ||
491 | |||
492 | [JZ4780_CLK_SSI0] = { | ||
493 | "ssi0", CGU_CLK_GATE, | ||
494 | .parents = { JZ4780_CLK_SSI, -1, -1, -1 }, | ||
495 | .gate = { CGU_REG_CLKGR0, 4 }, | ||
496 | }, | ||
497 | |||
498 | [JZ4780_CLK_SMB0] = { | ||
499 | "smb0", CGU_CLK_GATE, | ||
500 | .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, | ||
501 | .gate = { CGU_REG_CLKGR0, 5 }, | ||
502 | }, | ||
503 | |||
504 | [JZ4780_CLK_SMB1] = { | ||
505 | "smb1", CGU_CLK_GATE, | ||
506 | .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, | ||
507 | .gate = { CGU_REG_CLKGR0, 6 }, | ||
508 | }, | ||
509 | |||
510 | [JZ4780_CLK_SCC] = { | ||
511 | "scc", CGU_CLK_GATE, | ||
512 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
513 | .gate = { CGU_REG_CLKGR0, 7 }, | ||
514 | }, | ||
515 | |||
516 | [JZ4780_CLK_AIC] = { | ||
517 | "aic", CGU_CLK_GATE, | ||
518 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
519 | .gate = { CGU_REG_CLKGR0, 8 }, | ||
520 | }, | ||
521 | |||
522 | [JZ4780_CLK_TSSI0] = { | ||
523 | "tssi0", CGU_CLK_GATE, | ||
524 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
525 | .gate = { CGU_REG_CLKGR0, 9 }, | ||
526 | }, | ||
527 | |||
528 | [JZ4780_CLK_OWI] = { | ||
529 | "owi", CGU_CLK_GATE, | ||
530 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
531 | .gate = { CGU_REG_CLKGR0, 10 }, | ||
532 | }, | ||
533 | |||
534 | [JZ4780_CLK_KBC] = { | ||
535 | "kbc", CGU_CLK_GATE, | ||
536 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
537 | .gate = { CGU_REG_CLKGR0, 13 }, | ||
538 | }, | ||
539 | |||
540 | [JZ4780_CLK_SADC] = { | ||
541 | "sadc", CGU_CLK_GATE, | ||
542 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
543 | .gate = { CGU_REG_CLKGR0, 14 }, | ||
544 | }, | ||
545 | |||
546 | [JZ4780_CLK_UART0] = { | ||
547 | "uart0", CGU_CLK_GATE, | ||
548 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
549 | .gate = { CGU_REG_CLKGR0, 15 }, | ||
550 | }, | ||
551 | |||
552 | [JZ4780_CLK_UART1] = { | ||
553 | "uart1", CGU_CLK_GATE, | ||
554 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
555 | .gate = { CGU_REG_CLKGR0, 16 }, | ||
556 | }, | ||
557 | |||
558 | [JZ4780_CLK_UART2] = { | ||
559 | "uart2", CGU_CLK_GATE, | ||
560 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
561 | .gate = { CGU_REG_CLKGR0, 17 }, | ||
562 | }, | ||
563 | |||
564 | [JZ4780_CLK_UART3] = { | ||
565 | "uart3", CGU_CLK_GATE, | ||
566 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
567 | .gate = { CGU_REG_CLKGR0, 18 }, | ||
568 | }, | ||
569 | |||
570 | [JZ4780_CLK_SSI1] = { | ||
571 | "ssi1", CGU_CLK_GATE, | ||
572 | .parents = { JZ4780_CLK_SSI, -1, -1, -1 }, | ||
573 | .gate = { CGU_REG_CLKGR0, 19 }, | ||
574 | }, | ||
575 | |||
576 | [JZ4780_CLK_SSI2] = { | ||
577 | "ssi2", CGU_CLK_GATE, | ||
578 | .parents = { JZ4780_CLK_SSI, -1, -1, -1 }, | ||
579 | .gate = { CGU_REG_CLKGR0, 20 }, | ||
580 | }, | ||
581 | |||
582 | [JZ4780_CLK_PDMA] = { | ||
583 | "pdma", CGU_CLK_GATE, | ||
584 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
585 | .gate = { CGU_REG_CLKGR0, 21 }, | ||
586 | }, | ||
587 | |||
588 | [JZ4780_CLK_GPS] = { | ||
589 | "gps", CGU_CLK_GATE, | ||
590 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
591 | .gate = { CGU_REG_CLKGR0, 22 }, | ||
592 | }, | ||
593 | |||
594 | [JZ4780_CLK_MAC] = { | ||
595 | "mac", CGU_CLK_GATE, | ||
596 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
597 | .gate = { CGU_REG_CLKGR0, 23 }, | ||
598 | }, | ||
599 | |||
600 | [JZ4780_CLK_SMB2] = { | ||
601 | "smb2", CGU_CLK_GATE, | ||
602 | .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, | ||
603 | .gate = { CGU_REG_CLKGR0, 24 }, | ||
604 | }, | ||
605 | |||
606 | [JZ4780_CLK_CIM] = { | ||
607 | "cim", CGU_CLK_GATE, | ||
608 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
609 | .gate = { CGU_REG_CLKGR0, 26 }, | ||
610 | }, | ||
611 | |||
612 | [JZ4780_CLK_LCD] = { | ||
613 | "lcd", CGU_CLK_GATE, | ||
614 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
615 | .gate = { CGU_REG_CLKGR0, 28 }, | ||
616 | }, | ||
617 | |||
618 | [JZ4780_CLK_TVE] = { | ||
619 | "tve", CGU_CLK_GATE, | ||
620 | .parents = { JZ4780_CLK_LCD, -1, -1, -1 }, | ||
621 | .gate = { CGU_REG_CLKGR0, 27 }, | ||
622 | }, | ||
623 | |||
624 | [JZ4780_CLK_IPU] = { | ||
625 | "ipu", CGU_CLK_GATE, | ||
626 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
627 | .gate = { CGU_REG_CLKGR0, 29 }, | ||
628 | }, | ||
629 | |||
630 | [JZ4780_CLK_DDR0] = { | ||
631 | "ddr0", CGU_CLK_GATE, | ||
632 | .parents = { JZ4780_CLK_DDR, -1, -1, -1 }, | ||
633 | .gate = { CGU_REG_CLKGR0, 30 }, | ||
634 | }, | ||
635 | |||
636 | [JZ4780_CLK_DDR1] = { | ||
637 | "ddr1", CGU_CLK_GATE, | ||
638 | .parents = { JZ4780_CLK_DDR, -1, -1, -1 }, | ||
639 | .gate = { CGU_REG_CLKGR0, 31 }, | ||
640 | }, | ||
641 | |||
642 | [JZ4780_CLK_SMB3] = { | ||
643 | "smb3", CGU_CLK_GATE, | ||
644 | .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, | ||
645 | .gate = { CGU_REG_CLKGR1, 0 }, | ||
646 | }, | ||
647 | |||
648 | [JZ4780_CLK_TSSI1] = { | ||
649 | "tssi1", CGU_CLK_GATE, | ||
650 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
651 | .gate = { CGU_REG_CLKGR1, 1 }, | ||
652 | }, | ||
653 | |||
654 | [JZ4780_CLK_COMPRESS] = { | ||
655 | "compress", CGU_CLK_GATE, | ||
656 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
657 | .gate = { CGU_REG_CLKGR1, 5 }, | ||
658 | }, | ||
659 | |||
660 | [JZ4780_CLK_AIC1] = { | ||
661 | "aic1", CGU_CLK_GATE, | ||
662 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
663 | .gate = { CGU_REG_CLKGR1, 6 }, | ||
664 | }, | ||
665 | |||
666 | [JZ4780_CLK_GPVLC] = { | ||
667 | "gpvlc", CGU_CLK_GATE, | ||
668 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
669 | .gate = { CGU_REG_CLKGR1, 7 }, | ||
670 | }, | ||
671 | |||
672 | [JZ4780_CLK_OTG1] = { | ||
673 | "otg1", CGU_CLK_GATE, | ||
674 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
675 | .gate = { CGU_REG_CLKGR1, 8 }, | ||
676 | }, | ||
677 | |||
678 | [JZ4780_CLK_UART4] = { | ||
679 | "uart4", CGU_CLK_GATE, | ||
680 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
681 | .gate = { CGU_REG_CLKGR1, 10 }, | ||
682 | }, | ||
683 | |||
684 | [JZ4780_CLK_AHBMON] = { | ||
685 | "ahb_mon", CGU_CLK_GATE, | ||
686 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
687 | .gate = { CGU_REG_CLKGR1, 11 }, | ||
688 | }, | ||
689 | |||
690 | [JZ4780_CLK_SMB4] = { | ||
691 | "smb4", CGU_CLK_GATE, | ||
692 | .parents = { JZ4780_CLK_PCLK, -1, -1, -1 }, | ||
693 | .gate = { CGU_REG_CLKGR1, 12 }, | ||
694 | }, | ||
695 | |||
696 | [JZ4780_CLK_DES] = { | ||
697 | "des", CGU_CLK_GATE, | ||
698 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
699 | .gate = { CGU_REG_CLKGR1, 13 }, | ||
700 | }, | ||
701 | |||
702 | [JZ4780_CLK_X2D] = { | ||
703 | "x2d", CGU_CLK_GATE, | ||
704 | .parents = { JZ4780_CLK_EXCLK, -1, -1, -1 }, | ||
705 | .gate = { CGU_REG_CLKGR1, 14 }, | ||
706 | }, | ||
707 | |||
708 | [JZ4780_CLK_CORE1] = { | ||
709 | "core1", CGU_CLK_GATE, | ||
710 | .parents = { JZ4780_CLK_CPU, -1, -1, -1 }, | ||
711 | .gate = { CGU_REG_CLKGR1, 15 }, | ||
712 | }, | ||
713 | |||
714 | }; | ||
715 | |||
716 | static void __init jz4780_cgu_init(struct device_node *np) | ||
717 | { | ||
718 | int retval; | ||
719 | |||
720 | cgu = ingenic_cgu_new(jz4780_cgu_clocks, | ||
721 | ARRAY_SIZE(jz4780_cgu_clocks), np); | ||
722 | if (!cgu) { | ||
723 | pr_err("%s: failed to initialise CGU\n", __func__); | ||
724 | return; | ||
725 | } | ||
726 | |||
727 | retval = ingenic_cgu_register_clocks(cgu); | ||
728 | if (retval) { | ||
729 | pr_err("%s: failed to register CGU Clocks\n", __func__); | ||
730 | return; | ||
731 | } | ||
732 | } | ||
733 | CLK_OF_DECLARE(jz4780_cgu, "ingenic,jz4780-cgu", jz4780_cgu_init); | ||