@@ -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 && ARM64)
+ 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)
@@ -52,6 +52,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
new file mode 100644
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/arm-smccc.h>
+#include <linux/bitfield.h>
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "cpufreq-dt.h"
+
+#define AIROHA_SIP_AVS_HANDLE 0x82000301
+#define AIROHA_AVS_OP_BASE 0xddddddd0
+#define AIROHA_AVS_OP_MASK GENMASK(1, 0)
+#define AIROHA_AVS_OP_FREQ_DYN_ADJ (AIROHA_AVS_OP_BASE | \
+ FIELD_PREP(AIROHA_AVS_OP_MASK, 0x1))
+#define AIROHA_AVS_OP_GET_FREQ (AIROHA_AVS_OP_BASE | \
+ FIELD_PREP(AIROHA_AVS_OP_MASK, 0x2))
+
+struct airoha_cpufreq_state {
+ struct platform_device *cpufreq_dt;
+};
+
+static struct airoha_cpufreq_state *airoha_cpufreq_state;
+
+static unsigned int airoha_cpufreq_get(unsigned int cpu)
+{
+ const struct arm_smccc_1_2_regs args = {
+ .a0 = AIROHA_SIP_AVS_HANDLE,
+ .a1 = AIROHA_AVS_OP_GET_FREQ,
+ };
+ struct arm_smccc_1_2_regs res;
+
+ arm_smccc_1_2_smc(&args, &res);
+
+ return (int)(res.a0 * 1000);
+}
+
+static int airoha_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
+{
+ const struct arm_smccc_1_2_regs args = {
+ .a0 = AIROHA_SIP_AVS_HANDLE,
+ .a1 = AIROHA_AVS_OP_FREQ_DYN_ADJ,
+ .a3 = index,
+ };
+ struct arm_smccc_1_2_regs res;
+
+ arm_smccc_1_2_smc(&args, &res);
+
+ /* SMC signal correct apply by unsetting BIT 0 */
+ return res.a0 & BIT(0) ? -EINVAL : 0;
+}
+
+static int __init airoha_cpufreq_driver_init(void)
+{
+ struct cpufreq_dt_platform_data pdata = { };
+ struct platform_device *cpufreq_dt;
+ struct device *cpu_dev;
+ int ret;
+
+ if (!of_machine_is_compatible("airoha,en7581"))
+ return -ENODEV;
+
+ cpu_dev = get_cpu_device(0);
+ if (!cpu_dev) {
+ dev_err(cpu_dev, "Cannot get CPU\n");
+ return -ENODEV;
+ }
+
+ airoha_cpufreq_state = kzalloc(sizeof(*airoha_cpufreq_state), GFP_KERNEL);
+ if (!airoha_cpufreq_state)
+ return -ENOMEM;
+
+ pdata.no_cpu_clk = true;
+ pdata.target_index = airoha_cpufreq_set_target;
+ pdata.get = airoha_cpufreq_get;
+
+ cpufreq_dt = platform_device_register_data(NULL, "cpufreq-dt", -1, &pdata,
+ sizeof(pdata));
+ ret = PTR_ERR_OR_ZERO(cpufreq_dt);
+ if (ret) {
+ dev_err(cpu_dev,
+ "failed to create cpufreq-dt device: %d\n", ret);
+ kfree(airoha_cpufreq_state);
+ return ret;
+ }
+
+ airoha_cpufreq_state->cpufreq_dt = cpufreq_dt;
+
+ return 0;
+}
+late_initcall(airoha_cpufreq_driver_init);
+
+static void __exit airoha_cpufreq_driver_remove(void)
+{
+ platform_device_unregister(airoha_cpufreq_state->cpufreq_dt);
+ kfree(airoha_cpufreq_state);
+}
+module_exit(airoha_cpufreq_driver_remove);
+
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("CPUfreq driver for Airoha SoCs");
+MODULE_LICENSE("GPL");
@@ -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-h6", },
{ .compatible = "allwinner,sun50i-h616", },
{ .compatible = "allwinner,sun50i-h618", },
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. Current shared CPU frequency is returned by the related SMC command. 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 <ansuelsmth@gmail.com> --- 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 | 103 +++++++++++++++++++++++++++ drivers/cpufreq/cpufreq-dt-platdev.c | 2 + 4 files changed, 114 insertions(+) create mode 100644 drivers/cpufreq/airoha-cpufreq.c