1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // |
3 | // OWL pll clock driver |
4 | // |
5 | // Copyright (c) 2014 Actions Semi Inc. |
6 | // Author: David Liu <liuwei@actions-semi.com> |
7 | // |
8 | // Copyright (c) 2018 Linaro Ltd. |
9 | // Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> |
10 | |
11 | #include <linux/clk-provider.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/io.h> |
14 | #include <linux/delay.h> |
15 | |
16 | #include "owl-pll.h" |
17 | |
18 | static u32 owl_pll_calculate_mul(struct owl_pll_hw *pll_hw, unsigned long rate) |
19 | { |
20 | u32 mul; |
21 | |
22 | mul = DIV_ROUND_CLOSEST(rate, pll_hw->bfreq); |
23 | if (mul < pll_hw->min_mul) |
24 | mul = pll_hw->min_mul; |
25 | else if (mul > pll_hw->max_mul) |
26 | mul = pll_hw->max_mul; |
27 | |
28 | return mul & mul_mask(pll_hw); |
29 | } |
30 | |
31 | static unsigned long _get_table_rate(const struct clk_pll_table *table, |
32 | unsigned int val) |
33 | { |
34 | const struct clk_pll_table *clkt; |
35 | |
36 | for (clkt = table; clkt->rate; clkt++) |
37 | if (clkt->val == val) |
38 | return clkt->rate; |
39 | |
40 | return 0; |
41 | } |
42 | |
43 | static const struct clk_pll_table *_get_pll_table( |
44 | const struct clk_pll_table *table, unsigned long rate) |
45 | { |
46 | const struct clk_pll_table *clkt; |
47 | |
48 | for (clkt = table; clkt->rate; clkt++) { |
49 | if (clkt->rate == rate) { |
50 | table = clkt; |
51 | break; |
52 | } else if (clkt->rate < rate) |
53 | table = clkt; |
54 | } |
55 | |
56 | return table; |
57 | } |
58 | |
59 | static long owl_pll_round_rate(struct clk_hw *hw, unsigned long rate, |
60 | unsigned long *parent_rate) |
61 | { |
62 | struct owl_pll *pll = hw_to_owl_pll(hw); |
63 | struct owl_pll_hw *pll_hw = &pll->pll_hw; |
64 | const struct clk_pll_table *clkt; |
65 | u32 mul; |
66 | |
67 | if (pll_hw->table) { |
68 | clkt = _get_pll_table(table: pll_hw->table, rate); |
69 | return clkt->rate; |
70 | } |
71 | |
72 | /* fixed frequency */ |
73 | if (pll_hw->width == 0) |
74 | return pll_hw->bfreq; |
75 | |
76 | mul = owl_pll_calculate_mul(pll_hw, rate); |
77 | |
78 | return pll_hw->bfreq * mul; |
79 | } |
80 | |
81 | static unsigned long owl_pll_recalc_rate(struct clk_hw *hw, |
82 | unsigned long parent_rate) |
83 | { |
84 | struct owl_pll *pll = hw_to_owl_pll(hw); |
85 | struct owl_pll_hw *pll_hw = &pll->pll_hw; |
86 | const struct owl_clk_common *common = &pll->common; |
87 | u32 val; |
88 | |
89 | if (pll_hw->table) { |
90 | regmap_read(map: common->regmap, reg: pll_hw->reg, val: &val); |
91 | |
92 | val = val >> pll_hw->shift; |
93 | val &= mul_mask(pll_hw); |
94 | |
95 | return _get_table_rate(table: pll_hw->table, val); |
96 | } |
97 | |
98 | /* fixed frequency */ |
99 | if (pll_hw->width == 0) |
100 | return pll_hw->bfreq; |
101 | |
102 | regmap_read(map: common->regmap, reg: pll_hw->reg, val: &val); |
103 | |
104 | val = val >> pll_hw->shift; |
105 | val &= mul_mask(pll_hw); |
106 | |
107 | return pll_hw->bfreq * val; |
108 | } |
109 | |
110 | static int owl_pll_is_enabled(struct clk_hw *hw) |
111 | { |
112 | struct owl_pll *pll = hw_to_owl_pll(hw); |
113 | struct owl_pll_hw *pll_hw = &pll->pll_hw; |
114 | const struct owl_clk_common *common = &pll->common; |
115 | u32 reg; |
116 | |
117 | regmap_read(map: common->regmap, reg: pll_hw->reg, val: ®); |
118 | |
119 | return !!(reg & BIT(pll_hw->bit_idx)); |
120 | } |
121 | |
122 | static void owl_pll_set(const struct owl_clk_common *common, |
123 | const struct owl_pll_hw *pll_hw, bool enable) |
124 | { |
125 | u32 reg; |
126 | |
127 | regmap_read(map: common->regmap, reg: pll_hw->reg, val: ®); |
128 | |
129 | if (enable) |
130 | reg |= BIT(pll_hw->bit_idx); |
131 | else |
132 | reg &= ~BIT(pll_hw->bit_idx); |
133 | |
134 | regmap_write(map: common->regmap, reg: pll_hw->reg, val: reg); |
135 | } |
136 | |
137 | static int owl_pll_enable(struct clk_hw *hw) |
138 | { |
139 | struct owl_pll *pll = hw_to_owl_pll(hw); |
140 | const struct owl_clk_common *common = &pll->common; |
141 | |
142 | owl_pll_set(common, pll_hw: &pll->pll_hw, enable: true); |
143 | |
144 | return 0; |
145 | } |
146 | |
147 | static void owl_pll_disable(struct clk_hw *hw) |
148 | { |
149 | struct owl_pll *pll = hw_to_owl_pll(hw); |
150 | const struct owl_clk_common *common = &pll->common; |
151 | |
152 | owl_pll_set(common, pll_hw: &pll->pll_hw, enable: false); |
153 | } |
154 | |
155 | static int owl_pll_set_rate(struct clk_hw *hw, unsigned long rate, |
156 | unsigned long parent_rate) |
157 | { |
158 | struct owl_pll *pll = hw_to_owl_pll(hw); |
159 | struct owl_pll_hw *pll_hw = &pll->pll_hw; |
160 | const struct owl_clk_common *common = &pll->common; |
161 | const struct clk_pll_table *clkt; |
162 | u32 val, reg; |
163 | |
164 | /* fixed frequency */ |
165 | if (pll_hw->width == 0) |
166 | return 0; |
167 | |
168 | if (pll_hw->table) { |
169 | clkt = _get_pll_table(table: pll_hw->table, rate); |
170 | val = clkt->val; |
171 | } else { |
172 | val = owl_pll_calculate_mul(pll_hw, rate); |
173 | } |
174 | |
175 | regmap_read(map: common->regmap, reg: pll_hw->reg, val: ®); |
176 | |
177 | reg &= ~mul_mask(pll_hw); |
178 | reg |= val << pll_hw->shift; |
179 | |
180 | regmap_write(map: common->regmap, reg: pll_hw->reg, val: reg); |
181 | |
182 | udelay(pll_hw->delay); |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | const struct clk_ops owl_pll_ops = { |
188 | .enable = owl_pll_enable, |
189 | .disable = owl_pll_disable, |
190 | .is_enabled = owl_pll_is_enabled, |
191 | .round_rate = owl_pll_round_rate, |
192 | .recalc_rate = owl_pll_recalc_rate, |
193 | .set_rate = owl_pll_set_rate, |
194 | }; |
195 | |