Blame view

drivers/cpufreq/sti-cpufreq.c 7.25 KB
aecd8454c   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
ab0ea257f   Lee Jones   cpufreq: st: Prov...
2
3
4
5
6
7
8
  /*
   * Match running platform with pre-defined OPP values for CPUFreq
   *
   * Author: Ajit Pal Singh <ajitpal.singh@st.com>
   *         Lee Jones <lee.jones@linaro.org>
   *
   * Copyright (C) 2015 STMicroelectronics (R&D) Limited
ab0ea257f   Lee Jones   cpufreq: st: Prov...
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
   */
  
  #include <linux/cpu.h>
  #include <linux/io.h>
  #include <linux/mfd/syscon.h>
  #include <linux/module.h>
  #include <linux/of.h>
  #include <linux/of_platform.h>
  #include <linux/pm_opp.h>
  #include <linux/regmap.h>
  
  #define VERSION_ELEMENTS	3
  #define MAX_PCODE_NAME_LEN	7
  
  #define VERSION_SHIFT		28
  #define HW_INFO_INDEX		1
  #define MAJOR_ID_INDEX		1
  #define MINOR_ID_INDEX		2
  
  /*
   * Only match on "suitable for ALL versions" entries
   *
   * This will be used with the BIT() macro.  It sets the
   * top bit of a 32bit value and is equal to 0x80000000.
   */
  #define DEFAULT_VERSION		31
  
  enum {
  	PCODE = 0,
  	SUBSTRATE,
  	DVFS_MAX_REGFIELDS,
  };
  
  /**
34adbcbd0   Lee Jones   cpufreq: sti-cpuf...
43
   * struct sti_cpufreq_ddata - ST CPUFreq Driver Data
ab0ea257f   Lee Jones   cpufreq: st: Prov...
44
   *
34adbcbd0   Lee Jones   cpufreq: sti-cpuf...
45
46
47
   * @cpu:		CPU's OF node
   * @syscfg_eng:		Engineering Syscon register map
   * @syscfg:		Syscon register map
ab0ea257f   Lee Jones   cpufreq: st: Prov...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
   */
  static struct sti_cpufreq_ddata {
  	struct device *cpu;
  	struct regmap *syscfg_eng;
  	struct regmap *syscfg;
  } ddata;
  
  static int sti_cpufreq_fetch_major(void) {
  	struct device_node *np = ddata.cpu->of_node;
  	struct device *dev = ddata.cpu;
  	unsigned int major_offset;
  	unsigned int socid;
  	int ret;
  
  	ret = of_property_read_u32_index(np, "st,syscfg",
  					 MAJOR_ID_INDEX, &major_offset);
  	if (ret) {
cc5a7a749   Rob Herring   cpufreq: Convert ...
65
66
67
  		dev_err(dev, "No major number offset provided in %pOF [%d]
  ",
  			np, ret);
ab0ea257f   Lee Jones   cpufreq: st: Prov...
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  		return ret;
  	}
  
  	ret = regmap_read(ddata.syscfg, major_offset, &socid);
  	if (ret) {
  		dev_err(dev, "Failed to read major number from syscon [%d]
  ",
  			ret);
  		return ret;
  	}
  
  	return ((socid >> VERSION_SHIFT) & 0xf) + 1;
  }
  
  static int sti_cpufreq_fetch_minor(void)
  {
  	struct device *dev = ddata.cpu;
  	struct device_node *np = dev->of_node;
  	unsigned int minor_offset;
  	unsigned int minid;
  	int ret;
  
  	ret = of_property_read_u32_index(np, "st,syscfg-eng",
  					 MINOR_ID_INDEX, &minor_offset);
  	if (ret) {
  		dev_err(dev,
cc5a7a749   Rob Herring   cpufreq: Convert ...
94
95
96
  			"No minor number offset provided %pOF [%d]
  ",
  			np, ret);
ab0ea257f   Lee Jones   cpufreq: st: Prov...
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  		return ret;
  	}
  
  	ret = regmap_read(ddata.syscfg_eng, minor_offset, &minid);
  	if (ret) {
  		dev_err(dev,
  			"Failed to read the minor number from syscon [%d]
  ",
  			ret);
  		return ret;
  	}
  
  	return minid & 0xf;
  }
  
  static int sti_cpufreq_fetch_regmap_field(const struct reg_field *reg_fields,
  					  int hw_info_offset, int field)
  {
  	struct regmap_field *regmap_field;
  	struct reg_field reg_field = reg_fields[field];
  	struct device *dev = ddata.cpu;
  	unsigned int value;
  	int ret;
  
  	reg_field.reg = hw_info_offset;
  	regmap_field = devm_regmap_field_alloc(dev,
  					       ddata.syscfg_eng,
  					       reg_field);
  	if (IS_ERR(regmap_field)) {
  		dev_err(dev, "Failed to allocate reg field
  ");
  		return PTR_ERR(regmap_field);
  	}
  
  	ret = regmap_field_read(regmap_field, &value);
  	if (ret) {
  		dev_err(dev, "Failed to read %s code
  ",
  			field ? "SUBSTRATE" : "PCODE");
  		return ret;
  	}
  
  	return value;
  }
  
  static const struct reg_field sti_stih407_dvfs_regfields[DVFS_MAX_REGFIELDS] = {
  	[PCODE]		= REG_FIELD(0, 16, 19),
  	[SUBSTRATE]	= REG_FIELD(0, 0, 2),
  };
  
  static const struct reg_field *sti_cpufreq_match(void)
  {
  	if (of_machine_is_compatible("st,stih407") ||
01a163c52   Alain Volmat   cpufreq: sti-cpuf...
150
151
  	    of_machine_is_compatible("st,stih410") ||
  	    of_machine_is_compatible("st,stih418"))
ab0ea257f   Lee Jones   cpufreq: st: Prov...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  		return sti_stih407_dvfs_regfields;
  
  	return NULL;
  }
  
  static int sti_cpufreq_set_opp_info(void)
  {
  	struct device *dev = ddata.cpu;
  	struct device_node *np = dev->of_node;
  	const struct reg_field *reg_fields;
  	unsigned int hw_info_offset;
  	unsigned int version[VERSION_ELEMENTS];
  	int pcode, substrate, major, minor;
  	int ret;
  	char name[MAX_PCODE_NAME_LEN];
fa30184d1   Viresh Kumar   PM / OPP: Return ...
167
  	struct opp_table *opp_table;
ab0ea257f   Lee Jones   cpufreq: st: Prov...
168
169
170
  
  	reg_fields = sti_cpufreq_match();
  	if (!reg_fields) {
9ad0a1b6a   Colin Ian King   cpufreq: st: add ...
171
172
  		dev_err(dev, "This SoC doesn't support voltage scaling
  ");
ab0ea257f   Lee Jones   cpufreq: st: Prov...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  		return -ENODEV;
  	}
  
  	ret = of_property_read_u32_index(np, "st,syscfg-eng",
  					 HW_INFO_INDEX, &hw_info_offset);
  	if (ret) {
  		dev_warn(dev, "Failed to read HW info offset from DT
  ");
  		substrate = DEFAULT_VERSION;
  		pcode = 0;
  		goto use_defaults;
  	}
  
  	pcode = sti_cpufreq_fetch_regmap_field(reg_fields,
  					       hw_info_offset,
  					       PCODE);
  	if (pcode < 0) {
  		dev_warn(dev, "Failed to obtain process code
  ");
  		/* Use default pcode */
  		pcode = 0;
  	}
  
  	substrate = sti_cpufreq_fetch_regmap_field(reg_fields,
  						   hw_info_offset,
  						   SUBSTRATE);
  	if (substrate) {
  		dev_warn(dev, "Failed to obtain substrate code
  ");
  		/* Use default substrate */
  		substrate = DEFAULT_VERSION;
  	}
  
  use_defaults:
  	major = sti_cpufreq_fetch_major();
  	if (major < 0) {
  		dev_err(dev, "Failed to obtain major version
  ");
  		/* Use default major number */
  		major = DEFAULT_VERSION;
  	}
  
  	minor = sti_cpufreq_fetch_minor();
  	if (minor < 0) {
  		dev_err(dev, "Failed to obtain minor version
  ");
  		/* Use default minor number */
  		minor = DEFAULT_VERSION;
  	}
  
  	snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);
fa30184d1   Viresh Kumar   PM / OPP: Return ...
224
225
  	opp_table = dev_pm_opp_set_prop_name(dev, name);
  	if (IS_ERR(opp_table)) {
ab0ea257f   Lee Jones   cpufreq: st: Prov...
226
227
  		dev_err(dev, "Failed to set prop name
  ");
fa30184d1   Viresh Kumar   PM / OPP: Return ...
228
  		return PTR_ERR(opp_table);
ab0ea257f   Lee Jones   cpufreq: st: Prov...
229
230
231
232
233
  	}
  
  	version[0] = BIT(major);
  	version[1] = BIT(minor);
  	version[2] = BIT(substrate);
fa30184d1   Viresh Kumar   PM / OPP: Return ...
234
235
  	opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
  	if (IS_ERR(opp_table)) {
ab0ea257f   Lee Jones   cpufreq: st: Prov...
236
237
  		dev_err(dev, "Failed to set supported hardware
  ");
fa30184d1   Viresh Kumar   PM / OPP: Return ...
238
  		return PTR_ERR(opp_table);
ab0ea257f   Lee Jones   cpufreq: st: Prov...
239
240
241
242
243
244
245
246
247
248
249
  	}
  
  	dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d
  ",
  		pcode, major, minor, substrate);
  	dev_dbg(dev, "version[0]: %x version[1]: %x version[2]: %x
  ",
  		version[0], version[1], version[2]);
  
  	return 0;
  }
ad61dd303   Stephen Boyd   scripts/spelling....
250
  static int sti_cpufreq_fetch_syscon_registers(void)
ab0ea257f   Lee Jones   cpufreq: st: Prov...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  {
  	struct device *dev = ddata.cpu;
  	struct device_node *np = dev->of_node;
  
  	ddata.syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
  	if (IS_ERR(ddata.syscfg)) {
  		dev_err(dev,  "\"st,syscfg\" not supplied
  ");
  		return PTR_ERR(ddata.syscfg);
  	}
  
  	ddata.syscfg_eng = syscon_regmap_lookup_by_phandle(np, "st,syscfg-eng");
  	if (IS_ERR(ddata.syscfg_eng)) {
  		dev_err(dev, "\"st,syscfg-eng\" not supplied
  ");
  		return PTR_ERR(ddata.syscfg_eng);
  	}
  
  	return 0;
  }
  
  static int sti_cpufreq_init(void)
  {
  	int ret;
2482bc31c   Sudeep Holla   cpufreq: st: enab...
275
  	if ((!of_machine_is_compatible("st,stih407")) &&
01a163c52   Alain Volmat   cpufreq: sti-cpuf...
276
277
  		(!of_machine_is_compatible("st,stih410")) &&
  		(!of_machine_is_compatible("st,stih418")))
2482bc31c   Sudeep Holla   cpufreq: st: enab...
278
  		return -ENODEV;
ab0ea257f   Lee Jones   cpufreq: st: Prov...
279
280
281
282
283
284
285
286
287
288
289
290
  	ddata.cpu = get_cpu_device(0);
  	if (!ddata.cpu) {
  		dev_err(ddata.cpu, "Failed to get device for CPU0
  ");
  		goto skip_voltage_scaling;
  	}
  
  	if (!of_get_property(ddata.cpu->of_node, "operating-points-v2", NULL)) {
  		dev_err(ddata.cpu, "OPP-v2 not supported
  ");
  		goto skip_voltage_scaling;
  	}
ad61dd303   Stephen Boyd   scripts/spelling....
291
  	ret = sti_cpufreq_fetch_syscon_registers();
ab0ea257f   Lee Jones   cpufreq: st: Prov...
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
  	if (ret)
  		goto skip_voltage_scaling;
  
  	ret = sti_cpufreq_set_opp_info();
  	if (!ret)
  		goto register_cpufreq_dt;
  
  skip_voltage_scaling:
  	dev_err(ddata.cpu, "Not doing voltage scaling
  ");
  
  register_cpufreq_dt:
  	platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
  
  	return 0;
  }
  module_init(sti_cpufreq_init);
b15fcc3a3   Pali Rohár   cpufreq: st: Add ...
309
310
311
312
313
314
  static const struct of_device_id __maybe_unused sti_cpufreq_of_match[] = {
  	{ .compatible = "st,stih407" },
  	{ .compatible = "st,stih410" },
  	{ },
  };
  MODULE_DEVICE_TABLE(of, sti_cpufreq_of_match);
ab0ea257f   Lee Jones   cpufreq: st: Prov...
315
316
317
318
  MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
  MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
  MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
  MODULE_LICENSE("GPL v2");