at v5.2 4.9 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/** 3 * Device connections 4 * 5 * Copyright (C) 2018 Intel Corporation 6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 7 */ 8 9#include <linux/device.h> 10#include <linux/property.h> 11 12static DEFINE_MUTEX(devcon_lock); 13static LIST_HEAD(devcon_list); 14 15typedef void *(*devcon_match_fn_t)(struct device_connection *con, int ep, 16 void *data); 17 18static void * 19fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id, 20 void *data, devcon_match_fn_t match) 21{ 22 struct device_connection con = { .id = con_id }; 23 struct fwnode_handle *ep; 24 void *ret; 25 26 fwnode_graph_for_each_endpoint(fwnode, ep) { 27 con.fwnode = fwnode_graph_get_remote_port_parent(ep); 28 if (!fwnode_device_is_available(con.fwnode)) 29 continue; 30 31 ret = match(&con, -1, data); 32 fwnode_handle_put(con.fwnode); 33 if (ret) { 34 fwnode_handle_put(ep); 35 return ret; 36 } 37 } 38 return NULL; 39} 40 41/** 42 * device_connection_find_match - Find physical connection to a device 43 * @dev: Device with the connection 44 * @con_id: Identifier for the connection 45 * @data: Data for the match function 46 * @match: Function to check and convert the connection description 47 * 48 * Find a connection with unique identifier @con_id between @dev and another 49 * device. @match will be used to convert the connection description to data the 50 * caller is expecting to be returned. 51 */ 52void *device_connection_find_match(struct device *dev, const char *con_id, 53 void *data, devcon_match_fn_t match) 54{ 55 struct fwnode_handle *fwnode = dev_fwnode(dev); 56 const char *devname = dev_name(dev); 57 struct device_connection *con; 58 void *ret = NULL; 59 int ep; 60 61 if (!match) 62 return NULL; 63 64 if (fwnode) { 65 ret = fwnode_graph_devcon_match(fwnode, con_id, data, match); 66 if (ret) 67 return ret; 68 } 69 70 mutex_lock(&devcon_lock); 71 72 list_for_each_entry(con, &devcon_list, list) { 73 ep = match_string(con->endpoint, 2, devname); 74 if (ep < 0) 75 continue; 76 77 if (con_id && strcmp(con->id, con_id)) 78 continue; 79 80 ret = match(con, !ep, data); 81 if (ret) 82 break; 83 } 84 85 mutex_unlock(&devcon_lock); 86 87 return ret; 88} 89EXPORT_SYMBOL_GPL(device_connection_find_match); 90 91extern struct bus_type platform_bus_type; 92extern struct bus_type pci_bus_type; 93extern struct bus_type i2c_bus_type; 94extern struct bus_type spi_bus_type; 95 96static struct bus_type *generic_match_buses[] = { 97 &platform_bus_type, 98#ifdef CONFIG_PCI 99 &pci_bus_type, 100#endif 101#ifdef CONFIG_I2C 102 &i2c_bus_type, 103#endif 104#ifdef CONFIG_SPI_MASTER 105 &spi_bus_type, 106#endif 107 NULL, 108}; 109 110static int device_fwnode_match(struct device *dev, void *fwnode) 111{ 112 return dev_fwnode(dev) == fwnode; 113} 114 115static void *device_connection_fwnode_match(struct device_connection *con) 116{ 117 struct bus_type *bus; 118 struct device *dev; 119 120 for (bus = generic_match_buses[0]; bus; bus++) { 121 dev = bus_find_device(bus, NULL, (void *)con->fwnode, 122 device_fwnode_match); 123 if (dev && !strncmp(dev_name(dev), con->id, strlen(con->id))) 124 return dev; 125 126 put_device(dev); 127 } 128 return NULL; 129} 130 131/* This tries to find the device from the most common bus types by name. */ 132static void *generic_match(struct device_connection *con, int ep, void *data) 133{ 134 struct bus_type *bus; 135 struct device *dev; 136 137 if (con->fwnode) 138 return device_connection_fwnode_match(con); 139 140 for (bus = generic_match_buses[0]; bus; bus++) { 141 dev = bus_find_device_by_name(bus, NULL, con->endpoint[ep]); 142 if (dev) 143 return dev; 144 } 145 146 /* 147 * We only get called if a connection was found, tell the caller to 148 * wait for the other device to show up. 149 */ 150 return ERR_PTR(-EPROBE_DEFER); 151} 152 153/** 154 * device_connection_find - Find two devices connected together 155 * @dev: Device with the connection 156 * @con_id: Identifier for the connection 157 * 158 * Find a connection with unique identifier @con_id between @dev and 159 * another device. On success returns handle to the device that is connected 160 * to @dev, with the reference count for the found device incremented. Returns 161 * NULL if no matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a 162 * connection was found but the other device has not been enumerated yet. 163 */ 164struct device *device_connection_find(struct device *dev, const char *con_id) 165{ 166 return device_connection_find_match(dev, con_id, NULL, generic_match); 167} 168EXPORT_SYMBOL_GPL(device_connection_find); 169 170/** 171 * device_connection_add - Register a connection description 172 * @con: The connection description to be registered 173 */ 174void device_connection_add(struct device_connection *con) 175{ 176 mutex_lock(&devcon_lock); 177 list_add_tail(&con->list, &devcon_list); 178 mutex_unlock(&devcon_lock); 179} 180EXPORT_SYMBOL_GPL(device_connection_add); 181 182/** 183 * device_connections_remove - Unregister connection description 184 * @con: The connection description to be unregistered 185 */ 186void device_connection_remove(struct device_connection *con) 187{ 188 mutex_lock(&devcon_lock); 189 list_del(&con->list); 190 mutex_unlock(&devcon_lock); 191} 192EXPORT_SYMBOL_GPL(device_connection_remove);