Skip to content

Commit 62b1fea

Browse files
claudiubezneageertu
authored andcommitted
clk: renesas: rzg2l: Add divider clock for RZ/G3S
Add a divider clock driver for RZ/G3S. This will be used on RZ/G3S for the SDHI, SPI, OCTA, I, I2, I3, P0, P1, P2, and P3 core clocks. The divider has some limitation for SDHI, OCTA and SPI clocks: - SDHI div cannot be 1 if parent rate is 800MHz, - OCTA, SPI div cannot be 1 if parent rate is 400MHz. To handle these limitations, a notifier is registered from platform specific clock driver, which makes sure proper actions are taken before the clock rate is changed, when needed. Signed-off-by: Claudiu Beznea <[email protected]> Reviewed-by: Geert Uytterhoeven <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Geert Uytterhoeven <[email protected]>
1 parent 16b86e5 commit 62b1fea

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
lines changed

drivers/clk/renesas/rzg2l-cpg.c

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,24 @@ struct sd_mux_hw_data {
9393

9494
#define to_sd_mux_hw_data(_hw) container_of(_hw, struct sd_mux_hw_data, hw_data)
9595

96+
/**
97+
* struct div_hw_data - divider clock hardware data
98+
* @hw_data: clock hw data
99+
* @dtable: pointer to divider table
100+
* @invalid_rate: invalid rate for divider
101+
* @max_rate: maximum rate for divider
102+
* @width: divider width
103+
*/
104+
struct div_hw_data {
105+
struct clk_hw_data hw_data;
106+
const struct clk_div_table *dtable;
107+
unsigned long invalid_rate;
108+
unsigned long max_rate;
109+
u32 width;
110+
};
111+
112+
#define to_div_hw_data(_hw) container_of(_hw, struct div_hw_data, hw_data)
113+
96114
struct rzg2l_pll5_param {
97115
u32 pl5_fracin;
98116
u8 pl5_refdiv;
@@ -201,6 +219,53 @@ int rzg2l_cpg_sd_clk_mux_notifier(struct notifier_block *nb, unsigned long event
201219
return notifier_from_errno(ret);
202220
}
203221

222+
int rzg3s_cpg_div_clk_notifier(struct notifier_block *nb, unsigned long event,
223+
void *data)
224+
{
225+
struct clk_notifier_data *cnd = data;
226+
struct clk_hw *hw = __clk_get_hw(cnd->clk);
227+
struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
228+
struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
229+
struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
230+
u32 off = GET_REG_OFFSET(clk_hw_data->conf);
231+
u32 shift = GET_SHIFT(clk_hw_data->conf);
232+
unsigned long flags;
233+
int ret = 0;
234+
u32 val;
235+
236+
if (event != PRE_RATE_CHANGE || !div_hw_data->invalid_rate ||
237+
div_hw_data->invalid_rate % cnd->new_rate)
238+
return NOTIFY_DONE;
239+
240+
spin_lock_irqsave(&priv->rmw_lock, flags);
241+
242+
val = readl(priv->base + off);
243+
val >>= shift;
244+
val &= GENMASK(GET_WIDTH(clk_hw_data->conf) - 1, 0);
245+
246+
/*
247+
* There are different constraints for the user of this notifiers as follows:
248+
* 1/ SD div cannot be 1 (val == 0) if parent rate is 800MHz
249+
* 2/ OCTA / SPI div cannot be 1 (val == 0) if parent rate is 400MHz
250+
* As SD can have only one parent having 800MHz and OCTA div can have
251+
* only one parent having 400MHz we took into account the parent rate
252+
* at the beginning of function (by checking invalid_rate % new_rate).
253+
* Now it is time to check the hardware divider and update it accordingly.
254+
*/
255+
if (!val) {
256+
writel((CPG_WEN_BIT | 1) << shift, priv->base + off);
257+
/* Wait for the update done. */
258+
ret = rzg2l_cpg_wait_clk_update_done(priv->base, clk_hw_data->sconf);
259+
}
260+
261+
spin_unlock_irqrestore(&priv->rmw_lock, flags);
262+
263+
if (ret)
264+
dev_err(priv->dev, "Failed to downgrade the div\n");
265+
266+
return notifier_from_errno(ret);
267+
}
268+
204269
static int rzg2l_register_notifier(struct clk_hw *hw, const struct cpg_core_clk *core,
205270
struct rzg2l_cpg_priv *priv)
206271
{
@@ -218,6 +283,124 @@ static int rzg2l_register_notifier(struct clk_hw *hw, const struct cpg_core_clk
218283
return clk_notifier_register(hw->clk, nb);
219284
}
220285

286+
static unsigned long rzg3s_div_clk_recalc_rate(struct clk_hw *hw,
287+
unsigned long parent_rate)
288+
{
289+
struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
290+
struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
291+
struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
292+
u32 val;
293+
294+
val = readl(priv->base + GET_REG_OFFSET(clk_hw_data->conf));
295+
val >>= GET_SHIFT(clk_hw_data->conf);
296+
val &= GENMASK(GET_WIDTH(clk_hw_data->conf) - 1, 0);
297+
298+
return divider_recalc_rate(hw, parent_rate, val, div_hw_data->dtable,
299+
CLK_DIVIDER_ROUND_CLOSEST, div_hw_data->width);
300+
}
301+
302+
static int rzg3s_div_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
303+
{
304+
struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
305+
struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
306+
307+
if (div_hw_data->max_rate && req->rate > div_hw_data->max_rate)
308+
req->rate = div_hw_data->max_rate;
309+
310+
return divider_determine_rate(hw, req, div_hw_data->dtable, div_hw_data->width,
311+
CLK_DIVIDER_ROUND_CLOSEST);
312+
}
313+
314+
static int rzg3s_div_clk_set_rate(struct clk_hw *hw, unsigned long rate,
315+
unsigned long parent_rate)
316+
{
317+
struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
318+
struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
319+
struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
320+
u32 off = GET_REG_OFFSET(clk_hw_data->conf);
321+
u32 shift = GET_SHIFT(clk_hw_data->conf);
322+
unsigned long flags;
323+
u32 val;
324+
int ret;
325+
326+
val = divider_get_val(rate, parent_rate, div_hw_data->dtable, div_hw_data->width,
327+
CLK_DIVIDER_ROUND_CLOSEST);
328+
329+
spin_lock_irqsave(&priv->rmw_lock, flags);
330+
writel((CPG_WEN_BIT | val) << shift, priv->base + off);
331+
/* Wait for the update done. */
332+
ret = rzg2l_cpg_wait_clk_update_done(priv->base, clk_hw_data->sconf);
333+
spin_unlock_irqrestore(&priv->rmw_lock, flags);
334+
335+
return ret;
336+
}
337+
338+
static const struct clk_ops rzg3s_div_clk_ops = {
339+
.recalc_rate = rzg3s_div_clk_recalc_rate,
340+
.determine_rate = rzg3s_div_clk_determine_rate,
341+
.set_rate = rzg3s_div_clk_set_rate,
342+
};
343+
344+
static struct clk * __init
345+
rzg3s_cpg_div_clk_register(const struct cpg_core_clk *core, struct clk **clks,
346+
void __iomem *base, struct rzg2l_cpg_priv *priv)
347+
{
348+
struct div_hw_data *div_hw_data;
349+
struct clk_init_data init = {};
350+
const struct clk_div_table *clkt;
351+
struct clk_hw *clk_hw;
352+
const struct clk *parent;
353+
const char *parent_name;
354+
u32 max = 0;
355+
int ret;
356+
357+
parent = clks[core->parent & 0xffff];
358+
if (IS_ERR(parent))
359+
return ERR_CAST(parent);
360+
361+
parent_name = __clk_get_name(parent);
362+
363+
div_hw_data = devm_kzalloc(priv->dev, sizeof(*div_hw_data), GFP_KERNEL);
364+
if (!div_hw_data)
365+
return ERR_PTR(-ENOMEM);
366+
367+
init.name = core->name;
368+
init.flags = core->flag;
369+
init.ops = &rzg3s_div_clk_ops;
370+
init.parent_names = &parent_name;
371+
init.num_parents = 1;
372+
373+
/* Get the maximum divider to retrieve div width. */
374+
for (clkt = core->dtable; clkt->div; clkt++) {
375+
if (max < clkt->div)
376+
max = clkt->div;
377+
}
378+
379+
div_hw_data->hw_data.priv = priv;
380+
div_hw_data->hw_data.conf = core->conf;
381+
div_hw_data->hw_data.sconf = core->sconf;
382+
div_hw_data->dtable = core->dtable;
383+
div_hw_data->invalid_rate = core->invalid_rate;
384+
div_hw_data->max_rate = core->max_rate;
385+
div_hw_data->width = fls(max) - 1;
386+
387+
clk_hw = &div_hw_data->hw_data.hw;
388+
clk_hw->init = &init;
389+
390+
ret = devm_clk_hw_register(priv->dev, clk_hw);
391+
if (ret)
392+
return ERR_PTR(ret);
393+
394+
ret = rzg2l_register_notifier(clk_hw, core, priv);
395+
if (ret) {
396+
dev_err(priv->dev, "Failed to register notifier for %s\n",
397+
core->name);
398+
return ERR_PTR(ret);
399+
}
400+
401+
return clk_hw->clk;
402+
}
403+
221404
static struct clk * __init
222405
rzg2l_cpg_div_clk_register(const struct cpg_core_clk *core,
223406
struct clk **clks,
@@ -963,6 +1146,9 @@ rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core,
9631146
clk = rzg2l_cpg_div_clk_register(core, priv->clks,
9641147
priv->base, priv);
9651148
break;
1149+
case CLK_TYPE_G3S_DIV:
1150+
clk = rzg3s_cpg_div_clk_register(core, priv->clks, priv->base, priv);
1151+
break;
9661152
case CLK_TYPE_MUX:
9671153
clk = rzg2l_cpg_mux_clk_register(core, priv->base, priv);
9681154
break;

drivers/clk/renesas/rzg2l-cpg.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ struct cpg_core_clk {
8989
unsigned int sconf;
9090
const struct clk_div_table *dtable;
9191
const u32 *mtable;
92+
const unsigned long invalid_rate;
93+
const unsigned long max_rate;
9294
const char * const *parent_names;
9395
notifier_fn_t notifier;
9496
u32 flag;
@@ -105,6 +107,7 @@ enum clk_types {
105107

106108
/* Clock with divider */
107109
CLK_TYPE_DIV,
110+
CLK_TYPE_G3S_DIV,
108111

109112
/* Clock with clock source selector */
110113
CLK_TYPE_MUX,
@@ -143,6 +146,13 @@ enum clk_types {
143146
DEF_TYPE(_name, _id, CLK_TYPE_DIV, .conf = _conf, \
144147
.parent = _parent, .dtable = _dtable, \
145148
.flag = CLK_DIVIDER_READ_ONLY)
149+
#define DEF_G3S_DIV(_name, _id, _parent, _conf, _sconf, _dtable, _invalid_rate, \
150+
_max_rate, _clk_flags, _notif) \
151+
DEF_TYPE(_name, _id, CLK_TYPE_G3S_DIV, .conf = _conf, .sconf = _sconf, \
152+
.parent = _parent, .dtable = _dtable, \
153+
.invalid_rate = _invalid_rate, \
154+
.max_rate = _max_rate, .flag = (_clk_flags), \
155+
.notifier = _notif)
146156
#define DEF_MUX(_name, _id, _conf, _parent_names) \
147157
DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \
148158
.parent_names = _parent_names, \
@@ -277,5 +287,6 @@ extern const struct rzg2l_cpg_info r9a07g054_cpg_info;
277287
extern const struct rzg2l_cpg_info r9a09g011_cpg_info;
278288

279289
int rzg2l_cpg_sd_clk_mux_notifier(struct notifier_block *nb, unsigned long event, void *data);
290+
int rzg3s_cpg_div_clk_notifier(struct notifier_block *nb, unsigned long event, void *data);
280291

281292
#endif

0 commit comments

Comments
 (0)