cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-cpu-8996.c (14686B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
      4 */
      5
      6/*
      7 * Each of the CPU clusters (Power and Perf) on msm8996 are
      8 * clocked via 2 PLLs, a primary and alternate. There are also
      9 * 2 Mux'es, a primary and secondary all connected together
     10 * as shown below
     11 *
     12 *                              +-------+
     13 *               XO             |       |
     14 *           +------------------>0      |
     15 *                              |       |
     16 *                    PLL/2     | SMUX  +----+
     17 *                      +------->1      |    |
     18 *                      |       |       |    |
     19 *                      |       +-------+    |    +-------+
     20 *                      |                    +---->0      |
     21 *                      |                         |       |
     22 * +---------------+    |             +----------->1      | CPU clk
     23 * |Primary PLL    +----+ PLL_EARLY   |           |       +------>
     24 * |               +------+-----------+    +------>2 PMUX |
     25 * +---------------+      |                |      |       |
     26 *                        |   +------+     |   +-->3      |
     27 *                        +--^+  ACD +-----+   |  +-------+
     28 * +---------------+          +------+         |
     29 * |Alt PLL        |                           |
     30 * |               +---------------------------+
     31 * +---------------+         PLL_EARLY
     32 *
     33 * The primary PLL is what drives the CPU clk, except for times
     34 * when we are reprogramming the PLL itself (for rate changes) when
     35 * we temporarily switch to an alternate PLL.
     36 *
     37 * The primary PLL operates on a single VCO range, between 600MHz
     38 * and 3GHz. However the CPUs do support OPPs with frequencies
     39 * between 300MHz and 600MHz. In order to support running the CPUs
     40 * at those frequencies we end up having to lock the PLL at twice
     41 * the rate and drive the CPU clk via the PLL/2 output and SMUX.
     42 *
     43 * So for frequencies above 600MHz we follow the following path
     44 *  Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk
     45 * and for frequencies between 300MHz and 600MHz we follow
     46 *  Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk
     47 *
     48 * ACD stands for Adaptive Clock Distribution and is used to
     49 * detect voltage droops.
     50 */
     51
     52#include <linux/clk.h>
     53#include <linux/clk-provider.h>
     54#include <linux/io.h>
     55#include <linux/module.h>
     56#include <linux/platform_device.h>
     57#include <linux/regmap.h>
     58#include <soc/qcom/kryo-l2-accessors.h>
     59
     60#include "clk-alpha-pll.h"
     61#include "clk-regmap.h"
     62
     63enum _pmux_input {
     64	DIV_2_INDEX = 0,
     65	PLL_INDEX,
     66	ACD_INDEX,
     67	ALT_INDEX,
     68	NUM_OF_PMUX_INPUTS
     69};
     70
     71#define DIV_2_THRESHOLD		600000000
     72#define PWRCL_REG_OFFSET 0x0
     73#define PERFCL_REG_OFFSET 0x80000
     74#define MUX_OFFSET	0x40
     75#define ALT_PLL_OFFSET	0x100
     76#define SSSCTL_OFFSET 0x160
     77
     78static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = {
     79	[PLL_OFF_L_VAL] = 0x04,
     80	[PLL_OFF_ALPHA_VAL] = 0x08,
     81	[PLL_OFF_USER_CTL] = 0x10,
     82	[PLL_OFF_CONFIG_CTL] = 0x18,
     83	[PLL_OFF_CONFIG_CTL_U] = 0x1c,
     84	[PLL_OFF_TEST_CTL] = 0x20,
     85	[PLL_OFF_TEST_CTL_U] = 0x24,
     86	[PLL_OFF_STATUS] = 0x28,
     87};
     88
     89static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = {
     90	[PLL_OFF_L_VAL] = 0x04,
     91	[PLL_OFF_ALPHA_VAL] = 0x08,
     92	[PLL_OFF_ALPHA_VAL_U] = 0x0c,
     93	[PLL_OFF_USER_CTL] = 0x10,
     94	[PLL_OFF_USER_CTL_U] = 0x14,
     95	[PLL_OFF_CONFIG_CTL] = 0x18,
     96	[PLL_OFF_TEST_CTL] = 0x20,
     97	[PLL_OFF_TEST_CTL_U] = 0x24,
     98	[PLL_OFF_STATUS] = 0x28,
     99};
    100
    101/* PLLs */
    102
    103static const struct alpha_pll_config hfpll_config = {
    104	.l = 60,
    105	.config_ctl_val = 0x200d4aa8,
    106	.config_ctl_hi_val = 0x006,
    107	.pre_div_mask = BIT(12),
    108	.post_div_mask = 0x3 << 8,
    109	.post_div_val = 0x1 << 8,
    110	.main_output_mask = BIT(0),
    111	.early_output_mask = BIT(3),
    112};
    113
    114static struct clk_alpha_pll perfcl_pll = {
    115	.offset = PERFCL_REG_OFFSET,
    116	.regs = prim_pll_regs,
    117	.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE,
    118	.clkr.hw.init = &(struct clk_init_data){
    119		.name = "perfcl_pll",
    120		.parent_names = (const char *[]){ "xo" },
    121		.num_parents = 1,
    122		.ops = &clk_alpha_pll_huayra_ops,
    123	},
    124};
    125
    126static struct clk_alpha_pll pwrcl_pll = {
    127	.offset = PWRCL_REG_OFFSET,
    128	.regs = prim_pll_regs,
    129	.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE,
    130	.clkr.hw.init = &(struct clk_init_data){
    131		.name = "pwrcl_pll",
    132		.parent_names = (const char *[]){ "xo" },
    133		.num_parents = 1,
    134		.ops = &clk_alpha_pll_huayra_ops,
    135	},
    136};
    137
    138static const struct pll_vco alt_pll_vco_modes[] = {
    139	VCO(3,  250000000,  500000000),
    140	VCO(2,  500000000,  750000000),
    141	VCO(1,  750000000, 1000000000),
    142	VCO(0, 1000000000, 2150400000),
    143};
    144
    145static const struct alpha_pll_config altpll_config = {
    146	.l = 16,
    147	.vco_val = 0x3 << 20,
    148	.vco_mask = 0x3 << 20,
    149	.config_ctl_val = 0x4001051b,
    150	.post_div_mask = 0x3 << 8,
    151	.post_div_val = 0x1 << 8,
    152	.main_output_mask = BIT(0),
    153	.early_output_mask = BIT(3),
    154};
    155
    156static struct clk_alpha_pll perfcl_alt_pll = {
    157	.offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET,
    158	.regs = alt_pll_regs,
    159	.vco_table = alt_pll_vco_modes,
    160	.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
    161	.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
    162	.clkr.hw.init = &(struct clk_init_data) {
    163		.name = "perfcl_alt_pll",
    164		.parent_names = (const char *[]){ "xo" },
    165		.num_parents = 1,
    166		.ops = &clk_alpha_pll_hwfsm_ops,
    167	},
    168};
    169
    170static struct clk_alpha_pll pwrcl_alt_pll = {
    171	.offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET,
    172	.regs = alt_pll_regs,
    173	.vco_table = alt_pll_vco_modes,
    174	.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
    175	.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
    176	.clkr.hw.init = &(struct clk_init_data) {
    177		.name = "pwrcl_alt_pll",
    178		.parent_names = (const char *[]){ "xo" },
    179		.num_parents = 1,
    180		.ops = &clk_alpha_pll_hwfsm_ops,
    181	},
    182};
    183
    184struct clk_cpu_8996_mux {
    185	u32	reg;
    186	u8	shift;
    187	u8	width;
    188	struct notifier_block nb;
    189	struct clk_hw	*pll;
    190	struct clk_hw	*pll_div_2;
    191	struct clk_regmap clkr;
    192};
    193
    194static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
    195			       void *data);
    196
    197#define to_clk_cpu_8996_mux_nb(_nb) \
    198	container_of(_nb, struct clk_cpu_8996_mux, nb)
    199
    200static inline struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw)
    201{
    202	return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr);
    203}
    204
    205static u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw)
    206{
    207	struct clk_regmap *clkr = to_clk_regmap(hw);
    208	struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
    209	u32 mask = GENMASK(cpuclk->width - 1, 0);
    210	u32 val;
    211
    212	regmap_read(clkr->regmap, cpuclk->reg, &val);
    213	val >>= cpuclk->shift;
    214
    215	return val & mask;
    216}
    217
    218static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
    219{
    220	struct clk_regmap *clkr = to_clk_regmap(hw);
    221	struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
    222	u32 mask = GENMASK(cpuclk->width + cpuclk->shift - 1, cpuclk->shift);
    223	u32 val;
    224
    225	val = index;
    226	val <<= cpuclk->shift;
    227
    228	return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val);
    229}
    230
    231static int clk_cpu_8996_mux_determine_rate(struct clk_hw *hw,
    232					   struct clk_rate_request *req)
    233{
    234	struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
    235	struct clk_hw *parent = cpuclk->pll;
    236
    237	if (cpuclk->pll_div_2 && req->rate < DIV_2_THRESHOLD) {
    238		if (req->rate < (DIV_2_THRESHOLD / 2))
    239			return -EINVAL;
    240
    241		parent = cpuclk->pll_div_2;
    242	}
    243
    244	req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
    245	req->best_parent_hw = parent;
    246
    247	return 0;
    248}
    249
    250static const struct clk_ops clk_cpu_8996_mux_ops = {
    251	.set_parent = clk_cpu_8996_mux_set_parent,
    252	.get_parent = clk_cpu_8996_mux_get_parent,
    253	.determine_rate = clk_cpu_8996_mux_determine_rate,
    254};
    255
    256static struct clk_cpu_8996_mux pwrcl_smux = {
    257	.reg = PWRCL_REG_OFFSET + MUX_OFFSET,
    258	.shift = 2,
    259	.width = 2,
    260	.clkr.hw.init = &(struct clk_init_data) {
    261		.name = "pwrcl_smux",
    262		.parent_names = (const char *[]){
    263			"xo",
    264			"pwrcl_pll_main",
    265		},
    266		.num_parents = 2,
    267		.ops = &clk_cpu_8996_mux_ops,
    268		.flags = CLK_SET_RATE_PARENT,
    269	},
    270};
    271
    272static struct clk_cpu_8996_mux perfcl_smux = {
    273	.reg = PERFCL_REG_OFFSET + MUX_OFFSET,
    274	.shift = 2,
    275	.width = 2,
    276	.clkr.hw.init = &(struct clk_init_data) {
    277		.name = "perfcl_smux",
    278		.parent_names = (const char *[]){
    279			"xo",
    280			"perfcl_pll_main",
    281		},
    282		.num_parents = 2,
    283		.ops = &clk_cpu_8996_mux_ops,
    284		.flags = CLK_SET_RATE_PARENT,
    285	},
    286};
    287
    288static struct clk_cpu_8996_mux pwrcl_pmux = {
    289	.reg = PWRCL_REG_OFFSET + MUX_OFFSET,
    290	.shift = 0,
    291	.width = 2,
    292	.pll = &pwrcl_pll.clkr.hw,
    293	.pll_div_2 = &pwrcl_smux.clkr.hw,
    294	.nb.notifier_call = cpu_clk_notifier_cb,
    295	.clkr.hw.init = &(struct clk_init_data) {
    296		.name = "pwrcl_pmux",
    297		.parent_names = (const char *[]){
    298			"pwrcl_smux",
    299			"pwrcl_pll",
    300			"pwrcl_pll_acd",
    301			"pwrcl_alt_pll",
    302		},
    303		.num_parents = 4,
    304		.ops = &clk_cpu_8996_mux_ops,
    305		/* CPU clock is critical and should never be gated */
    306		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
    307	},
    308};
    309
    310static struct clk_cpu_8996_mux perfcl_pmux = {
    311	.reg = PERFCL_REG_OFFSET + MUX_OFFSET,
    312	.shift = 0,
    313	.width = 2,
    314	.pll = &perfcl_pll.clkr.hw,
    315	.pll_div_2 = &perfcl_smux.clkr.hw,
    316	.nb.notifier_call = cpu_clk_notifier_cb,
    317	.clkr.hw.init = &(struct clk_init_data) {
    318		.name = "perfcl_pmux",
    319		.parent_names = (const char *[]){
    320			"perfcl_smux",
    321			"perfcl_pll",
    322			"perfcl_pll_acd",
    323			"perfcl_alt_pll",
    324		},
    325		.num_parents = 4,
    326		.ops = &clk_cpu_8996_mux_ops,
    327		/* CPU clock is critical and should never be gated */
    328		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
    329	},
    330};
    331
    332static const struct regmap_config cpu_msm8996_regmap_config = {
    333	.reg_bits		= 32,
    334	.reg_stride		= 4,
    335	.val_bits		= 32,
    336	.max_register		= 0x80210,
    337	.fast_io		= true,
    338	.val_format_endian	= REGMAP_ENDIAN_LITTLE,
    339};
    340
    341static struct clk_regmap *cpu_msm8996_clks[] = {
    342	&perfcl_pll.clkr,
    343	&pwrcl_pll.clkr,
    344	&perfcl_alt_pll.clkr,
    345	&pwrcl_alt_pll.clkr,
    346	&perfcl_smux.clkr,
    347	&pwrcl_smux.clkr,
    348	&perfcl_pmux.clkr,
    349	&pwrcl_pmux.clkr,
    350};
    351
    352static int qcom_cpu_clk_msm8996_register_clks(struct device *dev,
    353					      struct regmap *regmap)
    354{
    355	int i, ret;
    356
    357	perfcl_smux.pll = clk_hw_register_fixed_factor(dev, "perfcl_pll_main",
    358						       "perfcl_pll",
    359						       CLK_SET_RATE_PARENT,
    360						       1, 2);
    361	if (IS_ERR(perfcl_smux.pll)) {
    362		dev_err(dev, "Failed to initialize perfcl_pll_main\n");
    363		return PTR_ERR(perfcl_smux.pll);
    364	}
    365
    366	pwrcl_smux.pll = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main",
    367						      "pwrcl_pll",
    368						      CLK_SET_RATE_PARENT,
    369						      1, 2);
    370	if (IS_ERR(pwrcl_smux.pll)) {
    371		dev_err(dev, "Failed to initialize pwrcl_pll_main\n");
    372		clk_hw_unregister(perfcl_smux.pll);
    373		return PTR_ERR(pwrcl_smux.pll);
    374	}
    375
    376	for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) {
    377		ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]);
    378		if (ret) {
    379			clk_hw_unregister(perfcl_smux.pll);
    380			clk_hw_unregister(pwrcl_smux.pll);
    381			return ret;
    382		}
    383	}
    384
    385	clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config);
    386	clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config);
    387	clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config);
    388	clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config);
    389
    390	/* Enable alt PLLs */
    391	clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk);
    392	clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk);
    393
    394	clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb);
    395	clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb);
    396
    397	return ret;
    398}
    399
    400static int qcom_cpu_clk_msm8996_unregister_clks(void)
    401{
    402	int ret = 0;
    403
    404	ret = clk_notifier_unregister(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb);
    405	if (ret)
    406		return ret;
    407
    408	ret = clk_notifier_unregister(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb);
    409	if (ret)
    410		return ret;
    411
    412	clk_hw_unregister(perfcl_smux.pll);
    413	clk_hw_unregister(pwrcl_smux.pll);
    414
    415	return 0;
    416}
    417
    418#define CPU_AFINITY_MASK 0xFFF
    419#define PWRCL_CPU_REG_MASK 0x3
    420#define PERFCL_CPU_REG_MASK 0x103
    421
    422#define L2ACDCR_REG 0x580ULL
    423#define L2ACDTD_REG 0x581ULL
    424#define L2ACDDVMRC_REG 0x584ULL
    425#define L2ACDSSCR_REG 0x589ULL
    426
    427static DEFINE_SPINLOCK(qcom_clk_acd_lock);
    428static void __iomem *base;
    429
    430static void qcom_cpu_clk_msm8996_acd_init(void __iomem *base)
    431{
    432	u64 hwid;
    433	unsigned long flags;
    434
    435	spin_lock_irqsave(&qcom_clk_acd_lock, flags);
    436
    437	hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK;
    438
    439	kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11);
    440	kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f);
    441	kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601);
    442
    443	if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) {
    444		writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET);
    445		kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd);
    446	}
    447
    448	if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) {
    449		kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd);
    450		writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET);
    451	}
    452
    453	spin_unlock_irqrestore(&qcom_clk_acd_lock, flags);
    454}
    455
    456static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
    457			       void *data)
    458{
    459	struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb);
    460	struct clk_notifier_data *cnd = data;
    461	int ret;
    462
    463	switch (event) {
    464	case PRE_RATE_CHANGE:
    465		ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX);
    466		qcom_cpu_clk_msm8996_acd_init(base);
    467		break;
    468	case POST_RATE_CHANGE:
    469		if (cnd->new_rate < DIV_2_THRESHOLD)
    470			ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw,
    471							  DIV_2_INDEX);
    472		else
    473			ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw,
    474							  ACD_INDEX);
    475		break;
    476	default:
    477		ret = 0;
    478		break;
    479	}
    480
    481	return notifier_from_errno(ret);
    482};
    483
    484static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
    485{
    486	struct regmap *regmap;
    487	struct clk_hw_onecell_data *data;
    488	struct device *dev = &pdev->dev;
    489	int ret;
    490
    491	data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL);
    492	if (!data)
    493		return -ENOMEM;
    494
    495	base = devm_platform_ioremap_resource(pdev, 0);
    496	if (IS_ERR(base))
    497		return PTR_ERR(base);
    498
    499	regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config);
    500	if (IS_ERR(regmap))
    501		return PTR_ERR(regmap);
    502
    503	ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap);
    504	if (ret)
    505		return ret;
    506
    507	qcom_cpu_clk_msm8996_acd_init(base);
    508
    509	data->hws[0] = &pwrcl_pmux.clkr.hw;
    510	data->hws[1] = &perfcl_pmux.clkr.hw;
    511	data->num = 2;
    512
    513	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
    514}
    515
    516static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev)
    517{
    518	return qcom_cpu_clk_msm8996_unregister_clks();
    519}
    520
    521static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = {
    522	{ .compatible = "qcom,msm8996-apcc" },
    523	{}
    524};
    525MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table);
    526
    527static struct platform_driver qcom_cpu_clk_msm8996_driver = {
    528	.probe = qcom_cpu_clk_msm8996_driver_probe,
    529	.remove = qcom_cpu_clk_msm8996_driver_remove,
    530	.driver = {
    531		.name = "qcom-msm8996-apcc",
    532		.of_match_table = qcom_cpu_clk_msm8996_match_table,
    533	},
    534};
    535module_platform_driver(qcom_cpu_clk_msm8996_driver);
    536
    537MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver");
    538MODULE_LICENSE("GPL v2");