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(dev, &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(dev, &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(&arg->port->dev, &arg->match, typec_port_compare, 56 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(&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 85void 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