clk: qcom: CPU clock driver for msm8996
Subsystems (8)
cover clk: qcom: CPU clock driver for msm8996
This series adds support for the CPU clocks on msm8996 devices. A previous post of RFC can be found here
https://patchwork.kernel.org/patch/9662917/
Ilia Lin (5):
soc: qcom: Separate kryo l2 accessors from PMU driver
clk: qcom: clk-cpu-8996: Prepare PLLs on probe
clk: qcom: Add ACD path to CPU clock driver for msm8996
DT: QCOM: Add cpufreq-dt to msm8996
DT: QCOM: Add thermal mitigation to msm8996
Rajendra Nayak (4):
clk: qcom: Make clk_alpha_pll_configure available to modules
clk: qcom: Add CPU clock driver for msm8996
clk: qcom: cpu-8996: Add support to switch to alternate PLL
clk: qcom: cpu-8996: Add support to switch below 600Mhz
Taniya Das (1):
clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic update
.../devicetree/bindings/clock/qcom,kryocc.txt | 17 + arch/arm64/Kconfig.platforms | 7 + arch/arm64/boot/dts/qcom/apq8096-db820c.dts | 2 +- arch/arm64/boot/dts/qcom/msm8996.dtsi | 282 ++++++++++- drivers/clk/qcom/Kconfig | 8 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-alpha-pll.c | 72 ++- drivers/clk/qcom/clk-alpha-pll.h | 5 + drivers/clk/qcom/clk-cpu-8996.c | 542 +++++++++++++++++++++ drivers/cpufreq/cpufreq-dt-platdev.c | 3 + drivers/perf/qcom_l2_pmu.c | 48 +- drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/kryo-l2-accessors.c | 64 +++ include/soc/qcom/kryo-l2-accessors.h | 27 + 14 files changed, 1009 insertions(+), 70 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/qcom,kryocc.txt create mode 100644 drivers/clk/qcom/clk-cpu-8996.c create mode 100644 drivers/soc/qcom/kryo-l2-accessors.c create mode 100644 include/soc/qcom/kryo-l2-accessors.h
Signature · 5 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation -- 1.9.1
PATCH 1/10 soc: qcom: Separate kryo l2 accessors from PMU driver
The driver provides kernel level API for other drivers to access the MSM8996 L2 cache registers. Separating the L2 access code from the PMU driver and making it public to allow other drivers use it. The accesses must be separated with a single spinlock, maintained in this driver.
--- arch/arm64/Kconfig.platforms | 7 ++++ drivers/perf/qcom_l2_pmu.c | 48 +-------------------------- drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/kryo-l2-accessors.c | 64 ++++++++++++++++++++++++++++++++++++ include/soc/qcom/kryo-l2-accessors.h | 27 +++++++++++++++ 5 files changed, 100 insertions(+), 47 deletions(-) create mode 100644 drivers/soc/qcom/kryo-l2-accessors.c create mode 100644 include/soc/qcom/kryo-l2-accessors.h
arch/arm64/Kconfig.platforms +7 -0
@@ -144,6 +144,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
drivers/perf/qcom_l2_pmu.c +1 -47
66 lines not on this page · raw message
drivers/soc/qcom/Makefile +1 -0
@@ -10,3 +10,4 @@ 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
drivers/soc/qcom/kryo-l2-accessors.c new file +64 -0
64 lines not on this page · raw message
include/soc/qcom/kryo-l2-accessors.h new file +27 -0
@@ -0,0 +1,27 @@ +/* +* Copyright (c) 2017, 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); +#else +static inline void set_l2_indirect_reg (u32 reg_addr,u32 val) {} +static inline u32 get_l2_indirect_reg (u32 reg_addr) +{ +return 0 ; +} +#endif +#endif
Signature · 1 line
1.9.1
Mark Rutland
maintainerPATCH 1/10 soc: qcom: Separate kryo l2 accessors from PMU driver
Hi,
Quoted 21 lines · Ilia Lin
The driver provides kernel level API for other drivers to access the MSM8996 L2 cache registers. Separating the L2 access code from the PMU driver and making it public to allow other drivers use it. The accesses must be separated with a single spinlock, maintained in this driver.
drivers/perf/qcom_l2_pmu.c +2
-11-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); -} +/** +* set_l2_indirect_reg: write value to an L2 register
drivers/perf/qcom_l2_pmu.c
Quoted 10 lines
drivers/perf/qcom_l2_pmu.c +10
-0+* @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; +mb ();
We didn't need this for the PMU driver, so it's unfortuante that it now has to pay the cost.
Can we please factor this mb() into the callers that need it?
Quoted 8 lines
drivers/soc/qcom/kryo-l2-accessors.c new file +8
-0+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);
[...]
include/soc/qcom/kryo-l2-accessors.h line 27
Quoted 11 lines
include/soc/qcom/kryo-l2-accessors.h new file +11
-0+#ifdef CONFIG_ARCH_QCOM +void set_l2_indirect_reg (u64 reg_addr,u64 val); +u64 get_l2_indirect_reg (u64 reg_addr); +#else +static inline void set_l2_indirect_reg (u32 reg_addr,u32 val) {} +static inline u32 get_l2_indirect_reg (u32 reg_addr) +{ +return 0 ; +} +#endif +#endif
Are there any drivers that will bne built for !CONFIG_ARCH_QCOM that reference this?
It might be better to not have the stub versions, so that we get a build-error if they are erroneously used.
Thannks,
Mark.
PATCH 1/10 soc: qcom: Separate kryo l2 accessors from PMU driver
Quoted 11 lines · Mark Rutland
drivers/soc/qcom/kryo-l2-accessors.c line 34
Quoted 6 lines · Ilia Lin
+ * accesses, and system registers with respect to device memory
+ */drivers/soc/qcom/kryo-l2-accessors.c new file +4
-0+void set_l2_indirect_reg (u64 reg,u64 val) +{ +unsigned long flags; +mb ();We didn't need this for the PMU driver, so it's unfortuante that it now has to pay the cost.
Quoted 1 line
Can we please factor this
mb()into the callers that need it?
+1
Quoted 27 lines
Quoted 9 lines
drivers/soc/qcom/kryo-l2-accessors.c new file +8
-0+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);[...]
include/soc/qcom/kryo-l2-accessors.h line 27
Quoted 11 lines
include/soc/qcom/kryo-l2-accessors.h new file +11
-0+#ifdef CONFIG_ARCH_QCOM +void set_l2_indirect_reg (u64 reg_addr,u64 val); +u64 get_l2_indirect_reg (u64 reg_addr); +#else +static inline void set_l2_indirect_reg (u32 reg_addr,u32 val) {} +static inline u32 get_l2_indirect_reg (u32 reg_addr) +{ +return 0 ; +} +#endif +#endif Are there any drivers that will bne built for !CONFIG_ARCH_QCOM that reference this?
Quoted 3 lines
It might be better to not have the stub versions, so that we get a build-error if they are erroneously used.
Does that approach make COMPILE_TEST break?
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH 1/10 soc: qcom: Separate kryo l2 accessors from PMU driver
Tested COMPILE_TEST and found OK.
Quoted 5 lines
-----Original Message-----
From: Stephen Boyd [mailto:sboyd at codeaurora.org]
Sent: Friday, December 22, 2017 4:07 AM
To: Mark Rutland <[email protected]>
Cc: Ilia Lin <[email protected]>; linux-clk at vger.kernel.org;
linux-arm-
Quoted 54 lines
kernel at lists.infradead.org; linux-arm-msm at vger.kernel.org; devicetree at vger.kernel.org; will.deacon at arm.com;
rnayak at codeaurora.org; qualcomm-lt at lists.linaro.org; celster at codeaurora.org; tfinkel at codeaurora.org
Subject: Re: [PATCH 01/10] soc: qcom: Separate kryo l2 accessors from PMU driverQuoted 10 lines · Mark Rutland
drivers/soc/qcom/kryo-l2-accessors.c line 34
Quoted 4 lines · Ilia Lin
+ * accesses, and system registers with respect to device memory */
drivers/soc/qcom/kryo-l2-accessors.c new file +3
-0+void set_l2_indirect_reg (u64 reg,u64 val) { +unsigned long flags; +mb ();We didn't need this for the PMU driver, so it's unfortuante that it now has to pay the cost.
Can we please factor this
mb()into the callers that need it?+1
Quoted 24 lines
Quoted 8 lines
drivers/soc/qcom/kryo-l2-accessors.c new file +7
-0+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);[...]
include/soc/qcom/kryo-l2-accessors.h line 27
Quoted 9 lines
include/soc/qcom/kryo-l2-accessors.h new file +9
-0+#ifdef CONFIG_ARCH_QCOM +void set_l2_indirect_reg (u64 reg_addr,u64 val); +u64 get_l2_indirect_reg (u64 reg_addr);#else static inline void +set_l2_indirect_reg (u32 reg_addr,u32 val) {}static inline u32 +get_l2_indirect_reg (u32 reg_addr) { +return 0 ; +} +#endif +#endif Are there any drivers that will bne built for !CONFIG_ARCH_QCOM that reference this?
Quoted 3 lines
It might be better to not have the stub versions, so that we get a build-error if they are erroneously used.
Does that approach make
COMPILE_TESTbreak?--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH 1/10 soc: qcom: Separate kryo l2 accessors from PMU driver
In reply to Mark RutlandThis is address in the V2: https://patchwork.kernel.org/patch/10144473/
Quoted 5 lines
-----Original Message-----
From: Mark Rutland [mailto:mark.rutland at arm.com]
Sent: Tuesday, December 12, 2017 4:03 PM
To: Ilia Lin <[email protected]>
Cc: linux-clk at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
linux-
Quoted 28 lines
arm-msm at vger.kernel.org; sboyd at codeaurora.org; devicetree at vger.kernel.org; will.deacon at arm.com;
rnayak at codeaurora.org; qualcomm-lt at lists.linaro.org; celster at codeaurora.org; tfinkel at codeaurora.org
Subject: Re: [PATCH 01/10] soc: qcom: Separate kryo l2 accessors from PMU driverHi,
Quoted 17 lines · Ilia Lin
The driver provides kernel level API for other drivers to access the MSM8996 L2 cache registers.
Separating the L2 access code from the PMU driver and making it public to allow other drivers use it.
The accesses must be separated with a single spinlock, maintained in this driver.drivers/perf/qcom_l2_pmu.c +0
-10-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); -}
Quoted 13 lines
drivers/soc/qcom/kryo-l2-accessors.c line 34
Quoted 11 lines
drivers/soc/qcom/kryo-l2-accessors.c new file +11
-0+/** +* 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; +mb ();We didn't need this for the PMU driver, so it's unfortuante that it now
has to
Quoted 27 lines
pay the cost.
Can we please factor this
mb()into the callers that need it?Quoted 7 lines
drivers/soc/qcom/kryo-l2-accessors.c new file +7
-0+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);[...]
include/soc/qcom/kryo-l2-accessors.h line 27
Quoted 9 lines
include/soc/qcom/kryo-l2-accessors.h new file +9
-0+#ifdef CONFIG_ARCH_QCOM +void set_l2_indirect_reg (u64 reg_addr,u64 val); +u64 get_l2_indirect_reg (u64 reg_addr);#else static inline void +set_l2_indirect_reg (u32 reg_addr,u32 val) {}static inline u32 +get_l2_indirect_reg (u32 reg_addr) { +return 0 ; +} +#endif +#endif Are there any drivers that will bne built for !CONFIG_ARCH_QCOM that reference this?
Quoted 1 line
It might be better to not have the stub versions, so that we get a
build-error
Quoted 4 lines
if they are erroneously used.
Thannks,
Mark.
PATCH 2/10 clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic update
In reply to Ilia LinFrom: Taniya Das <[email protected]>
From: Taniya Das <[email protected]>
Alpha PLLs which do not support dynamic update feature need to be explicitly disabled before a rate change. The ones which do support dynamic update do so within a single vco range, so add a min/max freq check for such PLLs so they fall in the vco range.
--- drivers/clk/qcom/clk-alpha-pll.c | 71 +++++++++++++++++++++++++++++++++------- drivers/clk/qcom/clk-alpha-pll.h | 5 +++ 2 files changed, 65 insertions(+), 11 deletions(-)
drivers/clk/qcom/clk-alpha-pll.c +60 -11
@@ -376,19 +376,46 @@ static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)return alpha_pll_calc_rate (prate, l, a); } -static int clk_alpha_pll_set_rate (struct clk_hw *hw,unsigned long rate, -unsigned long prate) +static int alpha_pll_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long prate, +int (*enable)(struct clk_hw *hw), +void (*disable)(struct clk_hw *hw)) { +bool enabled;struct clk_alpha_pll *pll =to_clk_alpha_pll (hw);const struct pll_vco *vco;u32 l, off = pll->offset;u64 a; rate =alpha_pll_round_rate (rate, prate, &l, &a); - vco =alpha_pll_find_vco (pll, rate); -if (!vco) { -pr_err ("alpha pll not in a valid vco range\n" ); -return -EINVAL ; + enabled =clk_hw_is_enabled (hw); + +if (pll->flags &SUPPORTS_DYNAMIC_UPDATE ) { +/* +* PLLs which support dynamic updates support one single +* vco range, between min_rate and max_rate supported +*/ +if (rate < pll->min_rate || rate > pll->max_rate) { +pr_err ("alpha pll rate outside supported min/max range\n" ); +return -EINVAL ; + } + }else { +/* +* All alpha PLLs which do not support dynamic update, +* should be disabled before a vco update. +*/ +if (enabled) +disable (hw); + + vco =alpha_pll_find_vco (pll, rate); +if (!vco) { +pr_err ("alpha pll not in a valid vco range\n" ); +return -EINVAL ; + } + +regmap_update_bits (pll->clkr.regmap, off +PLL_USER_CTL , +PLL_VCO_MASK <<PLL_VCO_SHIFT , + vco->val <<PLL_VCO_SHIFT ); }regmap_write (pll->clkr.regmap, off +PLL_L_VAL , l);
@@ -401,16 +428,29 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,regmap_write (pll->clkr.regmap, off +PLL_ALPHA_VAL_U , a >>32 ); } -regmap_update_bits (pll->clkr.regmap, off +PLL_USER_CTL , -PLL_VCO_MASK <<PLL_VCO_SHIFT , - vco->val <<PLL_VCO_SHIFT ); -regmap_update_bits (pll->clkr.regmap, off +PLL_USER_CTL ,PLL_ALPHA_EN ,PLL_ALPHA_EN ); +if (!(pll->flags &SUPPORTS_DYNAMIC_UPDATE ) && enabled) +enable (hw); +return 0 ; } +static int clk_alpha_pll_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long prate) +{ +return alpha_pll_set_rate (hw, rate, prate, clk_alpha_pll_enable, + clk_alpha_pll_disable); +} + +static int clk_alpha_pll_hwfsm_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long prate) +{ +return alpha_pll_set_rate (hw, rate, prate, clk_alpha_pll_hwfsm_enable, + clk_alpha_pll_hwfsm_disable); +} +static long clk_alpha_pll_round_rate (struct clk_hw *hw,unsigned long rate,unsigned long *prate) {
@@ -420,6 +460,15 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,unsigned long min_freq, max_freq; rate =alpha_pll_round_rate (rate, *prate, &l, &a); + +if (pll->flags &SUPPORTS_DYNAMIC_UPDATE ) { +if (rate < pll->min_rate) + rate = pll->min_rate; +else if (rate > pll->max_rate) + rate = pll->max_rate; +return rate; + } +if (alpha_pll_find_vco (pll, rate))return rate;
@@ -445,7 +494,7 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate, .is_enabled = clk_alpha_pll_hwfsm_is_enabled, .recalc_rate = clk_alpha_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, - .set_rate = clk_alpha_pll_set_rate, + .set_rate = clk_alpha_pll_hwfsm_set_rate, };EXPORT_SYMBOL_GPL (clk_alpha_pll_hwfsm_ops);
drivers/clk/qcom/clk-alpha-pll.h +5 -0
@@ -27,6 +27,8 @@ struct pll_vco {* struct clk_alpha_pll - phase locked loop (PLL) * @offset: base address of registers * @vco_table: array of VCO settings +* @min_rate: Minimim rate for PLLs with single VCO range +* @max_rate: Maximun rate for PLLs with single VCO range * @clkr: regmap clock handle */ struct clk_alpha_pll {
@@ -37,8 +39,11 @@ struct clk_alpha_pll {#define SUPPORTS_OFFLINE_REQ BIT(0)#define SUPPORTS_16BIT_ALPHA BIT(1)#define SUPPORTS_FSM_MODE BIT(2) +#define SUPPORTS_DYNAMIC_UPDATE BIT(3)u8 flags; +unsigned long min_rate; +unsigned long max_rate;struct clk_regmap clkr; };
Signature · 1 line
1.9.1
PATCH 2/10 clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic update
Hi,
Quoted 24 lines · Ilia Lin
From: Taniya Das <[email protected]>
From: Taniya Das <[email protected]>
Alpha PLLs which do not support dynamic update feature need to be explicitly disabled before a rate change. The ones which do support dynamic update do so within a single vco range, so add a min/max freq check for such PLLs so they fall in the vco range.
Signed-off-by Taniya Das <[email protected]>Signed-off-by Rajendra Nayak <[email protected]>Signed-off-by Ilia Lin <[email protected]>--- drivers/clk/qcom/clk-alpha-pll.c | 71 +++++++++++++++++++++++++++++++++------- drivers/clk/qcom/clk-alpha-pll.h | 5 +++ 2 files changed, 65 insertions(+), 11 deletions(-)drivers/clk/qcom/clk-alpha-pll.c +0
-0@@ -376,19 +376,46 @@ static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)return alpha_pll_calc_rate (prate, l, a);
drivers/clk/qcom/clk-alpha-pll.c static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a) · line 384
Quoted 10 lines
drivers/clk/qcom/clk-alpha-pll.c +5
-2} -static int clk_alpha_pll_set_rate (struct clk_hw *hw,unsigned long rate, -unsigned long prate) +static int alpha_pll_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long prate, +int (*enable)(struct clk_hw *hw), +void (*disable)(struct clk_hw *hw)) { +bool enabled;
Some remarks about this.
drivers/clk/qcom/clk-alpha-pll.c static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a) · line 391
Quoted 11 lines
drivers/clk/qcom/clk-alpha-pll.c +1
-4struct clk_alpha_pll *pll =to_clk_alpha_pll (hw);const struct pll_vco *vco;u32 l, off = pll->offset;u64 a; rate =alpha_pll_round_rate (rate, prate, &l, &a); - vco =alpha_pll_find_vco (pll, rate); -if (!vco) { -pr_err ("alpha pll not in a valid vco range\n" ); -return -EINVAL ; + enabled =clk_hw_is_enabled (hw);
This is not needed unless we go through the 'else' branch.
Quoted 34 lines
drivers/clk/qcom/clk-alpha-pll.c +27
-0+ +if (pll->flags &SUPPORTS_DYNAMIC_UPDATE ) { +/* +* PLLs which support dynamic updates support one single +* vco range, between min_rate and max_rate supported +*/ +if (rate < pll->min_rate || rate > pll->max_rate) { +pr_err ("alpha pll rate outside supported min/max range\n" ); +return -EINVAL ; + } + }else { +/* +* All alpha PLLs which do not support dynamic update, +* should be disabled before a vco update. +*/ +if (enabled) +disable (hw); + + vco =alpha_pll_find_vco (pll, rate); +if (!vco) { +pr_err ("alpha pll not in a valid vco range\n" ); +return -EINVAL ; + } + +regmap_update_bits (pll->clkr.regmap, off +PLL_USER_CTL , +PLL_VCO_MASK <<PLL_VCO_SHIFT , + vco->val <<PLL_VCO_SHIFT ); }regmap_write (pll->clkr.regmap, off +PLL_L_VAL , l);@@ -401,16 +428,29 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,regmap_write (pll->clkr.regmap, off +PLL_ALPHA_VAL_U , a >>32 ); }
drivers/clk/qcom/clk-alpha-pll.c static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, · line 436
Quoted 10 lines
drivers/clk/qcom/clk-alpha-pll.c +3
-4-regmap_update_bits (pll->clkr.regmap, off +PLL_USER_CTL , -PLL_VCO_MASK <<PLL_VCO_SHIFT , - vco->val <<PLL_VCO_SHIFT ); -regmap_update_bits (pll->clkr.regmap, off +PLL_USER_CTL ,PLL_ALPHA_EN ,PLL_ALPHA_EN ); +if (!(pll->flags &SUPPORTS_DYNAMIC_UPDATE ) && enabled) +enable (hw); +
This condition is only "did we disable the clock and need to reenable it?".
To make it clearer, I'd suggest renaming 'enabled' to something like 'need_reenabling' and the code look like this:
static int alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long prate,
int (*enable)(struct clk_hw *hw), void (*disable)(struct clk_hw *hw))
{
bool need_reenabling = false;
[...]
if(pll->flags & SUPPORTS_DYNAMIC_UPDATE) {
[...]
} else {
if (clk_hw_is_enabled(hw)) {
disable(hw);
need_reenabling = true;
}
[...]
}
[...]
if (need_reenabling)
enable(hw);}
Cheers,
Signature · 1 line
Julien Thierry
PATCH 2/10 clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic update
This is address in the V2: https://patchwork.kernel.org/patch/10144477/
Quoted 154 lines
-----Original Message-----
From: Julien Thierry [mailto:julien.thierry at arm.com]
Sent: Tuesday, December 12, 2017 5:06 PM
To: Ilia Lin <[email protected]>; linux-clk at vger.kernel.org; linux-arm- kernel at lists.infradead.org; linux-arm-msm at vger.kernel.org; sboyd at codeaurora.org
Cc: mark.rutland at arm.com; devicetree at vger.kernel.org;
rnayak at codeaurora.org; will.deacon at arm.com; tfinkel at codeaurora.org; qualcomm-lt at lists.linaro.org; celster at codeaurora.org; Taniya Das <[email protected]>
Subject: Re: [PATCH 02/10] clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic updateHi,
Quoted 10 lines · Ilia Lin
From: Taniya Das <[email protected]>
From: Taniya Das <[email protected]>
Alpha PLLs which do not support dynamic update feature need to be explicitly disabled before a rate change.
The ones which do support dynamic update do so within a single vco range, so add a min/max freq check for such PLLs so they fall in the vco range.Quoted 5 lines
Signed-off-by Taniya Das <[email protected]>Signed-off-by Rajendra Nayak <[email protected]>Signed-off-by Ilia Lin <[email protected]>--- drivers/clk/qcom/clk-alpha-pll.c | 71+++++++++++++++++++++++++++++++++-------
drivers/clk/qcom/clk-alpha-pll.c static unsigned long alpha_pll_calc_rate(u64
Quoted 9 lines
drivers/clk/qcom/clk-alpha-pll.h | 5 +++ 2 files changed, 65 insertions(+), 11 deletions(-)drivers/clk/qcom/clk-alpha-pll.c +0
-0@@ -376,19 +376,46 @@ static unsigned long alpha_pll_calc_rate(u64prate, u32 l, u32 a)
drivers/clk/qcom/clk-alpha-pll.c static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a) · line 384
Quoted 11 lines
drivers/clk/qcom/clk-alpha-pll.c +5
-2return alpha_pll_calc_rate (prate, l, a); } -static int clk_alpha_pll_set_rate (struct clk_hw *hw,unsigned long rate, -unsigned long prate) +static int alpha_pll_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long prate, +int (*enable)(struct clk_hw *hw), +void (*disable)(struct clk_hw *hw)) { +bool enabled;Some remarks about this.
drivers/clk/qcom/clk-alpha-pll.c static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a) · line 391
Quoted 11 lines
drivers/clk/qcom/clk-alpha-pll.c +1
-4struct clk_alpha_pll *pll =to_clk_alpha_pll (hw);const struct pll_vco *vco;u32 l, off = pll->offset;u64 a; rate =alpha_pll_round_rate (rate, prate, &l, &a); - vco =alpha_pll_find_vco (pll, rate); -if (!vco) { -pr_err ("alpha pll not in a valid vco range\n" ); -return -EINVAL ; + enabled =clk_hw_is_enabled (hw);This is not needed unless we go through the 'else' branch.
drivers/clk/qcom/clk-alpha-pll.c static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
Quoted 8 lines
drivers/clk/qcom/clk-alpha-pll.c +8
-0+ +if (pll->flags &SUPPORTS_DYNAMIC_UPDATE ) { +/* +* PLLs which support dynamic updates support one single +* vco range, between min_rate and max_rate supported +*/ +if (rate < pll->min_rate || rate > pll->max_rate) { + pr_err("alpha pll rate outside supported min/maxrange\n");
Quoted 13 lines
drivers/clk/qcom/clk-alpha-pll.c +13
-0+return -EINVAL ; + } + }else { +/* +* All alpha PLLs which do not support dynamic update, +* should be disabled before a vco update. +*/ +if (enabled) +disable (hw); + + vco =alpha_pll_find_vco (pll, rate); +if (!vco) { +pr_err ("alpha pll not in a valid vco range\n" );drivers/clk/qcom/clk-alpha-pll.c static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
Quoted 10 lines
drivers/clk/qcom/clk-alpha-pll.c +7
-0+return -EINVAL ; + } + +regmap_update_bits (pll->clkr.regmap, off +PLL_USER_CTL , +PLL_VCO_MASK <<PLL_VCO_SHIFT , + vco->val <<PLL_VCO_SHIFT ); } regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l); @@ -401,16 +428,29 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsignedlong rate,
drivers/clk/qcom/clk-alpha-pll.c static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
Quoted 9 lines
regmap_write(pll->clkr.regmap, off +
PLL_ALPHA_VAL_U, aQuoted 1 line
32);
}
drivers/clk/qcom/clk-alpha-pll.c +0
-4-regmap_update_bits (pll->clkr.regmap, off +PLL_USER_CTL , -PLL_VCO_MASK <<PLL_VCO_SHIFT , - vco->val <<PLL_VCO_SHIFT ); - regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
PLL_ALPHA_EN,drivers/clk/qcom/clk-alpha-pll.c static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, · line 436
Quoted 5 lines
PLL_ALPHA_EN);drivers/clk/qcom/clk-alpha-pll.c +3
-0+if (!(pll->flags &SUPPORTS_DYNAMIC_UPDATE ) && enabled) +enable (hw); +This condition is only "did we disable the clock and need to reenable it?".
To make it clearer, I'd suggest renaming 'enabled' to something like '
need_reenabling' and the code look like this:static int alpha_pll_set_rate(struct
clk_hw*hw, unsigned long rate,unsigned long prate, int (*enable)(struct clk_hw *hw), void (*disable)(struct clk_hw *hw)) { bool need_reenabling = false; [...] if(pll->flags & SUPPORTS_DYNAMIC_UPDATE) { [...] } else { if (clk_hw_is_enabled(hw)) { disable(hw); need_reenabling = true; } [...] } [...] if (need_reenabling) enable(hw);}
Cheers,
--
Julien Thierry
PATCH 3/10 clk: qcom: Make clk_alpha_pll_configure available to modules
In reply to Ilia LinFrom: Rajendra Nayak <[email protected]>
Allow clk_alpha_pll_configure to be called from loadable kernel modules.
--- drivers/clk/qcom/clk-alpha-pll.c | 1 + 1 file changed, 1 insertion(+)
drivers/clk/qcom/clk-alpha-pll.c +1 -0
@@ -141,6 +141,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, off +PLL_MODE ,6 ,0 ); } +EXPORT_SYMBOL_GPL (clk_alpha_pll_configure);static int clk_alpha_pll_hwfsm_enable (struct clk_hw *hw) {
Signature · 1 line
1.9.1
PATCH 4/10 clk: qcom: Add CPU clock driver for msm8996
In reply to Ilia LinFrom: Rajendra Nayak <[email protected]>
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_EARLYThe 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. A subsequent patch adds support to switch between primary and alternate PLL during rate changes.
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
Support for this is added in a subsequent patch as well.
ACD stands for Adaptive Clock Distribution and is used to detect voltage droops. We do not add support for ACD as yet. This can be added at a later point as needed.
--- .../devicetree/bindings/clock/qcom,kryocc.txt | 17 + drivers/clk/qcom/Kconfig | 8 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-cpu-8996.c | 388 +++++++++++++++++++++ 4 files changed, 414 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,kryocc.txt create mode 100644 drivers/clk/qcom/clk-cpu-8996.c
Documentation/devicetree/bindings/clock/qcom,kryocc.txt new file +17 -0
@@ -0,0 +1,17 @@ +Qualcomm CPUSS clock controller for Kryo CPUs +---------------------------------------------------- + +Required properties : +- compatible : shall contain only one of the following: + + "qcom,apcc-msm8996" + +- reg : shall contain base register location and length +- #clock-cells : shall contain 1 + +Example: + kryocc: clock-controller at 6400000 { + compatible = "qcom,apcc-msm8996"; + reg = <0x6400000 0x90000>; + #clock-cells = <1>; + };
drivers/clk/qcom/Kconfig +8 -0
@@ -196,3 +196,11 @@ config MSM_MMCC_8996 Support for the multimedia clock controller on msm8996 devices. Say Y if you want to support multimedia devices such as display, graphics, video encode/decode, camera, etc. + +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.
drivers/clk/qcom/Makefile +1 -0
@@ -32,5 +32,6 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.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_CLK_RPM) += clk-rpm.o obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
drivers/clk/qcom/clk-cpu-8996.c new file +388 -0
388 lines not on this page · raw message
Signature · 1 line
1.9.1
Rob Herring
maintainerPATCH 4/10 clk: qcom: Add CPU clock driver for msm8996
Quoted 46 lines · Ilia Lin
From: Rajendra Nayak <[email protected]>
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_EARLYThe 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. A subsequent patch adds support to switch between primary and alternate PLL during rate changes.
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
Support for this is added in a subsequent patch as well.
Quoted 8 lines
ACD stands for Adaptive Clock Distribution and is used to detect voltage droops. We do not add support for ACD as yet. This can be added at a later point as needed.
Signed-off-by Rajendra Nayak <[email protected]>Signed-off-by Ilia Lin <[email protected]>--- .../devicetree/bindings/clock/qcom,kryocc.txt | 17 +
If you respin, please make bindings a separate patch. In any case,
Quoted 6 lines
drivers/clk/qcom/Kconfig | 8 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-cpu-8996.c | 388 +++++++++++++++++++++ 4 files changed, 414 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,kryocc.txt create mode 100644 drivers/clk/qcom/clk-cpu-8996.c
PATCH 4/10 clk: qcom: Add CPU clock driver for msm8996
This is address in the V2: https://patchwork.kernel.org/patch/10144489/
Quoted 5 lines
-----Original Message-----
From: Rob Herring [mailto:robh at kernel.org]
Sent: Saturday, December 16, 2017 12:35 AM
To: Ilia Lin <[email protected]>
Cc: linux-clk at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
linux-
Quoted 75 lines
arm-msm at vger.kernel.org; sboyd at codeaurora.org;
devicetree at vger.kernel.org; mark.rutland at arm.com; will.deacon at arm.com; rnayak at codeaurora.org; qualcomm-
lt at lists.linaro.org; celster at codeaurora.org; tfinkel at codeaurora.org
Subject: Re: [PATCH 04/10] clk: qcom: Add CPU clock driver for msm8996Quoted 28 lines · Ilia Lin
From: Rajendra Nayak <[email protected]>
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_EARLYQuoted 8 lines
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. A subsequent patch adds support to switch between primary and alternate PLL during rate changes.
The primary PLL operates on a single VCO range, between 600Mhz and 3Ghz. However the CPUs do support OPPs with frequencies between
300Mhz
Quoted 10 lines
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 Support for this is added in a subsequent patch as well.Quoted 8 lines
ACD stands for Adaptive Clock Distribution and is used to detect voltage droops. We do not add support for ACD as yet. This can be added at a later point as needed.
Signed-off-by Rajendra Nayak <[email protected]>Signed-off-by Ilia Lin <[email protected]>--- .../devicetree/bindings/clock/qcom,kryocc.txt | 17 +If you respin, please make bindings a separate patch. In any case,
Reviewed-by Rob Herring <[email protected]>Quoted 3 lines
drivers/clk/qcom/Kconfig | 8 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-cpu-8996.c | 388+++++++++++++++++++++
Quoted 4 lines
4 files changed, 414 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,kryocc.txt create mode 100644 drivers/clk/qcom/clk-cpu-8996.c
PATCH 5/10 clk: qcom: cpu-8996: Add support to switch to alternate PLL
In reply to Ilia LinFrom: Rajendra Nayak <[email protected]>
Each of the CPU clusters on msm8996 and powered via a primary PLL and a secondary PLL. The primary PLL is what drivers the CPU clk, except for times when we are reprogramming the PLL itself, when we temporarily switch to an alternate PLL. Use clock rate change notifiers to support this.
--- drivers/clk/qcom/clk-cpu-8996.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+)
drivers/clk/qcom/clk-cpu-8996.c +35 -0
@@ -119,10 +119,14 @@ struct clk_cpu_8996_mux {u32 reg;u32 shift;u32 width; +struct notifier_block nb;struct clk_hw *pll;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) {
@@ -173,6 +177,27 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)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); + +switch (event) { +case PRE_RATE_CHANGE : + ret =clk_cpu_8996_mux_set_parent (&cpuclk->clkr.hw,ALT_INDEX ); +break ; +case POST_RATE_CHANGE : + ret =clk_cpu_8996_mux_set_parent (&cpuclk->clkr.hw,PLL_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,
@@ -216,6 +241,7 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index) .shift = 0, .width = 2, .pll = &pwrcl_pll.clkr.hw, + .nb.notifier_call = cpu_clk_notifier_cb, .clkr.hw.init = &(struct clk_init_data ) { .name ="pwrcl_pmux" , .parent_names = (const char *[]){
@@ -235,6 +261,7 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index) .shift = 0, .width = 2, .pll = &perfcl_pll.clkr.hw, + .nb.notifier_call = cpu_clk_notifier_cb, .clkr.hw.init = &(struct clk_init_data ) { .name ="perfcl_pmux" , .parent_names = (const char *[]){
@@ -310,6 +337,14 @@ struct clk_hw_clks {clk_alpha_pll_configure (&perfcl_alt_pll, regmap, &altpll_config);clk_alpha_pll_configure (&pwrcl_alt_pll, regmap, &altpll_config); + 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; }
Signature · 1 line
1.9.1
PATCH 6/10 clk: qcom: cpu-8996: Add support to switch below 600Mhz
In reply to Ilia LinFrom: Rajendra Nayak <[email protected]>
The CPU clock controllers 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
--- drivers/clk/qcom/clk-cpu-8996.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
drivers/clk/qcom/clk-cpu-8996.c +18 -1
@@ -28,6 +28,7 @@#define PLL_INDEX 1#define ACD_INDEX 2#define ALT_INDEX 3 +#define DIV_2_THRESHOLD 600000000/* PLLs */
@@ -121,6 +122,7 @@ struct clk_cpu_8996_mux {u32 width;struct notifier_block nb;struct clk_hw *pll; +struct clk_hw *pll_div_2;struct clk_regmap clkr; };
@@ -171,6 +173,13 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)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;
@@ -182,13 +191,19 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, {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 );break ;case POST_RATE_CHANGE : - ret =clk_cpu_8996_mux_set_parent (&cpuclk->clkr.hw,PLL_INDEX ); +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, +PLL_INDEX );break ;default : ret =0 ;
@@ -241,6 +256,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, .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" ,
@@ -261,6 +277,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, .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" ,
Signature · 1 line
1.9.1
PATCH 7/10 clk: qcom: clk-cpu-8996: Prepare PLLs on probe
In reply to Ilia LinThe PLLs must be prepared enabled during the probe to be accessible by the OPPs. Otherwise an OPP may switch to non-enabled clock.
--- drivers/clk/qcom/clk-cpu-8996.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-)
drivers/clk/qcom/clk-cpu-8996.c +17 -5
@@ -15,7 +15,7 @@#include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> - +#include <linux/clk-provider.h> #include "clk-alpha-pll.h" #define VCO (a, b, c) { \
@@ -159,7 +159,7 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index) cpuclk->shift); val = index; - val = cpuclk->shift; + val <<= cpuclk->shift;return regmap_update_bits (clkr->regmap, cpuclk->reg, mask, val); }
@@ -268,7 +268,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, }, .num_parents = 4, .ops = &clk_cpu_8996_mux_ops, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, }, };
@@ -284,12 +284,12 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, .parent_names = (const char *[]){"perfcl_smux" ,"perfcl_pll" , -"pwrcl_pll_acd" , +"perfcl_pll_acd" ,"perfcl_alt_pll" , }, .num_parents = 4, .ops = &clk_cpu_8996_mux_ops, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, }, };
@@ -354,6 +354,18 @@ struct clk_hw_clks {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;
Signature · 1 line
1.9.1
PATCH 8/10 clk: qcom: Add ACD path to CPU clock driver for msm8996
In reply to Ilia LinThe PMUX for each duplex allows for selection of ACD clock source. The DVM (Dynamic Variation Monitor) will flag an error
when a voltage droop event is detected. This flagged error enables ACD to provide a div-by-2 clock, sourced from the primary PLL. The duplex will be provided the divided clock
until a pre-programmed delay has expired.
This change configures ACD during the probe and switches the PMUXes to the ACD clock source.
--- drivers/clk/qcom/clk-cpu-8996.c | 122 ++++++++++++++++++++++++++++++++++------ 1 file changed, 106 insertions(+), 16 deletions(-)
drivers/clk/qcom/clk-cpu-8996.c +106 -16
@@ -17,6 +17,7 @@#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,\
@@ -29,6 +30,27 @@#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 ];/* PLLs */
@@ -43,7 +65,7 @@ };static struct clk_alpha_pll perfcl_pll = { - .offset =0x80000 , + .offset =PERFCL_REG_OFFSET , .min_rate =600000000 , .max_rate =3000000000 , .flags =SUPPORTS_DYNAMIC_UPDATE |SUPPORTS_16BIT_ALPHA
@@ -57,7 +79,7 @@ };static struct clk_alpha_pll pwrcl_pll = { - .offset =0x0 , + .offset =PWRCL_REG_OFFSET , .min_rate =600000000 , .max_rate =3000000000 , .flags =SUPPORTS_DYNAMIC_UPDATE |SUPPORTS_16BIT_ALPHA
@@ -89,7 +111,7 @@ };static struct clk_alpha_pll perfcl_alt_pll = { - .offset =0x80100 , + .offset =PERFCL_REG_OFFSET +ALT_PLL_OFFSET , .vco_table = alt_pll_vco_modes, .num_vco =ARRAY_SIZE (alt_pll_vco_modes), .flags =SUPPORTS_OFFLINE_REQ |SUPPORTS_FSM_MODE ,
@@ -102,7 +124,7 @@ };static struct clk_alpha_pll pwrcl_alt_pll = { - .offset =0x100 , + .offset =PWRCL_REG_OFFSET +ALT_PLL_OFFSET , .vco_table = alt_pll_vco_modes, .num_vco =ARRAY_SIZE (alt_pll_vco_modes), .flags =SUPPORTS_OFFLINE_REQ |SUPPORTS_FSM_MODE ,
@@ -113,6 +135,7 @@ .ops = &clk_alpha_pll_hwfsm_ops, }, }; +static void qcom_cpu_clk_msm8996_acd_init (void );/* Mux'es */
@@ -196,6 +219,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,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 )
@@ -203,7 +227,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, DIV_2_INDEX); else ret =clk_cpu_8996_mux_set_parent (&cpuclk->clkr.hw, -PLL_INDEX ); +ACD_INDEX );break ;default : ret =0 ;
@@ -220,7 +244,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, };static struct clk_cpu_8996_mux pwrcl_smux = { - .reg =0x40 , + .reg =PWRCL_REG_OFFSET +MUX_OFFSET , .shift =2 , .width =2 , .clkr.hw.init = &(struct clk_init_data ) {
@@ -236,7 +260,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, };static struct clk_cpu_8996_mux perfcl_smux = { - .reg =0x80040 , + .reg =PERFCL_REG_OFFSET +MUX_OFFSET , .shift =2 , .width =2 , .clkr.hw.init = &(struct clk_init_data ) {
@@ -252,7 +276,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, };static struct clk_cpu_8996_mux pwrcl_pmux = { - .reg =0x40 , + .reg =PWRCL_REG_OFFSET +MUX_OFFSET , .shift =0 , .width =2 , .pll = &pwrcl_pll.clkr.hw,
@@ -273,7 +297,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, };static struct clk_cpu_8996_mux perfcl_pmux = { - .reg =0x80040 , + .reg =PERFCL_REG_OFFSET +MUX_OFFSET , .shift =0 , .width =2 , .pll = &perfcl_pll.clkr.hw,
@@ -341,7 +365,17 @@ struct clk_hw_clks { CLK_SET_RATE_PARENT, 1, 2); pwrcl_smux.pll = hws->hws[1 ]; - hws->num =2 ; + hws->hws[2 ] =clk_hw_register_fixed_factor (dev,"perfcl_pll_acd" , +"perfcl_pll" , +CLK_SET_RATE_PARENT ,1 ,1 ); + perfcl_pmux.pll = hws->hws[2 ]; + + hws->hws[3 ] =clk_hw_register_fixed_factor (dev,"pwrcl_pll_acd" , +"pwrcl_pll" , +CLK_SET_RATE_PARENT ,1 ,1 ); + pwrcl_pmux.pll = hws->hws[3 ]; + + hws->num =4 ;for (i =0 ; i <ARRAY_SIZE (clks); i++) { ret =devm_clk_register_regmap (dev, clks[i]);
@@ -377,10 +411,65 @@ struct clk_hw_clks {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; -void __iomem *base;struct resource *res;struct regmap *regmap_cpu;struct clk_hw_clks *hws;
@@ -393,17 +482,17 @@ static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)if (!data)return -ENOMEM ; - hws =devm_kzalloc (dev,sizeof (*hws) +2 *sizeof (struct clk_hw *), + 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 ); - base =devm_ioremap_resource (dev, res); -if (IS_ERR (base)) -return PTR_ERR (base); + 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, 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);
@@ -411,6 +500,7 @@ static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) 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;
Signature · 1 line
1.9.1
PATCH 9/10 DT: QCOM: Add cpufreq-dt to msm8996
In reply to Ilia LinAdd device tree frequency table for the MSM8996 to be used by the upstream cpufreq-dt driver with the clk-cpu-8996 driver as infrastructure.
--- arch/arm64/boot/dts/qcom/apq8096-db820c.dts | 2 +- arch/arm64/boot/dts/qcom/msm8996.dtsi | 184 ++++++++++++++++++++++++++++ drivers/cpufreq/cpufreq-dt-platdev.c | 3 + 3 files changed, 188 insertions(+), 1 deletion(-)
arch/arm64/boot/dts/qcom/apq8096-db820c.dts +1 -1
@@ -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"; };
arch/arm64/boot/dts/qcom/msm8996.dtsi +184 -0
214 lines not on this page · raw message
drivers/cpufreq/cpufreq-dt-platdev.c +3 -0
@@ -95,6 +95,9 @@ { .compatible = "xlnx,zynq-7000", }, { .compatible = "xlnx,zynqmp", }, + { .compatible = "qcom,msm8996", }, + { .compatible = "qcom,apq8096", }, + { } };
Signature · 1 line
1.9.1
PATCH 10/10 DT: QCOM: Add thermal mitigation to msm8996
In reply to Ilia LinAdd thermal mitigation configuration to msm8996. With the cpufreq-dt OPP table the CPU frequency may be scaled and therefore throttled by the thermal config.
--- arch/arm64/boot/dts/qcom/msm8996.dtsi | 98 +++++++++++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 11 deletions(-)
arch/arm64/boot/dts/qcom/msm8996.dtsi +87 -11
@@ -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";
@@ -88,6 +89,10 @@ 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";
@@ -102,6 +107,10 @@ 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>; };
@@ -112,6 +121,10 @@ 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";
@@ -126,6 +139,10 @@ 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>; };
@@ -336,18 +353,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 {
@@ -357,18 +389,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 {
@@ -378,18 +425,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 {
@@ -399,18 +460,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>; + }; + }; }; };
Signature · 1 line
1.9.1