xref: /linux/drivers/clk/meson/a1-pll.c (revision 522ba450b56fff29f868b1552bdc2965f55de7ed)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
4  * Author: Jian Hu <jian.hu@amlogic.com>
5  *
6  * Copyright (c) 2023, SberDevices. All Rights Reserved.
7  * Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
8  */
9 
10 #include <linux/clk-provider.h>
11 #include <linux/mod_devicetable.h>
12 #include <linux/platform_device.h>
13 #include "clk-pll.h"
14 #include "clk-regmap.h"
15 #include "meson-clkc-utils.h"
16 
17 #define ANACTRL_FIXPLL_CTRL0	0x0
18 #define ANACTRL_FIXPLL_CTRL1	0x4
19 #define ANACTRL_FIXPLL_STS	0x14
20 #define ANACTRL_HIFIPLL_CTRL0	0xc0
21 #define ANACTRL_HIFIPLL_CTRL1	0xc4
22 #define ANACTRL_HIFIPLL_CTRL2	0xc8
23 #define ANACTRL_HIFIPLL_CTRL3	0xcc
24 #define ANACTRL_HIFIPLL_CTRL4	0xd0
25 #define ANACTRL_HIFIPLL_STS	0xd4
26 
27 #include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
28 
29 static struct clk_regmap a1_fixed_pll_dco = {
30 	.data = &(struct meson_clk_pll_data){
31 		.en = {
32 			.reg_off = ANACTRL_FIXPLL_CTRL0,
33 			.shift   = 28,
34 			.width   = 1,
35 		},
36 		.m = {
37 			.reg_off = ANACTRL_FIXPLL_CTRL0,
38 			.shift   = 0,
39 			.width   = 8,
40 		},
41 		.n = {
42 			.reg_off = ANACTRL_FIXPLL_CTRL0,
43 			.shift   = 10,
44 			.width   = 5,
45 		},
46 		.frac = {
47 			.reg_off = ANACTRL_FIXPLL_CTRL1,
48 			.shift   = 0,
49 			.width   = 19,
50 		},
51 		.l = {
52 			.reg_off = ANACTRL_FIXPLL_STS,
53 			.shift   = 31,
54 			.width   = 1,
55 		},
56 		.rst = {
57 			.reg_off = ANACTRL_FIXPLL_CTRL0,
58 			.shift   = 29,
59 			.width   = 1,
60 		},
61 	},
62 	.hw.init = &(struct clk_init_data){
63 		.name = "fixed_pll_dco",
64 		.ops = &meson_clk_pll_ro_ops,
65 		.parent_data = &(const struct clk_parent_data) {
66 			.fw_name = "fixpll_in",
67 		},
68 		.num_parents = 1,
69 	},
70 };
71 
72 static struct clk_regmap a1_fixed_pll = {
73 	.data = &(struct clk_regmap_gate_data){
74 		.offset = ANACTRL_FIXPLL_CTRL0,
75 		.bit_idx = 20,
76 	},
77 	.hw.init = &(struct clk_init_data) {
78 		.name = "fixed_pll",
79 		.ops = &clk_regmap_gate_ops,
80 		.parent_hws = (const struct clk_hw *[]) {
81 			&a1_fixed_pll_dco.hw
82 		},
83 		.num_parents = 1,
84 	},
85 };
86 
87 static const struct pll_mult_range a1_hifi_pll_range = {
88 	.min = 32,
89 	.max = 64,
90 };
91 
92 static const struct reg_sequence a1_hifi_pll_init_regs[] = {
93 	{ .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x01800000 },
94 	{ .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00001100 },
95 	{ .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x100a1100 },
96 	{ .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x00302000 },
97 	{ .reg = ANACTRL_HIFIPLL_CTRL0, .def = 0x01f18000 },
98 };
99 
100 static struct clk_regmap a1_hifi_pll = {
101 	.data = &(struct meson_clk_pll_data){
102 		.en = {
103 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
104 			.shift   = 28,
105 			.width   = 1,
106 		},
107 		.m = {
108 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
109 			.shift   = 0,
110 			.width   = 8,
111 		},
112 		.n = {
113 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
114 			.shift   = 10,
115 			.width   = 5,
116 		},
117 		.frac = {
118 			.reg_off = ANACTRL_HIFIPLL_CTRL1,
119 			.shift   = 0,
120 			.width   = 19,
121 		},
122 		.l = {
123 			.reg_off = ANACTRL_HIFIPLL_STS,
124 			.shift   = 31,
125 			.width   = 1,
126 		},
127 		.current_en = {
128 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
129 			.shift   = 26,
130 			.width   = 1,
131 		},
132 		.l_detect = {
133 			.reg_off = ANACTRL_HIFIPLL_CTRL2,
134 			.shift   = 6,
135 			.width   = 1,
136 		},
137 		.range = &a1_hifi_pll_range,
138 		.init_regs = a1_hifi_pll_init_regs,
139 		.init_count = ARRAY_SIZE(a1_hifi_pll_init_regs),
140 	},
141 	.hw.init = &(struct clk_init_data){
142 		.name = "hifi_pll",
143 		.ops = &meson_clk_pll_ops,
144 		.parent_data = &(const struct clk_parent_data) {
145 			.fw_name = "hifipll_in",
146 		},
147 		.num_parents = 1,
148 	},
149 };
150 
151 static struct clk_fixed_factor a1_fclk_div2_div = {
152 	.mult = 1,
153 	.div = 2,
154 	.hw.init = &(struct clk_init_data){
155 		.name = "fclk_div2_div",
156 		.ops = &clk_fixed_factor_ops,
157 		.parent_hws = (const struct clk_hw *[]) {
158 			&a1_fixed_pll.hw
159 		},
160 		.num_parents = 1,
161 	},
162 };
163 
164 static struct clk_regmap a1_fclk_div2 = {
165 	.data = &(struct clk_regmap_gate_data){
166 		.offset = ANACTRL_FIXPLL_CTRL0,
167 		.bit_idx = 21,
168 	},
169 	.hw.init = &(struct clk_init_data){
170 		.name = "fclk_div2",
171 		.ops = &clk_regmap_gate_ops,
172 		.parent_hws = (const struct clk_hw *[]) {
173 			&a1_fclk_div2_div.hw
174 		},
175 		.num_parents = 1,
176 		/*
177 		 * This clock is used by DDR clock in BL2 firmware
178 		 * and is required by the platform to operate correctly.
179 		 * Until the following condition are met, we need this clock to
180 		 * be marked as critical:
181 		 * a) Mark the clock used by a firmware resource, if possible
182 		 * b) CCF has a clock hand-off mechanism to make the sure the
183 		 *    clock stays on until the proper driver comes along
184 		 */
185 		.flags = CLK_IS_CRITICAL,
186 	},
187 };
188 
189 static struct clk_fixed_factor a1_fclk_div3_div = {
190 	.mult = 1,
191 	.div = 3,
192 	.hw.init = &(struct clk_init_data){
193 		.name = "fclk_div3_div",
194 		.ops = &clk_fixed_factor_ops,
195 		.parent_hws = (const struct clk_hw *[]) {
196 			&a1_fixed_pll.hw
197 		},
198 		.num_parents = 1,
199 	},
200 };
201 
202 static struct clk_regmap a1_fclk_div3 = {
203 	.data = &(struct clk_regmap_gate_data){
204 		.offset = ANACTRL_FIXPLL_CTRL0,
205 		.bit_idx = 22,
206 	},
207 	.hw.init = &(struct clk_init_data){
208 		.name = "fclk_div3",
209 		.ops = &clk_regmap_gate_ops,
210 		.parent_hws = (const struct clk_hw *[]) {
211 			&a1_fclk_div3_div.hw
212 		},
213 		.num_parents = 1,
214 		/*
215 		 * This clock is used by APB bus which is set in boot ROM code
216 		 * and is required by the platform to operate correctly.
217 		 */
218 		.flags = CLK_IS_CRITICAL,
219 	},
220 };
221 
222 static struct clk_fixed_factor a1_fclk_div5_div = {
223 	.mult = 1,
224 	.div = 5,
225 	.hw.init = &(struct clk_init_data){
226 		.name = "fclk_div5_div",
227 		.ops = &clk_fixed_factor_ops,
228 		.parent_hws = (const struct clk_hw *[]) {
229 			&a1_fixed_pll.hw
230 		},
231 		.num_parents = 1,
232 	},
233 };
234 
235 static struct clk_regmap a1_fclk_div5 = {
236 	.data = &(struct clk_regmap_gate_data){
237 		.offset = ANACTRL_FIXPLL_CTRL0,
238 		.bit_idx = 23,
239 	},
240 	.hw.init = &(struct clk_init_data){
241 		.name = "fclk_div5",
242 		.ops = &clk_regmap_gate_ops,
243 		.parent_hws = (const struct clk_hw *[]) {
244 			&a1_fclk_div5_div.hw
245 		},
246 		.num_parents = 1,
247 		/*
248 		 * This clock is used by AXI bus which setted in Romcode
249 		 * and is required by the platform to operate correctly.
250 		 */
251 		.flags = CLK_IS_CRITICAL,
252 	},
253 };
254 
255 static struct clk_fixed_factor a1_fclk_div7_div = {
256 	.mult = 1,
257 	.div = 7,
258 	.hw.init = &(struct clk_init_data){
259 		.name = "fclk_div7_div",
260 		.ops = &clk_fixed_factor_ops,
261 		.parent_hws = (const struct clk_hw *[]) {
262 			&a1_fixed_pll.hw
263 		},
264 		.num_parents = 1,
265 	},
266 };
267 
268 static struct clk_regmap a1_fclk_div7 = {
269 	.data = &(struct clk_regmap_gate_data){
270 		.offset = ANACTRL_FIXPLL_CTRL0,
271 		.bit_idx = 24,
272 	},
273 	.hw.init = &(struct clk_init_data){
274 		.name = "fclk_div7",
275 		.ops = &clk_regmap_gate_ops,
276 		.parent_hws = (const struct clk_hw *[]) {
277 			&a1_fclk_div7_div.hw
278 		},
279 		.num_parents = 1,
280 	},
281 };
282 
283 /* Array of all clocks registered by this provider */
284 static struct clk_hw *a1_pll_hw_clks[] = {
285 	[CLKID_FIXED_PLL_DCO]	= &a1_fixed_pll_dco.hw,
286 	[CLKID_FIXED_PLL]	= &a1_fixed_pll.hw,
287 	[CLKID_FCLK_DIV2_DIV]	= &a1_fclk_div2_div.hw,
288 	[CLKID_FCLK_DIV3_DIV]	= &a1_fclk_div3_div.hw,
289 	[CLKID_FCLK_DIV5_DIV]	= &a1_fclk_div5_div.hw,
290 	[CLKID_FCLK_DIV7_DIV]	= &a1_fclk_div7_div.hw,
291 	[CLKID_FCLK_DIV2]	= &a1_fclk_div2.hw,
292 	[CLKID_FCLK_DIV3]	= &a1_fclk_div3.hw,
293 	[CLKID_FCLK_DIV5]	= &a1_fclk_div5.hw,
294 	[CLKID_FCLK_DIV7]	= &a1_fclk_div7.hw,
295 	[CLKID_HIFI_PLL]	= &a1_hifi_pll.hw,
296 };
297 
298 static const struct meson_clkc_data a1_pll_clkc_data = {
299 	.hw_clks = {
300 		.hws = a1_pll_hw_clks,
301 		.num = ARRAY_SIZE(a1_pll_hw_clks),
302 	},
303 };
304 
305 static const struct of_device_id a1_pll_clkc_match_table[] = {
306 	{
307 		.compatible = "amlogic,a1-pll-clkc",
308 		.data = &a1_pll_clkc_data,
309 	},
310 	{}
311 };
312 MODULE_DEVICE_TABLE(of, a1_pll_clkc_match_table);
313 
314 static struct platform_driver a1_pll_clkc_driver = {
315 	.probe = meson_clkc_mmio_probe,
316 	.driver = {
317 		.name = "a1-pll-clkc",
318 		.of_match_table = a1_pll_clkc_match_table,
319 	},
320 };
321 module_platform_driver(a1_pll_clkc_driver);
322 
323 MODULE_DESCRIPTION("Amlogic A1 PLL Clock Controller driver");
324 MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
325 MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>");
326 MODULE_LICENSE("GPL");
327 MODULE_IMPORT_NS("CLK_MESON");
328