* [PATCH v6 1/3] dt-bindings: cpufreq: cpufreq-qcom-hw: Add cpufreq clock provider
2022-11-09 8:01 [PATCH v6 0/3] qcom-cpufreq-hw: Add CPU clock provider support Manivannan Sadhasivam
@ 2022-11-09 8:01 ` Manivannan Sadhasivam
2022-11-09 8:01 ` [PATCH v6 2/3] arm64: dts: qcom: sm8450: Supply clock from cpufreq node to CPUs Manivannan Sadhasivam
2022-11-09 8:01 ` [PATCH v6 3/3] cpufreq: qcom-hw: Add CPU clock provider support Manivannan Sadhasivam
2 siblings, 0 replies; 5+ messages in thread
From: Manivannan Sadhasivam @ 2022-11-09 8:01 UTC (permalink / raw)
To: andersson, viresh.kumar, krzysztof.kozlowski+dt, rafael, robh+dt
Cc: johan, devicetree, linux-arm-msm, linux-kernel, linux-pm,
Manivannan Sadhasivam, Rob Herring
Qualcomm platforms making use of CPUFreq HW Engine (EPSS/OSM) supply clocks
to the CPU cores. Document the same in the binding to reflect the actual
implementation.
CPUFreq HW will become the clock provider and CPU cores will become the
clock consumers.
The clock index for each CPU core is based on the frequency domain index.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml
index e58c55f78aaa..676d369a6fdd 100644
--- a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml
+++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml
@@ -56,6 +56,9 @@ properties:
'#freq-domain-cells':
const: 1
+ '#clock-cells':
+ const: 1
+
required:
- compatible
- reg
@@ -83,6 +86,7 @@ examples:
enable-method = "psci";
next-level-cache = <&L2_0>;
qcom,freq-domain = <&cpufreq_hw 0>;
+ clocks = <&cpufreq_hw 0>;
L2_0: l2-cache {
compatible = "cache";
cache-unified;
@@ -103,6 +107,7 @@ examples:
enable-method = "psci";
next-level-cache = <&L2_100>;
qcom,freq-domain = <&cpufreq_hw 0>;
+ clocks = <&cpufreq_hw 0>;
L2_100: l2-cache {
compatible = "cache";
cache-unified;
@@ -118,6 +123,7 @@ examples:
enable-method = "psci";
next-level-cache = <&L2_200>;
qcom,freq-domain = <&cpufreq_hw 0>;
+ clocks = <&cpufreq_hw 0>;
L2_200: l2-cache {
compatible = "cache";
cache-unified;
@@ -133,6 +139,7 @@ examples:
enable-method = "psci";
next-level-cache = <&L2_300>;
qcom,freq-domain = <&cpufreq_hw 0>;
+ clocks = <&cpufreq_hw 0>;
L2_300: l2-cache {
compatible = "cache";
cache-unified;
@@ -148,6 +155,7 @@ examples:
enable-method = "psci";
next-level-cache = <&L2_400>;
qcom,freq-domain = <&cpufreq_hw 1>;
+ clocks = <&cpufreq_hw 1>;
L2_400: l2-cache {
compatible = "cache";
cache-unified;
@@ -163,6 +171,7 @@ examples:
enable-method = "psci";
next-level-cache = <&L2_500>;
qcom,freq-domain = <&cpufreq_hw 1>;
+ clocks = <&cpufreq_hw 1>;
L2_500: l2-cache {
compatible = "cache";
cache-unified;
@@ -178,6 +187,7 @@ examples:
enable-method = "psci";
next-level-cache = <&L2_600>;
qcom,freq-domain = <&cpufreq_hw 1>;
+ clocks = <&cpufreq_hw 1>;
L2_600: l2-cache {
compatible = "cache";
cache-unified;
@@ -193,6 +203,7 @@ examples:
enable-method = "psci";
next-level-cache = <&L2_700>;
qcom,freq-domain = <&cpufreq_hw 1>;
+ clocks = <&cpufreq_hw 1>;
L2_700: l2-cache {
compatible = "cache";
cache-unified;
@@ -215,6 +226,7 @@ examples:
clock-names = "xo", "alternate";
#freq-domain-cells = <1>;
+ #clock-cells = <1>;
};
};
...
--
2.25.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v6 2/3] arm64: dts: qcom: sm8450: Supply clock from cpufreq node to CPUs
2022-11-09 8:01 [PATCH v6 0/3] qcom-cpufreq-hw: Add CPU clock provider support Manivannan Sadhasivam
2022-11-09 8:01 ` [PATCH v6 1/3] dt-bindings: cpufreq: cpufreq-qcom-hw: Add cpufreq clock provider Manivannan Sadhasivam
@ 2022-11-09 8:01 ` Manivannan Sadhasivam
2022-11-09 8:01 ` [PATCH v6 3/3] cpufreq: qcom-hw: Add CPU clock provider support Manivannan Sadhasivam
2 siblings, 0 replies; 5+ messages in thread
From: Manivannan Sadhasivam @ 2022-11-09 8:01 UTC (permalink / raw)
To: andersson, viresh.kumar, krzysztof.kozlowski+dt, rafael, robh+dt
Cc: johan, devicetree, linux-arm-msm, linux-kernel, linux-pm,
Manivannan Sadhasivam
Qualcomm platforms making use of CPUFreq HW Engine (EPSS/OSM) supply clocks
to the CPU cores. But this relationship is not represented in DTS so far.
So let's make cpufreq node as the clock provider and CPU nodes as the
consumers. The clock index for each CPU node is based on the frequency
domain index.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
arch/arm64/boot/dts/qcom/sm8450.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi
index d32f08df743d..234d2722a4fa 100644
--- a/arch/arm64/boot/dts/qcom/sm8450.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi
@@ -51,6 +51,7 @@ CPU0: cpu@0 {
power-domain-names = "psci";
qcom,freq-domain = <&cpufreq_hw 0>;
#cooling-cells = <2>;
+ clocks = <&cpufreq_hw 0>;
L2_0: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
@@ -70,6 +71,7 @@ CPU1: cpu@100 {
power-domain-names = "psci";
qcom,freq-domain = <&cpufreq_hw 0>;
#cooling-cells = <2>;
+ clocks = <&cpufreq_hw 0>;
L2_100: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
@@ -86,6 +88,7 @@ CPU2: cpu@200 {
power-domain-names = "psci";
qcom,freq-domain = <&cpufreq_hw 0>;
#cooling-cells = <2>;
+ clocks = <&cpufreq_hw 0>;
L2_200: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
@@ -102,6 +105,7 @@ CPU3: cpu@300 {
power-domain-names = "psci";
qcom,freq-domain = <&cpufreq_hw 0>;
#cooling-cells = <2>;
+ clocks = <&cpufreq_hw 0>;
L2_300: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
@@ -118,6 +122,7 @@ CPU4: cpu@400 {
power-domain-names = "psci";
qcom,freq-domain = <&cpufreq_hw 1>;
#cooling-cells = <2>;
+ clocks = <&cpufreq_hw 1>;
L2_400: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
@@ -134,6 +139,7 @@ CPU5: cpu@500 {
power-domain-names = "psci";
qcom,freq-domain = <&cpufreq_hw 1>;
#cooling-cells = <2>;
+ clocks = <&cpufreq_hw 1>;
L2_500: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
@@ -151,6 +157,7 @@ CPU6: cpu@600 {
power-domain-names = "psci";
qcom,freq-domain = <&cpufreq_hw 1>;
#cooling-cells = <2>;
+ clocks = <&cpufreq_hw 1>;
L2_600: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
@@ -167,6 +174,7 @@ CPU7: cpu@700 {
power-domain-names = "psci";
qcom,freq-domain = <&cpufreq_hw 2>;
#cooling-cells = <2>;
+ clocks = <&cpufreq_hw 2>;
L2_700: l2-cache {
compatible = "cache";
next-level-cache = <&L3_0>;
@@ -3075,6 +3083,7 @@ cpufreq_hw: cpufreq@17d91000 {
<GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "dcvsh-irq-0", "dcvsh-irq-1", "dcvsh-irq-2";
#freq-domain-cells = <1>;
+ #clock-cells = <1>;
};
gem_noc: interconnect@19100000 {
--
2.25.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v6 3/3] cpufreq: qcom-hw: Add CPU clock provider support
2022-11-09 8:01 [PATCH v6 0/3] qcom-cpufreq-hw: Add CPU clock provider support Manivannan Sadhasivam
2022-11-09 8:01 ` [PATCH v6 1/3] dt-bindings: cpufreq: cpufreq-qcom-hw: Add cpufreq clock provider Manivannan Sadhasivam
2022-11-09 8:01 ` [PATCH v6 2/3] arm64: dts: qcom: sm8450: Supply clock from cpufreq node to CPUs Manivannan Sadhasivam
@ 2022-11-09 8:01 ` Manivannan Sadhasivam
2022-11-09 8:16 ` Johan Hovold
2 siblings, 1 reply; 5+ messages in thread
From: Manivannan Sadhasivam @ 2022-11-09 8:01 UTC (permalink / raw)
To: andersson, viresh.kumar, krzysztof.kozlowski+dt, rafael, robh+dt
Cc: johan, devicetree, linux-arm-msm, linux-kernel, linux-pm,
Manivannan Sadhasivam
Qcom CPUFreq hardware (EPSS/OSM) controls clock and voltage to the CPU
cores. But this relationship is not represented with the clk framework
so far.
So, let's make the qcom-cpufreq-hw driver a clock provider. This makes the
clock producer/consumer relationship cleaner and is also useful for CPU
related frameworks like OPP to know the frequency at which the CPUs are
running.
The clock frequency provided by the driver is for each frequency domain.
We cannot get the frequency of each CPU core because, not all platforms
support per-core DCVS feature.
Also the frequency supplied by the driver is the actual frequency that
comes out of the EPSS/OSM block after the DCVS operation. This frequency is
not same as what the CPUFreq framework has set but it is the one that gets
supplied to the CPUs after throttling by LMh.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
drivers/cpufreq/qcom-cpufreq-hw.c | 41 +++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c
index 5e0598730a04..c0e4b223f9c1 100644
--- a/drivers/cpufreq/qcom-cpufreq-hw.c
+++ b/drivers/cpufreq/qcom-cpufreq-hw.c
@@ -4,6 +4,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <linux/interconnect.h>
@@ -54,6 +55,7 @@ struct qcom_cpufreq_data {
bool cancel_throttle;
struct delayed_work throttle_work;
struct cpufreq_policy *policy;
+ struct clk_hw cpu_clk;
bool per_core_dcvs;
@@ -615,8 +617,20 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = {
.ready = qcom_cpufreq_ready,
};
+static unsigned long qcom_cpufreq_hw_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct qcom_cpufreq_data *data = container_of(hw, struct qcom_cpufreq_data, cpu_clk);
+
+ return qcom_lmh_get_throttle_freq(data);
+}
+
+static const struct clk_ops qcom_cpufreq_hw_clk_ops = {
+ .recalc_rate = qcom_cpufreq_hw_recalc_rate,
+};
+
static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
{
+ struct clk_hw_onecell_data *clk_data;
struct device *dev = &pdev->dev;
struct device *cpu_dev;
struct clk *clk;
@@ -659,8 +673,15 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
qcom_cpufreq.soc_data = of_device_get_match_data(dev);
+ clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, num_domains), GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clk_data->num = num_domains;
+
for (i = 0; i < num_domains; i++) {
struct qcom_cpufreq_data *data = &qcom_cpufreq.data[i];
+ struct clk_init_data clk_init = {};
struct resource *res;
void __iomem *base;
@@ -672,6 +693,26 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
data->base = base;
data->res = res;
+
+ /* Register CPU clock for each frequency domain */
+ clk_init.name = devm_kasprintf(dev, GFP_KERNEL, "qcom_cpufreq%d", i);
+ clk_init.flags = CLK_GET_RATE_NOCACHE;
+ clk_init.ops = &qcom_cpufreq_hw_clk_ops;
+ data->cpu_clk.init = &clk_init;
+
+ ret = devm_clk_hw_register(dev, &data->cpu_clk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register Qcom CPUFreq clock(%d)\n", i);
+ return ret;
+ }
+
+ clk_data->hws[i] = &data->cpu_clk;
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+ if (ret < 0) {
+ dev_err(dev, "Failed to add Qcom CPUFreq clock provider\n");
+ return ret;
}
ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver);
--
2.25.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH v6 3/3] cpufreq: qcom-hw: Add CPU clock provider support
2022-11-09 8:01 ` [PATCH v6 3/3] cpufreq: qcom-hw: Add CPU clock provider support Manivannan Sadhasivam
@ 2022-11-09 8:16 ` Johan Hovold
0 siblings, 0 replies; 5+ messages in thread
From: Johan Hovold @ 2022-11-09 8:16 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: andersson, viresh.kumar, krzysztof.kozlowski+dt, rafael, robh+dt,
devicetree, linux-arm-msm, linux-kernel, linux-pm
On Wed, Nov 09, 2022 at 01:31:20PM +0530, Manivannan Sadhasivam wrote:
> Qcom CPUFreq hardware (EPSS/OSM) controls clock and voltage to the CPU
> cores. But this relationship is not represented with the clk framework
> so far.
>
> So, let's make the qcom-cpufreq-hw driver a clock provider. This makes the
> clock producer/consumer relationship cleaner and is also useful for CPU
> related frameworks like OPP to know the frequency at which the CPUs are
> running.
>
> The clock frequency provided by the driver is for each frequency domain.
> We cannot get the frequency of each CPU core because, not all platforms
> support per-core DCVS feature.
>
> Also the frequency supplied by the driver is the actual frequency that
> comes out of the EPSS/OSM block after the DCVS operation. This frequency is
> not same as what the CPUFreq framework has set but it is the one that gets
> supplied to the CPUs after throttling by LMh.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
> drivers/cpufreq/qcom-cpufreq-hw.c | 41 +++++++++++++++++++++++++++++++
> 1 file changed, 41 insertions(+)
>
> diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c
> index 5e0598730a04..c0e4b223f9c1 100644
> --- a/drivers/cpufreq/qcom-cpufreq-hw.c
> +++ b/drivers/cpufreq/qcom-cpufreq-hw.c
> @@ -672,6 +693,26 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
>
> data->base = base;
> data->res = res;
> +
> + /* Register CPU clock for each frequency domain */
> + clk_init.name = devm_kasprintf(dev, GFP_KERNEL, "qcom_cpufreq%d", i);
This is still an allocation and can in theory fail.
But it is also unnecessary to keep these around after registering the
clocks so it's better to just reuse a single stack allocated buffer for
this.
> + clk_init.flags = CLK_GET_RATE_NOCACHE;
> + clk_init.ops = &qcom_cpufreq_hw_clk_ops;
> + data->cpu_clk.init = &clk_init;
> +
> + ret = devm_clk_hw_register(dev, &data->cpu_clk);
> + if (ret < 0) {
> + dev_err(dev, "Failed to register Qcom CPUFreq clock(%d)\n", i);
nit: This looks unnecessarily verbose. Using
"failed to register clock %d: %d\n", i, ret
should do just fine as you are using dev_err().
> + return ret;
> + }
> +
> + clk_data->hws[i] = &data->cpu_clk;
> + }
> +
> + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
> + if (ret < 0) {
> + dev_err(dev, "Failed to add Qcom CPUFreq clock provider\n");
> + return ret;
> }
>
> ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver);
Johan
^ permalink raw reply [flat|nested] 5+ messages in thread