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