Warning: That file was not part of the compilation database. It may have many parsing errors.

1/*
2 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 */
10
11#include <linux/clk-provider.h>
12#include <linux/clkdev.h>
13#include <linux/clk/at91_pmc.h>
14#include <linux/of.h>
15#include <linux/mfd/syscon.h>
16#include <linux/regmap.h>
17
18#include "pmc.h"
19
20#define SAM9X5_USB_DIV_SHIFT 8
21#define SAM9X5_USB_MAX_DIV 0xf
22
23#define RM9200_USB_DIV_SHIFT 28
24#define RM9200_USB_DIV_TAB_SIZE 4
25
26struct at91sam9x5_clk_usb {
27 struct clk_hw hw;
28 struct regmap *regmap;
29};
30
31#define to_at91sam9x5_clk_usb(hw) \
32 container_of(hw, struct at91sam9x5_clk_usb, hw)
33
34struct at91rm9200_clk_usb {
35 struct clk_hw hw;
36 struct regmap *regmap;
37 u32 divisors[4];
38};
39
40#define to_at91rm9200_clk_usb(hw) \
41 container_of(hw, struct at91rm9200_clk_usb, hw)
42
43static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
44 unsigned long parent_rate)
45{
46 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
47 unsigned int usbr;
48 u8 usbdiv;
49
50 regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
51 usbdiv = (usbr & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
52
53 return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
54}
55
56static int at91sam9x5_clk_usb_determine_rate(struct clk_hw *hw,
57 struct clk_rate_request *req)
58{
59 struct clk_hw *parent;
60 long best_rate = -EINVAL;
61 unsigned long tmp_rate;
62 int best_diff = -1;
63 int tmp_diff;
64 int i;
65
66 for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
67 int div;
68
69 parent = clk_hw_get_parent_by_index(hw, i);
70 if (!parent)
71 continue;
72
73 for (div = 1; div < SAM9X5_USB_MAX_DIV + 2; div++) {
74 unsigned long tmp_parent_rate;
75
76 tmp_parent_rate = req->rate * div;
77 tmp_parent_rate = clk_hw_round_rate(parent,
78 tmp_parent_rate);
79 tmp_rate = DIV_ROUND_CLOSEST(tmp_parent_rate, div);
80 if (tmp_rate < req->rate)
81 tmp_diff = req->rate - tmp_rate;
82 else
83 tmp_diff = tmp_rate - req->rate;
84
85 if (best_diff < 0 || best_diff > tmp_diff) {
86 best_rate = tmp_rate;
87 best_diff = tmp_diff;
88 req->best_parent_rate = tmp_parent_rate;
89 req->best_parent_hw = parent;
90 }
91
92 if (!best_diff || tmp_rate < req->rate)
93 break;
94 }
95
96 if (!best_diff)
97 break;
98 }
99
100 if (best_rate < 0)
101 return best_rate;
102
103 req->rate = best_rate;
104 return 0;
105}
106
107static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
108{
109 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
110
111 if (index > 1)
112 return -EINVAL;
113
114 regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
115 index ? AT91_PMC_USBS : 0);
116
117 return 0;
118}
119
120static u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw)
121{
122 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
123 unsigned int usbr;
124
125 regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
126
127 return usbr & AT91_PMC_USBS;
128}
129
130static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
131 unsigned long parent_rate)
132{
133 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
134 unsigned long div;
135
136 if (!rate)
137 return -EINVAL;
138
139 div = DIV_ROUND_CLOSEST(parent_rate, rate);
140 if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
141 return -EINVAL;
142
143 regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
144 (div - 1) << SAM9X5_USB_DIV_SHIFT);
145
146 return 0;
147}
148
149static const struct clk_ops at91sam9x5_usb_ops = {
150 .recalc_rate = at91sam9x5_clk_usb_recalc_rate,
151 .determine_rate = at91sam9x5_clk_usb_determine_rate,
152 .get_parent = at91sam9x5_clk_usb_get_parent,
153 .set_parent = at91sam9x5_clk_usb_set_parent,
154 .set_rate = at91sam9x5_clk_usb_set_rate,
155};
156
157static int at91sam9n12_clk_usb_enable(struct clk_hw *hw)
158{
159 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
160
161 regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
162 AT91_PMC_USBS);
163
164 return 0;
165}
166
167static void at91sam9n12_clk_usb_disable(struct clk_hw *hw)
168{
169 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
170
171 regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0);
172}
173
174static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw)
175{
176 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
177 unsigned int usbr;
178
179 regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
180
181 return usbr & AT91_PMC_USBS;
182}
183
184static const struct clk_ops at91sam9n12_usb_ops = {
185 .enable = at91sam9n12_clk_usb_enable,
186 .disable = at91sam9n12_clk_usb_disable,
187 .is_enabled = at91sam9n12_clk_usb_is_enabled,
188 .recalc_rate = at91sam9x5_clk_usb_recalc_rate,
189 .determine_rate = at91sam9x5_clk_usb_determine_rate,
190 .set_rate = at91sam9x5_clk_usb_set_rate,
191};
192
193struct clk_hw * __init
194at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
195 const char **parent_names, u8 num_parents)
196{
197 struct at91sam9x5_clk_usb *usb;
198 struct clk_hw *hw;
199 struct clk_init_data init;
200 int ret;
201
202 usb = kzalloc(sizeof(*usb), GFP_KERNEL);
203 if (!usb)
204 return ERR_PTR(-ENOMEM);
205
206 init.name = name;
207 init.ops = &at91sam9x5_usb_ops;
208 init.parent_names = parent_names;
209 init.num_parents = num_parents;
210 init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
211 CLK_SET_RATE_PARENT;
212
213 usb->hw.init = &init;
214 usb->regmap = regmap;
215
216 hw = &usb->hw;
217 ret = clk_hw_register(NULL, &usb->hw);
218 if (ret) {
219 kfree(usb);
220 hw = ERR_PTR(ret);
221 }
222
223 return hw;
224}
225
226struct clk_hw * __init
227at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
228 const char *parent_name)
229{
230 struct at91sam9x5_clk_usb *usb;
231 struct clk_hw *hw;
232 struct clk_init_data init;
233 int ret;
234
235 usb = kzalloc(sizeof(*usb), GFP_KERNEL);
236 if (!usb)
237 return ERR_PTR(-ENOMEM);
238
239 init.name = name;
240 init.ops = &at91sam9n12_usb_ops;
241 init.parent_names = &parent_name;
242 init.num_parents = 1;
243 init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT;
244
245 usb->hw.init = &init;
246 usb->regmap = regmap;
247
248 hw = &usb->hw;
249 ret = clk_hw_register(NULL, &usb->hw);
250 if (ret) {
251 kfree(usb);
252 hw = ERR_PTR(ret);
253 }
254
255 return hw;
256}
257
258static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
259 unsigned long parent_rate)
260{
261 struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
262 unsigned int pllbr;
263 u8 usbdiv;
264
265 regmap_read(usb->regmap, AT91_CKGR_PLLBR, &pllbr);
266
267 usbdiv = (pllbr & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT;
268 if (usb->divisors[usbdiv])
269 return parent_rate / usb->divisors[usbdiv];
270
271 return 0;
272}
273
274static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
275 unsigned long *parent_rate)
276{
277 struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
278 struct clk_hw *parent = clk_hw_get_parent(hw);
279 unsigned long bestrate = 0;
280 int bestdiff = -1;
281 unsigned long tmprate;
282 int tmpdiff;
283 int i = 0;
284
285 for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
286 unsigned long tmp_parent_rate;
287
288 if (!usb->divisors[i])
289 continue;
290
291 tmp_parent_rate = rate * usb->divisors[i];
292 tmp_parent_rate = clk_hw_round_rate(parent, tmp_parent_rate);
293 tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
294 if (tmprate < rate)
295 tmpdiff = rate - tmprate;
296 else
297 tmpdiff = tmprate - rate;
298
299 if (bestdiff < 0 || bestdiff > tmpdiff) {
300 bestrate = tmprate;
301 bestdiff = tmpdiff;
302 *parent_rate = tmp_parent_rate;
303 }
304
305 if (!bestdiff)
306 break;
307 }
308
309 return bestrate;
310}
311
312static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
313 unsigned long parent_rate)
314{
315 int i;
316 struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
317 unsigned long div;
318
319 if (!rate)
320 return -EINVAL;
321
322 div = DIV_ROUND_CLOSEST(parent_rate, rate);
323
324 for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
325 if (usb->divisors[i] == div) {
326 regmap_update_bits(usb->regmap, AT91_CKGR_PLLBR,
327 AT91_PMC_USBDIV,
328 i << RM9200_USB_DIV_SHIFT);
329
330 return 0;
331 }
332 }
333
334 return -EINVAL;
335}
336
337static const struct clk_ops at91rm9200_usb_ops = {
338 .recalc_rate = at91rm9200_clk_usb_recalc_rate,
339 .round_rate = at91rm9200_clk_usb_round_rate,
340 .set_rate = at91rm9200_clk_usb_set_rate,
341};
342
343struct clk_hw * __init
344at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
345 const char *parent_name, const u32 *divisors)
346{
347 struct at91rm9200_clk_usb *usb;
348 struct clk_hw *hw;
349 struct clk_init_data init;
350 int ret;
351
352 usb = kzalloc(sizeof(*usb), GFP_KERNEL);
353 if (!usb)
354 return ERR_PTR(-ENOMEM);
355
356 init.name = name;
357 init.ops = &at91rm9200_usb_ops;
358 init.parent_names = &parent_name;
359 init.num_parents = 1;
360 init.flags = CLK_SET_RATE_PARENT;
361
362 usb->hw.init = &init;
363 usb->regmap = regmap;
364 memcpy(usb->divisors, divisors, sizeof(usb->divisors));
365
366 hw = &usb->hw;
367 ret = clk_hw_register(NULL, &usb->hw);
368 if (ret) {
369 kfree(usb);
370 hw = ERR_PTR(ret);
371 }
372
373 return hw;
374}
375

Warning: That file was not part of the compilation database. It may have many parsing errors.