1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
4 * Author: Jian Hu <jian.hu@amlogic.com>
6 * Copyright (c) 2023, SberDevices. All Rights Reserved.
7 * Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
10 #include <linux/clk-provider.h>
11 #include <linux/mod_devicetable.h>
12 #include <linux/platform_device.h>
14 #include "clk-regmap.h"
15 #include "meson-clkc-utils.h"
17 #include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
19 static struct clk_regmap fixed_pll_dco
= {
20 .data
= &(struct meson_clk_pll_data
){
22 .reg_off
= ANACTRL_FIXPLL_CTRL0
,
27 .reg_off
= ANACTRL_FIXPLL_CTRL0
,
32 .reg_off
= ANACTRL_FIXPLL_CTRL0
,
37 .reg_off
= ANACTRL_FIXPLL_CTRL1
,
42 .reg_off
= ANACTRL_FIXPLL_STS
,
47 .reg_off
= ANACTRL_FIXPLL_CTRL0
,
52 .hw
.init
= &(struct clk_init_data
){
53 .name
= "fixed_pll_dco",
54 .ops
= &meson_clk_pll_ro_ops
,
55 .parent_data
= &(const struct clk_parent_data
) {
56 .fw_name
= "fixpll_in",
62 static struct clk_regmap fixed_pll
= {
63 .data
= &(struct clk_regmap_gate_data
){
64 .offset
= ANACTRL_FIXPLL_CTRL0
,
67 .hw
.init
= &(struct clk_init_data
) {
69 .ops
= &clk_regmap_gate_ops
,
70 .parent_hws
= (const struct clk_hw
*[]) {
77 static const struct pll_mult_range hifi_pll_mult_range
= {
82 static const struct reg_sequence hifi_init_regs
[] = {
83 { .reg
= ANACTRL_HIFIPLL_CTRL1
, .def
= 0x01800000 },
84 { .reg
= ANACTRL_HIFIPLL_CTRL2
, .def
= 0x00001100 },
85 { .reg
= ANACTRL_HIFIPLL_CTRL3
, .def
= 0x100a1100 },
86 { .reg
= ANACTRL_HIFIPLL_CTRL4
, .def
= 0x00302000 },
87 { .reg
= ANACTRL_HIFIPLL_CTRL0
, .def
= 0x01f18000 },
90 static struct clk_regmap hifi_pll
= {
91 .data
= &(struct meson_clk_pll_data
){
93 .reg_off
= ANACTRL_HIFIPLL_CTRL0
,
98 .reg_off
= ANACTRL_HIFIPLL_CTRL0
,
103 .reg_off
= ANACTRL_HIFIPLL_CTRL0
,
108 .reg_off
= ANACTRL_HIFIPLL_CTRL1
,
113 .reg_off
= ANACTRL_HIFIPLL_STS
,
118 .reg_off
= ANACTRL_HIFIPLL_CTRL0
,
123 .reg_off
= ANACTRL_HIFIPLL_CTRL2
,
127 .range
= &hifi_pll_mult_range
,
128 .init_regs
= hifi_init_regs
,
129 .init_count
= ARRAY_SIZE(hifi_init_regs
),
131 .hw
.init
= &(struct clk_init_data
){
133 .ops
= &meson_clk_pll_ops
,
134 .parent_data
= &(const struct clk_parent_data
) {
135 .fw_name
= "hifipll_in",
141 static struct clk_fixed_factor fclk_div2_div
= {
144 .hw
.init
= &(struct clk_init_data
){
145 .name
= "fclk_div2_div",
146 .ops
= &clk_fixed_factor_ops
,
147 .parent_hws
= (const struct clk_hw
*[]) {
154 static struct clk_regmap fclk_div2
= {
155 .data
= &(struct clk_regmap_gate_data
){
156 .offset
= ANACTRL_FIXPLL_CTRL0
,
159 .hw
.init
= &(struct clk_init_data
){
161 .ops
= &clk_regmap_gate_ops
,
162 .parent_hws
= (const struct clk_hw
*[]) {
167 * This clock is used by DDR clock in BL2 firmware
168 * and is required by the platform to operate correctly.
169 * Until the following condition are met, we need this clock to
170 * be marked as critical:
171 * a) Mark the clock used by a firmware resource, if possible
172 * b) CCF has a clock hand-off mechanism to make the sure the
173 * clock stays on until the proper driver comes along
175 .flags
= CLK_IS_CRITICAL
,
179 static struct clk_fixed_factor fclk_div3_div
= {
182 .hw
.init
= &(struct clk_init_data
){
183 .name
= "fclk_div3_div",
184 .ops
= &clk_fixed_factor_ops
,
185 .parent_hws
= (const struct clk_hw
*[]) {
192 static struct clk_regmap fclk_div3
= {
193 .data
= &(struct clk_regmap_gate_data
){
194 .offset
= ANACTRL_FIXPLL_CTRL0
,
197 .hw
.init
= &(struct clk_init_data
){
199 .ops
= &clk_regmap_gate_ops
,
200 .parent_hws
= (const struct clk_hw
*[]) {
205 * This clock is used by APB bus which is set in boot ROM code
206 * and is required by the platform to operate correctly.
208 .flags
= CLK_IS_CRITICAL
,
212 static struct clk_fixed_factor fclk_div5_div
= {
215 .hw
.init
= &(struct clk_init_data
){
216 .name
= "fclk_div5_div",
217 .ops
= &clk_fixed_factor_ops
,
218 .parent_hws
= (const struct clk_hw
*[]) {
225 static struct clk_regmap fclk_div5
= {
226 .data
= &(struct clk_regmap_gate_data
){
227 .offset
= ANACTRL_FIXPLL_CTRL0
,
230 .hw
.init
= &(struct clk_init_data
){
232 .ops
= &clk_regmap_gate_ops
,
233 .parent_hws
= (const struct clk_hw
*[]) {
238 * This clock is used by AXI bus which setted in Romcode
239 * and is required by the platform to operate correctly.
241 .flags
= CLK_IS_CRITICAL
,
245 static struct clk_fixed_factor fclk_div7_div
= {
248 .hw
.init
= &(struct clk_init_data
){
249 .name
= "fclk_div7_div",
250 .ops
= &clk_fixed_factor_ops
,
251 .parent_hws
= (const struct clk_hw
*[]) {
258 static struct clk_regmap fclk_div7
= {
259 .data
= &(struct clk_regmap_gate_data
){
260 .offset
= ANACTRL_FIXPLL_CTRL0
,
263 .hw
.init
= &(struct clk_init_data
){
265 .ops
= &clk_regmap_gate_ops
,
266 .parent_hws
= (const struct clk_hw
*[]) {
273 /* Array of all clocks registered by this provider */
274 static struct clk_hw
*a1_pll_hw_clks
[] = {
275 [CLKID_FIXED_PLL_DCO
] = &fixed_pll_dco
.hw
,
276 [CLKID_FIXED_PLL
] = &fixed_pll
.hw
,
277 [CLKID_FCLK_DIV2_DIV
] = &fclk_div2_div
.hw
,
278 [CLKID_FCLK_DIV3_DIV
] = &fclk_div3_div
.hw
,
279 [CLKID_FCLK_DIV5_DIV
] = &fclk_div5_div
.hw
,
280 [CLKID_FCLK_DIV7_DIV
] = &fclk_div7_div
.hw
,
281 [CLKID_FCLK_DIV2
] = &fclk_div2
.hw
,
282 [CLKID_FCLK_DIV3
] = &fclk_div3
.hw
,
283 [CLKID_FCLK_DIV5
] = &fclk_div5
.hw
,
284 [CLKID_FCLK_DIV7
] = &fclk_div7
.hw
,
285 [CLKID_HIFI_PLL
] = &hifi_pll
.hw
,
288 static struct clk_regmap
*const a1_pll_regmaps
[] = {
298 static const struct regmap_config a1_pll_regmap_cfg
= {
302 .max_register
= ANACTRL_HIFIPLL_STS
,
305 static struct meson_clk_hw_data a1_pll_clks
= {
306 .hws
= a1_pll_hw_clks
,
307 .num
= ARRAY_SIZE(a1_pll_hw_clks
),
310 static int meson_a1_pll_probe(struct platform_device
*pdev
)
312 struct device
*dev
= &pdev
->dev
;
317 base
= devm_platform_ioremap_resource(pdev
, 0);
319 return dev_err_probe(dev
, PTR_ERR(base
),
320 "can't ioremap resource\n");
322 map
= devm_regmap_init_mmio(dev
, base
, &a1_pll_regmap_cfg
);
324 return dev_err_probe(dev
, PTR_ERR(map
),
325 "can't init regmap mmio region\n");
327 /* Populate regmap for the regmap backed clocks */
328 for (i
= 0; i
< ARRAY_SIZE(a1_pll_regmaps
); i
++)
329 a1_pll_regmaps
[i
]->map
= map
;
331 /* Register clocks */
332 for (clkid
= 0; clkid
< a1_pll_clks
.num
; clkid
++) {
333 err
= devm_clk_hw_register(dev
, a1_pll_clks
.hws
[clkid
]);
335 return dev_err_probe(dev
, err
,
336 "clock[%d] registration failed\n",
340 return devm_of_clk_add_hw_provider(dev
, meson_clk_hw_get
,
344 static const struct of_device_id a1_pll_clkc_match_table
[] = {
345 { .compatible
= "amlogic,a1-pll-clkc", },
348 MODULE_DEVICE_TABLE(of
, a1_pll_clkc_match_table
);
350 static struct platform_driver a1_pll_clkc_driver
= {
351 .probe
= meson_a1_pll_probe
,
353 .name
= "a1-pll-clkc",
354 .of_match_table
= a1_pll_clkc_match_table
,
357 module_platform_driver(a1_pll_clkc_driver
);
359 MODULE_DESCRIPTION("Amlogic S4 PLL Clock Controller driver");
360 MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
361 MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>");
362 MODULE_LICENSE("GPL");
363 MODULE_IMPORT_NS("CLK_MESON");