xref: /linux/drivers/usb/typec/port-mapper.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * USB Type-C Connector Class Port Mapping Utility
4  *
5  * Copyright (C) 2021, Intel Corporation
6  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7  */
8 
9 #include <linux/acpi.h>
10 #include <linux/component.h>
11 #include <linux/usb.h>
12 
13 #include "class.h"
14 
typec_aggregate_bind(struct device * dev)15 static int typec_aggregate_bind(struct device *dev)
16 {
17 	struct typec_port *port = to_typec_port(dev);
18 
19 	return component_bind_all(dev, &port->con);
20 }
21 
typec_aggregate_unbind(struct device * dev)22 static void typec_aggregate_unbind(struct device *dev)
23 {
24 	struct typec_port *port = to_typec_port(dev);
25 
26 	component_unbind_all(dev, &port->con);
27 }
28 
29 static const struct component_master_ops typec_aggregate_ops = {
30 	.bind = typec_aggregate_bind,
31 	.unbind = typec_aggregate_unbind,
32 };
33 
34 struct each_port_arg {
35 	struct typec_port *port;
36 	struct component_match *match;
37 };
38 
typec_port_compare(struct device * dev,void * fwnode)39 static int typec_port_compare(struct device *dev, void *fwnode)
40 {
41 	return device_match_fwnode(dev, fwnode);
42 }
43 
typec_port_match(struct device * dev,void * data)44 static int typec_port_match(struct device *dev, void *data)
45 {
46 	struct acpi_device *adev = to_acpi_device(dev);
47 	struct each_port_arg *arg = data;
48 	struct acpi_device *con_adev;
49 
50 	con_adev = ACPI_COMPANION(&arg->port->dev);
51 	if (con_adev == adev)
52 		return 0;
53 
54 	if (con_adev->pld_crc == adev->pld_crc)
55 		component_match_add(&arg->port->dev, &arg->match, typec_port_compare,
56 				    acpi_fwnode_handle(adev));
57 	return 0;
58 }
59 
typec_link_ports(struct typec_port * con)60 int typec_link_ports(struct typec_port *con)
61 {
62 	struct each_port_arg arg = { .port = con, .match = NULL };
63 
64 	if (!has_acpi_companion(&con->dev))
65 		return 0;
66 
67 	acpi_bus_for_each_dev(typec_port_match, &arg);
68 	if (!arg.match)
69 		return 0;
70 
71 	/*
72 	 * REVISIT: Now each connector can have only a single component master.
73 	 * So far only the USB ports connected to the USB Type-C connector share
74 	 * the _PLD with it, but if there one day is something else (like maybe
75 	 * the DisplayPort ACPI device object) that also shares the _PLD with
76 	 * the connector, every one of those needs to have its own component
77 	 * master, because each different type of component needs to be bind to
78 	 * the connector independently of the other components. That requires
79 	 * improvements to the component framework. Right now you can only have
80 	 * one master per device.
81 	 */
82 	return component_master_add_with_match(&con->dev, &typec_aggregate_ops, arg.match);
83 }
84 
typec_unlink_ports(struct typec_port * con)85 void typec_unlink_ports(struct typec_port *con)
86 {
87 	if (has_acpi_companion(&con->dev))
88 		component_master_del(&con->dev, &typec_aggregate_ops);
89 }
90