summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/usb4_port.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/usb4_port.c')
-rw-r--r--drivers/thunderbolt/usb4_port.c92
1 files changed, 89 insertions, 3 deletions
diff --git a/drivers/thunderbolt/usb4_port.c b/drivers/thunderbolt/usb4_port.c
index 29e2a4f9c9f5..b5e06237261b 100644
--- a/drivers/thunderbolt/usb4_port.c
+++ b/drivers/thunderbolt/usb4_port.c
@@ -7,9 +7,37 @@
*/
#include <linux/pm_runtime.h>
+#include <linux/component.h>
+#include <linux/property.h>
#include "tb.h"
+static int connector_bind(struct device *dev, struct device *connector, void *data)
+{
+ int ret;
+
+ ret = sysfs_create_link(&dev->kobj, &connector->kobj, "connector");
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_link(&connector->kobj, &dev->kobj, dev_name(dev));
+ if (ret)
+ sysfs_remove_link(&dev->kobj, "connector");
+
+ return ret;
+}
+
+static void connector_unbind(struct device *dev, struct device *connector, void *data)
+{
+ sysfs_remove_link(&connector->kobj, dev_name(dev));
+ sysfs_remove_link(&dev->kobj, "connector");
+}
+
+static const struct component_ops connector_ops = {
+ .bind = connector_bind,
+ .unbind = connector_unbind,
+};
+
static ssize_t link_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -25,6 +53,8 @@ static ssize_t link_show(struct device *dev, struct device_attribute *attr,
link = port->sw->link_usb4 ? "usb4" : "tbt";
else if (tb_port_has_remote(port))
link = port->remote->sw->link_usb4 ? "usb4" : "tbt";
+ else if (port->xdomain)
+ link = port->xdomain->link_usb4 ? "usb4" : "tbt";
else
link = "none";
@@ -75,6 +105,49 @@ static void usb4_port_online(struct usb4_port *usb4)
tb_acpi_power_off_retimers(port);
}
+/**
+ * usb4_usb3_port_match() - Matches USB4 port device with USB 3.x port device
+ * @usb4_port_dev: USB4 port device
+ * @usb3_port_fwnode: USB 3.x port firmware node
+ *
+ * Checks if USB 3.x port @usb3_port_fwnode is tunneled through USB4 port @usb4_port_dev.
+ * Returns true if match is found, false otherwise.
+ *
+ * Function is designed to be used with component framework (component_match_add).
+ */
+bool usb4_usb3_port_match(struct device *usb4_port_dev,
+ const struct fwnode_handle *usb3_port_fwnode)
+{
+ struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = NULL;
+ struct usb4_port *usb4;
+ struct tb_switch *sw;
+ struct tb_nhi *nhi;
+ u8 usb4_port_num;
+ struct tb *tb;
+
+ usb4 = tb_to_usb4_port_device(usb4_port_dev);
+ if (!usb4)
+ return false;
+
+ sw = usb4->port->sw;
+ tb = sw->tb;
+ nhi = tb->nhi;
+
+ nhi_fwnode = fwnode_find_reference(usb3_port_fwnode, "usb4-host-interface", 0);
+ if (IS_ERR(nhi_fwnode))
+ return false;
+
+ /* Check if USB3 fwnode references same NHI where USB4 port resides */
+ if (!device_match_fwnode(&nhi->pdev->dev, nhi_fwnode))
+ return false;
+
+ if (fwnode_property_read_u8(usb3_port_fwnode, "usb4-port-number", &usb4_port_num))
+ return false;
+
+ return usb4_port_index(sw, usb4->port) == usb4_port_num;
+}
+EXPORT_SYMBOL_GPL(usb4_usb3_port_match);
+
static ssize_t offline_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -213,7 +286,7 @@ static void usb4_port_device_release(struct device *dev)
kfree(usb4);
}
-struct device_type usb4_port_device_type = {
+const struct device_type usb4_port_device_type = {
.name = "usb4_port",
.groups = usb4_port_device_groups,
.release = usb4_port_device_release,
@@ -223,8 +296,9 @@ struct device_type usb4_port_device_type = {
* usb4_port_device_add() - Add USB4 port device
* @port: Lane 0 adapter port to add the USB4 port
*
- * Creates and registers a USB4 port device for @port. Returns the new
- * USB4 port device pointer or ERR_PTR() in case of error.
+ * Creates and registers a USB4 port device for @port.
+ *
+ * Return: Pointer to &struct usb4_port or ERR_PTR() in case of an error.
*/
struct usb4_port *usb4_port_device_add(struct tb_port *port)
{
@@ -246,6 +320,15 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port)
return ERR_PTR(ret);
}
+ ret = component_add(&usb4->dev, &connector_ops);
+ if (ret) {
+ dev_err(&usb4->dev, "failed to add component\n");
+ device_unregister(&usb4->dev);
+ }
+
+ if (!tb_is_upstream_port(port))
+ device_set_wakeup_capable(&usb4->dev, true);
+
pm_runtime_no_callbacks(&usb4->dev);
pm_runtime_set_active(&usb4->dev);
pm_runtime_enable(&usb4->dev);
@@ -265,6 +348,7 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port)
*/
void usb4_port_device_remove(struct usb4_port *usb4)
{
+ component_del(&usb4->dev, &connector_ops);
device_unregister(&usb4->dev);
}
@@ -273,6 +357,8 @@ void usb4_port_device_remove(struct usb4_port *usb4)
* @usb4: USB4 port device
*
* Used to resume USB4 port device after sleep state.
+ *
+ * Return: %0 on success, negative errno otherwise.
*/
int usb4_port_device_resume(struct usb4_port *usb4)
{