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 struct regmap_config a1_pll_regmap_cfg = {
304 static struct meson_clk_hw_data a1_pll_clks = {
305 .hws = a1_pll_hw_clks,
306 .num = ARRAY_SIZE(a1_pll_hw_clks),
309 static int meson_a1_pll_probe(struct platform_device *pdev)
311 struct device *dev = &pdev->dev;
316 base = devm_platform_ioremap_resource(pdev, 0);
318 return dev_err_probe(dev, PTR_ERR(base),
319 "can't ioremap resource\n");
321 map = devm_regmap_init_mmio(dev, base, &a1_pll_regmap_cfg);
323 return dev_err_probe(dev, PTR_ERR(map),
324 "can't init regmap mmio region\n");
326 /* Populate regmap for the regmap backed clocks */
327 for (i = 0; i < ARRAY_SIZE(a1_pll_regmaps); i++)
328 a1_pll_regmaps[i]->map = map;
330 /* Register clocks */
331 for (clkid = 0; clkid < a1_pll_clks.num; clkid++) {
332 err = devm_clk_hw_register(dev, a1_pll_clks.hws[clkid]);
334 return dev_err_probe(dev, err,
335 "clock[%d] registration failed\n",
339 return devm_of_clk_add_hw_provider(dev, meson_clk_hw_get,
343 static const struct of_device_id a1_pll_clkc_match_table[] = {
344 { .compatible = "amlogic,a1-pll-clkc", },
347 MODULE_DEVICE_TABLE(of, a1_pll_clkc_match_table);
349 static struct platform_driver a1_pll_clkc_driver = {
350 .probe = meson_a1_pll_probe,
352 .name = "a1-pll-clkc",
353 .of_match_table = a1_pll_clkc_match_table,
357 module_platform_driver(a1_pll_clkc_driver);
358 MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
359 MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>");
360 MODULE_LICENSE("GPL");