diff options
author | Linaro CI <ci_notify@linaro.org> | 2018-07-10 21:02:08 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2018-07-10 21:02:08 +0000 |
commit | b6cc78aa6ec429b7f474efd7368a1da86dc02b57 (patch) | |
tree | 9c7b4ae295dc0d77ddfba71b7bcdc417354aa68f | |
parent | a0b10df42645d7c4769d14c4ba9a4983aa790e64 (diff) | |
parent | cf89899d571db142f11c165be9047e267a3262eb (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.txt | 17 | ||||
-rw-r--r-- | arch/arm64/Kconfig.platforms | 7 | ||||
-rw-r--r-- | arch/arm64/boot/dts/qcom/apq8096-db820c.dts | 4 | ||||
-rw-r--r-- | arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi | 2 | ||||
-rw-r--r-- | arch/arm64/boot/dts/qcom/msm8996.dtsi | 286 | ||||
-rw-r--r-- | arch/arm64/configs/defconfig | 2 | ||||
-rw-r--r-- | drivers/clk/qcom/Kconfig | 8 | ||||
-rw-r--r-- | drivers/clk/qcom/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/qcom/clk-alpha-pll.c | 1 | ||||
-rw-r--r-- | drivers/clk/qcom/clk-cpu-8996.c | 554 | ||||
-rw-r--r-- | drivers/cpufreq/cpufreq-dt-platdev.c | 3 | ||||
-rw-r--r-- | drivers/perf/qcom_l2_pmu.c | 50 | ||||
-rw-r--r-- | drivers/soc/qcom/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/qcom/kryo-l2-accessors.c | 64 | ||||
-rw-r--r-- | include/soc/qcom/kryo-l2-accessors.h | 22 |
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 |