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