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