2 * R-Car Gen3 Clock Pulse Generator
4 * Copyright (C) 2015-2016 Glider bvba
6 * Based on clk-rcar-gen3.c
8 * Copyright (C) 2015 Renesas Electronics Corp.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
15 #include <linux/bug.h>
16 #include <linux/clk.h>
17 #include <linux/clk-provider.h>
18 #include <linux/device.h>
19 #include <linux/err.h>
20 #include <linux/init.h>
23 #include <linux/slab.h>
24 #include <linux/sys_soc.h>
26 #include "renesas-cpg-mssr.h"
27 #include "rcar-gen3-cpg.h"
29 #define CPG_PLL0CR 0x00d8
30 #define CPG_PLL2CR 0x002c
31 #define CPG_PLL4CR 0x01f4
33 struct cpg_simple_notifier
{
34 struct notifier_block nb
;
39 static int cpg_simple_notifier_call(struct notifier_block
*nb
,
40 unsigned long action
, void *data
)
42 struct cpg_simple_notifier
*csn
=
43 container_of(nb
, struct cpg_simple_notifier
, nb
);
46 case PM_EVENT_SUSPEND
:
47 csn
->saved
= readl(csn
->reg
);
51 writel(csn
->saved
, csn
->reg
);
57 static void cpg_simple_notifier_register(struct raw_notifier_head
*notifiers
,
58 struct cpg_simple_notifier
*csn
)
60 csn
->nb
.notifier_call
= cpg_simple_notifier_call
;
61 raw_notifier_chain_register(notifiers
, &csn
->nb
);
67 #define CPG_SD_STP_HCK BIT(9)
68 #define CPG_SD_STP_CK BIT(8)
70 #define CPG_SD_STP_MASK (CPG_SD_STP_HCK | CPG_SD_STP_CK)
71 #define CPG_SD_FC_MASK (0x7 << 2 | 0x3 << 0)
73 #define CPG_SD_DIV_TABLE_DATA(stp_hck, stp_ck, sd_srcfc, sd_fc, sd_div) \
75 .val = ((stp_hck) ? CPG_SD_STP_HCK : 0) | \
76 ((stp_ck) ? CPG_SD_STP_CK : 0) | \
89 const struct sd_div_table
*div_table
;
90 struct cpg_simple_notifier csn
;
94 unsigned int cur_div_idx
;
99 * stp_hck stp_ck (div) (div) = sd_srcfc x sd_fc
100 *-------------------------------------------------------------------
105 * 1 0 4 (16) 1 (4) 64
110 * 1 0 4 (16) 0 (2) 32
112 static const struct sd_div_table cpg_sd_div_table
[] = {
113 /* CPG_SD_DIV_TABLE_DATA(stp_hck, stp_ck, sd_srcfc, sd_fc, sd_div) */
114 CPG_SD_DIV_TABLE_DATA(0, 0, 0, 1, 4),
115 CPG_SD_DIV_TABLE_DATA(0, 0, 1, 1, 8),
116 CPG_SD_DIV_TABLE_DATA(1, 0, 2, 1, 16),
117 CPG_SD_DIV_TABLE_DATA(1, 0, 3, 1, 32),
118 CPG_SD_DIV_TABLE_DATA(1, 0, 4, 1, 64),
119 CPG_SD_DIV_TABLE_DATA(0, 0, 0, 0, 2),
120 CPG_SD_DIV_TABLE_DATA(0, 0, 1, 0, 4),
121 CPG_SD_DIV_TABLE_DATA(1, 0, 2, 0, 8),
122 CPG_SD_DIV_TABLE_DATA(1, 0, 3, 0, 16),
123 CPG_SD_DIV_TABLE_DATA(1, 0, 4, 0, 32),
126 #define to_sd_clock(_hw) container_of(_hw, struct sd_clock, hw)
128 static int cpg_sd_clock_enable(struct clk_hw
*hw
)
130 struct sd_clock
*clock
= to_sd_clock(hw
);
131 u32 val
= readl(clock
->csn
.reg
);
133 val
&= ~(CPG_SD_STP_MASK
);
134 val
|= clock
->div_table
[clock
->cur_div_idx
].val
& CPG_SD_STP_MASK
;
136 writel(val
, clock
->csn
.reg
);
141 static void cpg_sd_clock_disable(struct clk_hw
*hw
)
143 struct sd_clock
*clock
= to_sd_clock(hw
);
145 writel(readl(clock
->csn
.reg
) | CPG_SD_STP_MASK
, clock
->csn
.reg
);
148 static int cpg_sd_clock_is_enabled(struct clk_hw
*hw
)
150 struct sd_clock
*clock
= to_sd_clock(hw
);
152 return !(readl(clock
->csn
.reg
) & CPG_SD_STP_MASK
);
155 static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw
*hw
,
156 unsigned long parent_rate
)
158 struct sd_clock
*clock
= to_sd_clock(hw
);
160 return DIV_ROUND_CLOSEST(parent_rate
,
161 clock
->div_table
[clock
->cur_div_idx
].div
);
164 static unsigned int cpg_sd_clock_calc_div(struct sd_clock
*clock
,
166 unsigned long parent_rate
)
173 div
= DIV_ROUND_CLOSEST(parent_rate
, rate
);
175 return clamp_t(unsigned int, div
, clock
->div_min
, clock
->div_max
);
178 static long cpg_sd_clock_round_rate(struct clk_hw
*hw
, unsigned long rate
,
179 unsigned long *parent_rate
)
181 struct sd_clock
*clock
= to_sd_clock(hw
);
182 unsigned int div
= cpg_sd_clock_calc_div(clock
, rate
, *parent_rate
);
184 return DIV_ROUND_CLOSEST(*parent_rate
, div
);
187 static int cpg_sd_clock_set_rate(struct clk_hw
*hw
, unsigned long rate
,
188 unsigned long parent_rate
)
190 struct sd_clock
*clock
= to_sd_clock(hw
);
191 unsigned int div
= cpg_sd_clock_calc_div(clock
, rate
, parent_rate
);
195 for (i
= 0; i
< clock
->div_num
; i
++)
196 if (div
== clock
->div_table
[i
].div
)
199 if (i
>= clock
->div_num
)
202 clock
->cur_div_idx
= i
;
204 val
= readl(clock
->csn
.reg
);
205 val
&= ~(CPG_SD_STP_MASK
| CPG_SD_FC_MASK
);
206 val
|= clock
->div_table
[i
].val
& (CPG_SD_STP_MASK
| CPG_SD_FC_MASK
);
207 writel(val
, clock
->csn
.reg
);
212 static const struct clk_ops cpg_sd_clock_ops
= {
213 .enable
= cpg_sd_clock_enable
,
214 .disable
= cpg_sd_clock_disable
,
215 .is_enabled
= cpg_sd_clock_is_enabled
,
216 .recalc_rate
= cpg_sd_clock_recalc_rate
,
217 .round_rate
= cpg_sd_clock_round_rate
,
218 .set_rate
= cpg_sd_clock_set_rate
,
221 static struct clk
* __init
cpg_sd_clk_register(const struct cpg_core_clk
*core
,
222 void __iomem
*base
, const char *parent_name
,
223 struct raw_notifier_head
*notifiers
)
225 struct clk_init_data init
;
226 struct sd_clock
*clock
;
231 clock
= kzalloc(sizeof(*clock
), GFP_KERNEL
);
233 return ERR_PTR(-ENOMEM
);
235 init
.name
= core
->name
;
236 init
.ops
= &cpg_sd_clock_ops
;
237 init
.flags
= CLK_IS_BASIC
| CLK_SET_RATE_PARENT
;
238 init
.parent_names
= &parent_name
;
239 init
.num_parents
= 1;
241 clock
->csn
.reg
= base
+ core
->offset
;
242 clock
->hw
.init
= &init
;
243 clock
->div_table
= cpg_sd_div_table
;
244 clock
->div_num
= ARRAY_SIZE(cpg_sd_div_table
);
246 sd_fc
= readl(clock
->csn
.reg
) & CPG_SD_FC_MASK
;
247 for (i
= 0; i
< clock
->div_num
; i
++)
248 if (sd_fc
== (clock
->div_table
[i
].val
& CPG_SD_FC_MASK
))
251 if (WARN_ON(i
>= clock
->div_num
)) {
253 return ERR_PTR(-EINVAL
);
256 clock
->cur_div_idx
= i
;
258 clock
->div_max
= clock
->div_table
[0].div
;
259 clock
->div_min
= clock
->div_max
;
260 for (i
= 1; i
< clock
->div_num
; i
++) {
261 clock
->div_max
= max(clock
->div_max
, clock
->div_table
[i
].div
);
262 clock
->div_min
= min(clock
->div_min
, clock
->div_table
[i
].div
);
265 clk
= clk_register(NULL
, &clock
->hw
);
269 cpg_simple_notifier_register(notifiers
, &clock
->csn
);
278 static const struct rcar_gen3_cpg_pll_config
*cpg_pll_config __initdata
;
279 static unsigned int cpg_clk_extalr __initdata
;
280 static u32 cpg_mode __initdata
;
281 static u32 cpg_quirks __initdata
;
283 #define PLL_ERRATA BIT(0) /* Missing PLL0/2/4 post-divider */
284 #define RCKCR_CKSEL BIT(1) /* Manual RCLK parent selection */
286 static const struct soc_device_attribute cpg_quirks_match
[] __initconst
= {
288 .soc_id
= "r8a7795", .revision
= "ES1.0",
289 .data
= (void *)(PLL_ERRATA
| RCKCR_CKSEL
),
292 .soc_id
= "r8a7795", .revision
= "ES1.*",
293 .data
= (void *)RCKCR_CKSEL
,
296 .soc_id
= "r8a7796", .revision
= "ES1.0",
297 .data
= (void *)RCKCR_CKSEL
,
302 struct clk
* __init
rcar_gen3_cpg_clk_register(struct device
*dev
,
303 const struct cpg_core_clk
*core
, const struct cpg_mssr_info
*info
,
304 struct clk
**clks
, void __iomem
*base
,
305 struct raw_notifier_head
*notifiers
)
307 const struct clk
*parent
;
308 unsigned int mult
= 1;
309 unsigned int div
= 1;
312 parent
= clks
[core
->parent
& 0xffff]; /* CLK_TYPE_PE uses high bits */
314 return ERR_CAST(parent
);
316 switch (core
->type
) {
317 case CLK_TYPE_GEN3_MAIN
:
318 div
= cpg_pll_config
->extal_div
;
321 case CLK_TYPE_GEN3_PLL0
:
323 * PLL0 is a configurable multiplier clock. Register it as a
324 * fixed factor clock for now as there's no generic multiplier
325 * clock implementation and we currently have no need to change
326 * the multiplier value.
328 value
= readl(base
+ CPG_PLL0CR
);
329 mult
= (((value
>> 24) & 0x7f) + 1) * 2;
330 if (cpg_quirks
& PLL_ERRATA
)
334 case CLK_TYPE_GEN3_PLL1
:
335 mult
= cpg_pll_config
->pll1_mult
;
336 div
= cpg_pll_config
->pll1_div
;
339 case CLK_TYPE_GEN3_PLL2
:
341 * PLL2 is a configurable multiplier clock. Register it as a
342 * fixed factor clock for now as there's no generic multiplier
343 * clock implementation and we currently have no need to change
344 * the multiplier value.
346 value
= readl(base
+ CPG_PLL2CR
);
347 mult
= (((value
>> 24) & 0x7f) + 1) * 2;
348 if (cpg_quirks
& PLL_ERRATA
)
352 case CLK_TYPE_GEN3_PLL3
:
353 mult
= cpg_pll_config
->pll3_mult
;
354 div
= cpg_pll_config
->pll3_div
;
357 case CLK_TYPE_GEN3_PLL4
:
359 * PLL4 is a configurable multiplier clock. Register it as a
360 * fixed factor clock for now as there's no generic multiplier
361 * clock implementation and we currently have no need to change
362 * the multiplier value.
364 value
= readl(base
+ CPG_PLL4CR
);
365 mult
= (((value
>> 24) & 0x7f) + 1) * 2;
366 if (cpg_quirks
& PLL_ERRATA
)
370 case CLK_TYPE_GEN3_SD
:
371 return cpg_sd_clk_register(core
, base
, __clk_get_name(parent
),
374 case CLK_TYPE_GEN3_R
:
375 if (cpg_quirks
& RCKCR_CKSEL
) {
376 struct cpg_simple_notifier
*csn
;
378 csn
= kzalloc(sizeof(*csn
), GFP_KERNEL
);
380 return ERR_PTR(-ENOMEM
);
382 csn
->reg
= base
+ CPG_RCKCR
;
386 * Only if EXTALR is populated, we switch to it.
388 value
= readl(csn
->reg
) & 0x3f;
390 if (clk_get_rate(clks
[cpg_clk_extalr
])) {
391 parent
= clks
[cpg_clk_extalr
];
395 writel(value
, csn
->reg
);
396 cpg_simple_notifier_register(notifiers
, csn
);
400 /* Select parent clock of RCLK by MD28 */
401 if (cpg_mode
& BIT(28))
402 parent
= clks
[cpg_clk_extalr
];
405 case CLK_TYPE_GEN3_PE
:
407 * Peripheral clock with a fixed divider, selectable between
408 * clean and spread spectrum parents using MD12
410 if (cpg_mode
& BIT(12)) {
412 div
= core
->div
& 0xffff;
415 parent
= clks
[core
->parent
>> 16];
417 return ERR_CAST(parent
);
418 div
= core
->div
>> 16;
424 return ERR_PTR(-EINVAL
);
427 return clk_register_fixed_factor(NULL
, core
->name
,
428 __clk_get_name(parent
), 0, mult
, div
);
431 int __init
rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config
*config
,
432 unsigned int clk_extalr
, u32 mode
)
434 const struct soc_device_attribute
*attr
;
436 cpg_pll_config
= config
;
437 cpg_clk_extalr
= clk_extalr
;
439 attr
= soc_device_match(cpg_quirks_match
);
441 cpg_quirks
= (uintptr_t)attr
->data
;
442 pr_debug("%s: mode = 0x%x quirks = 0x%x\n", __func__
, mode
, cpg_quirks
);