summaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/Kconfig.arm10
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/scpi-cpufreq.c124
3 files changed, 135 insertions, 0 deletions
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 642fd49793b0..1582c1c016b0 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -199,6 +199,16 @@ config ARM_SA1100_CPUFREQ
199config ARM_SA1110_CPUFREQ 199config ARM_SA1110_CPUFREQ
200 bool 200 bool
201 201
202config ARM_SCPI_CPUFREQ
203 tristate "SCPI based CPUfreq driver"
204 depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL
205 help
206 This adds the CPUfreq driver support for ARM big.LITTLE platforms
207 using SCPI protocol for CPU power management.
208
209 This driver uses SCPI Message Protocol driver to interact with the
210 firmware providing the CPU DVFS functionality.
211
202config ARM_SPEAR_CPUFREQ 212config ARM_SPEAR_CPUFREQ
203 bool "SPEAr CPUFreq support" 213 bool "SPEAr CPUFreq support"
204 depends on PLAT_SPEAR 214 depends on PLAT_SPEAR
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index d11309c487d0..c0af1a1281c8 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
71obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o 71obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
72obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o 72obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
73obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o 73obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
74obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o
74obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o 75obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
75obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o 76obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
76obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o 77obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c
new file mode 100644
index 000000000000..2c3b16fd3a01
--- /dev/null
+++ b/drivers/cpufreq/scpi-cpufreq.c
@@ -0,0 +1,124 @@
1/*
2 * System Control and Power Interface (SCPI) based CPUFreq Interface driver
3 *
4 * It provides necessary ops to arm_big_little cpufreq driver.
5 *
6 * Copyright (C) 2015 ARM Ltd.
7 * Sudeep Holla <sudeep.holla@arm.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
14 * kind, whether express or implied; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
21#include <linux/cpufreq.h>
22#include <linux/module.h>
23#include <linux/platform_device.h>
24#include <linux/pm_opp.h>
25#include <linux/scpi_protocol.h>
26#include <linux/types.h>
27
28#include "arm_big_little.h"
29
30static struct scpi_ops *scpi_ops;
31
32static struct scpi_dvfs_info *scpi_get_dvfs_info(struct device *cpu_dev)
33{
34 u8 domain = topology_physical_package_id(cpu_dev->id);
35
36 if (domain < 0)
37 return ERR_PTR(-EINVAL);
38 return scpi_ops->dvfs_get_info(domain);
39}
40
41static int scpi_opp_table_ops(struct device *cpu_dev, bool remove)
42{
43 int idx, ret = 0;
44 struct scpi_opp *opp;
45 struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
46
47 if (IS_ERR(info))
48 return PTR_ERR(info);
49
50 if (!info->opps)
51 return -EIO;
52
53 for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) {
54 if (remove)
55 dev_pm_opp_remove(cpu_dev, opp->freq);
56 else
57 ret = dev_pm_opp_add(cpu_dev, opp->freq,
58 opp->m_volt * 1000);
59 if (ret) {
60 dev_warn(cpu_dev, "failed to add opp %uHz %umV\n",
61 opp->freq, opp->m_volt);
62 while (idx-- > 0)
63 dev_pm_opp_remove(cpu_dev, (--opp)->freq);
64 return ret;
65 }
66 }
67 return ret;
68}
69
70static int scpi_get_transition_latency(struct device *cpu_dev)
71{
72 struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
73
74 if (IS_ERR(info))
75 return PTR_ERR(info);
76 return info->latency;
77}
78
79static int scpi_init_opp_table(struct device *cpu_dev)
80{
81 return scpi_opp_table_ops(cpu_dev, false);
82}
83
84static void scpi_free_opp_table(struct device *cpu_dev)
85{
86 scpi_opp_table_ops(cpu_dev, true);
87}
88
89static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = {
90 .name = "scpi",
91 .get_transition_latency = scpi_get_transition_latency,
92 .init_opp_table = scpi_init_opp_table,
93 .free_opp_table = scpi_free_opp_table,
94};
95
96static int scpi_cpufreq_probe(struct platform_device *pdev)
97{
98 scpi_ops = get_scpi_ops();
99 if (!scpi_ops)
100 return -EIO;
101
102 return bL_cpufreq_register(&scpi_cpufreq_ops);
103}
104
105static int scpi_cpufreq_remove(struct platform_device *pdev)
106{
107 bL_cpufreq_unregister(&scpi_cpufreq_ops);
108 scpi_ops = NULL;
109 return 0;
110}
111
112static struct platform_driver scpi_cpufreq_platdrv = {
113 .driver = {
114 .name = "scpi-cpufreq",
115 .owner = THIS_MODULE,
116 },
117 .probe = scpi_cpufreq_probe,
118 .remove = scpi_cpufreq_remove,
119};
120module_platform_driver(scpi_cpufreq_platdrv);
121
122MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
123MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
124MODULE_LICENSE("GPL v2");