Add support for QCOM cpufreq FW driver
v2 cover Add support for QCOM cpufreq FW driver
[v2]
* Address comments given in v0 series.
[v1]
* Fixed compilation reported by Amit K.
The CPUfreq FW present in some QCOM chipsets offloads the steps necessary for changing the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware.
Taniya Das (2):
dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
.../bindings/cpufreq/cpufreq-qcom-fw.txt | 68 +++++ drivers/cpufreq/Kconfig.arm | 9 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/qcom-cpufreq-fw.c | 317 +++++++++++++++++++++ 4 files changed, 395 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt create mode 100644 drivers/cpufreq/qcom-cpufreq-fw.c
--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member of the Code Aurora Forum, hosted by the Linux Foundation.
PATCH v2 1/2 dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
Add QCOM cpufreq firmware device bindings for Qualcomm Technology Inc's SoCs. This is required for managing the cpu frequency transitions which are controlled by firmware.
--- .../bindings/cpufreq/cpufreq-qcom-fw.txt | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +68 -0
@@ -0,0 +1,68 @@ +Qualcomm Technologies, Inc. CPUFREQ Bindings + +CPUFREQ FW is a hardware engine used by some Qualcomm Technologies, Inc. (QTI) +SoCs to manage frequency in hardware. It is capable of controlling frequency +for multiple clusters. + +Properties: +- compatible + Usage: required + Value type: <string> + Definition: must be "qcom,cpufreq-fw". + +Note that #address-cells, #size-cells, and ranges shall be present to ensure +the cpufreq can address a freq-domain registers. + +A freq-domain sub-node would be defined for the cpus with the following +properties: + +- compatible: + Usage: required + Value type: <string> + Definition: must be "cpufreq". + +- reg + Usage: required + Value type: <prop-encoded-array> + Definition: Addresses and sizes for the memory of the perf_base + , lut_base and en_base. +- reg-names + Usage: required + Value type: <stringlist> + Definition: Address names. Must be "perf_base", "lut_base", + "en_base". + Must be specified in the same order as the + corresponding addresses are specified in the reg + property. + +- qcom,cpulist + Usage: required + Value type: <phandles of CPU> + Definition: List of related cpu handles which are under a cluster. + +Example: + qcom,cpufreq-fw { + compatible = "qcom,cpufreq-fw"; + + #address-cells = <1>; + #size-cells = <1>; + ranges; + + freq-domain-0 { + compatible = "cpufreq"; + reg = <0x17d43920 0x4>, + <0x17d43110 0x500>, + <0x17d41000 0x4>; + reg-names = "perf_base", "lut_base", "en_base"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>; + }; + + freq-domain-1 { + compatible = "cpufreq"; + reg = <0x17d46120 0x4>, + <0x17d45910 0x500>, + <0x17d45800 0x4>; + reg-names = "perf_base", "lut_base", "en_base"; + qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>; + }; + };
--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member of the Code Aurora Forum, hosted by the Linux Foundation.
Rob Herring
maintainerRe: [PATCH v2 1/2] dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
Quoted 17 lines · Taniya Das
Add QCOM cpufreq firmware device bindings for Qualcomm Technology Inc's SoCs. This is required for managing the cpu frequency transitions which are controlled by firmware.
Signed-off-by Taniya Das <[email protected]>--- .../bindings/cpufreq/cpufreq-qcom-fw.txt | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txtDocumentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +1
-0@@ -0,0 +1,68 @@ +Qualcomm Technologies, Inc. CPUFREQ Bindings
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 11
Quoted 10 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +10
-0+ +CPUFREQ FW is a hardware engine used by some Qualcomm Technologies, Inc. (QTI) +SoCs to manage frequency in hardware. It is capable of controlling frequency +for multiple clusters. + +Properties: +- compatible + Usage: required + Value type: <string> + Definition: must be "qcom,cpufreq-fw".
Only 1 version ever?
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 22
Quoted 11 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +11
-0+ +Note that #address-cells, #size-cells, and ranges shall be present to ensure +the cpufreq can address a freq-domain registers. + +A freq-domain sub-node would be defined for the cpus with the following +properties: + +- compatible: + Usage: required + Value type: <string> + Definition: must be "cpufreq".
Too generic. This is very much a QCom specific binding. Perhaps just drop the compatible.
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 33
Quoted 11 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +11
-0+ +- reg + Usage: required + Value type: <prop-encoded-array> + Definition: Addresses and sizes for the memory of the perf_base + , lut_base and en_base. +- reg-names + Usage: required + Value type: <stringlist> + Definition: Address names. Must be "perf_base", "lut_base", + "en_base".
_base is redundant.
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 36
Quoted 3 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +3
-0+ Must be specified in the same order as the + corresponding addresses are specified in the reg + property.
Actually, must be specified in the order you specify here.
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 49
Quoted 13 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +13
-0+ +- qcom,cpulist + Usage: required + Value type: <phandles of CPU> + Definition: List of related cpu handles which are under a cluster. + +Example: + qcom,cpufreq-fw { + compatible = "qcom,cpufreq-fw"; + + #address-cells = <1>; + #size-cells = <1>; + ranges;
There's not a smaller address range you can restrict children to?
Quoted 23 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +19
-1+ + freq-domain-0 { + compatible = "cpufreq"; + reg = <0x17d43920 0x4>, + <0x17d43110 0x500>, + <0x17d41000 0x4>; + reg-names = "perf_base", "lut_base", "en_base"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>; + }; + + freq-domain-1 { + compatible = "cpufreq"; + reg = <0x17d46120 0x4>, + <0x17d45910 0x500>, + <0x17d45800 0x4>; + reg-names = "perf_base", "lut_base", "en_base"; + qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>; + }; + }; --Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member of the Code Aurora Forum, hosted by the Linux Foundation.
Viresh Kumar
maintainerRe: [PATCH v2 1/2] dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
Quoted 8 lines · Rob Herring
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 57
Quoted 7 lines · Taniya Das
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +7
-0+ freq-domain-0 { + compatible = "cpufreq"; + reg = <0x17d43920 0x4>, + <0x17d43110 0x500>, + <0x17d41000 0x4>; + reg-names = "perf_base", "lut_base", "en_base"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>;
I was thinking, can't we add platform specific properties in the CPU nodes ? If yes, then we can point the phandle of fw node from the CPUs and this awkward list can go away.
Signature · 1 line
viresh
Rob Herring
maintainerRe: [PATCH v2 1/2] dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
Quoted 10 lines · Viresh Kumar
Quoted 8 lines · Rob Herring
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 57
Quoted 7 lines · Taniya Das
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +7
-0+ freq-domain-0 { + compatible = "cpufreq"; + reg = <0x17d43920 0x4>, + <0x17d43110 0x500>, + <0x17d41000 0x4>; + reg-names = "perf_base", "lut_base", "en_base"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>;
Quoted 3 lines
I was thinking, can't we add platform specific properties in the CPU nodes ? If yes, then we can point the phandle of fw node from the CPUs and this awkward list can go away.
Yes, that's fine. That would be more like OPP binding in that the CPU points to the OPP table rather than the OPP pointing to the CPUs.
With that, you can get rid of the child nodes completely. Just make the parent reg property N sets of 3 addresses for N domains.
Rob
Re: [PATCH v2 1/2] dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
Hello Rob, Viresh,
Thank you for the comments. If I understand correctly, the device tree nodes should look something like the below.
Though I am not sure if any vendor name could be associated in the cpu nodes.
Please suggest if my understanding is wrong.
cpu@0 {
qcom,freq-domain = <&freq_domain_table0>;
…
};same follows for cpu 1/2/3
cpu@400 {
qcom,freq-domain = <&freq_domain_table1>;
…
};same follows for cpu 5/6/7
freq_domain_table0 : freq_table {
reg = < >, < >, < > ;
reg-names = "perf_base", "lut_base", "en_base";
};freq_domain_table1 : freq_table {
reg = < >, < >, < > ;
reg-names = "perf_base", "lut_base", "en_base";
};Quoted 23 lines · Rob Herring
Quoted 10 lines · Viresh Kumar
Quoted 8 lines · Rob Herring
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 57
Quoted 7 lines · Taniya Das
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +7
-0+ freq-domain-0 { + compatible = "cpufreq"; + reg = <0x17d43920 0x4>, + <0x17d43110 0x500>, + <0x17d41000 0x4>; + reg-names = "perf_base", "lut_base", "en_base"; + qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>;Quoted 3 lines
I was thinking, can't we add platform specific properties in the CPU nodes ? If yes, then we can point the phandle of fw node from the CPUs and this awkward list can go away.
Yes, that's fine. That would be more like OPP binding in that the CPU points to the OPP table rather than the OPP pointing to the CPUs.
With that, you can get rid of the child nodes completely. Just make the parent reg property N sets of 3 addresses for N domains.
Rob
Signature · 4 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. --
Viresh Kumar
maintainerRe: [PATCH v2 1/2] dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
Quoted 7 lines · Taniya Das
Hello Rob, Viresh,
Thank you for the comments. If I understand correctly, the device tree nodes should look something like the below.
Though I am not sure if any vendor name could be associated in the cpu nodes.
Well Rob said Yes to that I think.
Quoted 20 lines
Please suggest if my understanding is wrong.
cpu@0 { qcom,freq-domain = <&freq_domain_table0>; … };same follows for cpu 1/2/3
cpu@400 { qcom,freq-domain = <&freq_domain_table1>; … };same follows for cpu 5/6/7
freq_domain_table0 : freq_table { reg = < >, < >, < > ; reg-names = "perf_base", "lut_base", "en_base"; };
Quoted 4 lines
freq_domain_table1 : freq_table {
reg = < >, < >, < > ;
reg-names = "perf_base", "lut_base", "en_base";
};Mostly yes.
Signature · 1 line
viresh
Re: [PATCH v2 1/2] dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
Hello Rob,
Could you please suggest if the below looks okay to be implemented?
Quoted 38 lines · Viresh Kumar
Quoted 7 lines · Taniya Das
Hello Rob, Viresh,
Thank you for the comments. If I understand correctly, the device tree nodes should look something like the below.
Though I am not sure if any vendor name could be associated in the cpu nodes.
Well Rob said Yes to that I think.
Quoted 20 lines
Please suggest if my understanding is wrong.
cpu@0 { qcom,freq-domain = <&freq_domain_table0>; … };same follows for cpu 1/2/3
cpu@400 { qcom,freq-domain = <&freq_domain_table1>; … };same follows for cpu 5/6/7
freq_domain_table0 : freq_table { reg = < >, < >, < > ; reg-names = "perf_base", "lut_base", "en_base"; };Quoted 4 lines
freq_domain_table1 : freq_table { reg = < >, < >, < > ; reg-names = "perf_base", "lut_base", "en_base"; };Mostly yes.
Signature · 4 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. --
Re: [PATCH v2 1/2] dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
In reply to Taniya DasQuoted 18 lines · Taniya Das
Add QCOM cpufreq firmware device bindings for Qualcomm Technology Inc's SoCs. This is required for managing the cpu frequency transitions which are controlled by firmware.
Signed-off-by Taniya Das <[email protected]>--- .../bindings/cpufreq/cpufreq-qcom-fw.txt | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txtDocumentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +2
-0@@ -0,0 +1,68 @@ +Qualcomm Technologies, Inc. CPUFREQ Bindings +
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 12
Quoted 10 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +10
-0+CPUFREQ FW is a hardware engine used by some Qualcomm Technologies, Inc. (QTI) +SoCs to manage frequency in hardware. It is capable of controlling frequency +for multiple clusters. + +Properties: +- compatible + Usage: required + Value type: <string> + Definition: must be "qcom,cpufreq-fw". +
If the firmware is referred with some other name, better to use that than cpufreq
Quoted 6 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +6
-0+Note that #address-cells, #size-cells, and ranges shall be present to ensure +the cpufreq can address a freq-domain registers. + +A freq-domain sub-node would be defined for the cpus with the following +properties: +
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 28
Quoted 10 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +10
-0+- compatible: + Usage: required + Value type: <string> + Definition: must be "cpufreq". + +- reg + Usage: required + Value type: <prop-encoded-array> + Definition: Addresses and sizes for the memory of the perf_base + , lut_base and en_base.
Can you explicitly define each one of them ? Either here or in reg-names.
Quoted 4 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +4
-0+- reg-names + Usage: required + Value type: <stringlist> + Definition: Address names. Must be "perf_base", "lut_base",
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 42
Quoted 10 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +10
-0+ "en_base". + Must be specified in the same order as the + corresponding addresses are specified in the reg + property. + +- qcom,cpulist + Usage: required + Value type: <phandles of CPU> + Definition: List of related cpu handles which are under a cluster. +
As already mentioned by Rob and Viresh, better to align with OPP style to avoid phandle list of CPUs.
Also I see similar bindings for devfreq, can't they be aligned ? E.g. lut_base here while it's ftbl_base in devfreq.
Signature · 4 lines
Regards, Sudeep [1] https://lkml.org/lkml/2018/5/18/136
Re: [PATCH v2 1/2] dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
Quoted 44 lines · Sudeep Holla
Quoted 18 lines · Taniya Das
Add QCOM cpufreq firmware device bindings for Qualcomm Technology Inc's SoCs. This is required for managing the cpu frequency transitions which are controlled by firmware.
Signed-off-by Taniya Das <[email protected]>--- .../bindings/cpufreq/cpufreq-qcom-fw.txt | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+)create mode 100644
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txtDocumentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +2
-0@@ -0,0 +1,68 @@ +Qualcomm Technologies, Inc. CPUFREQ Bindings +Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 12
Quoted 10 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +10
-0+CPUFREQ FW is a hardware engine used by some Qualcomm Technologies, Inc. (QTI) +SoCs to manage frequency in hardware. It is capable of controlling frequency +for multiple clusters. + +Properties: +- compatible + Usage: required + Value type: <string> + Definition: must be "qcom,cpufreq-fw". +If the firmware is referred with some other name, better to use that than cpufreq
Quoted 10 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +10
-0+Note that #address-cells, #size-cells, and ranges shall be present to ensure +the cpufreq can address a freq-domain registers. + +A freq-domain sub-node would be defined for the cpus with the following +properties: + +- compatible: + Usage: required + Value type: <string> + Definition: must be "cpufreq".
Quoted 9 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 28
Quoted 6 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +6
-0+ +- reg + Usage: required + Value type: <prop-encoded-array> + Definition: Addresses and sizes for the memory of the perf_base + , lut_base and en_base.Can you explicitly define each one of them ? Either here or in reg-names.
Sure will define each of them in the next series.
Quoted 9 lines
Quoted 9 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +9
-0+- reg-names + Usage: required + Value type: <stringlist> + Definition: Address names. Must be "perf_base", "lut_base", + "en_base". + Must be specified in the same order as the + corresponding addresses are specified in the reg + property. +
Quoted 9 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt line 42
Quoted 5 lines
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt new file +5
-0+- qcom,cpulist + Usage: required + Value type: <phandles of CPU> + Definition: List of related cpu handles which are under a cluster. +As already mentioned by Rob and Viresh, better to align with OPP style to avoid phandle list of CPUs.
I have put down an example device tree node for Rob & Viresh to review.
Quoted 3 lines
Also I see similar bindings for devfreq, can't they be aligned ? E.g.
lut_basehere while it'sftbl_basein devfreq.
It is actually the look up table, thus was the name given as "lut". Will check with Saravana for devfreq and align accordingly.
Signature · 4 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. --
PATCH v2 2/2 cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
In reply to Taniya DasThe CPUfreq FW present in some QCOM chipsets offloads the steps necessary for changing the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware.
--- drivers/cpufreq/Kconfig.arm | 9 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/qcom-cpufreq-fw.c | 317 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 drivers/cpufreq/qcom-cpufreq-fw.c
drivers/cpufreq/Kconfig.arm +9 -0
@@ -301,3 +301,12 @@ config ARM_PXA2xx_CPUFREQ This add the CPUFreq driver support for Intel PXA2xx SOCs. If in doubt, say N. + +config ARM_QCOM_CPUFREQ_FW + bool "QCOM CPUFreq FW driver" + help + Support for the CPUFreq FW driver. + The CPUfreq FW preset in some QCOM chipsets offloads the steps + necessary for changing the frequency of CPUs. The driver + implements the cpufreq driver interface for this firmware. + Say Y if you want to support CPUFreq FW.
drivers/cpufreq/Makefile +1 -0
@@ -85,6 +85,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o +obj-$(CONFIG_ARM_QCOM_CPUFREQ_FW) += qcom-cpufreq-fw.o ##################################################################################
drivers/cpufreq/qcom-cpufreq-fw.c new file +317 -0
317 lines not on this page · raw message
--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member of the Code Aurora Forum, hosted by the Linux Foundation.
Viresh Kumar
maintainerRe: [PATCH v2 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
Quoted 18 lines · Taniya Das
The CPUfreq FW present in some QCOM chipsets offloads the steps necessary for changing the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware.
Signed-off-by Saravana Kannan <[email protected]>Signed-off-by Taniya Das <[email protected]>--- drivers/cpufreq/Kconfig.arm | 9 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/qcom-cpufreq-fw.c | 317 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 drivers/cpufreq/qcom-cpufreq-fw.cdrivers/cpufreq/Kconfig.arm +0
-0
drivers/cpufreq/Kconfig.arm config ARM_PXA2xx_CPUFREQ · line 306
Quoted 6 lines
drivers/cpufreq/Kconfig.arm +3
-0@@ -301,3 +301,12 @@ config ARM_PXA2xx_CPUFREQ This add the CPUFreq driver support for Intel PXA2xx SOCs. If in doubt, say N. + +config ARM_QCOM_CPUFREQ_FW + bool "QCOM CPUFreq FW driver"
During last review I didn't say that this driver shouldn't be a module, but that you need to fix things to make it a module. I am fine though if you don't want this to be a module ever.
Quoted 54 lines
drivers/cpufreq/Kconfig.arm +6
-0+ help + Support for the CPUFreq FW driver. + The CPUfreq FW preset in some QCOM chipsets offloads the steps + necessary for changing the frequency of CPUs. The driver + implements the cpufreq driver interface for this firmware. + Say Y if you want to support CPUFreq FW.drivers/cpufreq/Makefile +1
-0@@ -85,6 +85,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o +obj-$(CONFIG_ARM_QCOM_CPUFREQ_FW) += qcom-cpufreq-fw.o ##################################################################################drivers/cpufreq/qcom-cpufreq-fw.c new file +30
-0@@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2018, The Linux Foundation. All rights reserved. +*/ + +#include <linux/cpufreq.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#define INIT_RATE 300000000UL +#define XO_RATE 19200000UL +#define LUT_MAX_ENTRIES 40U +#define CORE_COUNT_VAL (val) ((val & GENMASK(18, 16)) >> 16) +#define LUT_ROW_SIZE 32 + +struct cpufreq_qcom { +struct cpufreq_frequency_table *table; +struct device *dev; +void __iomem *perf_base; +void __iomem *lut_base; +cpumask_t related_cpus; +unsigned int max_cores; +}; + +static struct cpufreq_qcom *qcom_freq_domain_map[NR_CPUS ]; + +static int
drivers/cpufreq/qcom-cpufreq-fw.c line 40
Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+qcom_cpufreq_fw_target_index(struct cpufreq_policy *policy, unsigned int index) +{ +struct cpufreq_qcom *c = policy->driver_data; + +if (index >=LUT_MAX_ENTRIES ) { +dev_err (c->dev, +"Passing an index (%u) that's greater than max (%d)\n" , + index,LUT_MAX_ENTRIES -1 ); +return -EINVAL ; + }
This is never going to happen unless there is a bug in cpufreq core. You are allocating only 40 entries for the cpufreq table and this will always be 0-39. None of the other drivers is checking this I believe and neither should you. This is the only routine which will get call very frequently and we better not add unnecessary stuff here.
drivers/cpufreq/qcom-cpufreq-fw.c line 45
Quoted 4 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +4
-0+writel_relaxed (index, c->perf_base); + +/* Make sure the write goes through before proceeding */ +mb ();
Btw what happens right after this is done ? Are we guaranteed that the frequency is updated in the hardware after this ? What about enabling fast-switch for your platform ? Look at drivers/cpufreq/scmi-cpufreq.c to see how that is done.
drivers/cpufreq/qcom-cpufreq-fw.c line 56
Quoted 11 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +11
-0+return 0 ; +} + +static unsigned int qcom_cpufreq_fw_get (unsigned int cpu) +{ +struct cpufreq_qcom *c; +unsigned int index; + + c = qcom_freq_domain_map[cpu]; +if (!c) +return -ENODEV ;
Return 0 on error here.
drivers/cpufreq/qcom-cpufreq-fw.c line 59
Quoted 3 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +3
-0+ + index =readl_relaxed (c->perf_base); + index =min (index,LUT_MAX_ENTRIES -1 );
Will the hardware ever read a value over 39 here ?
Quoted 52 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +52
-0+ +return c->table[index].frequency; +} + +static int qcom_cpufreq_fw_cpu_init (struct cpufreq_policy *policy) +{ +struct cpufreq_qcom *c; + + c = qcom_freq_domain_map[policy->cpu]; +if (!c) { +pr_err ("No scaling support for CPU%d\n" , policy->cpu); +return -ENODEV ; + } + +cpumask_copy (policy->cpus, &c->related_cpus); + policy->freq_table = c->table; + policy->driver_data = c; + +return 0 ; +} + +static struct freq_attr *qcom_cpufreq_fw_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + &cpufreq_freq_attr_scaling_boost_freqs, +NULL +}; + +static struct cpufreq_driver cpufreq_qcom_fw_driver = { + .flags =CPUFREQ_STICKY |CPUFREQ_NEED_INITIAL_FREQ_CHECK | +CPUFREQ_HAVE_GOVERNOR_PER_POLICY , + .verify = cpufreq_generic_frequency_table_verify, + .target_index = qcom_cpufreq_fw_target_index, + .get = qcom_cpufreq_fw_get, + .init = qcom_cpufreq_fw_cpu_init, + .name ="qcom-cpufreq-fw" , + .attr = qcom_cpufreq_fw_attr, + .boost_enabled = true, +}; + +static int qcom_read_lut (struct platform_device *pdev, +struct cpufreq_qcom *c) +{ +struct device *dev = &pdev->dev; +u32 data, src, lval, i, core_count, prev_cc; + + c->table =devm_kcalloc (dev,LUT_MAX_ENTRIES +1 , +sizeof (*c->table),GFP_KERNEL ); +if (!c->table) +return -ENOMEM ; + +for (i =0 ; i <LUT_MAX_ENTRIES ; i++) { + data =readl_relaxed (c->lut_base + i *LUT_ROW_SIZE );
drivers/cpufreq/qcom-cpufreq-fw.c line 121
Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+ src = ((data &GENMASK (31 ,30 )) >>30 ); + lval = (data &GENMASK (7 ,0 )); + core_count =CORE_COUNT_VAL (data); + +if (!src) + c->table[i].frequency =INIT_RATE /1000 ; +else + c->table[i].frequency =XO_RATE * lval /1000 ; + + c->table[i].driver_data = c->table[i].frequency;
Why do you need to use driver_data here? Why can't you simple use frequency field in the below conditional expressions ?
Quoted 6 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +6
-0+ +dev_dbg (dev,"index=%d freq=%d, core_count %d\n" , + i, c->table[i].frequency, core_count); + +if (core_count != c->max_cores) + c->table[i].frequency =CPUFREQ_ENTRY_INVALID ;
drivers/cpufreq/qcom-cpufreq-fw.c line 137
Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+ +/* +* Two of the same frequencies with the same core counts means +* end of table. +*/ +if (i >0 && c->table[i -1 ].driver_data == + c->table[i].driver_data && prev_cc == core_count) { +struct cpufreq_frequency_table *prev = &c->table[i -1 ]; + +if (prev->frequency ==CPUFREQ_ENTRY_INVALID ) {
There can only be a single boost frequency at max ?
drivers/cpufreq/qcom-cpufreq-fw.c line 139
Quoted 2 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +2
-0+ prev->flags =CPUFREQ_BOOST_FREQ ; + prev->frequency = prev->driver_data;
Okay you are using driver_data as a local variable to keep this value safe which you might have overwritten. Maybe use a simple variable prev_freq for this. It would be much more readable in that case and you wouldn't end up abusing the driver_data field.
drivers/cpufreq/qcom-cpufreq-fw.c line 146
Quoted 7 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +7
-0+ } + +break ; + } + prev_cc = core_count; + } + c->table[i].frequency =CPUFREQ_TABLE_END ;
Wouldn't you end up writing on c->table[40].frequency here if there are 40 frequency value present ?
Quoted 5 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +5
-0+ +return 0 ; +} + +static int qcom_get_related_cpus (struct device_node *np,struct cpumask *m)
drivers/cpufreq/qcom-cpufreq-fw.c line 161
Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+{ +struct device_node *dev_phandle; +struct device *cpu_dev; +int cpu, i =0 ; + + dev_phandle =of_parse_phandle (np,"qcom,cpulist" , i++); +while (dev_phandle) { +for_each_possible_cpu (cpu) { + cpu_dev =get_cpu_device (cpu); +if (cpu_dev && cpu_dev->of_node == dev_phandle) {
You can use of_cpu_device_node_get() here.
Quoted 159 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +156
-1+cpumask_set_cpu (cpu, m); +break ; + } + } + dev_phandle =of_parse_phandle (np,"qcom,cpulist" , i++); + } + +if (cpumask_empty (m)) +return -ENOENT ; + +return 0 ; +} + +static int qcom_cpu_resources_init (struct platform_device *pdev, +struct device_node *np) +{ +struct cpufreq_qcom *c; +struct resource res; +struct device *dev = &pdev->dev; +void __iomem *en_base; +int cpu, index, ret; + + c =devm_kzalloc (dev,sizeof (*c),GFP_KERNEL ); +if (!c) +return -ENOMEM ; + + index =of_property_match_string (np,"reg-names" ,"en_base" ); +if (index <0 ) +return index; + +if (of_address_to_resource (np, index, &res)) +return -ENOMEM ; + + en_base =devm_ioremap (dev, res.start,resource_size (&res)); +if (!en_base) { +dev_err (dev,"Unable to map %s en-base\n" , np->name); +return -ENOMEM ; + } + +/* FW should be in enabled state to proceed */ +if (!(readl_relaxed (en_base) &0x1 )) { +dev_err (dev,"%s firmware not enabled\n" , np->name); +return -ENODEV ; + } + +devm_iounmap (&pdev->dev, en_base); + + index =of_property_match_string (np,"reg-names" ,"perf_base" ); +if (index <0 ) +return index; + +if (of_address_to_resource (np, index, &res)) +return -ENOMEM ; + + c->perf_base =devm_ioremap (dev, res.start,resource_size (&res)); +if (!c->perf_base) { +dev_err (dev,"Unable to map %s perf-base\n" , np->name); +return -ENOMEM ; + } + + index =of_property_match_string (np,"reg-names" ,"lut_base" ); +if (index <0 ) +return index; + +if (of_address_to_resource (np, index, &res)) +return -ENOMEM ; + + c->lut_base =devm_ioremap (dev, res.start,resource_size (&res)); +if (!c->lut_base) { +dev_err (dev,"Unable to map %s lut-base\n" , np->name); +return -ENOMEM ; + } + + ret =qcom_get_related_cpus (np, &c->related_cpus); +if (ret) { +dev_err (dev,"%s failed to get core phandles\n" , np->name); +return ret; + } + + c->max_cores =cpumask_weight (&c->related_cpus); + + ret =qcom_read_lut (pdev, c); +if (ret) { +dev_err (dev,"%s failed to read LUT\n" , np->name); +return ret; + } + +for_each_cpu (cpu, &c->related_cpus) + qcom_freq_domain_map[cpu] = c; + +return 0 ; +} + +static int qcom_resources_init (struct platform_device *pdev) +{ +struct device_node *np; +int ret; + +if (!of_get_available_child_count (pdev->dev.of_node)) +return -ENODEV ; + +for_each_available_child_of_node (pdev->dev.of_node, np) { +if (of_device_is_compatible (np,"cpufreq" )) { + ret =qcom_cpu_resources_init (pdev, np); +if (ret) +return ret; + } + } + +return 0 ; +} + +static int qcom_cpufreq_fw_driver_probe (struct platform_device *pdev) +{ +int rc; + +/* Get the bases of cpufreq for domains */ + rc =qcom_resources_init (pdev); +if (rc) { +dev_err (&pdev->dev,"CPUFreq resource init failed\n" ); +return rc; + } + + rc =cpufreq_register_driver (&cpufreq_qcom_fw_driver); +if (rc) { +dev_err (&pdev->dev,"CPUFreq FW driver failed to register\n" ); +return rc; + } + +dev_info (&pdev->dev,"QCOM CPUFreq FW driver inited\n" ); + +return 0 ; +} + +static const struct of_device_id match_table[] = { + { .compatible ="qcom,cpufreq-fw" }, + {} +}; + +static struct platform_driver qcom_cpufreq_fw_driver = { + .probe = qcom_cpufreq_fw_driver_probe, + .driver = { + .name ="qcom-cpufreq-fw" , + .of_match_table = match_table, + .owner =THIS_MODULE , + }, +}; + +static int __init qcom_cpufreq_fw_init (void ) +{ +return platform_driver_register (&qcom_cpufreq_fw_driver); +} +subsys_initcall (qcom_cpufreq_fw_init); + +MODULE_DESCRIPTION ("QCOM CPU Frequency FW" ); +MODULE_LICENSE ("GPL v2" ); --Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member of the Code Aurora Forum, hosted by the Linux Foundation.
Signature · 1 line
viresh
Re: [PATCH v2 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
Quoted 105 lines · Viresh Kumar
Quoted 19 lines · Taniya Das
The CPUfreq FW present in some QCOM chipsets offloads the steps necessary for changing the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware.
Signed-off-by Saravana Kannan <[email protected]>Signed-off-by Taniya Das <[email protected]>--- drivers/cpufreq/Kconfig.arm | 9 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/qcom-cpufreq-fw.c | 317 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 drivers/cpufreq/qcom-cpufreq-fw.cdrivers/cpufreq/Kconfig.arm +0
-0drivers/cpufreq/Kconfig.arm config ARM_PXA2xx_CPUFREQ · line 306
Quoted 6 lines
drivers/cpufreq/Kconfig.arm +3
-0@@ -301,3 +301,12 @@ config ARM_PXA2xx_CPUFREQ This add the CPUFreq driver support for Intel PXA2xx SOCs. If in doubt, say N. + +config ARM_QCOM_CPUFREQ_FW + bool "QCOM CPUFreq FW driver"During last review I didn't say that this driver shouldn't be a module, but that you need to fix things to make it a module. I am fine though if you don't want this to be a module ever.
Quoted 58 lines
drivers/cpufreq/Kconfig.arm +6
-0+ help + Support for the CPUFreq FW driver. + The CPUfreq FW preset in some QCOM chipsets offloads the steps + necessary for changing the frequency of CPUs. The driver + implements the cpufreq driver interface for this firmware. + Say Y if you want to support CPUFreq FW.drivers/cpufreq/Makefile +0
-0@@ -85,6 +85,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) +=tegra124-cpufreq.o
obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o
obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
+obj-$(CONFIG_ARM_QCOM_CPUFREQ_FW) += qcom-cpufreq-fw.o##################################################################################
drivers/cpufreq/qcom-cpufreq-fw.c new file +31
-0@@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2018, The Linux Foundation. All rights reserved. +*/ + +#include <linux/cpufreq.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#define INIT_RATE 300000000UL +#define XO_RATE 19200000UL +#define LUT_MAX_ENTRIES 40U +#define CORE_COUNT_VAL (val) ((val & GENMASK(18, 16)) >> 16) +#define LUT_ROW_SIZE 32 + +struct cpufreq_qcom { +struct cpufreq_frequency_table *table; +struct device *dev; +void __iomem *perf_base; +void __iomem *lut_base; +cpumask_t related_cpus; +unsigned int max_cores; +}; + +static struct cpufreq_qcom *qcom_freq_domain_map[NR_CPUS ]; + +static int +qcom_cpufreq_fw_target_index(struct cpufreq_policy *policy, unsignedint index)
drivers/cpufreq/qcom-cpufreq-fw.c line 40
Quoted 9 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +9
-0+{ +struct cpufreq_qcom *c = policy->driver_data; + +if (index >=LUT_MAX_ENTRIES ) { +dev_err (c->dev, +"Passing an index (%u) that's greater than max (%d)\n" , + index,LUT_MAX_ENTRIES -1 ); +return -EINVAL ; + }This is never going to happen unless there is a bug in cpufreq core. You are allocating only 40 entries for the cpufreq table and this will always be 0-39. None of the other drivers is checking this I believe and neither should you. This is the only routine which will get call very frequently and we better not add unnecessary stuff here.
Quoted 9 lines
drivers/cpufreq/qcom-cpufreq-fw.c line 45
Quoted 4 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +4
-0+writel_relaxed (index, c->perf_base); + +/* Make sure the write goes through before proceeding */ +mb ();Btw what happens right after this is done ? Are we guaranteed that the frequency is updated in the hardware after this ? What about enabling fast-switch for your platform ? Look at
drivers/cpufreq/scmi-cpufreq.cto see how that is done.
Yeah, I don't think this is needed really.
Quoted 15 lines
drivers/cpufreq/qcom-cpufreq-fw.c line 56
Quoted 12 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +11
-0+return 0 ; +} + +static unsigned int qcom_cpufreq_fw_get (unsigned int cpu) +{ +struct cpufreq_qcom *c; +unsigned int index; + + c = qcom_freq_domain_map[cpu]; +if (!c) +return -ENODEV ;Return 0 on error here.
Quoted 5 lines
drivers/cpufreq/qcom-cpufreq-fw.c line 59
Quoted 3 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +3
-0+ + index =readl_relaxed (c->perf_base); + index =min (index,LUT_MAX_ENTRIES -1 );Will the hardware ever read a value over 39 here ?
The register could be initialized to whatever before the kernel is brought up. Don't want to depend on it being correct to avoid out of bounds access that could leak data.
Quoted 76 lines
Quoted 52 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +52
-0+ +return c->table[index].frequency; +} + +static int qcom_cpufreq_fw_cpu_init (struct cpufreq_policy *policy) +{ +struct cpufreq_qcom *c; + + c = qcom_freq_domain_map[policy->cpu]; +if (!c) { +pr_err ("No scaling support for CPU%d\n" , policy->cpu); +return -ENODEV ; + } + +cpumask_copy (policy->cpus, &c->related_cpus); + policy->freq_table = c->table; + policy->driver_data = c; + +return 0 ; +} + +static struct freq_attr *qcom_cpufreq_fw_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + &cpufreq_freq_attr_scaling_boost_freqs, +NULL +}; + +static struct cpufreq_driver cpufreq_qcom_fw_driver = { + .flags =CPUFREQ_STICKY |CPUFREQ_NEED_INITIAL_FREQ_CHECK | +CPUFREQ_HAVE_GOVERNOR_PER_POLICY , + .verify = cpufreq_generic_frequency_table_verify, + .target_index = qcom_cpufreq_fw_target_index, + .get = qcom_cpufreq_fw_get, + .init = qcom_cpufreq_fw_cpu_init, + .name ="qcom-cpufreq-fw" , + .attr = qcom_cpufreq_fw_attr, + .boost_enabled = true, +}; + +static int qcom_read_lut (struct platform_device *pdev, +struct cpufreq_qcom *c) +{ +struct device *dev = &pdev->dev; +u32 data, src, lval, i, core_count, prev_cc; + + c->table =devm_kcalloc (dev,LUT_MAX_ENTRIES +1 , +sizeof (*c->table),GFP_KERNEL ); +if (!c->table) +return -ENOMEM ; + +for (i =0 ; i <LUT_MAX_ENTRIES ; i++) { + data =readl_relaxed (c->lut_base + i *LUT_ROW_SIZE );drivers/cpufreq/qcom-cpufreq-fw.c line 121
Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+ src = ((data &GENMASK (31 ,30 )) >>30 ); + lval = (data &GENMASK (7 ,0 )); + core_count =CORE_COUNT_VAL (data); + +if (!src) + c->table[i].frequency =INIT_RATE /1000 ; +else + c->table[i].frequency =XO_RATE * lval /1000 ; + + c->table[i].driver_data = c->table[i].frequency;Why do you need to use
driver_datahere? Why can't you simple use frequency field in the below conditional expressions ?Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+ +dev_dbg (dev,"index=%d freq=%d, core_count %d\n" , + i, c->table[i].frequency, core_count); + +if (core_count != c->max_cores) + c->table[i].frequency =CPUFREQ_ENTRY_INVALID ; + +/* +* Two of the same frequencies with the same core counts means +* end of table.
Quoted 8 lines
drivers/cpufreq/qcom-cpufreq-fw.c line 137
Quoted 6 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +6
-0+*/ +if (i >0 && c->table[i -1 ].driver_data == + c->table[i].driver_data && prev_cc == core_count) { +struct cpufreq_frequency_table *prev = &c->table[i -1 ]; + +if (prev->frequency ==CPUFREQ_ENTRY_INVALID ) {There can only be a single boost frequency at max ?
As of now, yes. If that changes, we'll change this code later.
Quoted 8 lines
drivers/cpufreq/qcom-cpufreq-fw.c line 139
Quoted 2 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +2
-0+ prev->flags =CPUFREQ_BOOST_FREQ ; + prev->frequency = prev->driver_data;Okay you are using
driver_dataas a local variable to keep this value safe which you might have overwritten. Maybe use a simple variableprev_freqfor this. It would be much more readable in that case and you wouldn't end up abusing thedriver_datafield.
Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c line 146
Quoted 7 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +7
-0+ } + +break ; + } + prev_cc = core_count; + } + c->table[i].frequency =CPUFREQ_TABLE_END ;Wouldn't you end up writing on c->table[40].frequency here if there are 40 frequency value present ?
Yeah, the loop condition needs to be fixed.
-Saravana
Re: [PATCH v2 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
Hello Viresh,
Thanks for your comments.
Quoted 99 lines · On 5/22/2018 12:36 AM, [email protected] wrote:
Quoted 98 lines · Viresh Kumar
Quoted 22 lines · Taniya Das
The CPUfreq FW present in some QCOM chipsets offloads the steps necessary for changing the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware.
Signed-off-by Saravana Kannan <[email protected]>Signed-off-by Taniya Das <[email protected]>---
drivers/cpufreq/Kconfig.arm | 9 ++
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/qcom-cpufreq-fw.c | 317
++++++++++++++++++++++++++++++++++++++3 files changed, 327 insertions(+)create mode 100644
drivers/cpufreq/qcom-cpufreq-fw.cdrivers/cpufreq/Kconfig.arm +0
-0@@ -301,3 +301,12 @@ config ARM_PXA2xx_CPUFREQThis add the CPUFreq driver support for Intel PXA2xx SOCs.
If in doubt, say N.
drivers/cpufreq/Kconfig.arm config ARM_PXA2xx_CPUFREQ · line 306
Quoted 3 lines
drivers/cpufreq/Kconfig.arm +3
-0+ +config ARM_QCOM_CPUFREQ_FW + bool "QCOM CPUFreq FW driver"During last review I didn't say that this driver shouldn't be a module, but that you need to fix things to make it a module. I am fine though if you don't want this to be a module ever.
Quoted 67 lines
+ help
+ Support for the CPUFreq FW driver.
+ The CPUfreq FW preset in some QCOM chipsets offloads the steps + necessary for changing the frequency of CPUs. The driver + implements the cpufreq driver interface for this firmware. + Say Y if you want to support CPUFreq FW.drivers/cpufreq/Makefile +0
-0@@ -85,6 +85,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) +=tegra124-cpufreq.o
obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o +obj-$(CONFIG_ARM_QCOM_CPUFREQ_FW) += qcom-cpufreq-fw.o##################################################################################
drivers/cpufreq/qcom-cpufreq-fw.c new file +31
-0@@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2018, The Linux Foundation. All rights reserved. +*/ + +#include <linux/cpufreq.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#define INIT_RATE 300000000UL +#define XO_RATE 19200000UL +#define LUT_MAX_ENTRIES 40U +#define CORE_COUNT_VAL (val) ((val & GENMASK(18, 16)) >> 16) +#define LUT_ROW_SIZE 32 + +struct cpufreq_qcom { + struct cpufreq_frequency_table *table; + struct device *dev; + void __iomem *perf_base; + void __iomem *lut_base; + cpumask_t related_cpus; + unsigned int max_cores; +}; + +static struct cpufreq_qcom *qcom_freq_domain_map[NR_CPUS ]; + +static int +qcom_cpufreq_fw_target_index(struct cpufreq_policy *policy, unsignedint index)
drivers/cpufreq/qcom-cpufreq-fw.c new file +8
-0+{ + struct cpufreq_qcom *c = policy->driver_data; + + if (index >= LUT_MAX_ENTRIES) { + dev_err(c->dev, + "Passing an index (%u) that's greater than max (%d)\n", + index, LUT_MAX_ENTRIES - 1); + return -EINVAL;
Quoted 8 lines
Quoted 8 lines
Quoted 1 line
+ }
This is never going to happen unless there is a bug in cpufreq core. You are allocating only 40 entries for the cpufreq table and this will always be 0-39. None of the other drivers is checking this I believe and neither should you. This is the only routine which will get call very frequently and we better not add unnecessary stuff here.
Yes, I would remove this in the next series.
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
+
writel_relaxed(index, c->perf_base);
+
+ /* Make sure the write goes through before proceeding */ +mb();
Quoted 7 lines
Quoted 4 lines
Btw what happens right after this is done ? Are we guaranteed that the frequency is updated in the hardware after this ? What about enabling fast-switch for your platform ? Look at
drivers/cpufreq/scmi-cpufreq.cto see how that is done.Yeah, I don't think this is needed really.
Just want to make sure it doesn't really sit in the write buffer before return.
Quoted 15 lines
Quoted 15 lines
drivers/cpufreq/qcom-cpufreq-fw.c line 56
Quoted 12 lines
+ return 0;
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+} + +static unsigned int qcom_cpufreq_fw_get (unsigned int cpu) +{ + struct cpufreq_qcom *c; + unsigned int index; + + c = qcom_freq_domain_map[cpu]; + if (!c) + return -ENODEV;Return 0 on error here.
Would update this in the next patch.
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
+
+ index =readl_relaxed(c->perf_base);
+ index =min(index, LUT_MAX_ENTRIES - 1);
Quoted 71 lines
Quoted 1 line
Will the hardware ever read a value over 39 here ?
The register could be initialized to whatever before the kernel is brought up. Don't want to depend on it being correct to avoid out of bounds access that could leak data.
Quoted 66 lines
Quoted 52 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +52
-0+ + return c->table[index].frequency; +} + +static int qcom_cpufreq_fw_cpu_init (struct cpufreq_policy *policy) +{ + struct cpufreq_qcom *c; + + c = qcom_freq_domain_map[policy->cpu]; + if (!c) { + pr_err("No scaling support for CPU%d\n", policy->cpu); + return -ENODEV; + } + + cpumask_copy(policy->cpus, &c->related_cpus); + policy->freq_table = c->table; + policy->driver_data = c; + + return 0; +} + +static struct freq_attr *qcom_cpufreq_fw_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + &cpufreq_freq_attr_scaling_boost_freqs, + NULL +}; + +static struct cpufreq_driver cpufreq_qcom_fw_driver = { + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + CPUFREQ_HAVE_GOVERNOR_PER_POLICY, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = qcom_cpufreq_fw_target_index, + .get = qcom_cpufreq_fw_get, + .init = qcom_cpufreq_fw_cpu_init, + .name = "qcom-cpufreq-fw", + .attr = qcom_cpufreq_fw_attr, + .boost_enabled = true, +}; + +static int qcom_read_lut (struct platform_device *pdev, + struct cpufreq_qcom *c) +{ + struct device *dev = &pdev->dev; + u32 data, src, lval, i, core_count, prev_cc; + + c->table = devm_kcalloc(dev, LUT_MAX_ENTRIES + 1, + sizeof(*c->table), GFP_KERNEL); + if (!c->table) + return -ENOMEM; + + for (i = 0; i < LUT_MAX_ENTRIES; i++) { + data = readl_relaxed(c->lut_base + i * LUT_ROW_SIZE);drivers/cpufreq/qcom-cpufreq-fw.c line 121
Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+ src = ((data & GENMASK(31, 30)) >> 30); + lval = (data & GENMASK(7, 0)); + core_count = CORE_COUNT_VAL(data); + + if (!src) + c->table[i].frequency = INIT_RATE / 1000; + else + c->table[i].frequency = XO_RATE * lval / 1000; + + c->table[i].driver_data = c->table[i].frequency;Why do you need to use
driver_datahere? Why can't you simple use frequency field in the below conditional expressions ?
The frequency field would be marked INVALID in case the core count does not match and the frequency data would be lost.
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+ + dev_dbg(dev, "index=%d freq=%d, core_count %d\n", + i, c->table[i].frequency, core_count); + + if (core_count != c->max_cores) + c->table[i].frequency = CPUFREQ_ENTRY_INVALID; + + /* +* Two of the same frequencies with the same core counts means +* end of table. Quoted 8 lines
Quoted 6 lines
+ */
+ if (i > 0 && c->table[i - 1].driver_data ==
+ c->table[i].driver_data &&prev_cc==core_count) { + structcpufreq_frequency_table*prev = &c->table[i - 1]; +
+ if (prev->frequency==CPUFREQ_ENTRY_INVALID) {There can only be a single boost frequency at max ?
Quoted 10 lines
As of now, yes. If that changes, we'll change this code later.
Quoted 8 lines
Quoted 2 lines
+
prev->flags=CPUFREQ_BOOST_FREQ; +prev->frequency=prev->driver_data;Okay you are using
driver_dataas a local variable to keep this value safe which you might have overwritten. Maybe use a simple variableprev_freqfor this. It would be much more readable in that case and you wouldn't end up abusing thedriver_datafield.
Please correct me, currently the driver_data is not used by cpufreq core and that was the reason to use it. In case you still think it is not a good way to handle it, I would try to handle it differently.
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
+ }
+
+ break;
+ }
+prev_cc=core_count;
+ }
+ c->table[i].frequency =CPUFREQ_TABLE_END;
Quoted 5 lines
Quoted 2 lines
Wouldn't you end up writing on c->table[40].frequency here if there are 40 frequency value present ?
Yeah, the loop condition needs to be fixed.
The table allocation is done for 'LUT_MAX_ENTRIES + 1'.
Yes in case we have all [0-39] (i.e 40 entries) read from the hardware, would store the same and mark the 40th index as table end. Please correct if I missed something in your comment.
Quoted 1 line
-Saravana
Signature · 4 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. --
Viresh Kumar
maintainerRe: [PATCH v2 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
Quoted 12 lines · Taniya Das
Quoted 11 lines · On 5/22/2018 12:36 AM, [email protected] wrote:
Quoted 9 lines · Viresh Kumar
Quoted 2 lines · Taniya Das
+ /* Make sure the write goes through before proceeding */ +
mb();Btw what happens right after this is done ? Are we guaranteed that the frequency is updated in the hardware after this ? What about enabling fast-switch for your platform ? Look at
drivers/cpufreq/scmi-cpufreq.cto see how that is done.
Quoted 5 lines
Quoted 2 lines
Yeah, I don't think this is needed really.
Just want to make sure it doesn't really sit in the write buffer before return.
As per Saravana you will be dropping it now, right ?
Quoted 8 lines
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
+ index =
readl_relaxed(c->perf_base);
+ index =min(index, LUT_MAX_ENTRIES - 1);Will the hardware ever read a value over 39 here ?
The register could be initialized to whatever before the kernel is brought up. Don't want to depend on it being correct to avoid out of bounds access that could leak data.
Fair enough.
Quoted 61 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +19
-0+static int qcom_read_lut (struct platform_device *pdev, + struct cpufreq_qcom *c) +{ + struct device *dev = &pdev->dev; + u32 data, src, lval, i, core_count, prev_cc; + + c->table = devm_kcalloc(dev, LUT_MAX_ENTRIES + 1, + sizeof(*c->table), GFP_KERNEL); + if (!c->table) + return -ENOMEM; + + for (i = 0; i < LUT_MAX_ENTRIES; i++) { + data = readl_relaxed(c->lut_base + i * LUT_ROW_SIZE); + src = ((data & GENMASK(31, 30)) >> 30); + lval = (data & GENMASK(7, 0)); + core_count = CORE_COUNT_VAL(data); + + if (!src) + c->table[i].frequency = INIT_RATE / 1000;Quoted 8 lines
Quoted 8 lines
Quoted 4 lines
+ else
+ c->table[i].frequency =XO_RATE* lval / 1000; +
+ c->table[i].driver_data = c->table[i].frequency;Why do you need to use
driver_datahere? Why can't you simple use frequency field in the below conditional expressions ?The frequency field would be marked INVALID in case the core count does not match and the frequency data would be lost.
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
drivers/cpufreq/qcom-cpufreq-fw.c new file +10
-0+ + dev_dbg(dev, "index=%d freq=%d, core_count %d\n", + i, c->table[i].frequency, core_count); + + if (core_count != c->max_cores) + c->table[i].frequency = CPUFREQ_ENTRY_INVALID; + + /* +* Two of the same frequencies with the same core counts means +* end of table. Quoted 8 lines
Quoted 6 lines
+ */
+ if (i > 0 && c->table[i - 1].driver_data ==
+ c->table[i].driver_data &&prev_cc==core_count) { + structcpufreq_frequency_table*prev = &c->table[i - 1]; +
+ if (prev->frequency==CPUFREQ_ENTRY_INVALID) {There can only be a single boost frequency at max ?
Quoted 10 lines
As of now, yes. If that changes, we'll change this code later.
Quoted 8 lines
Quoted 2 lines
+
prev->flags=CPUFREQ_BOOST_FREQ; +prev->frequency=prev->driver_data;Okay you are using
driver_dataas a local variable to keep this value safe which you might have overwritten. Maybe use a simple variableprev_freqfor this. It would be much more readable in that case and you wouldn't end up abusing thedriver_datafield.
Quoted 3 lines
Please correct me, currently the
driver_datais not used by cpufreq core and that was the reason to use it. In case you still think it is not a good way to handle it, I would try to handle it differently.
Yeah, the driver data will not be used by cpufreq core, but I think the code would be far more readable if you use a local variable like prev_freq instead.
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
Quoted 7 lines
+ }
+
+ break;
+ }
+prev_cc=core_count;
+ }
+ c->table[i].frequency =CPUFREQ_TABLE_END;Wouldn't you end up writing on c->table[40].frequency here if there are 40 frequency value present ?
Quoted 7 lines
Quoted 2 lines
Yeah, the loop condition needs to be fixed.
The table allocation is done for '
LUT_MAX_ENTRIES+ 1'.
Yes in case we have all [0-39] (i.e 40 entries) read from the hardware, would store the same and mark the 40th index as table end. Please correct if I missed something in your comment.
Ahh, you are right. I misread it.
Signature · 1 line
viresh
Viresh Kumar
maintainerQuoted 2 lines · Taniya Das
[v2]
* Address comments given in v0 series.
That's not how you do it. You need to explain every change in enough detail here so that the reviewers don't need to go to their previous emails to see what changed.
Quoted 22 lines
[v1]
* Fixed compilation reported by Amit K.The CPUfreq FW present in some QCOM chipsets offloads the steps necessary for changing the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware.
Taniya Das (2):
dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver.../bindings/cpufreq/cpufreq-qcom-fw.txt | 68 +++++ drivers/cpufreq/Kconfig.arm | 9 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/qcom-cpufreq-fw.c | 317 +++++++++++++++++++++ 4 files changed, 395 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txt create mode 100644 drivers/cpufreq/qcom-cpufreq-fw.c--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member of the Code Aurora Forum, hosted by the Linux Foundation.
Signature · 1 line
viresh
Hello Viresh,
Sure, will do it next time. Hope updating the fixes in this email is fine. Do let me know in case you need me to send it across again the series with cover letter updated.
Quoted 8 lines · Viresh Kumar
Quoted 2 lines · Taniya Das
[v2]
* Address comments given in v0 series.That's not how you do it. You need to explain every change in enough detail here so that the reviewers don't need to go to their previous emails to see what changed.
Fixes in [v2]
* Fixed the alignment issues in "qcom_cpufreq_fw_target_index" for
dev_err and also for "qcom_cpu_resources_init".
* Removed ret = 0 from qcom_get_related_cpus and added to check forcpu_mask_empty to return -ENOENT.
* Fixes qcom_cpu_resources_init function
* Remove initialization of 'index'
* Check for valid 'c'
* Removed initialization of 'prev_cc' from 'qcom_read_lut'.
* Remove initialization of 'ret' from function qcom_resources_init and
add return -ENODEV based on 'of_get_available_child_count'.
* Removed initialization of 'rc' from qcom_cpufreq_fw_driver_probe
* Removed module_exit as this driver would not be used as module, also
updated the Kconfig to bool from tristate.
* Updated the subsystem in device tree bindings.
Quoted 23 lines
Quoted 22 lines
[v1]
* Fixed compilation reported by Amit K.The CPUfreq FW present in some QCOM chipsets offloads the steps necessary for changing the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware.
Taniya Das (2):
dt-bindings: cpufreq: Introduce QCOM CPUFREQ FW bindings
cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver.../bindings/cpufreq/cpufreq-qcom-fw.txt | 68 +++++ drivers/cpufreq/Kconfig.arm | 9 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/qcom-cpufreq-fw.c | 317 +++++++++++++++++++++ 4 files changed, 395 insertions(+)create mode 100644
Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-fw.txtcreate mode 100644drivers/cpufreq/qcom-cpufreq-fw.c--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member of the Code Aurora Forum, hosted by the Linux Foundation.
Signature · 4 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. --
Viresh Kumar
maintainerQuoted 15 lines · Taniya Das
Hello Viresh,
Sure, will do it next time. Hope updating the fixes in this email is fine. Do let me know in case you need me to send it across again the series with cover letter updated.
Quoted 8 lines · Viresh Kumar
Quoted 2 lines · Taniya Das
[v2]
* Address comments given in v0 series.That's not how you do it. You need to explain every change in enough detail here so that the reviewers don't need to go to their previous emails to see what changed.
Quoted 15 lines
Fixes in [v2]
* Fixed the alignment issues in "qcom_cpufreq_fw_target_index" fordev_errand also for "qcom_cpu_resources_init".
* Removed ret = 0 fromqcom_get_related_cpusand added to check forcpu_mask_emptyto return-ENOENT.
* Fixesqcom_cpu_resources_initfunction
* Remove initialization of 'index'
* Check for valid 'c'
* Removed initialization of 'prev_cc' from 'qcom_read_lut'.
* Remove initialization of 'ret' from functionqcom_resources_initand add
return-ENODEVbased on 'of_get_available_child_count'.
* Removed initialization of 'rc' fromqcom_cpufreq_fw_driver_probe
* Removedmodule_exitas this driver would not be used as module, also
updated theKconfigto bool from tristate.
* Updated the subsystem in device tree bindings.
That's what it should look like. Thanks.
Signature · 1 line
viresh