linux/drivers/clk/pxa/clk-pxa.c
<<
>>
Prefs
   1/*
   2 * Marvell PXA family clocks
   3 *
   4 * Copyright (C) 2014 Robert Jarzmik
   5 *
   6 * Common clock code for PXA clocks ("CKEN" type clocks + DT)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; version 2 of the License.
  11 *
  12 */
  13#include <linux/clk.h>
  14#include <linux/clk-provider.h>
  15#include <linux/clkdev.h>
  16#include <linux/of.h>
  17
  18#include <dt-bindings/clock/pxa-clock.h>
  19#include "clk-pxa.h"
  20
  21DEFINE_SPINLOCK(lock);
  22
  23static struct clk *pxa_clocks[CLK_MAX];
  24static struct clk_onecell_data onecell_data = {
  25        .clks = pxa_clocks,
  26        .clk_num = CLK_MAX,
  27};
  28
  29struct pxa_clk {
  30        struct clk_hw hw;
  31        struct clk_fixed_factor lp;
  32        struct clk_fixed_factor hp;
  33        struct clk_gate gate;
  34        bool (*is_in_low_power)(void);
  35};
  36
  37#define to_pxa_clk(_hw) container_of(_hw, struct pxa_clk, hw)
  38
  39static unsigned long cken_recalc_rate(struct clk_hw *hw,
  40                                      unsigned long parent_rate)
  41{
  42        struct pxa_clk *pclk = to_pxa_clk(hw);
  43        struct clk_fixed_factor *fix;
  44
  45        if (!pclk->is_in_low_power || pclk->is_in_low_power())
  46                fix = &pclk->lp;
  47        else
  48                fix = &pclk->hp;
  49        __clk_hw_set_clk(&fix->hw, hw);
  50        return clk_fixed_factor_ops.recalc_rate(&fix->hw, parent_rate);
  51}
  52
  53static struct clk_ops cken_rate_ops = {
  54        .recalc_rate = cken_recalc_rate,
  55};
  56
  57static u8 cken_get_parent(struct clk_hw *hw)
  58{
  59        struct pxa_clk *pclk = to_pxa_clk(hw);
  60
  61        if (!pclk->is_in_low_power)
  62                return 0;
  63        return pclk->is_in_low_power() ? 0 : 1;
  64}
  65
  66static struct clk_ops cken_mux_ops = {
  67        .get_parent = cken_get_parent,
  68        .set_parent = dummy_clk_set_parent,
  69};
  70
  71void __init clkdev_pxa_register(int ckid, const char *con_id,
  72                                const char *dev_id, struct clk *clk)
  73{
  74        if (!IS_ERR(clk) && (ckid != CLK_NONE))
  75                pxa_clocks[ckid] = clk;
  76        if (!IS_ERR(clk))
  77                clk_register_clkdev(clk, con_id, dev_id);
  78}
  79
  80int __init clk_pxa_cken_init(const struct desc_clk_cken *clks, int nb_clks)
  81{
  82        int i;
  83        struct pxa_clk *pxa_clk;
  84        struct clk *clk;
  85
  86        for (i = 0; i < nb_clks; i++) {
  87                pxa_clk = kzalloc(sizeof(*pxa_clk), GFP_KERNEL);
  88                pxa_clk->is_in_low_power = clks[i].is_in_low_power;
  89                pxa_clk->lp = clks[i].lp;
  90                pxa_clk->hp = clks[i].hp;
  91                pxa_clk->gate = clks[i].gate;
  92                pxa_clk->gate.lock = &lock;
  93                clk = clk_register_composite(NULL, clks[i].name,
  94                                             clks[i].parent_names, 2,
  95                                             &pxa_clk->hw, &cken_mux_ops,
  96                                             &pxa_clk->hw, &cken_rate_ops,
  97                                             &pxa_clk->gate.hw, &clk_gate_ops,
  98                                             clks[i].flags);
  99                clkdev_pxa_register(clks[i].ckid, clks[i].con_id,
 100                                    clks[i].dev_id, clk);
 101        }
 102        return 0;
 103}
 104
 105void __init clk_pxa_dt_common_init(struct device_node *np)
 106{
 107        of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data);
 108}
 109