From 34059ed0f30f3b83aac0576c131e79d39bc143d8 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Thu, 14 Nov 2024 07:59:16 +0530 Subject: [PATCH 01/14] cpufreq: scmi: Register for limit change notifications Register for limit change notifications if supported and use the throttled frequency from the notification to apply HW pressure. Signed-off-by: Sibi Sankar Tested-by: Mike Tipton Reviewed-by: Cristian Marussi Reviewed-by: Lukasz Luba Signed-off-by: Viresh Kumar --- drivers/cpufreq/scmi-cpufreq.c | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index 07d6f9a9b7c8..b8fe758aeb01 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,8 @@ struct scmi_data { int nr_opp; struct device *cpu_dev; cpumask_var_t opp_shared_cpus; + struct notifier_block limit_notify_nb; + struct freq_qos_request limits_freq_req; }; static struct scmi_protocol_handle *ph; @@ -174,6 +177,22 @@ static struct freq_attr *scmi_cpufreq_hw_attr[] = { NULL, }; +static int scmi_limit_notify_cb(struct notifier_block *nb, unsigned long event, void *data) +{ + struct scmi_data *priv = container_of(nb, struct scmi_data, limit_notify_nb); + struct scmi_perf_limits_report *limit_notify = data; + unsigned int limit_freq_khz; + int ret; + + limit_freq_khz = limit_notify->range_max_freq / HZ_PER_KHZ; + + ret = freq_qos_update_request(&priv->limits_freq_req, limit_freq_khz); + if (ret < 0) + pr_warn("failed to update freq constraint: %d\n", ret); + + return NOTIFY_OK; +} + static int scmi_cpufreq_init(struct cpufreq_policy *policy) { int ret, nr_opp, domain; @@ -181,6 +200,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) struct device *cpu_dev; struct scmi_data *priv; struct cpufreq_frequency_table *freq_table; + struct scmi_device *sdev = cpufreq_get_driver_data(); cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -294,6 +314,23 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) } } + ret = freq_qos_add_request(&policy->constraints, &priv->limits_freq_req, FREQ_QOS_MAX, + FREQ_QOS_MAX_DEFAULT_VALUE); + if (ret < 0) { + dev_err(cpu_dev, "failed to add qos limits request: %d\n", ret); + goto out_free_table; + } + + priv->limit_notify_nb.notifier_call = scmi_limit_notify_cb; + ret = sdev->handle->notify_ops->event_notifier_register(sdev->handle, SCMI_PROTOCOL_PERF, + SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED, + &priv->domain_id, + &priv->limit_notify_nb); + if (ret) + dev_warn(&sdev->dev, + "failed to register for limits change notifier for domain %d\n", + priv->domain_id); + return 0; out_free_table: @@ -313,7 +350,13 @@ out_free_priv: static void scmi_cpufreq_exit(struct cpufreq_policy *policy) { struct scmi_data *priv = policy->driver_data; + struct scmi_device *sdev = cpufreq_get_driver_data(); + sdev->handle->notify_ops->event_notifier_unregister(sdev->handle, SCMI_PROTOCOL_PERF, + SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED, + &priv->domain_id, + &priv->limit_notify_nb); + freq_qos_remove_request(&priv->limits_freq_req); dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); dev_pm_opp_remove_all_dynamic(priv->cpu_dev); free_cpumask_var(priv->opp_shared_cpus); @@ -372,6 +415,8 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev) if (!handle) return -ENODEV; + scmi_cpufreq_driver.driver_data = sdev; + perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph); if (IS_ERR(perf_ops)) return PTR_ERR(perf_ops); From f1f010c9d9c62c865d9f54e94075800ba764b4d9 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Sun, 3 Nov 2024 22:02:51 +0100 Subject: [PATCH 02/14] cpufreq: fix using cpufreq-dt as module This driver can be built as a module since commit 3b062a086984 ("cpufreq: dt-platdev: Support building as module"), but unfortunately this caused a regression because the cputfreq-dt-platdev.ko module does not autoload. Usually, this is solved by just using the MODULE_DEVICE_TABLE() macro to export all the device IDs as module aliases. But this driver is special due how matches with devices and decides what platform supports. There are two of_device_id lists, an allow list that are for CPU devices that always match and a deny list that's for devices that must not match. The driver registers a cpufreq-dt platform device for all the CPU device nodes that either are in the allow list or contain an operating-points-v2 property and are not in the deny list. Enforce builtin compile of cpufreq-dt-platdev to make autoload work. Fixes: 3b062a086984 ("cpufreq: dt-platdev: Support building as module") Link: https://lore.kernel.org/all/20241104201424.2a42efdd@akair/ Link: https://lore.kernel.org/all/20241119111918.1732531-1-javierm@redhat.com/ Cc: stable@vger.kernel.org Signed-off-by: Andreas Kemnade Reported-by: Radu Rendec Reported-by: Javier Martinez Canillas [ Viresh: Picked commit log from Javier, updated tags ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig | 2 +- drivers/cpufreq/cpufreq-dt-platdev.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 92a83a9bb2e1..ea9afdc119fb 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -232,7 +232,7 @@ config CPUFREQ_VIRT If in doubt, say N. config CPUFREQ_DT_PLATDEV - tristate "Generic DT based cpufreq platdev driver" + bool "Generic DT based cpufreq platdev driver" depends on OF help This adds a generic DT based cpufreq platdev driver for frequency diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 2a3e8bd317c9..9c198bd4f7e9 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -235,5 +235,3 @@ create_pdev: sizeof(struct cpufreq_dt_platform_data))); } core_initcall(cpufreq_dt_platdev_init); -MODULE_DESCRIPTION("Generic DT based cpufreq platdev driver"); -MODULE_LICENSE("GPL"); From ab16dfb99cb565fb21ddf29320890c7bad35024c Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 6 Dec 2024 22:11:24 +0100 Subject: [PATCH 03/14] dt-bindings: cpufreq: Document support for Airoha EN7581 CPUFreq On newer Airoha SoC, CPU Frequency is scaled indirectly with SMC commands to ATF. A virtual clock is exposed. This virtual clock is a get-only clock and is used to expose the current global CPU clock. The frequency info comes by the output of the SMC command that reports the clock in MHz. The SMC sets the CPU clock by providing an index, this is modelled as performance states in a power domain. CPUs can't be individually scaled as the CPU frequency is shared across all CPUs and is global. Signed-off-by: Christian Marangi Reviewed-by: Ulf Hansson Reviewed-by: Rob Herring (Arm) Signed-off-by: Viresh Kumar --- .../cpufreq/airoha,en7581-cpufreq.yaml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/airoha,en7581-cpufreq.yaml diff --git a/Documentation/devicetree/bindings/cpufreq/airoha,en7581-cpufreq.yaml b/Documentation/devicetree/bindings/cpufreq/airoha,en7581-cpufreq.yaml new file mode 100644 index 000000000000..7d4510b3219c --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/airoha,en7581-cpufreq.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/cpufreq/airoha,en7581-cpufreq.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha EN7581 CPUFreq + +maintainers: + - Christian Marangi + +description: | + On newer Airoha SoC, CPU Frequency is scaled indirectly with SMC commands + to ATF. + + A virtual clock is exposed. This virtual clock is a get-only clock and + is used to expose the current global CPU clock. The frequency info comes + by the output of the SMC command that reports the clock in MHz. + + The SMC sets the CPU clock by providing an index, this is modelled as + performance states in a power domain. + + CPUs can't be individually scaled as the CPU frequency is shared across + all CPUs and is global. + +properties: + compatible: + const: airoha,en7581-cpufreq + + '#clock-cells': + const: 0 + + '#power-domain-cells': + const: 0 + + operating-points-v2: true + +required: + - compatible + - '#clock-cells' + - '#power-domain-cells' + - operating-points-v2 + +additionalProperties: false + +examples: + - | + performance-domain { + compatible = "airoha,en7581-cpufreq"; + + operating-points-v2 = <&cpu_smcc_opp_table>; + + #power-domain-cells = <0>; + #clock-cells = <0>; + }; From a248d90ddc3f73d193327275da2f16fb80c862cf Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Thu, 19 Dec 2024 02:25:03 +0800 Subject: [PATCH 04/14] dt-bindings: cpufreq: apple,cluster-cpufreq: Add A7-A11, T2 compatibles Add compatibles for Apple A7-A11, T2 SoCs. Apple A7, A8, A8X gets the per-SoC compatible and the A7 "apple,s5l8960x-cluster-cpufreq" compatible. Apple A9, A9X, A10, A10X, T2, A11 gets the per-SoC compatible, M1 "apple,t8103-cluster-cpufreq" compatible, then the "apple,cluster-cpufreq" fallback compatible. Acked-by: Krzysztof Kozlowski Signed-off-by: Nick Chan Signed-off-by: Viresh Kumar --- .../bindings/cpufreq/apple,cluster-cpufreq.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml b/Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml index 76cb9726660e..896276b8c6bb 100644 --- a/Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml +++ b/Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml @@ -24,9 +24,17 @@ properties: - apple,t8112-cluster-cpufreq - const: apple,cluster-cpufreq - items: - - const: apple,t6000-cluster-cpufreq + - enum: + - apple,s8000-cluster-cpufreq + - apple,t8010-cluster-cpufreq + - apple,t8015-cluster-cpufreq + - apple,t6000-cluster-cpufreq - const: apple,t8103-cluster-cpufreq - const: apple,cluster-cpufreq + - items: + - const: apple,t7000-cluster-cpufreq + - const: apple,s5l8960x-cluster-cpufreq + - const: apple,s5l8960x-cluster-cpufreq reg: maxItems: 1 From 4a06c250abaaf7cd1e32abcb90694b551712fa63 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 19 Dec 2024 02:25:04 +0800 Subject: [PATCH 05/14] cpufreq: apple-soc: Drop setting the PS2 field on M2+ Newer device do not use this. It is not known what this field does, but change the behavior to be same as macOS to be safe. Signed-off-by: Hector Martin Signed-off-by: Nick Chan Signed-off-by: Viresh Kumar --- drivers/cpufreq/apple-soc-cpufreq.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 4dcacab9b4bf..ad6c7b8f290c 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -25,7 +25,7 @@ #define APPLE_DVFS_CMD 0x20 #define APPLE_DVFS_CMD_BUSY BIT(31) #define APPLE_DVFS_CMD_SET BIT(25) -#define APPLE_DVFS_CMD_PS2 GENMASK(16, 12) +#define APPLE_DVFS_CMD_PS2 GENMASK(15, 12) #define APPLE_DVFS_CMD_PS1 GENMASK(4, 0) /* Same timebase as CPU counter (24MHz) */ @@ -55,6 +55,7 @@ #define APPLE_DVFS_TRANSITION_TIMEOUT 100 struct apple_soc_cpufreq_info { + bool has_ps2; u64 max_pstate; u64 cur_pstate_mask; u64 cur_pstate_shift; @@ -69,18 +70,21 @@ struct apple_cpu_priv { static struct cpufreq_driver apple_soc_cpufreq_driver; static const struct apple_soc_cpufreq_info soc_t8103_info = { + .has_ps2 = true, .max_pstate = 15, .cur_pstate_mask = APPLE_DVFS_STATUS_CUR_PS_T8103, .cur_pstate_shift = APPLE_DVFS_STATUS_CUR_PS_SHIFT_T8103, }; static const struct apple_soc_cpufreq_info soc_t8112_info = { + .has_ps2 = false, .max_pstate = 31, .cur_pstate_mask = APPLE_DVFS_STATUS_CUR_PS_T8112, .cur_pstate_shift = APPLE_DVFS_STATUS_CUR_PS_SHIFT_T8112, }; static const struct apple_soc_cpufreq_info soc_default_info = { + .has_ps2 = false, .max_pstate = 15, .cur_pstate_mask = 0, /* fallback */ }; @@ -148,9 +152,12 @@ static int apple_soc_cpufreq_set_target(struct cpufreq_policy *policy, return -EIO; } - reg &= ~(APPLE_DVFS_CMD_PS1 | APPLE_DVFS_CMD_PS2); + reg &= ~APPLE_DVFS_CMD_PS1; reg |= FIELD_PREP(APPLE_DVFS_CMD_PS1, pstate); - reg |= FIELD_PREP(APPLE_DVFS_CMD_PS2, pstate); + if (priv->info->has_ps2) { + reg &= ~APPLE_DVFS_CMD_PS2; + reg |= FIELD_PREP(APPLE_DVFS_CMD_PS2, pstate); + } reg |= APPLE_DVFS_CMD_SET; writeq_relaxed(reg, priv->reg_base + APPLE_DVFS_CMD); From 0755a9376ec949eb7d881324548266ba8667a206 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Thu, 19 Dec 2024 02:25:05 +0800 Subject: [PATCH 06/14] cpufreq: apple-soc: Allow per-SoC configuration of APPLE_DVFS_CMD_PS1 Support for SoC that has a different APPLE_DVFS_CMD_PS1 will be added soon, so modify the driver first to allow it to be configured per-SoC. Signed-off-by: Nick Chan Signed-off-by: Viresh Kumar --- drivers/cpufreq/apple-soc-cpufreq.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index ad6c7b8f290c..90e34105b50b 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -27,6 +27,7 @@ #define APPLE_DVFS_CMD_SET BIT(25) #define APPLE_DVFS_CMD_PS2 GENMASK(15, 12) #define APPLE_DVFS_CMD_PS1 GENMASK(4, 0) +#define APPLE_DVFS_CMD_PS1_SHIFT 0 /* Same timebase as CPU counter (24MHz) */ #define APPLE_DVFS_LAST_CHG_TIME 0x38 @@ -59,6 +60,8 @@ struct apple_soc_cpufreq_info { u64 max_pstate; u64 cur_pstate_mask; u64 cur_pstate_shift; + u64 ps1_mask; + u64 ps1_shift; }; struct apple_cpu_priv { @@ -74,6 +77,8 @@ static const struct apple_soc_cpufreq_info soc_t8103_info = { .max_pstate = 15, .cur_pstate_mask = APPLE_DVFS_STATUS_CUR_PS_T8103, .cur_pstate_shift = APPLE_DVFS_STATUS_CUR_PS_SHIFT_T8103, + .ps1_mask = APPLE_DVFS_CMD_PS1, + .ps1_shift = APPLE_DVFS_CMD_PS1_SHIFT, }; static const struct apple_soc_cpufreq_info soc_t8112_info = { @@ -81,12 +86,16 @@ static const struct apple_soc_cpufreq_info soc_t8112_info = { .max_pstate = 31, .cur_pstate_mask = APPLE_DVFS_STATUS_CUR_PS_T8112, .cur_pstate_shift = APPLE_DVFS_STATUS_CUR_PS_SHIFT_T8112, + .ps1_mask = APPLE_DVFS_CMD_PS1, + .ps1_shift = APPLE_DVFS_CMD_PS1_SHIFT, }; static const struct apple_soc_cpufreq_info soc_default_info = { .has_ps2 = false, .max_pstate = 15, .cur_pstate_mask = 0, /* fallback */ + .ps1_mask = APPLE_DVFS_CMD_PS1, + .ps1_shift = APPLE_DVFS_CMD_PS1_SHIFT, }; static const struct of_device_id apple_soc_cpufreq_of_match[] __maybe_unused = { @@ -152,8 +161,8 @@ static int apple_soc_cpufreq_set_target(struct cpufreq_policy *policy, return -EIO; } - reg &= ~APPLE_DVFS_CMD_PS1; - reg |= FIELD_PREP(APPLE_DVFS_CMD_PS1, pstate); + reg &= ~priv->info->ps1_mask; + reg |= pstate << priv->info->ps1_shift; if (priv->info->has_ps2) { reg &= ~APPLE_DVFS_CMD_PS2; reg |= FIELD_PREP(APPLE_DVFS_CMD_PS2, pstate); From 55aac9f570b0f8a300a9b0128620ced15c4742d8 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Thu, 19 Dec 2024 02:25:06 +0800 Subject: [PATCH 07/14] cpufreq: apple-soc: Use 32-bit read for status register Apple A7-A9(X) SoCs requires 32-bit reads on the status register. Newer SoCs accepts 32-bit reads on the status register as well. Signed-off-by: Nick Chan Signed-off-by: Viresh Kumar --- drivers/cpufreq/apple-soc-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 90e34105b50b..b27d261fe5a6 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -122,7 +122,7 @@ static unsigned int apple_soc_cpufreq_get_rate(unsigned int cpu) unsigned int pstate; if (priv->info->cur_pstate_mask) { - u64 reg = readq_relaxed(priv->reg_base + APPLE_DVFS_STATUS); + u32 reg = readl_relaxed(priv->reg_base + APPLE_DVFS_STATUS); pstate = (reg & priv->info->cur_pstate_mask) >> priv->info->cur_pstate_shift; } else { From 0dc21f609149b958ca8c010d17f9935ed9cc96fe Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Thu, 19 Dec 2024 02:25:07 +0800 Subject: [PATCH 08/14] cpufreq: apple-soc: Increase cluster switch timeout to 400us Apple A11 SoC takes a long time to switch. Maximum switch time observed is 345us, so increase the cluster switch timeout to 400us to be safe. Signed-off-by: Nick Chan Signed-off-by: Viresh Kumar --- drivers/cpufreq/apple-soc-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index b27d261fe5a6..94e57f055a5f 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -53,7 +53,7 @@ #define APPLE_DVFS_PLL_FACTOR_MULT GENMASK(31, 16) #define APPLE_DVFS_PLL_FACTOR_DIV GENMASK(15, 0) -#define APPLE_DVFS_TRANSITION_TIMEOUT 100 +#define APPLE_DVFS_TRANSITION_TIMEOUT 400 struct apple_soc_cpufreq_info { bool has_ps2; From 13b147b2a9f8a9a0b18eddd23abaf84d7bea80d1 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Thu, 19 Dec 2024 02:25:08 +0800 Subject: [PATCH 09/14] cpufreq: apple-soc: Set fallback transition latency to APPLE_DVFS_TRANSITION_TIMEOUT The driver already assumes transitions will not take longer than APPLE_DVFS_TRANSITION_TIMEOUT in apple_soc_cpufreq_set_target(), so it makes little sense to set CPUFREQ_ETERNAL as the transition latency when the transistion latency is not given by the opp-table. Reviewed-by: Christian Loehle Signed-off-by: Nick Chan Signed-off-by: Viresh Kumar --- drivers/cpufreq/apple-soc-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 94e57f055a5f..879ddec9e557 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -291,7 +291,7 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy) transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); if (!transition_latency) - transition_latency = CPUFREQ_ETERNAL; + transition_latency = APPLE_DVFS_TRANSITION_TIMEOUT * NSEC_PER_USEC; policy->cpuinfo.transition_latency = transition_latency; policy->dvfs_possible_from_any_cpu = true; From 1a4ddf6ab9dde8a7e1e025c44745862705636b2f Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Thu, 19 Dec 2024 02:25:09 +0800 Subject: [PATCH 10/14] cpufreq: apple-soc: Add Apple A7-A8X SoC cpufreq support These SoCs only use 3 bits for p-states, and have a different APPLE_DVFS_CMD_PS1 mask value. Signed-off-by: Nick Chan Signed-off-by: Viresh Kumar --- drivers/cpufreq/apple-soc-cpufreq.c | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 879ddec9e557..269b18c62d04 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -22,12 +22,14 @@ #include #include -#define APPLE_DVFS_CMD 0x20 -#define APPLE_DVFS_CMD_BUSY BIT(31) -#define APPLE_DVFS_CMD_SET BIT(25) -#define APPLE_DVFS_CMD_PS2 GENMASK(15, 12) -#define APPLE_DVFS_CMD_PS1 GENMASK(4, 0) -#define APPLE_DVFS_CMD_PS1_SHIFT 0 +#define APPLE_DVFS_CMD 0x20 +#define APPLE_DVFS_CMD_BUSY BIT(31) +#define APPLE_DVFS_CMD_SET BIT(25) +#define APPLE_DVFS_CMD_PS1_S5L8960X GENMASK(24, 22) +#define APPLE_DVFS_CMD_PS1_S5L8960X_SHIFT 22 +#define APPLE_DVFS_CMD_PS2 GENMASK(15, 12) +#define APPLE_DVFS_CMD_PS1 GENMASK(4, 0) +#define APPLE_DVFS_CMD_PS1_SHIFT 0 /* Same timebase as CPU counter (24MHz) */ #define APPLE_DVFS_LAST_CHG_TIME 0x38 @@ -36,6 +38,9 @@ * Apple ran out of bits and had to shift this in T8112... */ #define APPLE_DVFS_STATUS 0x50 +#define APPLE_DVFS_STATUS_CUR_PS_S5L8960X GENMASK(5, 3) +#define APPLE_DVFS_STATUS_CUR_PS_SHIFT_S5L8960X 3 +#define APPLE_DVFS_STATUS_TGT_PS_S5L8960X GENMASK(2, 0) #define APPLE_DVFS_STATUS_CUR_PS_T8103 GENMASK(7, 4) #define APPLE_DVFS_STATUS_CUR_PS_SHIFT_T8103 4 #define APPLE_DVFS_STATUS_TGT_PS_T8103 GENMASK(3, 0) @@ -72,6 +77,15 @@ struct apple_cpu_priv { static struct cpufreq_driver apple_soc_cpufreq_driver; +static const struct apple_soc_cpufreq_info soc_s5l8960x_info = { + .has_ps2 = false, + .max_pstate = 7, + .cur_pstate_mask = APPLE_DVFS_STATUS_CUR_PS_S5L8960X, + .cur_pstate_shift = APPLE_DVFS_STATUS_CUR_PS_SHIFT_S5L8960X, + .ps1_mask = APPLE_DVFS_CMD_PS1_S5L8960X, + .ps1_shift = APPLE_DVFS_CMD_PS1_S5L8960X_SHIFT, +}; + static const struct apple_soc_cpufreq_info soc_t8103_info = { .has_ps2 = true, .max_pstate = 15, @@ -99,6 +113,10 @@ static const struct apple_soc_cpufreq_info soc_default_info = { }; static const struct of_device_id apple_soc_cpufreq_of_match[] __maybe_unused = { + { + .compatible = "apple,s5l8960x-cluster-cpufreq", + .data = &soc_s5l8960x_info, + }, { .compatible = "apple,t8103-cluster-cpufreq", .data = &soc_t8103_info, From 85d8b11351a8f15d6ec7a5e97909861cb3b6bcec Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 5 Dec 2024 22:20:28 +0530 Subject: [PATCH 11/14] cpufreq: qcom: Fix qcom_cpufreq_hw_recalc_rate() to query LUT if LMh IRQ is not available Currently, qcom_cpufreq_hw_recalc_rate() returns the LMh throttled frequency for the domain even if LMh IRQ is not available. But as per qcom_cpufreq_hw_get(), the driver has to query LUT entries to get the actual frequency of the domain. So do the same in qcom_cpufreq_hw_recalc_rate(). While doing so, refactor the existing qcom_cpufreq_hw_get() function so that qcom_cpufreq_hw_recalc_rate() can make use of the existing code and avoid code duplication. This also requires setting the qcom_cpufreq_data::policy even if LMh IRQ is not available. Fixes: 4370232c727b ("cpufreq: qcom-hw: Add CPU clock provider support") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-hw.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 98129565acb8..c145ab7b0bb2 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -143,14 +143,12 @@ static unsigned long qcom_lmh_get_throttle_freq(struct qcom_cpufreq_data *data) } /* Get the frequency requested by the cpufreq core for the CPU */ -static unsigned int qcom_cpufreq_get_freq(unsigned int cpu) +static unsigned int qcom_cpufreq_get_freq(struct cpufreq_policy *policy) { struct qcom_cpufreq_data *data; const struct qcom_cpufreq_soc_data *soc_data; - struct cpufreq_policy *policy; unsigned int index; - policy = cpufreq_cpu_get_raw(cpu); if (!policy) return 0; @@ -163,12 +161,10 @@ static unsigned int qcom_cpufreq_get_freq(unsigned int cpu) return policy->freq_table[index].frequency; } -static unsigned int qcom_cpufreq_hw_get(unsigned int cpu) +static unsigned int __qcom_cpufreq_hw_get(struct cpufreq_policy *policy) { struct qcom_cpufreq_data *data; - struct cpufreq_policy *policy; - policy = cpufreq_cpu_get_raw(cpu); if (!policy) return 0; @@ -177,7 +173,12 @@ static unsigned int qcom_cpufreq_hw_get(unsigned int cpu) if (data->throttle_irq >= 0) return qcom_lmh_get_throttle_freq(data) / HZ_PER_KHZ; - return qcom_cpufreq_get_freq(cpu); + return qcom_cpufreq_get_freq(policy); +} + +static unsigned int qcom_cpufreq_hw_get(unsigned int cpu) +{ + return __qcom_cpufreq_hw_get(cpufreq_cpu_get_raw(cpu)); } static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, @@ -363,7 +364,7 @@ static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data) * If h/w throttled frequency is higher than what cpufreq has requested * for, then stop polling and switch back to interrupt mechanism. */ - if (throttled_freq >= qcom_cpufreq_get_freq(cpu)) + if (throttled_freq >= qcom_cpufreq_get_freq(cpufreq_cpu_get_raw(cpu))) enable_irq(data->throttle_irq); else mod_delayed_work(system_highpri_wq, &data->throttle_work, @@ -441,7 +442,6 @@ static int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index) return data->throttle_irq; data->cancel_throttle = false; - data->policy = policy; mutex_init(&data->throttle_lock); INIT_DEFERRABLE_WORK(&data->throttle_work, qcom_lmh_dcvs_poll); @@ -552,6 +552,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) policy->driver_data = data; policy->dvfs_possible_from_any_cpu = true; + data->policy = policy; ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy); if (ret) { @@ -622,7 +623,7 @@ static unsigned long qcom_cpufreq_hw_recalc_rate(struct clk_hw *hw, unsigned lon { struct qcom_cpufreq_data *data = container_of(hw, struct qcom_cpufreq_data, cpu_clk); - return qcom_lmh_get_throttle_freq(data); + return __qcom_cpufreq_hw_get(data->policy) * HZ_PER_KHZ; } static const struct clk_ops qcom_cpufreq_hw_clk_ops = { From a9ba290d0b829012574b6821ba08815046e60c94 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 5 Dec 2024 22:20:29 +0530 Subject: [PATCH 12/14] cpufreq: qcom: Implement clk_ops::determine_rate() for qcom_cpufreq* clocks determine_rate() callback is used by the clk_set_rate() API to get the closest rate of the target rate supported by the clock. If this callback is not implemented (nor round_rate() callback), then the API will assume that the clock cannot set the requested rate. And since there is no parent, it will return -EINVAL. This is not an issue right now as clk_set_rate() mistakenly compares the target rate with cached rate and bails out early. But once that is fixed to compare the target rate with the actual rate of the clock (returned by recalc_rate()), then clk_set_rate() for this clock will start to fail as below: cpu cpu0: _opp_config_clk_single: failed to set clock rate: -22 So implement the determine_rate() callback that just returns the actual rate at which the clock is passed to the CPUs in a domain. Fixes: 4370232c727b ("cpufreq: qcom-hw: Add CPU clock provider support") Reported-by: Johan Hovold Suggested-by: Stephen Boyd Signed-off-by: Manivannan Sadhasivam Reviewed-by: Stephen Boyd Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-hw.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index c145ab7b0bb2..b2e7e89feaac 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -626,8 +626,21 @@ static unsigned long qcom_cpufreq_hw_recalc_rate(struct clk_hw *hw, unsigned lon return __qcom_cpufreq_hw_get(data->policy) * HZ_PER_KHZ; } +/* + * Since we cannot determine the closest rate of the target rate, let's just + * return the actual rate at which the clock is running at. This is needed to + * make clk_set_rate() API work properly. + */ +static int qcom_cpufreq_hw_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + req->rate = qcom_cpufreq_hw_recalc_rate(hw, 0); + + return 0; +} + static const struct clk_ops qcom_cpufreq_hw_clk_ops = { .recalc_rate = qcom_cpufreq_hw_recalc_rate, + .determine_rate = qcom_cpufreq_hw_determine_rate, }; static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) From af6cc45af3db12f452bfdc9a515da54b56412756 Mon Sep 17 00:00:00 2001 From: Ethan Carter Edwards Date: Mon, 23 Dec 2024 14:41:24 +0000 Subject: [PATCH 13/14] cpufreq: sparc: change kzalloc to kcalloc Refactor to use kcalloc instead of kzalloc when multiplying allocation size by count. This refactor prevents unintentional memory overflows. Discovered by checkpatch.pl. Signed-off-by: Ethan Carter Edwards Signed-off-by: Viresh Kumar --- drivers/cpufreq/sparc-us2e-cpufreq.c | 2 +- drivers/cpufreq/sparc-us3-cpufreq.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c index 8a0cd5312a59..15899dd77c08 100644 --- a/drivers/cpufreq/sparc-us2e-cpufreq.c +++ b/drivers/cpufreq/sparc-us2e-cpufreq.c @@ -323,7 +323,7 @@ static int __init us2e_freq_init(void) impl = ((ver >> 32) & 0xffff); if (manuf == 0x17 && impl == 0x13) { - us2e_freq_table = kzalloc(NR_CPUS * sizeof(*us2e_freq_table), + us2e_freq_table = kcalloc(NR_CPUS, sizeof(*us2e_freq_table), GFP_KERNEL); if (!us2e_freq_table) return -ENOMEM; diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c index b50f9d13e6d2..de50a2f3b124 100644 --- a/drivers/cpufreq/sparc-us3-cpufreq.c +++ b/drivers/cpufreq/sparc-us3-cpufreq.c @@ -171,7 +171,7 @@ static int __init us3_freq_init(void) impl == CHEETAH_PLUS_IMPL || impl == JAGUAR_IMPL || impl == PANTHER_IMPL)) { - us3_freq_table = kzalloc(NR_CPUS * sizeof(*us3_freq_table), + us3_freq_table = kcalloc(NR_CPUS, sizeof(*us3_freq_table), GFP_KERNEL); if (!us3_freq_table) return -ENOMEM; From 84cf9e541cccb8cb698518a9897942e8c78f1d83 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 9 Jan 2025 14:12:58 +0100 Subject: [PATCH 14/14] cpufreq: airoha: Add EN7581 CPUFreq SMCCC driver Add simple CPU Freq driver for Airoha EN7581 SoC that control CPU frequency scaling with SMC APIs and register a generic "cpufreq-dt" device. All CPU share the same frequency and can't be controlled independently. CPU frequency is controlled by the attached PM domain. Add SoC compatible to cpufreq-dt-plat block list as a dedicated cpufreq driver is needed with OPP v2 nodes declared in DTS. Signed-off-by: Christian Marangi Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 8 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/airoha-cpufreq.c | 152 +++++++++++++++++++++++++++ drivers/cpufreq/cpufreq-dt-platdev.c | 2 + 4 files changed, 163 insertions(+) create mode 100644 drivers/cpufreq/airoha-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5f7e13e60c80..704e84d00639 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -15,6 +15,14 @@ config ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM To compile this driver as a module, choose M here: the module will be called sun50i-cpufreq-nvmem. +config ARM_AIROHA_SOC_CPUFREQ + tristate "Airoha EN7581 SoC CPUFreq support" + depends on ARCH_AIROHA || COMPILE_TEST + select PM_OPP + default ARCH_AIROHA + help + This adds the CPUFreq driver for Airoha EN7581 SoCs. + config ARM_APPLE_SOC_CPUFREQ tristate "Apple Silicon SoC CPUFreq support" depends on ARCH_APPLE || (COMPILE_TEST && 64BIT) diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index d35a28dd9463..890fff99f37d 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) += amd_freq_sensitivity.o ################################################################################## # ARM SoC drivers +obj-$(CONFIG_ARM_AIROHA_SOC_CPUFREQ) += airoha-cpufreq.o obj-$(CONFIG_ARM_APPLE_SOC_CPUFREQ) += apple-soc-cpufreq.o obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o diff --git a/drivers/cpufreq/airoha-cpufreq.c b/drivers/cpufreq/airoha-cpufreq.c new file mode 100644 index 000000000000..4fe39eadd163 --- /dev/null +++ b/drivers/cpufreq/airoha-cpufreq.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include + +#include "cpufreq-dt.h" + +struct airoha_cpufreq_priv { + int opp_token; + struct dev_pm_domain_list *pd_list; + struct platform_device *cpufreq_dt; +}; + +static struct platform_device *cpufreq_pdev; + +/* NOP function to disable OPP from setting clock */ +static int airoha_cpufreq_config_clks_nop(struct device *dev, + struct opp_table *opp_table, + struct dev_pm_opp *opp, + void *data, bool scaling_down) +{ + return 0; +} + +static const char * const airoha_cpufreq_clk_names[] = { "cpu", NULL }; +static const char * const airoha_cpufreq_pd_names[] = { "perf" }; + +static int airoha_cpufreq_probe(struct platform_device *pdev) +{ + const struct dev_pm_domain_attach_data attach_data = { + .pd_names = airoha_cpufreq_pd_names, + .num_pd_names = ARRAY_SIZE(airoha_cpufreq_pd_names), + .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, + }; + struct dev_pm_opp_config config = { + .clk_names = airoha_cpufreq_clk_names, + .config_clks = airoha_cpufreq_config_clks_nop, + }; + struct platform_device *cpufreq_dt; + struct airoha_cpufreq_priv *priv; + struct device *dev = &pdev->dev; + struct device *cpu_dev; + int ret; + + /* CPUs refer to the same OPP table */ + cpu_dev = get_cpu_device(0); + if (!cpu_dev) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Set OPP table conf with NOP config_clks */ + priv->opp_token = dev_pm_opp_set_config(cpu_dev, &config); + if (priv->opp_token < 0) + return dev_err_probe(dev, priv->opp_token, "Failed to set OPP config\n"); + + /* Attach PM for OPP */ + ret = dev_pm_domain_attach_list(cpu_dev, &attach_data, + &priv->pd_list); + if (ret) + goto clear_opp_config; + + cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); + ret = PTR_ERR_OR_ZERO(cpufreq_dt); + if (ret) { + dev_err(dev, "failed to create cpufreq-dt device: %d\n", ret); + goto detach_pm; + } + + priv->cpufreq_dt = cpufreq_dt; + platform_set_drvdata(pdev, priv); + + return 0; + +detach_pm: + dev_pm_domain_detach_list(priv->pd_list); +clear_opp_config: + dev_pm_opp_clear_config(priv->opp_token); + + return ret; +} + +static void airoha_cpufreq_remove(struct platform_device *pdev) +{ + struct airoha_cpufreq_priv *priv = platform_get_drvdata(pdev); + + platform_device_unregister(priv->cpufreq_dt); + + dev_pm_domain_detach_list(priv->pd_list); + + dev_pm_opp_clear_config(priv->opp_token); +} + +static struct platform_driver airoha_cpufreq_driver = { + .probe = airoha_cpufreq_probe, + .remove = airoha_cpufreq_remove, + .driver = { + .name = "airoha-cpufreq", + }, +}; + +static const struct of_device_id airoha_cpufreq_match_list[] __initconst = { + { .compatible = "airoha,en7581" }, + {}, +}; +MODULE_DEVICE_TABLE(of, airoha_cpufreq_match_list); + +static int __init airoha_cpufreq_init(void) +{ + struct device_node *np = of_find_node_by_path("/"); + const struct of_device_id *match; + int ret; + + if (!np) + return -ENODEV; + + match = of_match_node(airoha_cpufreq_match_list, np); + of_node_put(np); + if (!match) + return -ENODEV; + + ret = platform_driver_register(&airoha_cpufreq_driver); + if (unlikely(ret < 0)) + return ret; + + cpufreq_pdev = platform_device_register_data(NULL, "airoha-cpufreq", + -1, match, sizeof(*match)); + ret = PTR_ERR_OR_ZERO(cpufreq_pdev); + if (ret) + platform_driver_unregister(&airoha_cpufreq_driver); + + return ret; +} +module_init(airoha_cpufreq_init); + +static void __exit airoha_cpufreq_exit(void) +{ + platform_device_unregister(cpufreq_pdev); + platform_driver_unregister(&airoha_cpufreq_driver); +} +module_exit(airoha_cpufreq_exit); + +MODULE_AUTHOR("Christian Marangi "); +MODULE_DESCRIPTION("CPUfreq driver for Airoha SoCs"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 9c198bd4f7e9..2aa00769cf09 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -103,6 +103,8 @@ static const struct of_device_id allowlist[] __initconst = { * platforms using "operating-points-v2" property. */ static const struct of_device_id blocklist[] __initconst = { + { .compatible = "airoha,en7581", }, + { .compatible = "allwinner,sun50i-a100" }, { .compatible = "allwinner,sun50i-h6", }, { .compatible = "allwinner,sun50i-h616", },