cpufreq: arm_big_little: add SPCI interface driver
authorSudeep Holla <sudeep.holla@arm.com>
Thu, 8 May 2014 16:47:48 +0000 (17:47 +0100)
committerSudeep Holla <sudeep.holla@arm.com>
Mon, 2 Jun 2014 17:20:22 +0000 (18:20 +0100)
On some ARM based systems, a separate Cortex-M based System Control
Processor(SCP) provides the overall power, clock, reset and system
control including CPU DVFS. SCPI Message Protocol is used to
communicate with the SCPI.

This patch adds a interface driver for adding OPPs and registering
the arm_big_little cpufreq driver for such systems.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/Makefile
drivers/cpufreq/scpi-cpufreq.c [new file with mode: 0644]

index 5805035..99a5cab 100644 (file)
@@ -24,6 +24,15 @@ config ARM_VEXPRESS_SPC_CPUFREQ
           This add the CPUfreq driver support for Versatile Express
          big.LITTLE platforms using SPC for power management.
 
+config ARM_SPCI_CPUFREQ
+        tristate "SPCI based CPUfreq driver"
+       depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL
+        help
+          This add the CPUfreq driver support for ARM big.LITTLE platforms
+         using SCPI interface for CPU power management.
+
+         This driver works only if firmware the supporting CPU DVFS adhere
+         to SCPI protocol.
 
 config ARM_EXYNOS_CPUFREQ
        bool
index 0dbb963..baae295 100644 (file)
@@ -75,6 +75,7 @@ obj-$(CONFIG_ARM_SA1110_CPUFREQ)      += sa1110-cpufreq.o
 obj-$(CONFIG_ARM_SPEAR_CPUFREQ)                += spear-cpufreq.o
 obj-$(CONFIG_ARM_TEGRA_CPUFREQ)                += tegra-cpufreq.o
 obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
+obj-$(CONFIG_ARM_SPCI_CPUFREQ)         += scpi-cpufreq.o
 
 ##################################################################################
 # PowerPC platform drivers
diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c
new file mode 100644 (file)
index 0000000..ec1868d
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * SCPI CPUFreq Interface driver
+ *
+ * It provides necessary ops to arm_big_little cpufreq driver.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Sudeep Holla <sudeep.holla@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/scpi_protocol.h>
+#include <linux/types.h>
+
+#include "arm_big_little.h"
+
+static int scpi_init_opp_table(struct device *cpu_dev)
+{
+       u8 domain = topology_physical_package_id(cpu_dev->id);
+       struct scpi_opp *opp;
+       int idx, ret = 0, max_opp;
+       u32 *freqs;
+
+       if ((dev_pm_opp_get_opp_count(cpu_dev)) > 0)
+               return 0;
+       opp = scpi_dvfs_get_opps(domain);
+       if (IS_ERR(opp))
+               return PTR_ERR(opp);
+
+       freqs = opp->freqs;
+       max_opp = opp->count;
+       for (idx = 0; idx < max_opp; idx++, freqs++) {
+               ret = dev_pm_opp_add(cpu_dev, *freqs, 900000000 /* TODO */);
+               if (ret) {
+                       dev_warn(cpu_dev, "failed to add opp %u\n", *freqs);
+                       return ret;
+               }
+       }
+       return ret;
+}
+
+static int scpi_get_transition_latency(struct device *cpu_dev)
+{
+       u8 domain = topology_physical_package_id(cpu_dev->id);
+       struct scpi_opp *opp;
+
+       opp = scpi_dvfs_get_opps(domain);
+       if (IS_ERR(opp))
+               return PTR_ERR(opp);
+
+       return opp->latency * 1000; /* SCPI returns in uS */
+}
+
+static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = {
+       .name   = "scpi",
+       .get_transition_latency = scpi_get_transition_latency,
+       .init_opp_table = scpi_init_opp_table,
+};
+
+static int scpi_cpufreq_probe(struct platform_device *pdev)
+{
+       return bL_cpufreq_register(&scpi_cpufreq_ops);
+}
+
+static int scpi_cpufreq_remove(struct platform_device *pdev)
+{
+       bL_cpufreq_unregister(&scpi_cpufreq_ops);
+       return 0;
+}
+
+static struct of_device_id scpi_cpufreq_of_match[] = {
+       { .compatible = "arm,scpi-cpufreq" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, scpi_cpufreq_of_match);
+
+static struct platform_driver scpi_cpufreq_platdrv = {
+       .driver = {
+               .name   = "scpi-cpufreq",
+               .owner  = THIS_MODULE,
+               .of_match_table = scpi_cpufreq_of_match,
+       },
+       .probe          = scpi_cpufreq_probe,
+       .remove         = scpi_cpufreq_remove,
+};
+module_platform_driver(scpi_cpufreq_platdrv);
+
+MODULE_LICENSE("GPL");