Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2020, The Linux Foundation. All rights reserved.
0004  */
0005 
0006 /*
0007  * Each of the CPU clusters (Power and Perf) on msm8996 are
0008  * clocked via 2 PLLs, a primary and alternate. There are also
0009  * 2 Mux'es, a primary and secondary all connected together
0010  * as shown below
0011  *
0012  *                              +-------+
0013  *               XO             |       |
0014  *           +------------------>0      |
0015  *                              |       |
0016  *                    PLL/2     | SMUX  +----+
0017  *                      +------->1      |    |
0018  *                      |       |       |    |
0019  *                      |       +-------+    |    +-------+
0020  *                      |                    +---->0      |
0021  *                      |                         |       |
0022  * +---------------+    |             +----------->1      | CPU clk
0023  * |Primary PLL    +----+ PLL_EARLY   |           |       +------>
0024  * |               +------+-----------+    +------>2 PMUX |
0025  * +---------------+      |                |      |       |
0026  *                        |   +------+     |   +-->3      |
0027  *                        +--^+  ACD +-----+   |  +-------+
0028  * +---------------+          +------+         |
0029  * |Alt PLL        |                           |
0030  * |               +---------------------------+
0031  * +---------------+         PLL_EARLY
0032  *
0033  * The primary PLL is what drives the CPU clk, except for times
0034  * when we are reprogramming the PLL itself (for rate changes) when
0035  * we temporarily switch to an alternate PLL.
0036  *
0037  * The primary PLL operates on a single VCO range, between 600MHz
0038  * and 3GHz. However the CPUs do support OPPs with frequencies
0039  * between 300MHz and 600MHz. In order to support running the CPUs
0040  * at those frequencies we end up having to lock the PLL at twice
0041  * the rate and drive the CPU clk via the PLL/2 output and SMUX.
0042  *
0043  * So for frequencies above 600MHz we follow the following path
0044  *  Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk
0045  * and for frequencies between 300MHz and 600MHz we follow
0046  *  Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk
0047  *
0048  * ACD stands for Adaptive Clock Distribution and is used to
0049  * detect voltage droops.
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 /* PLLs */
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         /* CPU clock is critical and should never be gated */
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         /* CPU clock is critical and should never be gated */
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     /* Enable alt PLLs */
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");