diff options
Diffstat (limited to 'drivers/clk/ti')
-rw-r--r-- | drivers/clk/ti/apll.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index b986f61f5a77..5428c9c547cd 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c | |||
@@ -221,3 +221,184 @@ cleanup: | |||
221 | kfree(init); | 221 | kfree(init); |
222 | } | 222 | } |
223 | CLK_OF_DECLARE(dra7_apll_clock, "ti,dra7-apll-clock", of_dra7_apll_setup); | 223 | CLK_OF_DECLARE(dra7_apll_clock, "ti,dra7-apll-clock", of_dra7_apll_setup); |
224 | |||
225 | #define OMAP2_EN_APLL_LOCKED 0x3 | ||
226 | #define OMAP2_EN_APLL_STOPPED 0x0 | ||
227 | |||
228 | static int omap2_apll_is_enabled(struct clk_hw *hw) | ||
229 | { | ||
230 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
231 | struct dpll_data *ad = clk->dpll_data; | ||
232 | u32 v; | ||
233 | |||
234 | v = ti_clk_ll_ops->clk_readl(ad->control_reg); | ||
235 | v &= ad->enable_mask; | ||
236 | |||
237 | v >>= __ffs(ad->enable_mask); | ||
238 | |||
239 | return v == OMAP2_EN_APLL_LOCKED ? 1 : 0; | ||
240 | } | ||
241 | |||
242 | static unsigned long omap2_apll_recalc(struct clk_hw *hw, | ||
243 | unsigned long parent_rate) | ||
244 | { | ||
245 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
246 | |||
247 | if (omap2_apll_is_enabled(hw)) | ||
248 | return clk->fixed_rate; | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int omap2_apll_enable(struct clk_hw *hw) | ||
254 | { | ||
255 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
256 | struct dpll_data *ad = clk->dpll_data; | ||
257 | u32 v; | ||
258 | int i = 0; | ||
259 | |||
260 | v = ti_clk_ll_ops->clk_readl(ad->control_reg); | ||
261 | v &= ~ad->enable_mask; | ||
262 | v |= OMAP2_EN_APLL_LOCKED << __ffs(ad->enable_mask); | ||
263 | ti_clk_ll_ops->clk_writel(v, ad->control_reg); | ||
264 | |||
265 | while (1) { | ||
266 | v = ti_clk_ll_ops->clk_readl(ad->idlest_reg); | ||
267 | if (v & ad->idlest_mask) | ||
268 | break; | ||
269 | if (i > MAX_APLL_WAIT_TRIES) | ||
270 | break; | ||
271 | i++; | ||
272 | udelay(1); | ||
273 | } | ||
274 | |||
275 | if (i == MAX_APLL_WAIT_TRIES) { | ||
276 | pr_warn("%s failed to transition to locked\n", | ||
277 | __clk_get_name(clk->hw.clk)); | ||
278 | return -EBUSY; | ||
279 | } | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static void omap2_apll_disable(struct clk_hw *hw) | ||
285 | { | ||
286 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
287 | struct dpll_data *ad = clk->dpll_data; | ||
288 | u32 v; | ||
289 | |||
290 | v = ti_clk_ll_ops->clk_readl(ad->control_reg); | ||
291 | v &= ~ad->enable_mask; | ||
292 | v |= OMAP2_EN_APLL_STOPPED << __ffs(ad->enable_mask); | ||
293 | ti_clk_ll_ops->clk_writel(v, ad->control_reg); | ||
294 | } | ||
295 | |||
296 | static struct clk_ops omap2_apll_ops = { | ||
297 | .enable = &omap2_apll_enable, | ||
298 | .disable = &omap2_apll_disable, | ||
299 | .is_enabled = &omap2_apll_is_enabled, | ||
300 | .recalc_rate = &omap2_apll_recalc, | ||
301 | }; | ||
302 | |||
303 | static void omap2_apll_set_autoidle(struct clk_hw_omap *clk, u32 val) | ||
304 | { | ||
305 | struct dpll_data *ad = clk->dpll_data; | ||
306 | u32 v; | ||
307 | |||
308 | v = ti_clk_ll_ops->clk_readl(ad->autoidle_reg); | ||
309 | v &= ~ad->autoidle_mask; | ||
310 | v |= val << __ffs(ad->autoidle_mask); | ||
311 | ti_clk_ll_ops->clk_writel(v, ad->control_reg); | ||
312 | } | ||
313 | |||
314 | #define OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP 0x3 | ||
315 | #define OMAP2_APLL_AUTOIDLE_DISABLE 0x0 | ||
316 | |||
317 | static void omap2_apll_allow_idle(struct clk_hw_omap *clk) | ||
318 | { | ||
319 | omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP); | ||
320 | } | ||
321 | |||
322 | static void omap2_apll_deny_idle(struct clk_hw_omap *clk) | ||
323 | { | ||
324 | omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_DISABLE); | ||
325 | } | ||
326 | |||
327 | static struct clk_hw_omap_ops omap2_apll_hwops = { | ||
328 | .allow_idle = &omap2_apll_allow_idle, | ||
329 | .deny_idle = &omap2_apll_deny_idle, | ||
330 | }; | ||
331 | |||
332 | static void __init of_omap2_apll_setup(struct device_node *node) | ||
333 | { | ||
334 | struct dpll_data *ad = NULL; | ||
335 | struct clk_hw_omap *clk_hw = NULL; | ||
336 | struct clk_init_data *init = NULL; | ||
337 | struct clk *clk; | ||
338 | const char *parent_name; | ||
339 | u32 val; | ||
340 | |||
341 | ad = kzalloc(sizeof(*clk_hw), GFP_KERNEL); | ||
342 | clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); | ||
343 | init = kzalloc(sizeof(*init), GFP_KERNEL); | ||
344 | |||
345 | if (!ad || !clk_hw || !init) | ||
346 | goto cleanup; | ||
347 | |||
348 | clk_hw->dpll_data = ad; | ||
349 | clk_hw->hw.init = init; | ||
350 | init->ops = &omap2_apll_ops; | ||
351 | init->name = node->name; | ||
352 | clk_hw->ops = &omap2_apll_hwops; | ||
353 | |||
354 | init->num_parents = of_clk_get_parent_count(node); | ||
355 | if (init->num_parents != 1) { | ||
356 | pr_err("%s must have one parent\n", node->name); | ||
357 | goto cleanup; | ||
358 | } | ||
359 | |||
360 | parent_name = of_clk_get_parent_name(node, 0); | ||
361 | init->parent_names = &parent_name; | ||
362 | |||
363 | if (of_property_read_u32(node, "ti,clock-frequency", &val)) { | ||
364 | pr_err("%s missing clock-frequency\n", node->name); | ||
365 | goto cleanup; | ||
366 | } | ||
367 | clk_hw->fixed_rate = val; | ||
368 | |||
369 | if (of_property_read_u32(node, "ti,bit-shift", &val)) { | ||
370 | pr_err("%s missing bit-shift\n", node->name); | ||
371 | goto cleanup; | ||
372 | } | ||
373 | |||
374 | clk_hw->enable_bit = val; | ||
375 | ad->enable_mask = 0x3 << val; | ||
376 | ad->autoidle_mask = 0x3 << val; | ||
377 | |||
378 | if (of_property_read_u32(node, "ti,idlest-shift", &val)) { | ||
379 | pr_err("%s missing idlest-shift\n", node->name); | ||
380 | goto cleanup; | ||
381 | } | ||
382 | |||
383 | ad->idlest_mask = 1 << val; | ||
384 | |||
385 | ad->control_reg = ti_clk_get_reg_addr(node, 0); | ||
386 | ad->autoidle_reg = ti_clk_get_reg_addr(node, 1); | ||
387 | ad->idlest_reg = ti_clk_get_reg_addr(node, 2); | ||
388 | |||
389 | if (!ad->control_reg || !ad->autoidle_reg || !ad->idlest_reg) | ||
390 | goto cleanup; | ||
391 | |||
392 | clk = clk_register(NULL, &clk_hw->hw); | ||
393 | if (!IS_ERR(clk)) { | ||
394 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
395 | kfree(init); | ||
396 | return; | ||
397 | } | ||
398 | cleanup: | ||
399 | kfree(ad); | ||
400 | kfree(clk_hw); | ||
401 | kfree(init); | ||
402 | } | ||
403 | CLK_OF_DECLARE(omap2_apll_clock, "ti,omap2-apll-clock", | ||
404 | of_omap2_apll_setup); | ||