364 lines · c
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 "a1-pll.h"14#include "clk-regmap.h"15#include "meson-clkc-utils.h"16 17#include <dt-bindings/clock/amlogic,a1-pll-clkc.h>18 19static struct clk_regmap fixed_pll_dco = {20 .data = &(struct meson_clk_pll_data){21 .en = {22 .reg_off = ANACTRL_FIXPLL_CTRL0,23 .shift = 28,24 .width = 1,25 },26 .m = {27 .reg_off = ANACTRL_FIXPLL_CTRL0,28 .shift = 0,29 .width = 8,30 },31 .n = {32 .reg_off = ANACTRL_FIXPLL_CTRL0,33 .shift = 10,34 .width = 5,35 },36 .frac = {37 .reg_off = ANACTRL_FIXPLL_CTRL1,38 .shift = 0,39 .width = 19,40 },41 .l = {42 .reg_off = ANACTRL_FIXPLL_STS,43 .shift = 31,44 .width = 1,45 },46 .rst = {47 .reg_off = ANACTRL_FIXPLL_CTRL0,48 .shift = 29,49 .width = 1,50 },51 },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",57 },58 .num_parents = 1,59 },60};61 62static struct clk_regmap fixed_pll = {63 .data = &(struct clk_regmap_gate_data){64 .offset = ANACTRL_FIXPLL_CTRL0,65 .bit_idx = 20,66 },67 .hw.init = &(struct clk_init_data) {68 .name = "fixed_pll",69 .ops = &clk_regmap_gate_ops,70 .parent_hws = (const struct clk_hw *[]) {71 &fixed_pll_dco.hw72 },73 .num_parents = 1,74 },75};76 77static const struct pll_mult_range hifi_pll_mult_range = {78 .min = 32,79 .max = 64,80};81 82static 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 },88};89 90static struct clk_regmap hifi_pll = {91 .data = &(struct meson_clk_pll_data){92 .en = {93 .reg_off = ANACTRL_HIFIPLL_CTRL0,94 .shift = 28,95 .width = 1,96 },97 .m = {98 .reg_off = ANACTRL_HIFIPLL_CTRL0,99 .shift = 0,100 .width = 8,101 },102 .n = {103 .reg_off = ANACTRL_HIFIPLL_CTRL0,104 .shift = 10,105 .width = 5,106 },107 .frac = {108 .reg_off = ANACTRL_HIFIPLL_CTRL1,109 .shift = 0,110 .width = 19,111 },112 .l = {113 .reg_off = ANACTRL_HIFIPLL_STS,114 .shift = 31,115 .width = 1,116 },117 .current_en = {118 .reg_off = ANACTRL_HIFIPLL_CTRL0,119 .shift = 26,120 .width = 1,121 },122 .l_detect = {123 .reg_off = ANACTRL_HIFIPLL_CTRL2,124 .shift = 6,125 .width = 1,126 },127 .range = &hifi_pll_mult_range,128 .init_regs = hifi_init_regs,129 .init_count = ARRAY_SIZE(hifi_init_regs),130 },131 .hw.init = &(struct clk_init_data){132 .name = "hifi_pll",133 .ops = &meson_clk_pll_ops,134 .parent_data = &(const struct clk_parent_data) {135 .fw_name = "hifipll_in",136 },137 .num_parents = 1,138 },139};140 141static struct clk_fixed_factor fclk_div2_div = {142 .mult = 1,143 .div = 2,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 *[]) {148 &fixed_pll.hw149 },150 .num_parents = 1,151 },152};153 154static struct clk_regmap fclk_div2 = {155 .data = &(struct clk_regmap_gate_data){156 .offset = ANACTRL_FIXPLL_CTRL0,157 .bit_idx = 21,158 },159 .hw.init = &(struct clk_init_data){160 .name = "fclk_div2",161 .ops = &clk_regmap_gate_ops,162 .parent_hws = (const struct clk_hw *[]) {163 &fclk_div2_div.hw164 },165 .num_parents = 1,166 /*167 * This clock is used by DDR clock in BL2 firmware168 * and is required by the platform to operate correctly.169 * Until the following condition are met, we need this clock to170 * be marked as critical:171 * a) Mark the clock used by a firmware resource, if possible172 * b) CCF has a clock hand-off mechanism to make the sure the173 * clock stays on until the proper driver comes along174 */175 .flags = CLK_IS_CRITICAL,176 },177};178 179static struct clk_fixed_factor fclk_div3_div = {180 .mult = 1,181 .div = 3,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 *[]) {186 &fixed_pll.hw187 },188 .num_parents = 1,189 },190};191 192static struct clk_regmap fclk_div3 = {193 .data = &(struct clk_regmap_gate_data){194 .offset = ANACTRL_FIXPLL_CTRL0,195 .bit_idx = 22,196 },197 .hw.init = &(struct clk_init_data){198 .name = "fclk_div3",199 .ops = &clk_regmap_gate_ops,200 .parent_hws = (const struct clk_hw *[]) {201 &fclk_div3_div.hw202 },203 .num_parents = 1,204 /*205 * This clock is used by APB bus which is set in boot ROM code206 * and is required by the platform to operate correctly.207 */208 .flags = CLK_IS_CRITICAL,209 },210};211 212static struct clk_fixed_factor fclk_div5_div = {213 .mult = 1,214 .div = 5,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 *[]) {219 &fixed_pll.hw220 },221 .num_parents = 1,222 },223};224 225static struct clk_regmap fclk_div5 = {226 .data = &(struct clk_regmap_gate_data){227 .offset = ANACTRL_FIXPLL_CTRL0,228 .bit_idx = 23,229 },230 .hw.init = &(struct clk_init_data){231 .name = "fclk_div5",232 .ops = &clk_regmap_gate_ops,233 .parent_hws = (const struct clk_hw *[]) {234 &fclk_div5_div.hw235 },236 .num_parents = 1,237 /*238 * This clock is used by AXI bus which setted in Romcode239 * and is required by the platform to operate correctly.240 */241 .flags = CLK_IS_CRITICAL,242 },243};244 245static struct clk_fixed_factor fclk_div7_div = {246 .mult = 1,247 .div = 7,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 *[]) {252 &fixed_pll.hw253 },254 .num_parents = 1,255 },256};257 258static struct clk_regmap fclk_div7 = {259 .data = &(struct clk_regmap_gate_data){260 .offset = ANACTRL_FIXPLL_CTRL0,261 .bit_idx = 24,262 },263 .hw.init = &(struct clk_init_data){264 .name = "fclk_div7",265 .ops = &clk_regmap_gate_ops,266 .parent_hws = (const struct clk_hw *[]) {267 &fclk_div7_div.hw268 },269 .num_parents = 1,270 },271};272 273/* Array of all clocks registered by this provider */274static 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,286};287 288static struct clk_regmap *const a1_pll_regmaps[] = {289 &fixed_pll_dco,290 &fixed_pll,291 &fclk_div2,292 &fclk_div3,293 &fclk_div5,294 &fclk_div7,295 &hifi_pll,296};297 298static const struct regmap_config a1_pll_regmap_cfg = {299 .reg_bits = 32,300 .val_bits = 32,301 .reg_stride = 4,302 .max_register = ANACTRL_HIFIPLL_STS,303};304 305static struct meson_clk_hw_data a1_pll_clks = {306 .hws = a1_pll_hw_clks,307 .num = ARRAY_SIZE(a1_pll_hw_clks),308};309 310static int meson_a1_pll_probe(struct platform_device *pdev)311{312 struct device *dev = &pdev->dev;313 void __iomem *base;314 struct regmap *map;315 int clkid, i, err;316 317 base = devm_platform_ioremap_resource(pdev, 0);318 if (IS_ERR(base))319 return dev_err_probe(dev, PTR_ERR(base),320 "can't ioremap resource\n");321 322 map = devm_regmap_init_mmio(dev, base, &a1_pll_regmap_cfg);323 if (IS_ERR(map))324 return dev_err_probe(dev, PTR_ERR(map),325 "can't init regmap mmio region\n");326 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;330 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]);334 if (err)335 return dev_err_probe(dev, err,336 "clock[%d] registration failed\n",337 clkid);338 }339 340 return devm_of_clk_add_hw_provider(dev, meson_clk_hw_get,341 &a1_pll_clks);342}343 344static const struct of_device_id a1_pll_clkc_match_table[] = {345 { .compatible = "amlogic,a1-pll-clkc", },346 {}347};348MODULE_DEVICE_TABLE(of, a1_pll_clkc_match_table);349 350static struct platform_driver a1_pll_clkc_driver = {351 .probe = meson_a1_pll_probe,352 .driver = {353 .name = "a1-pll-clkc",354 .of_match_table = a1_pll_clkc_match_table,355 },356};357module_platform_driver(a1_pll_clkc_driver);358 359MODULE_DESCRIPTION("Amlogic S4 PLL Clock Controller driver");360MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");361MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>");362MODULE_LICENSE("GPL");363MODULE_IMPORT_NS(CLK_MESON);364