1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * R7S9210 Clock Pulse Generator / Module Standby |
4 | * |
5 | * Based on r8a7795-cpg-mssr.c |
6 | * |
7 | * Copyright (C) 2018 Chris Brandt |
8 | * Copyright (C) 2018 Renesas Electronics Corp. |
9 | * |
10 | */ |
11 | |
12 | #include <linux/clk.h> |
13 | #include <linux/clk-provider.h> |
14 | #include <linux/io.h> |
15 | #include <dt-bindings/clock/r7s9210-cpg-mssr.h> |
16 | #include "renesas-cpg-mssr.h" |
17 | |
18 | #define CPG_FRQCR 0x00 |
19 | |
20 | static u8 cpg_mode; |
21 | |
22 | /* Internal Clock ratio table */ |
23 | static const struct { |
24 | unsigned int i; |
25 | unsigned int g; |
26 | unsigned int b; |
27 | unsigned int p1; |
28 | /* p0 is always 32 */; |
29 | } ratio_tab[5] = { /* I, G, B, P1 */ |
30 | { 2, 4, 8, 16}, /* FRQCR = 0x012 */ |
31 | { 4, 4, 8, 16}, /* FRQCR = 0x112 */ |
32 | { 8, 4, 8, 16}, /* FRQCR = 0x212 */ |
33 | { 16, 8, 16, 16}, /* FRQCR = 0x322 */ |
34 | { 16, 16, 32, 32}, /* FRQCR = 0x333 */ |
35 | }; |
36 | |
37 | enum rz_clk_types { |
38 | CLK_TYPE_RZA_MAIN = CLK_TYPE_CUSTOM, |
39 | CLK_TYPE_RZA_PLL, |
40 | }; |
41 | |
42 | enum clk_ids { |
43 | /* Core Clock Outputs exported to DT */ |
44 | LAST_DT_CORE_CLK = R7S9210_CLK_P0, |
45 | |
46 | /* External Input Clocks */ |
47 | CLK_EXTAL, |
48 | |
49 | /* Internal Core Clocks */ |
50 | CLK_MAIN, |
51 | CLK_PLL, |
52 | |
53 | /* Module Clocks */ |
54 | MOD_CLK_BASE |
55 | }; |
56 | |
57 | static struct cpg_core_clk r7s9210_early_core_clks[] = { |
58 | /* External Clock Inputs */ |
59 | DEF_INPUT("extal" , CLK_EXTAL), |
60 | |
61 | /* Internal Core Clocks */ |
62 | DEF_BASE(".main" , CLK_MAIN, CLK_TYPE_RZA_MAIN, CLK_EXTAL), |
63 | DEF_BASE(".pll" , CLK_PLL, CLK_TYPE_RZA_PLL, CLK_MAIN), |
64 | |
65 | /* Core Clock Outputs */ |
66 | DEF_FIXED("p1c" , R7S9210_CLK_P1C, CLK_PLL, 16, 1), |
67 | }; |
68 | |
69 | static const struct mssr_mod_clk r7s9210_early_mod_clks[] __initconst = { |
70 | DEF_MOD_STB("ostm2" , 34, R7S9210_CLK_P1C), |
71 | DEF_MOD_STB("ostm1" , 35, R7S9210_CLK_P1C), |
72 | DEF_MOD_STB("ostm0" , 36, R7S9210_CLK_P1C), |
73 | }; |
74 | |
75 | static struct cpg_core_clk r7s9210_core_clks[] = { |
76 | /* Core Clock Outputs */ |
77 | DEF_FIXED("i" , R7S9210_CLK_I, CLK_PLL, 2, 1), |
78 | DEF_FIXED("g" , R7S9210_CLK_G, CLK_PLL, 4, 1), |
79 | DEF_FIXED("b" , R7S9210_CLK_B, CLK_PLL, 8, 1), |
80 | DEF_FIXED("p1" , R7S9210_CLK_P1, CLK_PLL, 16, 1), |
81 | DEF_FIXED("p0" , R7S9210_CLK_P0, CLK_PLL, 32, 1), |
82 | }; |
83 | |
84 | static const struct mssr_mod_clk r7s9210_mod_clks[] __initconst = { |
85 | DEF_MOD_STB("scif4" , 43, R7S9210_CLK_P1C), |
86 | DEF_MOD_STB("scif3" , 44, R7S9210_CLK_P1C), |
87 | DEF_MOD_STB("scif2" , 45, R7S9210_CLK_P1C), |
88 | DEF_MOD_STB("scif1" , 46, R7S9210_CLK_P1C), |
89 | DEF_MOD_STB("scif0" , 47, R7S9210_CLK_P1C), |
90 | |
91 | DEF_MOD_STB("usb1" , 60, R7S9210_CLK_B), |
92 | DEF_MOD_STB("usb0" , 61, R7S9210_CLK_B), |
93 | DEF_MOD_STB("ether1" , 64, R7S9210_CLK_B), |
94 | DEF_MOD_STB("ether0" , 65, R7S9210_CLK_B), |
95 | |
96 | DEF_MOD_STB("spibsc" , 83, R7S9210_CLK_P1), |
97 | DEF_MOD_STB("i2c3" , 84, R7S9210_CLK_P1), |
98 | DEF_MOD_STB("i2c2" , 85, R7S9210_CLK_P1), |
99 | DEF_MOD_STB("i2c1" , 86, R7S9210_CLK_P1), |
100 | DEF_MOD_STB("i2c0" , 87, R7S9210_CLK_P1), |
101 | |
102 | DEF_MOD_STB("spi2" , 95, R7S9210_CLK_P1), |
103 | DEF_MOD_STB("spi1" , 96, R7S9210_CLK_P1), |
104 | DEF_MOD_STB("spi0" , 97, R7S9210_CLK_P1), |
105 | |
106 | DEF_MOD_STB("sdhi11" , 100, R7S9210_CLK_B), |
107 | DEF_MOD_STB("sdhi10" , 101, R7S9210_CLK_B), |
108 | DEF_MOD_STB("sdhi01" , 102, R7S9210_CLK_B), |
109 | DEF_MOD_STB("sdhi00" , 103, R7S9210_CLK_B), |
110 | }; |
111 | |
112 | /* The clock dividers in the table vary based on DT and register settings */ |
113 | static void __init r7s9210_update_clk_table(struct clk *extal_clk, |
114 | void __iomem *base) |
115 | { |
116 | int i; |
117 | u16 frqcr; |
118 | u8 index; |
119 | |
120 | /* If EXTAL is above 12MHz, then we know it is Mode 1 */ |
121 | if (clk_get_rate(clk: extal_clk) > 12000000) |
122 | cpg_mode = 1; |
123 | |
124 | frqcr = readl(addr: base + CPG_FRQCR) & 0xFFF; |
125 | if (frqcr == 0x012) |
126 | index = 0; |
127 | else if (frqcr == 0x112) |
128 | index = 1; |
129 | else if (frqcr == 0x212) |
130 | index = 2; |
131 | else if (frqcr == 0x322) |
132 | index = 3; |
133 | else if (frqcr == 0x333) |
134 | index = 4; |
135 | else |
136 | BUG_ON(1); /* Illegal FRQCR value */ |
137 | |
138 | for (i = 0; i < ARRAY_SIZE(r7s9210_core_clks); i++) { |
139 | switch (r7s9210_core_clks[i].id) { |
140 | case R7S9210_CLK_I: |
141 | r7s9210_core_clks[i].div = ratio_tab[index].i; |
142 | break; |
143 | case R7S9210_CLK_G: |
144 | r7s9210_core_clks[i].div = ratio_tab[index].g; |
145 | break; |
146 | case R7S9210_CLK_B: |
147 | r7s9210_core_clks[i].div = ratio_tab[index].b; |
148 | break; |
149 | case R7S9210_CLK_P1: |
150 | case R7S9210_CLK_P1C: |
151 | r7s9210_core_clks[i].div = ratio_tab[index].p1; |
152 | break; |
153 | case R7S9210_CLK_P0: |
154 | r7s9210_core_clks[i].div = 32; |
155 | break; |
156 | } |
157 | } |
158 | } |
159 | |
160 | static struct clk * __init rza2_cpg_clk_register(struct device *dev, |
161 | const struct cpg_core_clk *core, const struct cpg_mssr_info *info, |
162 | struct clk **clks, void __iomem *base, |
163 | struct raw_notifier_head *notifiers) |
164 | { |
165 | struct clk *parent; |
166 | unsigned int mult = 1; |
167 | unsigned int div = 1; |
168 | |
169 | parent = clks[core->parent]; |
170 | if (IS_ERR(ptr: parent)) |
171 | return ERR_CAST(ptr: parent); |
172 | |
173 | switch (core->id) { |
174 | case CLK_MAIN: |
175 | break; |
176 | |
177 | case CLK_PLL: |
178 | if (cpg_mode) |
179 | mult = 44; /* Divider 1 is 1/2 */ |
180 | else |
181 | mult = 88; /* Divider 1 is 1 */ |
182 | break; |
183 | |
184 | default: |
185 | return ERR_PTR(error: -EINVAL); |
186 | } |
187 | |
188 | if (core->id == CLK_MAIN) |
189 | r7s9210_update_clk_table(extal_clk: parent, base); |
190 | |
191 | return clk_register_fixed_factor(NULL, name: core->name, |
192 | parent_name: __clk_get_name(clk: parent), flags: 0, mult, div); |
193 | } |
194 | |
195 | const struct cpg_mssr_info r7s9210_cpg_mssr_info __initconst = { |
196 | /* Early Clocks */ |
197 | .early_core_clks = r7s9210_early_core_clks, |
198 | .num_early_core_clks = ARRAY_SIZE(r7s9210_early_core_clks), |
199 | .early_mod_clks = r7s9210_early_mod_clks, |
200 | .num_early_mod_clks = ARRAY_SIZE(r7s9210_early_mod_clks), |
201 | |
202 | /* Core Clocks */ |
203 | .core_clks = r7s9210_core_clks, |
204 | .num_core_clks = ARRAY_SIZE(r7s9210_core_clks), |
205 | .last_dt_core_clk = LAST_DT_CORE_CLK, |
206 | .num_total_core_clks = MOD_CLK_BASE, |
207 | |
208 | /* Module Clocks */ |
209 | .mod_clks = r7s9210_mod_clks, |
210 | .num_mod_clks = ARRAY_SIZE(r7s9210_mod_clks), |
211 | .num_hw_mod_clks = 11 * 32, /* includes STBCR0 which doesn't exist */ |
212 | |
213 | /* Callbacks */ |
214 | .cpg_clk_register = rza2_cpg_clk_register, |
215 | |
216 | /* RZ/A2 has Standby Control Registers */ |
217 | .reg_layout = CLK_REG_LAYOUT_RZ_A, |
218 | }; |
219 | |
220 | static void __init r7s9210_cpg_mssr_early_init(struct device_node *np) |
221 | { |
222 | cpg_mssr_early_init(np, info: &r7s9210_cpg_mssr_info); |
223 | } |
224 | |
225 | CLK_OF_DECLARE_DRIVER(cpg_mstp_clks, "renesas,r7s9210-cpg-mssr" , |
226 | r7s9210_cpg_mssr_early_init); |
227 | |