Skip to content

Commit 28f3be5

Browse files
rockosovjbrun3t
authored andcommitted
clk: meson: a1: add Amlogic A1 PLL clock controller driver
Introduce PLL clock controller for Amlogic A1 SoC family. The clock unit is an APB slave module that is designed for generating all of the internal and system clocks. The SoC uses an external 24MHz crystal; there are 4 internal PLLs: SYS_PLL/HIFI_PLL/USB_PLL/(FIXPLL), these PLLs generate 27 clock sources. Signed-off-by: Jian Hu <[email protected]> Signed-off-by: Dmitry Rokosov <[email protected]> Reviewed-by: Martin Blumenstingl <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jerome Brunet <[email protected]>
1 parent b6ec400 commit 28f3be5

File tree

4 files changed

+414
-0
lines changed

4 files changed

+414
-0
lines changed

drivers/clk/meson/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ config COMMON_CLK_AXG_AUDIO
9999
Support for the audio clock controller on AmLogic A113D devices,
100100
aka axg, Say Y if you want audio subsystem to work.
101101

102+
config COMMON_CLK_A1_PLL
103+
tristate "Amlogic A1 SoC PLL controller support"
104+
depends on ARM64
105+
select COMMON_CLK_MESON_REGMAP
106+
select COMMON_CLK_MESON_PLL
107+
help
108+
Support for the PLL clock controller on Amlogic A113L based
109+
device, A1 SoC Family. Say Y if you want A1 PLL clock controller
110+
to work.
111+
102112
config COMMON_CLK_G12A
103113
tristate "G12 and SM1 SoC clock controllers support"
104114
depends on ARM64

drivers/clk/meson/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
1616

1717
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
1818
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
19+
obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
1920
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
2021
obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
2122
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o

drivers/clk/meson/a1-pll.c

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
4+
* Author: Jian Hu <[email protected]>
5+
*
6+
* Copyright (c) 2023, SberDevices. All Rights Reserved.
7+
* Author: Dmitry Rokosov <[email protected]>
8+
*/
9+
10+
#include <linux/clk-provider.h>
11+
#include <linux/of_device.h>
12+
#include <linux/platform_device.h>
13+
#include "a1-pll.h"
14+
#include "clk-regmap.h"
15+
16+
static struct clk_regmap fixed_pll_dco = {
17+
.data = &(struct meson_clk_pll_data){
18+
.en = {
19+
.reg_off = ANACTRL_FIXPLL_CTRL0,
20+
.shift = 28,
21+
.width = 1,
22+
},
23+
.m = {
24+
.reg_off = ANACTRL_FIXPLL_CTRL0,
25+
.shift = 0,
26+
.width = 8,
27+
},
28+
.n = {
29+
.reg_off = ANACTRL_FIXPLL_CTRL0,
30+
.shift = 10,
31+
.width = 5,
32+
},
33+
.frac = {
34+
.reg_off = ANACTRL_FIXPLL_CTRL1,
35+
.shift = 0,
36+
.width = 19,
37+
},
38+
.l = {
39+
.reg_off = ANACTRL_FIXPLL_STS,
40+
.shift = 31,
41+
.width = 1,
42+
},
43+
.rst = {
44+
.reg_off = ANACTRL_FIXPLL_CTRL0,
45+
.shift = 29,
46+
.width = 1,
47+
},
48+
},
49+
.hw.init = &(struct clk_init_data){
50+
.name = "fixed_pll_dco",
51+
.ops = &meson_clk_pll_ro_ops,
52+
.parent_data = &(const struct clk_parent_data) {
53+
.fw_name = "fixpll_in",
54+
},
55+
.num_parents = 1,
56+
},
57+
};
58+
59+
static struct clk_regmap fixed_pll = {
60+
.data = &(struct clk_regmap_gate_data){
61+
.offset = ANACTRL_FIXPLL_CTRL0,
62+
.bit_idx = 20,
63+
},
64+
.hw.init = &(struct clk_init_data) {
65+
.name = "fixed_pll",
66+
.ops = &clk_regmap_gate_ops,
67+
.parent_hws = (const struct clk_hw *[]) {
68+
&fixed_pll_dco.hw
69+
},
70+
.num_parents = 1,
71+
},
72+
};
73+
74+
static const struct pll_mult_range hifi_pll_mult_range = {
75+
.min = 32,
76+
.max = 64,
77+
};
78+
79+
static const struct reg_sequence hifi_init_regs[] = {
80+
{ .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x01800000 },
81+
{ .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00001100 },
82+
{ .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x100a1100 },
83+
{ .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x00302000 },
84+
{ .reg = ANACTRL_HIFIPLL_CTRL0, .def = 0x01f18000 },
85+
};
86+
87+
static struct clk_regmap hifi_pll = {
88+
.data = &(struct meson_clk_pll_data){
89+
.en = {
90+
.reg_off = ANACTRL_HIFIPLL_CTRL0,
91+
.shift = 28,
92+
.width = 1,
93+
},
94+
.m = {
95+
.reg_off = ANACTRL_HIFIPLL_CTRL0,
96+
.shift = 0,
97+
.width = 8,
98+
},
99+
.n = {
100+
.reg_off = ANACTRL_HIFIPLL_CTRL0,
101+
.shift = 10,
102+
.width = 5,
103+
},
104+
.frac = {
105+
.reg_off = ANACTRL_HIFIPLL_CTRL1,
106+
.shift = 0,
107+
.width = 19,
108+
},
109+
.l = {
110+
.reg_off = ANACTRL_HIFIPLL_STS,
111+
.shift = 31,
112+
.width = 1,
113+
},
114+
.current_en = {
115+
.reg_off = ANACTRL_HIFIPLL_CTRL0,
116+
.shift = 26,
117+
.width = 1,
118+
},
119+
.l_detect = {
120+
.reg_off = ANACTRL_HIFIPLL_CTRL2,
121+
.shift = 6,
122+
.width = 1,
123+
},
124+
.range = &hifi_pll_mult_range,
125+
.init_regs = hifi_init_regs,
126+
.init_count = ARRAY_SIZE(hifi_init_regs),
127+
},
128+
.hw.init = &(struct clk_init_data){
129+
.name = "hifi_pll",
130+
.ops = &meson_clk_pll_ops,
131+
.parent_data = &(const struct clk_parent_data) {
132+
.fw_name = "hifipll_in",
133+
},
134+
.num_parents = 1,
135+
},
136+
};
137+
138+
static struct clk_fixed_factor fclk_div2_div = {
139+
.mult = 1,
140+
.div = 2,
141+
.hw.init = &(struct clk_init_data){
142+
.name = "fclk_div2_div",
143+
.ops = &clk_fixed_factor_ops,
144+
.parent_hws = (const struct clk_hw *[]) {
145+
&fixed_pll.hw
146+
},
147+
.num_parents = 1,
148+
},
149+
};
150+
151+
static struct clk_regmap fclk_div2 = {
152+
.data = &(struct clk_regmap_gate_data){
153+
.offset = ANACTRL_FIXPLL_CTRL0,
154+
.bit_idx = 21,
155+
},
156+
.hw.init = &(struct clk_init_data){
157+
.name = "fclk_div2",
158+
.ops = &clk_regmap_gate_ops,
159+
.parent_hws = (const struct clk_hw *[]) {
160+
&fclk_div2_div.hw
161+
},
162+
.num_parents = 1,
163+
/*
164+
* This clock is used by DDR clock in BL2 firmware
165+
* and is required by the platform to operate correctly.
166+
* Until the following condition are met, we need this clock to
167+
* be marked as critical:
168+
* a) Mark the clock used by a firmware resource, if possible
169+
* b) CCF has a clock hand-off mechanism to make the sure the
170+
* clock stays on until the proper driver comes along
171+
*/
172+
.flags = CLK_IS_CRITICAL,
173+
},
174+
};
175+
176+
static struct clk_fixed_factor fclk_div3_div = {
177+
.mult = 1,
178+
.div = 3,
179+
.hw.init = &(struct clk_init_data){
180+
.name = "fclk_div3_div",
181+
.ops = &clk_fixed_factor_ops,
182+
.parent_hws = (const struct clk_hw *[]) {
183+
&fixed_pll.hw
184+
},
185+
.num_parents = 1,
186+
},
187+
};
188+
189+
static struct clk_regmap fclk_div3 = {
190+
.data = &(struct clk_regmap_gate_data){
191+
.offset = ANACTRL_FIXPLL_CTRL0,
192+
.bit_idx = 22,
193+
},
194+
.hw.init = &(struct clk_init_data){
195+
.name = "fclk_div3",
196+
.ops = &clk_regmap_gate_ops,
197+
.parent_hws = (const struct clk_hw *[]) {
198+
&fclk_div3_div.hw
199+
},
200+
.num_parents = 1,
201+
/*
202+
* This clock is used by APB bus which is set in boot ROM code
203+
* and is required by the platform to operate correctly.
204+
*/
205+
.flags = CLK_IS_CRITICAL,
206+
},
207+
};
208+
209+
static struct clk_fixed_factor fclk_div5_div = {
210+
.mult = 1,
211+
.div = 5,
212+
.hw.init = &(struct clk_init_data){
213+
.name = "fclk_div5_div",
214+
.ops = &clk_fixed_factor_ops,
215+
.parent_hws = (const struct clk_hw *[]) {
216+
&fixed_pll.hw
217+
},
218+
.num_parents = 1,
219+
},
220+
};
221+
222+
static struct clk_regmap fclk_div5 = {
223+
.data = &(struct clk_regmap_gate_data){
224+
.offset = ANACTRL_FIXPLL_CTRL0,
225+
.bit_idx = 23,
226+
},
227+
.hw.init = &(struct clk_init_data){
228+
.name = "fclk_div5",
229+
.ops = &clk_regmap_gate_ops,
230+
.parent_hws = (const struct clk_hw *[]) {
231+
&fclk_div5_div.hw
232+
},
233+
.num_parents = 1,
234+
/*
235+
* This clock is used by AXI bus which setted in Romcode
236+
* and is required by the platform to operate correctly.
237+
*/
238+
.flags = CLK_IS_CRITICAL,
239+
},
240+
};
241+
242+
static struct clk_fixed_factor fclk_div7_div = {
243+
.mult = 1,
244+
.div = 7,
245+
.hw.init = &(struct clk_init_data){
246+
.name = "fclk_div7_div",
247+
.ops = &clk_fixed_factor_ops,
248+
.parent_hws = (const struct clk_hw *[]) {
249+
&fixed_pll.hw
250+
},
251+
.num_parents = 1,
252+
},
253+
};
254+
255+
static struct clk_regmap fclk_div7 = {
256+
.data = &(struct clk_regmap_gate_data){
257+
.offset = ANACTRL_FIXPLL_CTRL0,
258+
.bit_idx = 24,
259+
},
260+
.hw.init = &(struct clk_init_data){
261+
.name = "fclk_div7",
262+
.ops = &clk_regmap_gate_ops,
263+
.parent_hws = (const struct clk_hw *[]) {
264+
&fclk_div7_div.hw
265+
},
266+
.num_parents = 1,
267+
},
268+
};
269+
270+
/* Array of all clocks registered by this provider */
271+
static struct clk_hw_onecell_data a1_pll_clks = {
272+
.hws = {
273+
[CLKID_FIXED_PLL_DCO] = &fixed_pll_dco.hw,
274+
[CLKID_FIXED_PLL] = &fixed_pll.hw,
275+
[CLKID_FCLK_DIV2_DIV] = &fclk_div2_div.hw,
276+
[CLKID_FCLK_DIV3_DIV] = &fclk_div3_div.hw,
277+
[CLKID_FCLK_DIV5_DIV] = &fclk_div5_div.hw,
278+
[CLKID_FCLK_DIV7_DIV] = &fclk_div7_div.hw,
279+
[CLKID_FCLK_DIV2] = &fclk_div2.hw,
280+
[CLKID_FCLK_DIV3] = &fclk_div3.hw,
281+
[CLKID_FCLK_DIV5] = &fclk_div5.hw,
282+
[CLKID_FCLK_DIV7] = &fclk_div7.hw,
283+
[CLKID_HIFI_PLL] = &hifi_pll.hw,
284+
[NR_PLL_CLKS] = NULL,
285+
},
286+
.num = NR_PLL_CLKS,
287+
};
288+
289+
static struct clk_regmap *const a1_pll_regmaps[] = {
290+
&fixed_pll_dco,
291+
&fixed_pll,
292+
&fclk_div2,
293+
&fclk_div3,
294+
&fclk_div5,
295+
&fclk_div7,
296+
&hifi_pll,
297+
};
298+
299+
static struct regmap_config a1_pll_regmap_cfg = {
300+
.reg_bits = 32,
301+
.val_bits = 32,
302+
.reg_stride = 4,
303+
};
304+
305+
static int meson_a1_pll_probe(struct platform_device *pdev)
306+
{
307+
struct device *dev = &pdev->dev;
308+
void __iomem *base;
309+
struct regmap *map;
310+
int clkid, i, err;
311+
312+
base = devm_platform_ioremap_resource(pdev, 0);
313+
if (IS_ERR(base))
314+
return dev_err_probe(dev, PTR_ERR(base),
315+
"can't ioremap resource\n");
316+
317+
map = devm_regmap_init_mmio(dev, base, &a1_pll_regmap_cfg);
318+
if (IS_ERR(map))
319+
return dev_err_probe(dev, PTR_ERR(map),
320+
"can't init regmap mmio region\n");
321+
322+
/* Populate regmap for the regmap backed clocks */
323+
for (i = 0; i < ARRAY_SIZE(a1_pll_regmaps); i++)
324+
a1_pll_regmaps[i]->map = map;
325+
326+
/* Register clocks */
327+
for (clkid = 0; clkid < a1_pll_clks.num; clkid++) {
328+
err = devm_clk_hw_register(dev, a1_pll_clks.hws[clkid]);
329+
if (err)
330+
return dev_err_probe(dev, err,
331+
"clock[%d] registration failed\n",
332+
clkid);
333+
}
334+
335+
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
336+
&a1_pll_clks);
337+
}
338+
339+
static const struct of_device_id a1_pll_clkc_match_table[] = {
340+
{ .compatible = "amlogic,a1-pll-clkc", },
341+
{}
342+
};
343+
MODULE_DEVICE_TABLE(of, a1_pll_clkc_match_table);
344+
345+
static struct platform_driver a1_pll_clkc_driver = {
346+
.probe = meson_a1_pll_probe,
347+
.driver = {
348+
.name = "a1-pll-clkc",
349+
.of_match_table = a1_pll_clkc_match_table,
350+
},
351+
};
352+
353+
module_platform_driver(a1_pll_clkc_driver);
354+
MODULE_AUTHOR("Jian Hu <[email protected]>");
355+
MODULE_AUTHOR("Dmitry Rokosov <[email protected]>");
356+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)