2018-11-09 17:21:36 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Software nodes for the firmware node framework .
*
* Copyright ( C ) 2018 , Intel Corporation
* Author : Heikki Krogerus < heikki . krogerus @ linux . intel . com >
*/
# include <linux/device.h>
# include <linux/kernel.h>
# include <linux/property.h>
# include <linux/slab.h>
2021-07-12 20:27:12 +03:00
# include "base.h"
2019-05-31 17:15:34 +03:00
struct swnode {
2018-11-09 17:21:36 +03:00
struct kobject kobj ;
struct fwnode_handle fwnode ;
2019-05-31 17:15:34 +03:00
const struct software_node * node ;
2021-03-29 18:12:05 +03:00
int id ;
2018-11-09 17:21:36 +03:00
/* hierarchy */
struct ida child_ids ;
struct list_head entry ;
struct list_head children ;
2019-05-31 17:15:34 +03:00
struct swnode * parent ;
2018-11-09 17:21:36 +03:00
2019-05-31 17:15:34 +03:00
unsigned int allocated : 1 ;
2021-02-04 17:17:06 +03:00
unsigned int managed : 1 ;
2018-11-09 17:21:36 +03:00
} ;
static DEFINE_IDA ( swnode_root_ids ) ;
static struct kset * swnode_kset ;
2019-05-31 17:15:34 +03:00
# define kobj_to_swnode(_kobj_) container_of(_kobj_, struct swnode, kobj)
2018-11-09 17:21:36 +03:00
static const struct fwnode_operations software_node_ops ;
bool is_software_node ( const struct fwnode_handle * fwnode )
{
return ! IS_ERR_OR_NULL ( fwnode ) & & fwnode - > ops = = & software_node_ops ;
}
2019-05-31 17:15:34 +03:00
EXPORT_SYMBOL_GPL ( is_software_node ) ;
2018-11-09 17:21:36 +03:00
2019-05-31 17:15:34 +03:00
# define to_swnode(__fwnode) \
2018-11-09 17:21:36 +03:00
( { \
2019-05-31 17:15:34 +03:00
typeof ( __fwnode ) __to_swnode_fwnode = __fwnode ; \
2018-11-09 17:21:36 +03:00
\
2019-05-31 17:15:34 +03:00
is_software_node ( __to_swnode_fwnode ) ? \
container_of ( __to_swnode_fwnode , \
struct swnode , fwnode ) : NULL ; \
2018-11-09 17:21:36 +03:00
} )
2021-01-15 12:49:11 +03:00
static inline struct swnode * dev_to_swnode ( struct device * dev )
{
struct fwnode_handle * fwnode = dev_fwnode ( dev ) ;
if ( ! fwnode )
return NULL ;
if ( ! is_software_node ( fwnode ) )
fwnode = fwnode - > secondary ;
return to_swnode ( fwnode ) ;
}
2019-05-31 17:15:34 +03:00
static struct swnode *
software_node_to_swnode ( const struct software_node * node )
{
2019-08-30 10:51:55 +03:00
struct swnode * swnode = NULL ;
2019-05-31 17:15:34 +03:00
struct kobject * k ;
if ( ! node )
return NULL ;
spin_lock ( & swnode_kset - > list_lock ) ;
list_for_each_entry ( k , & swnode_kset - > list , entry ) {
swnode = kobj_to_swnode ( k ) ;
if ( swnode - > node = = node )
break ;
swnode = NULL ;
}
spin_unlock ( & swnode_kset - > list_lock ) ;
return swnode ;
}
2019-10-03 15:32:09 +03:00
const struct software_node * to_software_node ( const struct fwnode_handle * fwnode )
2019-05-31 17:15:34 +03:00
{
2019-10-03 15:32:09 +03:00
const struct swnode * swnode = to_swnode ( fwnode ) ;
2019-05-31 17:15:34 +03:00
return swnode ? swnode - > node : NULL ;
}
EXPORT_SYMBOL_GPL ( to_software_node ) ;
struct fwnode_handle * software_node_fwnode ( const struct software_node * node )
{
struct swnode * swnode = software_node_to_swnode ( node ) ;
return swnode ? & swnode - > fwnode : NULL ;
}
EXPORT_SYMBOL_GPL ( software_node_fwnode ) ;
2018-11-09 17:21:36 +03:00
/* -------------------------------------------------------------------------- */
/* property_entry processing */
static const struct property_entry *
property_entry_get ( const struct property_entry * prop , const char * name )
{
if ( ! prop )
return NULL ;
for ( ; prop - > name ; prop + + )
if ( ! strcmp ( name , prop - > name ) )
return prop ;
return NULL ;
}
static const void * property_get_pointer ( const struct property_entry * prop )
{
2019-10-23 23:02:24 +03:00
if ( ! prop - > length )
2018-11-09 17:21:36 +03:00
return NULL ;
2019-10-23 23:02:24 +03:00
2019-11-08 07:22:20 +03:00
return prop - > is_inline ? & prop - > value : prop - > pointer ;
2018-11-09 17:21:36 +03:00
}
static const void * property_entry_find ( const struct property_entry * props ,
const char * propname , size_t length )
{
const struct property_entry * prop ;
const void * pointer ;
prop = property_entry_get ( props , propname ) ;
if ( ! prop )
return ERR_PTR ( - EINVAL ) ;
pointer = property_get_pointer ( prop ) ;
if ( ! pointer )
return ERR_PTR ( - ENODATA ) ;
if ( length > prop - > length )
return ERR_PTR ( - EOVERFLOW ) ;
return pointer ;
}
static int
property_entry_count_elems_of_size ( const struct property_entry * props ,
const char * propname , size_t length )
{
const struct property_entry * prop ;
prop = property_entry_get ( props , propname ) ;
if ( ! prop )
return - EINVAL ;
return prop - > length / length ;
}
static int property_entry_read_int_array ( const struct property_entry * props ,
const char * name ,
unsigned int elem_size , void * val ,
size_t nval )
{
2019-10-23 23:02:25 +03:00
const void * pointer ;
size_t length ;
2018-11-09 17:21:36 +03:00
if ( ! val )
return property_entry_count_elems_of_size ( props , name ,
elem_size ) ;
2019-10-23 23:02:25 +03:00
if ( ! is_power_of_2 ( elem_size ) | | elem_size > sizeof ( u64 ) )
return - ENXIO ;
length = nval * elem_size ;
pointer = property_entry_find ( props , name , length ) ;
if ( IS_ERR ( pointer ) )
return PTR_ERR ( pointer ) ;
memcpy ( val , pointer , length ) ;
return 0 ;
2018-11-09 17:21:36 +03:00
}
static int property_entry_read_string_array ( const struct property_entry * props ,
const char * propname ,
const char * * strings , size_t nval )
{
const void * pointer ;
2019-10-23 23:02:27 +03:00
size_t length ;
int array_len ;
2018-11-09 17:21:36 +03:00
/* Find out the array length. */
2019-10-23 23:02:27 +03:00
array_len = property_entry_count_elems_of_size ( props , propname ,
sizeof ( const char * ) ) ;
if ( array_len < 0 )
return array_len ;
2018-11-09 17:21:36 +03:00
/* Return how many there are if strings is NULL. */
if ( ! strings )
return array_len ;
2019-10-23 23:02:27 +03:00
array_len = min_t ( size_t , nval , array_len ) ;
2018-11-09 17:21:36 +03:00
length = array_len * sizeof ( * strings ) ;
pointer = property_entry_find ( props , propname , length ) ;
if ( IS_ERR ( pointer ) )
return PTR_ERR ( pointer ) ;
memcpy ( strings , pointer , length ) ;
return array_len ;
}
2018-11-09 17:21:37 +03:00
static void property_entry_free_data ( const struct property_entry * p )
{
2019-10-23 23:02:24 +03:00
const char * const * src_str ;
2018-11-09 17:21:37 +03:00
size_t i , nval ;
2019-11-08 07:22:21 +03:00
if ( p - > type = = DEV_PROP_STRING ) {
src_str = property_get_pointer ( p ) ;
nval = p - > length / sizeof ( * src_str ) ;
for ( i = 0 ; i < nval ; i + + )
kfree ( src_str [ i ] ) ;
2018-11-09 17:21:37 +03:00
}
2019-11-08 07:22:21 +03:00
if ( ! p - > is_inline )
kfree ( p - > pointer ) ;
2018-11-09 17:21:37 +03:00
kfree ( p - > name ) ;
}
2019-11-08 07:22:21 +03:00
static bool property_copy_string_array ( const char * * dst_ptr ,
const char * const * src_ptr ,
size_t nval )
2018-11-09 17:21:37 +03:00
{
int i ;
for ( i = 0 ; i < nval ; i + + ) {
2019-11-08 07:22:21 +03:00
dst_ptr [ i ] = kstrdup ( src_ptr [ i ] , GFP_KERNEL ) ;
if ( ! dst_ptr [ i ] & & src_ptr [ i ] ) {
2018-11-09 17:21:37 +03:00
while ( - - i > = 0 )
2019-11-08 07:22:21 +03:00
kfree ( dst_ptr [ i ] ) ;
return false ;
2018-11-09 17:21:37 +03:00
}
}
2019-11-08 07:22:21 +03:00
return true ;
2018-11-09 17:21:37 +03:00
}
static int property_entry_copy_data ( struct property_entry * dst ,
const struct property_entry * src )
{
const void * pointer = property_get_pointer ( src ) ;
2019-11-08 07:22:21 +03:00
void * dst_ptr ;
size_t nval ;
/*
* Properties with no data should not be marked as stored
* out of line .
*/
if ( ! src - > is_inline & & ! src - > length )
return - ENODATA ;
software node: implement reference properties
It is possible to store references to software nodes in the same fashion as
other static properties, so that users do not need to define separate
structures:
static const struct software_node gpio_bank_b_node = {
.name = "B",
};
static const struct property_entry simone_key_enter_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_ENTER),
PROPERTY_ENTRY_STRING("label", "enter"),
PROPERTY_ENTRY_REF("gpios", &gpio_bank_b_node, 123, GPIO_ACTIVE_LOW),
{ }
};
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2019-11-08 07:22:22 +03:00
/*
* Reference properties are never stored inline as
* they are too big .
*/
if ( src - > type = = DEV_PROP_REF & & src - > is_inline )
return - EINVAL ;
2019-11-08 07:22:21 +03:00
if ( src - > length < = sizeof ( dst - > value ) ) {
dst_ptr = & dst - > value ;
dst - > is_inline = true ;
} else {
dst_ptr = kmalloc ( src - > length , GFP_KERNEL ) ;
if ( ! dst_ptr )
2018-11-09 17:21:37 +03:00
return - ENOMEM ;
2019-11-08 07:22:21 +03:00
dst - > pointer = dst_ptr ;
}
2019-10-23 23:02:24 +03:00
2019-11-08 07:22:21 +03:00
if ( src - > type = = DEV_PROP_STRING ) {
nval = src - > length / sizeof ( const char * ) ;
if ( ! property_copy_string_array ( dst_ptr , pointer , nval ) ) {
if ( ! dst - > is_inline )
kfree ( dst - > pointer ) ;
return - ENOMEM ;
}
2018-11-09 17:21:37 +03:00
} else {
2019-11-08 07:22:21 +03:00
memcpy ( dst_ptr , pointer , src - > length ) ;
2018-11-09 17:21:37 +03:00
}
dst - > length = src - > length ;
dst - > type = src - > type ;
dst - > name = kstrdup ( src - > name , GFP_KERNEL ) ;
2019-11-08 07:22:21 +03:00
if ( ! dst - > name ) {
property_entry_free_data ( dst ) ;
return - ENOMEM ;
}
2018-11-09 17:21:37 +03:00
return 0 ;
}
/**
* property_entries_dup - duplicate array of properties
* @ properties : array of properties to copy
*
* This function creates a deep copy of the given NULL - terminated array
* of property entries .
*/
struct property_entry *
property_entries_dup ( const struct property_entry * properties )
{
struct property_entry * p ;
int i , n = 0 ;
int ret ;
2019-05-31 17:15:32 +03:00
if ( ! properties )
return NULL ;
2018-11-09 17:21:37 +03:00
while ( properties [ n ] . name )
n + + ;
p = kcalloc ( n + 1 , sizeof ( * p ) , GFP_KERNEL ) ;
if ( ! p )
return ERR_PTR ( - ENOMEM ) ;
for ( i = 0 ; i < n ; i + + ) {
ret = property_entry_copy_data ( & p [ i ] , & properties [ i ] ) ;
if ( ret ) {
while ( - - i > = 0 )
property_entry_free_data ( & p [ i ] ) ;
kfree ( p ) ;
return ERR_PTR ( ret ) ;
}
}
return p ;
}
EXPORT_SYMBOL_GPL ( property_entries_dup ) ;
/**
* property_entries_free - free previously allocated array of properties
* @ properties : array of properties to destroy
*
* This function frees given NULL - terminated array of property entries ,
* along with their data .
*/
void property_entries_free ( const struct property_entry * properties )
{
const struct property_entry * p ;
if ( ! properties )
return ;
for ( p = properties ; p - > name ; p + + )
property_entry_free_data ( p ) ;
kfree ( properties ) ;
}
EXPORT_SYMBOL_GPL ( property_entries_free ) ;
2018-11-09 17:21:36 +03:00
/* -------------------------------------------------------------------------- */
/* fwnode operations */
static struct fwnode_handle * software_node_get ( struct fwnode_handle * fwnode )
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode = to_swnode ( fwnode ) ;
2018-11-09 17:21:36 +03:00
kobject_get ( & swnode - > kobj ) ;
return & swnode - > fwnode ;
}
static void software_node_put ( struct fwnode_handle * fwnode )
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode = to_swnode ( fwnode ) ;
2018-11-09 17:21:36 +03:00
kobject_put ( & swnode - > kobj ) ;
}
static bool software_node_property_present ( const struct fwnode_handle * fwnode ,
const char * propname )
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode = to_swnode ( fwnode ) ;
return ! ! property_entry_get ( swnode - > node - > properties , propname ) ;
2018-11-09 17:21:36 +03:00
}
static int software_node_read_int_array ( const struct fwnode_handle * fwnode ,
const char * propname ,
unsigned int elem_size , void * val ,
size_t nval )
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode = to_swnode ( fwnode ) ;
2018-11-09 17:21:36 +03:00
2019-05-31 17:15:34 +03:00
return property_entry_read_int_array ( swnode - > node - > properties , propname ,
2018-11-09 17:21:36 +03:00
elem_size , val , nval ) ;
}
static int software_node_read_string_array ( const struct fwnode_handle * fwnode ,
const char * propname ,
const char * * val , size_t nval )
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode = to_swnode ( fwnode ) ;
2018-11-09 17:21:36 +03:00
2019-05-31 17:15:34 +03:00
return property_entry_read_string_array ( swnode - > node - > properties ,
propname , val , nval ) ;
2018-11-09 17:21:36 +03:00
}
2019-10-03 15:32:12 +03:00
static const char *
software_node_get_name ( const struct fwnode_handle * fwnode )
{
const struct swnode * swnode = to_swnode ( fwnode ) ;
return kobject_name ( & swnode - > kobj ) ;
}
2019-10-03 15:32:13 +03:00
static const char *
software_node_get_name_prefix ( const struct fwnode_handle * fwnode )
{
struct fwnode_handle * parent ;
const char * prefix ;
parent = fwnode_get_parent ( fwnode ) ;
if ( ! parent )
return " " ;
/* Figure out the prefix from the parents. */
while ( is_software_node ( parent ) )
parent = fwnode_get_next_parent ( parent ) ;
prefix = fwnode_get_name_prefix ( parent ) ;
fwnode_handle_put ( parent ) ;
/* Guess something if prefix was NULL. */
return prefix ? : " / " ;
}
2019-03-19 18:20:42 +03:00
static struct fwnode_handle *
2018-11-09 17:21:36 +03:00
software_node_get_parent ( const struct fwnode_handle * fwnode )
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode = to_swnode ( fwnode ) ;
2018-11-09 17:21:36 +03:00
2019-10-03 15:32:08 +03:00
if ( ! swnode | | ! swnode - > parent )
return NULL ;
return fwnode_handle_get ( & swnode - > parent - > fwnode ) ;
2018-11-09 17:21:36 +03:00
}
2019-03-19 18:20:42 +03:00
static struct fwnode_handle *
2018-11-09 17:21:36 +03:00
software_node_get_next_child ( const struct fwnode_handle * fwnode ,
struct fwnode_handle * child )
{
2019-05-31 17:15:34 +03:00
struct swnode * p = to_swnode ( fwnode ) ;
struct swnode * c = to_swnode ( child ) ;
2018-11-09 17:21:36 +03:00
2018-12-22 15:49:39 +03:00
if ( ! p | | list_empty ( & p - > children ) | |
2021-01-07 16:28:24 +03:00
( c & & list_is_last ( & c - > entry , & p - > children ) ) ) {
fwnode_handle_put ( child ) ;
2018-11-09 17:21:36 +03:00
return NULL ;
2021-01-07 16:28:24 +03:00
}
2018-11-09 17:21:36 +03:00
if ( c )
c = list_next_entry ( c , entry ) ;
else
2019-05-31 17:15:34 +03:00
c = list_first_entry ( & p - > children , struct swnode , entry ) ;
2021-01-07 16:28:24 +03:00
fwnode_handle_put ( child ) ;
return fwnode_handle_get ( & c - > fwnode ) ;
2018-11-09 17:21:36 +03:00
}
2019-02-13 14:55:49 +03:00
static struct fwnode_handle *
software_node_get_named_child_node ( const struct fwnode_handle * fwnode ,
const char * childname )
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode = to_swnode ( fwnode ) ;
struct swnode * child ;
2019-02-13 14:55:49 +03:00
if ( ! swnode | | list_empty ( & swnode - > children ) )
return NULL ;
list_for_each_entry ( child , & swnode - > children , entry ) {
2019-05-31 17:15:35 +03:00
if ( ! strcmp ( childname , kobject_name ( & child - > kobj ) ) ) {
2019-02-13 14:55:49 +03:00
kobject_get ( & child - > kobj ) ;
return & child - > fwnode ;
}
}
return NULL ;
}
2018-11-09 17:21:36 +03:00
2019-05-31 17:15:36 +03:00
static int
software_node_get_reference_args ( const struct fwnode_handle * fwnode ,
const char * propname , const char * nargs_prop ,
unsigned int nargs , unsigned int index ,
struct fwnode_reference_args * args )
{
struct swnode * swnode = to_swnode ( fwnode ) ;
software node: implement reference properties
It is possible to store references to software nodes in the same fashion as
other static properties, so that users do not need to define separate
structures:
static const struct software_node gpio_bank_b_node = {
.name = "B",
};
static const struct property_entry simone_key_enter_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_ENTER),
PROPERTY_ENTRY_STRING("label", "enter"),
PROPERTY_ENTRY_REF("gpios", &gpio_bank_b_node, 123, GPIO_ACTIVE_LOW),
{ }
};
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2019-11-08 07:22:22 +03:00
const struct software_node_ref_args * ref_array ;
2019-11-08 07:22:24 +03:00
const struct software_node_ref_args * ref ;
2019-05-31 17:15:36 +03:00
const struct property_entry * prop ;
struct fwnode_handle * refnode ;
2019-11-08 07:22:21 +03:00
u32 nargs_prop_val ;
int error ;
2019-05-31 17:15:36 +03:00
int i ;
software node: implement reference properties
It is possible to store references to software nodes in the same fashion as
other static properties, so that users do not need to define separate
structures:
static const struct software_node gpio_bank_b_node = {
.name = "B",
};
static const struct property_entry simone_key_enter_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_ENTER),
PROPERTY_ENTRY_STRING("label", "enter"),
PROPERTY_ENTRY_REF("gpios", &gpio_bank_b_node, 123, GPIO_ACTIVE_LOW),
{ }
};
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2019-11-08 07:22:22 +03:00
prop = property_entry_get ( swnode - > node - > properties , propname ) ;
2019-11-08 07:22:24 +03:00
if ( ! prop )
return - ENOENT ;
if ( prop - > type ! = DEV_PROP_REF )
return - EINVAL ;
software node: implement reference properties
It is possible to store references to software nodes in the same fashion as
other static properties, so that users do not need to define separate
structures:
static const struct software_node gpio_bank_b_node = {
.name = "B",
};
static const struct property_entry simone_key_enter_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_ENTER),
PROPERTY_ENTRY_STRING("label", "enter"),
PROPERTY_ENTRY_REF("gpios", &gpio_bank_b_node, 123, GPIO_ACTIVE_LOW),
{ }
};
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2019-11-08 07:22:22 +03:00
2019-11-08 07:22:24 +03:00
/*
* We expect that references are never stored inline , even
* single ones , as they are too big .
*/
if ( prop - > is_inline )
return - EINVAL ;
software node: implement reference properties
It is possible to store references to software nodes in the same fashion as
other static properties, so that users do not need to define separate
structures:
static const struct software_node gpio_bank_b_node = {
.name = "B",
};
static const struct property_entry simone_key_enter_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_ENTER),
PROPERTY_ENTRY_STRING("label", "enter"),
PROPERTY_ENTRY_REF("gpios", &gpio_bank_b_node, 123, GPIO_ACTIVE_LOW),
{ }
};
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2019-11-08 07:22:22 +03:00
2019-11-08 07:22:24 +03:00
if ( index * sizeof ( * ref ) > = prop - > length )
return - ENOENT ;
software node: implement reference properties
It is possible to store references to software nodes in the same fashion as
other static properties, so that users do not need to define separate
structures:
static const struct software_node gpio_bank_b_node = {
.name = "B",
};
static const struct property_entry simone_key_enter_props[] = {
PROPERTY_ENTRY_U32("linux,code", KEY_ENTER),
PROPERTY_ENTRY_STRING("label", "enter"),
PROPERTY_ENTRY_REF("gpios", &gpio_bank_b_node, 123, GPIO_ACTIVE_LOW),
{ }
};
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2019-11-08 07:22:22 +03:00
2019-11-08 07:22:24 +03:00
ref_array = prop - > pointer ;
ref = & ref_array [ index ] ;
2019-05-31 17:15:36 +03:00
2019-11-08 07:22:24 +03:00
refnode = software_node_fwnode ( ref - > node ) ;
2019-05-31 17:15:36 +03:00
if ( ! refnode )
return - ENOENT ;
if ( nargs_prop ) {
2021-12-21 00:05:33 +03:00
error = property_entry_read_int_array ( ref - > node - > properties ,
2019-11-08 07:22:21 +03:00
nargs_prop , sizeof ( u32 ) ,
& nargs_prop_val , 1 ) ;
if ( error )
return error ;
2019-05-31 17:15:36 +03:00
2019-11-08 07:22:21 +03:00
nargs = nargs_prop_val ;
2019-05-31 17:15:36 +03:00
}
if ( nargs > NR_FWNODE_REFERENCE_ARGS )
return - EINVAL ;
args - > fwnode = software_node_get ( refnode ) ;
args - > nargs = nargs ;
for ( i = 0 ; i < nargs ; i + + )
2019-11-08 07:22:24 +03:00
args - > args [ i ] = ref - > args [ i ] ;
2019-05-31 17:15:36 +03:00
return 0 ;
}
2021-01-07 16:28:31 +03:00
static struct fwnode_handle *
swnode_graph_find_next_port ( const struct fwnode_handle * parent ,
struct fwnode_handle * port )
{
struct fwnode_handle * old = port ;
while ( ( port = software_node_get_next_child ( parent , old ) ) ) {
/*
* fwnode ports have naming style " port@ " , so we search for any
* children that follow that convention .
*/
if ( ! strncmp ( to_swnode ( port ) - > node - > name , " port@ " ,
strlen ( " port@ " ) ) )
return port ;
old = port ;
}
return NULL ;
}
static struct fwnode_handle *
software_node_graph_get_next_endpoint ( const struct fwnode_handle * fwnode ,
struct fwnode_handle * endpoint )
{
struct swnode * swnode = to_swnode ( fwnode ) ;
struct fwnode_handle * parent ;
struct fwnode_handle * port ;
if ( ! swnode )
return NULL ;
if ( endpoint ) {
port = software_node_get_parent ( endpoint ) ;
parent = software_node_get_parent ( port ) ;
} else {
parent = software_node_get_named_child_node ( fwnode , " ports " ) ;
if ( ! parent )
parent = software_node_get ( & swnode - > fwnode ) ;
port = swnode_graph_find_next_port ( parent , NULL ) ;
}
for ( ; port ; port = swnode_graph_find_next_port ( parent , port ) ) {
endpoint = software_node_get_next_child ( port , endpoint ) ;
if ( endpoint ) {
fwnode_handle_put ( port ) ;
break ;
}
}
fwnode_handle_put ( parent ) ;
return endpoint ;
}
static struct fwnode_handle *
software_node_graph_get_remote_endpoint ( const struct fwnode_handle * fwnode )
{
struct swnode * swnode = to_swnode ( fwnode ) ;
const struct software_node_ref_args * ref ;
const struct property_entry * prop ;
if ( ! swnode )
return NULL ;
prop = property_entry_get ( swnode - > node - > properties , " remote-endpoint " ) ;
if ( ! prop | | prop - > type ! = DEV_PROP_REF | | prop - > is_inline )
return NULL ;
ref = prop - > pointer ;
return software_node_get ( software_node_fwnode ( ref [ 0 ] . node ) ) ;
}
static struct fwnode_handle *
software_node_graph_get_port_parent ( struct fwnode_handle * fwnode )
{
struct swnode * swnode = to_swnode ( fwnode ) ;
swnode = swnode - > parent ;
if ( swnode & & ! strcmp ( swnode - > node - > name , " ports " ) )
swnode = swnode - > parent ;
return swnode ? software_node_get ( & swnode - > fwnode ) : NULL ;
}
static int
software_node_graph_parse_endpoint ( const struct fwnode_handle * fwnode ,
struct fwnode_endpoint * endpoint )
{
struct swnode * swnode = to_swnode ( fwnode ) ;
const char * parent_name = swnode - > parent - > node - > name ;
int ret ;
if ( strlen ( " port@ " ) > = strlen ( parent_name ) | |
strncmp ( parent_name , " port@ " , strlen ( " port@ " ) ) )
return - EINVAL ;
/* Ports have naming style "port@n", we need to select the n */
ret = kstrtou32 ( parent_name + strlen ( " port@ " ) , 10 , & endpoint - > port ) ;
if ( ret )
return ret ;
endpoint - > id = swnode - > id ;
endpoint - > local_fwnode = fwnode ;
return 0 ;
}
2018-11-09 17:21:36 +03:00
static const struct fwnode_operations software_node_ops = {
. get = software_node_get ,
. put = software_node_put ,
. property_present = software_node_property_present ,
. property_read_int_array = software_node_read_int_array ,
. property_read_string_array = software_node_read_string_array ,
2019-10-03 15:32:12 +03:00
. get_name = software_node_get_name ,
2019-10-03 15:32:13 +03:00
. get_name_prefix = software_node_get_name_prefix ,
2018-11-09 17:21:36 +03:00
. get_parent = software_node_get_parent ,
. get_next_child_node = software_node_get_next_child ,
2019-02-13 14:55:49 +03:00
. get_named_child_node = software_node_get_named_child_node ,
2021-01-07 16:28:31 +03:00
. get_reference_args = software_node_get_reference_args ,
. graph_get_next_endpoint = software_node_graph_get_next_endpoint ,
. graph_get_remote_endpoint = software_node_graph_get_remote_endpoint ,
. graph_get_port_parent = software_node_graph_get_port_parent ,
. graph_parse_endpoint = software_node_graph_parse_endpoint ,
2018-11-09 17:21:36 +03:00
} ;
/* -------------------------------------------------------------------------- */
2019-08-19 13:07:22 +03:00
/**
* software_node_find_by_name - Find software node by name
* @ parent : Parent of the software node
* @ name : Name of the software node
*
* The function will find a node that is child of @ parent and that is named
* @ name . If no node is found , the function returns NULL .
*
* NOTE : you will need to drop the reference with fwnode_handle_put ( ) after use .
*/
const struct software_node *
software_node_find_by_name ( const struct software_node * parent , const char * name )
{
2019-08-30 10:51:56 +03:00
struct swnode * swnode = NULL ;
2019-08-19 13:07:22 +03:00
struct kobject * k ;
if ( ! name )
return NULL ;
spin_lock ( & swnode_kset - > list_lock ) ;
list_for_each_entry ( k , & swnode_kset - > list , entry ) {
swnode = kobj_to_swnode ( k ) ;
if ( parent = = swnode - > node - > parent & & swnode - > node - > name & &
! strcmp ( name , swnode - > node - > name ) ) {
kobject_get ( & swnode - > kobj ) ;
break ;
}
swnode = NULL ;
}
spin_unlock ( & swnode_kset - > list_lock ) ;
return swnode ? swnode - > node : NULL ;
}
EXPORT_SYMBOL_GPL ( software_node_find_by_name ) ;
2021-03-29 18:12:03 +03:00
static struct software_node * software_node_alloc ( const struct property_entry * properties )
2018-11-09 17:21:36 +03:00
{
struct property_entry * props ;
2021-03-29 18:12:03 +03:00
struct software_node * node ;
2018-11-09 17:21:36 +03:00
props = property_entries_dup ( properties ) ;
if ( IS_ERR ( props ) )
2021-03-29 18:12:03 +03:00
return ERR_CAST ( props ) ;
node = kzalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! node ) {
property_entries_free ( props ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2018-11-09 17:21:36 +03:00
2019-05-31 17:15:34 +03:00
node - > properties = props ;
2018-11-09 17:21:36 +03:00
2021-03-29 18:12:03 +03:00
return node ;
}
static void software_node_free ( const struct software_node * node )
{
property_entries_free ( node - > properties ) ;
kfree ( node ) ;
2018-11-09 17:21:36 +03:00
}
static void software_node_release ( struct kobject * kobj )
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode = kobj_to_swnode ( kobj ) ;
2018-11-09 17:21:36 +03:00
2020-02-28 03:00:01 +03:00
if ( swnode - > parent ) {
ida_simple_remove ( & swnode - > parent - > child_ids , swnode - > id ) ;
list_del ( & swnode - > entry ) ;
} else {
ida_simple_remove ( & swnode_root_ids , swnode - > id ) ;
}
2021-03-29 18:12:03 +03:00
if ( swnode - > allocated )
software_node_free ( swnode - > node ) ;
2018-11-09 17:21:36 +03:00
ida_destroy ( & swnode - > child_ids ) ;
kfree ( swnode ) ;
}
static struct kobj_type software_node_type = {
. release = software_node_release ,
. sysfs_ops = & kobj_sysfs_ops ,
} ;
2019-05-31 17:15:34 +03:00
static struct fwnode_handle *
swnode_register ( const struct software_node * node , struct swnode * parent ,
unsigned int allocated )
2018-11-09 17:21:36 +03:00
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode ;
2018-11-09 17:21:36 +03:00
int ret ;
swnode = kzalloc ( sizeof ( * swnode ) , GFP_KERNEL ) ;
2021-03-29 18:12:02 +03:00
if ( ! swnode )
return ERR_PTR ( - ENOMEM ) ;
2018-11-09 17:21:36 +03:00
2019-05-31 17:15:34 +03:00
ret = ida_simple_get ( parent ? & parent - > child_ids : & swnode_root_ids ,
0 , 0 , GFP_KERNEL ) ;
2018-11-09 17:21:36 +03:00
if ( ret < 0 ) {
kfree ( swnode ) ;
2021-03-29 18:12:02 +03:00
return ERR_PTR ( ret ) ;
2018-11-09 17:21:36 +03:00
}
swnode - > id = ret ;
2019-05-31 17:15:34 +03:00
swnode - > node = node ;
swnode - > parent = parent ;
2018-11-09 17:21:36 +03:00
swnode - > kobj . kset = swnode_kset ;
2020-11-21 05:02:22 +03:00
fwnode_init ( & swnode - > fwnode , & software_node_ops ) ;
2018-11-09 17:21:36 +03:00
ida_init ( & swnode - > child_ids ) ;
INIT_LIST_HEAD ( & swnode - > entry ) ;
INIT_LIST_HEAD ( & swnode - > children ) ;
2019-05-31 17:15:34 +03:00
if ( node - > name )
ret = kobject_init_and_add ( & swnode - > kobj , & software_node_type ,
parent ? & parent - > kobj : NULL ,
" %s " , node - > name ) ;
else
ret = kobject_init_and_add ( & swnode - > kobj , & software_node_type ,
parent ? & parent - > kobj : NULL ,
" node%d " , swnode - > id ) ;
2018-11-09 17:21:36 +03:00
if ( ret ) {
kobject_put ( & swnode - > kobj ) ;
return ERR_PTR ( ret ) ;
}
2021-03-29 18:12:02 +03:00
/*
* Assign the flag only in the successful case , so
* the above kobject_put ( ) won ' t mess up with properties .
*/
swnode - > allocated = allocated ;
2019-05-31 17:15:34 +03:00
if ( parent )
list_add_tail ( & swnode - > entry , & parent - > children ) ;
kobject_uevent ( & swnode - > kobj , KOBJ_ADD ) ;
return & swnode - > fwnode ;
}
/**
* software_node_register_nodes - Register an array of software nodes
* @ nodes : Zero terminated array of software nodes to be registered
*
2021-01-07 16:28:28 +03:00
* Register multiple software nodes at once . If any node in the array
* has its . parent pointer set ( which can only be to another software_node ) ,
* then its parent * * must * * have been registered before it is ; either outside
* of this function or by ordering the array such that parent comes before
* child .
2019-05-31 17:15:34 +03:00
*/
int software_node_register_nodes ( const struct software_node * nodes )
{
int ret ;
int i ;
for ( i = 0 ; nodes [ i ] . name ; i + + ) {
2021-01-07 16:28:28 +03:00
const struct software_node * parent = nodes [ i ] . parent ;
if ( parent & & ! software_node_to_swnode ( parent ) ) {
ret = - EINVAL ;
goto err_unregister_nodes ;
2019-05-31 17:15:34 +03:00
}
2021-01-07 16:28:28 +03:00
ret = software_node_register ( & nodes [ i ] ) ;
if ( ret )
goto err_unregister_nodes ;
2019-05-31 17:15:34 +03:00
}
return 0 ;
2021-01-07 16:28:28 +03:00
err_unregister_nodes :
software_node_unregister_nodes ( nodes ) ;
return ret ;
2019-05-31 17:15:34 +03:00
}
EXPORT_SYMBOL_GPL ( software_node_register_nodes ) ;
/**
* software_node_unregister_nodes - Unregister an array of software nodes
* @ nodes : Zero terminated array of software nodes to be unregistered
*
2021-01-07 16:28:28 +03:00
* Unregister multiple software nodes at once . If parent pointers are set up
* in any of the software nodes then the array * * must * * be ordered such that
* parents come before their children .
2020-05-24 18:30:40 +03:00
*
2021-01-07 16:28:28 +03:00
* NOTE : If you are uncertain whether the array is ordered such that
* parents will be unregistered before their children , it is wiser to
* remove the nodes individually , in the correct order ( child before
* parent ) .
2019-05-31 17:15:34 +03:00
*/
void software_node_unregister_nodes ( const struct software_node * nodes )
{
2021-01-07 16:28:28 +03:00
unsigned int i = 0 ;
while ( nodes [ i ] . name )
i + + ;
2019-05-31 17:15:34 +03:00
2021-01-07 16:28:28 +03:00
while ( i - - )
2020-05-24 18:30:40 +03:00
software_node_unregister ( & nodes [ i ] ) ;
2019-05-31 17:15:34 +03:00
}
EXPORT_SYMBOL_GPL ( software_node_unregister_nodes ) ;
2020-04-08 19:09:02 +03:00
/**
* software_node_register_node_group - Register a group of software nodes
* @ node_group : NULL terminated array of software node pointers to be registered
*
2021-03-08 13:36:44 +03:00
* Register multiple software nodes at once . If any node in the array
* has its . parent pointer set ( which can only be to another software_node ) ,
* then its parent * * must * * have been registered before it is ; either outside
* of this function or by ordering the array such that parent comes before
* child .
2020-04-08 19:09:02 +03:00
*/
int software_node_register_node_group ( const struct software_node * * node_group )
{
unsigned int i ;
int ret ;
if ( ! node_group )
return 0 ;
for ( i = 0 ; node_group [ i ] ; i + + ) {
ret = software_node_register ( node_group [ i ] ) ;
if ( ret ) {
software_node_unregister_node_group ( node_group ) ;
return ret ;
}
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( software_node_register_node_group ) ;
/**
* software_node_unregister_node_group - Unregister a group of software nodes
* @ node_group : NULL terminated array of software node pointers to be unregistered
*
2021-03-08 13:36:44 +03:00
* Unregister multiple software nodes at once . If parent pointers are set up
* in any of the software nodes then the array * * must * * be ordered such that
* parents come before their children .
*
* NOTE : If you are uncertain whether the array is ordered such that
* parents will be unregistered before their children , it is wiser to
* remove the nodes individually , in the correct order ( child before
* parent ) .
2020-04-08 19:09:02 +03:00
*/
2021-01-07 16:28:29 +03:00
void software_node_unregister_node_group (
const struct software_node * * node_group )
2020-04-08 19:09:02 +03:00
{
2021-01-07 16:28:29 +03:00
unsigned int i = 0 ;
2020-04-08 19:09:02 +03:00
if ( ! node_group )
return ;
2021-01-07 16:28:29 +03:00
while ( node_group [ i ] )
i + + ;
while ( i - - )
2020-06-22 11:21:08 +03:00
software_node_unregister ( node_group [ i ] ) ;
2020-04-08 19:09:02 +03:00
}
EXPORT_SYMBOL_GPL ( software_node_unregister_node_group ) ;
2019-05-31 17:15:34 +03:00
/**
* software_node_register - Register static software node
* @ node : The software node to be registered
*/
int software_node_register ( const struct software_node * node )
{
struct swnode * parent = software_node_to_swnode ( node - > parent ) ;
if ( software_node_to_swnode ( node ) )
return - EEXIST ;
2021-03-01 17:30:11 +03:00
if ( node - > parent & & ! parent )
return - EINVAL ;
2019-05-31 17:15:34 +03:00
return PTR_ERR_OR_ZERO ( swnode_register ( node , parent , 0 ) ) ;
}
EXPORT_SYMBOL_GPL ( software_node_register ) ;
2020-05-24 18:30:40 +03:00
/**
* software_node_unregister - Unregister static software node
* @ node : The software node to be unregistered
*/
void software_node_unregister ( const struct software_node * node )
{
struct swnode * swnode ;
swnode = software_node_to_swnode ( node ) ;
if ( swnode )
fwnode_remove_software_node ( & swnode - > fwnode ) ;
}
EXPORT_SYMBOL_GPL ( software_node_unregister ) ;
2019-05-31 17:15:34 +03:00
struct fwnode_handle *
fwnode_create_software_node ( const struct property_entry * properties ,
const struct fwnode_handle * parent )
{
2021-03-29 18:12:02 +03:00
struct fwnode_handle * fwnode ;
2019-05-31 17:15:34 +03:00
struct software_node * node ;
2021-03-29 18:12:04 +03:00
struct swnode * p ;
if ( IS_ERR ( parent ) )
return ERR_CAST ( parent ) ;
p = to_swnode ( parent ) ;
if ( parent & & ! p )
return ERR_PTR ( - EINVAL ) ;
2019-05-31 17:15:34 +03:00
2021-03-29 18:12:03 +03:00
node = software_node_alloc ( properties ) ;
if ( IS_ERR ( node ) )
return ERR_CAST ( node ) ;
2018-11-09 17:21:36 +03:00
2019-05-31 17:15:34 +03:00
node - > parent = p ? p - > node : NULL ;
2019-05-31 17:15:33 +03:00
2021-03-29 18:12:02 +03:00
fwnode = swnode_register ( node , p , 1 ) ;
2021-03-29 18:12:03 +03:00
if ( IS_ERR ( fwnode ) )
software_node_free ( node ) ;
2021-03-29 18:12:02 +03:00
return fwnode ;
2018-11-09 17:21:36 +03:00
}
EXPORT_SYMBOL_GPL ( fwnode_create_software_node ) ;
void fwnode_remove_software_node ( struct fwnode_handle * fwnode )
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode = to_swnode ( fwnode ) ;
2018-11-09 17:21:36 +03:00
if ( ! swnode )
return ;
kobject_put ( & swnode - > kobj ) ;
}
EXPORT_SYMBOL_GPL ( fwnode_remove_software_node ) ;
2021-01-15 12:49:11 +03:00
/**
* device_add_software_node - Assign software node to a device
* @ dev : The device the software node is meant for .
2021-03-01 17:30:12 +03:00
* @ node : The software node .
2021-01-15 12:49:11 +03:00
*
2021-03-01 17:30:12 +03:00
* This function will make @ node the secondary firmware node pointer of @ dev . If
* @ dev has no primary node , then @ node will become the primary node . The
* function will register @ node automatically if it wasn ' t already registered .
2021-01-15 12:49:11 +03:00
*/
2021-03-01 17:30:12 +03:00
int device_add_software_node ( struct device * dev , const struct software_node * node )
2021-01-15 12:49:11 +03:00
{
2021-03-01 17:30:12 +03:00
struct swnode * swnode ;
2021-01-15 12:49:11 +03:00
int ret ;
/* Only one software node per device. */
if ( dev_to_swnode ( dev ) )
return - EBUSY ;
2021-03-01 17:30:12 +03:00
swnode = software_node_to_swnode ( node ) ;
if ( swnode ) {
kobject_get ( & swnode - > kobj ) ;
} else {
ret = software_node_register ( node ) ;
if ( ret )
return ret ;
swnode = software_node_to_swnode ( node ) ;
}
2021-01-15 12:49:11 +03:00
2021-03-01 17:30:12 +03:00
set_secondary_fwnode ( dev , & swnode - > fwnode ) ;
2021-06-23 16:14:21 +03:00
/*
* If the device has been fully registered by the time this function is
* called , software_node_notify ( ) must be called separately so that the
* symlinks get created and the reference count of the node is kept in
* balance .
*/
if ( device_is_registered ( dev ) )
2021-07-12 20:27:12 +03:00
software_node_notify ( dev ) ;
2021-01-15 12:49:11 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( device_add_software_node ) ;
/**
* device_remove_software_node - Remove device ' s software node
* @ dev : The device with the software node .
*
* This function will unregister the software node of @ dev .
*/
void device_remove_software_node ( struct device * dev )
{
struct swnode * swnode ;
swnode = dev_to_swnode ( dev ) ;
if ( ! swnode )
return ;
2021-06-23 16:14:21 +03:00
if ( device_is_registered ( dev ) )
2021-07-12 20:27:12 +03:00
software_node_notify_remove ( dev ) ;
2021-01-15 12:49:11 +03:00
set_secondary_fwnode ( dev , NULL ) ;
kobject_put ( & swnode - > kobj ) ;
}
EXPORT_SYMBOL_GPL ( device_remove_software_node ) ;
2021-02-04 17:17:06 +03:00
/**
* device_create_managed_software_node - Create a software node for a device
* @ dev : The device the software node is assigned to .
* @ properties : Device properties for the software node .
* @ parent : Parent of the software node .
*
* Creates a software node as a managed resource for @ dev , which means the
* lifetime of the newly created software node is tied to the lifetime of @ dev .
* Software nodes created with this function should not be reused or shared
* because of that . The function takes a deep copy of @ properties for the
* software node .
*
* Since the new software node is assigned directly to @ dev , and since it should
* not be shared , it is not returned to the caller . The function returns 0 on
* success , and errno in case of an error .
*/
int device_create_managed_software_node ( struct device * dev ,
const struct property_entry * properties ,
const struct software_node * parent )
{
struct fwnode_handle * p = software_node_fwnode ( parent ) ;
struct fwnode_handle * fwnode ;
if ( parent & & ! p )
return - EINVAL ;
fwnode = fwnode_create_software_node ( properties , p ) ;
if ( IS_ERR ( fwnode ) )
return PTR_ERR ( fwnode ) ;
to_swnode ( fwnode ) - > managed = true ;
set_secondary_fwnode ( dev , fwnode ) ;
2021-09-15 11:09:39 +03:00
if ( device_is_registered ( dev ) )
software_node_notify ( dev ) ;
2021-02-04 17:17:06 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( device_create_managed_software_node ) ;
2021-07-12 20:27:12 +03:00
void software_node_notify ( struct device * dev )
2018-11-09 17:21:36 +03:00
{
2019-05-31 17:15:34 +03:00
struct swnode * swnode ;
2018-11-09 17:21:36 +03:00
int ret ;
2021-01-15 12:49:11 +03:00
swnode = dev_to_swnode ( dev ) ;
if ( ! swnode )
2021-07-12 20:27:12 +03:00
return ;
2018-11-09 17:21:36 +03:00
2021-07-12 20:27:12 +03:00
ret = sysfs_create_link ( & dev - > kobj , & swnode - > kobj , " software_node " ) ;
if ( ret )
return ;
2018-11-09 17:21:36 +03:00
2021-07-12 20:27:12 +03:00
ret = sysfs_create_link ( & swnode - > kobj , & dev - > kobj , dev_name ( dev ) ) ;
if ( ret ) {
2018-11-09 17:21:36 +03:00
sysfs_remove_link ( & dev - > kobj , " software_node " ) ;
2021-07-12 20:27:12 +03:00
return ;
2018-11-09 17:21:36 +03:00
}
2021-07-12 20:27:12 +03:00
kobject_get ( & swnode - > kobj ) ;
}
void software_node_notify_remove ( struct device * dev )
{
struct swnode * swnode ;
swnode = dev_to_swnode ( dev ) ;
if ( ! swnode )
return ;
sysfs_remove_link ( & swnode - > kobj , dev_name ( dev ) ) ;
sysfs_remove_link ( & dev - > kobj , " software_node " ) ;
kobject_put ( & swnode - > kobj ) ;
if ( swnode - > managed ) {
set_secondary_fwnode ( dev , NULL ) ;
kobject_put ( & swnode - > kobj ) ;
}
2018-11-09 17:21:36 +03:00
}
static int __init software_node_init ( void )
{
swnode_kset = kset_create_and_add ( " software_nodes " , NULL , kernel_kobj ) ;
if ( ! swnode_kset )
return - ENOMEM ;
return 0 ;
}
postcore_initcall ( software_node_init ) ;
static void __exit software_node_exit ( void )
{
ida_destroy ( & swnode_root_ids ) ;
kset_unregister ( swnode_kset ) ;
}
__exitcall ( software_node_exit ) ;