.. | .. |
---|
9 | 9 | #include <linux/device.h> |
---|
10 | 10 | #include <linux/module.h> |
---|
11 | 11 | #include <linux/mutex.h> |
---|
12 | | -#include <linux/of.h> |
---|
13 | 12 | #include <linux/property.h> |
---|
14 | 13 | #include <linux/slab.h> |
---|
| 14 | +#include <linux/usb/pd_vdo.h> |
---|
| 15 | +#include <linux/android_kabi.h> |
---|
15 | 16 | |
---|
16 | 17 | #include "bus.h" |
---|
17 | 18 | |
---|
.. | .. |
---|
19 | 20 | struct device dev; |
---|
20 | 21 | enum typec_plug_index index; |
---|
21 | 22 | struct ida mode_ids; |
---|
| 23 | + int num_altmodes; |
---|
| 24 | + ANDROID_KABI_RESERVE(1); |
---|
22 | 25 | }; |
---|
23 | 26 | |
---|
24 | 27 | struct typec_cable { |
---|
.. | .. |
---|
26 | 29 | enum typec_plug_type type; |
---|
27 | 30 | struct usb_pd_identity *identity; |
---|
28 | 31 | unsigned int active:1; |
---|
| 32 | + u16 pd_revision; /* 0300H = "3.0" */ |
---|
| 33 | + ANDROID_KABI_RESERVE(1); |
---|
29 | 34 | }; |
---|
30 | 35 | |
---|
31 | 36 | struct typec_partner { |
---|
.. | .. |
---|
34 | 39 | struct usb_pd_identity *identity; |
---|
35 | 40 | enum typec_accessory accessory; |
---|
36 | 41 | struct ida mode_ids; |
---|
| 42 | + int num_altmodes; |
---|
| 43 | + u16 pd_revision; /* 0300H = "3.0" */ |
---|
| 44 | + enum usb_pd_svdm_ver svdm_version; |
---|
| 45 | + ANDROID_KABI_RESERVE(1); |
---|
37 | 46 | }; |
---|
38 | 47 | |
---|
39 | 48 | struct typec_port { |
---|
.. | .. |
---|
54 | 63 | struct typec_mux *mux; |
---|
55 | 64 | |
---|
56 | 65 | const struct typec_capability *cap; |
---|
| 66 | + const struct typec_operations *ops; |
---|
| 67 | + ANDROID_KABI_RESERVE(1); |
---|
57 | 68 | }; |
---|
58 | 69 | |
---|
59 | 70 | #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev) |
---|
.. | .. |
---|
81 | 92 | [TYPEC_ACCESSORY_DEBUG] = "debug", |
---|
82 | 93 | }; |
---|
83 | 94 | |
---|
| 95 | +/* Product types defined in USB PD Specification R3.0 V2.0 */ |
---|
| 96 | +static const char * const product_type_ufp[8] = { |
---|
| 97 | + [IDH_PTYPE_NOT_UFP] = "not_ufp", |
---|
| 98 | + [IDH_PTYPE_HUB] = "hub", |
---|
| 99 | + [IDH_PTYPE_PERIPH] = "peripheral", |
---|
| 100 | + [IDH_PTYPE_PSD] = "psd", |
---|
| 101 | + [IDH_PTYPE_AMA] = "ama", |
---|
| 102 | +}; |
---|
| 103 | + |
---|
| 104 | +static const char * const product_type_dfp[8] = { |
---|
| 105 | + [IDH_PTYPE_NOT_DFP] = "not_dfp", |
---|
| 106 | + [IDH_PTYPE_DFP_HUB] = "hub", |
---|
| 107 | + [IDH_PTYPE_DFP_HOST] = "host", |
---|
| 108 | + [IDH_PTYPE_DFP_PB] = "power_brick", |
---|
| 109 | +}; |
---|
| 110 | + |
---|
| 111 | +static const char * const product_type_cable[8] = { |
---|
| 112 | + [IDH_PTYPE_NOT_CABLE] = "not_cable", |
---|
| 113 | + [IDH_PTYPE_PCABLE] = "passive", |
---|
| 114 | + [IDH_PTYPE_ACABLE] = "active", |
---|
| 115 | + [IDH_PTYPE_VPD] = "vpd", |
---|
| 116 | +}; |
---|
| 117 | + |
---|
84 | 118 | static struct usb_pd_identity *get_pd_identity(struct device *dev) |
---|
85 | 119 | { |
---|
86 | 120 | if (is_typec_partner(dev)) { |
---|
.. | .. |
---|
93 | 127 | return cable->identity; |
---|
94 | 128 | } |
---|
95 | 129 | return NULL; |
---|
| 130 | +} |
---|
| 131 | + |
---|
| 132 | +static const char *get_pd_product_type(struct device *dev) |
---|
| 133 | +{ |
---|
| 134 | + struct typec_port *port = to_typec_port(dev->parent); |
---|
| 135 | + struct usb_pd_identity *id = get_pd_identity(dev); |
---|
| 136 | + const char *ptype = NULL; |
---|
| 137 | + |
---|
| 138 | + if (is_typec_partner(dev)) { |
---|
| 139 | + if (!id) |
---|
| 140 | + return NULL; |
---|
| 141 | + |
---|
| 142 | + if (port->data_role == TYPEC_HOST) |
---|
| 143 | + ptype = product_type_ufp[PD_IDH_PTYPE(id->id_header)]; |
---|
| 144 | + else |
---|
| 145 | + ptype = product_type_dfp[PD_IDH_DFP_PTYPE(id->id_header)]; |
---|
| 146 | + } else if (is_typec_cable(dev)) { |
---|
| 147 | + if (id) |
---|
| 148 | + ptype = product_type_cable[PD_IDH_PTYPE(id->id_header)]; |
---|
| 149 | + else |
---|
| 150 | + ptype = to_typec_cable(dev)->active ? |
---|
| 151 | + product_type_cable[IDH_PTYPE_ACABLE] : |
---|
| 152 | + product_type_cable[IDH_PTYPE_PCABLE]; |
---|
| 153 | + } |
---|
| 154 | + |
---|
| 155 | + return ptype; |
---|
96 | 156 | } |
---|
97 | 157 | |
---|
98 | 158 | static ssize_t id_header_show(struct device *dev, struct device_attribute *attr, |
---|
.. | .. |
---|
122 | 182 | } |
---|
123 | 183 | static DEVICE_ATTR_RO(product); |
---|
124 | 184 | |
---|
| 185 | +static ssize_t product_type_vdo1_show(struct device *dev, struct device_attribute *attr, |
---|
| 186 | + char *buf) |
---|
| 187 | +{ |
---|
| 188 | + struct usb_pd_identity *id = get_pd_identity(dev); |
---|
| 189 | + |
---|
| 190 | + return sysfs_emit(buf, "0x%08x\n", id->vdo[0]); |
---|
| 191 | +} |
---|
| 192 | +static DEVICE_ATTR_RO(product_type_vdo1); |
---|
| 193 | + |
---|
| 194 | +static ssize_t product_type_vdo2_show(struct device *dev, struct device_attribute *attr, |
---|
| 195 | + char *buf) |
---|
| 196 | +{ |
---|
| 197 | + struct usb_pd_identity *id = get_pd_identity(dev); |
---|
| 198 | + |
---|
| 199 | + return sysfs_emit(buf, "0x%08x\n", id->vdo[1]); |
---|
| 200 | +} |
---|
| 201 | +static DEVICE_ATTR_RO(product_type_vdo2); |
---|
| 202 | + |
---|
| 203 | +static ssize_t product_type_vdo3_show(struct device *dev, struct device_attribute *attr, |
---|
| 204 | + char *buf) |
---|
| 205 | +{ |
---|
| 206 | + struct usb_pd_identity *id = get_pd_identity(dev); |
---|
| 207 | + |
---|
| 208 | + return sysfs_emit(buf, "0x%08x\n", id->vdo[2]); |
---|
| 209 | +} |
---|
| 210 | +static DEVICE_ATTR_RO(product_type_vdo3); |
---|
| 211 | + |
---|
125 | 212 | static struct attribute *usb_pd_id_attrs[] = { |
---|
126 | 213 | &dev_attr_id_header.attr, |
---|
127 | 214 | &dev_attr_cert_stat.attr, |
---|
128 | 215 | &dev_attr_product.attr, |
---|
| 216 | + &dev_attr_product_type_vdo1.attr, |
---|
| 217 | + &dev_attr_product_type_vdo2.attr, |
---|
| 218 | + &dev_attr_product_type_vdo3.attr, |
---|
129 | 219 | NULL |
---|
130 | 220 | }; |
---|
131 | 221 | |
---|
.. | .. |
---|
139 | 229 | NULL, |
---|
140 | 230 | }; |
---|
141 | 231 | |
---|
| 232 | +static void typec_product_type_notify(struct device *dev) |
---|
| 233 | +{ |
---|
| 234 | + char *envp[2] = { }; |
---|
| 235 | + const char *ptype; |
---|
| 236 | + |
---|
| 237 | + ptype = get_pd_product_type(dev); |
---|
| 238 | + if (!ptype) |
---|
| 239 | + return; |
---|
| 240 | + |
---|
| 241 | + sysfs_notify(&dev->kobj, NULL, "type"); |
---|
| 242 | + |
---|
| 243 | + envp[0] = kasprintf(GFP_KERNEL, "PRODUCT_TYPE=%s", ptype); |
---|
| 244 | + if (!envp[0]) |
---|
| 245 | + return; |
---|
| 246 | + |
---|
| 247 | + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); |
---|
| 248 | + kfree(envp[0]); |
---|
| 249 | +} |
---|
| 250 | + |
---|
142 | 251 | static void typec_report_identity(struct device *dev) |
---|
143 | 252 | { |
---|
144 | 253 | sysfs_notify(&dev->kobj, "identity", "id_header"); |
---|
145 | 254 | sysfs_notify(&dev->kobj, "identity", "cert_stat"); |
---|
146 | 255 | sysfs_notify(&dev->kobj, "identity", "product"); |
---|
| 256 | + sysfs_notify(&dev->kobj, "identity", "product_type_vdo1"); |
---|
| 257 | + sysfs_notify(&dev->kobj, "identity", "product_type_vdo2"); |
---|
| 258 | + sysfs_notify(&dev->kobj, "identity", "product_type_vdo3"); |
---|
| 259 | + typec_product_type_notify(dev); |
---|
147 | 260 | } |
---|
| 261 | + |
---|
| 262 | +static ssize_t |
---|
| 263 | +type_show(struct device *dev, struct device_attribute *attr, char *buf) |
---|
| 264 | +{ |
---|
| 265 | + const char *ptype; |
---|
| 266 | + |
---|
| 267 | + ptype = get_pd_product_type(dev); |
---|
| 268 | + if (!ptype) |
---|
| 269 | + return 0; |
---|
| 270 | + |
---|
| 271 | + return sysfs_emit(buf, "%s\n", ptype); |
---|
| 272 | +} |
---|
| 273 | +static DEVICE_ATTR_RO(type); |
---|
| 274 | + |
---|
| 275 | +static ssize_t usb_power_delivery_revision_show(struct device *dev, |
---|
| 276 | + struct device_attribute *attr, |
---|
| 277 | + char *buf); |
---|
| 278 | +static DEVICE_ATTR_RO(usb_power_delivery_revision); |
---|
148 | 279 | |
---|
149 | 280 | /* ------------------------------------------------------------------------- */ |
---|
150 | 281 | /* Alternate Modes */ |
---|
.. | .. |
---|
205 | 336 | } |
---|
206 | 337 | put_device(&adev->dev); |
---|
207 | 338 | } |
---|
208 | | - |
---|
209 | | -static int typec_port_fwnode_match(struct device *dev, const void *fwnode) |
---|
210 | | -{ |
---|
211 | | - return dev_fwnode(dev) == fwnode; |
---|
212 | | -} |
---|
213 | | - |
---|
214 | | -static int typec_port_name_match(struct device *dev, const void *name) |
---|
215 | | -{ |
---|
216 | | - return !strcmp((const char *)name, dev_name(dev)); |
---|
217 | | -} |
---|
218 | | - |
---|
219 | | -static void *typec_port_match(struct device_connection *con, int ep, void *data) |
---|
220 | | -{ |
---|
221 | | - struct device *dev; |
---|
222 | | - |
---|
223 | | - /* |
---|
224 | | - * FIXME: Check does the fwnode supports the requested SVID. If it does |
---|
225 | | - * we need to return ERR_PTR(-PROBE_DEFER) when there is no device. |
---|
226 | | - */ |
---|
227 | | - if (con->fwnode) |
---|
228 | | - return class_find_device(typec_class, NULL, con->fwnode, |
---|
229 | | - typec_port_fwnode_match); |
---|
230 | | - |
---|
231 | | - dev = class_find_device(typec_class, NULL, con->endpoint[ep], |
---|
232 | | - typec_port_name_match); |
---|
233 | | - |
---|
234 | | - return dev ? dev : ERR_PTR(-EPROBE_DEFER); |
---|
235 | | -} |
---|
236 | | - |
---|
237 | | -struct typec_altmode * |
---|
238 | | -typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, |
---|
239 | | - struct notifier_block *nb) |
---|
240 | | -{ |
---|
241 | | - struct typec_device_id id = { svid, mode, }; |
---|
242 | | - struct device *altmode_dev; |
---|
243 | | - struct device *port_dev; |
---|
244 | | - struct altmode *altmode; |
---|
245 | | - int ret; |
---|
246 | | - |
---|
247 | | - /* Find the port linked to the caller */ |
---|
248 | | - port_dev = device_connection_find_match(dev, NULL, NULL, |
---|
249 | | - typec_port_match); |
---|
250 | | - if (IS_ERR_OR_NULL(port_dev)) |
---|
251 | | - return port_dev ? ERR_CAST(port_dev) : ERR_PTR(-ENODEV); |
---|
252 | | - |
---|
253 | | - /* Find the altmode with matching svid */ |
---|
254 | | - altmode_dev = device_find_child(port_dev, &id, altmode_match); |
---|
255 | | - |
---|
256 | | - put_device(port_dev); |
---|
257 | | - |
---|
258 | | - if (!altmode_dev) |
---|
259 | | - return ERR_PTR(-ENODEV); |
---|
260 | | - |
---|
261 | | - altmode = to_altmode(to_typec_altmode(altmode_dev)); |
---|
262 | | - |
---|
263 | | - /* Register notifier */ |
---|
264 | | - ret = blocking_notifier_chain_register(&altmode->nh, nb); |
---|
265 | | - if (ret) { |
---|
266 | | - put_device(altmode_dev); |
---|
267 | | - return ERR_PTR(ret); |
---|
268 | | - } |
---|
269 | | - |
---|
270 | | - return &altmode->adev; |
---|
271 | | -} |
---|
272 | | -EXPORT_SYMBOL_GPL(typec_altmode_register_notifier); |
---|
273 | | - |
---|
274 | | -void typec_altmode_unregister_notifier(struct typec_altmode *adev, |
---|
275 | | - struct notifier_block *nb) |
---|
276 | | -{ |
---|
277 | | - struct altmode *altmode = to_altmode(adev); |
---|
278 | | - |
---|
279 | | - blocking_notifier_chain_unregister(&altmode->nh, nb); |
---|
280 | | - put_device(&adev->dev); |
---|
281 | | -} |
---|
282 | | -EXPORT_SYMBOL_GPL(typec_altmode_unregister_notifier); |
---|
283 | 339 | |
---|
284 | 340 | /** |
---|
285 | 341 | * typec_altmode_update_active - Report Enter/Exit mode |
---|
.. | .. |
---|
444 | 500 | &dev_attr_vdo.attr, |
---|
445 | 501 | NULL |
---|
446 | 502 | }; |
---|
447 | | -ATTRIBUTE_GROUPS(typec_altmode); |
---|
| 503 | + |
---|
| 504 | +static umode_t typec_altmode_attr_is_visible(struct kobject *kobj, |
---|
| 505 | + struct attribute *attr, int n) |
---|
| 506 | +{ |
---|
| 507 | + struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj)); |
---|
| 508 | + |
---|
| 509 | + if (attr == &dev_attr_active.attr) |
---|
| 510 | + if (!adev->ops || !adev->ops->activate) |
---|
| 511 | + return 0444; |
---|
| 512 | + |
---|
| 513 | + return attr->mode; |
---|
| 514 | +} |
---|
| 515 | + |
---|
| 516 | +static const struct attribute_group typec_altmode_group = { |
---|
| 517 | + .is_visible = typec_altmode_attr_is_visible, |
---|
| 518 | + .attrs = typec_altmode_attrs, |
---|
| 519 | +}; |
---|
| 520 | + |
---|
| 521 | +static const struct attribute_group *typec_altmode_groups[] = { |
---|
| 522 | + &typec_altmode_group, |
---|
| 523 | + NULL |
---|
| 524 | +}; |
---|
448 | 525 | |
---|
449 | 526 | static int altmode_id_get(struct device *dev) |
---|
450 | 527 | { |
---|
.. | .. |
---|
531 | 608 | dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id); |
---|
532 | 609 | |
---|
533 | 610 | /* Link partners and plugs with the ports */ |
---|
534 | | - if (is_port) |
---|
535 | | - BLOCKING_INIT_NOTIFIER_HEAD(&alt->nh); |
---|
536 | | - else |
---|
| 611 | + if (!is_port) |
---|
537 | 612 | typec_altmode_set_partner(alt); |
---|
538 | 613 | |
---|
539 | 614 | /* The partners are bind to drivers */ |
---|
540 | 615 | if (is_typec_partner(parent)) |
---|
541 | 616 | alt->adev.dev.bus = &typec_bus; |
---|
| 617 | + |
---|
| 618 | + /* Plug alt modes need a class to generate udev events. */ |
---|
| 619 | + if (is_typec_plug(parent)) |
---|
| 620 | + alt->adev.dev.class = typec_class; |
---|
542 | 621 | |
---|
543 | 622 | ret = device_register(&alt->adev.dev); |
---|
544 | 623 | if (ret) { |
---|
.. | .. |
---|
590 | 669 | } |
---|
591 | 670 | static DEVICE_ATTR_RO(supports_usb_power_delivery); |
---|
592 | 671 | |
---|
| 672 | +static ssize_t number_of_alternate_modes_show(struct device *dev, struct device_attribute *attr, |
---|
| 673 | + char *buf) |
---|
| 674 | +{ |
---|
| 675 | + struct typec_partner *partner; |
---|
| 676 | + struct typec_plug *plug; |
---|
| 677 | + int num_altmodes; |
---|
| 678 | + |
---|
| 679 | + if (is_typec_partner(dev)) { |
---|
| 680 | + partner = to_typec_partner(dev); |
---|
| 681 | + num_altmodes = partner->num_altmodes; |
---|
| 682 | + } else if (is_typec_plug(dev)) { |
---|
| 683 | + plug = to_typec_plug(dev); |
---|
| 684 | + num_altmodes = plug->num_altmodes; |
---|
| 685 | + } else { |
---|
| 686 | + return 0; |
---|
| 687 | + } |
---|
| 688 | + |
---|
| 689 | + return sysfs_emit(buf, "%d\n", num_altmodes); |
---|
| 690 | +} |
---|
| 691 | +static DEVICE_ATTR_RO(number_of_alternate_modes); |
---|
| 692 | + |
---|
593 | 693 | static struct attribute *typec_partner_attrs[] = { |
---|
594 | 694 | &dev_attr_accessory_mode.attr, |
---|
595 | 695 | &dev_attr_supports_usb_power_delivery.attr, |
---|
| 696 | + &dev_attr_number_of_alternate_modes.attr, |
---|
| 697 | + &dev_attr_type.attr, |
---|
| 698 | + &dev_attr_usb_power_delivery_revision.attr, |
---|
596 | 699 | NULL |
---|
597 | 700 | }; |
---|
598 | | -ATTRIBUTE_GROUPS(typec_partner); |
---|
| 701 | + |
---|
| 702 | +static umode_t typec_partner_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) |
---|
| 703 | +{ |
---|
| 704 | + struct typec_partner *partner = to_typec_partner(kobj_to_dev(kobj)); |
---|
| 705 | + |
---|
| 706 | + if (attr == &dev_attr_number_of_alternate_modes.attr) { |
---|
| 707 | + if (partner->num_altmodes < 0) |
---|
| 708 | + return 0; |
---|
| 709 | + } |
---|
| 710 | + |
---|
| 711 | + if (attr == &dev_attr_type.attr) |
---|
| 712 | + if (!get_pd_product_type(kobj_to_dev(kobj))) |
---|
| 713 | + return 0; |
---|
| 714 | + |
---|
| 715 | + return attr->mode; |
---|
| 716 | +} |
---|
| 717 | + |
---|
| 718 | +static const struct attribute_group typec_partner_group = { |
---|
| 719 | + .is_visible = typec_partner_attr_is_visible, |
---|
| 720 | + .attrs = typec_partner_attrs |
---|
| 721 | +}; |
---|
| 722 | + |
---|
| 723 | +static const struct attribute_group *typec_partner_groups[] = { |
---|
| 724 | + &typec_partner_group, |
---|
| 725 | + NULL |
---|
| 726 | +}; |
---|
599 | 727 | |
---|
600 | 728 | static void typec_partner_release(struct device *dev) |
---|
601 | 729 | { |
---|
.. | .. |
---|
629 | 757 | EXPORT_SYMBOL_GPL(typec_partner_set_identity); |
---|
630 | 758 | |
---|
631 | 759 | /** |
---|
| 760 | + * typec_partner_set_pd_revision - Set the PD revision supported by the partner |
---|
| 761 | + * @partner: The partner to be updated. |
---|
| 762 | + * @pd_revision: USB Power Delivery Specification Revision supported by partner |
---|
| 763 | + * |
---|
| 764 | + * This routine is used to report that the PD revision of the port partner has |
---|
| 765 | + * become available. |
---|
| 766 | + */ |
---|
| 767 | +void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revision) |
---|
| 768 | +{ |
---|
| 769 | + if (partner->pd_revision == pd_revision) |
---|
| 770 | + return; |
---|
| 771 | + |
---|
| 772 | + partner->pd_revision = pd_revision; |
---|
| 773 | + sysfs_notify(&partner->dev.kobj, NULL, "usb_power_delivery_revision"); |
---|
| 774 | + if (pd_revision != 0 && !partner->usb_pd) { |
---|
| 775 | + partner->usb_pd = 1; |
---|
| 776 | + sysfs_notify(&partner->dev.kobj, NULL, |
---|
| 777 | + "supports_usb_power_delivery"); |
---|
| 778 | + } |
---|
| 779 | + kobject_uevent(&partner->dev.kobj, KOBJ_CHANGE); |
---|
| 780 | +} |
---|
| 781 | +EXPORT_SYMBOL_GPL(typec_partner_set_pd_revision); |
---|
| 782 | + |
---|
| 783 | +/** |
---|
| 784 | + * typec_partner_set_num_altmodes - Set the number of available partner altmodes |
---|
| 785 | + * @partner: The partner to be updated. |
---|
| 786 | + * @num_altmodes: The number of altmodes we want to specify as available. |
---|
| 787 | + * |
---|
| 788 | + * This routine is used to report the number of alternate modes supported by the |
---|
| 789 | + * partner. This value is *not* enforced in alternate mode registration routines. |
---|
| 790 | + * |
---|
| 791 | + * @partner.num_altmodes is set to -1 on partner registration, denoting that |
---|
| 792 | + * a valid value has not been set for it yet. |
---|
| 793 | + * |
---|
| 794 | + * Returns 0 on success or negative error number on failure. |
---|
| 795 | + */ |
---|
| 796 | +int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes) |
---|
| 797 | +{ |
---|
| 798 | + int ret; |
---|
| 799 | + |
---|
| 800 | + if (num_altmodes < 0) |
---|
| 801 | + return -EINVAL; |
---|
| 802 | + |
---|
| 803 | + partner->num_altmodes = num_altmodes; |
---|
| 804 | + ret = sysfs_update_group(&partner->dev.kobj, &typec_partner_group); |
---|
| 805 | + if (ret < 0) |
---|
| 806 | + return ret; |
---|
| 807 | + |
---|
| 808 | + sysfs_notify(&partner->dev.kobj, NULL, "number_of_alternate_modes"); |
---|
| 809 | + |
---|
| 810 | + return 0; |
---|
| 811 | +} |
---|
| 812 | +EXPORT_SYMBOL_GPL(typec_partner_set_num_altmodes); |
---|
| 813 | + |
---|
| 814 | +/** |
---|
632 | 815 | * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode |
---|
633 | 816 | * @partner: USB Type-C Partner that supports the alternate mode |
---|
634 | 817 | * @desc: Description of the alternate mode |
---|
.. | .. |
---|
638 | 821 | * SVID listed in response to Discover Modes command need to be listed in an |
---|
639 | 822 | * array in @desc. |
---|
640 | 823 | * |
---|
641 | | - * Returns handle to the alternate mode on success or NULL on failure. |
---|
| 824 | + * Returns handle to the alternate mode on success or ERR_PTR on failure. |
---|
642 | 825 | */ |
---|
643 | 826 | struct typec_altmode * |
---|
644 | 827 | typec_partner_register_altmode(struct typec_partner *partner, |
---|
.. | .. |
---|
647 | 830 | return typec_register_altmode(&partner->dev, desc); |
---|
648 | 831 | } |
---|
649 | 832 | EXPORT_SYMBOL_GPL(typec_partner_register_altmode); |
---|
| 833 | + |
---|
| 834 | +/** |
---|
| 835 | + * typec_partner_set_svdm_version - Set negotiated Structured VDM (SVDM) Version |
---|
| 836 | + * @partner: USB Type-C Partner that supports SVDM |
---|
| 837 | + * @svdm_version: Negotiated SVDM Version |
---|
| 838 | + * |
---|
| 839 | + * This routine is used to save the negotiated SVDM Version. |
---|
| 840 | + */ |
---|
| 841 | +void typec_partner_set_svdm_version(struct typec_partner *partner, |
---|
| 842 | + enum usb_pd_svdm_ver svdm_version) |
---|
| 843 | +{ |
---|
| 844 | + partner->svdm_version = svdm_version; |
---|
| 845 | +} |
---|
| 846 | +EXPORT_SYMBOL_GPL(typec_partner_set_svdm_version); |
---|
650 | 847 | |
---|
651 | 848 | /** |
---|
652 | 849 | * typec_register_partner - Register a USB Type-C Partner |
---|
.. | .. |
---|
670 | 867 | ida_init(&partner->mode_ids); |
---|
671 | 868 | partner->usb_pd = desc->usb_pd; |
---|
672 | 869 | partner->accessory = desc->accessory; |
---|
| 870 | + partner->num_altmodes = -1; |
---|
| 871 | + partner->pd_revision = desc->pd_revision; |
---|
| 872 | + partner->svdm_version = port->cap->svdm_version; |
---|
673 | 873 | |
---|
674 | 874 | if (desc->identity) { |
---|
675 | 875 | /* |
---|
.. | .. |
---|
720 | 920 | kfree(plug); |
---|
721 | 921 | } |
---|
722 | 922 | |
---|
| 923 | +static struct attribute *typec_plug_attrs[] = { |
---|
| 924 | + &dev_attr_number_of_alternate_modes.attr, |
---|
| 925 | + NULL |
---|
| 926 | +}; |
---|
| 927 | + |
---|
| 928 | +static umode_t typec_plug_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) |
---|
| 929 | +{ |
---|
| 930 | + struct typec_plug *plug = to_typec_plug(kobj_to_dev(kobj)); |
---|
| 931 | + |
---|
| 932 | + if (attr == &dev_attr_number_of_alternate_modes.attr) { |
---|
| 933 | + if (plug->num_altmodes < 0) |
---|
| 934 | + return 0; |
---|
| 935 | + } |
---|
| 936 | + |
---|
| 937 | + return attr->mode; |
---|
| 938 | +} |
---|
| 939 | + |
---|
| 940 | +static const struct attribute_group typec_plug_group = { |
---|
| 941 | + .is_visible = typec_plug_attr_is_visible, |
---|
| 942 | + .attrs = typec_plug_attrs |
---|
| 943 | +}; |
---|
| 944 | + |
---|
| 945 | +static const struct attribute_group *typec_plug_groups[] = { |
---|
| 946 | + &typec_plug_group, |
---|
| 947 | + NULL |
---|
| 948 | +}; |
---|
| 949 | + |
---|
723 | 950 | static const struct device_type typec_plug_dev_type = { |
---|
724 | 951 | .name = "typec_plug", |
---|
| 952 | + .groups = typec_plug_groups, |
---|
725 | 953 | .release = typec_plug_release, |
---|
726 | 954 | }; |
---|
| 955 | + |
---|
| 956 | +/** |
---|
| 957 | + * typec_plug_set_num_altmodes - Set the number of available plug altmodes |
---|
| 958 | + * @plug: The plug to be updated. |
---|
| 959 | + * @num_altmodes: The number of altmodes we want to specify as available. |
---|
| 960 | + * |
---|
| 961 | + * This routine is used to report the number of alternate modes supported by the |
---|
| 962 | + * plug. This value is *not* enforced in alternate mode registration routines. |
---|
| 963 | + * |
---|
| 964 | + * @plug.num_altmodes is set to -1 on plug registration, denoting that |
---|
| 965 | + * a valid value has not been set for it yet. |
---|
| 966 | + * |
---|
| 967 | + * Returns 0 on success or negative error number on failure. |
---|
| 968 | + */ |
---|
| 969 | +int typec_plug_set_num_altmodes(struct typec_plug *plug, int num_altmodes) |
---|
| 970 | +{ |
---|
| 971 | + int ret; |
---|
| 972 | + |
---|
| 973 | + if (num_altmodes < 0) |
---|
| 974 | + return -EINVAL; |
---|
| 975 | + |
---|
| 976 | + plug->num_altmodes = num_altmodes; |
---|
| 977 | + ret = sysfs_update_group(&plug->dev.kobj, &typec_plug_group); |
---|
| 978 | + if (ret < 0) |
---|
| 979 | + return ret; |
---|
| 980 | + |
---|
| 981 | + sysfs_notify(&plug->dev.kobj, NULL, "number_of_alternate_modes"); |
---|
| 982 | + |
---|
| 983 | + return 0; |
---|
| 984 | +} |
---|
| 985 | +EXPORT_SYMBOL_GPL(typec_plug_set_num_altmodes); |
---|
727 | 986 | |
---|
728 | 987 | /** |
---|
729 | 988 | * typec_plug_register_altmode - Register USB Type-C Cable Plug Alternate Mode |
---|
.. | .. |
---|
770 | 1029 | sprintf(name, "plug%d", desc->index); |
---|
771 | 1030 | |
---|
772 | 1031 | ida_init(&plug->mode_ids); |
---|
| 1032 | + plug->num_altmodes = -1; |
---|
773 | 1033 | plug->index = desc->index; |
---|
774 | 1034 | plug->dev.class = typec_class; |
---|
775 | 1035 | plug->dev.parent = &cable->dev; |
---|
.. | .. |
---|
802 | 1062 | |
---|
803 | 1063 | /* Type-C Cables */ |
---|
804 | 1064 | |
---|
805 | | -static ssize_t |
---|
806 | | -type_show(struct device *dev, struct device_attribute *attr, char *buf) |
---|
807 | | -{ |
---|
808 | | - struct typec_cable *cable = to_typec_cable(dev); |
---|
809 | | - |
---|
810 | | - return sprintf(buf, "%s\n", cable->active ? "active" : "passive"); |
---|
811 | | -} |
---|
812 | | -static DEVICE_ATTR_RO(type); |
---|
813 | | - |
---|
814 | 1065 | static const char * const typec_plug_types[] = { |
---|
815 | 1066 | [USB_PLUG_NONE] = "unknown", |
---|
816 | 1067 | [USB_PLUG_TYPE_A] = "type-a", |
---|
.. | .. |
---|
831 | 1082 | static struct attribute *typec_cable_attrs[] = { |
---|
832 | 1083 | &dev_attr_type.attr, |
---|
833 | 1084 | &dev_attr_plug_type.attr, |
---|
| 1085 | + &dev_attr_usb_power_delivery_revision.attr, |
---|
834 | 1086 | NULL |
---|
835 | 1087 | }; |
---|
836 | 1088 | ATTRIBUTE_GROUPS(typec_cable); |
---|
.. | .. |
---|
847 | 1099 | .groups = typec_cable_groups, |
---|
848 | 1100 | .release = typec_cable_release, |
---|
849 | 1101 | }; |
---|
| 1102 | + |
---|
| 1103 | +static int cable_match(struct device *dev, void *data) |
---|
| 1104 | +{ |
---|
| 1105 | + return is_typec_cable(dev); |
---|
| 1106 | +} |
---|
| 1107 | + |
---|
| 1108 | +/** |
---|
| 1109 | + * typec_cable_get - Get a reference to the USB Type-C cable |
---|
| 1110 | + * @port: The USB Type-C Port the cable is connected to |
---|
| 1111 | + * |
---|
| 1112 | + * The caller must decrement the reference count with typec_cable_put() after |
---|
| 1113 | + * use. |
---|
| 1114 | + */ |
---|
| 1115 | +struct typec_cable *typec_cable_get(struct typec_port *port) |
---|
| 1116 | +{ |
---|
| 1117 | + struct device *dev; |
---|
| 1118 | + |
---|
| 1119 | + dev = device_find_child(&port->dev, NULL, cable_match); |
---|
| 1120 | + if (!dev) |
---|
| 1121 | + return NULL; |
---|
| 1122 | + |
---|
| 1123 | + return to_typec_cable(dev); |
---|
| 1124 | +} |
---|
| 1125 | +EXPORT_SYMBOL_GPL(typec_cable_get); |
---|
| 1126 | + |
---|
| 1127 | +/** |
---|
| 1128 | + * typec_cable_put - Decrement the reference count on USB Type-C cable |
---|
| 1129 | + * @cable: The USB Type-C cable |
---|
| 1130 | + */ |
---|
| 1131 | +void typec_cable_put(struct typec_cable *cable) |
---|
| 1132 | +{ |
---|
| 1133 | + put_device(&cable->dev); |
---|
| 1134 | +} |
---|
| 1135 | +EXPORT_SYMBOL_GPL(typec_cable_put); |
---|
| 1136 | + |
---|
| 1137 | +/** |
---|
| 1138 | + * typec_cable_is_active - Check is the USB Type-C cable active or passive |
---|
| 1139 | + * @cable: The USB Type-C Cable |
---|
| 1140 | + * |
---|
| 1141 | + * Return 1 if the cable is active or 0 if it's passive. |
---|
| 1142 | + */ |
---|
| 1143 | +int typec_cable_is_active(struct typec_cable *cable) |
---|
| 1144 | +{ |
---|
| 1145 | + return cable->active; |
---|
| 1146 | +} |
---|
| 1147 | +EXPORT_SYMBOL_GPL(typec_cable_is_active); |
---|
850 | 1148 | |
---|
851 | 1149 | /** |
---|
852 | 1150 | * typec_cable_set_identity - Report result from Discover Identity command |
---|
.. | .. |
---|
887 | 1185 | |
---|
888 | 1186 | cable->type = desc->type; |
---|
889 | 1187 | cable->active = desc->active; |
---|
| 1188 | + cable->pd_revision = desc->pd_revision; |
---|
890 | 1189 | |
---|
891 | 1190 | if (desc->identity) { |
---|
892 | 1191 | /* |
---|
.. | .. |
---|
929 | 1228 | /* ------------------------------------------------------------------------- */ |
---|
930 | 1229 | /* USB Type-C ports */ |
---|
931 | 1230 | |
---|
| 1231 | +static const char * const typec_orientations[] = { |
---|
| 1232 | + [TYPEC_ORIENTATION_NONE] = "unknown", |
---|
| 1233 | + [TYPEC_ORIENTATION_NORMAL] = "normal", |
---|
| 1234 | + [TYPEC_ORIENTATION_REVERSE] = "reverse", |
---|
| 1235 | +}; |
---|
| 1236 | + |
---|
932 | 1237 | static const char * const typec_roles[] = { |
---|
933 | 1238 | [TYPEC_SINK] = "sink", |
---|
934 | 1239 | [TYPEC_SOURCE] = "source", |
---|
.. | .. |
---|
970 | 1275 | return -EOPNOTSUPP; |
---|
971 | 1276 | } |
---|
972 | 1277 | |
---|
973 | | - if (!port->cap->try_role) { |
---|
| 1278 | + if (!port->ops || !port->ops->try_role) { |
---|
974 | 1279 | dev_dbg(dev, "Setting preferred role not supported\n"); |
---|
975 | 1280 | return -EOPNOTSUPP; |
---|
976 | 1281 | } |
---|
.. | .. |
---|
983 | 1288 | return -EINVAL; |
---|
984 | 1289 | } |
---|
985 | 1290 | |
---|
986 | | - ret = port->cap->try_role(port->cap, role); |
---|
| 1291 | + ret = port->ops->try_role(port, role); |
---|
987 | 1292 | if (ret) |
---|
988 | 1293 | return ret; |
---|
989 | 1294 | |
---|
.. | .. |
---|
1014 | 1319 | struct typec_port *port = to_typec_port(dev); |
---|
1015 | 1320 | int ret; |
---|
1016 | 1321 | |
---|
1017 | | - if (!port->cap->dr_set) { |
---|
| 1322 | + if (!port->ops || !port->ops->dr_set) { |
---|
1018 | 1323 | dev_dbg(dev, "data role swapping not supported\n"); |
---|
1019 | 1324 | return -EOPNOTSUPP; |
---|
1020 | 1325 | } |
---|
.. | .. |
---|
1029 | 1334 | goto unlock_and_ret; |
---|
1030 | 1335 | } |
---|
1031 | 1336 | |
---|
1032 | | - ret = port->cap->dr_set(port->cap, ret); |
---|
| 1337 | + ret = port->ops->dr_set(port, ret); |
---|
1033 | 1338 | if (ret) |
---|
1034 | 1339 | goto unlock_and_ret; |
---|
1035 | 1340 | |
---|
.. | .. |
---|
1059 | 1364 | struct typec_port *port = to_typec_port(dev); |
---|
1060 | 1365 | int ret; |
---|
1061 | 1366 | |
---|
1062 | | - if (!port->cap->pd_revision) { |
---|
1063 | | - dev_dbg(dev, "USB Power Delivery not supported\n"); |
---|
1064 | | - return -EOPNOTSUPP; |
---|
1065 | | - } |
---|
1066 | | - |
---|
1067 | | - if (!port->cap->pr_set) { |
---|
| 1367 | + if (!port->ops || !port->ops->pr_set) { |
---|
1068 | 1368 | dev_dbg(dev, "power role swapping not supported\n"); |
---|
1069 | 1369 | return -EOPNOTSUPP; |
---|
1070 | 1370 | } |
---|
.. | .. |
---|
1086 | 1386 | goto unlock_and_ret; |
---|
1087 | 1387 | } |
---|
1088 | 1388 | |
---|
1089 | | - ret = port->cap->pr_set(port->cap, ret); |
---|
| 1389 | + ret = port->ops->pr_set(port, ret); |
---|
1090 | 1390 | if (ret) |
---|
1091 | 1391 | goto unlock_and_ret; |
---|
1092 | 1392 | |
---|
.. | .. |
---|
1117 | 1417 | int ret; |
---|
1118 | 1418 | enum typec_port_type type; |
---|
1119 | 1419 | |
---|
1120 | | - if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) { |
---|
| 1420 | + if (port->cap->type != TYPEC_PORT_DRP || |
---|
| 1421 | + !port->ops || !port->ops->port_type_set) { |
---|
1121 | 1422 | dev_dbg(dev, "changing port type not supported\n"); |
---|
1122 | 1423 | return -EOPNOTSUPP; |
---|
1123 | 1424 | } |
---|
.. | .. |
---|
1134 | 1435 | goto unlock_and_ret; |
---|
1135 | 1436 | } |
---|
1136 | 1437 | |
---|
1137 | | - ret = port->cap->port_type_set(port->cap, type); |
---|
| 1438 | + ret = port->ops->port_type_set(port, type); |
---|
1138 | 1439 | if (ret) |
---|
1139 | 1440 | goto unlock_and_ret; |
---|
1140 | 1441 | |
---|
.. | .. |
---|
1190 | 1491 | return -EOPNOTSUPP; |
---|
1191 | 1492 | } |
---|
1192 | 1493 | |
---|
1193 | | - if (!port->cap->vconn_set) { |
---|
| 1494 | + if (!port->ops || !port->ops->vconn_set) { |
---|
1194 | 1495 | dev_dbg(dev, "VCONN swapping not supported\n"); |
---|
1195 | 1496 | return -EOPNOTSUPP; |
---|
1196 | 1497 | } |
---|
.. | .. |
---|
1199 | 1500 | if (ret) |
---|
1200 | 1501 | return ret; |
---|
1201 | 1502 | |
---|
1202 | | - ret = port->cap->vconn_set(port->cap, (enum typec_role)source); |
---|
| 1503 | + ret = port->ops->vconn_set(port, (enum typec_role)source); |
---|
1203 | 1504 | if (ret) |
---|
1204 | 1505 | return ret; |
---|
1205 | 1506 | |
---|
.. | .. |
---|
1254 | 1555 | struct device_attribute *attr, |
---|
1255 | 1556 | char *buf) |
---|
1256 | 1557 | { |
---|
1257 | | - struct typec_port *p = to_typec_port(dev); |
---|
| 1558 | + u16 rev = 0; |
---|
1258 | 1559 | |
---|
1259 | | - return sprintf(buf, "%d\n", (p->cap->pd_revision >> 8) & 0xff); |
---|
| 1560 | + if (is_typec_partner(dev)) { |
---|
| 1561 | + struct typec_partner *partner = to_typec_partner(dev); |
---|
| 1562 | + |
---|
| 1563 | + rev = partner->pd_revision; |
---|
| 1564 | + } else if (is_typec_cable(dev)) { |
---|
| 1565 | + struct typec_cable *cable = to_typec_cable(dev); |
---|
| 1566 | + |
---|
| 1567 | + rev = cable->pd_revision; |
---|
| 1568 | + } else if (is_typec_port(dev)) { |
---|
| 1569 | + struct typec_port *p = to_typec_port(dev); |
---|
| 1570 | + |
---|
| 1571 | + rev = p->cap->pd_revision; |
---|
| 1572 | + } |
---|
| 1573 | + return sysfs_emit(buf, "%d.%d\n", (rev >> 8) & 0xff, (rev >> 4) & 0xf); |
---|
1260 | 1574 | } |
---|
1261 | | -static DEVICE_ATTR_RO(usb_power_delivery_revision); |
---|
| 1575 | + |
---|
| 1576 | +static ssize_t orientation_show(struct device *dev, |
---|
| 1577 | + struct device_attribute *attr, |
---|
| 1578 | + char *buf) |
---|
| 1579 | +{ |
---|
| 1580 | + struct typec_port *port = to_typec_port(dev); |
---|
| 1581 | + |
---|
| 1582 | + return sprintf(buf, "%s\n", typec_orientations[port->orientation]); |
---|
| 1583 | +} |
---|
| 1584 | +static DEVICE_ATTR_RO(orientation); |
---|
1262 | 1585 | |
---|
1263 | 1586 | static struct attribute *typec_attrs[] = { |
---|
1264 | 1587 | &dev_attr_data_role.attr, |
---|
.. | .. |
---|
1270 | 1593 | &dev_attr_usb_typec_revision.attr, |
---|
1271 | 1594 | &dev_attr_vconn_source.attr, |
---|
1272 | 1595 | &dev_attr_port_type.attr, |
---|
| 1596 | + &dev_attr_orientation.attr, |
---|
1273 | 1597 | NULL, |
---|
1274 | 1598 | }; |
---|
1275 | | -ATTRIBUTE_GROUPS(typec); |
---|
| 1599 | + |
---|
| 1600 | +static umode_t typec_attr_is_visible(struct kobject *kobj, |
---|
| 1601 | + struct attribute *attr, int n) |
---|
| 1602 | +{ |
---|
| 1603 | + struct typec_port *port = to_typec_port(kobj_to_dev(kobj)); |
---|
| 1604 | + |
---|
| 1605 | + if (attr == &dev_attr_data_role.attr) { |
---|
| 1606 | + if (port->cap->data != TYPEC_PORT_DRD || |
---|
| 1607 | + !port->ops || !port->ops->dr_set) |
---|
| 1608 | + return 0444; |
---|
| 1609 | + } else if (attr == &dev_attr_power_role.attr) { |
---|
| 1610 | + if (port->cap->type != TYPEC_PORT_DRP || |
---|
| 1611 | + !port->ops || !port->ops->pr_set) |
---|
| 1612 | + return 0444; |
---|
| 1613 | + } else if (attr == &dev_attr_vconn_source.attr) { |
---|
| 1614 | + if (!port->cap->pd_revision || |
---|
| 1615 | + !port->ops || !port->ops->vconn_set) |
---|
| 1616 | + return 0444; |
---|
| 1617 | + } else if (attr == &dev_attr_preferred_role.attr) { |
---|
| 1618 | + if (port->cap->type != TYPEC_PORT_DRP || |
---|
| 1619 | + !port->ops || !port->ops->try_role) |
---|
| 1620 | + return 0444; |
---|
| 1621 | + } else if (attr == &dev_attr_port_type.attr) { |
---|
| 1622 | + if (!port->ops || !port->ops->port_type_set) |
---|
| 1623 | + return 0; |
---|
| 1624 | + if (port->cap->type != TYPEC_PORT_DRP) |
---|
| 1625 | + return 0444; |
---|
| 1626 | + } else if (attr == &dev_attr_orientation.attr) { |
---|
| 1627 | + if (port->cap->orientation_aware) |
---|
| 1628 | + return 0444; |
---|
| 1629 | + return 0; |
---|
| 1630 | + } |
---|
| 1631 | + |
---|
| 1632 | + return attr->mode; |
---|
| 1633 | +} |
---|
| 1634 | + |
---|
| 1635 | +static const struct attribute_group typec_group = { |
---|
| 1636 | + .is_visible = typec_attr_is_visible, |
---|
| 1637 | + .attrs = typec_attrs, |
---|
| 1638 | +}; |
---|
| 1639 | + |
---|
| 1640 | +static const struct attribute_group *typec_groups[] = { |
---|
| 1641 | + &typec_group, |
---|
| 1642 | + NULL |
---|
| 1643 | +}; |
---|
1276 | 1644 | |
---|
1277 | 1645 | static int typec_uevent(struct device *dev, struct kobj_uevent_env *env) |
---|
1278 | 1646 | { |
---|
.. | .. |
---|
1293 | 1661 | ida_destroy(&port->mode_ids); |
---|
1294 | 1662 | typec_switch_put(port->sw); |
---|
1295 | 1663 | typec_mux_put(port->mux); |
---|
| 1664 | + kfree(port->cap); |
---|
1296 | 1665 | kfree(port); |
---|
1297 | 1666 | } |
---|
1298 | 1667 | |
---|
.. | .. |
---|
1306 | 1675 | /* --------------------------------------- */ |
---|
1307 | 1676 | /* Driver callbacks to report role updates */ |
---|
1308 | 1677 | |
---|
| 1678 | +static int partner_match(struct device *dev, void *data) |
---|
| 1679 | +{ |
---|
| 1680 | + return is_typec_partner(dev); |
---|
| 1681 | +} |
---|
| 1682 | + |
---|
1309 | 1683 | /** |
---|
1310 | 1684 | * typec_set_data_role - Report data role change |
---|
1311 | 1685 | * @port: The USB Type-C Port where the role was changed |
---|
.. | .. |
---|
1315 | 1689 | */ |
---|
1316 | 1690 | void typec_set_data_role(struct typec_port *port, enum typec_data_role role) |
---|
1317 | 1691 | { |
---|
| 1692 | + struct device *partner_dev; |
---|
| 1693 | + |
---|
1318 | 1694 | if (port->data_role == role) |
---|
1319 | 1695 | return; |
---|
1320 | 1696 | |
---|
1321 | 1697 | port->data_role = role; |
---|
1322 | 1698 | sysfs_notify(&port->dev.kobj, NULL, "data_role"); |
---|
1323 | 1699 | kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); |
---|
| 1700 | + |
---|
| 1701 | + partner_dev = device_find_child(&port->dev, NULL, partner_match); |
---|
| 1702 | + if (!partner_dev) |
---|
| 1703 | + return; |
---|
| 1704 | + |
---|
| 1705 | + if (to_typec_partner(partner_dev)->identity) |
---|
| 1706 | + typec_product_type_notify(partner_dev); |
---|
| 1707 | + |
---|
| 1708 | + put_device(partner_dev); |
---|
1324 | 1709 | } |
---|
1325 | 1710 | EXPORT_SYMBOL_GPL(typec_set_data_role); |
---|
1326 | 1711 | |
---|
.. | .. |
---|
1361 | 1746 | } |
---|
1362 | 1747 | EXPORT_SYMBOL_GPL(typec_set_vconn_role); |
---|
1363 | 1748 | |
---|
1364 | | -static int partner_match(struct device *dev, void *data) |
---|
1365 | | -{ |
---|
1366 | | - return is_typec_partner(dev); |
---|
1367 | | -} |
---|
1368 | | - |
---|
1369 | 1749 | /** |
---|
1370 | 1750 | * typec_set_pwr_opmode - Report changed power operation mode |
---|
1371 | 1751 | * @port: The USB Type-C Port where the mode was changed |
---|
.. | .. |
---|
1396 | 1776 | partner->usb_pd = 1; |
---|
1397 | 1777 | sysfs_notify(&partner_dev->kobj, NULL, |
---|
1398 | 1778 | "supports_usb_power_delivery"); |
---|
| 1779 | + kobject_uevent(&partner_dev->kobj, KOBJ_CHANGE); |
---|
1399 | 1780 | } |
---|
1400 | 1781 | put_device(partner_dev); |
---|
1401 | 1782 | } |
---|
1402 | 1783 | } |
---|
1403 | 1784 | EXPORT_SYMBOL_GPL(typec_set_pwr_opmode); |
---|
| 1785 | + |
---|
| 1786 | +/** |
---|
| 1787 | + * typec_find_pwr_opmode - Get the typec power operation mode capability |
---|
| 1788 | + * @name: power operation mode string |
---|
| 1789 | + * |
---|
| 1790 | + * This routine is used to find the typec_pwr_opmode by its string @name. |
---|
| 1791 | + * |
---|
| 1792 | + * Returns typec_pwr_opmode if success, otherwise negative error code. |
---|
| 1793 | + */ |
---|
| 1794 | +int typec_find_pwr_opmode(const char *name) |
---|
| 1795 | +{ |
---|
| 1796 | + return match_string(typec_pwr_opmodes, |
---|
| 1797 | + ARRAY_SIZE(typec_pwr_opmodes), name); |
---|
| 1798 | +} |
---|
| 1799 | +EXPORT_SYMBOL_GPL(typec_find_pwr_opmode); |
---|
| 1800 | + |
---|
| 1801 | +/** |
---|
| 1802 | + * typec_find_orientation - Convert orientation string to enum typec_orientation |
---|
| 1803 | + * @name: Orientation string |
---|
| 1804 | + * |
---|
| 1805 | + * This routine is used to find the typec_orientation by its string name @name. |
---|
| 1806 | + * |
---|
| 1807 | + * Returns the orientation value on success, otherwise negative error code. |
---|
| 1808 | + */ |
---|
| 1809 | +int typec_find_orientation(const char *name) |
---|
| 1810 | +{ |
---|
| 1811 | + return match_string(typec_orientations, ARRAY_SIZE(typec_orientations), |
---|
| 1812 | + name); |
---|
| 1813 | +} |
---|
| 1814 | +EXPORT_SYMBOL_GPL(typec_find_orientation); |
---|
1404 | 1815 | |
---|
1405 | 1816 | /** |
---|
1406 | 1817 | * typec_find_port_power_role - Get the typec port power capability |
---|
.. | .. |
---|
1461 | 1872 | { |
---|
1462 | 1873 | int ret; |
---|
1463 | 1874 | |
---|
1464 | | - if (port->sw) { |
---|
1465 | | - ret = port->sw->set(port->sw, orientation); |
---|
1466 | | - if (ret) |
---|
1467 | | - return ret; |
---|
1468 | | - } |
---|
| 1875 | + ret = typec_switch_set(port->sw, orientation); |
---|
| 1876 | + if (ret) |
---|
| 1877 | + return ret; |
---|
1469 | 1878 | |
---|
1470 | 1879 | port->orientation = orientation; |
---|
| 1880 | + sysfs_notify(&port->dev.kobj, NULL, "orientation"); |
---|
| 1881 | + kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); |
---|
1471 | 1882 | |
---|
1472 | 1883 | return 0; |
---|
1473 | 1884 | } |
---|
.. | .. |
---|
1495 | 1906 | */ |
---|
1496 | 1907 | int typec_set_mode(struct typec_port *port, int mode) |
---|
1497 | 1908 | { |
---|
1498 | | - return port->mux ? port->mux->set(port->mux, mode) : 0; |
---|
| 1909 | + struct typec_mux_state state = { }; |
---|
| 1910 | + |
---|
| 1911 | + state.mode = mode; |
---|
| 1912 | + |
---|
| 1913 | + return typec_mux_set(port->mux, &state); |
---|
1499 | 1914 | } |
---|
1500 | 1915 | EXPORT_SYMBOL_GPL(typec_set_mode); |
---|
1501 | 1916 | |
---|
1502 | 1917 | /* --------------------------------------- */ |
---|
| 1918 | + |
---|
| 1919 | +/** |
---|
| 1920 | + * typec_get_negotiated_svdm_version - Get negotiated SVDM Version |
---|
| 1921 | + * @port: USB Type-C Port. |
---|
| 1922 | + * |
---|
| 1923 | + * Get the negotiated SVDM Version. The Version is set to the port default |
---|
| 1924 | + * value stored in typec_capability on partner registration, and updated after |
---|
| 1925 | + * a successful Discover Identity if the negotiated value is less than the |
---|
| 1926 | + * default value. |
---|
| 1927 | + * |
---|
| 1928 | + * Returns usb_pd_svdm_ver if the partner has been registered otherwise -ENODEV. |
---|
| 1929 | + */ |
---|
| 1930 | +int typec_get_negotiated_svdm_version(struct typec_port *port) |
---|
| 1931 | +{ |
---|
| 1932 | + enum usb_pd_svdm_ver svdm_version; |
---|
| 1933 | + struct device *partner_dev; |
---|
| 1934 | + |
---|
| 1935 | + partner_dev = device_find_child(&port->dev, NULL, partner_match); |
---|
| 1936 | + if (!partner_dev) |
---|
| 1937 | + return -ENODEV; |
---|
| 1938 | + |
---|
| 1939 | + svdm_version = to_typec_partner(partner_dev)->svdm_version; |
---|
| 1940 | + put_device(partner_dev); |
---|
| 1941 | + |
---|
| 1942 | + return svdm_version; |
---|
| 1943 | +} |
---|
| 1944 | +EXPORT_SYMBOL_GPL(typec_get_negotiated_svdm_version); |
---|
| 1945 | + |
---|
| 1946 | +/** |
---|
| 1947 | + * typec_get_drvdata - Return private driver data pointer |
---|
| 1948 | + * @port: USB Type-C port |
---|
| 1949 | + */ |
---|
| 1950 | +void *typec_get_drvdata(struct typec_port *port) |
---|
| 1951 | +{ |
---|
| 1952 | + return dev_get_drvdata(&port->dev); |
---|
| 1953 | +} |
---|
| 1954 | +EXPORT_SYMBOL_GPL(typec_get_drvdata); |
---|
1503 | 1955 | |
---|
1504 | 1956 | /** |
---|
1505 | 1957 | * typec_port_register_altmode - Register USB Type-C Port Alternate Mode |
---|
.. | .. |
---|
1551 | 2003 | ret = fwnode_property_read_u32(child, "svid", &svid); |
---|
1552 | 2004 | if (ret) { |
---|
1553 | 2005 | dev_err(&port->dev, "Error reading svid for altmode %s\n", |
---|
1554 | | - kbasename(of_node_full_name(to_of_node(child)))); |
---|
| 2006 | + fwnode_get_name(child)); |
---|
1555 | 2007 | continue; |
---|
1556 | 2008 | } |
---|
1557 | 2009 | |
---|
1558 | 2010 | ret = fwnode_property_read_u32(child, "vdo", &vdo); |
---|
1559 | 2011 | if (ret) { |
---|
1560 | 2012 | dev_err(&port->dev, "Error reading vdo for altmode %s\n", |
---|
1561 | | - kbasename(of_node_full_name(to_of_node(child)))); |
---|
| 2013 | + fwnode_get_name(child)); |
---|
1562 | 2014 | continue; |
---|
1563 | 2015 | } |
---|
1564 | 2016 | |
---|
1565 | 2017 | if (index >= n) { |
---|
1566 | 2018 | dev_err(&port->dev, "Error not enough space for altmode %s\n", |
---|
1567 | | - kbasename(of_node_full_name(to_of_node(child)))); |
---|
| 2019 | + fwnode_get_name(child)); |
---|
1568 | 2020 | continue; |
---|
1569 | 2021 | } |
---|
1570 | 2022 | |
---|
.. | .. |
---|
1574 | 2026 | alt = typec_port_register_altmode(port, &desc); |
---|
1575 | 2027 | if (IS_ERR(alt)) { |
---|
1576 | 2028 | dev_err(&port->dev, "Error registering altmode %s\n", |
---|
1577 | | - kbasename(of_node_full_name(to_of_node(child)))); |
---|
| 2029 | + fwnode_get_name(child)); |
---|
1578 | 2030 | continue; |
---|
1579 | 2031 | } |
---|
1580 | 2032 | |
---|
.. | .. |
---|
1648 | 2100 | mutex_init(&port->port_type_lock); |
---|
1649 | 2101 | |
---|
1650 | 2102 | port->id = id; |
---|
1651 | | - port->cap = cap; |
---|
| 2103 | + port->ops = cap->ops; |
---|
1652 | 2104 | port->port_type = cap->type; |
---|
1653 | 2105 | port->prefer_role = cap->prefer_role; |
---|
1654 | 2106 | |
---|
.. | .. |
---|
1658 | 2110 | port->dev.fwnode = cap->fwnode; |
---|
1659 | 2111 | port->dev.type = &typec_port_dev_type; |
---|
1660 | 2112 | dev_set_name(&port->dev, "port%d", id); |
---|
| 2113 | + dev_set_drvdata(&port->dev, cap->driver_data); |
---|
| 2114 | + |
---|
| 2115 | + port->cap = kmemdup(cap, sizeof(*cap), GFP_KERNEL); |
---|
| 2116 | + if (!port->cap) { |
---|
| 2117 | + put_device(&port->dev); |
---|
| 2118 | + return ERR_PTR(-ENOMEM); |
---|
| 2119 | + } |
---|
1661 | 2120 | |
---|
1662 | 2121 | port->sw = typec_switch_get(&port->dev); |
---|
1663 | 2122 | if (IS_ERR(port->sw)) { |
---|