Merge branch 'clk-qcom' into clk-next
- Enable CPU clks on Qualcomm MSM8996 SoCs * clk-qcom: clk: qcom: Add CPU clock driver for msm8996 dt-bindings: clk: qcom: Add bindings for CPU clock for msm8996 soc: qcom: Separate kryo l2 accessors from PMU driver clk: qcom: Fix return value check in apss_ipq6018_probe()
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/clock/qcom,kryocc.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Qualcomm clock controller for MSM8996 CPUs
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Loic Poulain <loic.poulain@linaro.org>
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Qualcomm CPU clock controller for MSM8996 CPUs, clock 0 is for Power cluster
|
||||||
|
and clock 1 is for Perf cluster.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- qcom,msm8996-apcc
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
'#clock-cells':
|
||||||
|
const: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
items:
|
||||||
|
- description: Primary PLL clock for power cluster (little)
|
||||||
|
- description: Primary PLL clock for perf cluster (big)
|
||||||
|
- description: Alternate PLL clock for power cluster (little)
|
||||||
|
- description: Alternate PLL clock for perf cluster (big)
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
items:
|
||||||
|
- const: pwrcl_pll
|
||||||
|
- const: perfcl_pll
|
||||||
|
- const: pwrcl_alt_pll
|
||||||
|
- const: perfcl_alt_pll
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- '#clock-cells'
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
# Example for msm8996
|
||||||
|
- |
|
||||||
|
kryocc: clock-controller@6400000 {
|
||||||
|
compatible = "qcom,msm8996-apcc";
|
||||||
|
reg = <0x6400000 0x90000>;
|
||||||
|
#clock-cells = <1>;
|
||||||
|
};
|
||||||
|
...
|
||||||
@@ -37,6 +37,15 @@ config QCOM_CLK_APCS_MSM8916
|
|||||||
Say Y if you want to support CPU frequency scaling on devices
|
Say Y if you want to support CPU frequency scaling on devices
|
||||||
such as msm8916.
|
such as msm8916.
|
||||||
|
|
||||||
|
config QCOM_CLK_APCC_MSM8996
|
||||||
|
tristate "MSM8996 CPU Clock Controller"
|
||||||
|
select QCOM_KRYO_L2_ACCESSORS
|
||||||
|
depends on ARM64
|
||||||
|
help
|
||||||
|
Support for the CPU clock controller on msm8996 devices.
|
||||||
|
Say Y if you want to support CPU clock scaling using CPUfreq
|
||||||
|
drivers for dyanmic power management.
|
||||||
|
|
||||||
config QCOM_CLK_RPM
|
config QCOM_CLK_RPM
|
||||||
tristate "RPM based Clock Controller"
|
tristate "RPM based Clock Controller"
|
||||||
depends on MFD_QCOM_RPM
|
depends on MFD_QCOM_RPM
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
|
|||||||
obj-$(CONFIG_MSM_MMCC_8998) += mmcc-msm8998.o
|
obj-$(CONFIG_MSM_MMCC_8998) += mmcc-msm8998.o
|
||||||
obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o
|
obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o
|
||||||
obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o
|
obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o
|
||||||
|
obj-$(CONFIG_QCOM_CLK_APCC_MSM8996) += clk-cpu-8996.o
|
||||||
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
|
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
|
||||||
obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o
|
obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o
|
||||||
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
|
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ static int apss_ipq6018_probe(struct platform_device *pdev)
|
|||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
|
||||||
regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
if (IS_ERR(regmap))
|
if (!regmap)
|
||||||
return PTR_ERR(regmap);
|
return -ENODEV;
|
||||||
|
|
||||||
return qcom_cc_really_probe(pdev, &apss_ipq6018_desc, regmap);
|
return qcom_cc_really_probe(pdev, &apss_ipq6018_desc, regmap);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ struct pll_vco {
|
|||||||
u32 val;
|
u32 val;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define VCO(a, b, c) { \
|
||||||
|
.val = a,\
|
||||||
|
.min_freq = b,\
|
||||||
|
.max_freq = c,\
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct clk_alpha_pll - phase locked loop (PLL)
|
* struct clk_alpha_pll - phase locked loop (PLL)
|
||||||
* @offset: base address of registers
|
* @offset: base address of registers
|
||||||
|
|||||||
538
drivers/clk/qcom/clk-cpu-8996.c
Normal file
538
drivers/clk/qcom/clk-cpu-8996.c
Normal file
@@ -0,0 +1,538 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Each of the CPU clusters (Power and Perf) on msm8996 are
|
||||||
|
* clocked via 2 PLLs, a primary and alternate. There are also
|
||||||
|
* 2 Mux'es, a primary and secondary all connected together
|
||||||
|
* as shown below
|
||||||
|
*
|
||||||
|
* +-------+
|
||||||
|
* XO | |
|
||||||
|
* +------------------>0 |
|
||||||
|
* | |
|
||||||
|
* PLL/2 | SMUX +----+
|
||||||
|
* +------->1 | |
|
||||||
|
* | | | |
|
||||||
|
* | +-------+ | +-------+
|
||||||
|
* | +---->0 |
|
||||||
|
* | | |
|
||||||
|
* +---------------+ | +----------->1 | CPU clk
|
||||||
|
* |Primary PLL +----+ PLL_EARLY | | +------>
|
||||||
|
* | +------+-----------+ +------>2 PMUX |
|
||||||
|
* +---------------+ | | | |
|
||||||
|
* | +------+ | +-->3 |
|
||||||
|
* +--^+ ACD +-----+ | +-------+
|
||||||
|
* +---------------+ +------+ |
|
||||||
|
* |Alt PLL | |
|
||||||
|
* | +---------------------------+
|
||||||
|
* +---------------+ PLL_EARLY
|
||||||
|
*
|
||||||
|
* The primary PLL is what drives the CPU clk, except for times
|
||||||
|
* when we are reprogramming the PLL itself (for rate changes) when
|
||||||
|
* we temporarily switch to an alternate PLL.
|
||||||
|
*
|
||||||
|
* The primary PLL operates on a single VCO range, between 600MHz
|
||||||
|
* and 3GHz. However the CPUs do support OPPs with frequencies
|
||||||
|
* between 300MHz and 600MHz. In order to support running the CPUs
|
||||||
|
* at those frequencies we end up having to lock the PLL at twice
|
||||||
|
* the rate and drive the CPU clk via the PLL/2 output and SMUX.
|
||||||
|
*
|
||||||
|
* So for frequencies above 600MHz we follow the following path
|
||||||
|
* Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk
|
||||||
|
* and for frequencies between 300MHz and 600MHz we follow
|
||||||
|
* Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk
|
||||||
|
*
|
||||||
|
* ACD stands for Adaptive Clock Distribution and is used to
|
||||||
|
* detect voltage droops.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <soc/qcom/kryo-l2-accessors.h>
|
||||||
|
|
||||||
|
#include "clk-alpha-pll.h"
|
||||||
|
#include "clk-regmap.h"
|
||||||
|
|
||||||
|
enum _pmux_input {
|
||||||
|
DIV_2_INDEX = 0,
|
||||||
|
PLL_INDEX,
|
||||||
|
ACD_INDEX,
|
||||||
|
ALT_INDEX,
|
||||||
|
NUM_OF_PMUX_INPUTS
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DIV_2_THRESHOLD 600000000
|
||||||
|
#define PWRCL_REG_OFFSET 0x0
|
||||||
|
#define PERFCL_REG_OFFSET 0x80000
|
||||||
|
#define MUX_OFFSET 0x40
|
||||||
|
#define ALT_PLL_OFFSET 0x100
|
||||||
|
#define SSSCTL_OFFSET 0x160
|
||||||
|
|
||||||
|
static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = {
|
||||||
|
[PLL_OFF_L_VAL] = 0x04,
|
||||||
|
[PLL_OFF_ALPHA_VAL] = 0x08,
|
||||||
|
[PLL_OFF_USER_CTL] = 0x10,
|
||||||
|
[PLL_OFF_CONFIG_CTL] = 0x18,
|
||||||
|
[PLL_OFF_CONFIG_CTL_U] = 0x1c,
|
||||||
|
[PLL_OFF_TEST_CTL] = 0x20,
|
||||||
|
[PLL_OFF_TEST_CTL_U] = 0x24,
|
||||||
|
[PLL_OFF_STATUS] = 0x28,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = {
|
||||||
|
[PLL_OFF_L_VAL] = 0x04,
|
||||||
|
[PLL_OFF_ALPHA_VAL] = 0x08,
|
||||||
|
[PLL_OFF_ALPHA_VAL_U] = 0x0c,
|
||||||
|
[PLL_OFF_USER_CTL] = 0x10,
|
||||||
|
[PLL_OFF_USER_CTL_U] = 0x14,
|
||||||
|
[PLL_OFF_CONFIG_CTL] = 0x18,
|
||||||
|
[PLL_OFF_TEST_CTL] = 0x20,
|
||||||
|
[PLL_OFF_TEST_CTL_U] = 0x24,
|
||||||
|
[PLL_OFF_STATUS] = 0x28,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* PLLs */
|
||||||
|
|
||||||
|
static const struct alpha_pll_config hfpll_config = {
|
||||||
|
.l = 60,
|
||||||
|
.config_ctl_val = 0x200d4aa8,
|
||||||
|
.config_ctl_hi_val = 0x006,
|
||||||
|
.pre_div_mask = BIT(12),
|
||||||
|
.post_div_mask = 0x3 << 8,
|
||||||
|
.post_div_val = 0x1 << 8,
|
||||||
|
.main_output_mask = BIT(0),
|
||||||
|
.early_output_mask = BIT(3),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk_alpha_pll perfcl_pll = {
|
||||||
|
.offset = PERFCL_REG_OFFSET,
|
||||||
|
.regs = prim_pll_regs,
|
||||||
|
.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE,
|
||||||
|
.clkr.hw.init = &(struct clk_init_data){
|
||||||
|
.name = "perfcl_pll",
|
||||||
|
.parent_names = (const char *[]){ "xo" },
|
||||||
|
.num_parents = 1,
|
||||||
|
.ops = &clk_alpha_pll_huayra_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk_alpha_pll pwrcl_pll = {
|
||||||
|
.offset = PWRCL_REG_OFFSET,
|
||||||
|
.regs = prim_pll_regs,
|
||||||
|
.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE,
|
||||||
|
.clkr.hw.init = &(struct clk_init_data){
|
||||||
|
.name = "pwrcl_pll",
|
||||||
|
.parent_names = (const char *[]){ "xo" },
|
||||||
|
.num_parents = 1,
|
||||||
|
.ops = &clk_alpha_pll_huayra_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pll_vco alt_pll_vco_modes[] = {
|
||||||
|
VCO(3, 250000000, 500000000),
|
||||||
|
VCO(2, 500000000, 750000000),
|
||||||
|
VCO(1, 750000000, 1000000000),
|
||||||
|
VCO(0, 1000000000, 2150400000),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct alpha_pll_config altpll_config = {
|
||||||
|
.l = 16,
|
||||||
|
.vco_val = 0x3 << 20,
|
||||||
|
.vco_mask = 0x3 << 20,
|
||||||
|
.config_ctl_val = 0x4001051b,
|
||||||
|
.post_div_mask = 0x3 << 8,
|
||||||
|
.post_div_val = 0x1 << 8,
|
||||||
|
.main_output_mask = BIT(0),
|
||||||
|
.early_output_mask = BIT(3),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk_alpha_pll perfcl_alt_pll = {
|
||||||
|
.offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET,
|
||||||
|
.regs = alt_pll_regs,
|
||||||
|
.vco_table = alt_pll_vco_modes,
|
||||||
|
.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
|
||||||
|
.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
|
||||||
|
.clkr.hw.init = &(struct clk_init_data) {
|
||||||
|
.name = "perfcl_alt_pll",
|
||||||
|
.parent_names = (const char *[]){ "xo" },
|
||||||
|
.num_parents = 1,
|
||||||
|
.ops = &clk_alpha_pll_hwfsm_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk_alpha_pll pwrcl_alt_pll = {
|
||||||
|
.offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET,
|
||||||
|
.regs = alt_pll_regs,
|
||||||
|
.vco_table = alt_pll_vco_modes,
|
||||||
|
.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
|
||||||
|
.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
|
||||||
|
.clkr.hw.init = &(struct clk_init_data) {
|
||||||
|
.name = "pwrcl_alt_pll",
|
||||||
|
.parent_names = (const char *[]){ "xo" },
|
||||||
|
.num_parents = 1,
|
||||||
|
.ops = &clk_alpha_pll_hwfsm_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct clk_cpu_8996_mux {
|
||||||
|
u32 reg;
|
||||||
|
u8 shift;
|
||||||
|
u8 width;
|
||||||
|
struct notifier_block nb;
|
||||||
|
struct clk_hw *pll;
|
||||||
|
struct clk_hw *pll_div_2;
|
||||||
|
struct clk_regmap clkr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
#define to_clk_cpu_8996_mux_nb(_nb) \
|
||||||
|
container_of(_nb, struct clk_cpu_8996_mux, nb)
|
||||||
|
|
||||||
|
static inline struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct clk_regmap *clkr = to_clk_regmap(hw);
|
||||||
|
struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
|
||||||
|
u32 mask = GENMASK(cpuclk->width - 1, 0);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
regmap_read(clkr->regmap, cpuclk->reg, &val);
|
||||||
|
val >>= cpuclk->shift;
|
||||||
|
|
||||||
|
return val & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||||
|
{
|
||||||
|
struct clk_regmap *clkr = to_clk_regmap(hw);
|
||||||
|
struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
|
||||||
|
u32 mask = GENMASK(cpuclk->width + cpuclk->shift - 1, cpuclk->shift);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = index;
|
||||||
|
val <<= cpuclk->shift;
|
||||||
|
|
||||||
|
return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int clk_cpu_8996_mux_determine_rate(struct clk_hw *hw,
|
||||||
|
struct clk_rate_request *req)
|
||||||
|
{
|
||||||
|
struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
|
||||||
|
struct clk_hw *parent = cpuclk->pll;
|
||||||
|
|
||||||
|
if (cpuclk->pll_div_2 && req->rate < DIV_2_THRESHOLD) {
|
||||||
|
if (req->rate < (DIV_2_THRESHOLD / 2))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
parent = cpuclk->pll_div_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
|
||||||
|
req->best_parent_hw = parent;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct clk_ops clk_cpu_8996_mux_ops = {
|
||||||
|
.set_parent = clk_cpu_8996_mux_set_parent,
|
||||||
|
.get_parent = clk_cpu_8996_mux_get_parent,
|
||||||
|
.determine_rate = clk_cpu_8996_mux_determine_rate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk_cpu_8996_mux pwrcl_smux = {
|
||||||
|
.reg = PWRCL_REG_OFFSET + MUX_OFFSET,
|
||||||
|
.shift = 2,
|
||||||
|
.width = 2,
|
||||||
|
.clkr.hw.init = &(struct clk_init_data) {
|
||||||
|
.name = "pwrcl_smux",
|
||||||
|
.parent_names = (const char *[]){
|
||||||
|
"xo",
|
||||||
|
"pwrcl_pll_main",
|
||||||
|
},
|
||||||
|
.num_parents = 2,
|
||||||
|
.ops = &clk_cpu_8996_mux_ops,
|
||||||
|
.flags = CLK_SET_RATE_PARENT,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk_cpu_8996_mux perfcl_smux = {
|
||||||
|
.reg = PERFCL_REG_OFFSET + MUX_OFFSET,
|
||||||
|
.shift = 2,
|
||||||
|
.width = 2,
|
||||||
|
.clkr.hw.init = &(struct clk_init_data) {
|
||||||
|
.name = "perfcl_smux",
|
||||||
|
.parent_names = (const char *[]){
|
||||||
|
"xo",
|
||||||
|
"perfcl_pll_main",
|
||||||
|
},
|
||||||
|
.num_parents = 2,
|
||||||
|
.ops = &clk_cpu_8996_mux_ops,
|
||||||
|
.flags = CLK_SET_RATE_PARENT,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk_cpu_8996_mux pwrcl_pmux = {
|
||||||
|
.reg = PWRCL_REG_OFFSET + MUX_OFFSET,
|
||||||
|
.shift = 0,
|
||||||
|
.width = 2,
|
||||||
|
.pll = &pwrcl_pll.clkr.hw,
|
||||||
|
.pll_div_2 = &pwrcl_smux.clkr.hw,
|
||||||
|
.nb.notifier_call = cpu_clk_notifier_cb,
|
||||||
|
.clkr.hw.init = &(struct clk_init_data) {
|
||||||
|
.name = "pwrcl_pmux",
|
||||||
|
.parent_names = (const char *[]){
|
||||||
|
"pwrcl_smux",
|
||||||
|
"pwrcl_pll",
|
||||||
|
"pwrcl_pll_acd",
|
||||||
|
"pwrcl_alt_pll",
|
||||||
|
},
|
||||||
|
.num_parents = 4,
|
||||||
|
.ops = &clk_cpu_8996_mux_ops,
|
||||||
|
/* CPU clock is critical and should never be gated */
|
||||||
|
.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk_cpu_8996_mux perfcl_pmux = {
|
||||||
|
.reg = PERFCL_REG_OFFSET + MUX_OFFSET,
|
||||||
|
.shift = 0,
|
||||||
|
.width = 2,
|
||||||
|
.pll = &perfcl_pll.clkr.hw,
|
||||||
|
.pll_div_2 = &perfcl_smux.clkr.hw,
|
||||||
|
.nb.notifier_call = cpu_clk_notifier_cb,
|
||||||
|
.clkr.hw.init = &(struct clk_init_data) {
|
||||||
|
.name = "perfcl_pmux",
|
||||||
|
.parent_names = (const char *[]){
|
||||||
|
"perfcl_smux",
|
||||||
|
"perfcl_pll",
|
||||||
|
"perfcl_pll_acd",
|
||||||
|
"perfcl_alt_pll",
|
||||||
|
},
|
||||||
|
.num_parents = 4,
|
||||||
|
.ops = &clk_cpu_8996_mux_ops,
|
||||||
|
/* CPU clock is critical and should never be gated */
|
||||||
|
.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_config cpu_msm8996_regmap_config = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.val_bits = 32,
|
||||||
|
.max_register = 0x80210,
|
||||||
|
.fast_io = true,
|
||||||
|
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct clk_regmap *cpu_msm8996_clks[] = {
|
||||||
|
&perfcl_pll.clkr,
|
||||||
|
&pwrcl_pll.clkr,
|
||||||
|
&perfcl_alt_pll.clkr,
|
||||||
|
&pwrcl_alt_pll.clkr,
|
||||||
|
&perfcl_smux.clkr,
|
||||||
|
&pwrcl_smux.clkr,
|
||||||
|
&perfcl_pmux.clkr,
|
||||||
|
&pwrcl_pmux.clkr,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int qcom_cpu_clk_msm8996_register_clks(struct device *dev,
|
||||||
|
struct regmap *regmap)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
perfcl_smux.pll = clk_hw_register_fixed_factor(dev, "perfcl_pll_main",
|
||||||
|
"perfcl_pll",
|
||||||
|
CLK_SET_RATE_PARENT,
|
||||||
|
1, 2);
|
||||||
|
if (IS_ERR(perfcl_smux.pll)) {
|
||||||
|
dev_err(dev, "Failed to initialize perfcl_pll_main\n");
|
||||||
|
return PTR_ERR(perfcl_smux.pll);
|
||||||
|
}
|
||||||
|
|
||||||
|
pwrcl_smux.pll = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main",
|
||||||
|
"pwrcl_pll",
|
||||||
|
CLK_SET_RATE_PARENT,
|
||||||
|
1, 2);
|
||||||
|
if (IS_ERR(pwrcl_smux.pll)) {
|
||||||
|
dev_err(dev, "Failed to initialize pwrcl_pll_main\n");
|
||||||
|
clk_hw_unregister(perfcl_smux.pll);
|
||||||
|
return PTR_ERR(pwrcl_smux.pll);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) {
|
||||||
|
ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]);
|
||||||
|
if (ret) {
|
||||||
|
clk_hw_unregister(perfcl_smux.pll);
|
||||||
|
clk_hw_unregister(pwrcl_smux.pll);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config);
|
||||||
|
clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config);
|
||||||
|
clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config);
|
||||||
|
clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config);
|
||||||
|
|
||||||
|
/* Enable alt PLLs */
|
||||||
|
clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk);
|
||||||
|
clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk);
|
||||||
|
|
||||||
|
clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb);
|
||||||
|
clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcom_cpu_clk_msm8996_unregister_clks(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = clk_notifier_unregister(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = clk_notifier_unregister(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
clk_hw_unregister(perfcl_smux.pll);
|
||||||
|
clk_hw_unregister(pwrcl_smux.pll);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CPU_AFINITY_MASK 0xFFF
|
||||||
|
#define PWRCL_CPU_REG_MASK 0x3
|
||||||
|
#define PERFCL_CPU_REG_MASK 0x103
|
||||||
|
|
||||||
|
#define L2ACDCR_REG 0x580ULL
|
||||||
|
#define L2ACDTD_REG 0x581ULL
|
||||||
|
#define L2ACDDVMRC_REG 0x584ULL
|
||||||
|
#define L2ACDSSCR_REG 0x589ULL
|
||||||
|
|
||||||
|
static DEFINE_SPINLOCK(qcom_clk_acd_lock);
|
||||||
|
static void __iomem *base;
|
||||||
|
|
||||||
|
static void qcom_cpu_clk_msm8996_acd_init(void __iomem *base)
|
||||||
|
{
|
||||||
|
u64 hwid;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&qcom_clk_acd_lock, flags);
|
||||||
|
|
||||||
|
hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK;
|
||||||
|
|
||||||
|
kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11);
|
||||||
|
kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f);
|
||||||
|
kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601);
|
||||||
|
|
||||||
|
if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) {
|
||||||
|
writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET);
|
||||||
|
kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) {
|
||||||
|
kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd);
|
||||||
|
writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&qcom_clk_acd_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb);
|
||||||
|
struct clk_notifier_data *cnd = data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case PRE_RATE_CHANGE:
|
||||||
|
ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX);
|
||||||
|
qcom_cpu_clk_msm8996_acd_init(base);
|
||||||
|
break;
|
||||||
|
case POST_RATE_CHANGE:
|
||||||
|
if (cnd->new_rate < DIV_2_THRESHOLD)
|
||||||
|
ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw,
|
||||||
|
DIV_2_INDEX);
|
||||||
|
else
|
||||||
|
ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw,
|
||||||
|
ACD_INDEX);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return notifier_from_errno(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct clk_hw_onecell_data *data;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
|
if (IS_ERR(base))
|
||||||
|
return PTR_ERR(base);
|
||||||
|
|
||||||
|
regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config);
|
||||||
|
if (IS_ERR(regmap))
|
||||||
|
return PTR_ERR(regmap);
|
||||||
|
|
||||||
|
ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
qcom_cpu_clk_msm8996_acd_init(base);
|
||||||
|
|
||||||
|
data->hws[0] = &pwrcl_pmux.clkr.hw;
|
||||||
|
data->hws[1] = &perfcl_pmux.clkr.hw;
|
||||||
|
data->num = 2;
|
||||||
|
|
||||||
|
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return qcom_cpu_clk_msm8996_unregister_clks();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = {
|
||||||
|
{ .compatible = "qcom,msm8996-apcc" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table);
|
||||||
|
|
||||||
|
static struct platform_driver qcom_cpu_clk_msm8996_driver = {
|
||||||
|
.probe = qcom_cpu_clk_msm8996_driver_probe,
|
||||||
|
.remove = qcom_cpu_clk_msm8996_driver_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "qcom-msm8996-apcc",
|
||||||
|
.of_match_table = qcom_cpu_clk_msm8996_match_table,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(qcom_cpu_clk_msm8996_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
@@ -82,6 +82,7 @@ config FSL_IMX8_DDR_PMU
|
|||||||
config QCOM_L2_PMU
|
config QCOM_L2_PMU
|
||||||
bool "Qualcomm Technologies L2-cache PMU"
|
bool "Qualcomm Technologies L2-cache PMU"
|
||||||
depends on ARCH_QCOM && ARM64 && ACPI
|
depends on ARCH_QCOM && ARM64 && ACPI
|
||||||
|
select QCOM_KRYO_L2_ACCESSORS
|
||||||
help
|
help
|
||||||
Provides support for the L2 cache performance monitor unit (PMU)
|
Provides support for the L2 cache performance monitor unit (PMU)
|
||||||
in Qualcomm Technologies processors.
|
in Qualcomm Technologies processors.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <asm/barrier.h>
|
#include <asm/barrier.h>
|
||||||
#include <asm/local64.h>
|
#include <asm/local64.h>
|
||||||
#include <asm/sysreg.h>
|
#include <asm/sysreg.h>
|
||||||
|
#include <soc/qcom/kryo-l2-accessors.h>
|
||||||
|
|
||||||
#define MAX_L2_CTRS 9
|
#define MAX_L2_CTRS 9
|
||||||
|
|
||||||
@@ -79,8 +80,6 @@
|
|||||||
#define L2_COUNTER_RELOAD BIT_ULL(31)
|
#define L2_COUNTER_RELOAD BIT_ULL(31)
|
||||||
#define L2_CYCLE_COUNTER_RELOAD BIT_ULL(63)
|
#define L2_CYCLE_COUNTER_RELOAD BIT_ULL(63)
|
||||||
|
|
||||||
#define L2CPUSRSELR_EL1 sys_reg(3, 3, 15, 0, 6)
|
|
||||||
#define L2CPUSRDR_EL1 sys_reg(3, 3, 15, 0, 7)
|
|
||||||
|
|
||||||
#define reg_idx(reg, i) (((i) * IA_L2_REG_OFFSET) + reg##_BASE)
|
#define reg_idx(reg, i) (((i) * IA_L2_REG_OFFSET) + reg##_BASE)
|
||||||
|
|
||||||
@@ -99,48 +98,7 @@
|
|||||||
#define L2_EVENT_STREX 0x421
|
#define L2_EVENT_STREX 0x421
|
||||||
#define L2_EVENT_CLREX 0x422
|
#define L2_EVENT_CLREX 0x422
|
||||||
|
|
||||||
static DEFINE_RAW_SPINLOCK(l2_access_lock);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set_l2_indirect_reg: write value to an L2 register
|
|
||||||
* @reg: Address of L2 register.
|
|
||||||
* @value: Value to be written to register.
|
|
||||||
*
|
|
||||||
* Use architecturally required barriers for ordering between system register
|
|
||||||
* accesses
|
|
||||||
*/
|
|
||||||
static void set_l2_indirect_reg(u64 reg, u64 val)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&l2_access_lock, flags);
|
|
||||||
write_sysreg_s(reg, L2CPUSRSELR_EL1);
|
|
||||||
isb();
|
|
||||||
write_sysreg_s(val, L2CPUSRDR_EL1);
|
|
||||||
isb();
|
|
||||||
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get_l2_indirect_reg: read an L2 register value
|
|
||||||
* @reg: Address of L2 register.
|
|
||||||
*
|
|
||||||
* Use architecturally required barriers for ordering between system register
|
|
||||||
* accesses
|
|
||||||
*/
|
|
||||||
static u64 get_l2_indirect_reg(u64 reg)
|
|
||||||
{
|
|
||||||
u64 val;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&l2_access_lock, flags);
|
|
||||||
write_sysreg_s(reg, L2CPUSRSELR_EL1);
|
|
||||||
isb();
|
|
||||||
val = read_sysreg_s(L2CPUSRDR_EL1);
|
|
||||||
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cluster_pmu;
|
struct cluster_pmu;
|
||||||
|
|
||||||
@@ -211,28 +169,28 @@ static inline struct cluster_pmu *get_cluster_pmu(
|
|||||||
static void cluster_pmu_reset(void)
|
static void cluster_pmu_reset(void)
|
||||||
{
|
{
|
||||||
/* Reset all counters */
|
/* Reset all counters */
|
||||||
set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
|
kryo_l2_set_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
|
||||||
set_l2_indirect_reg(L2PMCNTENCLR, l2_counter_present_mask);
|
kryo_l2_set_indirect_reg(L2PMCNTENCLR, l2_counter_present_mask);
|
||||||
set_l2_indirect_reg(L2PMINTENCLR, l2_counter_present_mask);
|
kryo_l2_set_indirect_reg(L2PMINTENCLR, l2_counter_present_mask);
|
||||||
set_l2_indirect_reg(L2PMOVSCLR, l2_counter_present_mask);
|
kryo_l2_set_indirect_reg(L2PMOVSCLR, l2_counter_present_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_enable(void)
|
static inline void cluster_pmu_enable(void)
|
||||||
{
|
{
|
||||||
set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_ENABLE);
|
kryo_l2_set_indirect_reg(L2PMCR, L2PMCR_COUNTERS_ENABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_disable(void)
|
static inline void cluster_pmu_disable(void)
|
||||||
{
|
{
|
||||||
set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_DISABLE);
|
kryo_l2_set_indirect_reg(L2PMCR, L2PMCR_COUNTERS_DISABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_counter_set_value(u32 idx, u64 value)
|
static inline void cluster_pmu_counter_set_value(u32 idx, u64 value)
|
||||||
{
|
{
|
||||||
if (idx == l2_cycle_ctr_idx)
|
if (idx == l2_cycle_ctr_idx)
|
||||||
set_l2_indirect_reg(L2PMCCNTR, value);
|
kryo_l2_set_indirect_reg(L2PMCCNTR, value);
|
||||||
else
|
else
|
||||||
set_l2_indirect_reg(reg_idx(IA_L2PMXEVCNTR, idx), value);
|
kryo_l2_set_indirect_reg(reg_idx(IA_L2PMXEVCNTR, idx), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u64 cluster_pmu_counter_get_value(u32 idx)
|
static inline u64 cluster_pmu_counter_get_value(u32 idx)
|
||||||
@@ -240,46 +198,46 @@ static inline u64 cluster_pmu_counter_get_value(u32 idx)
|
|||||||
u64 value;
|
u64 value;
|
||||||
|
|
||||||
if (idx == l2_cycle_ctr_idx)
|
if (idx == l2_cycle_ctr_idx)
|
||||||
value = get_l2_indirect_reg(L2PMCCNTR);
|
value = kryo_l2_get_indirect_reg(L2PMCCNTR);
|
||||||
else
|
else
|
||||||
value = get_l2_indirect_reg(reg_idx(IA_L2PMXEVCNTR, idx));
|
value = kryo_l2_get_indirect_reg(reg_idx(IA_L2PMXEVCNTR, idx));
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_counter_enable(u32 idx)
|
static inline void cluster_pmu_counter_enable(u32 idx)
|
||||||
{
|
{
|
||||||
set_l2_indirect_reg(L2PMCNTENSET, idx_to_reg_bit(idx));
|
kryo_l2_set_indirect_reg(L2PMCNTENSET, idx_to_reg_bit(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_counter_disable(u32 idx)
|
static inline void cluster_pmu_counter_disable(u32 idx)
|
||||||
{
|
{
|
||||||
set_l2_indirect_reg(L2PMCNTENCLR, idx_to_reg_bit(idx));
|
kryo_l2_set_indirect_reg(L2PMCNTENCLR, idx_to_reg_bit(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_counter_enable_interrupt(u32 idx)
|
static inline void cluster_pmu_counter_enable_interrupt(u32 idx)
|
||||||
{
|
{
|
||||||
set_l2_indirect_reg(L2PMINTENSET, idx_to_reg_bit(idx));
|
kryo_l2_set_indirect_reg(L2PMINTENSET, idx_to_reg_bit(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_counter_disable_interrupt(u32 idx)
|
static inline void cluster_pmu_counter_disable_interrupt(u32 idx)
|
||||||
{
|
{
|
||||||
set_l2_indirect_reg(L2PMINTENCLR, idx_to_reg_bit(idx));
|
kryo_l2_set_indirect_reg(L2PMINTENCLR, idx_to_reg_bit(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_set_evccntcr(u32 val)
|
static inline void cluster_pmu_set_evccntcr(u32 val)
|
||||||
{
|
{
|
||||||
set_l2_indirect_reg(L2PMCCNTCR, val);
|
kryo_l2_set_indirect_reg(L2PMCCNTCR, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_set_evcntcr(u32 ctr, u32 val)
|
static inline void cluster_pmu_set_evcntcr(u32 ctr, u32 val)
|
||||||
{
|
{
|
||||||
set_l2_indirect_reg(reg_idx(IA_L2PMXEVCNTCR, ctr), val);
|
kryo_l2_set_indirect_reg(reg_idx(IA_L2PMXEVCNTCR, ctr), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cluster_pmu_set_evtyper(u32 ctr, u32 val)
|
static inline void cluster_pmu_set_evtyper(u32 ctr, u32 val)
|
||||||
{
|
{
|
||||||
set_l2_indirect_reg(reg_idx(IA_L2PMXEVTYPER, ctr), val);
|
kryo_l2_set_indirect_reg(reg_idx(IA_L2PMXEVTYPER, ctr), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cluster_pmu_set_resr(struct cluster_pmu *cluster,
|
static void cluster_pmu_set_resr(struct cluster_pmu *cluster,
|
||||||
@@ -295,11 +253,11 @@ static void cluster_pmu_set_resr(struct cluster_pmu *cluster,
|
|||||||
|
|
||||||
spin_lock_irqsave(&cluster->pmu_lock, flags);
|
spin_lock_irqsave(&cluster->pmu_lock, flags);
|
||||||
|
|
||||||
resr_val = get_l2_indirect_reg(L2PMRESR);
|
resr_val = kryo_l2_get_indirect_reg(L2PMRESR);
|
||||||
resr_val &= ~(L2PMRESR_GROUP_MASK << shift);
|
resr_val &= ~(L2PMRESR_GROUP_MASK << shift);
|
||||||
resr_val |= field;
|
resr_val |= field;
|
||||||
resr_val |= L2PMRESR_EN;
|
resr_val |= L2PMRESR_EN;
|
||||||
set_l2_indirect_reg(L2PMRESR, resr_val);
|
kryo_l2_set_indirect_reg(L2PMRESR, resr_val);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&cluster->pmu_lock, flags);
|
spin_unlock_irqrestore(&cluster->pmu_lock, flags);
|
||||||
}
|
}
|
||||||
@@ -315,14 +273,14 @@ static inline void cluster_pmu_set_evfilter_sys_mode(u32 ctr)
|
|||||||
L2PMXEVFILTER_ORGFILTER_IDINDEP |
|
L2PMXEVFILTER_ORGFILTER_IDINDEP |
|
||||||
L2PMXEVFILTER_ORGFILTER_ALL;
|
L2PMXEVFILTER_ORGFILTER_ALL;
|
||||||
|
|
||||||
set_l2_indirect_reg(reg_idx(IA_L2PMXEVFILTER, ctr), val);
|
kryo_l2_set_indirect_reg(reg_idx(IA_L2PMXEVFILTER, ctr), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 cluster_pmu_getreset_ovsr(void)
|
static inline u32 cluster_pmu_getreset_ovsr(void)
|
||||||
{
|
{
|
||||||
u32 result = get_l2_indirect_reg(L2PMOVSSET);
|
u32 result = kryo_l2_get_indirect_reg(L2PMOVSSET);
|
||||||
|
|
||||||
set_l2_indirect_reg(L2PMOVSCLR, result);
|
kryo_l2_set_indirect_reg(L2PMOVSCLR, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -767,7 +725,7 @@ static int get_num_counters(void)
|
|||||||
{
|
{
|
||||||
int val;
|
int val;
|
||||||
|
|
||||||
val = get_l2_indirect_reg(L2PMCR);
|
val = kryo_l2_get_indirect_reg(L2PMCR);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read number of counters from L2PMCR and add 1
|
* Read number of counters from L2PMCR and add 1
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ config QCOM_LLCC
|
|||||||
SDM845. This provides interfaces to clients that use the LLCC.
|
SDM845. This provides interfaces to clients that use the LLCC.
|
||||||
Say yes here to enable LLCC slice driver.
|
Say yes here to enable LLCC slice driver.
|
||||||
|
|
||||||
|
config QCOM_KRYO_L2_ACCESSORS
|
||||||
|
bool
|
||||||
|
depends on ARCH_QCOM && ARM64 || COMPILE_TEST
|
||||||
|
|
||||||
config QCOM_MDT_LOADER
|
config QCOM_MDT_LOADER
|
||||||
tristate
|
tristate
|
||||||
select QCOM_SCM
|
select QCOM_SCM
|
||||||
|
|||||||
@@ -24,3 +24,4 @@ obj-$(CONFIG_QCOM_APR) += apr.o
|
|||||||
obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
|
obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
|
||||||
obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o
|
obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o
|
||||||
obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o
|
obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o
|
||||||
|
obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o
|
||||||
|
|||||||
57
drivers/soc/qcom/kryo-l2-accessors.c
Normal file
57
drivers/soc/qcom/kryo-l2-accessors.c
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <asm/barrier.h>
|
||||||
|
#include <asm/sysreg.h>
|
||||||
|
#include <soc/qcom/kryo-l2-accessors.h>
|
||||||
|
|
||||||
|
#define L2CPUSRSELR_EL1 sys_reg(3, 3, 15, 0, 6)
|
||||||
|
#define L2CPUSRDR_EL1 sys_reg(3, 3, 15, 0, 7)
|
||||||
|
|
||||||
|
static DEFINE_RAW_SPINLOCK(l2_access_lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kryo_l2_set_indirect_reg() - write value to an L2 register
|
||||||
|
* @reg: Address of L2 register.
|
||||||
|
* @value: Value to be written to register.
|
||||||
|
*
|
||||||
|
* Use architecturally required barriers for ordering between system register
|
||||||
|
* accesses, and system registers with respect to device memory
|
||||||
|
*/
|
||||||
|
void kryo_l2_set_indirect_reg(u64 reg, u64 val)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&l2_access_lock, flags);
|
||||||
|
write_sysreg_s(reg, L2CPUSRSELR_EL1);
|
||||||
|
isb();
|
||||||
|
write_sysreg_s(val, L2CPUSRDR_EL1);
|
||||||
|
isb();
|
||||||
|
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(kryo_l2_set_indirect_reg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kryo_l2_get_indirect_reg() - read an L2 register value
|
||||||
|
* @reg: Address of L2 register.
|
||||||
|
*
|
||||||
|
* Use architecturally required barriers for ordering between system register
|
||||||
|
* accesses, and system registers with respect to device memory
|
||||||
|
*/
|
||||||
|
u64 kryo_l2_get_indirect_reg(u64 reg)
|
||||||
|
{
|
||||||
|
u64 val;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&l2_access_lock, flags);
|
||||||
|
write_sysreg_s(reg, L2CPUSRSELR_EL1);
|
||||||
|
isb();
|
||||||
|
val = read_sysreg_s(L2CPUSRDR_EL1);
|
||||||
|
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(kryo_l2_get_indirect_reg);
|
||||||
12
include/soc/qcom/kryo-l2-accessors.h
Normal file
12
include/soc/qcom/kryo-l2-accessors.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SOC_ARCH_QCOM_KRYO_L2_ACCESSORS_H
|
||||||
|
#define __SOC_ARCH_QCOM_KRYO_L2_ACCESSORS_H
|
||||||
|
|
||||||
|
void kryo_l2_set_indirect_reg(u64 reg, u64 val);
|
||||||
|
u64 kryo_l2_get_indirect_reg(u64 reg);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user