]> Git Repo - linux.git/blob - drivers/clk/at91/clk-peripheral.c
dm bufio: fix deadlock with loop device
[linux.git] / drivers / clk / at91 / clk-peripheral.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Copyright (C) 2013 Boris BREZILLON <[email protected]>
4  */
5
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>
13
14 #include "pmc.h"
15
16 DEFINE_SPINLOCK(pmc_pcr_lock);
17
18 #define PERIPHERAL_ID_MIN       2
19 #define PERIPHERAL_ID_MAX       31
20 #define PERIPHERAL_MASK(id)     (1 << ((id) & PERIPHERAL_ID_MAX))
21
22 #define PERIPHERAL_MAX_SHIFT    3
23
24 struct clk_peripheral {
25         struct clk_hw hw;
26         struct regmap *regmap;
27         u32 id;
28 };
29
30 #define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
31
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;
41 };
42
43 #define to_clk_sam9x5_peripheral(hw) \
44         container_of(hw, struct clk_sam9x5_peripheral, hw)
45
46 static int clk_peripheral_enable(struct clk_hw *hw)
47 {
48         struct clk_peripheral *periph = to_clk_peripheral(hw);
49         int offset = AT91_PMC_PCER;
50         u32 id = periph->id;
51
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));
57
58         return 0;
59 }
60
61 static void clk_peripheral_disable(struct clk_hw *hw)
62 {
63         struct clk_peripheral *periph = to_clk_peripheral(hw);
64         int offset = AT91_PMC_PCDR;
65         u32 id = periph->id;
66
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));
72 }
73
74 static int clk_peripheral_is_enabled(struct clk_hw *hw)
75 {
76         struct clk_peripheral *periph = to_clk_peripheral(hw);
77         int offset = AT91_PMC_PCSR;
78         unsigned int status;
79         u32 id = periph->id;
80
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);
86
87         return status & PERIPHERAL_MASK(id) ? 1 : 0;
88 }
89
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,
94 };
95
96 struct clk_hw * __init
97 at91_clk_register_peripheral(struct regmap *regmap, const char *name,
98                              const char *parent_name, u32 id)
99 {
100         struct clk_peripheral *periph;
101         struct clk_init_data init;
102         struct clk_hw *hw;
103         int ret;
104
105         if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
106                 return ERR_PTR(-EINVAL);
107
108         periph = kzalloc(sizeof(*periph), GFP_KERNEL);
109         if (!periph)
110                 return ERR_PTR(-ENOMEM);
111
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;
117
118         periph->id = id;
119         periph->hw.init = &init;
120         periph->regmap = regmap;
121
122         hw = &periph->hw;
123         ret = clk_hw_register(NULL, &periph->hw);
124         if (ret) {
125                 kfree(periph);
126                 hw = ERR_PTR(ret);
127         }
128
129         return hw;
130 }
131
132 static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
133 {
134         struct clk_hw *parent;
135         unsigned long parent_rate;
136         int shift = 0;
137
138         if (!periph->auto_div)
139                 return;
140
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;
146
147                 for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
148                         if (parent_rate >> shift <= periph->range.max)
149                                 break;
150                 }
151         }
152
153         periph->auto_div = false;
154         periph->div = shift;
155 }
156
157 static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
158 {
159         struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
160         unsigned long flags;
161
162         if (periph->id < PERIPHERAL_ID_MIN)
163                 return 0;
164
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);
175
176         return 0;
177 }
178
179 static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
180 {
181         struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
182         unsigned long flags;
183
184         if (periph->id < PERIPHERAL_ID_MIN)
185                 return;
186
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);
194 }
195
196 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
197 {
198         struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
199         unsigned long flags;
200         unsigned int status;
201
202         if (periph->id < PERIPHERAL_ID_MIN)
203                 return 1;
204
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);
210
211         return status & AT91_PMC_PCR_EN ? 1 : 0;
212 }
213
214 static unsigned long
215 clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
216                                   unsigned long parent_rate)
217 {
218         struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
219         unsigned long flags;
220         unsigned int status;
221
222         if (periph->id < PERIPHERAL_ID_MIN)
223                 return parent_rate;
224
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);
230
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);
236         }
237
238         return parent_rate >> periph->div;
239 }
240
241 static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
242                                              unsigned long rate,
243                                              unsigned long *parent_rate)
244 {
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);
251
252         if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
253                 return *parent_rate;
254
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;
260                 }
261         }
262
263         if (rate >= cur_rate)
264                 return cur_rate;
265
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;
274
275                 if (cur_diff < best_diff) {
276                         best_diff = cur_diff;
277                         best_rate = cur_rate;
278                 }
279
280                 if (!best_diff || cur_rate < rate)
281                         break;
282         }
283
284         return best_rate;
285 }
286
287 static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
288                                           unsigned long rate,
289                                           unsigned long parent_rate)
290 {
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;
298         }
299
300         if (periph->range.max && rate > periph->range.max)
301                 return -EINVAL;
302
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;
308                 }
309         }
310
311         return -EINVAL;
312 }
313
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,
321 };
322
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)
328 {
329         struct clk_sam9x5_peripheral *periph;
330         struct clk_init_data init;
331         struct clk_hw *hw;
332         int ret;
333
334         if (!name || !parent_name)
335                 return ERR_PTR(-EINVAL);
336
337         periph = kzalloc(sizeof(*periph), GFP_KERNEL);
338         if (!periph)
339                 return ERR_PTR(-ENOMEM);
340
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;
346
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;
356
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);
365         }
366
367         return hw;
368 }
This page took 0.052994 seconds and 4 git commands to generate.