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 | |
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(parent: dev, data: &port->con); |
20 | } |
21 | |
22 | static 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 | |
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 | |
39 | static int typec_port_compare(struct device *dev, void *fwnode) |
40 | { |
41 | return device_match_fwnode(dev, fwnode); |
42 | } |
43 | |
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(parent: &arg->port->dev, matchptr: &arg->match, compare: typec_port_compare, |
56 | compare_data: acpi_fwnode_handle(adev)); |
57 | return 0; |
58 | } |
59 | |
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(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 | |
85 | void 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 | |