| .. | .. |
|---|
| 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 |
|---|
| .. | .. |
|---|
| 1532 | 1984 | } |
|---|
| 1533 | 1985 | EXPORT_SYMBOL_GPL(typec_port_register_altmode); |
|---|
| 1534 | 1986 | |
|---|
| 1987 | +#ifdef CONFIG_NO_GKI |
|---|
| 1535 | 1988 | void typec_port_register_altmodes(struct typec_port *port, |
|---|
| 1536 | 1989 | const struct typec_altmode_ops *ops, void *drvdata, |
|---|
| 1537 | 1990 | struct typec_altmode **altmodes, size_t n) |
|---|
| .. | .. |
|---|
| 1551 | 2004 | ret = fwnode_property_read_u32(child, "svid", &svid); |
|---|
| 1552 | 2005 | if (ret) { |
|---|
| 1553 | 2006 | dev_err(&port->dev, "Error reading svid for altmode %s\n", |
|---|
| 1554 | | - kbasename(of_node_full_name(to_of_node(child)))); |
|---|
| 2007 | + fwnode_get_name(child)); |
|---|
| 1555 | 2008 | continue; |
|---|
| 1556 | 2009 | } |
|---|
| 1557 | 2010 | |
|---|
| 1558 | 2011 | ret = fwnode_property_read_u32(child, "vdo", &vdo); |
|---|
| 1559 | 2012 | if (ret) { |
|---|
| 1560 | 2013 | dev_err(&port->dev, "Error reading vdo for altmode %s\n", |
|---|
| 1561 | | - kbasename(of_node_full_name(to_of_node(child)))); |
|---|
| 2014 | + fwnode_get_name(child)); |
|---|
| 1562 | 2015 | continue; |
|---|
| 1563 | 2016 | } |
|---|
| 1564 | 2017 | |
|---|
| 1565 | 2018 | if (index >= n) { |
|---|
| 1566 | 2019 | dev_err(&port->dev, "Error not enough space for altmode %s\n", |
|---|
| 1567 | | - kbasename(of_node_full_name(to_of_node(child)))); |
|---|
| 2020 | + fwnode_get_name(child)); |
|---|
| 1568 | 2021 | continue; |
|---|
| 1569 | 2022 | } |
|---|
| 1570 | 2023 | |
|---|
| .. | .. |
|---|
| 1574 | 2027 | alt = typec_port_register_altmode(port, &desc); |
|---|
| 1575 | 2028 | if (IS_ERR(alt)) { |
|---|
| 1576 | 2029 | dev_err(&port->dev, "Error registering altmode %s\n", |
|---|
| 1577 | | - kbasename(of_node_full_name(to_of_node(child)))); |
|---|
| 2030 | + fwnode_get_name(child)); |
|---|
| 1578 | 2031 | continue; |
|---|
| 1579 | 2032 | } |
|---|
| 1580 | 2033 | |
|---|
| .. | .. |
|---|
| 1585 | 2038 | } |
|---|
| 1586 | 2039 | } |
|---|
| 1587 | 2040 | EXPORT_SYMBOL_GPL(typec_port_register_altmodes); |
|---|
| 2041 | +#endif /* CONFIG_NO_GKI */ |
|---|
| 1588 | 2042 | |
|---|
| 1589 | 2043 | /** |
|---|
| 1590 | 2044 | * typec_register_port - Register a USB Type-C Port |
|---|
| .. | .. |
|---|
| 1648 | 2102 | mutex_init(&port->port_type_lock); |
|---|
| 1649 | 2103 | |
|---|
| 1650 | 2104 | port->id = id; |
|---|
| 1651 | | - port->cap = cap; |
|---|
| 2105 | + port->ops = cap->ops; |
|---|
| 1652 | 2106 | port->port_type = cap->type; |
|---|
| 1653 | 2107 | port->prefer_role = cap->prefer_role; |
|---|
| 1654 | 2108 | |
|---|
| .. | .. |
|---|
| 1658 | 2112 | port->dev.fwnode = cap->fwnode; |
|---|
| 1659 | 2113 | port->dev.type = &typec_port_dev_type; |
|---|
| 1660 | 2114 | dev_set_name(&port->dev, "port%d", id); |
|---|
| 2115 | + dev_set_drvdata(&port->dev, cap->driver_data); |
|---|
| 2116 | + |
|---|
| 2117 | + port->cap = kmemdup(cap, sizeof(*cap), GFP_KERNEL); |
|---|
| 2118 | + if (!port->cap) { |
|---|
| 2119 | + put_device(&port->dev); |
|---|
| 2120 | + return ERR_PTR(-ENOMEM); |
|---|
| 2121 | + } |
|---|
| 1661 | 2122 | |
|---|
| 1662 | 2123 | port->sw = typec_switch_get(&port->dev); |
|---|
| 1663 | 2124 | if (IS_ERR(port->sw)) { |
|---|