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 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 <jian.hu@amlogic.com>"); 355 MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>"); 356 MODULE_LICENSE("GPL"); 357