Merge remote-tracking branch 'asoc/fix/intel' into asoc-linus
[deliverable/linux.git] / drivers / clk / sunxi / clk-sun6i-ar100.c
1 /*
2 * Copyright (C) 2014 Free Electrons
3 *
4 * License Terms: GNU General Public License v2
5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
6 *
7 * Allwinner A31 AR100 clock driver
8 *
9 */
10
11 #include <linux/clk-provider.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/platform_device.h>
15
16 #define SUN6I_AR100_MAX_PARENTS 4
17 #define SUN6I_AR100_SHIFT_MASK 0x3
18 #define SUN6I_AR100_SHIFT_MAX SUN6I_AR100_SHIFT_MASK
19 #define SUN6I_AR100_SHIFT_SHIFT 4
20 #define SUN6I_AR100_DIV_MASK 0x1f
21 #define SUN6I_AR100_DIV_MAX (SUN6I_AR100_DIV_MASK + 1)
22 #define SUN6I_AR100_DIV_SHIFT 8
23 #define SUN6I_AR100_MUX_MASK 0x3
24 #define SUN6I_AR100_MUX_SHIFT 16
25
26 struct ar100_clk {
27 struct clk_hw hw;
28 void __iomem *reg;
29 };
30
31 static inline struct ar100_clk *to_ar100_clk(struct clk_hw *hw)
32 {
33 return container_of(hw, struct ar100_clk, hw);
34 }
35
36 static unsigned long ar100_recalc_rate(struct clk_hw *hw,
37 unsigned long parent_rate)
38 {
39 struct ar100_clk *clk = to_ar100_clk(hw);
40 u32 val = readl(clk->reg);
41 int shift = (val >> SUN6I_AR100_SHIFT_SHIFT) & SUN6I_AR100_SHIFT_MASK;
42 int div = (val >> SUN6I_AR100_DIV_SHIFT) & SUN6I_AR100_DIV_MASK;
43
44 return (parent_rate >> shift) / (div + 1);
45 }
46
47 static long ar100_determine_rate(struct clk_hw *hw, unsigned long rate,
48 unsigned long min_rate,
49 unsigned long max_rate,
50 unsigned long *best_parent_rate,
51 struct clk_hw **best_parent_clk)
52 {
53 int nparents = __clk_get_num_parents(hw->clk);
54 long best_rate = -EINVAL;
55 int i;
56
57 *best_parent_clk = NULL;
58
59 for (i = 0; i < nparents; i++) {
60 unsigned long parent_rate;
61 unsigned long tmp_rate;
62 struct clk *parent;
63 unsigned long div;
64 int shift;
65
66 parent = clk_get_parent_by_index(hw->clk, i);
67 parent_rate = __clk_get_rate(parent);
68 div = DIV_ROUND_UP(parent_rate, rate);
69
70 /*
71 * The AR100 clk contains 2 divisors:
72 * - one power of 2 divisor
73 * - one regular divisor
74 *
75 * First check if we can safely shift (or divide by a power
76 * of 2) without losing precision on the requested rate.
77 */
78 shift = ffs(div) - 1;
79 if (shift > SUN6I_AR100_SHIFT_MAX)
80 shift = SUN6I_AR100_SHIFT_MAX;
81
82 div >>= shift;
83
84 /*
85 * Then if the divisor is still bigger than what the HW
86 * actually supports, use a bigger shift (or power of 2
87 * divider) value and accept to lose some precision.
88 */
89 while (div > SUN6I_AR100_DIV_MAX) {
90 shift++;
91 div >>= 1;
92 if (shift > SUN6I_AR100_SHIFT_MAX)
93 break;
94 }
95
96 /*
97 * If the shift value (or power of 2 divider) is bigger
98 * than what the HW actually support, skip this parent.
99 */
100 if (shift > SUN6I_AR100_SHIFT_MAX)
101 continue;
102
103 tmp_rate = (parent_rate >> shift) / div;
104 if (!*best_parent_clk || tmp_rate > best_rate) {
105 *best_parent_clk = __clk_get_hw(parent);
106 *best_parent_rate = parent_rate;
107 best_rate = tmp_rate;
108 }
109 }
110
111 return best_rate;
112 }
113
114 static int ar100_set_parent(struct clk_hw *hw, u8 index)
115 {
116 struct ar100_clk *clk = to_ar100_clk(hw);
117 u32 val = readl(clk->reg);
118
119 if (index >= SUN6I_AR100_MAX_PARENTS)
120 return -EINVAL;
121
122 val &= ~(SUN6I_AR100_MUX_MASK << SUN6I_AR100_MUX_SHIFT);
123 val |= (index << SUN6I_AR100_MUX_SHIFT);
124 writel(val, clk->reg);
125
126 return 0;
127 }
128
129 static u8 ar100_get_parent(struct clk_hw *hw)
130 {
131 struct ar100_clk *clk = to_ar100_clk(hw);
132 return (readl(clk->reg) >> SUN6I_AR100_MUX_SHIFT) &
133 SUN6I_AR100_MUX_MASK;
134 }
135
136 static int ar100_set_rate(struct clk_hw *hw, unsigned long rate,
137 unsigned long parent_rate)
138 {
139 unsigned long div = parent_rate / rate;
140 struct ar100_clk *clk = to_ar100_clk(hw);
141 u32 val = readl(clk->reg);
142 int shift;
143
144 if (parent_rate % rate)
145 return -EINVAL;
146
147 shift = ffs(div) - 1;
148 if (shift > SUN6I_AR100_SHIFT_MAX)
149 shift = SUN6I_AR100_SHIFT_MAX;
150
151 div >>= shift;
152
153 if (div > SUN6I_AR100_DIV_MAX)
154 return -EINVAL;
155
156 val &= ~((SUN6I_AR100_SHIFT_MASK << SUN6I_AR100_SHIFT_SHIFT) |
157 (SUN6I_AR100_DIV_MASK << SUN6I_AR100_DIV_SHIFT));
158 val |= (shift << SUN6I_AR100_SHIFT_SHIFT) |
159 (div << SUN6I_AR100_DIV_SHIFT);
160 writel(val, clk->reg);
161
162 return 0;
163 }
164
165 static struct clk_ops ar100_ops = {
166 .recalc_rate = ar100_recalc_rate,
167 .determine_rate = ar100_determine_rate,
168 .set_parent = ar100_set_parent,
169 .get_parent = ar100_get_parent,
170 .set_rate = ar100_set_rate,
171 };
172
173 static int sun6i_a31_ar100_clk_probe(struct platform_device *pdev)
174 {
175 const char *parents[SUN6I_AR100_MAX_PARENTS];
176 struct device_node *np = pdev->dev.of_node;
177 const char *clk_name = np->name;
178 struct clk_init_data init;
179 struct ar100_clk *ar100;
180 struct resource *r;
181 struct clk *clk;
182 int nparents;
183 int i;
184
185 ar100 = devm_kzalloc(&pdev->dev, sizeof(*ar100), GFP_KERNEL);
186 if (!ar100)
187 return -ENOMEM;
188
189 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
190 ar100->reg = devm_ioremap_resource(&pdev->dev, r);
191 if (IS_ERR(ar100->reg))
192 return PTR_ERR(ar100->reg);
193
194 nparents = of_clk_get_parent_count(np);
195 if (nparents > SUN6I_AR100_MAX_PARENTS)
196 nparents = SUN6I_AR100_MAX_PARENTS;
197
198 for (i = 0; i < nparents; i++)
199 parents[i] = of_clk_get_parent_name(np, i);
200
201 of_property_read_string(np, "clock-output-names", &clk_name);
202
203 init.name = clk_name;
204 init.ops = &ar100_ops;
205 init.parent_names = parents;
206 init.num_parents = nparents;
207 init.flags = 0;
208
209 ar100->hw.init = &init;
210
211 clk = clk_register(&pdev->dev, &ar100->hw);
212 if (IS_ERR(clk))
213 return PTR_ERR(clk);
214
215 return of_clk_add_provider(np, of_clk_src_simple_get, clk);
216 }
217
218 static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = {
219 { .compatible = "allwinner,sun6i-a31-ar100-clk" },
220 { /* sentinel */ }
221 };
222
223 static struct platform_driver sun6i_a31_ar100_clk_driver = {
224 .driver = {
225 .name = "sun6i-a31-ar100-clk",
226 .of_match_table = sun6i_a31_ar100_clk_dt_ids,
227 },
228 .probe = sun6i_a31_ar100_clk_probe,
229 };
230 module_platform_driver(sun6i_a31_ar100_clk_driver);
231
232 MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
233 MODULE_DESCRIPTION("Allwinner A31 AR100 clock Driver");
234 MODULE_LICENSE("GPL v2");
This page took 0.035191 seconds and 6 git commands to generate.