1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // System Control and Management Interface (SCMI) based regulator driver |
4 | // |
5 | // Copyright (C) 2020-2021 ARM Ltd. |
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 | |
36 | static const struct scmi_voltage_proto_ops *voltage_ops; |
37 | |
38 | struct scmi_regulator { |
39 | u32 id; |
40 | struct scmi_device *sdev; |
41 | struct scmi_protocol_handle *ph; |
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); |
56 | |
57 | return voltage_ops->config_set(sreg->ph, sreg->id, |
58 | SCMI_VOLTAGE_ARCH_STATE_ON); |
59 | } |
60 | |
61 | static int scmi_reg_disable(struct regulator_dev *rdev) |
62 | { |
63 | struct scmi_regulator *sreg = rdev_get_drvdata(rdev); |
64 | |
65 | return voltage_ops->config_set(sreg->ph, sreg->id, |
66 | SCMI_VOLTAGE_ARCH_STATE_OFF); |
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); |
74 | |
75 | ret = voltage_ops->config_get(sreg->ph, sreg->id, &config); |
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); |
91 | |
92 | ret = voltage_ops->level_get(sreg->ph, sreg->id, &volt_uV); |
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); |
104 | |
105 | volt_uV = sreg->desc.ops->list_voltage(rdev, selector); |
106 | if (volt_uV <= 0) |
107 | return -EINVAL; |
108 | |
109 | return voltage_ops->level_set(sreg->ph, sreg->id, 0x0, volt_uV); |
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; |
176 | sreg->desc.n_voltages = (delta_uV / sreg->desc.uV_step) + 1; |
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; |
204 | struct device *dev = &sreg->sdev->dev; |
205 | const struct scmi_voltage_info *vinfo; |
206 | |
207 | vinfo = voltage_ops->info_get(sreg->ph, sreg->id); |
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, fmt: "%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, |
256 | struct scmi_protocol_handle *ph, |
257 | struct device_node *np, |
258 | struct scmi_regulator_info *rinfo) |
259 | { |
260 | u32 dom, ret; |
261 | |
262 | ret = of_property_read_u32(np, propname: "reg" , out_value: &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(dev: &sdev->dev, |
277 | size: 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; |
284 | rinfo->sregv[dom]->ph = ph; |
285 | |
286 | /* get hold of good nodes */ |
287 | of_node_get(node: 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; |
303 | struct scmi_protocol_handle *ph; |
304 | |
305 | if (!handle) |
306 | return -ENODEV; |
307 | |
308 | voltage_ops = handle->devm_protocol_get(sdev, |
309 | SCMI_PROTOCOL_VOLTAGE, &ph); |
310 | if (IS_ERR(ptr: voltage_ops)) |
311 | return PTR_ERR(ptr: voltage_ops); |
312 | |
313 | num_doms = voltage_ops->num_domains_get(ph); |
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); |
320 | |
321 | return num_doms; |
322 | } |
323 | |
324 | rinfo = devm_kzalloc(dev: &sdev->dev, size: sizeof(*rinfo), GFP_KERNEL); |
325 | if (!rinfo) |
326 | return -ENOMEM; |
327 | |
328 | /* Allocate pointers array for all possible domains */ |
329 | rinfo->sregv = devm_kcalloc(dev: &sdev->dev, n: num_doms, |
330 | size: 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 | */ |
342 | of_node_get(node: handle->dev->of_node); |
343 | np = of_find_node_by_name(from: handle->dev->of_node, name: "regulators" ); |
344 | for_each_child_of_node(np, child) { |
345 | ret = process_scmi_regulator_of_node(sdev, ph, np: child, rinfo); |
346 | /* abort on any mem issue */ |
347 | if (ret == -ENOMEM) { |
348 | of_node_put(node: child); |
349 | return ret; |
350 | } |
351 | } |
352 | of_node_put(node: np); |
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(dev: &sdev->dev, regulator_desc: &sreg->desc, |
371 | config: &sreg->conf); |
372 | if (IS_ERR(ptr: 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(dev: &sdev->dev, data: 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(dev: &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(node: 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" ); |
421 | |