|
|
@@ -0,0 +1,253 @@
|
|
|
+From fa27cb99b297a1a9c0a5824afe5a670e424fff61 Mon Sep 17 00:00:00 2001
|
|
|
+From: Christian Marangi <[email protected]>
|
|
|
+Date: Wed, 16 Oct 2024 18:00:57 +0200
|
|
|
+Subject: [PATCH v9 2/2] 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 <[email protected]>
|
|
|
+---
|
|
|
+Changes v9:
|
|
|
+- Fix compile error targetting wrong branch (remove_new change and new PM OPs)
|
|
|
+Changes v8:
|
|
|
+- Split in dedicated PM domain driver
|
|
|
+Changes v7:
|
|
|
+- No changes
|
|
|
+Changes v6:
|
|
|
+- Improve Kconfig depends logic
|
|
|
+- Select PM (PM_GENERIC_DOMAINS depends on it)
|
|
|
+- Drop (int) cast for
|
|
|
+Changes v5:
|
|
|
+- Rename cpu_pd to perf for power domain name
|
|
|
+- Use remove instead of remove_new
|
|
|
+Changes v4:
|
|
|
+- Rework to clk-only + PM set_performance_state implementation
|
|
|
+Changes v3:
|
|
|
+- Adapt to new cpufreq-dt APIs
|
|
|
+- Register cpufreq-dt instead of custom freq driver
|
|
|
+Changes v2:
|
|
|
+- Fix kernel bot error with missing slab.h and bitfield.h header
|
|
|
+- Limit COMPILE_TEST to ARM64 due to smcc 1.2
|
|
|
+
|
|
|
+ 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
|
|
|
+
|
|
|
+--- a/drivers/cpufreq/Kconfig.arm
|
|
|
++++ b/drivers/cpufreq/Kconfig.arm
|
|
|
+@@ -41,6 +41,14 @@ config ARM_ALLWINNER_SUN50I_CPUFREQ_NVME
|
|
|
+ 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)
|
|
|
+--- a/drivers/cpufreq/Makefile
|
|
|
++++ b/drivers/cpufreq/Makefile
|
|
|
+@@ -52,6 +52,7 @@ obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) +
|
|
|
+
|
|
|
+ ##################################################################################
|
|
|
+ # 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
|
|
|
+--- /dev/null
|
|
|
++++ b/drivers/cpufreq/airoha-cpufreq.c
|
|
|
+@@ -0,0 +1,166 @@
|
|
|
++// SPDX-License-Identifier: GPL-2.0
|
|
|
++
|
|
|
++#include <linux/bitfield.h>
|
|
|
++#include <linux/cpufreq.h>
|
|
|
++#include <linux/module.h>
|
|
|
++#include <linux/platform_device.h>
|
|
|
++#include <linux/pm_runtime.h>
|
|
|
++#include <linux/slab.h>
|
|
|
++
|
|
|
++#include "cpufreq-dt.h"
|
|
|
++
|
|
|
++struct airoha_cpufreq_priv {
|
|
|
++ int opp_token;
|
|
|
++ struct device **virt_devs;
|
|
|
++ 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 *old_opp,
|
|
|
++ 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_genpd_names[] = { "cpu_pd", NULL };
|
|
|
++
|
|
|
++static int airoha_cpufreq_probe(struct platform_device *pdev)
|
|
|
++{
|
|
|
++ struct dev_pm_opp_config config = { };
|
|
|
++ struct platform_device *cpufreq_dt;
|
|
|
++ struct airoha_cpufreq_priv *priv;
|
|
|
++ struct device *dev = &pdev->dev;
|
|
|
++ struct device **virt_devs = NULL;
|
|
|
++ 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;
|
|
|
++
|
|
|
++ config.clk_names = airoha_cpufreq_clk_names;
|
|
|
++ config.config_clks = airoha_cpufreq_config_clks_nop;
|
|
|
++ config.genpd_names = airoha_cpufreq_genpd_names;
|
|
|
++ config.virt_devs = &virt_devs;
|
|
|
++
|
|
|
++ 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");
|
|
|
++
|
|
|
++ /* Set Attached PM for OPP ACTIVE */
|
|
|
++ if (virt_devs) {
|
|
|
++ const char * const *name = airoha_cpufreq_genpd_names;
|
|
|
++ int i, j;
|
|
|
++
|
|
|
++ for (i = 0; *name; i++, name++) {
|
|
|
++ ret = pm_runtime_resume_and_get(virt_devs[i]);
|
|
|
++ if (ret) {
|
|
|
++ dev_err(cpu_dev, "failed to resume %s: %d\n",
|
|
|
++ *name, ret);
|
|
|
++
|
|
|
++ /* Rollback previous PM runtime calls */
|
|
|
++ name = config.genpd_names;
|
|
|
++ for (j = 0; *name && j < i; j++, name++)
|
|
|
++ pm_runtime_put(virt_devs[j]);
|
|
|
++
|
|
|
++ goto err_register_cpufreq;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ priv->virt_devs = virt_devs;
|
|
|
++ }
|
|
|
++
|
|
|
++ 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 err_register_cpufreq;
|
|
|
++ }
|
|
|
++
|
|
|
++ priv->cpufreq_dt = cpufreq_dt;
|
|
|
++ platform_set_drvdata(pdev, priv);
|
|
|
++
|
|
|
++ return 0;
|
|
|
++
|
|
|
++err_register_cpufreq:
|
|
|
++ 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);
|
|
|
++ const char * const *name = airoha_cpufreq_genpd_names;
|
|
|
++ int i;
|
|
|
++
|
|
|
++ platform_device_unregister(priv->cpufreq_dt);
|
|
|
++
|
|
|
++ dev_pm_opp_clear_config(priv->opp_token);
|
|
|
++
|
|
|
++ for (i = 0; *name; i++, name++)
|
|
|
++ pm_runtime_put(priv->virt_devs[i]);
|
|
|
++}
|
|
|
++
|
|
|
++static struct platform_driver airoha_cpufreq_driver = {
|
|
|
++ .probe = airoha_cpufreq_probe,
|
|
|
++ .remove_new = 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 <[email protected]>");
|
|
|
++MODULE_DESCRIPTION("CPUfreq driver for Airoha SoCs");
|
|
|
++MODULE_LICENSE("GPL");
|
|
|
+--- a/drivers/cpufreq/cpufreq-dt-platdev.c
|
|
|
++++ b/drivers/cpufreq/cpufreq-dt-platdev.c
|
|
|
+@@ -103,6 +103,8 @@ static const struct of_device_id allowli
|
|
|
+ * platforms using "operating-points-v2" property.
|
|
|
+ */
|
|
|
+ static const struct of_device_id blocklist[] __initconst = {
|
|
|
++ { .compatible = "airoha,en7581", },
|
|
|
++
|
|
|
+ { .compatible = "allwinner,sun50i-h6", },
|
|
|
+
|
|
|
+ { .compatible = "apple,arm-platform", },
|