1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * USB Power Delivery sysfs entries |
4 | * |
5 | * Copyright (C) 2022, Intel Corporation |
6 | * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> |
7 | */ |
8 | |
9 | #include <linux/slab.h> |
10 | #include <linux/usb/pd.h> |
11 | |
12 | #include "pd.h" |
13 | |
14 | static DEFINE_IDA(pd_ida); |
15 | |
16 | static struct class pd_class = { |
17 | .name = "usb_power_delivery" , |
18 | }; |
19 | |
20 | #define to_pdo(o) container_of(o, struct pdo, dev) |
21 | |
22 | struct pdo { |
23 | struct device dev; |
24 | int object_position; |
25 | u32 pdo; |
26 | }; |
27 | |
28 | static void pdo_release(struct device *dev) |
29 | { |
30 | kfree(to_pdo(dev)); |
31 | } |
32 | |
33 | /* -------------------------------------------------------------------------- */ |
34 | /* Fixed Supply */ |
35 | |
36 | static ssize_t |
37 | dual_role_power_show(struct device *dev, struct device_attribute *attr, char *buf) |
38 | { |
39 | return sysfs_emit(buf, fmt: "%u\n" , !!(to_pdo(dev)->pdo & PDO_FIXED_DUAL_ROLE)); |
40 | } |
41 | static DEVICE_ATTR_RO(dual_role_power); |
42 | |
43 | static ssize_t |
44 | usb_suspend_supported_show(struct device *dev, struct device_attribute *attr, char *buf) |
45 | { |
46 | return sysfs_emit(buf, fmt: "%u\n" , !!(to_pdo(dev)->pdo & PDO_FIXED_SUSPEND)); |
47 | } |
48 | static DEVICE_ATTR_RO(usb_suspend_supported); |
49 | |
50 | static ssize_t |
51 | higher_capability_show(struct device *dev, struct device_attribute *attr, char *buf) |
52 | { |
53 | return sysfs_emit(buf, fmt: "%u\n" , !!(to_pdo(dev)->pdo & PDO_FIXED_HIGHER_CAP)); |
54 | } |
55 | static DEVICE_ATTR_RO(higher_capability); |
56 | |
57 | static ssize_t |
58 | unconstrained_power_show(struct device *dev, struct device_attribute *attr, char *buf) |
59 | { |
60 | return sysfs_emit(buf, fmt: "%u\n" , !!(to_pdo(dev)->pdo & PDO_FIXED_EXTPOWER)); |
61 | } |
62 | static DEVICE_ATTR_RO(unconstrained_power); |
63 | |
64 | static ssize_t |
65 | usb_communication_capable_show(struct device *dev, struct device_attribute *attr, char *buf) |
66 | { |
67 | return sysfs_emit(buf, fmt: "%u\n" , !!(to_pdo(dev)->pdo & PDO_FIXED_USB_COMM)); |
68 | } |
69 | static DEVICE_ATTR_RO(usb_communication_capable); |
70 | |
71 | static ssize_t |
72 | dual_role_data_show(struct device *dev, struct device_attribute *attr, char *buf) |
73 | { |
74 | return sysfs_emit(buf, fmt: "%u\n" , !!(to_pdo(dev)->pdo & PDO_FIXED_DATA_SWAP)); |
75 | } |
76 | static DEVICE_ATTR_RO(dual_role_data); |
77 | |
78 | static ssize_t |
79 | unchunked_extended_messages_supported_show(struct device *dev, |
80 | struct device_attribute *attr, char *buf) |
81 | { |
82 | return sysfs_emit(buf, fmt: "%u\n" , !!(to_pdo(dev)->pdo & PDO_FIXED_UNCHUNK_EXT)); |
83 | } |
84 | static DEVICE_ATTR_RO(unchunked_extended_messages_supported); |
85 | |
86 | static ssize_t |
87 | peak_current_show(struct device *dev, struct device_attribute *attr, char *buf) |
88 | { |
89 | return sysfs_emit(buf, fmt: "%u\n" , (to_pdo(dev)->pdo >> PDO_FIXED_PEAK_CURR_SHIFT) & 3); |
90 | } |
91 | static DEVICE_ATTR_RO(peak_current); |
92 | |
93 | static ssize_t |
94 | fast_role_swap_current_show(struct device *dev, struct device_attribute *attr, char *buf) |
95 | { |
96 | return sysfs_emit(buf, fmt: "%u\n" , (to_pdo(dev)->pdo >> PDO_FIXED_FRS_CURR_SHIFT) & 3); |
97 | } |
98 | static DEVICE_ATTR_RO(fast_role_swap_current); |
99 | |
100 | static ssize_t voltage_show(struct device *dev, struct device_attribute *attr, char *buf) |
101 | { |
102 | return sysfs_emit(buf, fmt: "%umV\n" , pdo_fixed_voltage(to_pdo(dev)->pdo)); |
103 | } |
104 | static DEVICE_ATTR_RO(voltage); |
105 | |
106 | /* Shared with Variable supplies, both source and sink */ |
107 | static ssize_t current_show(struct device *dev, struct device_attribute *attr, char *buf) |
108 | { |
109 | return sysfs_emit(buf, fmt: "%umA\n" , pdo_max_current(to_pdo(dev)->pdo)); |
110 | } |
111 | |
112 | /* Shared with Variable type supplies */ |
113 | static struct device_attribute maximum_current_attr = { |
114 | .attr = { |
115 | .name = "maximum_current" , |
116 | .mode = 0444, |
117 | }, |
118 | .show = current_show, |
119 | }; |
120 | |
121 | static struct device_attribute operational_current_attr = { |
122 | .attr = { |
123 | .name = "operational_current" , |
124 | .mode = 0444, |
125 | }, |
126 | .show = current_show, |
127 | }; |
128 | |
129 | static struct attribute *source_fixed_supply_attrs[] = { |
130 | &dev_attr_dual_role_power.attr, |
131 | &dev_attr_usb_suspend_supported.attr, |
132 | &dev_attr_unconstrained_power.attr, |
133 | &dev_attr_usb_communication_capable.attr, |
134 | &dev_attr_dual_role_data.attr, |
135 | &dev_attr_unchunked_extended_messages_supported.attr, |
136 | &dev_attr_peak_current.attr, |
137 | &dev_attr_voltage.attr, |
138 | &maximum_current_attr.attr, |
139 | NULL |
140 | }; |
141 | |
142 | static umode_t fixed_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) |
143 | { |
144 | if (to_pdo(kobj_to_dev(kobj))->object_position && |
145 | attr != &dev_attr_peak_current.attr && |
146 | attr != &dev_attr_voltage.attr && |
147 | attr != &maximum_current_attr.attr && |
148 | attr != &operational_current_attr.attr) |
149 | return 0; |
150 | |
151 | return attr->mode; |
152 | } |
153 | |
154 | static const struct attribute_group source_fixed_supply_group = { |
155 | .is_visible = fixed_attr_is_visible, |
156 | .attrs = source_fixed_supply_attrs, |
157 | }; |
158 | __ATTRIBUTE_GROUPS(source_fixed_supply); |
159 | |
160 | static const struct device_type source_fixed_supply_type = { |
161 | .name = "pdo" , |
162 | .release = pdo_release, |
163 | .groups = source_fixed_supply_groups, |
164 | }; |
165 | |
166 | static struct attribute *sink_fixed_supply_attrs[] = { |
167 | &dev_attr_dual_role_power.attr, |
168 | &dev_attr_higher_capability.attr, |
169 | &dev_attr_unconstrained_power.attr, |
170 | &dev_attr_usb_communication_capable.attr, |
171 | &dev_attr_dual_role_data.attr, |
172 | &dev_attr_unchunked_extended_messages_supported.attr, |
173 | &dev_attr_fast_role_swap_current.attr, |
174 | &dev_attr_voltage.attr, |
175 | &operational_current_attr.attr, |
176 | NULL |
177 | }; |
178 | |
179 | static const struct attribute_group sink_fixed_supply_group = { |
180 | .is_visible = fixed_attr_is_visible, |
181 | .attrs = sink_fixed_supply_attrs, |
182 | }; |
183 | __ATTRIBUTE_GROUPS(sink_fixed_supply); |
184 | |
185 | static const struct device_type sink_fixed_supply_type = { |
186 | .name = "pdo" , |
187 | .release = pdo_release, |
188 | .groups = sink_fixed_supply_groups, |
189 | }; |
190 | |
191 | /* -------------------------------------------------------------------------- */ |
192 | /* Variable Supply */ |
193 | |
194 | static ssize_t |
195 | maximum_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) |
196 | { |
197 | return sysfs_emit(buf, fmt: "%umV\n" , pdo_max_voltage(to_pdo(dev)->pdo)); |
198 | } |
199 | static DEVICE_ATTR_RO(maximum_voltage); |
200 | |
201 | static ssize_t |
202 | minimum_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) |
203 | { |
204 | return sysfs_emit(buf, fmt: "%umV\n" , pdo_min_voltage(to_pdo(dev)->pdo)); |
205 | } |
206 | static DEVICE_ATTR_RO(minimum_voltage); |
207 | |
208 | static struct attribute *source_variable_supply_attrs[] = { |
209 | &dev_attr_maximum_voltage.attr, |
210 | &dev_attr_minimum_voltage.attr, |
211 | &maximum_current_attr.attr, |
212 | NULL |
213 | }; |
214 | ATTRIBUTE_GROUPS(source_variable_supply); |
215 | |
216 | static const struct device_type source_variable_supply_type = { |
217 | .name = "pdo" , |
218 | .release = pdo_release, |
219 | .groups = source_variable_supply_groups, |
220 | }; |
221 | |
222 | static struct attribute *sink_variable_supply_attrs[] = { |
223 | &dev_attr_maximum_voltage.attr, |
224 | &dev_attr_minimum_voltage.attr, |
225 | &operational_current_attr.attr, |
226 | NULL |
227 | }; |
228 | ATTRIBUTE_GROUPS(sink_variable_supply); |
229 | |
230 | static const struct device_type sink_variable_supply_type = { |
231 | .name = "pdo" , |
232 | .release = pdo_release, |
233 | .groups = sink_variable_supply_groups, |
234 | }; |
235 | |
236 | /* -------------------------------------------------------------------------- */ |
237 | /* Battery */ |
238 | |
239 | static ssize_t |
240 | maximum_power_show(struct device *dev, struct device_attribute *attr, char *buf) |
241 | { |
242 | return sysfs_emit(buf, fmt: "%umW\n" , pdo_max_power(to_pdo(dev)->pdo)); |
243 | } |
244 | static DEVICE_ATTR_RO(maximum_power); |
245 | |
246 | static ssize_t |
247 | operational_power_show(struct device *dev, struct device_attribute *attr, char *buf) |
248 | { |
249 | return sysfs_emit(buf, fmt: "%umW\n" , pdo_max_power(to_pdo(dev)->pdo)); |
250 | } |
251 | static DEVICE_ATTR_RO(operational_power); |
252 | |
253 | static struct attribute *source_battery_attrs[] = { |
254 | &dev_attr_maximum_voltage.attr, |
255 | &dev_attr_minimum_voltage.attr, |
256 | &dev_attr_maximum_power.attr, |
257 | NULL |
258 | }; |
259 | ATTRIBUTE_GROUPS(source_battery); |
260 | |
261 | static const struct device_type source_battery_type = { |
262 | .name = "pdo" , |
263 | .release = pdo_release, |
264 | .groups = source_battery_groups, |
265 | }; |
266 | |
267 | static struct attribute *sink_battery_attrs[] = { |
268 | &dev_attr_maximum_voltage.attr, |
269 | &dev_attr_minimum_voltage.attr, |
270 | &dev_attr_operational_power.attr, |
271 | NULL |
272 | }; |
273 | ATTRIBUTE_GROUPS(sink_battery); |
274 | |
275 | static const struct device_type sink_battery_type = { |
276 | .name = "pdo" , |
277 | .release = pdo_release, |
278 | .groups = sink_battery_groups, |
279 | }; |
280 | |
281 | /* -------------------------------------------------------------------------- */ |
282 | /* Standard Power Range (SPR) Programmable Power Supply (PPS) */ |
283 | |
284 | static ssize_t |
285 | pps_power_limited_show(struct device *dev, struct device_attribute *attr, char *buf) |
286 | { |
287 | return sysfs_emit(buf, fmt: "%u\n" , !!(to_pdo(dev)->pdo & BIT(27))); |
288 | } |
289 | static DEVICE_ATTR_RO(pps_power_limited); |
290 | |
291 | static ssize_t |
292 | pps_max_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) |
293 | { |
294 | return sysfs_emit(buf, fmt: "%umV\n" , pdo_pps_apdo_max_voltage(to_pdo(dev)->pdo)); |
295 | } |
296 | |
297 | static ssize_t |
298 | pps_min_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) |
299 | { |
300 | return sysfs_emit(buf, fmt: "%umV\n" , pdo_pps_apdo_min_voltage(to_pdo(dev)->pdo)); |
301 | } |
302 | |
303 | static ssize_t |
304 | pps_max_current_show(struct device *dev, struct device_attribute *attr, char *buf) |
305 | { |
306 | return sysfs_emit(buf, fmt: "%umA\n" , pdo_pps_apdo_max_current(to_pdo(dev)->pdo)); |
307 | } |
308 | |
309 | static struct device_attribute pps_max_voltage_attr = { |
310 | .attr = { |
311 | .name = "maximum_voltage" , |
312 | .mode = 0444, |
313 | }, |
314 | .show = pps_max_voltage_show, |
315 | }; |
316 | |
317 | static struct device_attribute pps_min_voltage_attr = { |
318 | .attr = { |
319 | .name = "minimum_voltage" , |
320 | .mode = 0444, |
321 | }, |
322 | .show = pps_min_voltage_show, |
323 | }; |
324 | |
325 | static struct device_attribute pps_max_current_attr = { |
326 | .attr = { |
327 | .name = "maximum_current" , |
328 | .mode = 0444, |
329 | }, |
330 | .show = pps_max_current_show, |
331 | }; |
332 | |
333 | static struct attribute *source_pps_attrs[] = { |
334 | &dev_attr_pps_power_limited.attr, |
335 | &pps_max_voltage_attr.attr, |
336 | &pps_min_voltage_attr.attr, |
337 | &pps_max_current_attr.attr, |
338 | NULL |
339 | }; |
340 | ATTRIBUTE_GROUPS(source_pps); |
341 | |
342 | static const struct device_type source_pps_type = { |
343 | .name = "pdo" , |
344 | .release = pdo_release, |
345 | .groups = source_pps_groups, |
346 | }; |
347 | |
348 | static struct attribute *sink_pps_attrs[] = { |
349 | &pps_max_voltage_attr.attr, |
350 | &pps_min_voltage_attr.attr, |
351 | &pps_max_current_attr.attr, |
352 | NULL |
353 | }; |
354 | ATTRIBUTE_GROUPS(sink_pps); |
355 | |
356 | static const struct device_type sink_pps_type = { |
357 | .name = "pdo" , |
358 | .release = pdo_release, |
359 | .groups = sink_pps_groups, |
360 | }; |
361 | |
362 | /* -------------------------------------------------------------------------- */ |
363 | |
364 | static const char * const supply_name[] = { |
365 | [PDO_TYPE_FIXED] = "fixed_supply" , |
366 | [PDO_TYPE_BATT] = "battery" , |
367 | [PDO_TYPE_VAR] = "variable_supply" , |
368 | }; |
369 | |
370 | static const char * const apdo_supply_name[] = { |
371 | [APDO_TYPE_PPS] = "programmable_supply" , |
372 | }; |
373 | |
374 | static const struct device_type *source_type[] = { |
375 | [PDO_TYPE_FIXED] = &source_fixed_supply_type, |
376 | [PDO_TYPE_BATT] = &source_battery_type, |
377 | [PDO_TYPE_VAR] = &source_variable_supply_type, |
378 | }; |
379 | |
380 | static const struct device_type *source_apdo_type[] = { |
381 | [APDO_TYPE_PPS] = &source_pps_type, |
382 | }; |
383 | |
384 | static const struct device_type *sink_type[] = { |
385 | [PDO_TYPE_FIXED] = &sink_fixed_supply_type, |
386 | [PDO_TYPE_BATT] = &sink_battery_type, |
387 | [PDO_TYPE_VAR] = &sink_variable_supply_type, |
388 | }; |
389 | |
390 | static const struct device_type *sink_apdo_type[] = { |
391 | [APDO_TYPE_PPS] = &sink_pps_type, |
392 | }; |
393 | |
394 | /* REVISIT: Export when EPR_*_Capabilities need to be supported. */ |
395 | static int add_pdo(struct usb_power_delivery_capabilities *cap, u32 pdo, int position) |
396 | { |
397 | const struct device_type *type; |
398 | const char *name; |
399 | struct pdo *p; |
400 | int ret; |
401 | |
402 | p = kzalloc(size: sizeof(*p), GFP_KERNEL); |
403 | if (!p) |
404 | return -ENOMEM; |
405 | |
406 | p->pdo = pdo; |
407 | p->object_position = position; |
408 | |
409 | if (pdo_type(pdo) == PDO_TYPE_APDO) { |
410 | /* FIXME: Only PPS supported for now! Skipping others. */ |
411 | if (pdo_apdo_type(pdo) > APDO_TYPE_PPS) { |
412 | dev_warn(&cap->dev, "Unknown APDO type. PDO 0x%08x\n" , pdo); |
413 | kfree(objp: p); |
414 | return 0; |
415 | } |
416 | |
417 | if (is_source(role: cap->role)) |
418 | type = source_apdo_type[pdo_apdo_type(pdo)]; |
419 | else |
420 | type = sink_apdo_type[pdo_apdo_type(pdo)]; |
421 | |
422 | name = apdo_supply_name[pdo_apdo_type(pdo)]; |
423 | } else { |
424 | if (is_source(role: cap->role)) |
425 | type = source_type[pdo_type(pdo)]; |
426 | else |
427 | type = sink_type[pdo_type(pdo)]; |
428 | |
429 | name = supply_name[pdo_type(pdo)]; |
430 | } |
431 | |
432 | p->dev.parent = &cap->dev; |
433 | p->dev.type = type; |
434 | dev_set_name(dev: &p->dev, name: "%u:%s" , position + 1, name); |
435 | |
436 | ret = device_register(dev: &p->dev); |
437 | if (ret) { |
438 | put_device(dev: &p->dev); |
439 | return ret; |
440 | } |
441 | |
442 | return 0; |
443 | } |
444 | |
445 | static int remove_pdo(struct device *dev, void *data) |
446 | { |
447 | device_unregister(dev); |
448 | return 0; |
449 | } |
450 | |
451 | /* -------------------------------------------------------------------------- */ |
452 | |
453 | static const char * const cap_name[] = { |
454 | [TYPEC_SINK] = "sink-capabilities" , |
455 | [TYPEC_SOURCE] = "source-capabilities" , |
456 | }; |
457 | |
458 | static void pd_capabilities_release(struct device *dev) |
459 | { |
460 | kfree(to_usb_power_delivery_capabilities(dev)); |
461 | } |
462 | |
463 | static const struct device_type pd_capabilities_type = { |
464 | .name = "capabilities" , |
465 | .release = pd_capabilities_release, |
466 | }; |
467 | |
468 | /** |
469 | * usb_power_delivery_register_capabilities - Register a set of capabilities. |
470 | * @pd: The USB PD instance that the capabilities belong to. |
471 | * @desc: Description of the Capabilities Message. |
472 | * |
473 | * This function registers a Capabilities Message described in @desc. The |
474 | * capabilities will have their own sub-directory under @pd in sysfs. |
475 | * |
476 | * The function returns pointer to struct usb_power_delivery_capabilities, or |
477 | * ERR_PRT(errno). |
478 | */ |
479 | struct usb_power_delivery_capabilities * |
480 | usb_power_delivery_register_capabilities(struct usb_power_delivery *pd, |
481 | struct usb_power_delivery_capabilities_desc *desc) |
482 | { |
483 | struct usb_power_delivery_capabilities *cap; |
484 | int ret; |
485 | int i; |
486 | |
487 | cap = kzalloc(size: sizeof(*cap), GFP_KERNEL); |
488 | if (!cap) |
489 | return ERR_PTR(error: -ENOMEM); |
490 | |
491 | cap->pd = pd; |
492 | cap->role = desc->role; |
493 | |
494 | cap->dev.parent = &pd->dev; |
495 | cap->dev.type = &pd_capabilities_type; |
496 | dev_set_name(dev: &cap->dev, name: "%s" , cap_name[cap->role]); |
497 | |
498 | ret = device_register(dev: &cap->dev); |
499 | if (ret) { |
500 | put_device(dev: &cap->dev); |
501 | return ERR_PTR(error: ret); |
502 | } |
503 | |
504 | for (i = 0; i < PDO_MAX_OBJECTS && desc->pdo[i]; i++) { |
505 | ret = add_pdo(cap, pdo: desc->pdo[i], position: i); |
506 | if (ret) { |
507 | usb_power_delivery_unregister_capabilities(cap); |
508 | return ERR_PTR(error: ret); |
509 | } |
510 | } |
511 | |
512 | return cap; |
513 | } |
514 | EXPORT_SYMBOL_GPL(usb_power_delivery_register_capabilities); |
515 | |
516 | /** |
517 | * usb_power_delivery_unregister_capabilities - Unregister a set of capabilities |
518 | * @cap: The capabilities |
519 | */ |
520 | void usb_power_delivery_unregister_capabilities(struct usb_power_delivery_capabilities *cap) |
521 | { |
522 | if (!cap) |
523 | return; |
524 | |
525 | device_for_each_child(dev: &cap->dev, NULL, fn: remove_pdo); |
526 | device_unregister(dev: &cap->dev); |
527 | } |
528 | EXPORT_SYMBOL_GPL(usb_power_delivery_unregister_capabilities); |
529 | |
530 | /* -------------------------------------------------------------------------- */ |
531 | |
532 | static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) |
533 | { |
534 | struct usb_power_delivery *pd = to_usb_power_delivery(dev); |
535 | |
536 | return sysfs_emit(buf, fmt: "%u.%u\n" , (pd->revision >> 8) & 0xff, (pd->revision >> 4) & 0xf); |
537 | } |
538 | static DEVICE_ATTR_RO(revision); |
539 | |
540 | static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) |
541 | { |
542 | struct usb_power_delivery *pd = to_usb_power_delivery(dev); |
543 | |
544 | return sysfs_emit(buf, fmt: "%u.%u\n" , (pd->version >> 8) & 0xff, (pd->version >> 4) & 0xf); |
545 | } |
546 | static DEVICE_ATTR_RO(version); |
547 | |
548 | static struct attribute *pd_attrs[] = { |
549 | &dev_attr_revision.attr, |
550 | &dev_attr_version.attr, |
551 | NULL |
552 | }; |
553 | |
554 | static umode_t pd_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) |
555 | { |
556 | struct usb_power_delivery *pd = to_usb_power_delivery(kobj_to_dev(kobj)); |
557 | |
558 | if (attr == &dev_attr_version.attr && !pd->version) |
559 | return 0; |
560 | |
561 | return attr->mode; |
562 | } |
563 | |
564 | static const struct attribute_group pd_group = { |
565 | .is_visible = pd_attr_is_visible, |
566 | .attrs = pd_attrs, |
567 | }; |
568 | __ATTRIBUTE_GROUPS(pd); |
569 | |
570 | static void pd_release(struct device *dev) |
571 | { |
572 | struct usb_power_delivery *pd = to_usb_power_delivery(dev); |
573 | |
574 | ida_free(&pd_ida, id: pd->id); |
575 | kfree(objp: pd); |
576 | } |
577 | |
578 | static const struct device_type pd_type = { |
579 | .name = "usb_power_delivery" , |
580 | .release = pd_release, |
581 | .groups = pd_groups, |
582 | }; |
583 | |
584 | struct usb_power_delivery *usb_power_delivery_find(const char *name) |
585 | { |
586 | struct device *dev; |
587 | |
588 | dev = class_find_device_by_name(class: &pd_class, name); |
589 | |
590 | return dev ? to_usb_power_delivery(dev) : NULL; |
591 | } |
592 | |
593 | /** |
594 | * usb_power_delivery_register - Register USB Power Delivery Support. |
595 | * @parent: Parent device. |
596 | * @desc: Description of the USB PD contract. |
597 | * |
598 | * This routine can be used to register USB Power Delivery capabilities that a |
599 | * device or devices can support. These capabilities represent all the |
600 | * capabilities that can be negotiated with a partner, so not only the Power |
601 | * Capabilities that are negotiated using the USB PD Capabilities Message. |
602 | * |
603 | * The USB Power Delivery Support object that this routine generates can be used |
604 | * as the parent object for all the actual USB Power Delivery Messages and |
605 | * objects that can be negotiated with the partner. |
606 | * |
607 | * Returns handle to struct usb_power_delivery or ERR_PTR. |
608 | */ |
609 | struct usb_power_delivery * |
610 | usb_power_delivery_register(struct device *parent, struct usb_power_delivery_desc *desc) |
611 | { |
612 | struct usb_power_delivery *pd; |
613 | int ret; |
614 | |
615 | pd = kzalloc(size: sizeof(*pd), GFP_KERNEL); |
616 | if (!pd) |
617 | return ERR_PTR(error: -ENOMEM); |
618 | |
619 | ret = ida_alloc(ida: &pd_ida, GFP_KERNEL); |
620 | if (ret < 0) { |
621 | kfree(objp: pd); |
622 | return ERR_PTR(error: ret); |
623 | } |
624 | |
625 | pd->id = ret; |
626 | pd->revision = desc->revision; |
627 | pd->version = desc->version; |
628 | |
629 | pd->dev.parent = parent; |
630 | pd->dev.type = &pd_type; |
631 | pd->dev.class = &pd_class; |
632 | dev_set_name(dev: &pd->dev, name: "pd%d" , pd->id); |
633 | |
634 | ret = device_register(dev: &pd->dev); |
635 | if (ret) { |
636 | put_device(dev: &pd->dev); |
637 | return ERR_PTR(error: ret); |
638 | } |
639 | |
640 | return pd; |
641 | } |
642 | EXPORT_SYMBOL_GPL(usb_power_delivery_register); |
643 | |
644 | /** |
645 | * usb_power_delivery_unregister - Unregister USB Power Delivery Support. |
646 | * @pd: The USB PD contract. |
647 | */ |
648 | void usb_power_delivery_unregister(struct usb_power_delivery *pd) |
649 | { |
650 | if (IS_ERR_OR_NULL(ptr: pd)) |
651 | return; |
652 | |
653 | device_unregister(dev: &pd->dev); |
654 | } |
655 | EXPORT_SYMBOL_GPL(usb_power_delivery_unregister); |
656 | |
657 | /** |
658 | * usb_power_delivery_link_device - Link device to its USB PD object. |
659 | * @pd: The USB PD instance. |
660 | * @dev: The device. |
661 | * |
662 | * This function can be used to create a symlink named "usb_power_delivery" for |
663 | * @dev that points to @pd. |
664 | */ |
665 | int usb_power_delivery_link_device(struct usb_power_delivery *pd, struct device *dev) |
666 | { |
667 | int ret; |
668 | |
669 | if (IS_ERR_OR_NULL(ptr: pd) || !dev) |
670 | return 0; |
671 | |
672 | ret = sysfs_create_link(kobj: &dev->kobj, target: &pd->dev.kobj, name: "usb_power_delivery" ); |
673 | if (ret) |
674 | return ret; |
675 | |
676 | get_device(dev: &pd->dev); |
677 | get_device(dev); |
678 | |
679 | return 0; |
680 | } |
681 | EXPORT_SYMBOL_GPL(usb_power_delivery_link_device); |
682 | |
683 | /** |
684 | * usb_power_delivery_unlink_device - Unlink device from its USB PD object. |
685 | * @pd: The USB PD instance. |
686 | * @dev: The device. |
687 | * |
688 | * Remove the symlink that was previously created with pd_link_device(). |
689 | */ |
690 | void usb_power_delivery_unlink_device(struct usb_power_delivery *pd, struct device *dev) |
691 | { |
692 | if (IS_ERR_OR_NULL(ptr: pd) || !dev) |
693 | return; |
694 | |
695 | sysfs_remove_link(kobj: &dev->kobj, name: "usb_power_delivery" ); |
696 | put_device(dev: &pd->dev); |
697 | put_device(dev); |
698 | } |
699 | EXPORT_SYMBOL_GPL(usb_power_delivery_unlink_device); |
700 | |
701 | /* -------------------------------------------------------------------------- */ |
702 | |
703 | int __init usb_power_delivery_init(void) |
704 | { |
705 | return class_register(class: &pd_class); |
706 | } |
707 | |
708 | void __exit usb_power_delivery_exit(void) |
709 | { |
710 | ida_destroy(ida: &pd_ida); |
711 | class_unregister(class: &pd_class); |
712 | } |
713 | |