at v5.3 5.4 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 41static void * 42fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id, 43 void *data, devcon_match_fn_t match) 44{ 45 struct device_connection con = { }; 46 void *ret; 47 int i; 48 49 for (i = 0; ; i++) { 50 con.fwnode = fwnode_find_reference(fwnode, con_id, i); 51 if (IS_ERR(con.fwnode)) 52 break; 53 54 ret = match(&con, -1, data); 55 fwnode_handle_put(con.fwnode); 56 if (ret) 57 return ret; 58 } 59 60 return NULL; 61} 62 63/** 64 * device_connection_find_match - Find physical connection to a device 65 * @dev: Device with the connection 66 * @con_id: Identifier for the connection 67 * @data: Data for the match function 68 * @match: Function to check and convert the connection description 69 * 70 * Find a connection with unique identifier @con_id between @dev and another 71 * device. @match will be used to convert the connection description to data the 72 * caller is expecting to be returned. 73 */ 74void *device_connection_find_match(struct device *dev, const char *con_id, 75 void *data, devcon_match_fn_t match) 76{ 77 struct fwnode_handle *fwnode = dev_fwnode(dev); 78 const char *devname = dev_name(dev); 79 struct device_connection *con; 80 void *ret = NULL; 81 int ep; 82 83 if (!match) 84 return NULL; 85 86 if (fwnode) { 87 ret = fwnode_graph_devcon_match(fwnode, con_id, data, match); 88 if (ret) 89 return ret; 90 91 ret = fwnode_devcon_match(fwnode, con_id, data, match); 92 if (ret) 93 return ret; 94 } 95 96 mutex_lock(&devcon_lock); 97 98 list_for_each_entry(con, &devcon_list, list) { 99 ep = match_string(con->endpoint, 2, devname); 100 if (ep < 0) 101 continue; 102 103 if (con_id && strcmp(con->id, con_id)) 104 continue; 105 106 ret = match(con, !ep, data); 107 if (ret) 108 break; 109 } 110 111 mutex_unlock(&devcon_lock); 112 113 return ret; 114} 115EXPORT_SYMBOL_GPL(device_connection_find_match); 116 117extern struct bus_type platform_bus_type; 118extern struct bus_type pci_bus_type; 119extern struct bus_type i2c_bus_type; 120extern struct bus_type spi_bus_type; 121 122static struct bus_type *generic_match_buses[] = { 123 &platform_bus_type, 124#ifdef CONFIG_PCI 125 &pci_bus_type, 126#endif 127#ifdef CONFIG_I2C 128 &i2c_bus_type, 129#endif 130#ifdef CONFIG_SPI_MASTER 131 &spi_bus_type, 132#endif 133 NULL, 134}; 135 136static int device_fwnode_match(struct device *dev, const void *fwnode) 137{ 138 return dev_fwnode(dev) == fwnode; 139} 140 141static void *device_connection_fwnode_match(struct device_connection *con) 142{ 143 struct bus_type *bus; 144 struct device *dev; 145 146 for (bus = generic_match_buses[0]; bus; bus++) { 147 dev = bus_find_device(bus, NULL, (void *)con->fwnode, 148 device_fwnode_match); 149 if (dev && !strncmp(dev_name(dev), con->id, strlen(con->id))) 150 return dev; 151 152 put_device(dev); 153 } 154 return NULL; 155} 156 157/* This tries to find the device from the most common bus types by name. */ 158static void *generic_match(struct device_connection *con, int ep, void *data) 159{ 160 struct bus_type *bus; 161 struct device *dev; 162 163 if (con->fwnode) 164 return device_connection_fwnode_match(con); 165 166 for (bus = generic_match_buses[0]; bus; bus++) { 167 dev = bus_find_device_by_name(bus, NULL, con->endpoint[ep]); 168 if (dev) 169 return dev; 170 } 171 172 /* 173 * We only get called if a connection was found, tell the caller to 174 * wait for the other device to show up. 175 */ 176 return ERR_PTR(-EPROBE_DEFER); 177} 178 179/** 180 * device_connection_find - Find two devices connected together 181 * @dev: Device with the connection 182 * @con_id: Identifier for the connection 183 * 184 * Find a connection with unique identifier @con_id between @dev and 185 * another device. On success returns handle to the device that is connected 186 * to @dev, with the reference count for the found device incremented. Returns 187 * NULL if no matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a 188 * connection was found but the other device has not been enumerated yet. 189 */ 190struct device *device_connection_find(struct device *dev, const char *con_id) 191{ 192 return device_connection_find_match(dev, con_id, NULL, generic_match); 193} 194EXPORT_SYMBOL_GPL(device_connection_find); 195 196/** 197 * device_connection_add - Register a connection description 198 * @con: The connection description to be registered 199 */ 200void device_connection_add(struct device_connection *con) 201{ 202 mutex_lock(&devcon_lock); 203 list_add_tail(&con->list, &devcon_list); 204 mutex_unlock(&devcon_lock); 205} 206EXPORT_SYMBOL_GPL(device_connection_add); 207 208/** 209 * device_connections_remove - Unregister connection description 210 * @con: The connection description to be unregistered 211 */ 212void device_connection_remove(struct device_connection *con) 213{ 214 mutex_lock(&devcon_lock); 215 list_del(&con->list); 216 mutex_unlock(&devcon_lock); 217} 218EXPORT_SYMBOL_GPL(device_connection_remove);