Device properties framework updates for v4.13-rc1

- Rearrange the core device properties code by moving the code
    specific to each supported platform configuration framework
    (ACPI, DT and build-in) into a separate file (Sakari Ailus).
 
  - Add helper functions for accessing device properties in a
    firmware-agnostic way (Sakari Ailus, Kieran Bingham).
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJZY/OvAAoJEILEb/54YlRx8NYP/3hL9jK8yBgDEVkoNnRDN7Xj
 gefqm++3YO6wy8pfuACfNal2lJRFat5NDWdkBUsFA7LAfaLYe6jkIZ8YW+XTcHq7
 H8dmFiDjfhVa4/urS6DTHmHqPyWLh4ayjTfwRP0yy2aW2XpXe1WNAgilLStu96iH
 OU44HY5FQCQipnIIcuJZpJwH51AMyJIuAV5rQJySoIuDyULRKmVQ8G/xCAfhzHCH
 tLcQHZnnYUdSCi29JwCu2RKKI6ta2o68EYmi1wxjvMEs2z27KVltCu+5Y9mpOIwl
 DU+X8hAmepzd5aJxpR69hzM+dr71+0feUOklEU+r0DJzlmdDzTK8NyEPW2Ysh0aa
 0SsdtgoRWv+kq0FuzL26fpa01WOcWB8N5uDp4XUZKFCriRmBk2HNc1KRoPtvILm2
 2ISOqdIPe2/fC/FOYT0mKcFXaezkONpdbZoUqWGKFw0OWkgc5Vesl7L0LlilLCS6
 XpHP/ISJBDfmIwjgaDjZIV1G6PkyaKu4tPkn9NsN+ztbH02dpTodIB0qDJSimyPX
 cYwuDBn6NpKhk/l8b7lNMLxX8xK5EpQKw90av3LFYwZcGpyBzsoqciLONPSqF+1p
 jHDK4yaV+Ddwo3q5368x+eXX/Y9hfDewikoAuAUG3y0cMzuwKwFZ7MRkfl1Azbcc
 CLNeBd8YXBUnbzDMYaNB
 =EcrF
 -----END PGP SIGNATURE-----

Merge tag 'devprop-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull device properties framework updates from Rafael Wysocki:
 "These mostly rearrange the device properties core code and add a few
  helper functions to it as a foundation for future work.

  Specifics:

   - Rearrange the core device properties code by moving the code
     specific to each supported platform configuration framework (ACPI,
     DT and build-in) into a separate file (Sakari Ailus).

   - Add helper functions for accessing device properties in a
     firmware-agnostic way (Sakari Ailus, Kieran Bingham)"

* tag 'devprop-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  device property: Add fwnode_graph_get_port_parent
  device property: Add FW type agnostic fwnode_graph_get_remote_node
  device property: Introduce fwnode_device_is_available()
  device property: Move fwnode graph ops to firmware specific locations
  device property: Move FW type specific functionality to FW specific files
  ACPI: Constify argument to acpi_device_is_present()
This commit is contained in:
Linus Torvalds 2017-07-10 15:23:45 -07:00
commit 548aa0e3c5
10 changed files with 509 additions and 214 deletions

View File

@ -262,8 +262,10 @@ int acpi_bus_init_power(struct acpi_device *device)
return -EINVAL;
device->power.state = ACPI_STATE_UNKNOWN;
if (!acpi_device_is_present(device))
if (!acpi_device_is_present(device)) {
device->flags.initialized = false;
return -ENXIO;
}
result = acpi_device_get_power(device, &state);
if (result)

View File

@ -111,7 +111,7 @@ int acpi_device_setup_files(struct acpi_device *dev);
void acpi_device_remove_files(struct acpi_device *dev);
void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
bool acpi_device_is_present(struct acpi_device *adev);
bool acpi_device_is_present(const struct acpi_device *adev);
bool acpi_device_is_battery(struct acpi_device *adev);
bool acpi_device_is_first_physical_node(struct acpi_device *adev,
const struct device *dev);

View File

@ -57,6 +57,7 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc,
dn->name = link->package.elements[0].string.pointer;
dn->fwnode.type = FWNODE_ACPI_DATA;
dn->fwnode.ops = &acpi_fwnode_ops;
dn->parent = parent;
INIT_LIST_HEAD(&dn->data.subnodes);
@ -1119,3 +1120,119 @@ int acpi_graph_get_remote_endpoint(struct fwnode_handle *fwnode,
return 0;
}
static bool acpi_fwnode_device_is_available(struct fwnode_handle *fwnode)
{
if (!is_acpi_device_node(fwnode))
return false;
return acpi_device_is_present(to_acpi_device_node(fwnode));
}
static bool acpi_fwnode_property_present(struct fwnode_handle *fwnode,
const char *propname)
{
return !acpi_node_prop_get(fwnode, propname, NULL);
}
static int acpi_fwnode_property_read_int_array(struct fwnode_handle *fwnode,
const char *propname,
unsigned int elem_size,
void *val, size_t nval)
{
enum dev_prop_type type;
switch (elem_size) {
case sizeof(u8):
type = DEV_PROP_U8;
break;
case sizeof(u16):
type = DEV_PROP_U16;
break;
case sizeof(u32):
type = DEV_PROP_U32;
break;
case sizeof(u64):
type = DEV_PROP_U64;
break;
default:
return -ENXIO;
}
return acpi_node_prop_read(fwnode, propname, type, val, nval);
}
static int acpi_fwnode_property_read_string_array(struct fwnode_handle *fwnode,
const char *propname,
const char **val, size_t nval)
{
return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
val, nval);
}
static struct fwnode_handle *
acpi_fwnode_get_named_child_node(struct fwnode_handle *fwnode,
const char *childname)
{
struct fwnode_handle *child;
/*
* Find first matching named child node of this fwnode.
* For ACPI this will be a data only sub-node.
*/
fwnode_for_each_child_node(fwnode, child)
if (acpi_data_node_match(child, childname))
return child;
return NULL;
}
static struct fwnode_handle *
acpi_fwnode_graph_get_next_endpoint(struct fwnode_handle *fwnode,
struct fwnode_handle *prev)
{
struct fwnode_handle *endpoint;
endpoint = acpi_graph_get_next_endpoint(fwnode, prev);
if (IS_ERR(endpoint))
return NULL;
return endpoint;
}
static struct fwnode_handle *
acpi_fwnode_graph_get_remote_endpoint(struct fwnode_handle *fwnode)
{
struct fwnode_handle *endpoint = NULL;
acpi_graph_get_remote_endpoint(fwnode, NULL, NULL, &endpoint);
return endpoint;
}
static int acpi_fwnode_graph_parse_endpoint(struct fwnode_handle *fwnode,
struct fwnode_endpoint *endpoint)
{
struct fwnode_handle *port_fwnode = fwnode_get_parent(fwnode);
endpoint->local_fwnode = fwnode;
fwnode_property_read_u32(port_fwnode, "port", &endpoint->port);
fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id);
return 0;
}
const struct fwnode_operations acpi_fwnode_ops = {
.device_is_available = acpi_fwnode_device_is_available,
.property_present = acpi_fwnode_property_present,
.property_read_int_array = acpi_fwnode_property_read_int_array,
.property_read_string_array = acpi_fwnode_property_read_string_array,
.get_parent = acpi_node_get_parent,
.get_next_child_node = acpi_get_next_subnode,
.get_named_child_node = acpi_fwnode_get_named_child_node,
.graph_get_next_endpoint = acpi_fwnode_graph_get_next_endpoint,
.graph_get_remote_endpoint = acpi_fwnode_graph_get_remote_endpoint,
.graph_get_port_parent = acpi_node_get_parent,
.graph_parse_endpoint = acpi_fwnode_graph_parse_endpoint,
};

View File

@ -1468,6 +1468,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
device->handle = handle;
device->parent = acpi_bus_get_parent(handle);
device->fwnode.type = FWNODE_ACPI;
device->fwnode.ops = &acpi_fwnode_ops;
acpi_set_device_status(device, sta);
acpi_device_get_busid(device);
acpi_set_pnp_ids(handle, &device->pnp, type);
@ -1600,13 +1601,9 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type,
return 0;
}
bool acpi_device_is_present(struct acpi_device *adev)
bool acpi_device_is_present(const struct acpi_device *adev)
{
if (adev->status.present || adev->status.functional)
return true;
adev->flags.initialized = false;
return false;
return adev->status.present || adev->status.functional;
}
static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
@ -1839,6 +1836,7 @@ static void acpi_bus_attach(struct acpi_device *device)
acpi_bus_get_status(device);
/* Skip devices that are not present. */
if (!acpi_device_is_present(device)) {
device->flags.initialized = false;
acpi_device_clear_enumerated(device);
device->flags.power_manageable = 0;
return;

View File

@ -187,6 +187,50 @@ struct fwnode_handle *dev_fwnode(struct device *dev)
}
EXPORT_SYMBOL_GPL(dev_fwnode);
static bool pset_fwnode_property_present(struct fwnode_handle *fwnode,
const char *propname)
{
return !!pset_prop_get(to_pset_node(fwnode), propname);
}
static int pset_fwnode_read_int_array(struct fwnode_handle *fwnode,
const char *propname,
unsigned int elem_size, void *val,
size_t nval)
{
struct property_set *node = to_pset_node(fwnode);
if (!val)
return pset_prop_count_elems_of_size(node, propname, elem_size);
switch (elem_size) {
case sizeof(u8):
return pset_prop_read_u8_array(node, propname, val, nval);
case sizeof(u16):
return pset_prop_read_u16_array(node, propname, val, nval);
case sizeof(u32):
return pset_prop_read_u32_array(node, propname, val, nval);
case sizeof(u64):
return pset_prop_read_u64_array(node, propname, val, nval);
}
return -ENXIO;
}
static int pset_fwnode_property_read_string_array(struct fwnode_handle *fwnode,
const char *propname,
const char **val, size_t nval)
{
return pset_prop_read_string_array(to_pset_node(fwnode), propname,
val, nval);
}
static const struct fwnode_operations pset_fwnode_ops = {
.property_present = pset_fwnode_property_present,
.property_read_int_array = pset_fwnode_read_int_array,
.property_read_string_array = pset_fwnode_property_read_string_array,
};
/**
* device_property_present - check if a property of a device is present
* @dev: Device whose property is being checked
@ -200,18 +244,6 @@ bool device_property_present(struct device *dev, const char *propname)
}
EXPORT_SYMBOL_GPL(device_property_present);
static bool __fwnode_property_present(struct fwnode_handle *fwnode,
const char *propname)
{
if (is_of_node(fwnode))
return of_property_read_bool(to_of_node(fwnode), propname);
else if (is_acpi_node(fwnode))
return !acpi_node_prop_get(fwnode, propname, NULL);
else if (is_pset_node(fwnode))
return !!pset_prop_get(to_pset_node(fwnode), propname);
return false;
}
/**
* fwnode_property_present - check if a property of a firmware node is present
* @fwnode: Firmware node whose property to check
@ -221,10 +253,11 @@ bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname)
{
bool ret;
ret = __fwnode_property_present(fwnode, propname);
ret = fwnode_call_int_op(fwnode, property_present, propname);
if (ret == false && !IS_ERR_OR_NULL(fwnode) &&
!IS_ERR_OR_NULL(fwnode->secondary))
ret = __fwnode_property_present(fwnode->secondary, propname);
ret = fwnode_call_int_op(fwnode->secondary, property_present,
propname);
return ret;
}
EXPORT_SYMBOL_GPL(fwnode_property_present);
@ -398,42 +431,23 @@ int device_property_match_string(struct device *dev, const char *propname,
}
EXPORT_SYMBOL_GPL(device_property_match_string);
#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
(val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
: of_property_count_elems_of_size((node), (propname), sizeof(type))
static int fwnode_property_read_int_array(struct fwnode_handle *fwnode,
const char *propname,
unsigned int elem_size, void *val,
size_t nval)
{
int ret;
#define PSET_PROP_READ_ARRAY(node, propname, type, val, nval) \
(val) ? pset_prop_read_##type##_array((node), (propname), (val), (nval)) \
: pset_prop_count_elems_of_size((node), (propname), sizeof(type))
ret = fwnode_call_int_op(fwnode, property_read_int_array, propname,
elem_size, val, nval);
if (ret == -EINVAL && !IS_ERR_OR_NULL(fwnode) &&
!IS_ERR_OR_NULL(fwnode->secondary))
ret = fwnode_call_int_op(
fwnode->secondary, property_read_int_array, propname,
elem_size, val, nval);
#define FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
({ \
int _ret_; \
if (is_of_node(_fwnode_)) \
_ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \
_type_, _val_, _nval_); \
else if (is_acpi_node(_fwnode_)) \
_ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \
_val_, _nval_); \
else if (is_pset_node(_fwnode_)) \
_ret_ = PSET_PROP_READ_ARRAY(to_pset_node(_fwnode_), _propname_, \
_type_, _val_, _nval_); \
else \
_ret_ = -ENXIO; \
_ret_; \
})
#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
({ \
int _ret_; \
_ret_ = FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, \
_val_, _nval_); \
if (_ret_ == -EINVAL && !IS_ERR_OR_NULL(_fwnode_) && \
!IS_ERR_OR_NULL(_fwnode_->secondary)) \
_ret_ = FWNODE_PROP_READ(_fwnode_->secondary, _propname_, _type_, \
_proptype_, _val_, _nval_); \
_ret_; \
})
return ret;
}
/**
* fwnode_property_read_u8_array - return a u8 array property of firmware node
@ -456,7 +470,7 @@ EXPORT_SYMBOL_GPL(device_property_match_string);
int fwnode_property_read_u8_array(struct fwnode_handle *fwnode,
const char *propname, u8 *val, size_t nval)
{
return FWNODE_PROP_READ_ARRAY(fwnode, propname, u8, DEV_PROP_U8,
return fwnode_property_read_int_array(fwnode, propname, sizeof(u8),
val, nval);
}
EXPORT_SYMBOL_GPL(fwnode_property_read_u8_array);
@ -482,7 +496,7 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u8_array);
int fwnode_property_read_u16_array(struct fwnode_handle *fwnode,
const char *propname, u16 *val, size_t nval)
{
return FWNODE_PROP_READ_ARRAY(fwnode, propname, u16, DEV_PROP_U16,
return fwnode_property_read_int_array(fwnode, propname, sizeof(u16),
val, nval);
}
EXPORT_SYMBOL_GPL(fwnode_property_read_u16_array);
@ -508,7 +522,7 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u16_array);
int fwnode_property_read_u32_array(struct fwnode_handle *fwnode,
const char *propname, u32 *val, size_t nval)
{
return FWNODE_PROP_READ_ARRAY(fwnode, propname, u32, DEV_PROP_U32,
return fwnode_property_read_int_array(fwnode, propname, sizeof(u32),
val, nval);
}
EXPORT_SYMBOL_GPL(fwnode_property_read_u32_array);
@ -534,29 +548,11 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u32_array);
int fwnode_property_read_u64_array(struct fwnode_handle *fwnode,
const char *propname, u64 *val, size_t nval)
{
return FWNODE_PROP_READ_ARRAY(fwnode, propname, u64, DEV_PROP_U64,
return fwnode_property_read_int_array(fwnode, propname, sizeof(u64),
val, nval);
}
EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array);
static int __fwnode_property_read_string_array(struct fwnode_handle *fwnode,
const char *propname,
const char **val, size_t nval)
{
if (is_of_node(fwnode))
return val ?
of_property_read_string_array(to_of_node(fwnode),
propname, val, nval) :
of_property_count_strings(to_of_node(fwnode), propname);
else if (is_acpi_node(fwnode))
return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
val, nval);
else if (is_pset_node(fwnode))
return pset_prop_read_string_array(to_pset_node(fwnode),
propname, val, nval);
return -ENXIO;
}
/**
* fwnode_property_read_string_array - return string array property of a node
* @fwnode: Firmware node to get the property of
@ -581,11 +577,13 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
{
int ret;
ret = __fwnode_property_read_string_array(fwnode, propname, val, nval);
ret = fwnode_call_int_op(fwnode, property_read_string_array, propname,
val, nval);
if (ret == -EINVAL && !IS_ERR_OR_NULL(fwnode) &&
!IS_ERR_OR_NULL(fwnode->secondary))
ret = __fwnode_property_read_string_array(fwnode->secondary,
propname, val, nval);
ret = fwnode_call_int_op(fwnode->secondary,
property_read_string_array, propname,
val, nval);
return ret;
}
EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
@ -903,6 +901,7 @@ int device_add_properties(struct device *dev,
return PTR_ERR(p);
p->fwnode.type = FWNODE_PDATA;
p->fwnode.ops = &pset_fwnode_ops;
set_secondary_fwnode(dev, &p->fwnode);
return 0;
}
@ -938,19 +937,7 @@ EXPORT_SYMBOL_GPL(fwnode_get_next_parent);
*/
struct fwnode_handle *fwnode_get_parent(struct fwnode_handle *fwnode)
{
struct fwnode_handle *parent = NULL;
if (is_of_node(fwnode)) {
struct device_node *node;
node = of_get_parent(to_of_node(fwnode));
if (node)
parent = &node->fwnode;
} else if (is_acpi_node(fwnode)) {
parent = acpi_node_get_parent(fwnode);
}
return parent;
return fwnode_call_ptr_op(fwnode, get_parent);
}
EXPORT_SYMBOL_GPL(fwnode_get_parent);
@ -962,18 +949,7 @@ EXPORT_SYMBOL_GPL(fwnode_get_parent);
struct fwnode_handle *fwnode_get_next_child_node(struct fwnode_handle *fwnode,
struct fwnode_handle *child)
{
if (is_of_node(fwnode)) {
struct device_node *node;
node = of_get_next_available_child(to_of_node(fwnode),
to_of_node(child));
if (node)
return &node->fwnode;
} else if (is_acpi_node(fwnode)) {
return acpi_get_next_subnode(fwnode, child);
}
return NULL;
return fwnode_call_ptr_op(fwnode, get_next_child_node, child);
}
EXPORT_SYMBOL_GPL(fwnode_get_next_child_node);
@ -1005,23 +981,7 @@ EXPORT_SYMBOL_GPL(device_get_next_child_node);
struct fwnode_handle *fwnode_get_named_child_node(struct fwnode_handle *fwnode,
const char *childname)
{
struct fwnode_handle *child;
/*
* Find first matching named child node of this fwnode.
* For ACPI this will be a data only sub-node.
*/
fwnode_for_each_child_node(fwnode, child) {
if (is_of_node(child)) {
if (!of_node_cmp(to_of_node(child)->name, childname))
return child;
} else if (is_acpi_data_node(child)) {
if (acpi_data_node_match(child, childname))
return child;
}
}
return NULL;
return fwnode_call_ptr_op(fwnode, get_named_child_node, childname);
}
EXPORT_SYMBOL_GPL(fwnode_get_named_child_node);
@ -1043,8 +1003,7 @@ EXPORT_SYMBOL_GPL(device_get_named_child_node);
*/
void fwnode_handle_get(struct fwnode_handle *fwnode)
{
if (is_of_node(fwnode))
of_node_get(to_of_node(fwnode));
fwnode_call_void_op(fwnode, get);
}
EXPORT_SYMBOL_GPL(fwnode_handle_get);
@ -1058,11 +1017,20 @@ EXPORT_SYMBOL_GPL(fwnode_handle_get);
*/
void fwnode_handle_put(struct fwnode_handle *fwnode)
{
if (is_of_node(fwnode))
of_node_put(to_of_node(fwnode));
fwnode_call_void_op(fwnode, put);
}
EXPORT_SYMBOL_GPL(fwnode_handle_put);
/**
* fwnode_device_is_available - check if a device is available for use
* @fwnode: Pointer to the fwnode of the device.
*/
bool fwnode_device_is_available(struct fwnode_handle *fwnode)
{
return fwnode_call_int_op(fwnode, device_is_available);
}
EXPORT_SYMBOL_GPL(fwnode_device_is_available);
/**
* device_get_child_node_count - return the number of child nodes for device
* @dev: Device to cound the child nodes for
@ -1198,27 +1166,30 @@ struct fwnode_handle *
fwnode_graph_get_next_endpoint(struct fwnode_handle *fwnode,
struct fwnode_handle *prev)
{
struct fwnode_handle *endpoint = NULL;
if (is_of_node(fwnode)) {
struct device_node *node;
node = of_graph_get_next_endpoint(to_of_node(fwnode),
to_of_node(prev));
if (node)
endpoint = &node->fwnode;
} else if (is_acpi_node(fwnode)) {
endpoint = acpi_graph_get_next_endpoint(fwnode, prev);
if (IS_ERR(endpoint))
endpoint = NULL;
}
return endpoint;
return fwnode_call_ptr_op(fwnode, graph_get_next_endpoint, prev);
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);
/**
* fwnode_graph_get_port_parent - Return the device fwnode of a port endpoint
* @endpoint: Endpoint firmware node of the port
*
* Return: the firmware node of the device the @endpoint belongs to.
*/
struct fwnode_handle *
fwnode_graph_get_port_parent(struct fwnode_handle *endpoint)
{
struct fwnode_handle *port, *parent;
port = fwnode_get_parent(endpoint);
parent = fwnode_call_ptr_op(port, graph_get_port_parent);
fwnode_handle_put(port);
return parent;
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_port_parent);
/**
* fwnode_graph_get_remote_port_parent - Return fwnode of a remote device
* @fwnode: Endpoint firmware node pointing to the remote endpoint
@ -1228,22 +1199,12 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);
struct fwnode_handle *
fwnode_graph_get_remote_port_parent(struct fwnode_handle *fwnode)
{
struct fwnode_handle *parent = NULL;
struct fwnode_handle *endpoint, *parent;
if (is_of_node(fwnode)) {
struct device_node *node;
endpoint = fwnode_graph_get_remote_endpoint(fwnode);
parent = fwnode_graph_get_port_parent(endpoint);
node = of_graph_get_remote_port_parent(to_of_node(fwnode));
if (node)
parent = &node->fwnode;
} else if (is_acpi_node(fwnode)) {
int ret;
ret = acpi_graph_get_remote_endpoint(fwnode, &parent, NULL,
NULL);
if (ret)
return NULL;
}
fwnode_handle_put(endpoint);
return parent;
}
@ -1257,23 +1218,7 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_port_parent);
*/
struct fwnode_handle *fwnode_graph_get_remote_port(struct fwnode_handle *fwnode)
{
struct fwnode_handle *port = NULL;
if (is_of_node(fwnode)) {
struct device_node *node;
node = of_graph_get_remote_port(to_of_node(fwnode));
if (node)
port = &node->fwnode;
} else if (is_acpi_node(fwnode)) {
int ret;
ret = acpi_graph_get_remote_endpoint(fwnode, NULL, &port, NULL);
if (ret)
return NULL;
}
return port;
return fwnode_get_next_parent(fwnode_graph_get_remote_endpoint(fwnode));
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_port);
@ -1286,28 +1231,47 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_port);
struct fwnode_handle *
fwnode_graph_get_remote_endpoint(struct fwnode_handle *fwnode)
{
struct fwnode_handle *endpoint = NULL;
if (is_of_node(fwnode)) {
struct device_node *node;
node = of_parse_phandle(to_of_node(fwnode), "remote-endpoint",
0);
if (node)
endpoint = &node->fwnode;
} else if (is_acpi_node(fwnode)) {
int ret;
ret = acpi_graph_get_remote_endpoint(fwnode, NULL, NULL,
&endpoint);
if (ret)
return NULL;
}
return endpoint;
return fwnode_call_ptr_op(fwnode, graph_get_remote_endpoint);
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_endpoint);
/**
* fwnode_graph_get_remote_node - get remote parent node for given port/endpoint
* @fwnode: pointer to parent fwnode_handle containing graph port/endpoint
* @port_id: identifier of the parent port node
* @endpoint_id: identifier of the endpoint node
*
* Return: Remote fwnode handle associated with remote endpoint node linked
* to @node. Use fwnode_node_put() on it when done.
*/
struct fwnode_handle *fwnode_graph_get_remote_node(struct fwnode_handle *fwnode,
u32 port_id, u32 endpoint_id)
{
struct fwnode_handle *endpoint = NULL;
while ((endpoint = fwnode_graph_get_next_endpoint(fwnode, endpoint))) {
struct fwnode_endpoint fwnode_ep;
struct fwnode_handle *remote;
int ret;
ret = fwnode_graph_parse_endpoint(endpoint, &fwnode_ep);
if (ret < 0)
continue;
if (fwnode_ep.port != port_id || fwnode_ep.id != endpoint_id)
continue;
remote = fwnode_graph_get_remote_port_parent(endpoint);
if (!remote)
return NULL;
return fwnode_device_is_available(remote) ? remote : NULL;
}
return NULL;
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_node);
/**
* fwnode_graph_parse_endpoint - parse common endpoint node properties
* @fwnode: pointer to endpoint fwnode_handle
@ -1320,22 +1284,8 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_endpoint);
int fwnode_graph_parse_endpoint(struct fwnode_handle *fwnode,
struct fwnode_endpoint *endpoint)
{
struct fwnode_handle *port_fwnode = fwnode_get_parent(fwnode);
memset(endpoint, 0, sizeof(*endpoint));
endpoint->local_fwnode = fwnode;
if (is_acpi_node(port_fwnode)) {
fwnode_property_read_u32(port_fwnode, "port", &endpoint->port);
fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id);
} else {
fwnode_property_read_u32(port_fwnode, "reg", &endpoint->port);
fwnode_property_read_u32(fwnode, "reg", &endpoint->id);
}
fwnode_handle_put(port_fwnode);
return 0;
return fwnode_call_int_op(fwnode, graph_parse_endpoint, endpoint);
}
EXPORT_SYMBOL(fwnode_graph_parse_endpoint);

View File

@ -804,3 +804,151 @@ struct device_node *of_graph_get_remote_node(const struct device_node *node,
return remote;
}
EXPORT_SYMBOL(of_graph_get_remote_node);
static void of_fwnode_get(struct fwnode_handle *fwnode)
{
of_node_get(to_of_node(fwnode));
}
static void of_fwnode_put(struct fwnode_handle *fwnode)
{
of_node_put(to_of_node(fwnode));
}
static bool of_fwnode_device_is_available(struct fwnode_handle *fwnode)
{
return of_device_is_available(to_of_node(fwnode));
}
static bool of_fwnode_property_present(struct fwnode_handle *fwnode,
const char *propname)
{
return of_property_read_bool(to_of_node(fwnode), propname);
}
static int of_fwnode_property_read_int_array(struct fwnode_handle *fwnode,
const char *propname,
unsigned int elem_size, void *val,
size_t nval)
{
struct device_node *node = to_of_node(fwnode);
if (!val)
return of_property_count_elems_of_size(node, propname,
elem_size);
switch (elem_size) {
case sizeof(u8):
return of_property_read_u8_array(node, propname, val, nval);
case sizeof(u16):
return of_property_read_u16_array(node, propname, val, nval);
case sizeof(u32):
return of_property_read_u32_array(node, propname, val, nval);
case sizeof(u64):
return of_property_read_u64_array(node, propname, val, nval);
}
return -ENXIO;
}
static int of_fwnode_property_read_string_array(struct fwnode_handle *fwnode,
const char *propname,
const char **val, size_t nval)
{
struct device_node *node = to_of_node(fwnode);
return val ?
of_property_read_string_array(node, propname, val, nval) :
of_property_count_strings(node, propname);
}
static struct fwnode_handle *of_fwnode_get_parent(struct fwnode_handle *fwnode)
{
return of_fwnode_handle(of_get_parent(to_of_node(fwnode)));
}
static struct fwnode_handle *
of_fwnode_get_next_child_node(struct fwnode_handle *fwnode,
struct fwnode_handle *child)
{
return of_fwnode_handle(of_get_next_available_child(to_of_node(fwnode),
to_of_node(child)));
}
static struct fwnode_handle *
of_fwnode_get_named_child_node(struct fwnode_handle *fwnode,
const char *childname)
{
struct device_node *node = to_of_node(fwnode);
struct device_node *child;
for_each_available_child_of_node(node, child)
if (!of_node_cmp(child->name, childname))
return of_fwnode_handle(child);
return NULL;
}
static struct fwnode_handle *
of_fwnode_graph_get_next_endpoint(struct fwnode_handle *fwnode,
struct fwnode_handle *prev)
{
return of_fwnode_handle(of_graph_get_next_endpoint(to_of_node(fwnode),
to_of_node(prev)));
}
static struct fwnode_handle *
of_fwnode_graph_get_remote_endpoint(struct fwnode_handle *fwnode)
{
return of_fwnode_handle(of_parse_phandle(to_of_node(fwnode),
"remote-endpoint", 0));
}
static struct fwnode_handle *
of_fwnode_graph_get_port_parent(struct fwnode_handle *fwnode)
{
struct device_node *np;
/* Get the parent of the port */
np = of_get_next_parent(to_of_node(fwnode));
if (!np)
return NULL;
/* Is this the "ports" node? If not, it's the port parent. */
if (of_node_cmp(np->name, "ports"))
return of_fwnode_handle(np);
return of_fwnode_handle(of_get_next_parent(np));
}
static int of_fwnode_graph_parse_endpoint(struct fwnode_handle *fwnode,
struct fwnode_endpoint *endpoint)
{
struct device_node *node = to_of_node(fwnode);
struct device_node *port_node = of_get_parent(node);
endpoint->local_fwnode = fwnode;
of_property_read_u32(port_node, "reg", &endpoint->port);
of_property_read_u32(node, "reg", &endpoint->id);
of_node_put(port_node);
return 0;
}
const struct fwnode_operations of_fwnode_ops = {
.get = of_fwnode_get,
.put = of_fwnode_put,
.device_is_available = of_fwnode_device_is_available,
.property_present = of_fwnode_property_present,
.property_read_int_array = of_fwnode_property_read_int_array,
.property_read_string_array = of_fwnode_property_read_string_array,
.get_parent = of_fwnode_get_parent,
.get_next_child_node = of_fwnode_get_next_child_node,
.get_named_child_node = of_fwnode_get_named_child_node,
.graph_get_next_endpoint = of_fwnode_graph_get_next_endpoint,
.graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint,
.graph_get_port_parent = of_fwnode_graph_get_port_parent,
.graph_parse_endpoint = of_fwnode_graph_parse_endpoint,
};

View File

@ -57,6 +57,9 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev)
acpi_fwnode_handle(adev) : NULL)
#define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev))
extern const struct fwnode_operations acpi_fwnode_ops;
static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
{
struct fwnode_handle *fwnode;
@ -66,6 +69,7 @@ static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
return NULL;
fwnode->type = FWNODE_ACPI_STATIC;
fwnode->ops = &acpi_fwnode_ops;
return fwnode;
}

View File

@ -12,6 +12,8 @@
#ifndef _LINUX_FWNODE_H_
#define _LINUX_FWNODE_H_
#include <linux/types.h>
enum fwnode_type {
FWNODE_INVALID = 0,
FWNODE_OF,
@ -22,9 +24,12 @@ enum fwnode_type {
FWNODE_IRQCHIP
};
struct fwnode_operations;
struct fwnode_handle {
enum fwnode_type type;
struct fwnode_handle *secondary;
const struct fwnode_operations *ops;
};
/**
@ -39,4 +44,68 @@ struct fwnode_endpoint {
const struct fwnode_handle *local_fwnode;
};
/**
* struct fwnode_operations - Operations for fwnode interface
* @get: Get a reference to an fwnode.
* @put: Put a reference to an fwnode.
* @property_present: Return true if a property is present.
* @property_read_integer_array: Read an array of integer properties. Return
* zero on success, a negative error code
* otherwise.
* @property_read_string_array: Read an array of string properties. Return zero
* on success, a negative error code otherwise.
* @get_parent: Return the parent of an fwnode.
* @get_next_child_node: Return the next child node in an iteration.
* @get_named_child_node: Return a child node with a given name.
* @graph_get_next_endpoint: Return an endpoint node in an iteration.
* @graph_get_remote_endpoint: Return the remote endpoint node of a local
* endpoint node.
* @graph_get_port_parent: Return the parent node of a port node.
* @graph_parse_endpoint: Parse endpoint for port and endpoint id.
*/
struct fwnode_operations {
void (*get)(struct fwnode_handle *fwnode);
void (*put)(struct fwnode_handle *fwnode);
bool (*device_is_available)(struct fwnode_handle *fwnode);
bool (*property_present)(struct fwnode_handle *fwnode,
const char *propname);
int (*property_read_int_array)(struct fwnode_handle *fwnode,
const char *propname,
unsigned int elem_size, void *val,
size_t nval);
int (*property_read_string_array)(struct fwnode_handle *fwnode_handle,
const char *propname,
const char **val, size_t nval);
struct fwnode_handle *(*get_parent)(struct fwnode_handle *fwnode);
struct fwnode_handle *
(*get_next_child_node)(struct fwnode_handle *fwnode,
struct fwnode_handle *child);
struct fwnode_handle *
(*get_named_child_node)(struct fwnode_handle *fwnode, const char *name);
struct fwnode_handle *
(*graph_get_next_endpoint)(struct fwnode_handle *fwnode,
struct fwnode_handle *prev);
struct fwnode_handle *
(*graph_get_remote_endpoint)(struct fwnode_handle *fwnode);
struct fwnode_handle *
(*graph_get_port_parent)(struct fwnode_handle *fwnode);
int (*graph_parse_endpoint)(struct fwnode_handle *fwnode,
struct fwnode_endpoint *endpoint);
};
#define fwnode_has_op(fwnode, op) \
((fwnode) && (fwnode)->ops && (fwnode)->ops->op)
#define fwnode_call_int_op(fwnode, op, ...) \
(fwnode ? (fwnode_has_op(fwnode, op) ? \
(fwnode)->ops->op(fwnode, ## __VA_ARGS__) : -ENXIO) : \
-EINVAL)
#define fwnode_call_ptr_op(fwnode, op, ...) \
(fwnode_has_op(fwnode, op) ? \
(fwnode)->ops->op(fwnode, ## __VA_ARGS__) : NULL)
#define fwnode_call_void_op(fwnode, op, ...) \
do { \
if (fwnode_has_op(fwnode, op)) \
(fwnode)->ops->op(fwnode, ## __VA_ARGS__); \
} while (false)
#endif

View File

@ -100,10 +100,12 @@ struct of_reconfig_data {
/* initialize a node */
extern struct kobj_type of_node_ktype;
extern const struct fwnode_operations of_fwnode_ops;
static inline void of_node_init(struct device_node *node)
{
kobject_init(&node->kobj, &of_node_ktype);
node->fwnode.type = FWNODE_OF;
node->fwnode.ops = &of_fwnode_ops;
}
/* true when node is initialized */

View File

@ -51,6 +51,7 @@ int device_property_read_string(struct device *dev, const char *propname,
int device_property_match_string(struct device *dev,
const char *propname, const char *string);
bool fwnode_device_is_available(struct fwnode_handle *fwnode);
bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname);
int fwnode_property_read_u8_array(struct fwnode_handle *fwnode,
const char *propname, u8 *val,
@ -274,12 +275,16 @@ void *device_get_mac_address(struct device *dev, char *addr, int alen);
struct fwnode_handle *fwnode_graph_get_next_endpoint(
struct fwnode_handle *fwnode, struct fwnode_handle *prev);
struct fwnode_handle *
fwnode_graph_get_port_parent(struct fwnode_handle *fwnode);
struct fwnode_handle *fwnode_graph_get_remote_port_parent(
struct fwnode_handle *fwnode);
struct fwnode_handle *fwnode_graph_get_remote_port(
struct fwnode_handle *fwnode);
struct fwnode_handle *fwnode_graph_get_remote_endpoint(
struct fwnode_handle *fwnode);
struct fwnode_handle *fwnode_graph_get_remote_node(struct fwnode_handle *fwnode,
u32 port, u32 endpoint);
int fwnode_graph_parse_endpoint(struct fwnode_handle *fwnode,
struct fwnode_endpoint *endpoint);