Merge tag 'mvebu-dt-4.2-3' of git://git.infradead.org/linux-mvebu into next/late
[firefly-linux-kernel-4.4.55.git] / drivers / clk / zte / clk-pll.c
1 /*
2  * Copyright 2014 Linaro Ltd.
3  * Copyright (C) 2014 ZTE Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9
10 #include <linux/clk-provider.h>
11 #include <linux/err.h>
12 #include <linux/io.h>
13 #include <linux/iopoll.h>
14 #include <linux/slab.h>
15 #include <linux/spinlock.h>
16
17 #include "clk.h"
18
19 #define to_clk_zx_pll(_hw) container_of(_hw, struct clk_zx_pll, hw)
20
21 #define CFG0_CFG1_OFFSET 4
22 #define LOCK_FLAG BIT(30)
23 #define POWER_DOWN BIT(31)
24
25 static int rate_to_idx(struct clk_zx_pll *zx_pll, unsigned long rate)
26 {
27         const struct zx_pll_config *config = zx_pll->lookup_table;
28         int i;
29
30         for (i = 0; i < zx_pll->count; i++) {
31                 if (config[i].rate > rate)
32                         return i > 0 ? i - 1 : 0;
33
34                 if (config[i].rate == rate)
35                         return i;
36         }
37
38         return i - 1;
39 }
40
41 static int hw_to_idx(struct clk_zx_pll *zx_pll)
42 {
43         const struct zx_pll_config *config = zx_pll->lookup_table;
44         u32 hw_cfg0, hw_cfg1;
45         int i;
46
47         hw_cfg0 = readl_relaxed(zx_pll->reg_base);
48         hw_cfg1 = readl_relaxed(zx_pll->reg_base + CFG0_CFG1_OFFSET);
49
50         /* For matching the value in lookup table */
51         hw_cfg0 &= ~LOCK_FLAG;
52         hw_cfg0 |= POWER_DOWN;
53
54         for (i = 0; i < zx_pll->count; i++) {
55                 if (hw_cfg0 == config[i].cfg0 && hw_cfg1 == config[i].cfg1)
56                         return i;
57         }
58
59         return -EINVAL;
60 }
61
62 static unsigned long zx_pll_recalc_rate(struct clk_hw *hw,
63                                         unsigned long parent_rate)
64 {
65         struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
66         int idx;
67
68         idx = hw_to_idx(zx_pll);
69         if (unlikely(idx == -EINVAL))
70                 return 0;
71
72         return zx_pll->lookup_table[idx].rate;
73 }
74
75 static long zx_pll_round_rate(struct clk_hw *hw, unsigned long rate,
76                               unsigned long *prate)
77 {
78         struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
79         int idx;
80
81         idx = rate_to_idx(zx_pll, rate);
82
83         return zx_pll->lookup_table[idx].rate;
84 }
85
86 static int zx_pll_set_rate(struct clk_hw *hw, unsigned long rate,
87                            unsigned long parent_rate)
88 {
89         /* Assume current cpu is not running on current PLL */
90         struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
91         const struct zx_pll_config *config;
92         int idx;
93
94         idx = rate_to_idx(zx_pll, rate);
95         config = &zx_pll->lookup_table[idx];
96
97         writel_relaxed(config->cfg0, zx_pll->reg_base);
98         writel_relaxed(config->cfg1, zx_pll->reg_base + CFG0_CFG1_OFFSET);
99
100         return 0;
101 }
102
103 static int zx_pll_enable(struct clk_hw *hw)
104 {
105         struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
106         u32 reg;
107
108         reg = readl_relaxed(zx_pll->reg_base);
109         writel_relaxed(reg & ~POWER_DOWN, zx_pll->reg_base);
110
111         return readl_relaxed_poll_timeout(zx_pll->reg_base, reg,
112                                           reg & LOCK_FLAG, 0, 100);
113 }
114
115 static void zx_pll_disable(struct clk_hw *hw)
116 {
117         struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
118         u32 reg;
119
120         reg = readl_relaxed(zx_pll->reg_base);
121         writel_relaxed(reg | POWER_DOWN, zx_pll->reg_base);
122 }
123
124 static int zx_pll_is_enabled(struct clk_hw *hw)
125 {
126         struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
127         u32 reg;
128
129         reg = readl_relaxed(zx_pll->reg_base);
130
131         return !(reg & POWER_DOWN);
132 }
133
134 static const struct clk_ops zx_pll_ops = {
135         .recalc_rate = zx_pll_recalc_rate,
136         .round_rate = zx_pll_round_rate,
137         .set_rate = zx_pll_set_rate,
138         .enable = zx_pll_enable,
139         .disable = zx_pll_disable,
140         .is_enabled = zx_pll_is_enabled,
141 };
142
143 struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
144         unsigned long flags, void __iomem *reg_base,
145         const struct zx_pll_config *lookup_table, int count, spinlock_t *lock)
146 {
147         struct clk_zx_pll *zx_pll;
148         struct clk *clk;
149         struct clk_init_data init;
150
151         zx_pll = kzalloc(sizeof(*zx_pll), GFP_KERNEL);
152         if (!zx_pll)
153                 return ERR_PTR(-ENOMEM);
154
155         init.name = name;
156         init.ops = &zx_pll_ops;
157         init.flags = flags;
158         init.parent_names = parent_name ? &parent_name : NULL;
159         init.num_parents = parent_name ? 1 : 0;
160
161         zx_pll->reg_base = reg_base;
162         zx_pll->lookup_table = lookup_table;
163         zx_pll->count = count;
164         zx_pll->lock = lock;
165         zx_pll->hw.init = &init;
166
167         clk = clk_register(NULL, &zx_pll->hw);
168         if (IS_ERR(clk))
169                 kfree(zx_pll);
170
171         return clk;
172 }