Hi,
This series adds OPP (Operating Points) support to UFSHCD driver.
Motivation behind adding OPP support is to scale both clocks as well as
regulators/performance state dynamically. Currently, UFSHCD just scales
clock frequency during runtime with the help of "freq-table-hz" property
defined in devicetree. With the addition of OPP tables in devicetree (as
done for Qcom SDM845 and SM8250 SoCs in this series) UFSHCD can now scale
both clocks and performance state of power domain which helps in power
saving.
For the addition of OPP support to UFSHCD, there are changes required to
the OPP framework and devfreq drivers. The OPP framework changes are already
merged and the devfreq change is added in this series.
Credits
=======
This series is a continuation of previous work by Krzysztof Kozlowski [1].
Testing
=======
This series is tested on 96Boards RB3 (SDM845 SoC) and RB5 (SM8250 SoC)
development boards.
Merging Strategy
================
Since the devfreq patch got an Ack from the maintainer, either it can be merged
to scsi tree with rest of the patches or merged separately through devfreq tree.
Thanks,
Mani
[1] https://lore.kernel.org/all/[email protected]/
Changes in v6:
* Collected tags from Dmitry
* Fixed bindings issues reported by Krzysztof
Changes in v5:
* Dropped the devfreq patch since it got applied
* Fixed the bindings issue reported by DT bot
* Rebased on top of mkp/scsi/for-next
Changes in v4:
* Rebased on top of v6.6-rc3
Changes in v3:
* Rebased on top of linux-next/master tag: next-20230731
* Dropped the already applied patches (dts, opp binding and framework)
* Moved the interconnect patches to a separate series:
https://lore.kernel.org/linux-scsi/[email protected]/
* Moved ufshcd_opp_config_clks() API to ufshcd.c to fix the build failure
reported by Kbuild bot: https://lore.kernel.org/all/[email protected]/
* Collected Acks
* v2: https://lore.kernel.org/all/[email protected]/
Changes in v2:
* Added more description to the bindings patch 2/15
* Fixed dev_pm_opp_put() usage in patch 10/15
* Added a new patch for adding enums for UFS lanes 14/15
* Changed the icc variables to mem_bw and cfg_bw and used
the enums for gears and lanes in bw_table
* Collected review tags
* Added SCSI list and folks
* Removed duplicate patches
Krzysztof Kozlowski (2):
dt-bindings: ufs: common: add OPP table
arm64: dts: qcom: sdm845: Add OPP table support to UFSHC
Manivannan Sadhasivam (3):
scsi: ufs: core: Add OPP support for scaling clocks and regulators
scsi: ufs: host: Add support for parsing OPP
arm64: dts: qcom: sm8250: Add OPP table support to UFSHC
.../devicetree/bindings/ufs/ufs-common.yaml | 35 +++-
arch/arm64/boot/dts/qcom/sdm845.dtsi | 42 +++-
arch/arm64/boot/dts/qcom/sm8250.dtsi | 39 +++-
drivers/ufs/core/ufshcd.c | 179 ++++++++++++++----
drivers/ufs/host/ufshcd-pltfrm.c | 78 ++++++++
include/ufs/ufshcd.h | 7 +
6 files changed, 325 insertions(+), 55 deletions(-)
--
2.25.1
From: Krzysztof Kozlowski <[email protected]>
UFS host controller, when scaling gears, should choose appropriate
performance state of RPMh power domain controller along with clock
frequency. So let's add the OPP table support to specify both clock
frequency and RPMh performance states replacing the old "freq-table-hz"
property.
Reviewed-by: Dmitry Baryshkov <[email protected]>
Signed-off-by: Krzysztof Kozlowski <[email protected]>
[mani: Splitted pd change and used rpmhpd_opp_low_svs]
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
arch/arm64/boot/dts/qcom/sdm845.dtsi | 42 +++++++++++++++++++++-------
1 file changed, 32 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 055ca80c0075..2ea6eb44953e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2605,22 +2605,44 @@ ufs_mem_hc: ufshc@1d84000 {
<&gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>,
<&gcc GCC_UFS_PHY_RX_SYMBOL_1_CLK>,
<&gcc GCC_UFS_PHY_ICE_CORE_CLK>;
- freq-table-hz =
- <50000000 200000000>,
- <0 0>,
- <0 0>,
- <37500000 150000000>,
- <0 0>,
- <0 0>,
- <0 0>,
- <0 0>,
- <75000000 300000000>;
+
+ operating-points-v2 = <&ufs_opp_table>;
interconnects = <&aggre1_noc MASTER_UFS_MEM 0 &mem_noc SLAVE_EBI1 0>,
<&gladiator_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_UFS_MEM_CFG 0>;
interconnect-names = "ufs-ddr", "cpu-ufs";
status = "disabled";
+
+ ufs_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-50000000 {
+ opp-hz = /bits/ 64 <50000000>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <37500000>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <75000000>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+
+ opp-200000000 {
+ opp-hz = /bits/ 64 <200000000>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <150000000>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <300000000>;
+ required-opps = <&rpmhpd_opp_nom>;
+ };
+ };
};
ufs_mem_phy: phy@1d87000 {
--
2.25.1
OPP framework can be used to scale the clocks along with other entities
such as regulators, performance state etc... So let's add support for
parsing OPP from devicetree. OPP support in devicetree is added through
the "operating-points-v2" property which accepts the OPP table defining
clock frequency, regulator voltage, power domain performance state etc...
Since the UFS controller requires multiple clocks to be controlled for
proper working, devm_pm_opp_set_config() has been used which supports
scaling multiple clocks through custom ufshcd_opp_config_clks() callback.
It should be noted that the OPP support is not compatible with the old
"freq-table-hz" property. So only one can be used at a time even though
the UFS core supports both.
Co-developed-by: Krzysztof Kozlowski <[email protected]>
Signed-off-by: Krzysztof Kozlowski <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/ufs/core/ufshcd.c | 35 ++++++++++++++
drivers/ufs/host/ufshcd-pltfrm.c | 78 ++++++++++++++++++++++++++++++++
include/ufs/ufshcd.h | 3 ++
3 files changed, 116 insertions(+)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 3f52e05002e4..8cbd08f81c56 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -1064,6 +1064,41 @@ static int ufshcd_set_clk_freq(struct ufs_hba *hba, bool scale_up)
return ret;
}
+int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
+ struct dev_pm_opp *opp, void *data,
+ bool scaling_down)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ struct list_head *head = &hba->clk_list_head;
+ struct ufs_clk_info *clki;
+ unsigned long freq;
+ u8 idx = 0;
+ int ret;
+
+ list_for_each_entry(clki, head, list) {
+ if (!IS_ERR_OR_NULL(clki->clk)) {
+ freq = dev_pm_opp_get_freq_indexed(opp, idx++);
+
+ /* Do not set rate for clocks having frequency as 0 */
+ if (!freq)
+ continue;
+
+ ret = clk_set_rate(clki->clk, freq);
+ if (ret) {
+ dev_err(dev, "%s: %s clk set rate(%ldHz) failed, %d\n",
+ __func__, clki->name, freq, ret);
+ return ret;
+ }
+
+ trace_ufshcd_clk_scaling(dev_name(dev),
+ (scaling_down ? "scaled down" : "scaled up"),
+ clki->name, hba->clk_scaling.target_freq, freq);
+ }
+ }
+
+ return 0;
+}
+
static int ufshcd_opp_set_rate(struct ufs_hba *hba, unsigned long freq)
{
struct dev_pm_opp *opp;
diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
index 61cf8b957da4..da2558e274b4 100644
--- a/drivers/ufs/host/ufshcd-pltfrm.c
+++ b/drivers/ufs/host/ufshcd-pltfrm.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
@@ -212,6 +213,77 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
}
}
+static int ufshcd_parse_operating_points(struct ufs_hba *hba)
+{
+ struct device *dev = hba->dev;
+ struct device_node *np = dev->of_node;
+ struct dev_pm_opp_config config = {};
+ struct ufs_clk_info *clki;
+ const char **clk_names;
+ int cnt, i, ret;
+
+ if (!of_find_property(np, "operating-points-v2", NULL))
+ return 0;
+
+ if (of_find_property(np, "freq-table-hz", NULL)) {
+ dev_err(dev, "%s: operating-points and freq-table-hz are incompatible\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ cnt = of_property_count_strings(np, "clock-names");
+ if (cnt <= 0) {
+ dev_err(dev, "%s: Missing clock-names\n", __func__);
+ return -ENODEV;
+ }
+
+ /* OPP expects clk_names to be NULL terminated */
+ clk_names = devm_kcalloc(dev, cnt + 1, sizeof(*clk_names), GFP_KERNEL);
+ if (!clk_names)
+ return -ENOMEM;
+
+ /*
+ * We still need to get reference to all clocks as the UFS core uses
+ * them separately.
+ */
+ for (i = 0; i < cnt; i++) {
+ ret = of_property_read_string_index(np, "clock-names", i,
+ &clk_names[i]);
+ if (ret)
+ return ret;
+
+ clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
+ if (!clki)
+ return -ENOMEM;
+
+ clki->name = devm_kstrdup(dev, clk_names[i], GFP_KERNEL);
+ if (!clki->name)
+ return -ENOMEM;
+
+ if (!strcmp(clk_names[i], "ref_clk"))
+ clki->keep_link_active = true;
+
+ list_add_tail(&clki->list, &hba->clk_list_head);
+ }
+
+ config.clk_names = clk_names,
+ config.config_clks = ufshcd_opp_config_clks;
+
+ ret = devm_pm_opp_set_config(dev, &config);
+ if (ret)
+ return ret;
+
+ ret = devm_pm_opp_of_add_table(dev);
+ if (ret) {
+ dev_err(dev, "Failed to add OPP table: %d\n", ret);
+ return ret;
+ }
+
+ hba->use_pm_opp = true;
+
+ return 0;
+}
+
/**
* ufshcd_get_pwr_dev_param - get finally agreed attributes for
* power mode change
@@ -378,6 +450,12 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
ufshcd_init_lanes_per_dir(hba);
+ err = ufshcd_parse_operating_points(hba);
+ if (err) {
+ dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
+ goto dealloc_host;
+ }
+
err = ufshcd_init(hba, mmio_base, irq);
if (err) {
dev_err_probe(dev, err, "Initialization failed with error %d\n",
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index fc0d6d37319a..7f0b2c5599cd 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -1254,6 +1254,9 @@ void ufshcd_mcq_make_queues_operational(struct ufs_hba *hba);
void ufshcd_mcq_enable_esi(struct ufs_hba *hba);
void ufshcd_mcq_config_esi(struct ufs_hba *hba, struct msi_msg *msg);
+int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
+ struct dev_pm_opp *opp, void *data,
+ bool scaling_down);
/**
* ufshcd_set_variant - set variant specific data to the hba
* @hba: per adapter instance
--
2.25.1
From: Krzysztof Kozlowski <[email protected]>
Except scaling UFS and bus clocks, it's necessary to scale also the
voltages of regulators or power domain performance state levels. Adding
Operating Performance Points table allows to adjust power domain
performance state, depending on the UFS clock speed.
OPPv2 deprecates previous property limited to clock scaling:
freq-table-hz.
Signed-off-by: Krzysztof Kozlowski <[email protected]>
Reviewed-by: Rob Herring <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
.../devicetree/bindings/ufs/ufs-common.yaml | 35 +++++++++++++++++--
1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/ufs/ufs-common.yaml b/Documentation/devicetree/bindings/ufs/ufs-common.yaml
index bbaee4f5f7b2..985ea8f64de8 100644
--- a/Documentation/devicetree/bindings/ufs/ufs-common.yaml
+++ b/Documentation/devicetree/bindings/ufs/ufs-common.yaml
@@ -20,11 +20,25 @@ properties:
items:
- description: Minimum frequency for given clock in Hz
- description: Maximum frequency for given clock in Hz
+ deprecated: true
description: |
+ Preferred is operating-points-v2.
+
Array of <min max> operating frequencies in Hz stored in the same order
- as the clocks property. If this property is not defined or a value in the
- array is "0" then it is assumed that the frequency is set by the parent
- clock or a fixed rate clock source.
+ as the clocks property. If either this property or operating-points-v2 is
+ not defined or a value in the array is "0" then it is assumed that the
+ frequency is set by the parent clock or a fixed rate clock source.
+
+ operating-points-v2:
+ description:
+ Preferred over freq-table-hz.
+ If present, each OPP must contain array of frequencies stored in the same
+ order for each clock. If clock frequency in the array is "0" then it is
+ assumed that the frequency is set by the parent clock or a fixed rate
+ clock source.
+
+ opp-table:
+ type: object
interrupts:
maxItems: 1
@@ -75,8 +89,23 @@ properties:
dependencies:
freq-table-hz: [ clocks ]
+ operating-points-v2: [ clocks, clock-names ]
required:
- interrupts
+allOf:
+ - if:
+ required:
+ - freq-table-hz
+ then:
+ properties:
+ operating-points-v2: false
+ - if:
+ required:
+ - operating-points-v2
+ then:
+ properties:
+ freq-table-hz: false
+
additionalProperties: true
--
2.25.1
UFS host controller, when scaling gears, should choose appropriate
performance state of RPMh power domain controller along with clock
frequency. So let's add the OPP table support to specify both clock
frequency and RPMh performance states replacing the old "freq-table-hz"
property.
Reviewed-by: Dmitry Baryshkov <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
arch/arm64/boot/dts/qcom/sm8250.dtsi | 39 +++++++++++++++++++++-------
1 file changed, 30 insertions(+), 9 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi
index a4e58ad731c3..33abd84aae53 100644
--- a/arch/arm64/boot/dts/qcom/sm8250.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi
@@ -2198,21 +2198,42 @@ ufs_mem_hc: ufshc@1d84000 {
<&gcc GCC_UFS_PHY_TX_SYMBOL_0_CLK>,
<&gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>,
<&gcc GCC_UFS_PHY_RX_SYMBOL_1_CLK>;
- freq-table-hz =
- <37500000 300000000>,
- <0 0>,
- <0 0>,
- <37500000 300000000>,
- <0 0>,
- <0 0>,
- <0 0>,
- <0 0>;
+
+ operating-points-v2 = <&ufs_opp_table>;
interconnects = <&aggre1_noc MASTER_UFS_MEM 0 &mc_virt SLAVE_EBI_CH0 0>,
<&gem_noc MASTER_AMPSS_M0 0 &config_noc SLAVE_UFS_MEM_CFG 0>;
interconnect-names = "ufs-ddr", "cpu-ufs";
status = "disabled";
+
+ ufs_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-37500000 {
+ opp-hz = /bits/ 64 <37500000>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <37500000>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+
+ opp-300000000 {
+ opp-hz = /bits/ 64 <300000000>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <300000000>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>,
+ /bits/ 64 <0>;
+ required-opps = <&rpmhpd_opp_nom>;
+ };
+ };
};
ufs_mem_phy: phy@1d87000 {
--
2.25.1