Files
android_kernel_xiaomi_sm8450/drivers/base/devcon.c
Linus Torvalds f632a8170a Merge tag 'driver-core-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core and debugfs updates from Greg KH:
 "Here is the "big" driver core and debugfs changes for 5.3-rc1

  It's a lot of different patches, all across the tree due to some api
  changes and lots of debugfs cleanups.

  Other than the debugfs cleanups, in this set of changes we have:

   - bus iteration function cleanups

   - scripts/get_abi.pl tool to display and parse Documentation/ABI
     entries in a simple way

   - cleanups to Documenatation/ABI/ entries to make them parse easier
     due to typos and other minor things

   - default_attrs use for some ktype users

   - driver model documentation file conversions to .rst

   - compressed firmware file loading

   - deferred probe fixes

  All of these have been in linux-next for a while, with a bunch of
  merge issues that Stephen has been patient with me for"

* tag 'driver-core-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (102 commits)
  debugfs: make error message a bit more verbose
  orangefs: fix build warning from debugfs cleanup patch
  ubifs: fix build warning after debugfs cleanup patch
  driver: core: Allow subsystems to continue deferring probe
  drivers: base: cacheinfo: Ensure cpu hotplug work is done before Intel RDT
  arch_topology: Remove error messages on out-of-memory conditions
  lib: notifier-error-inject: no need to check return value of debugfs_create functions
  swiotlb: no need to check return value of debugfs_create functions
  ceph: no need to check return value of debugfs_create functions
  sunrpc: no need to check return value of debugfs_create functions
  ubifs: no need to check return value of debugfs_create functions
  orangefs: no need to check return value of debugfs_create functions
  nfsd: no need to check return value of debugfs_create functions
  lib: 842: no need to check return value of debugfs_create functions
  debugfs: provide pr_fmt() macro
  debugfs: log errors when something goes wrong
  drivers: s390/cio: Fix compilation warning about const qualifiers
  drivers: Add generic helper to match by of_node
  driver_find_device: Unify the match function with class_find_device()
  bus_find_device: Unify the match callback with class_find_device
  ...
2019-07-12 12:24:03 -07:00

219 lines
5.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/**
* Device connections
*
* Copyright (C) 2018 Intel Corporation
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*/
#include <linux/device.h>
#include <linux/property.h>
static DEFINE_MUTEX(devcon_lock);
static LIST_HEAD(devcon_list);
typedef void *(*devcon_match_fn_t)(struct device_connection *con, int ep,
void *data);
static void *
fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
void *data, devcon_match_fn_t match)
{
struct device_connection con = { .id = con_id };
struct fwnode_handle *ep;
void *ret;
fwnode_graph_for_each_endpoint(fwnode, ep) {
con.fwnode = fwnode_graph_get_remote_port_parent(ep);
if (!fwnode_device_is_available(con.fwnode))
continue;
ret = match(&con, -1, data);
fwnode_handle_put(con.fwnode);
if (ret) {
fwnode_handle_put(ep);
return ret;
}
}
return NULL;
}
static void *
fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
void *data, devcon_match_fn_t match)
{
struct device_connection con = { };
void *ret;
int i;
for (i = 0; ; i++) {
con.fwnode = fwnode_find_reference(fwnode, con_id, i);
if (IS_ERR(con.fwnode))
break;
ret = match(&con, -1, data);
fwnode_handle_put(con.fwnode);
if (ret)
return ret;
}
return NULL;
}
/**
* device_connection_find_match - Find physical connection to a device
* @dev: Device with the connection
* @con_id: Identifier for the connection
* @data: Data for the match function
* @match: Function to check and convert the connection description
*
* Find a connection with unique identifier @con_id between @dev and another
* device. @match will be used to convert the connection description to data the
* caller is expecting to be returned.
*/
void *device_connection_find_match(struct device *dev, const char *con_id,
void *data, devcon_match_fn_t match)
{
struct fwnode_handle *fwnode = dev_fwnode(dev);
const char *devname = dev_name(dev);
struct device_connection *con;
void *ret = NULL;
int ep;
if (!match)
return NULL;
if (fwnode) {
ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
if (ret)
return ret;
ret = fwnode_devcon_match(fwnode, con_id, data, match);
if (ret)
return ret;
}
mutex_lock(&devcon_lock);
list_for_each_entry(con, &devcon_list, list) {
ep = match_string(con->endpoint, 2, devname);
if (ep < 0)
continue;
if (con_id && strcmp(con->id, con_id))
continue;
ret = match(con, !ep, data);
if (ret)
break;
}
mutex_unlock(&devcon_lock);
return ret;
}
EXPORT_SYMBOL_GPL(device_connection_find_match);
extern struct bus_type platform_bus_type;
extern struct bus_type pci_bus_type;
extern struct bus_type i2c_bus_type;
extern struct bus_type spi_bus_type;
static struct bus_type *generic_match_buses[] = {
&platform_bus_type,
#ifdef CONFIG_PCI
&pci_bus_type,
#endif
#ifdef CONFIG_I2C
&i2c_bus_type,
#endif
#ifdef CONFIG_SPI_MASTER
&spi_bus_type,
#endif
NULL,
};
static int device_fwnode_match(struct device *dev, const void *fwnode)
{
return dev_fwnode(dev) == fwnode;
}
static void *device_connection_fwnode_match(struct device_connection *con)
{
struct bus_type *bus;
struct device *dev;
for (bus = generic_match_buses[0]; bus; bus++) {
dev = bus_find_device(bus, NULL, (void *)con->fwnode,
device_fwnode_match);
if (dev && !strncmp(dev_name(dev), con->id, strlen(con->id)))
return dev;
put_device(dev);
}
return NULL;
}
/* This tries to find the device from the most common bus types by name. */
static void *generic_match(struct device_connection *con, int ep, void *data)
{
struct bus_type *bus;
struct device *dev;
if (con->fwnode)
return device_connection_fwnode_match(con);
for (bus = generic_match_buses[0]; bus; bus++) {
dev = bus_find_device_by_name(bus, NULL, con->endpoint[ep]);
if (dev)
return dev;
}
/*
* We only get called if a connection was found, tell the caller to
* wait for the other device to show up.
*/
return ERR_PTR(-EPROBE_DEFER);
}
/**
* device_connection_find - Find two devices connected together
* @dev: Device with the connection
* @con_id: Identifier for the connection
*
* Find a connection with unique identifier @con_id between @dev and
* another device. On success returns handle to the device that is connected
* to @dev, with the reference count for the found device incremented. Returns
* NULL if no matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a
* connection was found but the other device has not been enumerated yet.
*/
struct device *device_connection_find(struct device *dev, const char *con_id)
{
return device_connection_find_match(dev, con_id, NULL, generic_match);
}
EXPORT_SYMBOL_GPL(device_connection_find);
/**
* device_connection_add - Register a connection description
* @con: The connection description to be registered
*/
void device_connection_add(struct device_connection *con)
{
mutex_lock(&devcon_lock);
list_add_tail(&con->list, &devcon_list);
mutex_unlock(&devcon_lock);
}
EXPORT_SYMBOL_GPL(device_connection_add);
/**
* device_connections_remove - Unregister connection description
* @con: The connection description to be unregistered
*/
void device_connection_remove(struct device_connection *con)
{
mutex_lock(&devcon_lock);
list_del(&con->list);
mutex_unlock(&devcon_lock);
}
EXPORT_SYMBOL_GPL(device_connection_remove);