#include <linux/usb/pd_vdo.h>
#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_retimer.h>
+#include <linux/usb.h>
#include "bus.h"
#include "class.h"
.release = typec_partner_release,
};
+static void typec_partner_link_device(struct typec_partner *partner, struct device *dev)
+{
+ int ret;
+
+ ret = sysfs_create_link(&dev->kobj, &partner->dev.kobj, "typec");
+ if (ret)
+ return;
+
+ ret = sysfs_create_link(&partner->dev.kobj, &dev->kobj, dev_name(dev));
+ if (ret) {
+ sysfs_remove_link(&dev->kobj, "typec");
+ return;
+ }
+
+ if (partner->attach)
+ partner->attach(partner, dev);
+}
+
+static void typec_partner_unlink_device(struct typec_partner *partner, struct device *dev)
+{
+ sysfs_remove_link(&partner->dev.kobj, dev_name(dev));
+ sysfs_remove_link(&dev->kobj, "typec");
+
+ if (partner->deattach)
+ partner->deattach(partner, dev);
+}
+
/**
* typec_partner_set_identity - Report result from Discover Identity command
* @partner: The partner updated identity values
partner->num_altmodes = -1;
partner->pd_revision = desc->pd_revision;
partner->svdm_version = port->cap->svdm_version;
+ partner->attach = desc->attach;
+ partner->deattach = desc->deattach;
if (desc->identity) {
/*
return ERR_PTR(ret);
}
+ if (port->usb2_dev)
+ typec_partner_link_device(partner, port->usb2_dev);
+ if (port->usb3_dev)
+ typec_partner_link_device(partner, port->usb3_dev);
+
return partner;
}
EXPORT_SYMBOL_GPL(typec_register_partner);
*/
void typec_unregister_partner(struct typec_partner *partner)
{
- if (!IS_ERR_OR_NULL(partner))
- device_unregister(&partner->dev);
+ struct typec_port *port;
+
+ if (IS_ERR_OR_NULL(partner))
+ return;
+
+ port = to_typec_port(partner->dev.parent);
+
+ if (port->usb2_dev)
+ typec_partner_unlink_device(partner, port->usb2_dev);
+ if (port->usb3_dev)
+ typec_partner_unlink_device(partner, port->usb3_dev);
+
+ device_unregister(&partner->dev);
}
EXPORT_SYMBOL_GPL(typec_unregister_partner);
return is_typec_partner(dev);
}
+static struct typec_partner *typec_get_partner(struct typec_port *port)
+{
+ struct device *dev;
+
+ dev = device_find_child(&port->dev, NULL, partner_match);
+ if (!dev)
+ return NULL;
+
+ return to_typec_partner(dev);
+}
+
+static void typec_partner_attach(struct typec_connector *con, struct device *dev)
+{
+ struct typec_port *port = container_of(con, struct typec_port, con);
+ struct typec_partner *partner = typec_get_partner(port);
+ struct usb_device *udev = to_usb_device(dev);
+
+ if (udev->speed < USB_SPEED_SUPER)
+ port->usb2_dev = dev;
+ else
+ port->usb3_dev = dev;
+
+ if (partner) {
+ typec_partner_link_device(partner, dev);
+ put_device(&partner->dev);
+ }
+}
+
+static void typec_partner_deattach(struct typec_connector *con, struct device *dev)
+{
+ struct typec_port *port = container_of(con, struct typec_port, con);
+ struct typec_partner *partner = typec_get_partner(port);
+
+ if (partner) {
+ typec_partner_unlink_device(partner, dev);
+ put_device(&partner->dev);
+ }
+
+ if (port->usb2_dev == dev)
+ port->usb2_dev = NULL;
+ else if (port->usb3_dev == dev)
+ port->usb3_dev = NULL;
+}
+
/**
* typec_set_data_role - Report data role change
* @port: The USB Type-C Port where the role was changed
*/
void typec_set_data_role(struct typec_port *port, enum typec_data_role role)
{
- struct device *partner_dev;
+ struct typec_partner *partner;
if (port->data_role == role)
return;
sysfs_notify(&port->dev.kobj, NULL, "data_role");
kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
- partner_dev = device_find_child(&port->dev, NULL, partner_match);
- if (!partner_dev)
+ partner = typec_get_partner(port);
+ if (!partner)
return;
- if (to_typec_partner(partner_dev)->identity)
- typec_product_type_notify(partner_dev);
+ if (partner->identity)
+ typec_product_type_notify(&partner->dev);
- put_device(partner_dev);
+ put_device(&partner->dev);
}
EXPORT_SYMBOL_GPL(typec_set_data_role);
port->ops = cap->ops;
port->port_type = cap->type;
port->prefer_role = cap->prefer_role;
+ port->con.attach = typec_partner_attach;
+ port->con.deattach = typec_partner_deattach;
device_initialize(&port->dev);
port->dev.class = &typec_class;
struct typec_mux;
struct typec_switch;
+struct usb_device;
struct typec_plug {
struct device dev;
enum usb_pd_svdm_ver svdm_version;
struct usb_power_delivery *pd;
+
+ void (*attach)(struct typec_partner *partner, struct device *dev);
+ void (*deattach)(struct typec_partner *partner, struct device *dev);
};
struct typec_port {
const struct typec_capability *cap;
const struct typec_operations *ops;
+
+ struct typec_connector con;
+
+ /*
+ * REVISIT: Only USB devices for now. If there are others, these need to
+ * be converted into a list.
+ *
+ * NOTE: These may be registered first before the typec_partner, so they
+ * will always have to be kept here instead of struct typec_partner.
+ */
+ struct device *usb2_dev;
+ struct device *usb3_dev;
};
#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
* @accessory: Audio, Debug or none.
* @identity: Discover Identity command data
* @pd_revision: USB Power Delivery Specification Revision if supported
+ * @attach: Notification about attached USB device
+ * @deattach: Notification about removed USB device
*
* Details about a partner that is attached to USB Type-C port. If @identity
* member exists when partner is registered, a directory named "identity" is
enum typec_accessory accessory;
struct usb_pd_identity *identity;
u16 pd_revision; /* 0300H = "3.0" */
+
+ void (*attach)(struct typec_partner *partner, struct device *dev);
+ void (*deattach)(struct typec_partner *partner, struct device *dev);
};
/**
int typec_partner_set_usb_power_delivery(struct typec_partner *partner,
struct usb_power_delivery *pd);
+/**
+ * struct typec_connector - Representation of Type-C port for external drivers
+ * @attach: notification about device removal
+ * @deattach: notification about device removal
+ *
+ * Drivers that control the USB and other ports (DisplayPorts, etc.), that are
+ * connected to the Type-C connectors, can use these callbacks to inform the
+ * Type-C connector class about connections and disconnections. That information
+ * can then be used by the typec-port drivers to power on or off parts that are
+ * needed or not needed - as an example, in USB mode if USB2 device is
+ * enumerated, USB3 components (retimers, phys, and what have you) do not need
+ * to be powered on.
+ *
+ * The attached (enumerated) devices will be liked with the typec-partner device.
+ */
+struct typec_connector {
+ void (*attach)(struct typec_connector *con, struct device *dev);
+ void (*deattach)(struct typec_connector *con, struct device *dev);
+};
+
+static inline void typec_attach(struct typec_connector *con, struct device *dev)
+{
+ if (con && con->attach)
+ con->attach(con, dev);
+}
+
+static inline void typec_deattach(struct typec_connector *con, struct device *dev)
+{
+ if (con && con->deattach)
+ con->deattach(con, dev);
+}
+
#endif /* __LINUX_USB_TYPEC_H */