root/drivers/base/devcon.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fwnode_graph_devcon_match
  2. fwnode_devcon_match
  3. fwnode_connection_find_match
  4. device_connection_find_match
  5. device_connection_fwnode_match
  6. generic_match
  7. device_connection_find
  8. device_connection_add
  9. device_connection_remove

   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 fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
  17                           void *data, devcon_match_fn_t match)
  18 {
  19         struct device_connection con = { .id = con_id };
  20         struct fwnode_handle *ep;
  21         void *ret;
  22 
  23         fwnode_graph_for_each_endpoint(fwnode, ep) {
  24                 con.fwnode = fwnode_graph_get_remote_port_parent(ep);
  25                 if (!fwnode_device_is_available(con.fwnode))
  26                         continue;
  27 
  28                 ret = match(&con, -1, data);
  29                 fwnode_handle_put(con.fwnode);
  30                 if (ret) {
  31                         fwnode_handle_put(ep);
  32                         return ret;
  33                 }
  34         }
  35         return NULL;
  36 }
  37 
  38 static void *
  39 fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
  40                     void *data, devcon_match_fn_t match)
  41 {
  42         struct device_connection con = { };
  43         void *ret;
  44         int i;
  45 
  46         for (i = 0; ; i++) {
  47                 con.fwnode = fwnode_find_reference(fwnode, con_id, i);
  48                 if (IS_ERR(con.fwnode))
  49                         break;
  50 
  51                 ret = match(&con, -1, data);
  52                 fwnode_handle_put(con.fwnode);
  53                 if (ret)
  54                         return ret;
  55         }
  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 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         if (!fwnode || !match)
  78                 return NULL;
  79 
  80         ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
  81         if (ret)
  82                 return ret;
  83 
  84         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 void *device_connection_find_match(struct device *dev, const char *con_id,
 100                                    void *data, devcon_match_fn_t match)
 101 {
 102         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         if (!match)
 109                 return NULL;
 110 
 111         ret = fwnode_connection_find_match(fwnode, con_id, data, match);
 112         if (ret)
 113                 return ret;
 114 
 115         mutex_lock(&devcon_lock);
 116 
 117         list_for_each_entry(con, &devcon_list, list) {
 118                 ep = match_string(con->endpoint, 2, devname);
 119                 if (ep < 0)
 120                         continue;
 121 
 122                 if (con_id && strcmp(con->id, con_id))
 123                         continue;
 124 
 125                 ret = match(con, !ep, data);
 126                 if (ret)
 127                         break;
 128         }
 129 
 130         mutex_unlock(&devcon_lock);
 131 
 132         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 static void *device_connection_fwnode_match(struct device_connection *con)
 156 {
 157         struct bus_type *bus;
 158         struct device *dev;
 159 
 160         for (bus = generic_match_buses[0]; bus; bus++) {
 161                 dev = bus_find_device_by_fwnode(bus, con->fwnode);
 162                 if (dev && !strncmp(dev_name(dev), con->id, strlen(con->id)))
 163                         return dev;
 164 
 165                 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 static void *generic_match(struct device_connection *con, int ep, void *data)
 172 {
 173         struct bus_type *bus;
 174         struct device *dev;
 175 
 176         if (con->fwnode)
 177                 return device_connection_fwnode_match(con);
 178 
 179         for (bus = generic_match_buses[0]; bus; bus++) {
 180                 dev = bus_find_device_by_name(bus, NULL, con->endpoint[ep]);
 181                 if (dev)
 182                         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 struct device *device_connection_find(struct device *dev, const char *con_id)
 204 {
 205         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 void device_connection_add(struct device_connection *con)
 214 {
 215         mutex_lock(&devcon_lock);
 216         list_add_tail(&con->list, &devcon_list);
 217         mutex_unlock(&devcon_lock);
 218 }
 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 void device_connection_remove(struct device_connection *con)
 226 {
 227         mutex_lock(&devcon_lock);
 228         list_del(&con->list);
 229         mutex_unlock(&devcon_lock);
 230 }
 231 EXPORT_SYMBOL_GPL(device_connection_remove);

/* [<][>][^][v][top][bottom][index][help] */