0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052 #include <linux/clk.h>
0053 #include <linux/clk-provider.h>
0054 #include <linux/io.h>
0055 #include <linux/module.h>
0056 #include <linux/platform_device.h>
0057 #include <linux/regmap.h>
0058 #include <soc/qcom/kryo-l2-accessors.h>
0059
0060 #include "clk-alpha-pll.h"
0061 #include "clk-regmap.h"
0062
0063 enum _pmux_input {
0064 DIV_2_INDEX = 0,
0065 PLL_INDEX,
0066 ACD_INDEX,
0067 ALT_INDEX,
0068 NUM_OF_PMUX_INPUTS
0069 };
0070
0071 #define DIV_2_THRESHOLD 600000000
0072 #define PWRCL_REG_OFFSET 0x0
0073 #define PERFCL_REG_OFFSET 0x80000
0074 #define MUX_OFFSET 0x40
0075 #define ALT_PLL_OFFSET 0x100
0076 #define SSSCTL_OFFSET 0x160
0077
0078 static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = {
0079 [PLL_OFF_L_VAL] = 0x04,
0080 [PLL_OFF_ALPHA_VAL] = 0x08,
0081 [PLL_OFF_USER_CTL] = 0x10,
0082 [PLL_OFF_CONFIG_CTL] = 0x18,
0083 [PLL_OFF_CONFIG_CTL_U] = 0x1c,
0084 [PLL_OFF_TEST_CTL] = 0x20,
0085 [PLL_OFF_TEST_CTL_U] = 0x24,
0086 [PLL_OFF_STATUS] = 0x28,
0087 };
0088
0089 static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = {
0090 [PLL_OFF_L_VAL] = 0x04,
0091 [PLL_OFF_ALPHA_VAL] = 0x08,
0092 [PLL_OFF_ALPHA_VAL_U] = 0x0c,
0093 [PLL_OFF_USER_CTL] = 0x10,
0094 [PLL_OFF_USER_CTL_U] = 0x14,
0095 [PLL_OFF_CONFIG_CTL] = 0x18,
0096 [PLL_OFF_TEST_CTL] = 0x20,
0097 [PLL_OFF_TEST_CTL_U] = 0x24,
0098 [PLL_OFF_STATUS] = 0x28,
0099 };
0100
0101
0102
0103 static const struct alpha_pll_config hfpll_config = {
0104 .l = 60,
0105 .config_ctl_val = 0x200d4aa8,
0106 .config_ctl_hi_val = 0x006,
0107 .pre_div_mask = BIT(12),
0108 .post_div_mask = 0x3 << 8,
0109 .post_div_val = 0x1 << 8,
0110 .main_output_mask = BIT(0),
0111 .early_output_mask = BIT(3),
0112 };
0113
0114 static struct clk_alpha_pll perfcl_pll = {
0115 .offset = PERFCL_REG_OFFSET,
0116 .regs = prim_pll_regs,
0117 .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE,
0118 .clkr.hw.init = &(struct clk_init_data){
0119 .name = "perfcl_pll",
0120 .parent_names = (const char *[]){ "xo" },
0121 .num_parents = 1,
0122 .ops = &clk_alpha_pll_huayra_ops,
0123 },
0124 };
0125
0126 static struct clk_alpha_pll pwrcl_pll = {
0127 .offset = PWRCL_REG_OFFSET,
0128 .regs = prim_pll_regs,
0129 .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE,
0130 .clkr.hw.init = &(struct clk_init_data){
0131 .name = "pwrcl_pll",
0132 .parent_names = (const char *[]){ "xo" },
0133 .num_parents = 1,
0134 .ops = &clk_alpha_pll_huayra_ops,
0135 },
0136 };
0137
0138 static const struct pll_vco alt_pll_vco_modes[] = {
0139 VCO(3, 250000000, 500000000),
0140 VCO(2, 500000000, 750000000),
0141 VCO(1, 750000000, 1000000000),
0142 VCO(0, 1000000000, 2150400000),
0143 };
0144
0145 static const struct alpha_pll_config altpll_config = {
0146 .l = 16,
0147 .vco_val = 0x3 << 20,
0148 .vco_mask = 0x3 << 20,
0149 .config_ctl_val = 0x4001051b,
0150 .post_div_mask = 0x3 << 8,
0151 .post_div_val = 0x1 << 8,
0152 .main_output_mask = BIT(0),
0153 .early_output_mask = BIT(3),
0154 };
0155
0156 static struct clk_alpha_pll perfcl_alt_pll = {
0157 .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET,
0158 .regs = alt_pll_regs,
0159 .vco_table = alt_pll_vco_modes,
0160 .num_vco = ARRAY_SIZE(alt_pll_vco_modes),
0161 .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
0162 .clkr.hw.init = &(struct clk_init_data) {
0163 .name = "perfcl_alt_pll",
0164 .parent_names = (const char *[]){ "xo" },
0165 .num_parents = 1,
0166 .ops = &clk_alpha_pll_hwfsm_ops,
0167 },
0168 };
0169
0170 static struct clk_alpha_pll pwrcl_alt_pll = {
0171 .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET,
0172 .regs = alt_pll_regs,
0173 .vco_table = alt_pll_vco_modes,
0174 .num_vco = ARRAY_SIZE(alt_pll_vco_modes),
0175 .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
0176 .clkr.hw.init = &(struct clk_init_data) {
0177 .name = "pwrcl_alt_pll",
0178 .parent_names = (const char *[]){ "xo" },
0179 .num_parents = 1,
0180 .ops = &clk_alpha_pll_hwfsm_ops,
0181 },
0182 };
0183
0184 struct clk_cpu_8996_mux {
0185 u32 reg;
0186 u8 shift;
0187 u8 width;
0188 struct notifier_block nb;
0189 struct clk_hw *pll;
0190 struct clk_hw *pll_div_2;
0191 struct clk_regmap clkr;
0192 };
0193
0194 static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
0195 void *data);
0196
0197 #define to_clk_cpu_8996_mux_nb(_nb) \
0198 container_of(_nb, struct clk_cpu_8996_mux, nb)
0199
0200 static inline struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw)
0201 {
0202 return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr);
0203 }
0204
0205 static u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw)
0206 {
0207 struct clk_regmap *clkr = to_clk_regmap(hw);
0208 struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
0209 u32 mask = GENMASK(cpuclk->width - 1, 0);
0210 u32 val;
0211
0212 regmap_read(clkr->regmap, cpuclk->reg, &val);
0213 val >>= cpuclk->shift;
0214
0215 return val & mask;
0216 }
0217
0218 static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
0219 {
0220 struct clk_regmap *clkr = to_clk_regmap(hw);
0221 struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
0222 u32 mask = GENMASK(cpuclk->width + cpuclk->shift - 1, cpuclk->shift);
0223 u32 val;
0224
0225 val = index;
0226 val <<= cpuclk->shift;
0227
0228 return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val);
0229 }
0230
0231 static int clk_cpu_8996_mux_determine_rate(struct clk_hw *hw,
0232 struct clk_rate_request *req)
0233 {
0234 struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
0235 struct clk_hw *parent = cpuclk->pll;
0236
0237 if (cpuclk->pll_div_2 && req->rate < DIV_2_THRESHOLD) {
0238 if (req->rate < (DIV_2_THRESHOLD / 2))
0239 return -EINVAL;
0240
0241 parent = cpuclk->pll_div_2;
0242 }
0243
0244 req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
0245 req->best_parent_hw = parent;
0246
0247 return 0;
0248 }
0249
0250 static const struct clk_ops clk_cpu_8996_mux_ops = {
0251 .set_parent = clk_cpu_8996_mux_set_parent,
0252 .get_parent = clk_cpu_8996_mux_get_parent,
0253 .determine_rate = clk_cpu_8996_mux_determine_rate,
0254 };
0255
0256 static struct clk_cpu_8996_mux pwrcl_smux = {
0257 .reg = PWRCL_REG_OFFSET + MUX_OFFSET,
0258 .shift = 2,
0259 .width = 2,
0260 .clkr.hw.init = &(struct clk_init_data) {
0261 .name = "pwrcl_smux",
0262 .parent_names = (const char *[]){
0263 "xo",
0264 "pwrcl_pll_main",
0265 },
0266 .num_parents = 2,
0267 .ops = &clk_cpu_8996_mux_ops,
0268 .flags = CLK_SET_RATE_PARENT,
0269 },
0270 };
0271
0272 static struct clk_cpu_8996_mux perfcl_smux = {
0273 .reg = PERFCL_REG_OFFSET + MUX_OFFSET,
0274 .shift = 2,
0275 .width = 2,
0276 .clkr.hw.init = &(struct clk_init_data) {
0277 .name = "perfcl_smux",
0278 .parent_names = (const char *[]){
0279 "xo",
0280 "perfcl_pll_main",
0281 },
0282 .num_parents = 2,
0283 .ops = &clk_cpu_8996_mux_ops,
0284 .flags = CLK_SET_RATE_PARENT,
0285 },
0286 };
0287
0288 static struct clk_cpu_8996_mux pwrcl_pmux = {
0289 .reg = PWRCL_REG_OFFSET + MUX_OFFSET,
0290 .shift = 0,
0291 .width = 2,
0292 .pll = &pwrcl_pll.clkr.hw,
0293 .pll_div_2 = &pwrcl_smux.clkr.hw,
0294 .nb.notifier_call = cpu_clk_notifier_cb,
0295 .clkr.hw.init = &(struct clk_init_data) {
0296 .name = "pwrcl_pmux",
0297 .parent_names = (const char *[]){
0298 "pwrcl_smux",
0299 "pwrcl_pll",
0300 "pwrcl_pll_acd",
0301 "pwrcl_alt_pll",
0302 },
0303 .num_parents = 4,
0304 .ops = &clk_cpu_8996_mux_ops,
0305
0306 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
0307 },
0308 };
0309
0310 static struct clk_cpu_8996_mux perfcl_pmux = {
0311 .reg = PERFCL_REG_OFFSET + MUX_OFFSET,
0312 .shift = 0,
0313 .width = 2,
0314 .pll = &perfcl_pll.clkr.hw,
0315 .pll_div_2 = &perfcl_smux.clkr.hw,
0316 .nb.notifier_call = cpu_clk_notifier_cb,
0317 .clkr.hw.init = &(struct clk_init_data) {
0318 .name = "perfcl_pmux",
0319 .parent_names = (const char *[]){
0320 "perfcl_smux",
0321 "perfcl_pll",
0322 "perfcl_pll_acd",
0323 "perfcl_alt_pll",
0324 },
0325 .num_parents = 4,
0326 .ops = &clk_cpu_8996_mux_ops,
0327
0328 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
0329 },
0330 };
0331
0332 static const struct regmap_config cpu_msm8996_regmap_config = {
0333 .reg_bits = 32,
0334 .reg_stride = 4,
0335 .val_bits = 32,
0336 .max_register = 0x80210,
0337 .fast_io = true,
0338 .val_format_endian = REGMAP_ENDIAN_LITTLE,
0339 };
0340
0341 static struct clk_regmap *cpu_msm8996_clks[] = {
0342 &perfcl_pll.clkr,
0343 &pwrcl_pll.clkr,
0344 &perfcl_alt_pll.clkr,
0345 &pwrcl_alt_pll.clkr,
0346 &perfcl_smux.clkr,
0347 &pwrcl_smux.clkr,
0348 &perfcl_pmux.clkr,
0349 &pwrcl_pmux.clkr,
0350 };
0351
0352 static int qcom_cpu_clk_msm8996_register_clks(struct device *dev,
0353 struct regmap *regmap)
0354 {
0355 int i, ret;
0356
0357 perfcl_smux.pll = clk_hw_register_fixed_factor(dev, "perfcl_pll_main",
0358 "perfcl_pll",
0359 CLK_SET_RATE_PARENT,
0360 1, 2);
0361 if (IS_ERR(perfcl_smux.pll)) {
0362 dev_err(dev, "Failed to initialize perfcl_pll_main\n");
0363 return PTR_ERR(perfcl_smux.pll);
0364 }
0365
0366 pwrcl_smux.pll = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main",
0367 "pwrcl_pll",
0368 CLK_SET_RATE_PARENT,
0369 1, 2);
0370 if (IS_ERR(pwrcl_smux.pll)) {
0371 dev_err(dev, "Failed to initialize pwrcl_pll_main\n");
0372 clk_hw_unregister(perfcl_smux.pll);
0373 return PTR_ERR(pwrcl_smux.pll);
0374 }
0375
0376 for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) {
0377 ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]);
0378 if (ret) {
0379 clk_hw_unregister(perfcl_smux.pll);
0380 clk_hw_unregister(pwrcl_smux.pll);
0381 return ret;
0382 }
0383 }
0384
0385 clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config);
0386 clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config);
0387 clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config);
0388 clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config);
0389
0390
0391 clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk);
0392 clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk);
0393
0394 clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb);
0395 clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb);
0396
0397 return ret;
0398 }
0399
0400 static int qcom_cpu_clk_msm8996_unregister_clks(void)
0401 {
0402 int ret = 0;
0403
0404 ret = clk_notifier_unregister(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb);
0405 if (ret)
0406 return ret;
0407
0408 ret = clk_notifier_unregister(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb);
0409 if (ret)
0410 return ret;
0411
0412 clk_hw_unregister(perfcl_smux.pll);
0413 clk_hw_unregister(pwrcl_smux.pll);
0414
0415 return 0;
0416 }
0417
0418 #define CPU_AFINITY_MASK 0xFFF
0419 #define PWRCL_CPU_REG_MASK 0x3
0420 #define PERFCL_CPU_REG_MASK 0x103
0421
0422 #define L2ACDCR_REG 0x580ULL
0423 #define L2ACDTD_REG 0x581ULL
0424 #define L2ACDDVMRC_REG 0x584ULL
0425 #define L2ACDSSCR_REG 0x589ULL
0426
0427 static DEFINE_SPINLOCK(qcom_clk_acd_lock);
0428 static void __iomem *base;
0429
0430 static void qcom_cpu_clk_msm8996_acd_init(void __iomem *base)
0431 {
0432 u64 hwid;
0433 unsigned long flags;
0434
0435 spin_lock_irqsave(&qcom_clk_acd_lock, flags);
0436
0437 hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK;
0438
0439 kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11);
0440 kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f);
0441 kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601);
0442
0443 if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) {
0444 writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET);
0445 kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd);
0446 }
0447
0448 if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) {
0449 kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd);
0450 writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET);
0451 }
0452
0453 spin_unlock_irqrestore(&qcom_clk_acd_lock, flags);
0454 }
0455
0456 static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
0457 void *data)
0458 {
0459 struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb);
0460 struct clk_notifier_data *cnd = data;
0461 int ret;
0462
0463 switch (event) {
0464 case PRE_RATE_CHANGE:
0465 ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX);
0466 qcom_cpu_clk_msm8996_acd_init(base);
0467 break;
0468 case POST_RATE_CHANGE:
0469 if (cnd->new_rate < DIV_2_THRESHOLD)
0470 ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw,
0471 DIV_2_INDEX);
0472 else
0473 ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw,
0474 ACD_INDEX);
0475 break;
0476 default:
0477 ret = 0;
0478 break;
0479 }
0480
0481 return notifier_from_errno(ret);
0482 };
0483
0484 static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
0485 {
0486 struct regmap *regmap;
0487 struct clk_hw_onecell_data *data;
0488 struct device *dev = &pdev->dev;
0489 int ret;
0490
0491 data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL);
0492 if (!data)
0493 return -ENOMEM;
0494
0495 base = devm_platform_ioremap_resource(pdev, 0);
0496 if (IS_ERR(base))
0497 return PTR_ERR(base);
0498
0499 regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config);
0500 if (IS_ERR(regmap))
0501 return PTR_ERR(regmap);
0502
0503 ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap);
0504 if (ret)
0505 return ret;
0506
0507 qcom_cpu_clk_msm8996_acd_init(base);
0508
0509 data->hws[0] = &pwrcl_pmux.clkr.hw;
0510 data->hws[1] = &perfcl_pmux.clkr.hw;
0511 data->num = 2;
0512
0513 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
0514 }
0515
0516 static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev)
0517 {
0518 return qcom_cpu_clk_msm8996_unregister_clks();
0519 }
0520
0521 static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = {
0522 { .compatible = "qcom,msm8996-apcc" },
0523 {}
0524 };
0525 MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table);
0526
0527 static struct platform_driver qcom_cpu_clk_msm8996_driver = {
0528 .probe = qcom_cpu_clk_msm8996_driver_probe,
0529 .remove = qcom_cpu_clk_msm8996_driver_remove,
0530 .driver = {
0531 .name = "qcom-msm8996-apcc",
0532 .of_match_table = qcom_cpu_clk_msm8996_match_table,
0533 },
0534 };
0535 module_platform_driver(qcom_cpu_clk_msm8996_driver);
0536
0537 MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver");
0538 MODULE_LICENSE("GPL v2");