diff options
Diffstat (limited to 'drivers/clk/sunxi/clk-sunxi.c')
-rw-r--r-- | drivers/clk/sunxi/clk-sunxi.c | 262 |
1 files changed, 222 insertions, 40 deletions
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 1818f404538d..379324eb5486 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c | |||
@@ -20,11 +20,221 @@ | |||
20 | #include <linux/of_address.h> | 20 | #include <linux/of_address.h> |
21 | #include <linux/reset-controller.h> | 21 | #include <linux/reset-controller.h> |
22 | #include <linux/spinlock.h> | 22 | #include <linux/spinlock.h> |
23 | #include <linux/log2.h> | ||
23 | 24 | ||
24 | #include "clk-factors.h" | 25 | #include "clk-factors.h" |
25 | 26 | ||
26 | static DEFINE_SPINLOCK(clk_lock); | 27 | static DEFINE_SPINLOCK(clk_lock); |
27 | 28 | ||
29 | /** | ||
30 | * sun6i_a31_ahb1_clk_setup() - Setup function for a31 ahb1 composite clk | ||
31 | */ | ||
32 | |||
33 | #define SUN6I_AHB1_MAX_PARENTS 4 | ||
34 | #define SUN6I_AHB1_MUX_PARENT_PLL6 3 | ||
35 | #define SUN6I_AHB1_MUX_SHIFT 12 | ||
36 | /* un-shifted mask is what mux_clk expects */ | ||
37 | #define SUN6I_AHB1_MUX_MASK 0x3 | ||
38 | #define SUN6I_AHB1_MUX_GET_PARENT(reg) ((reg >> SUN6I_AHB1_MUX_SHIFT) & \ | ||
39 | SUN6I_AHB1_MUX_MASK) | ||
40 | |||
41 | #define SUN6I_AHB1_DIV_SHIFT 4 | ||
42 | #define SUN6I_AHB1_DIV_MASK (0x3 << SUN6I_AHB1_DIV_SHIFT) | ||
43 | #define SUN6I_AHB1_DIV_GET(reg) ((reg & SUN6I_AHB1_DIV_MASK) >> \ | ||
44 | SUN6I_AHB1_DIV_SHIFT) | ||
45 | #define SUN6I_AHB1_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_DIV_MASK) | \ | ||
46 | (div << SUN6I_AHB1_DIV_SHIFT)) | ||
47 | #define SUN6I_AHB1_PLL6_DIV_SHIFT 6 | ||
48 | #define SUN6I_AHB1_PLL6_DIV_MASK (0x3 << SUN6I_AHB1_PLL6_DIV_SHIFT) | ||
49 | #define SUN6I_AHB1_PLL6_DIV_GET(reg) ((reg & SUN6I_AHB1_PLL6_DIV_MASK) >> \ | ||
50 | SUN6I_AHB1_PLL6_DIV_SHIFT) | ||
51 | #define SUN6I_AHB1_PLL6_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_PLL6_DIV_MASK) | \ | ||
52 | (div << SUN6I_AHB1_PLL6_DIV_SHIFT)) | ||
53 | |||
54 | struct sun6i_ahb1_clk { | ||
55 | struct clk_hw hw; | ||
56 | void __iomem *reg; | ||
57 | }; | ||
58 | |||
59 | #define to_sun6i_ahb1_clk(_hw) container_of(_hw, struct sun6i_ahb1_clk, hw) | ||
60 | |||
61 | static unsigned long sun6i_ahb1_clk_recalc_rate(struct clk_hw *hw, | ||
62 | unsigned long parent_rate) | ||
63 | { | ||
64 | struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw); | ||
65 | unsigned long rate; | ||
66 | u32 reg; | ||
67 | |||
68 | /* Fetch the register value */ | ||
69 | reg = readl(ahb1->reg); | ||
70 | |||
71 | /* apply pre-divider first if parent is pll6 */ | ||
72 | if (SUN6I_AHB1_MUX_GET_PARENT(reg) == SUN6I_AHB1_MUX_PARENT_PLL6) | ||
73 | parent_rate /= SUN6I_AHB1_PLL6_DIV_GET(reg) + 1; | ||
74 | |||
75 | /* clk divider */ | ||
76 | rate = parent_rate >> SUN6I_AHB1_DIV_GET(reg); | ||
77 | |||
78 | return rate; | ||
79 | } | ||
80 | |||
81 | static long sun6i_ahb1_clk_round(unsigned long rate, u8 *divp, u8 *pre_divp, | ||
82 | u8 parent, unsigned long parent_rate) | ||
83 | { | ||
84 | u8 div, calcp, calcm = 1; | ||
85 | |||
86 | /* | ||
87 | * clock can only divide, so we will never be able to achieve | ||
88 | * frequencies higher than the parent frequency | ||
89 | */ | ||
90 | if (parent_rate && rate > parent_rate) | ||
91 | rate = parent_rate; | ||
92 | |||
93 | div = DIV_ROUND_UP(parent_rate, rate); | ||
94 | |||
95 | /* calculate pre-divider if parent is pll6 */ | ||
96 | if (parent == SUN6I_AHB1_MUX_PARENT_PLL6) { | ||
97 | if (div < 4) | ||
98 | calcp = 0; | ||
99 | else if (div / 2 < 4) | ||
100 | calcp = 1; | ||
101 | else if (div / 4 < 4) | ||
102 | calcp = 2; | ||
103 | else | ||
104 | calcp = 3; | ||
105 | |||
106 | calcm = DIV_ROUND_UP(div, 1 << calcp); | ||
107 | } else { | ||
108 | calcp = __roundup_pow_of_two(div); | ||
109 | calcp = calcp > 3 ? 3 : calcp; | ||
110 | } | ||
111 | |||
112 | /* we were asked to pass back divider values */ | ||
113 | if (divp) { | ||
114 | *divp = calcp; | ||
115 | *pre_divp = calcm - 1; | ||
116 | } | ||
117 | |||
118 | return (parent_rate / calcm) >> calcp; | ||
119 | } | ||
120 | |||
121 | static long sun6i_ahb1_clk_determine_rate(struct clk_hw *hw, unsigned long rate, | ||
122 | unsigned long min_rate, | ||
123 | unsigned long max_rate, | ||
124 | unsigned long *best_parent_rate, | ||
125 | struct clk_hw **best_parent_clk) | ||
126 | { | ||
127 | struct clk *clk = hw->clk, *parent, *best_parent = NULL; | ||
128 | int i, num_parents; | ||
129 | unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0; | ||
130 | |||
131 | /* find the parent that can help provide the fastest rate <= rate */ | ||
132 | num_parents = __clk_get_num_parents(clk); | ||
133 | for (i = 0; i < num_parents; i++) { | ||
134 | parent = clk_get_parent_by_index(clk, i); | ||
135 | if (!parent) | ||
136 | continue; | ||
137 | if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT) | ||
138 | parent_rate = __clk_round_rate(parent, rate); | ||
139 | else | ||
140 | parent_rate = __clk_get_rate(parent); | ||
141 | |||
142 | child_rate = sun6i_ahb1_clk_round(rate, NULL, NULL, i, | ||
143 | parent_rate); | ||
144 | |||
145 | if (child_rate <= rate && child_rate > best_child_rate) { | ||
146 | best_parent = parent; | ||
147 | best = parent_rate; | ||
148 | best_child_rate = child_rate; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | if (best_parent) | ||
153 | *best_parent_clk = __clk_get_hw(best_parent); | ||
154 | *best_parent_rate = best; | ||
155 | |||
156 | return best_child_rate; | ||
157 | } | ||
158 | |||
159 | static int sun6i_ahb1_clk_set_rate(struct clk_hw *hw, unsigned long rate, | ||
160 | unsigned long parent_rate) | ||
161 | { | ||
162 | struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw); | ||
163 | unsigned long flags; | ||
164 | u8 div, pre_div, parent; | ||
165 | u32 reg; | ||
166 | |||
167 | spin_lock_irqsave(&clk_lock, flags); | ||
168 | |||
169 | reg = readl(ahb1->reg); | ||
170 | |||
171 | /* need to know which parent is used to apply pre-divider */ | ||
172 | parent = SUN6I_AHB1_MUX_GET_PARENT(reg); | ||
173 | sun6i_ahb1_clk_round(rate, &div, &pre_div, parent, parent_rate); | ||
174 | |||
175 | reg = SUN6I_AHB1_DIV_SET(reg, div); | ||
176 | reg = SUN6I_AHB1_PLL6_DIV_SET(reg, pre_div); | ||
177 | writel(reg, ahb1->reg); | ||
178 | |||
179 | spin_unlock_irqrestore(&clk_lock, flags); | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static const struct clk_ops sun6i_ahb1_clk_ops = { | ||
185 | .determine_rate = sun6i_ahb1_clk_determine_rate, | ||
186 | .recalc_rate = sun6i_ahb1_clk_recalc_rate, | ||
187 | .set_rate = sun6i_ahb1_clk_set_rate, | ||
188 | }; | ||
189 | |||
190 | static void __init sun6i_ahb1_clk_setup(struct device_node *node) | ||
191 | { | ||
192 | struct clk *clk; | ||
193 | struct sun6i_ahb1_clk *ahb1; | ||
194 | struct clk_mux *mux; | ||
195 | const char *clk_name = node->name; | ||
196 | const char *parents[SUN6I_AHB1_MAX_PARENTS]; | ||
197 | void __iomem *reg; | ||
198 | int i = 0; | ||
199 | |||
200 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | ||
201 | |||
202 | /* we have a mux, we will have >1 parents */ | ||
203 | while (i < SUN6I_AHB1_MAX_PARENTS && | ||
204 | (parents[i] = of_clk_get_parent_name(node, i)) != NULL) | ||
205 | i++; | ||
206 | |||
207 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
208 | |||
209 | ahb1 = kzalloc(sizeof(struct sun6i_ahb1_clk), GFP_KERNEL); | ||
210 | if (!ahb1) | ||
211 | return; | ||
212 | |||
213 | mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); | ||
214 | if (!mux) { | ||
215 | kfree(ahb1); | ||
216 | return; | ||
217 | } | ||
218 | |||
219 | /* set up clock properties */ | ||
220 | mux->reg = reg; | ||
221 | mux->shift = SUN6I_AHB1_MUX_SHIFT; | ||
222 | mux->mask = SUN6I_AHB1_MUX_MASK; | ||
223 | mux->lock = &clk_lock; | ||
224 | ahb1->reg = reg; | ||
225 | |||
226 | clk = clk_register_composite(NULL, clk_name, parents, i, | ||
227 | &mux->hw, &clk_mux_ops, | ||
228 | &ahb1->hw, &sun6i_ahb1_clk_ops, | ||
229 | NULL, NULL, 0); | ||
230 | |||
231 | if (!IS_ERR(clk)) { | ||
232 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
233 | clk_register_clkdev(clk, clk_name, NULL); | ||
234 | } | ||
235 | } | ||
236 | CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk", sun6i_ahb1_clk_setup); | ||
237 | |||
28 | /* Maximum number of parents our clocks have */ | 238 | /* Maximum number of parents our clocks have */ |
29 | #define SUNXI_MAX_PARENTS 5 | 239 | #define SUNXI_MAX_PARENTS 5 |
30 | 240 | ||
@@ -355,43 +565,6 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, | |||
355 | } | 565 | } |
356 | 566 | ||
357 | /** | 567 | /** |
358 | * clk_sunxi_mmc_phase_control() - configures MMC clock phase control | ||
359 | */ | ||
360 | |||
361 | void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output) | ||
362 | { | ||
363 | #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) | ||
364 | #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) | ||
365 | |||
366 | struct clk_hw *hw = __clk_get_hw(clk); | ||
367 | struct clk_composite *composite = to_clk_composite(hw); | ||
368 | struct clk_hw *rate_hw = composite->rate_hw; | ||
369 | struct clk_factors *factors = to_clk_factors(rate_hw); | ||
370 | unsigned long flags = 0; | ||
371 | u32 reg; | ||
372 | |||
373 | if (factors->lock) | ||
374 | spin_lock_irqsave(factors->lock, flags); | ||
375 | |||
376 | reg = readl(factors->reg); | ||
377 | |||
378 | /* set sample clock phase control */ | ||
379 | reg &= ~(0x7 << 20); | ||
380 | reg |= ((sample & 0x7) << 20); | ||
381 | |||
382 | /* set output clock phase control */ | ||
383 | reg &= ~(0x7 << 8); | ||
384 | reg |= ((output & 0x7) << 8); | ||
385 | |||
386 | writel(reg, factors->reg); | ||
387 | |||
388 | if (factors->lock) | ||
389 | spin_unlock_irqrestore(factors->lock, flags); | ||
390 | } | ||
391 | EXPORT_SYMBOL(clk_sunxi_mmc_phase_control); | ||
392 | |||
393 | |||
394 | /** | ||
395 | * sunxi_factors_clk_setup() - Setup function for factor clocks | 568 | * sunxi_factors_clk_setup() - Setup function for factor clocks |
396 | */ | 569 | */ |
397 | 570 | ||
@@ -413,6 +586,7 @@ static struct clk_factors_config sun6i_a31_pll1_config = { | |||
413 | .kwidth = 2, | 586 | .kwidth = 2, |
414 | .mshift = 0, | 587 | .mshift = 0, |
415 | .mwidth = 2, | 588 | .mwidth = 2, |
589 | .n_start = 1, | ||
416 | }; | 590 | }; |
417 | 591 | ||
418 | static struct clk_factors_config sun8i_a23_pll1_config = { | 592 | static struct clk_factors_config sun8i_a23_pll1_config = { |
@@ -520,7 +694,16 @@ static const struct factors_data sun7i_a20_out_data __initconst = { | |||
520 | static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, | 694 | static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, |
521 | const struct factors_data *data) | 695 | const struct factors_data *data) |
522 | { | 696 | { |
523 | return sunxi_factors_register(node, data, &clk_lock); | 697 | void __iomem *reg; |
698 | |||
699 | reg = of_iomap(node, 0); | ||
700 | if (!reg) { | ||
701 | pr_err("Could not get registers for factors-clk: %s\n", | ||
702 | node->name); | ||
703 | return NULL; | ||
704 | } | ||
705 | |||
706 | return sunxi_factors_register(node, data, &clk_lock, reg); | ||
524 | } | 707 | } |
525 | 708 | ||
526 | 709 | ||
@@ -561,7 +744,7 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, | |||
561 | of_property_read_string(node, "clock-output-names", &clk_name); | 744 | of_property_read_string(node, "clock-output-names", &clk_name); |
562 | 745 | ||
563 | clk = clk_register_mux(NULL, clk_name, parents, i, | 746 | clk = clk_register_mux(NULL, clk_name, parents, i, |
564 | CLK_SET_RATE_NO_REPARENT, reg, | 747 | CLK_SET_RATE_PARENT, reg, |
565 | data->shift, SUNXI_MUX_GATE_WIDTH, | 748 | data->shift, SUNXI_MUX_GATE_WIDTH, |
566 | 0, &clk_lock); | 749 | 0, &clk_lock); |
567 | 750 | ||
@@ -1217,7 +1400,6 @@ CLK_OF_DECLARE(sun7i_a20_clk_init, "allwinner,sun7i-a20", sun5i_init_clocks); | |||
1217 | 1400 | ||
1218 | static const char *sun6i_critical_clocks[] __initdata = { | 1401 | static const char *sun6i_critical_clocks[] __initdata = { |
1219 | "cpu", | 1402 | "cpu", |
1220 | "ahb1_sdram", | ||
1221 | }; | 1403 | }; |
1222 | 1404 | ||
1223 | static void __init sun6i_init_clocks(struct device_node *node) | 1405 | static void __init sun6i_init_clocks(struct device_node *node) |