aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLee Jones <lee.jones@linaro.org>2015-12-10 04:42:16 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-12-11 20:55:21 -0500
commitab0ea257fc58d8742f73f50fba3797dfe001aa3c (patch)
tree91b6fdb5b477c0bcf922209229e07c1d25dee0af
parent68435c0d4a2d73a9f758a4b8444d165b686a8b6a (diff)
cpufreq: st: Provide runtime initialised driver for ST's platforms
The bootloader is charged with the responsibility to provide platform specific Dynamic Voltage and Frequency Scaling (DVFS) information via Device Tree. This driver takes the supplied configuration and registers it with the new generic OPP framework, to then be used with CPUFreq. Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Lee Jones <lee.jones@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/cpufreq/Kconfig.arm10
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/sti-cpufreq.c294
3 files changed, 305 insertions, 0 deletions
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 80fbfb32b5a9..ff9be3661480 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -219,6 +219,16 @@ config ARM_SPEAR_CPUFREQ
219 help 219 help
220 This adds the CPUFreq driver support for SPEAr SOCs. 220 This adds the CPUFreq driver support for SPEAr SOCs.
221 221
222config ARM_STI_CPUFREQ
223 tristate "STi CPUFreq support"
224 depends on SOC_STIH407
225 help
226 This driver uses the generic OPP framework to match the running
227 platform with a predefined set of suitable values. If not provided
228 we will fall-back so safe-values contained in Device Tree. Enable
229 this config option if you wish to add CPUFreq support for STi based
230 SoCs.
231
222config ARM_TEGRA20_CPUFREQ 232config ARM_TEGRA20_CPUFREQ
223 bool "Tegra20 CPUFreq support" 233 bool "Tegra20 CPUFreq support"
224 depends on ARCH_TEGRA 234 depends on ARCH_TEGRA
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index c0af1a1281c8..9e63fb1b09f8 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -73,6 +73,7 @@ obj-$(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_SCPI_CPUFREQ) += scpi-cpufreq.o
75obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o 75obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
76obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o
76obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o 77obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
77obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o 78obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
78obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o 79obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c
new file mode 100644
index 000000000000..a9c659f58974
--- /dev/null
+++ b/drivers/cpufreq/sti-cpufreq.c
@@ -0,0 +1,294 @@
1/*
2 * Match running platform with pre-defined OPP values for CPUFreq
3 *
4 * Author: Ajit Pal Singh <ajitpal.singh@st.com>
5 * Lee Jones <lee.jones@linaro.org>
6 *
7 * Copyright (C) 2015 STMicroelectronics (R&D) Limited
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the version 2 of the GNU General Public License as
11 * published by the Free Software Foundation
12 */
13
14#include <linux/cpu.h>
15#include <linux/io.h>
16#include <linux/mfd/syscon.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/of_platform.h>
20#include <linux/pm_opp.h>
21#include <linux/regmap.h>
22
23#define VERSION_ELEMENTS 3
24#define MAX_PCODE_NAME_LEN 7
25
26#define VERSION_SHIFT 28
27#define HW_INFO_INDEX 1
28#define MAJOR_ID_INDEX 1
29#define MINOR_ID_INDEX 2
30
31/*
32 * Only match on "suitable for ALL versions" entries
33 *
34 * This will be used with the BIT() macro. It sets the
35 * top bit of a 32bit value and is equal to 0x80000000.
36 */
37#define DEFAULT_VERSION 31
38
39enum {
40 PCODE = 0,
41 SUBSTRATE,
42 DVFS_MAX_REGFIELDS,
43};
44
45/**
46 * ST CPUFreq Driver Data
47 *
48 * @cpu_node CPU's OF node
49 * @syscfg_eng Engineering Syscon register map
50 * @regmap Syscon register map
51 */
52static struct sti_cpufreq_ddata {
53 struct device *cpu;
54 struct regmap *syscfg_eng;
55 struct regmap *syscfg;
56} ddata;
57
58static int sti_cpufreq_fetch_major(void) {
59 struct device_node *np = ddata.cpu->of_node;
60 struct device *dev = ddata.cpu;
61 unsigned int major_offset;
62 unsigned int socid;
63 int ret;
64
65 ret = of_property_read_u32_index(np, "st,syscfg",
66 MAJOR_ID_INDEX, &major_offset);
67 if (ret) {
68 dev_err(dev, "No major number offset provided in %s [%d]\n",
69 np->full_name, ret);
70 return ret;
71 }
72
73 ret = regmap_read(ddata.syscfg, major_offset, &socid);
74 if (ret) {
75 dev_err(dev, "Failed to read major number from syscon [%d]\n",
76 ret);
77 return ret;
78 }
79
80 return ((socid >> VERSION_SHIFT) & 0xf) + 1;
81}
82
83static int sti_cpufreq_fetch_minor(void)
84{
85 struct device *dev = ddata.cpu;
86 struct device_node *np = dev->of_node;
87 unsigned int minor_offset;
88 unsigned int minid;
89 int ret;
90
91 ret = of_property_read_u32_index(np, "st,syscfg-eng",
92 MINOR_ID_INDEX, &minor_offset);
93 if (ret) {
94 dev_err(dev,
95 "No minor number offset provided %s [%d]\n",
96 np->full_name, ret);
97 return ret;
98 }
99
100 ret = regmap_read(ddata.syscfg_eng, minor_offset, &minid);
101 if (ret) {
102 dev_err(dev,
103 "Failed to read the minor number from syscon [%d]\n",
104 ret);
105 return ret;
106 }
107
108 return minid & 0xf;
109}
110
111static int sti_cpufreq_fetch_regmap_field(const struct reg_field *reg_fields,
112 int hw_info_offset, int field)
113{
114 struct regmap_field *regmap_field;
115 struct reg_field reg_field = reg_fields[field];
116 struct device *dev = ddata.cpu;
117 unsigned int value;
118 int ret;
119
120 reg_field.reg = hw_info_offset;
121 regmap_field = devm_regmap_field_alloc(dev,
122 ddata.syscfg_eng,
123 reg_field);
124 if (IS_ERR(regmap_field)) {
125 dev_err(dev, "Failed to allocate reg field\n");
126 return PTR_ERR(regmap_field);
127 }
128
129 ret = regmap_field_read(regmap_field, &value);
130 if (ret) {
131 dev_err(dev, "Failed to read %s code\n",
132 field ? "SUBSTRATE" : "PCODE");
133 return ret;
134 }
135
136 return value;
137}
138
139static const struct reg_field sti_stih407_dvfs_regfields[DVFS_MAX_REGFIELDS] = {
140 [PCODE] = REG_FIELD(0, 16, 19),
141 [SUBSTRATE] = REG_FIELD(0, 0, 2),
142};
143
144static const struct reg_field *sti_cpufreq_match(void)
145{
146 if (of_machine_is_compatible("st,stih407") ||
147 of_machine_is_compatible("st,stih410"))
148 return sti_stih407_dvfs_regfields;
149
150 return NULL;
151}
152
153static int sti_cpufreq_set_opp_info(void)
154{
155 struct device *dev = ddata.cpu;
156 struct device_node *np = dev->of_node;
157 const struct reg_field *reg_fields;
158 unsigned int hw_info_offset;
159 unsigned int version[VERSION_ELEMENTS];
160 int pcode, substrate, major, minor;
161 int ret;
162 char name[MAX_PCODE_NAME_LEN];
163
164 reg_fields = sti_cpufreq_match();
165 if (!reg_fields) {
166 dev_err(dev, "This SoC doesn't support voltage scaling");
167 return -ENODEV;
168 }
169
170 ret = of_property_read_u32_index(np, "st,syscfg-eng",
171 HW_INFO_INDEX, &hw_info_offset);
172 if (ret) {
173 dev_warn(dev, "Failed to read HW info offset from DT\n");
174 substrate = DEFAULT_VERSION;
175 pcode = 0;
176 goto use_defaults;
177 }
178
179 pcode = sti_cpufreq_fetch_regmap_field(reg_fields,
180 hw_info_offset,
181 PCODE);
182 if (pcode < 0) {
183 dev_warn(dev, "Failed to obtain process code\n");
184 /* Use default pcode */
185 pcode = 0;
186 }
187
188 substrate = sti_cpufreq_fetch_regmap_field(reg_fields,
189 hw_info_offset,
190 SUBSTRATE);
191 if (substrate) {
192 dev_warn(dev, "Failed to obtain substrate code\n");
193 /* Use default substrate */
194 substrate = DEFAULT_VERSION;
195 }
196
197use_defaults:
198 major = sti_cpufreq_fetch_major();
199 if (major < 0) {
200 dev_err(dev, "Failed to obtain major version\n");
201 /* Use default major number */
202 major = DEFAULT_VERSION;
203 }
204
205 minor = sti_cpufreq_fetch_minor();
206 if (minor < 0) {
207 dev_err(dev, "Failed to obtain minor version\n");
208 /* Use default minor number */
209 minor = DEFAULT_VERSION;
210 }
211
212 snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);
213
214 ret = dev_pm_opp_set_prop_name(dev, name);
215 if (ret) {
216 dev_err(dev, "Failed to set prop name\n");
217 return ret;
218 }
219
220 version[0] = BIT(major);
221 version[1] = BIT(minor);
222 version[2] = BIT(substrate);
223
224 ret = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
225 if (ret) {
226 dev_err(dev, "Failed to set supported hardware\n");
227 return ret;
228 }
229
230 dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
231 pcode, major, minor, substrate);
232 dev_dbg(dev, "version[0]: %x version[1]: %x version[2]: %x\n",
233 version[0], version[1], version[2]);
234
235 return 0;
236}
237
238static int sti_cpufreq_fetch_syscon_regsiters(void)
239{
240 struct device *dev = ddata.cpu;
241 struct device_node *np = dev->of_node;
242
243 ddata.syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
244 if (IS_ERR(ddata.syscfg)) {
245 dev_err(dev, "\"st,syscfg\" not supplied\n");
246 return PTR_ERR(ddata.syscfg);
247 }
248
249 ddata.syscfg_eng = syscon_regmap_lookup_by_phandle(np, "st,syscfg-eng");
250 if (IS_ERR(ddata.syscfg_eng)) {
251 dev_err(dev, "\"st,syscfg-eng\" not supplied\n");
252 return PTR_ERR(ddata.syscfg_eng);
253 }
254
255 return 0;
256}
257
258static int sti_cpufreq_init(void)
259{
260 int ret;
261
262 ddata.cpu = get_cpu_device(0);
263 if (!ddata.cpu) {
264 dev_err(ddata.cpu, "Failed to get device for CPU0\n");
265 goto skip_voltage_scaling;
266 }
267
268 if (!of_get_property(ddata.cpu->of_node, "operating-points-v2", NULL)) {
269 dev_err(ddata.cpu, "OPP-v2 not supported\n");
270 goto skip_voltage_scaling;
271 }
272
273 ret = sti_cpufreq_fetch_syscon_regsiters();
274 if (ret)
275 goto skip_voltage_scaling;
276
277 ret = sti_cpufreq_set_opp_info();
278 if (!ret)
279 goto register_cpufreq_dt;
280
281skip_voltage_scaling:
282 dev_err(ddata.cpu, "Not doing voltage scaling\n");
283
284register_cpufreq_dt:
285 platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
286
287 return 0;
288}
289module_init(sti_cpufreq_init);
290
291MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
292MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
293MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
294MODULE_LICENSE("GPL v2");