2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-05-23 11:50:58 +02:00
/*
* Linux I2C core OF support code
*
* Copyright ( C ) 2008 Jochen Friedrich < jochen @ scram . de >
* based on a previous patch from Jon Smirl < jonsmirl @ gmail . com >
*
2020-05-02 14:18:35 +02:00
* Copyright ( C ) 2013 , 2018 Wolfram Sang < wsa @ kernel . org >
2017-05-23 11:50:58 +02:00
*/
# include <dt-bindings/i2c/i2c.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
2019-06-19 19:02:45 +02:00
# include <linux/sysfs.h>
2017-05-23 11:50:58 +02:00
# include "i2c-core.h"
2018-03-25 14:49:03 +02:00
int of_i2c_get_board_info ( struct device * dev , struct device_node * node ,
struct i2c_board_info * info )
2017-05-23 11:50:58 +02:00
{
u32 addr ;
2018-01-18 13:11:33 +01:00
int ret ;
2017-05-23 11:50:58 +02:00
2018-03-25 14:49:03 +02:00
memset ( info , 0 , sizeof ( * info ) ) ;
2017-05-23 11:50:58 +02:00
2023-04-04 18:21:16 +01:00
if ( of_alias_from_compatible ( node , info - > type , sizeof ( info - > type ) ) < 0 ) {
2018-03-25 14:49:03 +02:00
dev_err ( dev , " of_i2c: modalias failure on %pOF \n " , node ) ;
return - EINVAL ;
2017-05-23 11:50:58 +02:00
}
2018-01-18 13:11:33 +01:00
ret = of_property_read_u32 ( node , " reg " , & addr ) ;
if ( ret ) {
2018-03-25 14:49:03 +02:00
dev_err ( dev , " of_i2c: invalid reg on %pOF \n " , node ) ;
return ret ;
2017-05-23 11:50:58 +02:00
}
if ( addr & I2C_TEN_BIT_ADDRESS ) {
addr & = ~ I2C_TEN_BIT_ADDRESS ;
2018-03-25 14:49:03 +02:00
info - > flags | = I2C_CLIENT_TEN ;
2017-05-23 11:50:58 +02:00
}
if ( addr & I2C_OWN_SLAVE_ADDRESS ) {
addr & = ~ I2C_OWN_SLAVE_ADDRESS ;
2018-03-25 14:49:03 +02:00
info - > flags | = I2C_CLIENT_SLAVE ;
2017-05-23 11:50:58 +02:00
}
2018-03-25 14:49:03 +02:00
info - > addr = addr ;
info - > of_node = node ;
2019-11-14 20:50:48 -08:00
info - > fwnode = of_fwnode_handle ( node ) ;
2017-05-23 11:50:58 +02:00
if ( of_property_read_bool ( node , " host-notify " ) )
2018-03-25 14:49:03 +02:00
info - > flags | = I2C_CLIENT_HOST_NOTIFY ;
2017-05-23 11:50:58 +02:00
2023-03-10 08:47:07 -06:00
if ( of_property_read_bool ( node , " wakeup-source " ) )
2018-03-25 14:49:03 +02:00
info - > flags | = I2C_CLIENT_WAKE ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( of_i2c_get_board_info ) ;
static struct i2c_client * of_i2c_register_device ( struct i2c_adapter * adap ,
struct device_node * node )
{
struct i2c_client * client ;
struct i2c_board_info info ;
int ret ;
dev_dbg ( & adap - > dev , " of_i2c: register %pOF \n " , node ) ;
ret = of_i2c_get_board_info ( & adap - > dev , node , & info ) ;
if ( ret )
return ERR_PTR ( ret ) ;
2017-05-23 11:50:58 +02:00
2020-01-07 18:47:44 +01:00
client = i2c_new_client_device ( adap , & info ) ;
if ( IS_ERR ( client ) )
2017-07-18 16:43:06 -05:00
dev_err ( & adap - > dev , " of_i2c: Failure registering %pOF \n " , node ) ;
2020-01-07 18:47:44 +01:00
2018-01-18 13:11:31 +01:00
return client ;
2017-05-23 11:50:58 +02:00
}
void of_i2c_register_devices ( struct i2c_adapter * adap )
{
struct device_node * bus , * node ;
struct i2c_client * client ;
/* Only register child devices if the adapter has a node pointer set */
if ( ! adap - > dev . of_node )
return ;
dev_dbg ( & adap - > dev , " of_i2c: walking child nodes \n " ) ;
bus = of_get_child_by_name ( adap - > dev . of_node , " i2c-bus " ) ;
if ( ! bus )
bus = of_node_get ( adap - > dev . of_node ) ;
for_each_available_child_of_node ( bus , node ) {
if ( of_node_test_and_set_flag ( node , OF_POPULATED ) )
continue ;
client = of_i2c_register_device ( adap , node ) ;
if ( IS_ERR ( client ) ) {
2018-01-18 13:11:29 +01:00
dev_err ( & adap - > dev ,
2017-07-18 16:43:06 -05:00
" Failed to create I2C device for %pOF \n " ,
node ) ;
2017-05-23 11:50:58 +02:00
of_node_clear_flag ( node , OF_POPULATED ) ;
}
}
of_node_put ( bus ) ;
}
static const struct of_device_id *
i2c_of_match_device_sysfs ( const struct of_device_id * matches ,
struct i2c_client * client )
{
const char * name ;
for ( ; matches - > compatible [ 0 ] ; matches + + ) {
/*
* Adding devices through the i2c sysfs interface provides us
* a string to match which may be compatible with the device
* tree compatible strings , however with no actual of_node the
* of_match_device ( ) will not match
*/
if ( sysfs_streq ( client - > name , matches - > compatible ) )
return matches ;
name = strchr ( matches - > compatible , ' , ' ) ;
if ( ! name )
name = matches - > compatible ;
else
name + + ;
if ( sysfs_streq ( client - > name , name ) )
return matches ;
}
return NULL ;
}
const struct of_device_id
* i2c_of_match_device ( const struct of_device_id * matches ,
struct i2c_client * client )
{
const struct of_device_id * match ;
if ( ! ( client & & matches ) )
return NULL ;
match = of_match_device ( matches , & client - > dev ) ;
if ( match )
return match ;
return i2c_of_match_device_sysfs ( matches , client ) ;
}
EXPORT_SYMBOL_GPL ( i2c_of_match_device ) ;
# if IS_ENABLED(CONFIG_OF_DYNAMIC)
static int of_i2c_notify ( struct notifier_block * nb , unsigned long action ,
void * arg )
{
struct of_reconfig_data * rd = arg ;
struct i2c_adapter * adap ;
struct i2c_client * client ;
switch ( of_reconfig_get_state_change ( action , rd ) ) {
case OF_RECONFIG_CHANGE_ADD :
adap = of_find_i2c_adapter_by_node ( rd - > dn - > parent ) ;
if ( adap = = NULL )
return NOTIFY_OK ; /* not for us */
if ( of_node_test_and_set_flag ( rd - > dn , OF_POPULATED ) ) {
put_device ( & adap - > dev ) ;
return NOTIFY_OK ;
}
2023-03-30 15:26:13 +02:00
/*
* Clear the flag before adding the device so that fw_devlink
* doesn ' t skip adding consumers to this device .
*/
rd - > dn - > fwnode . flags & = ~ FWNODE_FLAG_NOT_DEVICE ;
2017-05-23 11:50:58 +02:00
client = of_i2c_register_device ( adap , rd - > dn ) ;
if ( IS_ERR ( client ) ) {
2017-07-18 16:43:06 -05:00
dev_err ( & adap - > dev , " failed to create client for '%pOF' \n " ,
rd - > dn ) ;
2019-11-08 16:36:48 +08:00
put_device ( & adap - > dev ) ;
2017-05-23 11:50:58 +02:00
of_node_clear_flag ( rd - > dn , OF_POPULATED ) ;
return notifier_from_errno ( PTR_ERR ( client ) ) ;
}
2019-11-08 16:36:48 +08:00
put_device ( & adap - > dev ) ;
2017-05-23 11:50:58 +02:00
break ;
case OF_RECONFIG_CHANGE_REMOVE :
/* already depopulated? */
if ( ! of_node_check_flag ( rd - > dn , OF_POPULATED ) )
return NOTIFY_OK ;
/* find our device by node */
client = of_find_i2c_device_by_node ( rd - > dn ) ;
if ( client = = NULL )
return NOTIFY_OK ; /* no? not meant for us */
/* unregister takes one ref away */
i2c_unregister_device ( client ) ;
/* and put the reference of the find */
put_device ( & client - > dev ) ;
break ;
}
return NOTIFY_OK ;
}
struct notifier_block i2c_of_notifier = {
. notifier_call = of_i2c_notify ,
} ;
# endif /* CONFIG_OF_DYNAMIC */