Commit | Line | Data |
---|---|---|
0fbeae70 CM |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // System Control and Management Interface (SCMI) based regulator driver | |
4 | // | |
59046d15 | 5 | // Copyright (C) 2020-2021 ARM Ltd. |
0fbeae70 CM |
6 | // |
7 | // Implements a regulator driver on top of the SCMI Voltage Protocol. | |
8 | // | |
9 | // The ARM SCMI Protocol aims in general to hide as much as possible all the | |
10 | // underlying operational details while providing an abstracted interface for | |
11 | // its users to operate upon: as a consequence the resulting operational | |
12 | // capabilities and configurability of this regulator device are much more | |
13 | // limited than the ones usually available on a standard physical regulator. | |
14 | // | |
15 | // The supported SCMI regulator ops are restricted to the bare minimum: | |
16 | // | |
17 | // - 'status_ops': enable/disable/is_enabled | |
18 | // - 'voltage_ops': get_voltage_sel/set_voltage_sel | |
19 | // list_voltage/map_voltage | |
20 | // | |
21 | // Each SCMI regulator instance is associated, through the means of a proper DT | |
22 | // entry description, to a specific SCMI Voltage Domain. | |
23 | ||
24 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
25 | ||
26 | #include <linux/linear_range.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/of.h> | |
29 | #include <linux/regulator/driver.h> | |
30 | #include <linux/regulator/machine.h> | |
31 | #include <linux/regulator/of_regulator.h> | |
32 | #include <linux/scmi_protocol.h> | |
33 | #include <linux/slab.h> | |
34 | #include <linux/types.h> | |
35 | ||
59046d15 CM |
36 | static const struct scmi_voltage_proto_ops *voltage_ops; |
37 | ||
0fbeae70 CM |
38 | struct scmi_regulator { |
39 | u32 id; | |
40 | struct scmi_device *sdev; | |
59046d15 | 41 | struct scmi_protocol_handle *ph; |
0fbeae70 CM |
42 | struct regulator_dev *rdev; |
43 | struct device_node *of_node; | |
44 | struct regulator_desc desc; | |
45 | struct regulator_config conf; | |
46 | }; | |
47 | ||
48 | struct scmi_regulator_info { | |
49 | int num_doms; | |
50 | struct scmi_regulator **sregv; | |
51 | }; | |
52 | ||
53 | static int scmi_reg_enable(struct regulator_dev *rdev) | |
54 | { | |
55 | struct scmi_regulator *sreg = rdev_get_drvdata(rdev); | |
0fbeae70 | 56 | |
59046d15 CM |
57 | return voltage_ops->config_set(sreg->ph, sreg->id, |
58 | SCMI_VOLTAGE_ARCH_STATE_ON); | |
0fbeae70 CM |
59 | } |
60 | ||
61 | static int scmi_reg_disable(struct regulator_dev *rdev) | |
62 | { | |
63 | struct scmi_regulator *sreg = rdev_get_drvdata(rdev); | |
0fbeae70 | 64 | |
59046d15 CM |
65 | return voltage_ops->config_set(sreg->ph, sreg->id, |
66 | SCMI_VOLTAGE_ARCH_STATE_OFF); | |
0fbeae70 CM |
67 | } |
68 | ||
69 | static int scmi_reg_is_enabled(struct regulator_dev *rdev) | |
70 | { | |
71 | int ret; | |
72 | u32 config; | |
73 | struct scmi_regulator *sreg = rdev_get_drvdata(rdev); | |
0fbeae70 | 74 | |
59046d15 | 75 | ret = voltage_ops->config_get(sreg->ph, sreg->id, &config); |
0fbeae70 CM |
76 | if (ret) { |
77 | dev_err(&sreg->sdev->dev, | |
78 | "Error %d reading regulator %s status.\n", | |
79 | ret, sreg->desc.name); | |
80 | return ret; | |
81 | } | |
82 | ||
83 | return config & SCMI_VOLTAGE_ARCH_STATE_ON; | |
84 | } | |
85 | ||
86 | static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev) | |
87 | { | |
88 | int ret; | |
89 | s32 volt_uV; | |
90 | struct scmi_regulator *sreg = rdev_get_drvdata(rdev); | |
0fbeae70 | 91 | |
59046d15 | 92 | ret = voltage_ops->level_get(sreg->ph, sreg->id, &volt_uV); |
0fbeae70 CM |
93 | if (ret) |
94 | return ret; | |
95 | ||
96 | return sreg->desc.ops->map_voltage(rdev, volt_uV, volt_uV); | |
97 | } | |
98 | ||
99 | static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev, | |
100 | unsigned int selector) | |
101 | { | |
102 | s32 volt_uV; | |
103 | struct scmi_regulator *sreg = rdev_get_drvdata(rdev); | |
0fbeae70 CM |
104 | |
105 | volt_uV = sreg->desc.ops->list_voltage(rdev, selector); | |
106 | if (volt_uV <= 0) | |
107 | return -EINVAL; | |
108 | ||
59046d15 | 109 | return voltage_ops->level_set(sreg->ph, sreg->id, 0x0, volt_uV); |
0fbeae70 CM |
110 | } |
111 | ||
112 | static const struct regulator_ops scmi_reg_fixed_ops = { | |
113 | .enable = scmi_reg_enable, | |
114 | .disable = scmi_reg_disable, | |
115 | .is_enabled = scmi_reg_is_enabled, | |
116 | }; | |
117 | ||
118 | static const struct regulator_ops scmi_reg_linear_ops = { | |
119 | .enable = scmi_reg_enable, | |
120 | .disable = scmi_reg_disable, | |
121 | .is_enabled = scmi_reg_is_enabled, | |
122 | .get_voltage_sel = scmi_reg_get_voltage_sel, | |
123 | .set_voltage_sel = scmi_reg_set_voltage_sel, | |
124 | .list_voltage = regulator_list_voltage_linear, | |
125 | .map_voltage = regulator_map_voltage_linear, | |
126 | }; | |
127 | ||
128 | static const struct regulator_ops scmi_reg_discrete_ops = { | |
129 | .enable = scmi_reg_enable, | |
130 | .disable = scmi_reg_disable, | |
131 | .is_enabled = scmi_reg_is_enabled, | |
132 | .get_voltage_sel = scmi_reg_get_voltage_sel, | |
133 | .set_voltage_sel = scmi_reg_set_voltage_sel, | |
134 | .list_voltage = regulator_list_voltage_table, | |
135 | .map_voltage = regulator_map_voltage_iterate, | |
136 | }; | |
137 | ||
138 | static int | |
139 | scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg, | |
140 | const struct scmi_voltage_info *vinfo) | |
141 | { | |
142 | s32 delta_uV; | |
143 | ||
144 | /* | |
145 | * Note that SCMI voltage domains describable by linear ranges | |
146 | * (segments) {low, high, step} are guaranteed to come in one single | |
147 | * triplet by the SCMI Voltage Domain protocol support itself. | |
148 | */ | |
149 | ||
150 | delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] - | |
151 | vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]); | |
152 | ||
153 | /* Rule out buggy negative-intervals answers from fw */ | |
154 | if (delta_uV < 0) { | |
155 | dev_err(&sreg->sdev->dev, | |
156 | "Invalid volt-range %d-%duV for domain %d\n", | |
157 | vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW], | |
158 | vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH], | |
159 | sreg->id); | |
160 | return -EINVAL; | |
161 | } | |
162 | ||
163 | if (!delta_uV) { | |
164 | /* Just one fixed voltage exposed by SCMI */ | |
165 | sreg->desc.fixed_uV = | |
166 | vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; | |
167 | sreg->desc.n_voltages = 1; | |
168 | sreg->desc.ops = &scmi_reg_fixed_ops; | |
169 | } else { | |
170 | /* One simple linear mapping. */ | |
171 | sreg->desc.min_uV = | |
172 | vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; | |
173 | sreg->desc.uV_step = | |
174 | vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP]; | |
175 | sreg->desc.linear_min_sel = 0; | |
36cb555f | 176 | sreg->desc.n_voltages = (delta_uV / sreg->desc.uV_step) + 1; |
0fbeae70 CM |
177 | sreg->desc.ops = &scmi_reg_linear_ops; |
178 | } | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | static int | |
184 | scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg, | |
185 | const struct scmi_voltage_info *vinfo) | |
186 | { | |
187 | /* Discrete non linear levels are mapped to volt_table */ | |
188 | sreg->desc.n_voltages = vinfo->num_levels; | |
189 | ||
190 | if (sreg->desc.n_voltages > 1) { | |
191 | sreg->desc.volt_table = (const unsigned int *)vinfo->levels_uv; | |
192 | sreg->desc.ops = &scmi_reg_discrete_ops; | |
193 | } else { | |
194 | sreg->desc.fixed_uV = vinfo->levels_uv[0]; | |
195 | sreg->desc.ops = &scmi_reg_fixed_ops; | |
196 | } | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
201 | static int scmi_regulator_common_init(struct scmi_regulator *sreg) | |
202 | { | |
203 | int ret; | |
0fbeae70 CM |
204 | struct device *dev = &sreg->sdev->dev; |
205 | const struct scmi_voltage_info *vinfo; | |
206 | ||
59046d15 | 207 | vinfo = voltage_ops->info_get(sreg->ph, sreg->id); |
0fbeae70 CM |
208 | if (!vinfo) { |
209 | dev_warn(dev, "Failure to get voltage domain %d\n", | |
210 | sreg->id); | |
211 | return -ENODEV; | |
212 | } | |
213 | ||
214 | /* | |
215 | * Regulator framework does not fully support negative voltages | |
216 | * so we discard any voltage domain reported as supporting negative | |
217 | * voltages: as a consequence each levels_uv entry is guaranteed to | |
218 | * be non-negative from here on. | |
219 | */ | |
220 | if (vinfo->negative_volts_allowed) { | |
221 | dev_warn(dev, "Negative voltages NOT supported...skip %s\n", | |
222 | sreg->of_node->full_name); | |
223 | return -EOPNOTSUPP; | |
224 | } | |
225 | ||
226 | sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name); | |
227 | if (!sreg->desc.name) | |
228 | return -ENOMEM; | |
229 | ||
230 | sreg->desc.id = sreg->id; | |
231 | sreg->desc.type = REGULATOR_VOLTAGE; | |
232 | sreg->desc.owner = THIS_MODULE; | |
233 | sreg->desc.of_match_full_name = true; | |
234 | sreg->desc.of_match = sreg->of_node->full_name; | |
235 | sreg->desc.regulators_node = "regulators"; | |
236 | if (vinfo->segmented) | |
237 | ret = scmi_config_linear_regulator_mappings(sreg, vinfo); | |
238 | else | |
239 | ret = scmi_config_discrete_regulator_mappings(sreg, vinfo); | |
240 | if (ret) | |
241 | return ret; | |
242 | ||
243 | /* | |
244 | * Using the scmi device here to have DT searched from Voltage | |
245 | * protocol node down. | |
246 | */ | |
247 | sreg->conf.dev = dev; | |
248 | ||
249 | /* Store for later retrieval via rdev_get_drvdata() */ | |
250 | sreg->conf.driver_data = sreg; | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | static int process_scmi_regulator_of_node(struct scmi_device *sdev, | |
59046d15 | 256 | struct scmi_protocol_handle *ph, |
0fbeae70 CM |
257 | struct device_node *np, |
258 | struct scmi_regulator_info *rinfo) | |
259 | { | |
260 | u32 dom, ret; | |
261 | ||
262 | ret = of_property_read_u32(np, "reg", &dom); | |
263 | if (ret) | |
264 | return ret; | |
265 | ||
266 | if (dom >= rinfo->num_doms) | |
267 | return -ENODEV; | |
268 | ||
269 | if (rinfo->sregv[dom]) { | |
270 | dev_err(&sdev->dev, | |
271 | "SCMI Voltage Domain %d already in use. Skipping: %s\n", | |
272 | dom, np->full_name); | |
273 | return -EINVAL; | |
274 | } | |
275 | ||
276 | rinfo->sregv[dom] = devm_kzalloc(&sdev->dev, | |
277 | sizeof(struct scmi_regulator), | |
278 | GFP_KERNEL); | |
279 | if (!rinfo->sregv[dom]) | |
280 | return -ENOMEM; | |
281 | ||
282 | rinfo->sregv[dom]->id = dom; | |
283 | rinfo->sregv[dom]->sdev = sdev; | |
59046d15 | 284 | rinfo->sregv[dom]->ph = ph; |
0fbeae70 CM |
285 | |
286 | /* get hold of good nodes */ | |
287 | of_node_get(np); | |
288 | rinfo->sregv[dom]->of_node = np; | |
289 | ||
290 | dev_dbg(&sdev->dev, | |
291 | "Found SCMI Regulator entry -- OF node [%d] -> %s\n", | |
292 | dom, np->full_name); | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | static int scmi_regulator_probe(struct scmi_device *sdev) | |
298 | { | |
299 | int d, ret, num_doms; | |
300 | struct device_node *np, *child; | |
301 | const struct scmi_handle *handle = sdev->handle; | |
302 | struct scmi_regulator_info *rinfo; | |
59046d15 | 303 | struct scmi_protocol_handle *ph; |
0fbeae70 | 304 | |
59046d15 | 305 | if (!handle) |
0fbeae70 CM |
306 | return -ENODEV; |
307 | ||
59046d15 CM |
308 | voltage_ops = handle->devm_protocol_get(sdev, |
309 | SCMI_PROTOCOL_VOLTAGE, &ph); | |
310 | if (IS_ERR(voltage_ops)) | |
311 | return PTR_ERR(voltage_ops); | |
312 | ||
313 | num_doms = voltage_ops->num_domains_get(ph); | |
668f777d CM |
314 | if (!num_doms) |
315 | return 0; | |
316 | ||
317 | if (num_doms < 0) { | |
318 | dev_err(&sdev->dev, "failed to get voltage domains - err:%d\n", | |
319 | num_doms); | |
0fbeae70 CM |
320 | |
321 | return num_doms; | |
322 | } | |
323 | ||
324 | rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL); | |
325 | if (!rinfo) | |
326 | return -ENOMEM; | |
327 | ||
328 | /* Allocate pointers array for all possible domains */ | |
329 | rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms, | |
330 | sizeof(void *), GFP_KERNEL); | |
331 | if (!rinfo->sregv) | |
332 | return -ENOMEM; | |
333 | ||
334 | rinfo->num_doms = num_doms; | |
335 | ||
336 | /* | |
337 | * Start collecting into rinfo->sregv possibly good SCMI Regulators as | |
338 | * described by a well-formed DT entry and associated with an existing | |
339 | * plausible SCMI Voltage Domain number, all belonging to this SCMI | |
340 | * platform instance node (handle->dev->of_node). | |
341 | */ | |
a9e37a82 | 342 | of_node_get(handle->dev->of_node); |
0fbeae70 CM |
343 | np = of_find_node_by_name(handle->dev->of_node, "regulators"); |
344 | for_each_child_of_node(np, child) { | |
59046d15 | 345 | ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo); |
0fbeae70 | 346 | /* abort on any mem issue */ |
45ee8b79 YL |
347 | if (ret == -ENOMEM) { |
348 | of_node_put(child); | |
0fbeae70 | 349 | return ret; |
45ee8b79 | 350 | } |
0fbeae70 | 351 | } |
68d6c847 | 352 | of_node_put(np); |
0fbeae70 CM |
353 | /* |
354 | * Register a regulator for each valid regulator-DT-entry that we | |
355 | * can successfully reach via SCMI and has a valid associated voltage | |
356 | * domain. | |
357 | */ | |
358 | for (d = 0; d < num_doms; d++) { | |
359 | struct scmi_regulator *sreg = rinfo->sregv[d]; | |
360 | ||
361 | /* Skip empty slots */ | |
362 | if (!sreg) | |
363 | continue; | |
364 | ||
365 | ret = scmi_regulator_common_init(sreg); | |
366 | /* Skip invalid voltage domains */ | |
367 | if (ret) | |
368 | continue; | |
369 | ||
370 | sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc, | |
371 | &sreg->conf); | |
372 | if (IS_ERR(sreg->rdev)) { | |
373 | sreg->rdev = NULL; | |
374 | continue; | |
375 | } | |
376 | ||
377 | dev_info(&sdev->dev, | |
378 | "Regulator %s registered for domain [%d]\n", | |
379 | sreg->desc.name, sreg->id); | |
380 | } | |
381 | ||
382 | dev_set_drvdata(&sdev->dev, rinfo); | |
383 | ||
384 | return 0; | |
385 | } | |
386 | ||
387 | static void scmi_regulator_remove(struct scmi_device *sdev) | |
388 | { | |
389 | int d; | |
390 | struct scmi_regulator_info *rinfo; | |
391 | ||
392 | rinfo = dev_get_drvdata(&sdev->dev); | |
393 | if (!rinfo) | |
394 | return; | |
395 | ||
396 | for (d = 0; d < rinfo->num_doms; d++) { | |
397 | if (!rinfo->sregv[d]) | |
398 | continue; | |
399 | of_node_put(rinfo->sregv[d]->of_node); | |
400 | } | |
401 | } | |
402 | ||
403 | static const struct scmi_device_id scmi_regulator_id_table[] = { | |
404 | { SCMI_PROTOCOL_VOLTAGE, "regulator" }, | |
405 | { }, | |
406 | }; | |
407 | MODULE_DEVICE_TABLE(scmi, scmi_regulator_id_table); | |
408 | ||
409 | static struct scmi_driver scmi_drv = { | |
410 | .name = "scmi-regulator", | |
411 | .probe = scmi_regulator_probe, | |
412 | .remove = scmi_regulator_remove, | |
413 | .id_table = scmi_regulator_id_table, | |
414 | }; | |
415 | ||
416 | module_scmi_driver(scmi_drv); | |
417 | ||
418 | MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>"); | |
419 | MODULE_DESCRIPTION("ARM SCMI regulator driver"); | |
420 | MODULE_LICENSE("GPL v2"); |