Skip to content

Commit ec74d20

Browse files
prabhakarladgeertu
authored andcommitted
clk: renesas: r9a09g077: Add xSPI core and module clocks
Add core clocks and module clock definitions required by the xSPI (Expanded SPI) IP on the R9A09G077 SoC. Define the new SCKCR fields FSELXSPI0/FSELXSPI1 and DIVSEL_XSPI0/1 and add two new core clocks XSPI_CLK0 and XSPI_CLK1. The xSPI block uses PCLKH as its bus clock (use as module clock parent) while the operation clock (XSPI_CLKn) is derived from PLL4. To support this arrangement provide mux/div selectors and divider tables for the supported XSPI operating rates. Add CLK_TYPE_RZT2H_FSELXSPI to implement a custom divider/mux clock where the determine_rate() callback enforces the hardware constraint: when the parent output is 600MHz only dividers 8 and 16 are valid, whereas for 800MHz operation the full divider set (6,8,16,32,64) may be used. The custom determine_rate() picks the best parent/divider pair to match the requested rate and programs the appropriate SCKCR fields. Signed-off-by: Lad Prabhakar <[email protected]> Reviewed-by: Geert Uytterhoeven <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Geert Uytterhoeven <[email protected]>
1 parent eede457 commit ec74d20

1 file changed

Lines changed: 190 additions & 3 deletions

File tree

drivers/clk/renesas/r9a09g077-cpg.c

Lines changed: 190 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <linux/device.h>
1212
#include <linux/init.h>
1313
#include <linux/kernel.h>
14+
#include <linux/math.h>
15+
#include <linux/types.h>
1416

1517
#include <dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h>
1618
#include <dt-bindings/clock/renesas,r9a09g087-cpg-mssr.h>
@@ -41,6 +43,12 @@
4143
#define GET_WIDTH(val) FIELD_GET(WIDTH_MASK, val)
4244
#define GET_REG_OFFSET(val) FIELD_GET(OFFSET_MASK, val)
4345

46+
#define FSELXSPI0 CONF_PACK(SCKCR, 0, 3)
47+
#define FSELXSPI1 CONF_PACK(SCKCR, 8, 3)
48+
#define DIVSEL_XSPI0 CONF_PACK(SCKCR, 6, 1)
49+
#define DIVSEL_XSPI1 CONF_PACK(SCKCR, 14, 1)
50+
#define SEL_PLL CONF_PACK(SCKCR, 22, 1)
51+
4452
#define DIVCA55C0 CONF_PACK(SCKCR2, 8, 1)
4553
#define DIVCA55C1 CONF_PACK(SCKCR2, 9, 1)
4654
#define DIVCA55C2 CONF_PACK(SCKCR2, 10, 1)
@@ -58,11 +66,10 @@
5866
#define DIVSCI3ASYNC CONF_PACK(SCKCR3, 12, 2)
5967
#define DIVSCI4ASYNC CONF_PACK(SCKCR3, 14, 2)
6068

61-
#define SEL_PLL CONF_PACK(SCKCR, 22, 1)
62-
6369
enum rzt2h_clk_types {
6470
CLK_TYPE_RZT2H_DIV = CLK_TYPE_CUSTOM, /* Clock with divider */
6571
CLK_TYPE_RZT2H_MUX, /* Clock with clock source selector */
72+
CLK_TYPE_RZT2H_FSELXSPI, /* Clock with FSELXSPIn source selector */
6673
};
6774

6875
#define DEF_DIV(_name, _id, _parent, _conf, _dtable) \
@@ -72,10 +79,13 @@ enum rzt2h_clk_types {
7279
DEF_TYPE(_name, _id, CLK_TYPE_RZT2H_MUX, .conf = _conf, \
7380
.parent_names = _parent_names, .num_parents = _num_parents, \
7481
.flag = 0, .mux_flags = _mux_flags)
82+
#define DEF_DIV_FSELXSPI(_name, _id, _parent, _conf, _dtable) \
83+
DEF_TYPE(_name, _id, CLK_TYPE_RZT2H_FSELXSPI, .conf = _conf, \
84+
.parent = _parent, .dtable = _dtable, .flag = 0)
7585

7686
enum clk_ids {
7787
/* Core Clock Outputs exported to DT */
78-
LAST_DT_CORE_CLK = R9A09G077_ETCLKE,
88+
LAST_DT_CORE_CLK = R9A09G077_XSPI_CLK1,
7989

8090
/* External Input Clocks */
8191
CLK_EXTAL,
@@ -91,6 +101,8 @@ enum clk_ids {
91101
CLK_SEL_CLK_PLL2,
92102
CLK_SEL_CLK_PLL4,
93103
CLK_PLL4D1,
104+
CLK_PLL4D1_DIV3,
105+
CLK_PLL4D1_DIV4,
94106
CLK_SCI0ASYNC,
95107
CLK_SCI1ASYNC,
96108
CLK_SCI2ASYNC,
@@ -101,6 +113,8 @@ enum clk_ids {
101113
CLK_SPI1ASYNC,
102114
CLK_SPI2ASYNC,
103115
CLK_SPI3ASYNC,
116+
CLK_DIVSELXSPI0_SCKCR,
117+
CLK_DIVSELXSPI1_SCKCR,
104118

105119
/* Module Clocks */
106120
MOD_CLK_BASE,
@@ -112,6 +126,15 @@ static const struct clk_div_table dtable_1_2[] = {
112126
{0, 0},
113127
};
114128

129+
static const struct clk_div_table dtable_6_8_16_32_64[] = {
130+
{6, 64},
131+
{5, 32},
132+
{4, 16},
133+
{3, 8},
134+
{2, 6},
135+
{0, 0},
136+
};
137+
115138
static const struct clk_div_table dtable_24_25_30_32[] = {
116139
{0, 32},
117140
{1, 30},
@@ -126,6 +149,7 @@ static const char * const sel_clk_pll0[] = { ".loco", ".pll0" };
126149
static const char * const sel_clk_pll1[] = { ".loco", ".pll1" };
127150
static const char * const sel_clk_pll2[] = { ".loco", ".pll2" };
128151
static const char * const sel_clk_pll4[] = { ".loco", ".pll4" };
152+
static const char * const sel_clk_pll4d1_div3_div4[] = { ".pll4d1_div3", ".pll4d1_div4" };
129153

130154
static const struct cpg_core_clk r9a09g077_core_clks[] __initconst = {
131155
/* External Clock Inputs */
@@ -148,6 +172,9 @@ static const struct cpg_core_clk r9a09g077_core_clks[] __initconst = {
148172
sel_clk_pll4, ARRAY_SIZE(sel_clk_pll4), CLK_MUX_READ_ONLY),
149173

150174
DEF_FIXED(".pll4d1", CLK_PLL4D1, CLK_SEL_CLK_PLL4, 1, 1),
175+
DEF_FIXED(".pll4d1_div3", CLK_PLL4D1_DIV3, CLK_PLL4D1, 3, 1),
176+
DEF_FIXED(".pll4d1_div4", CLK_PLL4D1_DIV4, CLK_PLL4D1, 4, 1),
177+
151178
DEF_DIV(".sci0async", CLK_SCI0ASYNC, CLK_PLL4D1, DIVSCI0ASYNC,
152179
dtable_24_25_30_32),
153180
DEF_DIV(".sci1async", CLK_SCI1ASYNC, CLK_PLL4D1, DIVSCI1ASYNC,
@@ -170,6 +197,13 @@ static const struct cpg_core_clk r9a09g077_core_clks[] __initconst = {
170197
DEF_DIV(".spi3async", CLK_SPI3ASYNC, CLK_PLL4D1, DIVSPI3ASYNC,
171198
dtable_24_25_30_32),
172199

200+
DEF_MUX(".divselxspi0", CLK_DIVSELXSPI0_SCKCR, DIVSEL_XSPI0,
201+
sel_clk_pll4d1_div3_div4,
202+
ARRAY_SIZE(sel_clk_pll4d1_div3_div4), 0),
203+
DEF_MUX(".divselxspi1", CLK_DIVSELXSPI1_SCKCR, DIVSEL_XSPI1,
204+
sel_clk_pll4d1_div3_div4,
205+
ARRAY_SIZE(sel_clk_pll4d1_div3_div4), 0),
206+
173207
/* Core output clk */
174208
DEF_DIV("CA55C0", R9A09G077_CLK_CA55C0, CLK_SEL_CLK_PLL0, DIVCA55C0,
175209
dtable_1_2),
@@ -194,9 +228,15 @@ static const struct cpg_core_clk r9a09g077_core_clks[] __initconst = {
194228
DEF_FIXED("ETCLKC", R9A09G077_ETCLKC, CLK_SEL_CLK_PLL1, 10, 1),
195229
DEF_FIXED("ETCLKD", R9A09G077_ETCLKD, CLK_SEL_CLK_PLL1, 20, 1),
196230
DEF_FIXED("ETCLKE", R9A09G077_ETCLKE, CLK_SEL_CLK_PLL1, 40, 1),
231+
DEF_DIV_FSELXSPI("XSPI_CLK0", R9A09G077_XSPI_CLK0, CLK_DIVSELXSPI0_SCKCR,
232+
FSELXSPI0, dtable_6_8_16_32_64),
233+
DEF_DIV_FSELXSPI("XSPI_CLK1", R9A09G077_XSPI_CLK1, CLK_DIVSELXSPI1_SCKCR,
234+
FSELXSPI1, dtable_6_8_16_32_64),
197235
};
198236

199237
static const struct mssr_mod_clk r9a09g077_mod_clks[] __initconst = {
238+
DEF_MOD("xspi0", 4, R9A09G077_CLK_PCLKH),
239+
DEF_MOD("xspi1", 5, R9A09G077_CLK_PCLKH),
200240
DEF_MOD("sci0fck", 8, CLK_SCI0ASYNC),
201241
DEF_MOD("sci1fck", 9, CLK_SCI1ASYNC),
202242
DEF_MOD("sci2fck", 10, CLK_SCI2ASYNC),
@@ -284,6 +324,151 @@ r9a09g077_cpg_mux_clk_register(struct device *dev,
284324
return clk_hw->clk;
285325
}
286326

327+
static unsigned int r9a09g077_cpg_fselxspi_get_divider(struct clk_hw *hw, unsigned long rate,
328+
unsigned int num_parents)
329+
{
330+
struct clk_fixed_factor *ff;
331+
struct clk_hw *parent_hw;
332+
unsigned long best_rate;
333+
unsigned int i;
334+
335+
for (i = 0; i < num_parents; i++) {
336+
parent_hw = clk_hw_get_parent_by_index(hw, i);
337+
best_rate = clk_hw_round_rate(parent_hw, rate);
338+
339+
if (best_rate == rate) {
340+
ff = to_clk_fixed_factor(parent_hw);
341+
return ff->div;
342+
}
343+
}
344+
345+
/* No parent could provide the exact rate - this should not happen */
346+
return 0;
347+
}
348+
349+
static int r9a09g077_cpg_fselxspi_determine_rate(struct clk_hw *hw,
350+
struct clk_rate_request *req)
351+
{
352+
struct clk_divider *divider = to_clk_divider(hw);
353+
unsigned long parent_rate, best = 0, now;
354+
const struct clk_div_table *clkt;
355+
unsigned long rate = req->rate;
356+
unsigned int num_parents;
357+
unsigned int divselxspi;
358+
unsigned int div = 0;
359+
360+
if (!rate)
361+
rate = 1;
362+
363+
/* Get the number of parents for FSELXSPIn */
364+
num_parents = clk_hw_get_num_parents(req->best_parent_hw);
365+
366+
for (clkt = divider->table; clkt->div; clkt++) {
367+
parent_rate = clk_hw_round_rate(req->best_parent_hw, rate * clkt->div);
368+
/* Skip if parent can't provide any valid rate */
369+
if (!parent_rate)
370+
continue;
371+
372+
/* Determine which DIVSELXSPIn divider (3 or 4) provides this parent_rate */
373+
divselxspi = r9a09g077_cpg_fselxspi_get_divider(req->best_parent_hw, parent_rate,
374+
num_parents);
375+
if (!divselxspi)
376+
continue;
377+
378+
/*
379+
* DIVSELXSPIx supports 800MHz and 600MHz operation.
380+
* When divselxspi is 4 (600MHz operation), only FSELXSPIn dividers of 8 and 16
381+
* are supported. Otherwise, when divselxspi is 3 (800MHz operation),
382+
* dividers of 6, 8, 16, 32, and 64 are supported. This check ensures that
383+
* FSELXSPIx is set correctly based on hardware limitations.
384+
*/
385+
if (divselxspi == 4 && (clkt->div != 8 && clkt->div != 16))
386+
continue;
387+
388+
now = DIV_ROUND_UP_ULL(parent_rate, clkt->div);
389+
if (abs(rate - now) < abs(rate - best)) {
390+
div = clkt->div;
391+
best = now;
392+
req->best_parent_rate = parent_rate;
393+
}
394+
}
395+
396+
if (!div) {
397+
req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw, 1);
398+
divselxspi = r9a09g077_cpg_fselxspi_get_divider(req->best_parent_hw,
399+
req->best_parent_rate,
400+
num_parents);
401+
/* default to divider 3 which will result DIVSELXSPIn = 800 MHz */
402+
if (!divselxspi)
403+
divselxspi = 3;
404+
405+
/*
406+
* Use the maximum divider based on the parent clock rate:
407+
* - 64 when DIVSELXSPIx is 800 MHz (divider = 3)
408+
* - 16 when DIVSELXSPIx is 600 MHz (divider = 4)
409+
*/
410+
div = divselxspi == 3 ? 64 : 16;
411+
}
412+
413+
req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div);
414+
415+
return 0;
416+
}
417+
418+
static struct clk * __init
419+
r9a09g077_cpg_fselxspi_div_clk_register(struct device *dev,
420+
const struct cpg_core_clk *core,
421+
void __iomem *addr,
422+
struct cpg_mssr_pub *pub)
423+
{
424+
static struct clk_ops *xspi_div_ops;
425+
struct clk_init_data init = {};
426+
const struct clk *parent;
427+
const char *parent_name;
428+
struct clk_divider *div;
429+
struct clk_hw *hw;
430+
int ret;
431+
432+
parent = pub->clks[core->parent];
433+
if (IS_ERR(parent))
434+
return ERR_CAST(parent);
435+
436+
div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
437+
if (!div)
438+
return ERR_PTR(-ENOMEM);
439+
440+
if (!xspi_div_ops) {
441+
xspi_div_ops = devm_kzalloc(dev, sizeof(*xspi_div_ops), GFP_KERNEL);
442+
if (!xspi_div_ops)
443+
return ERR_PTR(-ENOMEM);
444+
memcpy(xspi_div_ops, &clk_divider_ops,
445+
sizeof(const struct clk_ops));
446+
xspi_div_ops->determine_rate = r9a09g077_cpg_fselxspi_determine_rate;
447+
}
448+
449+
parent_name = __clk_get_name(parent);
450+
init.name = core->name;
451+
init.ops = xspi_div_ops;
452+
init.flags = CLK_SET_RATE_PARENT;
453+
init.parent_names = &parent_name;
454+
init.num_parents = 1;
455+
456+
div->reg = addr;
457+
div->shift = GET_SHIFT(core->conf);
458+
div->width = GET_WIDTH(core->conf);
459+
div->flags = core->flag;
460+
div->lock = &pub->rmw_lock;
461+
div->hw.init = &init;
462+
div->table = core->dtable;
463+
464+
hw = &div->hw;
465+
ret = devm_clk_hw_register(dev, hw);
466+
if (ret)
467+
return ERR_PTR(ret);
468+
469+
return hw->clk;
470+
}
471+
287472
static struct clk * __init
288473
r9a09g077_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core,
289474
const struct cpg_mssr_info *info,
@@ -298,6 +483,8 @@ r9a09g077_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core,
298483
return r9a09g077_cpg_div_clk_register(dev, core, addr, pub);
299484
case CLK_TYPE_RZT2H_MUX:
300485
return r9a09g077_cpg_mux_clk_register(dev, core, addr, pub);
486+
case CLK_TYPE_RZT2H_FSELXSPI:
487+
return r9a09g077_cpg_fselxspi_div_clk_register(dev, core, addr, pub);
301488
default:
302489
return ERR_PTR(-EINVAL);
303490
}

0 commit comments

Comments
 (0)