Device properties framework updates for 5.19-rc1

- Allow error pointer to be passed to fwnode APIs (Andy Shevchenko).
 
  - Introduce fwnode_for_each_parent_node() (Andy Shevchenko, Douglas
    Anderson).
 
  - Advertise fwnode and device property count API calls (Andy
    Shevchenko).
 
  - Clean up fwnode_is_ancestor_of() (Andy Shevchenko).
 
  - Convert device_{dma_supported,get_dma_attr} to fwnode (Sakari Ailus).
 
  - Release subnode properties with data nodes (Sakari Ailus).
 
  - Add ->iomap() and ->irq_get() to fwnode operations (Sakari Ailus).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmKL4qwSHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRxO+AP/ir+1VKydUioBbH9vh8grCF1vAoJhknv
 jb0STEBq+SH7+EwWbpj/J9+ldACvIJ0wnrsZ83vbl9k0z5+mrddw3HiQrrCc2PSc
 hRfWCi4ihnTlDK2Sctm/suBSNivh8kcGwJUcYOwYaWEVdGoUXrqldWzzRo48DYgo
 KnHm4e2V5Gob2u4edbdgtOl4BGcBcOPNDdOe15Ra6pjcp+0DUWP55j51kmhpjLSk
 PuAEtbNLPOzOVIVFpANU4Q7/3G3gjKIwPjKwAsWaa2UKYdsmcLhr8gaBo05UBV6g
 M6VPq14OHLGBlwxcxrsFgsNy9uBHnxEAHE0NRg7hrA2eHAqiuA7R+itBvzcgx8Wj
 HRTI9ZFjZabx82rWkb7iKI/K/ok5igzX3/zrbr7Bf3+7QY0+UEMqpmPVak01xqe7
 nFk7x3rHgZOsKPs+0ZN8pG16vEyuPFEQLJV0o25fiyhK2ob8tbAZxy3nVBRm8H/o
 yzuD9rYmZKHHwp3Ff4Bn+S8/T6vpYhsQkEz+NjjGMcXZXMBhbH+wsu9aBtVY/Jdp
 wVrmkxm/RC3b97oVoisqY05Wv/tgUwybSkPbARZWQjmshuazrvaJAi+N/LWwBuMK
 gsBVLTivWthjlCiPwRZlADTKbhlpdwSikWaC0xktsyqaO3JDtJFn305V7L9oWpQz
 tShfSJ2GRBOT
 =ly4P
 -----END PGP SIGNATURE-----

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

Pull device properties framework updates from Rafael Wysocki:
 "These mostly extend the device property API and make it easier to use
  in some cases.

  Specifics:

   - Allow error pointer to be passed to fwnode APIs (Andy Shevchenko).

   - Introduce fwnode_for_each_parent_node() (Andy Shevchenko, Douglas
     Anderson).

   - Advertise fwnode and device property count API calls (Andy
     Shevchenko).

   - Clean up fwnode_is_ancestor_of() (Andy Shevchenko).

   - Convert device_{dma_supported,get_dma_attr} to fwnode (Sakari
     Ailus).

   - Release subnode properties with data nodes (Sakari Ailus).

   - Add ->iomap() and ->irq_get() to fwnode operations (Sakari Ailus)"

* tag 'devprop-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  device property: Advertise fwnode and device property count API calls
  device property: Fix recent breakage of fwnode_get_next_parent_dev()
  device property: Drop 'test' prefix in parameters of fwnode_is_ancestor_of()
  device property: Introduce fwnode_for_each_parent_node()
  device property: Allow error pointer to be passed to fwnode APIs
  ACPI: property: Release subnode properties with data nodes
  device property: Add irq_get to fwnode operation
  device property: Add iomap to fwnode operations
  ACPI: property: Move acpi_fwnode_device_get_match_data() up
  device property: Convert device_{dma_supported,get_dma_attr} to fwnode
This commit is contained in:
Linus Torvalds 2022-05-24 16:34:14 -07:00
commit 268db333b5
5 changed files with 211 additions and 121 deletions

View File

@ -433,6 +433,16 @@ void acpi_init_properties(struct acpi_device *adev)
acpi_extract_apple_properties(adev); acpi_extract_apple_properties(adev);
} }
static void acpi_free_device_properties(struct list_head *list)
{
struct acpi_device_properties *props, *tmp;
list_for_each_entry_safe(props, tmp, list, list) {
list_del(&props->list);
kfree(props);
}
}
static void acpi_destroy_nondev_subnodes(struct list_head *list) static void acpi_destroy_nondev_subnodes(struct list_head *list)
{ {
struct acpi_data_node *dn, *next; struct acpi_data_node *dn, *next;
@ -445,22 +455,18 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list)
wait_for_completion(&dn->kobj_done); wait_for_completion(&dn->kobj_done);
list_del(&dn->sibling); list_del(&dn->sibling);
ACPI_FREE((void *)dn->data.pointer); ACPI_FREE((void *)dn->data.pointer);
acpi_free_device_properties(&dn->data.properties);
kfree(dn); kfree(dn);
} }
} }
void acpi_free_properties(struct acpi_device *adev) void acpi_free_properties(struct acpi_device *adev)
{ {
struct acpi_device_properties *props, *tmp;
acpi_destroy_nondev_subnodes(&adev->data.subnodes); acpi_destroy_nondev_subnodes(&adev->data.subnodes);
ACPI_FREE((void *)adev->data.pointer); ACPI_FREE((void *)adev->data.pointer);
adev->data.of_compatible = NULL; adev->data.of_compatible = NULL;
adev->data.pointer = NULL; adev->data.pointer = NULL;
list_for_each_entry_safe(props, tmp, &adev->data.properties, list) { acpi_free_device_properties(&adev->data.properties);
list_del(&props->list);
kfree(props);
}
} }
/** /**
@ -1256,6 +1262,24 @@ static bool acpi_fwnode_device_is_available(const struct fwnode_handle *fwnode)
return acpi_device_is_present(to_acpi_device_node(fwnode)); return acpi_device_is_present(to_acpi_device_node(fwnode));
} }
static const void *
acpi_fwnode_device_get_match_data(const struct fwnode_handle *fwnode,
const struct device *dev)
{
return acpi_device_get_match_data(dev);
}
static bool acpi_fwnode_device_dma_supported(const struct fwnode_handle *fwnode)
{
return acpi_dma_supported(to_acpi_device_node(fwnode));
}
static enum dev_dma_attr
acpi_fwnode_device_get_dma_attr(const struct fwnode_handle *fwnode)
{
return acpi_get_dma_attr(to_acpi_device_node(fwnode));
}
static bool acpi_fwnode_property_present(const struct fwnode_handle *fwnode, static bool acpi_fwnode_property_present(const struct fwnode_handle *fwnode,
const char *propname) const char *propname)
{ {
@ -1376,17 +1400,26 @@ static int acpi_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
return 0; return 0;
} }
static const void * static int acpi_fwnode_irq_get(const struct fwnode_handle *fwnode,
acpi_fwnode_device_get_match_data(const struct fwnode_handle *fwnode, unsigned int index)
const struct device *dev)
{ {
return acpi_device_get_match_data(dev); struct resource res;
int ret;
ret = acpi_irq_get(ACPI_HANDLE_FWNODE(fwnode), index, &res);
if (ret)
return ret;
return res.start;
} }
#define DECLARE_ACPI_FWNODE_OPS(ops) \ #define DECLARE_ACPI_FWNODE_OPS(ops) \
const struct fwnode_operations ops = { \ const struct fwnode_operations ops = { \
.device_is_available = acpi_fwnode_device_is_available, \ .device_is_available = acpi_fwnode_device_is_available, \
.device_get_match_data = acpi_fwnode_device_get_match_data, \ .device_get_match_data = acpi_fwnode_device_get_match_data, \
.device_dma_supported = \
acpi_fwnode_device_dma_supported, \
.device_get_dma_attr = acpi_fwnode_device_get_dma_attr, \
.property_present = acpi_fwnode_property_present, \ .property_present = acpi_fwnode_property_present, \
.property_read_int_array = \ .property_read_int_array = \
acpi_fwnode_property_read_int_array, \ acpi_fwnode_property_read_int_array, \
@ -1404,6 +1437,7 @@ acpi_fwnode_device_get_match_data(const struct fwnode_handle *fwnode,
acpi_graph_get_remote_endpoint, \ acpi_graph_get_remote_endpoint, \
.graph_get_port_parent = acpi_fwnode_get_parent, \ .graph_get_port_parent = acpi_fwnode_get_parent, \
.graph_parse_endpoint = acpi_fwnode_graph_parse_endpoint, \ .graph_parse_endpoint = acpi_fwnode_graph_parse_endpoint, \
.irq_get = acpi_fwnode_irq_get, \
}; \ }; \
EXPORT_SYMBOL_GPL(ops) EXPORT_SYMBOL_GPL(ops)

View File

@ -47,12 +47,14 @@ bool fwnode_property_present(const struct fwnode_handle *fwnode,
{ {
bool ret; bool ret;
if (IS_ERR_OR_NULL(fwnode))
return false;
ret = fwnode_call_bool_op(fwnode, property_present, propname); ret = fwnode_call_bool_op(fwnode, property_present, propname);
if (ret == false && !IS_ERR_OR_NULL(fwnode) && if (ret)
!IS_ERR_OR_NULL(fwnode->secondary)) return ret;
ret = fwnode_call_bool_op(fwnode->secondary, property_present,
propname); return fwnode_call_bool_op(fwnode->secondary, property_present, propname);
return ret;
} }
EXPORT_SYMBOL_GPL(fwnode_property_present); EXPORT_SYMBOL_GPL(fwnode_property_present);
@ -66,6 +68,9 @@ EXPORT_SYMBOL_GPL(fwnode_property_present);
* Function reads an array of u8 properties with @propname from the device * Function reads an array of u8 properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* It's recommended to call device_property_count_u8() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values if @val was %NULL, * Return: number of values if @val was %NULL,
* %0 if the property was found (success), * %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -91,6 +96,9 @@ EXPORT_SYMBOL_GPL(device_property_read_u8_array);
* Function reads an array of u16 properties with @propname from the device * Function reads an array of u16 properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* It's recommended to call device_property_count_u16() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values if @val was %NULL, * Return: number of values if @val was %NULL,
* %0 if the property was found (success), * %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -116,6 +124,9 @@ EXPORT_SYMBOL_GPL(device_property_read_u16_array);
* Function reads an array of u32 properties with @propname from the device * Function reads an array of u32 properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* It's recommended to call device_property_count_u32() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values if @val was %NULL, * Return: number of values if @val was %NULL,
* %0 if the property was found (success), * %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -141,6 +152,9 @@ EXPORT_SYMBOL_GPL(device_property_read_u32_array);
* Function reads an array of u64 properties with @propname from the device * Function reads an array of u64 properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* It's recommended to call device_property_count_u64() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values if @val was %NULL, * Return: number of values if @val was %NULL,
* %0 if the property was found (success), * %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -166,6 +180,9 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array);
* Function reads an array of string properties with @propname from the device * Function reads an array of string properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* It's recommended to call device_property_string_array_count() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values read on success if @val is non-NULL, * Return: number of values read on success if @val is non-NULL,
* number of values available on success if @val is NULL, * number of values available on success if @val is NULL,
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -232,15 +249,16 @@ static int fwnode_property_read_int_array(const struct fwnode_handle *fwnode,
{ {
int ret; int ret;
if (IS_ERR_OR_NULL(fwnode))
return -EINVAL;
ret = fwnode_call_int_op(fwnode, property_read_int_array, propname, ret = fwnode_call_int_op(fwnode, property_read_int_array, propname,
elem_size, val, nval); elem_size, val, nval);
if (ret == -EINVAL && !IS_ERR_OR_NULL(fwnode) && if (ret != -EINVAL)
!IS_ERR_OR_NULL(fwnode->secondary)) return ret;
ret = fwnode_call_int_op(
fwnode->secondary, property_read_int_array, propname,
elem_size, val, nval);
return ret; return fwnode_call_int_op(fwnode->secondary, property_read_int_array, propname,
elem_size, val, nval);
} }
/** /**
@ -253,6 +271,9 @@ static int fwnode_property_read_int_array(const struct fwnode_handle *fwnode,
* Read an array of u8 properties with @propname from @fwnode and stores them to * Read an array of u8 properties with @propname from @fwnode and stores them to
* @val if found. * @val if found.
* *
* It's recommended to call fwnode_property_count_u8() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values if @val was %NULL, * Return: number of values if @val was %NULL,
* %0 if the property was found (success), * %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -279,6 +300,9 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u8_array);
* Read an array of u16 properties with @propname from @fwnode and store them to * Read an array of u16 properties with @propname from @fwnode and store them to
* @val if found. * @val if found.
* *
* It's recommended to call fwnode_property_count_u16() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values if @val was %NULL, * Return: number of values if @val was %NULL,
* %0 if the property was found (success), * %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -305,6 +329,9 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u16_array);
* Read an array of u32 properties with @propname from @fwnode store them to * Read an array of u32 properties with @propname from @fwnode store them to
* @val if found. * @val if found.
* *
* It's recommended to call fwnode_property_count_u32() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values if @val was %NULL, * Return: number of values if @val was %NULL,
* %0 if the property was found (success), * %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -331,6 +358,9 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u32_array);
* Read an array of u64 properties with @propname from @fwnode and store them to * Read an array of u64 properties with @propname from @fwnode and store them to
* @val if found. * @val if found.
* *
* It's recommended to call fwnode_property_count_u64() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values if @val was %NULL, * Return: number of values if @val was %NULL,
* %0 if the property was found (success), * %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -357,6 +387,9 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array);
* Read an string list property @propname from the given firmware node and store * Read an string list property @propname from the given firmware node and store
* them to @val if found. * them to @val if found.
* *
* It's recommended to call fwnode_property_string_array_count() instead of calling
* this function with @val equals %NULL and @nval equals 0.
*
* Return: number of values read on success if @val is non-NULL, * Return: number of values read on success if @val is non-NULL,
* number of values available on success if @val is NULL, * number of values available on success if @val is NULL,
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
@ -371,14 +404,16 @@ int fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
{ {
int ret; int ret;
if (IS_ERR_OR_NULL(fwnode))
return -EINVAL;
ret = fwnode_call_int_op(fwnode, property_read_string_array, propname, ret = fwnode_call_int_op(fwnode, property_read_string_array, propname,
val, nval); val, nval);
if (ret == -EINVAL && !IS_ERR_OR_NULL(fwnode) && if (ret != -EINVAL)
!IS_ERR_OR_NULL(fwnode->secondary)) return ret;
ret = fwnode_call_int_op(fwnode->secondary,
property_read_string_array, propname, return fwnode_call_int_op(fwnode->secondary, property_read_string_array, propname,
val, nval); val, nval);
return ret;
} }
EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
@ -480,15 +515,19 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode,
{ {
int ret; int ret;
if (IS_ERR_OR_NULL(fwnode))
return -ENOENT;
ret = fwnode_call_int_op(fwnode, get_reference_args, prop, nargs_prop, ret = fwnode_call_int_op(fwnode, get_reference_args, prop, nargs_prop,
nargs, index, args); nargs, index, args);
if (ret == 0)
return ret;
if (ret < 0 && !IS_ERR_OR_NULL(fwnode) && if (IS_ERR_OR_NULL(fwnode->secondary))
!IS_ERR_OR_NULL(fwnode->secondary)) return ret;
ret = fwnode_call_int_op(fwnode->secondary, get_reference_args,
prop, nargs_prop, nargs, index, args);
return ret; return fwnode_call_int_op(fwnode->secondary, get_reference_args, prop, nargs_prop,
nargs, index, args);
} }
EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args); EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args);
@ -587,17 +626,17 @@ EXPORT_SYMBOL_GPL(fwnode_get_next_parent);
*/ */
struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode) struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode)
{ {
struct fwnode_handle *parent;
struct device *dev; struct device *dev;
fwnode_handle_get(fwnode); fwnode_for_each_parent_node(fwnode, parent) {
do { dev = get_dev_from_fwnode(parent);
fwnode = fwnode_get_next_parent(fwnode); if (dev) {
if (!fwnode) fwnode_handle_put(parent);
return NULL; return dev;
dev = get_dev_from_fwnode(fwnode); }
} while (!dev); }
fwnode_handle_put(fwnode); return NULL;
return dev;
} }
/** /**
@ -608,13 +647,11 @@ struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode)
*/ */
unsigned int fwnode_count_parents(const struct fwnode_handle *fwnode) unsigned int fwnode_count_parents(const struct fwnode_handle *fwnode)
{ {
struct fwnode_handle *__fwnode; struct fwnode_handle *parent;
unsigned int count; unsigned int count = 0;
__fwnode = fwnode_get_parent(fwnode); fwnode_for_each_parent_node(fwnode, parent)
count++;
for (count = 0; __fwnode; count++)
__fwnode = fwnode_get_next_parent(__fwnode);
return count; return count;
} }
@ -635,40 +672,43 @@ EXPORT_SYMBOL_GPL(fwnode_count_parents);
struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode, struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode,
unsigned int depth) unsigned int depth)
{ {
unsigned int i; struct fwnode_handle *parent;
fwnode_handle_get(fwnode); if (depth == 0)
return fwnode_handle_get(fwnode);
for (i = 0; i < depth && fwnode; i++) fwnode_for_each_parent_node(fwnode, parent) {
fwnode = fwnode_get_next_parent(fwnode); if (--depth == 0)
return parent;
return fwnode; }
return NULL;
} }
EXPORT_SYMBOL_GPL(fwnode_get_nth_parent); EXPORT_SYMBOL_GPL(fwnode_get_nth_parent);
/** /**
* fwnode_is_ancestor_of - Test if @test_ancestor is ancestor of @test_child * fwnode_is_ancestor_of - Test if @ancestor is ancestor of @child
* @test_ancestor: Firmware which is tested for being an ancestor * @ancestor: Firmware which is tested for being an ancestor
* @test_child: Firmware which is tested for being the child * @child: Firmware which is tested for being the child
* *
* A node is considered an ancestor of itself too. * A node is considered an ancestor of itself too.
* *
* Returns true if @test_ancestor is an ancestor of @test_child. * Returns true if @ancestor is an ancestor of @child. Otherwise, returns false.
* Otherwise, returns false.
*/ */
bool fwnode_is_ancestor_of(struct fwnode_handle *test_ancestor, bool fwnode_is_ancestor_of(struct fwnode_handle *ancestor, struct fwnode_handle *child)
struct fwnode_handle *test_child)
{ {
if (!test_ancestor) struct fwnode_handle *parent;
if (IS_ERR_OR_NULL(ancestor))
return false; return false;
fwnode_handle_get(test_child); if (child == ancestor)
while (test_child) { return true;
if (test_child == test_ancestor) {
fwnode_handle_put(test_child); fwnode_for_each_parent_node(child, parent) {
if (parent == ancestor) {
fwnode_handle_put(parent);
return true; return true;
} }
test_child = fwnode_get_next_parent(test_child);
} }
return false; return false;
} }
@ -698,7 +738,7 @@ fwnode_get_next_available_child_node(const struct fwnode_handle *fwnode,
{ {
struct fwnode_handle *next_child = child; struct fwnode_handle *next_child = child;
if (!fwnode) if (IS_ERR_OR_NULL(fwnode))
return NULL; return NULL;
do { do {
@ -722,16 +762,16 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev,
const struct fwnode_handle *fwnode = dev_fwnode(dev); const struct fwnode_handle *fwnode = dev_fwnode(dev);
struct fwnode_handle *next; struct fwnode_handle *next;
if (IS_ERR_OR_NULL(fwnode))
return NULL;
/* Try to find a child in primary fwnode */ /* Try to find a child in primary fwnode */
next = fwnode_get_next_child_node(fwnode, child); next = fwnode_get_next_child_node(fwnode, child);
if (next) if (next)
return next; return next;
/* When no more children in primary, continue with secondary */ /* When no more children in primary, continue with secondary */
if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) return fwnode_get_next_child_node(fwnode->secondary, child);
next = fwnode_get_next_child_node(fwnode->secondary, child);
return next;
} }
EXPORT_SYMBOL_GPL(device_get_next_child_node); EXPORT_SYMBOL_GPL(device_get_next_child_node);
@ -798,6 +838,9 @@ EXPORT_SYMBOL_GPL(fwnode_handle_put);
*/ */
bool fwnode_device_is_available(const struct fwnode_handle *fwnode) bool fwnode_device_is_available(const struct fwnode_handle *fwnode)
{ {
if (IS_ERR_OR_NULL(fwnode))
return false;
if (!fwnode_has_op(fwnode, device_is_available)) if (!fwnode_has_op(fwnode, device_is_available))
return true; return true;
@ -823,33 +866,16 @@ EXPORT_SYMBOL_GPL(device_get_child_node_count);
bool device_dma_supported(struct device *dev) bool device_dma_supported(struct device *dev)
{ {
const struct fwnode_handle *fwnode = dev_fwnode(dev); return fwnode_call_bool_op(dev_fwnode(dev), device_dma_supported);
/* For DT, this is always supported.
* For ACPI, this depends on CCA, which
* is determined by the acpi_dma_supported().
*/
if (is_of_node(fwnode))
return true;
return acpi_dma_supported(to_acpi_device_node(fwnode));
} }
EXPORT_SYMBOL_GPL(device_dma_supported); EXPORT_SYMBOL_GPL(device_dma_supported);
enum dev_dma_attr device_get_dma_attr(struct device *dev) enum dev_dma_attr device_get_dma_attr(struct device *dev)
{ {
const struct fwnode_handle *fwnode = dev_fwnode(dev); if (!fwnode_has_op(dev_fwnode(dev), device_get_dma_attr))
enum dev_dma_attr attr = DEV_DMA_NOT_SUPPORTED; return DEV_DMA_NOT_SUPPORTED;
if (is_of_node(fwnode)) { return fwnode_call_int_op(dev_fwnode(dev), device_get_dma_attr);
if (of_dma_is_coherent(to_of_node(fwnode)))
attr = DEV_DMA_COHERENT;
else
attr = DEV_DMA_NON_COHERENT;
} else
attr = acpi_get_dma_attr(to_acpi_device_node(fwnode));
return attr;
} }
EXPORT_SYMBOL_GPL(device_get_dma_attr); EXPORT_SYMBOL_GPL(device_get_dma_attr);
@ -904,10 +930,7 @@ EXPORT_SYMBOL_GPL(device_get_phy_mode);
*/ */
void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index) void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index)
{ {
if (IS_ENABLED(CONFIG_OF_ADDRESS) && is_of_node(fwnode)) return fwnode_call_ptr_op(fwnode, iomap, index);
return of_iomap(to_of_node(fwnode), index);
return NULL;
} }
EXPORT_SYMBOL(fwnode_iomap); EXPORT_SYMBOL(fwnode_iomap);
@ -921,17 +944,7 @@ EXPORT_SYMBOL(fwnode_iomap);
*/ */
int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index) int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index)
{ {
struct resource res; return fwnode_call_int_op(fwnode, irq_get, index);
int ret;
if (is_of_node(fwnode))
return of_irq_get(to_of_node(fwnode), index);
ret = acpi_irq_get(ACPI_HANDLE_FWNODE(fwnode), index, &res);
if (ret)
return ret;
return res.start;
} }
EXPORT_SYMBOL(fwnode_irq_get); EXPORT_SYMBOL(fwnode_irq_get);
@ -988,14 +1001,14 @@ fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
parent = fwnode_graph_get_port_parent(prev); parent = fwnode_graph_get_port_parent(prev);
else else
parent = fwnode; parent = fwnode;
if (IS_ERR_OR_NULL(parent))
return NULL;
ep = fwnode_call_ptr_op(parent, graph_get_next_endpoint, prev); ep = fwnode_call_ptr_op(parent, graph_get_next_endpoint, prev);
if (ep)
return ep;
if (IS_ERR_OR_NULL(ep) && return fwnode_graph_get_next_endpoint(parent->secondary, NULL);
!IS_ERR_OR_NULL(parent) && !IS_ERR_OR_NULL(parent->secondary))
ep = fwnode_graph_get_next_endpoint(parent->secondary, NULL);
return ep;
} }
EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint); EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);

View File

@ -22,6 +22,7 @@
#define pr_fmt(fmt) "OF: " fmt #define pr_fmt(fmt) "OF: " fmt
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_graph.h> #include <linux/of_graph.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
@ -872,6 +873,20 @@ static bool of_fwnode_device_is_available(const struct fwnode_handle *fwnode)
return of_device_is_available(to_of_node(fwnode)); return of_device_is_available(to_of_node(fwnode));
} }
static bool of_fwnode_device_dma_supported(const struct fwnode_handle *fwnode)
{
return true;
}
static enum dev_dma_attr
of_fwnode_device_get_dma_attr(const struct fwnode_handle *fwnode)
{
if (of_dma_is_coherent(to_of_node(fwnode)))
return DEV_DMA_COHERENT;
else
return DEV_DMA_NON_COHERENT;
}
static bool of_fwnode_property_present(const struct fwnode_handle *fwnode, static bool of_fwnode_property_present(const struct fwnode_handle *fwnode,
const char *propname) const char *propname)
{ {
@ -1450,6 +1465,21 @@ static int of_link_property(struct device_node *con_np, const char *prop_name)
return 0; return 0;
} }
static void __iomem *of_fwnode_iomap(struct fwnode_handle *fwnode, int index)
{
#ifdef CONFIG_OF_ADDRESS
return of_iomap(to_of_node(fwnode), index);
#else
return NULL;
#endif
}
static int of_fwnode_irq_get(const struct fwnode_handle *fwnode,
unsigned int index)
{
return of_irq_get(to_of_node(fwnode), index);
}
static int of_fwnode_add_links(struct fwnode_handle *fwnode) static int of_fwnode_add_links(struct fwnode_handle *fwnode)
{ {
struct property *p; struct property *p;
@ -1472,6 +1502,8 @@ const struct fwnode_operations of_fwnode_ops = {
.put = of_fwnode_put, .put = of_fwnode_put,
.device_is_available = of_fwnode_device_is_available, .device_is_available = of_fwnode_device_is_available,
.device_get_match_data = of_fwnode_device_get_match_data, .device_get_match_data = of_fwnode_device_get_match_data,
.device_dma_supported = of_fwnode_device_dma_supported,
.device_get_dma_attr = of_fwnode_device_get_dma_attr,
.property_present = of_fwnode_property_present, .property_present = of_fwnode_property_present,
.property_read_int_array = of_fwnode_property_read_int_array, .property_read_int_array = of_fwnode_property_read_int_array,
.property_read_string_array = of_fwnode_property_read_string_array, .property_read_string_array = of_fwnode_property_read_string_array,
@ -1485,6 +1517,8 @@ const struct fwnode_operations of_fwnode_ops = {
.graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint, .graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint,
.graph_get_port_parent = of_fwnode_graph_get_port_parent, .graph_get_port_parent = of_fwnode_graph_get_port_parent,
.graph_parse_endpoint = of_fwnode_graph_parse_endpoint, .graph_parse_endpoint = of_fwnode_graph_parse_endpoint,
.iomap = of_fwnode_iomap,
.irq_get = of_fwnode_irq_get,
.add_links = of_fwnode_add_links, .add_links = of_fwnode_add_links,
}; };
EXPORT_SYMBOL_GPL(of_fwnode_ops); EXPORT_SYMBOL_GPL(of_fwnode_ops);

View File

@ -113,6 +113,9 @@ struct fwnode_operations {
bool (*device_is_available)(const struct fwnode_handle *fwnode); bool (*device_is_available)(const struct fwnode_handle *fwnode);
const void *(*device_get_match_data)(const struct fwnode_handle *fwnode, const void *(*device_get_match_data)(const struct fwnode_handle *fwnode,
const struct device *dev); const struct device *dev);
bool (*device_dma_supported)(const struct fwnode_handle *fwnode);
enum dev_dma_attr
(*device_get_dma_attr)(const struct fwnode_handle *fwnode);
bool (*property_present)(const struct fwnode_handle *fwnode, bool (*property_present)(const struct fwnode_handle *fwnode,
const char *propname); const char *propname);
int (*property_read_int_array)(const struct fwnode_handle *fwnode, int (*property_read_int_array)(const struct fwnode_handle *fwnode,
@ -145,15 +148,17 @@ struct fwnode_operations {
(*graph_get_port_parent)(struct fwnode_handle *fwnode); (*graph_get_port_parent)(struct fwnode_handle *fwnode);
int (*graph_parse_endpoint)(const struct fwnode_handle *fwnode, int (*graph_parse_endpoint)(const struct fwnode_handle *fwnode,
struct fwnode_endpoint *endpoint); struct fwnode_endpoint *endpoint);
void __iomem *(*iomap)(struct fwnode_handle *fwnode, int index);
int (*irq_get)(const struct fwnode_handle *fwnode, unsigned int index);
int (*add_links)(struct fwnode_handle *fwnode); int (*add_links)(struct fwnode_handle *fwnode);
}; };
#define fwnode_has_op(fwnode, op) \ #define fwnode_has_op(fwnode, op) \
((fwnode) && (fwnode)->ops && (fwnode)->ops->op) (!IS_ERR_OR_NULL(fwnode) && (fwnode)->ops && (fwnode)->ops->op)
#define fwnode_call_int_op(fwnode, op, ...) \ #define fwnode_call_int_op(fwnode, op, ...) \
(fwnode ? (fwnode_has_op(fwnode, op) ? \ (fwnode_has_op(fwnode, op) ? \
(fwnode)->ops->op(fwnode, ## __VA_ARGS__) : -ENXIO) : \ (fwnode)->ops->op(fwnode, ## __VA_ARGS__) : (IS_ERR_OR_NULL(fwnode) ? -EINVAL : -ENXIO))
-EINVAL)
#define fwnode_call_bool_op(fwnode, op, ...) \ #define fwnode_call_bool_op(fwnode, op, ...) \
(fwnode_has_op(fwnode, op) ? \ (fwnode_has_op(fwnode, op) ? \

View File

@ -83,15 +83,19 @@ struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode,
const char *fwnode_get_name(const struct fwnode_handle *fwnode); const char *fwnode_get_name(const struct fwnode_handle *fwnode);
const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode); const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode);
struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode); struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode);
struct fwnode_handle *fwnode_get_next_parent( struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode);
struct fwnode_handle *fwnode);
#define fwnode_for_each_parent_node(fwnode, parent) \
for (parent = fwnode_get_parent(fwnode); parent; \
parent = fwnode_get_next_parent(parent))
struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode); struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode);
unsigned int fwnode_count_parents(const struct fwnode_handle *fwn); unsigned int fwnode_count_parents(const struct fwnode_handle *fwn);
struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwn, struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwn,
unsigned int depth); unsigned int depth);
bool fwnode_is_ancestor_of(struct fwnode_handle *test_ancestor, bool fwnode_is_ancestor_of(struct fwnode_handle *ancestor, struct fwnode_handle *child);
struct fwnode_handle *test_child);
struct fwnode_handle *fwnode_get_next_child_node( struct fwnode_handle *fwnode_get_next_child_node(
const struct fwnode_handle *fwnode, struct fwnode_handle *child); const struct fwnode_handle *fwnode, struct fwnode_handle *child);
struct fwnode_handle *fwnode_get_next_available_child_node( struct fwnode_handle *fwnode_get_next_available_child_node(