dm writecache: fix incorrect flush sequence when doing SSD mode commit
[linux/fpc-iii.git] / drivers / clk / at91 / clk-peripheral.c
blobc2ab4860a2bfdd56fd8fa48f7af6fbb3a0cbf589
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4 */
6 #include <linux/bitops.h>
7 #include <linux/clk-provider.h>
8 #include <linux/clkdev.h>
9 #include <linux/clk/at91_pmc.h>
10 #include <linux/of.h>
11 #include <linux/mfd/syscon.h>
12 #include <linux/regmap.h>
14 #include "pmc.h"
16 DEFINE_SPINLOCK(pmc_pcr_lock);
18 #define PERIPHERAL_ID_MIN 2
19 #define PERIPHERAL_ID_MAX 31
20 #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
22 #define PERIPHERAL_MAX_SHIFT 3
24 struct clk_peripheral {
25 struct clk_hw hw;
26 struct regmap *regmap;
27 u32 id;
30 #define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
32 struct clk_sam9x5_peripheral {
33 struct clk_hw hw;
34 struct regmap *regmap;
35 struct clk_range range;
36 spinlock_t *lock;
37 u32 id;
38 u32 div;
39 const struct clk_pcr_layout *layout;
40 bool auto_div;
43 #define to_clk_sam9x5_peripheral(hw) \
44 container_of(hw, struct clk_sam9x5_peripheral, hw)
46 static int clk_peripheral_enable(struct clk_hw *hw)
48 struct clk_peripheral *periph = to_clk_peripheral(hw);
49 int offset = AT91_PMC_PCER;
50 u32 id = periph->id;
52 if (id < PERIPHERAL_ID_MIN)
53 return 0;
54 if (id > PERIPHERAL_ID_MAX)
55 offset = AT91_PMC_PCER1;
56 regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
58 return 0;
61 static void clk_peripheral_disable(struct clk_hw *hw)
63 struct clk_peripheral *periph = to_clk_peripheral(hw);
64 int offset = AT91_PMC_PCDR;
65 u32 id = periph->id;
67 if (id < PERIPHERAL_ID_MIN)
68 return;
69 if (id > PERIPHERAL_ID_MAX)
70 offset = AT91_PMC_PCDR1;
71 regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
74 static int clk_peripheral_is_enabled(struct clk_hw *hw)
76 struct clk_peripheral *periph = to_clk_peripheral(hw);
77 int offset = AT91_PMC_PCSR;
78 unsigned int status;
79 u32 id = periph->id;
81 if (id < PERIPHERAL_ID_MIN)
82 return 1;
83 if (id > PERIPHERAL_ID_MAX)
84 offset = AT91_PMC_PCSR1;
85 regmap_read(periph->regmap, offset, &status);
87 return status & PERIPHERAL_MASK(id) ? 1 : 0;
90 static const struct clk_ops peripheral_ops = {
91 .enable = clk_peripheral_enable,
92 .disable = clk_peripheral_disable,
93 .is_enabled = clk_peripheral_is_enabled,
96 struct clk_hw * __init
97 at91_clk_register_peripheral(struct regmap *regmap, const char *name,
98 const char *parent_name, u32 id)
100 struct clk_peripheral *periph;
101 struct clk_init_data init;
102 struct clk_hw *hw;
103 int ret;
105 if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
106 return ERR_PTR(-EINVAL);
108 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
109 if (!periph)
110 return ERR_PTR(-ENOMEM);
112 init.name = name;
113 init.ops = &peripheral_ops;
114 init.parent_names = (parent_name ? &parent_name : NULL);
115 init.num_parents = (parent_name ? 1 : 0);
116 init.flags = 0;
118 periph->id = id;
119 periph->hw.init = &init;
120 periph->regmap = regmap;
122 hw = &periph->hw;
123 ret = clk_hw_register(NULL, &periph->hw);
124 if (ret) {
125 kfree(periph);
126 hw = ERR_PTR(ret);
129 return hw;
132 static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
134 struct clk_hw *parent;
135 unsigned long parent_rate;
136 int shift = 0;
138 if (!periph->auto_div)
139 return;
141 if (periph->range.max) {
142 parent = clk_hw_get_parent_by_index(&periph->hw, 0);
143 parent_rate = clk_hw_get_rate(parent);
144 if (!parent_rate)
145 return;
147 for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
148 if (parent_rate >> shift <= periph->range.max)
149 break;
153 periph->auto_div = false;
154 periph->div = shift;
157 static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
159 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
160 unsigned long flags;
162 if (periph->id < PERIPHERAL_ID_MIN)
163 return 0;
165 spin_lock_irqsave(periph->lock, flags);
166 regmap_write(periph->regmap, periph->layout->offset,
167 (periph->id & periph->layout->pid_mask));
168 regmap_update_bits(periph->regmap, periph->layout->offset,
169 periph->layout->div_mask | periph->layout->cmd |
170 AT91_PMC_PCR_EN,
171 field_prep(periph->layout->div_mask, periph->div) |
172 periph->layout->cmd |
173 AT91_PMC_PCR_EN);
174 spin_unlock_irqrestore(periph->lock, flags);
176 return 0;
179 static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
181 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
182 unsigned long flags;
184 if (periph->id < PERIPHERAL_ID_MIN)
185 return;
187 spin_lock_irqsave(periph->lock, flags);
188 regmap_write(periph->regmap, periph->layout->offset,
189 (periph->id & periph->layout->pid_mask));
190 regmap_update_bits(periph->regmap, periph->layout->offset,
191 AT91_PMC_PCR_EN | periph->layout->cmd,
192 periph->layout->cmd);
193 spin_unlock_irqrestore(periph->lock, flags);
196 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
198 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
199 unsigned long flags;
200 unsigned int status;
202 if (periph->id < PERIPHERAL_ID_MIN)
203 return 1;
205 spin_lock_irqsave(periph->lock, flags);
206 regmap_write(periph->regmap, periph->layout->offset,
207 (periph->id & periph->layout->pid_mask));
208 regmap_read(periph->regmap, periph->layout->offset, &status);
209 spin_unlock_irqrestore(periph->lock, flags);
211 return status & AT91_PMC_PCR_EN ? 1 : 0;
214 static unsigned long
215 clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
216 unsigned long parent_rate)
218 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
219 unsigned long flags;
220 unsigned int status;
222 if (periph->id < PERIPHERAL_ID_MIN)
223 return parent_rate;
225 spin_lock_irqsave(periph->lock, flags);
226 regmap_write(periph->regmap, periph->layout->offset,
227 (periph->id & periph->layout->pid_mask));
228 regmap_read(periph->regmap, periph->layout->offset, &status);
229 spin_unlock_irqrestore(periph->lock, flags);
231 if (status & AT91_PMC_PCR_EN) {
232 periph->div = field_get(periph->layout->div_mask, status);
233 periph->auto_div = false;
234 } else {
235 clk_sam9x5_peripheral_autodiv(periph);
238 return parent_rate >> periph->div;
241 static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
242 unsigned long rate,
243 unsigned long *parent_rate)
245 int shift = 0;
246 unsigned long best_rate;
247 unsigned long best_diff;
248 unsigned long cur_rate = *parent_rate;
249 unsigned long cur_diff;
250 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
252 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
253 return *parent_rate;
255 if (periph->range.max) {
256 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
257 cur_rate = *parent_rate >> shift;
258 if (cur_rate <= periph->range.max)
259 break;
263 if (rate >= cur_rate)
264 return cur_rate;
266 best_diff = cur_rate - rate;
267 best_rate = cur_rate;
268 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
269 cur_rate = *parent_rate >> shift;
270 if (cur_rate < rate)
271 cur_diff = rate - cur_rate;
272 else
273 cur_diff = cur_rate - rate;
275 if (cur_diff < best_diff) {
276 best_diff = cur_diff;
277 best_rate = cur_rate;
280 if (!best_diff || cur_rate < rate)
281 break;
284 return best_rate;
287 static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
288 unsigned long rate,
289 unsigned long parent_rate)
291 int shift;
292 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
293 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
294 if (parent_rate == rate)
295 return 0;
296 else
297 return -EINVAL;
300 if (periph->range.max && rate > periph->range.max)
301 return -EINVAL;
303 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
304 if (parent_rate >> shift == rate) {
305 periph->auto_div = false;
306 periph->div = shift;
307 return 0;
311 return -EINVAL;
314 static const struct clk_ops sam9x5_peripheral_ops = {
315 .enable = clk_sam9x5_peripheral_enable,
316 .disable = clk_sam9x5_peripheral_disable,
317 .is_enabled = clk_sam9x5_peripheral_is_enabled,
318 .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
319 .round_rate = clk_sam9x5_peripheral_round_rate,
320 .set_rate = clk_sam9x5_peripheral_set_rate,
323 struct clk_hw * __init
324 at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
325 const struct clk_pcr_layout *layout,
326 const char *name, const char *parent_name,
327 u32 id, const struct clk_range *range)
329 struct clk_sam9x5_peripheral *periph;
330 struct clk_init_data init;
331 struct clk_hw *hw;
332 int ret;
334 if (!name || !parent_name)
335 return ERR_PTR(-EINVAL);
337 periph = kzalloc(sizeof(*periph), GFP_KERNEL);
338 if (!periph)
339 return ERR_PTR(-ENOMEM);
341 init.name = name;
342 init.ops = &sam9x5_peripheral_ops;
343 init.parent_names = (parent_name ? &parent_name : NULL);
344 init.num_parents = (parent_name ? 1 : 0);
345 init.flags = 0;
347 periph->id = id;
348 periph->hw.init = &init;
349 periph->div = 0;
350 periph->regmap = regmap;
351 periph->lock = lock;
352 if (layout->div_mask)
353 periph->auto_div = true;
354 periph->layout = layout;
355 periph->range = *range;
357 hw = &periph->hw;
358 ret = clk_hw_register(NULL, &periph->hw);
359 if (ret) {
360 kfree(periph);
361 hw = ERR_PTR(ret);
362 } else {
363 clk_sam9x5_peripheral_autodiv(periph);
364 pmc_register_id(id);
367 return hw;