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