blob: d7fe1303f79dcb5a8f958156f5f6cea11c0825b4 [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001// SPDX-License-Identifier: GPL-2.0-or-later
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002/*
3 * Copyright (C) 2015 Atmel Corporation,
4 * Nicolas Ferre <nicolas.ferre@atmel.com>
5 *
6 * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON.
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00007 */
8
David Brazdil0f672f62019-12-10 10:32:29 +00009#include <linux/bitfield.h>
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000010#include <linux/clk-provider.h>
11#include <linux/clkdev.h>
12#include <linux/clk/at91_pmc.h>
13#include <linux/of.h>
14#include <linux/mfd/syscon.h>
15#include <linux/regmap.h>
16
17#include "pmc.h"
18
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000019#define GENERATED_MAX_DIV 255
20
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000021struct clk_generated {
22 struct clk_hw hw;
23 struct regmap *regmap;
24 struct clk_range range;
25 spinlock_t *lock;
26 u32 id;
27 u32 gckdiv;
David Brazdil0f672f62019-12-10 10:32:29 +000028 const struct clk_pcr_layout *layout;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000029 u8 parent_id;
Olivier Deprez0e641232021-09-23 10:07:05 +020030 int chg_pid;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000031};
32
33#define to_clk_generated(hw) \
34 container_of(hw, struct clk_generated, hw)
35
36static int clk_generated_enable(struct clk_hw *hw)
37{
38 struct clk_generated *gck = to_clk_generated(hw);
39 unsigned long flags;
40
41 pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
42 __func__, gck->gckdiv, gck->parent_id);
43
44 spin_lock_irqsave(gck->lock, flags);
David Brazdil0f672f62019-12-10 10:32:29 +000045 regmap_write(gck->regmap, gck->layout->offset,
46 (gck->id & gck->layout->pid_mask));
47 regmap_update_bits(gck->regmap, gck->layout->offset,
48 AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask |
49 gck->layout->cmd | AT91_PMC_PCR_GCKEN,
50 field_prep(gck->layout->gckcss_mask, gck->parent_id) |
51 gck->layout->cmd |
52 FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) |
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000053 AT91_PMC_PCR_GCKEN);
54 spin_unlock_irqrestore(gck->lock, flags);
55 return 0;
56}
57
58static void clk_generated_disable(struct clk_hw *hw)
59{
60 struct clk_generated *gck = to_clk_generated(hw);
61 unsigned long flags;
62
63 spin_lock_irqsave(gck->lock, flags);
David Brazdil0f672f62019-12-10 10:32:29 +000064 regmap_write(gck->regmap, gck->layout->offset,
65 (gck->id & gck->layout->pid_mask));
66 regmap_update_bits(gck->regmap, gck->layout->offset,
67 gck->layout->cmd | AT91_PMC_PCR_GCKEN,
68 gck->layout->cmd);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000069 spin_unlock_irqrestore(gck->lock, flags);
70}
71
72static int clk_generated_is_enabled(struct clk_hw *hw)
73{
74 struct clk_generated *gck = to_clk_generated(hw);
75 unsigned long flags;
76 unsigned int status;
77
78 spin_lock_irqsave(gck->lock, flags);
David Brazdil0f672f62019-12-10 10:32:29 +000079 regmap_write(gck->regmap, gck->layout->offset,
80 (gck->id & gck->layout->pid_mask));
81 regmap_read(gck->regmap, gck->layout->offset, &status);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000082 spin_unlock_irqrestore(gck->lock, flags);
83
84 return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
85}
86
87static unsigned long
88clk_generated_recalc_rate(struct clk_hw *hw,
89 unsigned long parent_rate)
90{
91 struct clk_generated *gck = to_clk_generated(hw);
92
93 return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1);
94}
95
96static void clk_generated_best_diff(struct clk_rate_request *req,
97 struct clk_hw *parent,
98 unsigned long parent_rate, u32 div,
99 int *best_diff, long *best_rate)
100{
101 unsigned long tmp_rate;
102 int tmp_diff;
103
104 if (!div)
105 tmp_rate = parent_rate;
106 else
107 tmp_rate = parent_rate / div;
108 tmp_diff = abs(req->rate - tmp_rate);
109
Olivier Deprez0e641232021-09-23 10:07:05 +0200110 if (*best_diff < 0 || *best_diff >= tmp_diff) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000111 *best_rate = tmp_rate;
112 *best_diff = tmp_diff;
113 req->best_parent_rate = parent_rate;
114 req->best_parent_hw = parent;
115 }
116}
117
118static int clk_generated_determine_rate(struct clk_hw *hw,
119 struct clk_rate_request *req)
120{
121 struct clk_generated *gck = to_clk_generated(hw);
122 struct clk_hw *parent = NULL;
123 struct clk_rate_request req_parent = *req;
124 long best_rate = -EINVAL;
125 unsigned long min_rate, parent_rate;
126 int best_diff = -1;
127 int i;
128 u32 div;
129
Olivier Deprez0e641232021-09-23 10:07:05 +0200130 /* do not look for a rate that is outside of our range */
131 if (gck->range.max && req->rate > gck->range.max)
132 req->rate = gck->range.max;
133 if (gck->range.min && req->rate < gck->range.min)
134 req->rate = gck->range.min;
135
136 for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
137 if (gck->chg_pid == i)
138 continue;
139
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000140 parent = clk_hw_get_parent_by_index(hw, i);
141 if (!parent)
142 continue;
143
144 parent_rate = clk_hw_get_rate(parent);
145 min_rate = DIV_ROUND_CLOSEST(parent_rate, GENERATED_MAX_DIV + 1);
146 if (!parent_rate ||
147 (gck->range.max && min_rate > gck->range.max))
148 continue;
149
150 div = DIV_ROUND_CLOSEST(parent_rate, req->rate);
David Brazdil0f672f62019-12-10 10:32:29 +0000151 if (div > GENERATED_MAX_DIV + 1)
152 div = GENERATED_MAX_DIV + 1;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000153
154 clk_generated_best_diff(req, parent, parent_rate, div,
155 &best_diff, &best_rate);
156
157 if (!best_diff)
158 break;
159 }
160
161 /*
162 * The audio_pll rate can be modified, unlike the five others clocks
163 * that should never be altered.
164 * The audio_pll can technically be used by multiple consumers. However,
165 * with the rate locking, the first consumer to enable to clock will be
166 * the one definitely setting the rate of the clock.
167 * Since audio IPs are most likely to request the same rate, we enforce
168 * that the only clks able to modify gck rate are those of audio IPs.
169 */
170
Olivier Deprez0e641232021-09-23 10:07:05 +0200171 if (gck->chg_pid < 0)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000172 goto end;
173
Olivier Deprez0e641232021-09-23 10:07:05 +0200174 parent = clk_hw_get_parent_by_index(hw, gck->chg_pid);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000175 if (!parent)
176 goto end;
177
178 for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
179 req_parent.rate = req->rate * div;
180 __clk_determine_rate(parent, &req_parent);
181 clk_generated_best_diff(req, parent, req_parent.rate, div,
182 &best_diff, &best_rate);
183
184 if (!best_diff)
185 break;
186 }
187
188end:
189 pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
190 __func__, best_rate,
191 __clk_get_name((req->best_parent_hw)->clk),
192 req->best_parent_rate);
193
194 if (best_rate < 0)
195 return best_rate;
196
197 req->rate = best_rate;
198 return 0;
199}
200
201/* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */
202static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
203{
204 struct clk_generated *gck = to_clk_generated(hw);
205
206 if (index >= clk_hw_get_num_parents(hw))
207 return -EINVAL;
208
209 gck->parent_id = index;
210 return 0;
211}
212
213static u8 clk_generated_get_parent(struct clk_hw *hw)
214{
215 struct clk_generated *gck = to_clk_generated(hw);
216
217 return gck->parent_id;
218}
219
220/* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */
221static int clk_generated_set_rate(struct clk_hw *hw,
222 unsigned long rate,
223 unsigned long parent_rate)
224{
225 struct clk_generated *gck = to_clk_generated(hw);
226 u32 div;
227
228 if (!rate)
229 return -EINVAL;
230
231 if (gck->range.max && rate > gck->range.max)
232 return -EINVAL;
233
234 div = DIV_ROUND_CLOSEST(parent_rate, rate);
235 if (div > GENERATED_MAX_DIV + 1 || !div)
236 return -EINVAL;
237
238 gck->gckdiv = div - 1;
239 return 0;
240}
241
242static const struct clk_ops generated_ops = {
243 .enable = clk_generated_enable,
244 .disable = clk_generated_disable,
245 .is_enabled = clk_generated_is_enabled,
246 .recalc_rate = clk_generated_recalc_rate,
247 .determine_rate = clk_generated_determine_rate,
248 .get_parent = clk_generated_get_parent,
249 .set_parent = clk_generated_set_parent,
250 .set_rate = clk_generated_set_rate,
251};
252
253/**
254 * clk_generated_startup - Initialize a given clock to its default parent and
255 * divisor parameter.
256 *
257 * @gck: Generated clock to set the startup parameters for.
258 *
259 * Take parameters from the hardware and update local clock configuration
260 * accordingly.
261 */
262static void clk_generated_startup(struct clk_generated *gck)
263{
264 u32 tmp;
265 unsigned long flags;
266
267 spin_lock_irqsave(gck->lock, flags);
David Brazdil0f672f62019-12-10 10:32:29 +0000268 regmap_write(gck->regmap, gck->layout->offset,
269 (gck->id & gck->layout->pid_mask));
270 regmap_read(gck->regmap, gck->layout->offset, &tmp);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000271 spin_unlock_irqrestore(gck->lock, flags);
272
David Brazdil0f672f62019-12-10 10:32:29 +0000273 gck->parent_id = field_get(gck->layout->gckcss_mask, tmp);
274 gck->gckdiv = FIELD_GET(AT91_PMC_PCR_GCKDIV_MASK, tmp);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000275}
276
David Brazdil0f672f62019-12-10 10:32:29 +0000277struct clk_hw * __init
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000278at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
David Brazdil0f672f62019-12-10 10:32:29 +0000279 const struct clk_pcr_layout *layout,
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000280 const char *name, const char **parent_names,
Olivier Deprez0e641232021-09-23 10:07:05 +0200281 u8 num_parents, u8 id,
282 const struct clk_range *range, int chg_pid)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000283{
284 struct clk_generated *gck;
285 struct clk_init_data init;
286 struct clk_hw *hw;
287 int ret;
288
289 gck = kzalloc(sizeof(*gck), GFP_KERNEL);
290 if (!gck)
291 return ERR_PTR(-ENOMEM);
292
293 init.name = name;
294 init.ops = &generated_ops;
295 init.parent_names = parent_names;
296 init.num_parents = num_parents;
Olivier Deprez0e641232021-09-23 10:07:05 +0200297 init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
298 if (chg_pid >= 0)
299 init.flags |= CLK_SET_RATE_PARENT;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000300
301 gck->id = id;
302 gck->hw.init = &init;
303 gck->regmap = regmap;
304 gck->lock = lock;
305 gck->range = *range;
Olivier Deprez0e641232021-09-23 10:07:05 +0200306 gck->chg_pid = chg_pid;
David Brazdil0f672f62019-12-10 10:32:29 +0000307 gck->layout = layout;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000308
309 clk_generated_startup(gck);
310 hw = &gck->hw;
311 ret = clk_hw_register(NULL, &gck->hw);
312 if (ret) {
313 kfree(gck);
314 hw = ERR_PTR(ret);
315 } else {
316 pmc_register_id(id);
317 }
318
319 return hw;
320}