aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinaro CI <ci_notify@linaro.org>2018-07-10 21:02:08 +0000
committerLinaro CI <ci_notify@linaro.org>2018-07-10 21:02:08 +0000
commitb6cc78aa6ec429b7f474efd7368a1da86dc02b57 (patch)
tree9c7b4ae295dc0d77ddfba71b7bcdc417354aa68f
parenta0b10df42645d7c4769d14c4ba9a4983aa790e64 (diff)
parentcf89899d571db142f11c165be9047e267a3262eb (diff)
Merge remote-tracking branch 'cpufreq-8996/tracking-qcomlt-8996-dvfs' into integration-linux-qcomlt
# Conflicts: # drivers/soc/qcom/Makefile
-rw-r--r--Documentation/devicetree/bindings/clock/qcom,kryocc.txt17
-rw-r--r--arch/arm64/Kconfig.platforms7
-rw-r--r--arch/arm64/boot/dts/qcom/apq8096-db820c.dts4
-rw-r--r--arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi2
-rw-r--r--arch/arm64/boot/dts/qcom/msm8996.dtsi286
-rw-r--r--arch/arm64/configs/defconfig2
-rw-r--r--drivers/clk/qcom/Kconfig8
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/clk-alpha-pll.c1
-rw-r--r--drivers/clk/qcom/clk-cpu-8996.c554
-rw-r--r--drivers/cpufreq/cpufreq-dt-platdev.c3
-rw-r--r--drivers/perf/qcom_l2_pmu.c50
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/kryo-l2-accessors.c64
-rw-r--r--include/soc/qcom/kryo-l2-accessors.h22
15 files changed, 958 insertions, 64 deletions
diff --git a/Documentation/devicetree/bindings/clock/qcom,kryocc.txt b/Documentation/devicetree/bindings/clock/qcom,kryocc.txt
new file mode 100644
index 000000000000..e7cf15a4ce64
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,kryocc.txt
@@ -0,0 +1,17 @@
+Qualcomm CPUSS clock controller for Kryo CPUs
+----------------------------------------------------
+
+Required properties :
+- compatible : shall contain only one of the following:
+
+ "qcom-msm8996-apcc"
+
+- reg : shall contain base register location and length
+- #clock-cells : shall contain 1
+
+Example:
+ kryocc: clock-controller@6400000 {
+ compatible = "qcom-msm8996-apcc";
+ reg = <0x6400000 0x90000>;
+ #clock-cells = <1>;
+ };
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index d5aeac351fc3..d69d383e962b 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -145,6 +145,13 @@ config ARCH_REALTEK
This enables support for the ARMv8 based Realtek chipsets,
like the RTD1295.
+config ARCH_MSM8996
+ bool "Enable Support for Qualcomm Technologies, Inc. MSM8996"
+ depends on ARCH_QCOM
+ help
+ This enables support for the MSM8996 chipset. If you do not
+ wish to build a kernel that runs on this chipset, say 'N' here.
+
config ARCH_ROCKCHIP
bool "Rockchip Platforms"
select ARCH_HAS_RESET_CONTROLLER
diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dts b/arch/arm64/boot/dts/qcom/apq8096-db820c.dts
index 230e9c8484ac..632cb42bdec5 100644
--- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dts
+++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dts
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2016,2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,5 +17,5 @@
/ {
model = "Qualcomm Technologies, Inc. DB820c";
- compatible = "arrow,apq8096-db820c", "qcom,apq8096-sbc";
+ compatible = "arrow,apq8096-db820c", "qcom,apq8096-sbc", "qcom,apq8096";
};
diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
index 4d5ef01f43a3..3151704dcce1 100644
--- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2016,2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi
index 8c7f9ca25b53..2aa6f08ca3dd 100644
--- a/arch/arm64/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015,2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,7 @@
#include <dt-bindings/clock/qcom,gcc-msm8996.h>
#include <dt-bindings/clock/qcom,mmcc-msm8996.h>
#include <dt-bindings/clock/qcom,rpmcc.h>
+#include <dt-bindings/thermal/thermal.h>
/ {
model = "Qualcomm Technologies, Inc. MSM8996";
@@ -97,6 +98,12 @@
compatible = "qcom,kryo";
reg = <0x0 0x0>;
enable-method = "psci";
+ clocks = <&kryocc 0>;
+ operating-points-v2 = <&cluster0_opp>;
+ /* cooling options */
+ cooling-min-level = <0>;
+ cooling-max-level = <15>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_0>;
L2_0: l2-cache {
compatible = "cache";
@@ -109,6 +116,12 @@
compatible = "qcom,kryo";
reg = <0x0 0x1>;
enable-method = "psci";
+ clocks = <&kryocc 0>;
+ operating-points-v2 = <&cluster0_opp>;
+ /* cooling options */
+ cooling-min-level = <0>;
+ cooling-max-level = <15>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_0>;
};
@@ -117,6 +130,12 @@
compatible = "qcom,kryo";
reg = <0x0 0x100>;
enable-method = "psci";
+ clocks = <&kryocc 1>;
+ operating-points-v2 = <&cluster1_opp>;
+ /* cooling options */
+ cooling-min-level = <0>;
+ cooling-max-level = <15>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_1>;
L2_1: l2-cache {
compatible = "cache";
@@ -129,6 +148,12 @@
compatible = "qcom,kryo";
reg = <0x0 0x101>;
enable-method = "psci";
+ clocks = <&kryocc 1>;
+ operating-points-v2 = <&cluster1_opp>;
+ /* cooling options */
+ cooling-min-level = <0>;
+ cooling-max-level = <15>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_1>;
};
@@ -155,6 +180,182 @@
};
};
+ cluster0_opp: opp_table0 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp@307200000 {
+ opp-hz = /bits/ 64 < 307200000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@422400000 {
+ opp-hz = /bits/ 64 < 422400000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@480000000 {
+ opp-hz = /bits/ 64 < 480000000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@556800000 {
+ opp-hz = /bits/ 64 < 556800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@652800000 {
+ opp-hz = /bits/ 64 < 652800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@729600000 {
+ opp-hz = /bits/ 64 < 729600000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@844800000 {
+ opp-hz = /bits/ 64 < 844800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@960000000 {
+ opp-hz = /bits/ 64 < 960000000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1036800000 {
+ opp-hz = /bits/ 64 < 1036800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1113600000 {
+ opp-hz = /bits/ 64 < 1113600000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1190400000 {
+ opp-hz = /bits/ 64 < 1190400000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1228800000 {
+ opp-hz = /bits/ 64 < 1228800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1324800000 {
+ opp-hz = /bits/ 64 < 1324800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1401600000 {
+ opp-hz = /bits/ 64 < 1401600000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1478400000 {
+ opp-hz = /bits/ 64 < 1478400000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1593600000 {
+ opp-hz = /bits/ 64 < 1593600000 >;
+ clock-latency-ns = <200000>;
+ };
+ };
+
+ cluster1_opp: opp_table1 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp@307200000 {
+ opp-hz = /bits/ 64 < 307200000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@403200000 {
+ opp-hz = /bits/ 64 < 403200000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@480000000 {
+ opp-hz = /bits/ 64 < 480000000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@556800000 {
+ opp-hz = /bits/ 64 < 556800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@652800000 {
+ opp-hz = /bits/ 64 < 652800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@729600000 {
+ opp-hz = /bits/ 64 < 729600000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@806400000 {
+ opp-hz = /bits/ 64 < 806400000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@883200000 {
+ opp-hz = /bits/ 64 < 883200000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@940800000 {
+ opp-hz = /bits/ 64 < 940800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1036800000 {
+ opp-hz = /bits/ 64 < 1036800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1113600000 {
+ opp-hz = /bits/ 64 < 1113600000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1190400000 {
+ opp-hz = /bits/ 64 < 1190400000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1248000000 {
+ opp-hz = /bits/ 64 < 1248000000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1324800000 {
+ opp-hz = /bits/ 64 < 1324800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1401600000 {
+ opp-hz = /bits/ 64 < 1401600000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1478400000 {
+ opp-hz = /bits/ 64 < 1478400000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1552000000 {
+ opp-hz = /bits/ 64 < 1552000000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1632000000 {
+ opp-hz = /bits/ 64 < 1632000000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1708800000 {
+ opp-hz = /bits/ 64 < 1708800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1785600000 {
+ opp-hz = /bits/ 64 < 1785600000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1824000000 {
+ opp-hz = /bits/ 64 < 1824000000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1920000000 {
+ opp-hz = /bits/ 64 < 1920000000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@1996800000 {
+ opp-hz = /bits/ 64 < 1996800000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@2073600000 {
+ opp-hz = /bits/ 64 < 2073600000 >;
+ clock-latency-ns = <200000>;
+ };
+ opp@2150400000 {
+ opp-hz = /bits/ 64 < 2150400000 >;
+ clock-latency-ns = <200000>;
+ };
+
+ };
thermal-zones {
cpu-thermal0 {
polling-delay-passive = <250>;
@@ -163,18 +364,33 @@
thermal-sensors = <&tsens0 3>;
trips {
- cpu_alert0: trip0 {
+ cpu_alert0: cpu_alert0 {
temperature = <75000>;
hysteresis = <2000>;
+ type = "active";
+ };
+ cpu_warn0: cpu_warn0 {
+ temperature = <90000>;
+ hysteresis = <2000>;
type = "passive";
};
-
- cpu_crit0: trip1 {
+ cpu_crit0: cpu_crit0 {
temperature = <110000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert0>;
+ cooling-device = <&CPU0 THERMAL_NO_LIMIT 7>;
+ };
+ map1 {
+ trip = <&cpu_warn0>;
+ cooling-device = <&CPU0 8 THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu-thermal1 {
@@ -184,18 +400,33 @@
thermal-sensors = <&tsens0 5>;
trips {
- cpu_alert1: trip0 {
+ cpu_alert1: cpu_alert1 {
temperature = <75000>;
hysteresis = <2000>;
+ type = "active";
+ };
+ cpu_warn1: cpu_warn1 {
+ temperature = <90000>;
+ hysteresis = <2000>;
type = "passive";
};
-
- cpu_crit1: trip1 {
+ cpu_crit1: cpu_crit1 {
temperature = <110000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&CPU0 THERMAL_NO_LIMIT 7>;
+ };
+ map1 {
+ trip = <&cpu_warn1>;
+ cooling-device = <&CPU0 8 THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu-thermal2 {
@@ -205,18 +436,32 @@
thermal-sensors = <&tsens0 8>;
trips {
- cpu_alert2: trip0 {
+ cpu_alert2: cpu_alert2 {
temperature = <75000>;
hysteresis = <2000>;
+ type = "active";
+ };
+ cpu_warn2: cpu_warn2 {
+ temperature = <90000>;
+ hysteresis = <2000>;
type = "passive";
};
-
- cpu_crit2: trip1 {
+ cpu_crit2: cpu_crit2 {
temperature = <110000>;
hysteresis = <2000>;
type = "critical";
};
};
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert2>;
+ cooling-device = <&CPU2 THERMAL_NO_LIMIT 7>;
+ };
+ map1 {
+ trip = <&cpu_warn2>;
+ cooling-device = <&CPU2 8 THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu-thermal3 {
@@ -226,18 +471,33 @@
thermal-sensors = <&tsens0 10>;
trips {
- cpu_alert3: trip0 {
+ cpu_alert3: cpu_alert3 {
temperature = <75000>;
hysteresis = <2000>;
+ type = "active";
+ };
+ cpu_warn3: cpu_warn3 {
+ temperature = <90000>;
+ hysteresis = <2000>;
type = "passive";
};
-
cpu_crit3: trip1 {
temperature = <110000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert3>;
+ cooling-device = <&CPU2 THERMAL_NO_LIMIT 7>;
+ };
+ map1 {
+ trip = <&cpu_warn3>;
+ cooling-device = <&CPU2 8 THERMAL_NO_LIMIT>;
+ };
+ };
};
};
@@ -414,7 +674,7 @@
};
kryocc: clock-controller@6400000 {
- compatible = "qcom,apcc-msm8996";
+ compatible = "qcom-msm8996-apcc";
reg = <0x6400000 0x90000>;
#clock-cells = <1>;
};
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 90b2b5f8bb26..b6655dcf7ba1 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -45,6 +45,7 @@ CONFIG_ARCH_MEDIATEK=y
CONFIG_ARCH_MESON=y
CONFIG_ARCH_MVEBU=y
CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8996=y
CONFIG_ARCH_ROCKCHIP=y
CONFIG_ARCH_SEATTLE=y
CONFIG_ARCH_SYNQUACER=y
@@ -561,6 +562,7 @@ CONFIG_IPQ_GCC_8074=y
CONFIG_MSM_GCC_8916=y
CONFIG_MSM_GCC_8994=y
CONFIG_MSM_MMCC_8996=y
+CONFIG_MSM_APCC_8996=y
CONFIG_HWSPINLOCK=y
CONFIG_HWSPINLOCK_QCOM=y
CONFIG_ARM_MHU=y
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 9c3480dcc38a..a89c8194e9a7 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -253,3 +253,11 @@ config SPMI_PMIC_CLKDIV
Technologies, Inc. SPMI PMIC. It configures the frequency of
clkdiv outputs of the PMIC. These clocks are typically wired
through alternate functions on GPIO pins.
+
+config MSM_APCC_8996
+ tristate "MSM8996 CPU Clock Controller"
+ depends on COMMON_CLK_QCOM
+ 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.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 762c01137c2f..00d5e6cda87e 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_MSM_GCC_8998) += gcc-msm8998.o
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
+obj-$(CONFIG_MSM_APCC_8996) += clk-cpu-8996.o
obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o
obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 3c49a60072f1..a43f80ac94a4 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -228,6 +228,7 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
if (pll->flags & SUPPORTS_FSM_MODE)
qcom_pll_set_fsm_mode(regmap, PLL_MODE(pll), 6, 0);
}
+EXPORT_SYMBOL_GPL(clk_alpha_pll_configure);
static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
{
diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c
new file mode 100644
index 000000000000..155279186c32
--- /dev/null
+++ b/drivers/clk/qcom/clk-cpu-8996.c
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/clk-provider.h>
+#include "clk-alpha-pll.h"
+#include <soc/qcom/kryo-l2-accessors.h>
+
+#define VCO(a, b, c) { \
+ .val = a,\
+ .min_freq = b,\
+ .max_freq = c,\
+}
+
+#define DIV_2_INDEX 0
+#define PLL_INDEX 1
+#define ACD_INDEX 2
+#define ALT_INDEX 3
+#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
+/*
+APCy_QLL_SSSCTL value:
+SACDRCLEN=1
+SSWEN=1
+SSTRTEN=1
+SSTPAPMSWEN=1
+*/
+#define SSSCTL_VAL 0xF
+
+enum {
+ APC_BASE,
+ EFUSE_BASE,
+ NUM_BASES
+};
+
+static void __iomem *vbases[NUM_BASES];
+
+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,
+ },
+};
+
+static void qcom_cpu_clk_msm8996_acd_init(void);
+
+/* Mux'es */
+
+struct clk_cpu_8996_mux {
+ u32 reg;
+ u32 shift;
+ u32 width;
+ struct notifier_block nb;
+ struct clk_hw *pll;
+ struct clk_hw *pll_div_2;
+ struct clk_regmap clkr;
+};
+
+#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)
+{
+ unsigned int val;
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+ struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
+ unsigned int mask = GENMASK(cpuclk->width - 1, 0);
+
+ regmap_read(clkr->regmap, cpuclk->reg, &val);
+
+ val >>= cpuclk->shift;
+ val &= mask;
+
+ return val;
+}
+
+static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ unsigned int val;
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+ struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
+ unsigned int mask = GENMASK(cpuclk->width + cpuclk->shift - 1,
+ cpuclk->shift);
+
+ 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)
+ return -EINVAL;
+
+ 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;
+}
+
+int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ int ret;
+ struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb);
+ struct clk_notifier_data *cnd = data;
+
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX);
+ qcom_cpu_clk_msm8996_acd_init();
+ 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);
+};
+
+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,
+ .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,
+ .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,
+};
+
+static const struct of_device_id match_table[] = {
+ { .compatible = "qcom-msm8996-apcc" },
+ {}
+};
+
+struct clk_regmap *clks[] = {
+ /* PLLs */
+ &perfcl_pll.clkr,
+ &pwrcl_pll.clkr,
+ &perfcl_alt_pll.clkr,
+ &pwrcl_alt_pll.clkr,
+ /* MUXs */
+ &perfcl_smux.clkr,
+ &pwrcl_smux.clkr,
+ &perfcl_pmux.clkr,
+ &pwrcl_pmux.clkr,
+};
+
+struct clk_hw_clks {
+ unsigned int num;
+ struct clk_hw *hws[];
+};
+
+static int
+qcom_cpu_clk_msm8996_register_clks(struct device *dev, struct clk_hw_clks *hws,
+ struct regmap *regmap)
+{
+ int i, ret;
+
+ hws->hws[0] = clk_hw_register_fixed_factor(dev, "perfcl_pll_main",
+ "perfcl_pll",
+ CLK_SET_RATE_PARENT, 1, 2);
+ perfcl_smux.pll = hws->hws[0];
+
+ hws->hws[1] = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main",
+ "pwrcl_pll",
+ CLK_SET_RATE_PARENT, 1, 2);
+ pwrcl_smux.pll = hws->hws[1];
+
+ hws->num = 2;
+
+ for (i = 0; i < ARRAY_SIZE(clks); i++) {
+ ret = devm_clk_register_regmap(dev, clks[i]);
+ if (ret)
+ 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 all PLLs and alt PLLs */
+ clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk);
+ clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk);
+ clk_prepare_enable(pwrcl_pll.clkr.hw.clk);
+ clk_prepare_enable(perfcl_pll.clkr.hw.clk);
+
+ /* Set initial boot frequencies for power/perf PLLs */
+ clk_set_rate(pwrcl_alt_pll.clkr.hw.clk, 652800000);
+ clk_set_rate(perfcl_alt_pll.clkr.hw.clk, 652800000);
+ clk_set_rate(pwrcl_pll.clkr.hw.clk, 652800000);
+ clk_set_rate(perfcl_pll.clkr.hw.clk, 652800000);
+
+ ret = clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb);
+ if (ret)
+ return ret;
+
+ ret = clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+#define CPU_AFINITY_MASK 0xFFF
+#define PWRCL_CPU_REG_MASK 0x3
+#define PERFCL_CPU_REG_MASK 0x103
+
+/* ACD static settings (HMSS HPG 7.2.2) */
+#define L2ACDCR_REG 0x580ULL
+#define L2ACDTD_REG 0x581ULL
+#define L2ACDDVMRC_REG 0x584ULL
+#define L2ACDSSCR_REG 0x589ULL
+#define ACDTD_VAL 0x00006A11
+#define ACDCR_VAL 0x002C5FFD
+#define ACDSSCR_VAL 0x00000601
+#define ACDDVMRC_VAL 0x000E0F0F
+
+static DEFINE_SPINLOCK(acd_lock);
+
+static void qcom_cpu_clk_msm8996_acd_init(void)
+{
+ u64 hwid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&acd_lock, flags);
+
+ hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK;
+
+ /* Program ACD Tunable-Length Delay (TLD) */
+ set_l2_indirect_reg(L2ACDTD_REG, ACDTD_VAL);
+ /* Initial ACD for *this* cluster */
+ set_l2_indirect_reg(L2ACDDVMRC_REG, ACDDVMRC_VAL);
+ /* Program ACD soft start control bits. */
+ set_l2_indirect_reg(L2ACDSSCR_REG, ACDSSCR_VAL);
+
+ if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) {
+ /* Enable Soft Stop/Start */
+ if (vbases[APC_BASE])
+ writel_relaxed(SSSCTL_VAL, vbases[APC_BASE] +
+ PWRCL_REG_OFFSET + SSSCTL_OFFSET);
+ /* Ensure SSSCTL config goes through before enabling ACD. */
+ mb();
+ /* Program ACD control bits */
+ set_l2_indirect_reg(L2ACDCR_REG, ACDCR_VAL);
+ }
+ if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) { //else {
+ /* Program ACD control bits */
+ set_l2_indirect_reg(L2ACDCR_REG, ACDCR_VAL);
+ /* Enable Soft Stop/Start */
+ if (vbases[APC_BASE])
+ writel_relaxed(SSSCTL_VAL, vbases[APC_BASE] +
+ PERFCL_REG_OFFSET + SSSCTL_OFFSET);
+ /* Ensure SSSCTL config goes through before enabling ACD. */
+ mb();
+ }
+
+ spin_unlock_irqrestore(&acd_lock, flags);
+}
+static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *res;
+ struct regmap *regmap_cpu;
+ struct clk_hw_clks *hws;
+ struct clk_hw_onecell_data *data;
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+
+ data = devm_kzalloc(dev, sizeof(*data) + 2 * sizeof(struct clk_hw *),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ hws = devm_kzalloc(dev, sizeof(*hws) + 4 * sizeof(struct clk_hw *),
+ GFP_KERNEL);
+ if (!hws)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ vbases[APC_BASE] = devm_ioremap_resource(dev, res);
+ if (IS_ERR(vbases[APC_BASE]))
+ return PTR_ERR(vbases[APC_BASE]);
+
+ regmap_cpu = devm_regmap_init_mmio(dev, vbases[APC_BASE],
+ &cpu_msm8996_regmap_config);
+ if (IS_ERR(regmap_cpu))
+ return PTR_ERR(regmap_cpu);
+
+ ret = qcom_cpu_clk_msm8996_register_clks(dev, hws, regmap_cpu);
+ if (ret)
+ return ret;
+ qcom_cpu_clk_msm8996_acd_init();
+
+ data->hws[0] = &pwrcl_pmux.clkr.hw;
+ data->hws[1] = &perfcl_pmux.clkr.hw;
+
+ data->num = 2;
+
+ platform_set_drvdata(pdev, hws);
+
+ return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data);
+}
+
+static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev)
+{
+ int i;
+ struct device *dev = &pdev->dev;
+ struct clk_hw_clks *hws = platform_get_drvdata(pdev);
+
+ for (i = 0; i < hws->num; i++)
+ clk_hw_unregister_fixed_rate(hws->hws[i]);
+
+ of_clk_del_provider(dev->of_node);
+
+ return 0;
+}
+
+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 = match_table,
+ },
+};
+
+module_platform_driver(qcom_cpu_clk_msm8996_driver);
+
+MODULE_ALIAS("platform:msm8996-apcc");
+MODULE_DESCRIPTION("QCOM MSM8996 CPU clock Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index 1b6230ac364d..6fcbe4977f97 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -93,6 +93,9 @@ static const struct of_device_id whitelist[] __initconst = {
{ .compatible = "xlnx,zynq-7000", },
{ .compatible = "xlnx,zynqmp", },
+ { .compatible = "qcom,msm8996", },
+ { .compatible = "qcom,apq8096", },
+
{ }
};
diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c
index 842135cf35a3..591877cacb7a 100644
--- a/drivers/perf/qcom_l2_pmu.c
+++ b/drivers/perf/qcom_l2_pmu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -30,7 +30,7 @@
#include <asm/barrier.h>
#include <asm/local64.h>
-#include <asm/sysreg.h>
+#include <soc/qcom/kryo-l2-accessors.h>
#define MAX_L2_CTRS 9
@@ -87,9 +87,6 @@
#define L2_COUNTER_RELOAD BIT_ULL(31)
#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)
/*
@@ -107,49 +104,6 @@
#define L2_EVENT_STREX 0x421
#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;
/*
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 19dcf957cb3a..12fd691bc1a5 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -14,4 +14,5 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
+obj-$(CONFIG_ARCH_MSM8996) += kryo-l2-accessors.o
obj-$(CONFIG_QCOM_APR) += apr.o
diff --git a/drivers/soc/qcom/kryo-l2-accessors.c b/drivers/soc/qcom/kryo-l2-accessors.c
new file mode 100644
index 000000000000..6743848d9a1f
--- /dev/null
+++ b/drivers/soc/qcom/kryo-l2-accessors.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014-2015, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/spinlock.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);
+
+/**
+ * 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, and system registers with respect to device memory
+ */
+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);
+}
+EXPORT_SYMBOL(set_l2_indirect_reg);
+
+/**
+ * get_l2_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 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;
+}
+EXPORT_SYMBOL(get_l2_indirect_reg);
diff --git a/include/soc/qcom/kryo-l2-accessors.h b/include/soc/qcom/kryo-l2-accessors.h
new file mode 100644
index 000000000000..e9a5eab80418
--- /dev/null
+++ b/include/soc/qcom/kryo-l2-accessors.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASM_ARCH_MSM_MSM_KRYO_L2_ACCESSORS_H
+#define __ASM_ARCH_MSM_MSM_KRYO_L2_ACCESSORS_H
+
+#ifdef CONFIG_ARCH_QCOM
+void set_l2_indirect_reg(u64 reg_addr, u64 val);
+u64 get_l2_indirect_reg(u64 reg_addr);
+#endif
+
+#endif