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
15static int typec_aggregate_bind(struct device *dev)
16{
17 struct typec_port *port = to_typec_port(dev);
18
19 return component_bind_all(parent: dev, data: &port->con);
20}
21
22static void typec_aggregate_unbind(struct device *dev)
23{
24 struct typec_port *port = to_typec_port(dev);
25
26 component_unbind_all(parent: dev, data: &port->con);
27}
28
29static const struct component_master_ops typec_aggregate_ops = {
30 .bind = typec_aggregate_bind,
31 .unbind = typec_aggregate_unbind,
32};
33
34struct each_port_arg {
35 struct typec_port *port;
36 struct component_match *match;
37};
38
39static int typec_port_compare(struct device *dev, void *fwnode)
40{
41 return device_match_fwnode(dev, fwnode);
42}
43
44static 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(parent: &arg->port->dev, matchptr: &arg->match, compare: typec_port_compare,
56 compare_data: acpi_fwnode_handle(adev));
57 return 0;
58}
59
60int typec_link_ports(struct typec_port *con)
61{
62 struct each_port_arg arg = { .port = con, .match = NULL };
63
64 if (!has_acpi_companion(dev: &con->dev))
65 return 0;
66
67 acpi_bus_for_each_dev(fn: typec_port_match, data: &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
85void typec_unlink_ports(struct typec_port *con)
86{
87 if (has_acpi_companion(dev: &con->dev))
88 component_master_del(&con->dev, &typec_aggregate_ops);
89}
90

source code of linux/drivers/usb/typec/port-mapper.c