diff options
Diffstat (limited to 'drivers/usb/typec/class.c')
-rw-r--r-- | drivers/usb/typec/class.c | 155 |
1 files changed, 80 insertions, 75 deletions
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 45f0bf65e9ab..b9429c9f65f6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -6,78 +6,23 @@ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> */ -#include <linux/device.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/property.h> #include <linux/slab.h> #include <linux/usb/pd_vdo.h> +#include <linux/usb/typec_mux.h> #include "bus.h" +#include "class.h" -struct typec_plug { - struct device dev; - enum typec_plug_index index; - struct ida mode_ids; - int num_altmodes; -}; - -struct typec_cable { - struct device dev; - enum typec_plug_type type; - struct usb_pd_identity *identity; - unsigned int active:1; - u16 pd_revision; /* 0300H = "3.0" */ -}; - -struct typec_partner { - struct device dev; - unsigned int usb_pd:1; - struct usb_pd_identity *identity; - enum typec_accessory accessory; - struct ida mode_ids; - int num_altmodes; - u16 pd_revision; /* 0300H = "3.0" */ - enum usb_pd_svdm_ver svdm_version; -}; +static DEFINE_IDA(typec_index_ida); -struct typec_port { - unsigned int id; - struct device dev; - struct ida mode_ids; - - int prefer_role; - enum typec_data_role data_role; - enum typec_role pwr_role; - enum typec_role vconn_role; - enum typec_pwr_opmode pwr_opmode; - enum typec_port_type port_type; - struct mutex port_type_lock; - - enum typec_orientation orientation; - struct typec_switch *sw; - struct typec_mux *mux; - - const struct typec_capability *cap; - const struct typec_operations *ops; +struct class typec_class = { + .name = "typec", + .owner = THIS_MODULE, }; -#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev) -#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev) -#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev) -#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev) - -static const struct device_type typec_partner_dev_type; -static const struct device_type typec_cable_dev_type; -static const struct device_type typec_plug_dev_type; - -#define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type) -#define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type) -#define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type) - -static DEFINE_IDA(typec_index_ida); -static struct class *typec_class; - /* ------------------------------------------------------------------------- */ /* Common attributes */ @@ -610,7 +555,7 @@ typec_register_altmode(struct device *parent, /* Plug alt modes need a class to generate udev events. */ if (is_typec_plug(parent)) - alt->adev.dev.class = typec_class; + alt->adev.dev.class = &typec_class; ret = device_register(&alt->adev.dev); if (ret) { @@ -726,7 +671,7 @@ static void typec_partner_release(struct device *dev) kfree(partner); } -static const struct device_type typec_partner_dev_type = { +const struct device_type typec_partner_dev_type = { .name = "typec_partner", .groups = typec_partner_groups, .release = typec_partner_release, @@ -874,7 +819,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->identity = desc->identity; } - partner->dev.class = typec_class; + partner->dev.class = &typec_class; partner->dev.parent = &port->dev; partner->dev.type = &typec_partner_dev_type; dev_set_name(&partner->dev, "%s-partner", dev_name(&port->dev)); @@ -941,7 +886,7 @@ static const struct attribute_group *typec_plug_groups[] = { NULL }; -static const struct device_type typec_plug_dev_type = { +const struct device_type typec_plug_dev_type = { .name = "typec_plug", .groups = typec_plug_groups, .release = typec_plug_release, @@ -1026,7 +971,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, ida_init(&plug->mode_ids); plug->num_altmodes = -1; plug->index = desc->index; - plug->dev.class = typec_class; + plug->dev.class = &typec_class; plug->dev.parent = &cable->dev; plug->dev.type = &typec_plug_dev_type; dev_set_name(&plug->dev, "%s-%s", dev_name(cable->dev.parent), name); @@ -1089,7 +1034,7 @@ static void typec_cable_release(struct device *dev) kfree(cable); } -static const struct device_type typec_cable_dev_type = { +const struct device_type typec_cable_dev_type = { .name = "typec_cable", .groups = typec_cable_groups, .release = typec_cable_release, @@ -1191,7 +1136,7 @@ struct typec_cable *typec_register_cable(struct typec_port *port, cable->identity = desc->identity; } - cable->dev.class = typec_class; + cable->dev.class = &typec_class; cable->dev.parent = &port->dev; cable->dev.type = &typec_cable_dev_type; dev_set_name(&cable->dev, "%s-cable", dev_name(&port->dev)); @@ -1978,6 +1923,60 @@ typec_port_register_altmode(struct typec_port *port, } EXPORT_SYMBOL_GPL(typec_port_register_altmode); +void typec_port_register_altmodes(struct typec_port *port, + const struct typec_altmode_ops *ops, void *drvdata, + struct typec_altmode **altmodes, size_t n) +{ + struct fwnode_handle *altmodes_node, *child; + struct typec_altmode_desc desc; + struct typec_altmode *alt; + size_t index = 0; + u32 svid, vdo; + int ret; + + altmodes_node = device_get_named_child_node(&port->dev, "altmodes"); + if (!altmodes_node) + return; /* No altmodes specified */ + + fwnode_for_each_child_node(altmodes_node, child) { + ret = fwnode_property_read_u32(child, "svid", &svid); + if (ret) { + dev_err(&port->dev, "Error reading svid for altmode %s\n", + fwnode_get_name(child)); + continue; + } + + ret = fwnode_property_read_u32(child, "vdo", &vdo); + if (ret) { + dev_err(&port->dev, "Error reading vdo for altmode %s\n", + fwnode_get_name(child)); + continue; + } + + if (index >= n) { + dev_err(&port->dev, "Error not enough space for altmode %s\n", + fwnode_get_name(child)); + continue; + } + + desc.svid = svid; + desc.vdo = vdo; + desc.mode = index + 1; + alt = typec_port_register_altmode(port, &desc); + if (IS_ERR(alt)) { + dev_err(&port->dev, "Error registering altmode %s\n", + fwnode_get_name(child)); + continue; + } + + alt->ops = ops; + typec_altmode_set_drvdata(alt, drvdata); + altmodes[index] = alt; + index++; + } +} +EXPORT_SYMBOL_GPL(typec_port_register_altmodes); + /** * typec_register_port - Register a USB Type-C Port * @parent: Parent device @@ -2038,6 +2037,8 @@ struct typec_port *typec_register_port(struct device *parent, ida_init(&port->mode_ids); mutex_init(&port->port_type_lock); + mutex_init(&port->port_list_lock); + INIT_LIST_HEAD(&port->port_list); port->id = id; port->ops = cap->ops; @@ -2045,7 +2046,7 @@ struct typec_port *typec_register_port(struct device *parent, port->prefer_role = cap->prefer_role; device_initialize(&port->dev); - port->dev.class = typec_class; + port->dev.class = &typec_class; port->dev.parent = parent; port->dev.fwnode = cap->fwnode; port->dev.type = &typec_port_dev_type; @@ -2079,6 +2080,10 @@ struct typec_port *typec_register_port(struct device *parent, return ERR_PTR(ret); } + ret = typec_link_ports(port); + if (ret) + dev_warn(&port->dev, "failed to create symlinks (%d)\n", ret); + return port; } EXPORT_SYMBOL_GPL(typec_register_port); @@ -2091,8 +2096,10 @@ EXPORT_SYMBOL_GPL(typec_register_port); */ void typec_unregister_port(struct typec_port *port) { - if (!IS_ERR_OR_NULL(port)) + if (!IS_ERR_OR_NULL(port)) { + typec_unlink_ports(port); device_unregister(&port->dev); + } } EXPORT_SYMBOL_GPL(typec_unregister_port); @@ -2108,11 +2115,9 @@ static int __init typec_init(void) if (ret) goto err_unregister_bus; - typec_class = class_create(THIS_MODULE, "typec"); - if (IS_ERR(typec_class)) { - ret = PTR_ERR(typec_class); + ret = class_register(&typec_class); + if (ret) goto err_unregister_mux_class; - } return 0; @@ -2128,7 +2133,7 @@ subsys_initcall(typec_init); static void __exit typec_exit(void) { - class_destroy(typec_class); + class_unregister(&typec_class); ida_destroy(&typec_index_ida); bus_unregister(&typec_bus); class_unregister(&typec_mux_class); |