1 | // SPDX-License-Identifier: GPL-2.0-only |
---|---|
2 | // |
3 | // Framework for Ethernet Power Sourcing Equipment |
4 | // |
5 | // Copyright (c) 2022 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> |
6 | // |
7 | |
8 | #include <linux/device.h> |
9 | #include <linux/ethtool.h> |
10 | #include <linux/of.h> |
11 | #include <linux/pse-pd/pse.h> |
12 | #include <linux/regulator/driver.h> |
13 | #include <linux/regulator/machine.h> |
14 | |
15 | static DEFINE_MUTEX(pse_list_mutex); |
16 | static LIST_HEAD(pse_controller_list); |
17 | |
18 | /** |
19 | * struct pse_control - a PSE control |
20 | * @pcdev: a pointer to the PSE controller device |
21 | * this PSE control belongs to |
22 | * @ps: PSE PI supply of the PSE control |
23 | * @list: list entry for the pcdev's PSE controller list |
24 | * @id: ID of the PSE line in the PSE controller device |
25 | * @refcnt: Number of gets of this pse_control |
26 | */ |
27 | struct pse_control { |
28 | struct pse_controller_dev *pcdev; |
29 | struct regulator *ps; |
30 | struct list_head list; |
31 | unsigned int id; |
32 | struct kref refcnt; |
33 | }; |
34 | |
35 | static int of_load_single_pse_pi_pairset(struct device_node *node, |
36 | struct pse_pi *pi, |
37 | int pairset_num) |
38 | { |
39 | struct device_node *pairset_np; |
40 | const char *name; |
41 | int ret; |
42 | |
43 | ret = of_property_read_string_index(np: node, propname: "pairset-names", |
44 | index: pairset_num, output: &name); |
45 | if (ret) |
46 | return ret; |
47 | |
48 | if (!strcmp(name, "alternative-a")) { |
49 | pi->pairset[pairset_num].pinout = ALTERNATIVE_A; |
50 | } else if (!strcmp(name, "alternative-b")) { |
51 | pi->pairset[pairset_num].pinout = ALTERNATIVE_B; |
52 | } else { |
53 | pr_err("pse: wrong pairset-names value %s (%pOF)\n", |
54 | name, node); |
55 | return -EINVAL; |
56 | } |
57 | |
58 | pairset_np = of_parse_phandle(np: node, phandle_name: "pairsets", index: pairset_num); |
59 | if (!pairset_np) |
60 | return -ENODEV; |
61 | |
62 | pi->pairset[pairset_num].np = pairset_np; |
63 | |
64 | return 0; |
65 | } |
66 | |
67 | /** |
68 | * of_load_pse_pi_pairsets - load PSE PI pairsets pinout and polarity |
69 | * @node: a pointer of the device node |
70 | * @pi: a pointer of the PSE PI to fill |
71 | * @npairsets: the number of pairsets (1 or 2) used by the PI |
72 | * |
73 | * Return: 0 on success and failure value on error |
74 | */ |
75 | static int of_load_pse_pi_pairsets(struct device_node *node, |
76 | struct pse_pi *pi, |
77 | int npairsets) |
78 | { |
79 | int i, ret; |
80 | |
81 | ret = of_property_count_strings(np: node, propname: "pairset-names"); |
82 | if (ret != npairsets) { |
83 | pr_err("pse: amount of pairsets and pairset-names is not equal %d != %d (%pOF)\n", |
84 | npairsets, ret, node); |
85 | return -EINVAL; |
86 | } |
87 | |
88 | for (i = 0; i < npairsets; i++) { |
89 | ret = of_load_single_pse_pi_pairset(node, pi, pairset_num: i); |
90 | if (ret) |
91 | goto out; |
92 | } |
93 | |
94 | if (npairsets == 2 && |
95 | pi->pairset[0].pinout == pi->pairset[1].pinout) { |
96 | pr_err("pse: two PI pairsets can not have identical pinout (%pOF)", |
97 | node); |
98 | ret = -EINVAL; |
99 | } |
100 | |
101 | out: |
102 | /* If an error appears, release all the pairset device node kref */ |
103 | if (ret) { |
104 | of_node_put(node: pi->pairset[0].np); |
105 | pi->pairset[0].np = NULL; |
106 | of_node_put(node: pi->pairset[1].np); |
107 | pi->pairset[1].np = NULL; |
108 | } |
109 | |
110 | return ret; |
111 | } |
112 | |
113 | static void pse_release_pis(struct pse_controller_dev *pcdev) |
114 | { |
115 | int i; |
116 | |
117 | for (i = 0; i < pcdev->nr_lines; i++) { |
118 | of_node_put(node: pcdev->pi[i].pairset[0].np); |
119 | of_node_put(node: pcdev->pi[i].pairset[1].np); |
120 | of_node_put(node: pcdev->pi[i].np); |
121 | } |
122 | kfree(objp: pcdev->pi); |
123 | } |
124 | |
125 | /** |
126 | * of_load_pse_pis - load all the PSE PIs |
127 | * @pcdev: a pointer to the PSE controller device |
128 | * |
129 | * Return: 0 on success and failure value on error |
130 | */ |
131 | static int of_load_pse_pis(struct pse_controller_dev *pcdev) |
132 | { |
133 | struct device_node *np = pcdev->dev->of_node; |
134 | struct device_node *node, *pis; |
135 | int ret; |
136 | |
137 | if (!np) |
138 | return -ENODEV; |
139 | |
140 | pcdev->pi = kcalloc(pcdev->nr_lines, sizeof(*pcdev->pi), GFP_KERNEL); |
141 | if (!pcdev->pi) |
142 | return -ENOMEM; |
143 | |
144 | pis = of_get_child_by_name(node: np, name: "pse-pis"); |
145 | if (!pis) { |
146 | /* no description of PSE PIs */ |
147 | pcdev->no_of_pse_pi = true; |
148 | return 0; |
149 | } |
150 | |
151 | for_each_child_of_node(pis, node) { |
152 | struct pse_pi pi = {0}; |
153 | u32 id; |
154 | |
155 | if (!of_node_name_eq(np: node, name: "pse-pi")) |
156 | continue; |
157 | |
158 | ret = of_property_read_u32(np: node, propname: "reg", out_value: &id); |
159 | if (ret) { |
160 | dev_err(pcdev->dev, |
161 | "can't get reg property for node '%pOF'", |
162 | node); |
163 | goto out; |
164 | } |
165 | |
166 | if (id >= pcdev->nr_lines) { |
167 | dev_err(pcdev->dev, |
168 | "reg value (%u) is out of range (%u) (%pOF)\n", |
169 | id, pcdev->nr_lines, node); |
170 | ret = -EINVAL; |
171 | goto out; |
172 | } |
173 | |
174 | if (pcdev->pi[id].np) { |
175 | dev_err(pcdev->dev, |
176 | "other node with same reg value was already registered. %pOF : %pOF\n", |
177 | pcdev->pi[id].np, node); |
178 | ret = -EINVAL; |
179 | goto out; |
180 | } |
181 | |
182 | ret = of_count_phandle_with_args(np: node, list_name: "pairsets", NULL); |
183 | /* npairsets is limited to value one or two */ |
184 | if (ret == 1 || ret == 2) { |
185 | ret = of_load_pse_pi_pairsets(node, pi: &pi, npairsets: ret); |
186 | if (ret) |
187 | goto out; |
188 | } else if (ret != ENOENT) { |
189 | dev_err(pcdev->dev, |
190 | "error: wrong number of pairsets. Should be 1 or 2, got %d (%pOF)\n", |
191 | ret, node); |
192 | ret = -EINVAL; |
193 | goto out; |
194 | } |
195 | |
196 | of_node_get(node); |
197 | pi.np = node; |
198 | memcpy(&pcdev->pi[id], &pi, sizeof(pi)); |
199 | } |
200 | |
201 | of_node_put(node: pis); |
202 | return 0; |
203 | |
204 | out: |
205 | pse_release_pis(pcdev); |
206 | of_node_put(node); |
207 | of_node_put(node: pis); |
208 | return ret; |
209 | } |
210 | |
211 | static int pse_pi_is_enabled(struct regulator_dev *rdev) |
212 | { |
213 | struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); |
214 | struct pse_admin_state admin_state = {0}; |
215 | const struct pse_controller_ops *ops; |
216 | int id, ret; |
217 | |
218 | ops = pcdev->ops; |
219 | if (!ops->pi_get_admin_state) |
220 | return -EOPNOTSUPP; |
221 | |
222 | id = rdev_get_id(rdev); |
223 | mutex_lock(&pcdev->lock); |
224 | ret = ops->pi_get_admin_state(pcdev, id, &admin_state); |
225 | if (ret) |
226 | goto out; |
227 | |
228 | if (admin_state.podl_admin_state == ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED || |
229 | admin_state.c33_admin_state == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED) |
230 | ret = 1; |
231 | |
232 | out: |
233 | mutex_unlock(lock: &pcdev->lock); |
234 | |
235 | return ret; |
236 | } |
237 | |
238 | static int pse_pi_enable(struct regulator_dev *rdev) |
239 | { |
240 | struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); |
241 | const struct pse_controller_ops *ops; |
242 | int id, ret; |
243 | |
244 | ops = pcdev->ops; |
245 | if (!ops->pi_enable) |
246 | return -EOPNOTSUPP; |
247 | |
248 | id = rdev_get_id(rdev); |
249 | mutex_lock(&pcdev->lock); |
250 | ret = ops->pi_enable(pcdev, id); |
251 | if (!ret) |
252 | pcdev->pi[id].admin_state_enabled = 1; |
253 | mutex_unlock(lock: &pcdev->lock); |
254 | |
255 | return ret; |
256 | } |
257 | |
258 | static int pse_pi_disable(struct regulator_dev *rdev) |
259 | { |
260 | struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); |
261 | const struct pse_controller_ops *ops; |
262 | int id, ret; |
263 | |
264 | ops = pcdev->ops; |
265 | if (!ops->pi_disable) |
266 | return -EOPNOTSUPP; |
267 | |
268 | id = rdev_get_id(rdev); |
269 | mutex_lock(&pcdev->lock); |
270 | ret = ops->pi_disable(pcdev, id); |
271 | if (!ret) |
272 | pcdev->pi[id].admin_state_enabled = 0; |
273 | mutex_unlock(lock: &pcdev->lock); |
274 | |
275 | return ret; |
276 | } |
277 | |
278 | static int _pse_pi_get_voltage(struct regulator_dev *rdev) |
279 | { |
280 | struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); |
281 | const struct pse_controller_ops *ops; |
282 | int id; |
283 | |
284 | ops = pcdev->ops; |
285 | if (!ops->pi_get_voltage) |
286 | return -EOPNOTSUPP; |
287 | |
288 | id = rdev_get_id(rdev); |
289 | return ops->pi_get_voltage(pcdev, id); |
290 | } |
291 | |
292 | static int pse_pi_get_voltage(struct regulator_dev *rdev) |
293 | { |
294 | struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); |
295 | int ret; |
296 | |
297 | mutex_lock(&pcdev->lock); |
298 | ret = _pse_pi_get_voltage(rdev); |
299 | mutex_unlock(lock: &pcdev->lock); |
300 | |
301 | return ret; |
302 | } |
303 | |
304 | static int pse_pi_get_current_limit(struct regulator_dev *rdev) |
305 | { |
306 | struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); |
307 | const struct pse_controller_ops *ops; |
308 | int id, uV, mW, ret; |
309 | s64 tmp_64; |
310 | |
311 | ops = pcdev->ops; |
312 | id = rdev_get_id(rdev); |
313 | if (!ops->pi_get_pw_limit || !ops->pi_get_voltage) |
314 | return -EOPNOTSUPP; |
315 | |
316 | mutex_lock(&pcdev->lock); |
317 | ret = ops->pi_get_pw_limit(pcdev, id); |
318 | if (ret < 0) |
319 | goto out; |
320 | mW = ret; |
321 | |
322 | ret = _pse_pi_get_voltage(rdev); |
323 | if (!ret) { |
324 | dev_err(pcdev->dev, "Voltage null\n"); |
325 | ret = -ERANGE; |
326 | goto out; |
327 | } |
328 | if (ret < 0) |
329 | goto out; |
330 | uV = ret; |
331 | |
332 | tmp_64 = mW; |
333 | tmp_64 *= 1000000000ull; |
334 | /* uA = mW * 1000000000 / uV */ |
335 | ret = DIV_ROUND_CLOSEST_ULL(tmp_64, uV); |
336 | |
337 | out: |
338 | mutex_unlock(lock: &pcdev->lock); |
339 | return ret; |
340 | } |
341 | |
342 | static int pse_pi_set_current_limit(struct regulator_dev *rdev, int min_uA, |
343 | int max_uA) |
344 | { |
345 | struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); |
346 | const struct pse_controller_ops *ops; |
347 | int id, mW, ret; |
348 | s64 tmp_64; |
349 | |
350 | ops = pcdev->ops; |
351 | if (!ops->pi_set_pw_limit || !ops->pi_get_voltage) |
352 | return -EOPNOTSUPP; |
353 | |
354 | if (max_uA > MAX_PI_CURRENT) |
355 | return -ERANGE; |
356 | |
357 | id = rdev_get_id(rdev); |
358 | mutex_lock(&pcdev->lock); |
359 | ret = _pse_pi_get_voltage(rdev); |
360 | if (!ret) { |
361 | dev_err(pcdev->dev, "Voltage null\n"); |
362 | ret = -ERANGE; |
363 | goto out; |
364 | } |
365 | if (ret < 0) |
366 | goto out; |
367 | |
368 | tmp_64 = ret; |
369 | tmp_64 *= max_uA; |
370 | /* mW = uA * uV / 1000000000 */ |
371 | mW = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000); |
372 | ret = ops->pi_set_pw_limit(pcdev, id, mW); |
373 | out: |
374 | mutex_unlock(lock: &pcdev->lock); |
375 | |
376 | return ret; |
377 | } |
378 | |
379 | static const struct regulator_ops pse_pi_ops = { |
380 | .is_enabled = pse_pi_is_enabled, |
381 | .enable = pse_pi_enable, |
382 | .disable = pse_pi_disable, |
383 | .get_voltage = pse_pi_get_voltage, |
384 | .get_current_limit = pse_pi_get_current_limit, |
385 | .set_current_limit = pse_pi_set_current_limit, |
386 | }; |
387 | |
388 | static int |
389 | devm_pse_pi_regulator_register(struct pse_controller_dev *pcdev, |
390 | char *name, int id) |
391 | { |
392 | struct regulator_init_data *rinit_data; |
393 | struct regulator_config rconfig = {0}; |
394 | struct regulator_desc *rdesc; |
395 | struct regulator_dev *rdev; |
396 | |
397 | rinit_data = devm_kzalloc(dev: pcdev->dev, size: sizeof(*rinit_data), |
398 | GFP_KERNEL); |
399 | if (!rinit_data) |
400 | return -ENOMEM; |
401 | |
402 | rdesc = devm_kzalloc(dev: pcdev->dev, size: sizeof(*rdesc), GFP_KERNEL); |
403 | if (!rdesc) |
404 | return -ENOMEM; |
405 | |
406 | /* Regulator descriptor id have to be the same as its associated |
407 | * PSE PI id for the well functioning of the PSE controls. |
408 | */ |
409 | rdesc->id = id; |
410 | rdesc->name = name; |
411 | rdesc->type = REGULATOR_VOLTAGE; |
412 | rdesc->ops = &pse_pi_ops; |
413 | rdesc->owner = pcdev->owner; |
414 | |
415 | rinit_data->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS; |
416 | |
417 | if (pcdev->ops->pi_set_pw_limit) |
418 | rinit_data->constraints.valid_ops_mask |= |
419 | REGULATOR_CHANGE_CURRENT; |
420 | |
421 | rinit_data->supply_regulator = "vpwr"; |
422 | |
423 | rconfig.dev = pcdev->dev; |
424 | rconfig.driver_data = pcdev; |
425 | rconfig.init_data = rinit_data; |
426 | rconfig.of_node = pcdev->pi[id].np; |
427 | |
428 | rdev = devm_regulator_register(dev: pcdev->dev, regulator_desc: rdesc, config: &rconfig); |
429 | if (IS_ERR(ptr: rdev)) { |
430 | dev_err_probe(dev: pcdev->dev, err: PTR_ERR(ptr: rdev), |
431 | fmt: "Failed to register regulator\n"); |
432 | return PTR_ERR(ptr: rdev); |
433 | } |
434 | |
435 | pcdev->pi[id].rdev = rdev; |
436 | |
437 | return 0; |
438 | } |
439 | |
440 | /** |
441 | * pse_controller_register - register a PSE controller device |
442 | * @pcdev: a pointer to the initialized PSE controller device |
443 | * |
444 | * Return: 0 on success and failure value on error |
445 | */ |
446 | int pse_controller_register(struct pse_controller_dev *pcdev) |
447 | { |
448 | size_t reg_name_len; |
449 | int ret, i; |
450 | |
451 | mutex_init(&pcdev->lock); |
452 | INIT_LIST_HEAD(list: &pcdev->pse_control_head); |
453 | |
454 | if (!pcdev->nr_lines) |
455 | pcdev->nr_lines = 1; |
456 | |
457 | if (!pcdev->ops->pi_get_admin_state || |
458 | !pcdev->ops->pi_get_pw_status) { |
459 | dev_err(pcdev->dev, |
460 | "Mandatory status report callbacks are missing"); |
461 | return -EINVAL; |
462 | } |
463 | |
464 | ret = of_load_pse_pis(pcdev); |
465 | if (ret) |
466 | return ret; |
467 | |
468 | if (pcdev->ops->setup_pi_matrix) { |
469 | ret = pcdev->ops->setup_pi_matrix(pcdev); |
470 | if (ret) |
471 | return ret; |
472 | } |
473 | |
474 | /* Each regulator name len is pcdev dev name + 7 char + |
475 | * int max digit number (10) + 1 |
476 | */ |
477 | reg_name_len = strlen(dev_name(pcdev->dev)) + 18; |
478 | |
479 | /* Register PI regulators */ |
480 | for (i = 0; i < pcdev->nr_lines; i++) { |
481 | char *reg_name; |
482 | |
483 | /* Do not register regulator for PIs not described */ |
484 | if (!pcdev->no_of_pse_pi && !pcdev->pi[i].np) |
485 | continue; |
486 | |
487 | reg_name = devm_kzalloc(dev: pcdev->dev, size: reg_name_len, GFP_KERNEL); |
488 | if (!reg_name) |
489 | return -ENOMEM; |
490 | |
491 | snprintf(buf: reg_name, size: reg_name_len, fmt: "pse-%s_pi%d", |
492 | dev_name(dev: pcdev->dev), i); |
493 | |
494 | ret = devm_pse_pi_regulator_register(pcdev, name: reg_name, id: i); |
495 | if (ret) |
496 | return ret; |
497 | } |
498 | |
499 | mutex_lock(&pse_list_mutex); |
500 | list_add(new: &pcdev->list, head: &pse_controller_list); |
501 | mutex_unlock(lock: &pse_list_mutex); |
502 | |
503 | return 0; |
504 | } |
505 | EXPORT_SYMBOL_GPL(pse_controller_register); |
506 | |
507 | /** |
508 | * pse_controller_unregister - unregister a PSE controller device |
509 | * @pcdev: a pointer to the PSE controller device |
510 | */ |
511 | void pse_controller_unregister(struct pse_controller_dev *pcdev) |
512 | { |
513 | pse_release_pis(pcdev); |
514 | mutex_lock(&pse_list_mutex); |
515 | list_del(entry: &pcdev->list); |
516 | mutex_unlock(lock: &pse_list_mutex); |
517 | } |
518 | EXPORT_SYMBOL_GPL(pse_controller_unregister); |
519 | |
520 | static void devm_pse_controller_release(struct device *dev, void *res) |
521 | { |
522 | pse_controller_unregister(*(struct pse_controller_dev **)res); |
523 | } |
524 | |
525 | /** |
526 | * devm_pse_controller_register - resource managed pse_controller_register() |
527 | * @dev: device that is registering this PSE controller |
528 | * @pcdev: a pointer to the initialized PSE controller device |
529 | * |
530 | * Managed pse_controller_register(). For PSE controllers registered by |
531 | * this function, pse_controller_unregister() is automatically called on |
532 | * driver detach. See pse_controller_register() for more information. |
533 | * |
534 | * Return: 0 on success and failure value on error |
535 | */ |
536 | int devm_pse_controller_register(struct device *dev, |
537 | struct pse_controller_dev *pcdev) |
538 | { |
539 | struct pse_controller_dev **pcdevp; |
540 | int ret; |
541 | |
542 | pcdevp = devres_alloc(devm_pse_controller_release, sizeof(*pcdevp), |
543 | GFP_KERNEL); |
544 | if (!pcdevp) |
545 | return -ENOMEM; |
546 | |
547 | ret = pse_controller_register(pcdev); |
548 | if (ret) { |
549 | devres_free(res: pcdevp); |
550 | return ret; |
551 | } |
552 | |
553 | *pcdevp = pcdev; |
554 | devres_add(dev, res: pcdevp); |
555 | |
556 | return 0; |
557 | } |
558 | EXPORT_SYMBOL_GPL(devm_pse_controller_register); |
559 | |
560 | /* PSE control section */ |
561 | |
562 | static void __pse_control_release(struct kref *kref) |
563 | { |
564 | struct pse_control *psec = container_of(kref, struct pse_control, |
565 | refcnt); |
566 | |
567 | lockdep_assert_held(&pse_list_mutex); |
568 | |
569 | if (psec->pcdev->pi[psec->id].admin_state_enabled) |
570 | regulator_disable(regulator: psec->ps); |
571 | devm_regulator_put(regulator: psec->ps); |
572 | |
573 | module_put(module: psec->pcdev->owner); |
574 | |
575 | list_del(entry: &psec->list); |
576 | kfree(objp: psec); |
577 | } |
578 | |
579 | static void __pse_control_put_internal(struct pse_control *psec) |
580 | { |
581 | lockdep_assert_held(&pse_list_mutex); |
582 | |
583 | kref_put(kref: &psec->refcnt, release: __pse_control_release); |
584 | } |
585 | |
586 | /** |
587 | * pse_control_put - free the PSE control |
588 | * @psec: PSE control pointer |
589 | */ |
590 | void pse_control_put(struct pse_control *psec) |
591 | { |
592 | if (IS_ERR_OR_NULL(ptr: psec)) |
593 | return; |
594 | |
595 | mutex_lock(&pse_list_mutex); |
596 | __pse_control_put_internal(psec); |
597 | mutex_unlock(lock: &pse_list_mutex); |
598 | } |
599 | EXPORT_SYMBOL_GPL(pse_control_put); |
600 | |
601 | static struct pse_control * |
602 | pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index) |
603 | { |
604 | struct pse_control *psec; |
605 | int ret; |
606 | |
607 | lockdep_assert_held(&pse_list_mutex); |
608 | |
609 | list_for_each_entry(psec, &pcdev->pse_control_head, list) { |
610 | if (psec->id == index) { |
611 | kref_get(kref: &psec->refcnt); |
612 | return psec; |
613 | } |
614 | } |
615 | |
616 | psec = kzalloc(sizeof(*psec), GFP_KERNEL); |
617 | if (!psec) |
618 | return ERR_PTR(error: -ENOMEM); |
619 | |
620 | if (!try_module_get(module: pcdev->owner)) { |
621 | ret = -ENODEV; |
622 | goto free_psec; |
623 | } |
624 | |
625 | psec->ps = devm_regulator_get_exclusive(dev: pcdev->dev, |
626 | id: rdev_get_name(rdev: pcdev->pi[index].rdev)); |
627 | if (IS_ERR(ptr: psec->ps)) { |
628 | ret = PTR_ERR(ptr: psec->ps); |
629 | goto put_module; |
630 | } |
631 | |
632 | ret = regulator_is_enabled(regulator: psec->ps); |
633 | if (ret < 0) |
634 | goto regulator_put; |
635 | |
636 | pcdev->pi[index].admin_state_enabled = ret; |
637 | |
638 | psec->pcdev = pcdev; |
639 | list_add(new: &psec->list, head: &pcdev->pse_control_head); |
640 | psec->id = index; |
641 | kref_init(kref: &psec->refcnt); |
642 | |
643 | return psec; |
644 | |
645 | regulator_put: |
646 | devm_regulator_put(regulator: psec->ps); |
647 | put_module: |
648 | module_put(module: pcdev->owner); |
649 | free_psec: |
650 | kfree(objp: psec); |
651 | |
652 | return ERR_PTR(error: ret); |
653 | } |
654 | |
655 | /** |
656 | * of_pse_match_pi - Find the PSE PI id matching the device node phandle |
657 | * @pcdev: a pointer to the PSE controller device |
658 | * @np: a pointer to the device node |
659 | * |
660 | * Return: id of the PSE PI, -EINVAL if not found |
661 | */ |
662 | static int of_pse_match_pi(struct pse_controller_dev *pcdev, |
663 | struct device_node *np) |
664 | { |
665 | int i; |
666 | |
667 | for (i = 0; i < pcdev->nr_lines; i++) { |
668 | if (pcdev->pi[i].np == np) |
669 | return i; |
670 | } |
671 | |
672 | return -EINVAL; |
673 | } |
674 | |
675 | /** |
676 | * psec_id_xlate - translate pse_spec to the PSE line number according |
677 | * to the number of pse-cells in case of no pse_pi node |
678 | * @pcdev: a pointer to the PSE controller device |
679 | * @pse_spec: PSE line specifier as found in the device tree |
680 | * |
681 | * Return: 0 if #pse-cells = <0>. Return PSE line number otherwise. |
682 | */ |
683 | static int psec_id_xlate(struct pse_controller_dev *pcdev, |
684 | const struct of_phandle_args *pse_spec) |
685 | { |
686 | if (!pcdev->of_pse_n_cells) |
687 | return 0; |
688 | |
689 | if (pcdev->of_pse_n_cells > 1 || |
690 | pse_spec->args[0] >= pcdev->nr_lines) |
691 | return -EINVAL; |
692 | |
693 | return pse_spec->args[0]; |
694 | } |
695 | |
696 | struct pse_control *of_pse_control_get(struct device_node *node) |
697 | { |
698 | struct pse_controller_dev *r, *pcdev; |
699 | struct of_phandle_args args; |
700 | struct pse_control *psec; |
701 | int psec_id; |
702 | int ret; |
703 | |
704 | if (!node) |
705 | return ERR_PTR(error: -EINVAL); |
706 | |
707 | ret = of_parse_phandle_with_args(np: node, list_name: "pses", cells_name: "#pse-cells", index: 0, out_args: &args); |
708 | if (ret) |
709 | return ERR_PTR(error: ret); |
710 | |
711 | mutex_lock(&pse_list_mutex); |
712 | pcdev = NULL; |
713 | list_for_each_entry(r, &pse_controller_list, list) { |
714 | if (!r->no_of_pse_pi) { |
715 | ret = of_pse_match_pi(pcdev: r, np: args.np); |
716 | if (ret >= 0) { |
717 | pcdev = r; |
718 | psec_id = ret; |
719 | break; |
720 | } |
721 | } else if (args.np == r->dev->of_node) { |
722 | pcdev = r; |
723 | break; |
724 | } |
725 | } |
726 | |
727 | if (!pcdev) { |
728 | psec = ERR_PTR(error: -EPROBE_DEFER); |
729 | goto out; |
730 | } |
731 | |
732 | if (WARN_ON(args.args_count != pcdev->of_pse_n_cells)) { |
733 | psec = ERR_PTR(error: -EINVAL); |
734 | goto out; |
735 | } |
736 | |
737 | if (pcdev->no_of_pse_pi) { |
738 | psec_id = psec_id_xlate(pcdev, pse_spec: &args); |
739 | if (psec_id < 0) { |
740 | psec = ERR_PTR(error: psec_id); |
741 | goto out; |
742 | } |
743 | } |
744 | |
745 | /* pse_list_mutex also protects the pcdev's pse_control list */ |
746 | psec = pse_control_get_internal(pcdev, index: psec_id); |
747 | |
748 | out: |
749 | mutex_unlock(lock: &pse_list_mutex); |
750 | of_node_put(node: args.np); |
751 | |
752 | return psec; |
753 | } |
754 | EXPORT_SYMBOL_GPL(of_pse_control_get); |
755 | |
756 | /** |
757 | * pse_ethtool_get_status - get status of PSE control |
758 | * @psec: PSE control pointer |
759 | * @extack: extack for reporting useful error messages |
760 | * @status: struct to store PSE status |
761 | * |
762 | * Return: 0 on success and failure value on error |
763 | */ |
764 | int pse_ethtool_get_status(struct pse_control *psec, |
765 | struct netlink_ext_ack *extack, |
766 | struct ethtool_pse_control_status *status) |
767 | { |
768 | struct pse_admin_state admin_state = {0}; |
769 | struct pse_pw_status pw_status = {0}; |
770 | const struct pse_controller_ops *ops; |
771 | struct pse_controller_dev *pcdev; |
772 | int ret; |
773 | |
774 | pcdev = psec->pcdev; |
775 | ops = pcdev->ops; |
776 | mutex_lock(&pcdev->lock); |
777 | ret = ops->pi_get_admin_state(pcdev, psec->id, &admin_state); |
778 | if (ret) |
779 | goto out; |
780 | status->podl_admin_state = admin_state.podl_admin_state; |
781 | status->c33_admin_state = admin_state.c33_admin_state; |
782 | |
783 | ret = ops->pi_get_pw_status(pcdev, psec->id, &pw_status); |
784 | if (ret) |
785 | goto out; |
786 | status->podl_pw_status = pw_status.podl_pw_status; |
787 | status->c33_pw_status = pw_status.c33_pw_status; |
788 | |
789 | if (ops->pi_get_ext_state) { |
790 | struct pse_ext_state_info ext_state_info = {0}; |
791 | |
792 | ret = ops->pi_get_ext_state(pcdev, psec->id, |
793 | &ext_state_info); |
794 | if (ret) |
795 | goto out; |
796 | |
797 | memcpy(&status->c33_ext_state_info, |
798 | &ext_state_info.c33_ext_state_info, |
799 | sizeof(status->c33_ext_state_info)); |
800 | } |
801 | |
802 | if (ops->pi_get_pw_class) { |
803 | ret = ops->pi_get_pw_class(pcdev, psec->id); |
804 | if (ret < 0) |
805 | goto out; |
806 | |
807 | status->c33_pw_class = ret; |
808 | } |
809 | |
810 | if (ops->pi_get_actual_pw) { |
811 | ret = ops->pi_get_actual_pw(pcdev, psec->id); |
812 | if (ret < 0) |
813 | goto out; |
814 | |
815 | status->c33_actual_pw = ret; |
816 | } |
817 | |
818 | if (ops->pi_get_pw_limit) { |
819 | ret = ops->pi_get_pw_limit(pcdev, psec->id); |
820 | if (ret < 0) |
821 | goto out; |
822 | |
823 | status->c33_avail_pw_limit = ret; |
824 | } |
825 | |
826 | if (ops->pi_get_pw_limit_ranges) { |
827 | struct pse_pw_limit_ranges pw_limit_ranges = {0}; |
828 | |
829 | ret = ops->pi_get_pw_limit_ranges(pcdev, psec->id, |
830 | &pw_limit_ranges); |
831 | if (ret < 0) |
832 | goto out; |
833 | |
834 | status->c33_pw_limit_ranges = |
835 | pw_limit_ranges.c33_pw_limit_ranges; |
836 | status->c33_pw_limit_nb_ranges = ret; |
837 | } |
838 | out: |
839 | mutex_unlock(lock: &psec->pcdev->lock); |
840 | return ret; |
841 | } |
842 | EXPORT_SYMBOL_GPL(pse_ethtool_get_status); |
843 | |
844 | static int pse_ethtool_c33_set_config(struct pse_control *psec, |
845 | const struct pse_control_config *config) |
846 | { |
847 | int err = 0; |
848 | |
849 | /* Look at admin_state_enabled status to not call regulator_enable |
850 | * or regulator_disable twice creating a regulator counter mismatch |
851 | */ |
852 | switch (config->c33_admin_control) { |
853 | case ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED: |
854 | /* We could have mismatch between admin_state_enabled and |
855 | * state reported by regulator_is_enabled. This can occur when |
856 | * the PI is forcibly turn off by the controller. Call |
857 | * regulator_disable on that case to fix the counters state. |
858 | */ |
859 | if (psec->pcdev->pi[psec->id].admin_state_enabled && |
860 | !regulator_is_enabled(regulator: psec->ps)) { |
861 | err = regulator_disable(regulator: psec->ps); |
862 | if (err) |
863 | break; |
864 | } |
865 | if (!psec->pcdev->pi[psec->id].admin_state_enabled) |
866 | err = regulator_enable(regulator: psec->ps); |
867 | break; |
868 | case ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED: |
869 | if (psec->pcdev->pi[psec->id].admin_state_enabled) |
870 | err = regulator_disable(regulator: psec->ps); |
871 | break; |
872 | default: |
873 | err = -EOPNOTSUPP; |
874 | } |
875 | |
876 | return err; |
877 | } |
878 | |
879 | static int pse_ethtool_podl_set_config(struct pse_control *psec, |
880 | const struct pse_control_config *config) |
881 | { |
882 | int err = 0; |
883 | |
884 | /* Look at admin_state_enabled status to not call regulator_enable |
885 | * or regulator_disable twice creating a regulator counter mismatch |
886 | */ |
887 | switch (config->podl_admin_control) { |
888 | case ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: |
889 | if (!psec->pcdev->pi[psec->id].admin_state_enabled) |
890 | err = regulator_enable(regulator: psec->ps); |
891 | break; |
892 | case ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: |
893 | if (psec->pcdev->pi[psec->id].admin_state_enabled) |
894 | err = regulator_disable(regulator: psec->ps); |
895 | break; |
896 | default: |
897 | err = -EOPNOTSUPP; |
898 | } |
899 | |
900 | return err; |
901 | } |
902 | |
903 | /** |
904 | * pse_ethtool_set_config - set PSE control configuration |
905 | * @psec: PSE control pointer |
906 | * @extack: extack for reporting useful error messages |
907 | * @config: Configuration of the test to run |
908 | * |
909 | * Return: 0 on success and failure value on error |
910 | */ |
911 | int pse_ethtool_set_config(struct pse_control *psec, |
912 | struct netlink_ext_ack *extack, |
913 | const struct pse_control_config *config) |
914 | { |
915 | int err = 0; |
916 | |
917 | if (pse_has_c33(psec) && config->c33_admin_control) { |
918 | err = pse_ethtool_c33_set_config(psec, config); |
919 | if (err) |
920 | return err; |
921 | } |
922 | |
923 | if (pse_has_podl(psec) && config->podl_admin_control) |
924 | err = pse_ethtool_podl_set_config(psec, config); |
925 | |
926 | return err; |
927 | } |
928 | EXPORT_SYMBOL_GPL(pse_ethtool_set_config); |
929 | |
930 | /** |
931 | * pse_ethtool_set_pw_limit - set PSE control power limit |
932 | * @psec: PSE control pointer |
933 | * @extack: extack for reporting useful error messages |
934 | * @pw_limit: power limit value in mW |
935 | * |
936 | * Return: 0 on success and failure value on error |
937 | */ |
938 | int pse_ethtool_set_pw_limit(struct pse_control *psec, |
939 | struct netlink_ext_ack *extack, |
940 | const unsigned int pw_limit) |
941 | { |
942 | int uV, uA, ret; |
943 | s64 tmp_64; |
944 | |
945 | if (pw_limit > MAX_PI_PW) |
946 | return -ERANGE; |
947 | |
948 | ret = regulator_get_voltage(regulator: psec->ps); |
949 | if (!ret) { |
950 | NL_SET_ERR_MSG(extack, |
951 | "Can't calculate the current, PSE voltage read is 0"); |
952 | return -ERANGE; |
953 | } |
954 | if (ret < 0) { |
955 | NL_SET_ERR_MSG(extack, |
956 | "Error reading PSE voltage"); |
957 | return ret; |
958 | } |
959 | uV = ret; |
960 | |
961 | tmp_64 = pw_limit; |
962 | tmp_64 *= 1000000000ull; |
963 | /* uA = mW * 1000000000 / uV */ |
964 | uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV); |
965 | |
966 | return regulator_set_current_limit(regulator: psec->ps, min_uA: 0, max_uA: uA); |
967 | } |
968 | EXPORT_SYMBOL_GPL(pse_ethtool_set_pw_limit); |
969 | |
970 | bool pse_has_podl(struct pse_control *psec) |
971 | { |
972 | return psec->pcdev->types & ETHTOOL_PSE_PODL; |
973 | } |
974 | EXPORT_SYMBOL_GPL(pse_has_podl); |
975 | |
976 | bool pse_has_c33(struct pse_control *psec) |
977 | { |
978 | return psec->pcdev->types & ETHTOOL_PSE_C33; |
979 | } |
980 | EXPORT_SYMBOL_GPL(pse_has_c33); |
981 |
Definitions
- pse_list_mutex
- pse_controller_list
- pse_control
- of_load_single_pse_pi_pairset
- of_load_pse_pi_pairsets
- pse_release_pis
- of_load_pse_pis
- pse_pi_is_enabled
- pse_pi_enable
- pse_pi_disable
- _pse_pi_get_voltage
- pse_pi_get_voltage
- pse_pi_get_current_limit
- pse_pi_set_current_limit
- pse_pi_ops
- devm_pse_pi_regulator_register
- pse_controller_register
- pse_controller_unregister
- devm_pse_controller_release
- devm_pse_controller_register
- __pse_control_release
- __pse_control_put_internal
- pse_control_put
- pse_control_get_internal
- of_pse_match_pi
- psec_id_xlate
- of_pse_control_get
- pse_ethtool_get_status
- pse_ethtool_c33_set_config
- pse_ethtool_podl_set_config
- pse_ethtool_set_config
- pse_ethtool_set_pw_limit
- pse_has_podl
Improve your Profiling and Debugging skills
Find out more