2017-12-14 11:19:34 +05:30
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
// Copyright(c) 2015-17 Intel Corporation.
# include <linux/acpi.h>
2019-08-29 17:35:12 +01:00
# include <linux/of.h>
2017-12-14 11:19:34 +05:30
# include <linux/soundwire/sdw.h>
# include <linux/soundwire/sdw_type.h>
# include "bus.h"
2020-09-24 14:44:30 -05:00
# include "sysfs_local.h"
2017-12-14 11:19:34 +05:30
static void sdw_slave_release ( struct device * dev )
{
struct sdw_slave * slave = dev_to_sdw_dev ( dev ) ;
kfree ( slave ) ;
}
2020-05-19 01:43:19 +08:00
struct device_type sdw_slave_type = {
. name = " sdw_slave " ,
. release = sdw_slave_release ,
. uevent = sdw_slave_uevent ,
} ;
2020-09-24 14:44:29 -05:00
int sdw_slave_add ( struct sdw_bus * bus ,
struct sdw_slave_id * id , struct fwnode_handle * fwnode )
2017-12-14 11:19:34 +05:30
{
struct sdw_slave * slave ;
int ret ;
2020-08-31 21:43:18 +08:00
int i ;
2017-12-14 11:19:34 +05:30
slave = kzalloc ( sizeof ( * slave ) , GFP_KERNEL ) ;
if ( ! slave )
return - ENOMEM ;
/* Initialize data structure */
memcpy ( & slave - > id , id , sizeof ( * id ) ) ;
slave - > dev . parent = bus - > dev ;
slave - > dev . fwnode = fwnode ;
2019-10-22 18:48:08 -05:00
if ( id - > unique_id = = SDW_IGNORED_UNIQUE_ID ) {
/* name shall be sdw:link:mfg:part:class */
2021-05-11 14:01:37 +08:00
dev_set_name ( & slave - > dev , " sdw:%01x:%04x:%04x:%02x " ,
2019-10-22 18:48:08 -05:00
bus - > link_id , id - > mfg_id , id - > part_id ,
id - > class_id ) ;
} else {
/* name shall be sdw:link:mfg:part:class:unique */
2021-05-11 14:01:37 +08:00
dev_set_name ( & slave - > dev , " sdw:%01x:%04x:%04x:%02x:%01x " ,
2019-10-22 18:48:08 -05:00
bus - > link_id , id - > mfg_id , id - > part_id ,
id - > class_id , id - > unique_id ) ;
}
2017-12-14 11:19:34 +05:30
slave - > dev . bus = & sdw_bus_type ;
2019-08-29 17:35:12 +01:00
slave - > dev . of_node = of_node_get ( to_of_node ( fwnode ) ) ;
2020-05-19 01:43:19 +08:00
slave - > dev . type = & sdw_slave_type ;
2020-09-24 14:44:30 -05:00
slave - > dev . groups = sdw_slave_status_attr_groups ;
2017-12-14 11:19:34 +05:30
slave - > bus = bus ;
slave - > status = SDW_SLAVE_UNATTACHED ;
2020-01-14 18:08:36 -06:00
init_completion ( & slave - > enumeration_complete ) ;
2020-01-14 18:08:37 -06:00
init_completion ( & slave - > initialization_complete ) ;
2017-12-14 11:19:34 +05:30
slave - > dev_num = 0 ;
2020-01-14 18:08:35 -06:00
init_completion ( & slave - > probe_complete ) ;
slave - > probed = false ;
2020-09-08 21:45:17 +08:00
slave - > first_interrupt_done = false ;
2017-12-14 11:19:34 +05:30
2020-08-31 21:43:18 +08:00
for ( i = 0 ; i < SDW_MAX_PORTS ; i + + )
init_completion ( & slave - > port_ready [ i ] ) ;
2017-12-14 11:19:34 +05:30
mutex_lock ( & bus - > bus_lock ) ;
list_add_tail ( & slave - > node , & bus - > slaves ) ;
mutex_unlock ( & bus - > bus_lock ) ;
ret = device_register ( & slave - > dev ) ;
if ( ret ) {
dev_err ( bus - > dev , " Failed to add slave: ret %d \n " , ret ) ;
/*
* On err , don ' t free but drop ref as this will be freed
* when release method is invoked .
*/
mutex_lock ( & bus - > bus_lock ) ;
list_del ( & slave - > node ) ;
mutex_unlock ( & bus - > bus_lock ) ;
put_device ( & slave - > dev ) ;
2020-04-20 02:51:15 +08:00
return ret ;
2017-12-14 11:19:34 +05:30
}
2019-08-21 13:58:18 -05:00
sdw_slave_debugfs_init ( slave ) ;
2017-12-14 11:19:34 +05:30
return ret ;
}
2021-03-30 15:47:17 +01:00
EXPORT_SYMBOL ( sdw_slave_add ) ;
2017-12-14 11:19:34 +05:30
# if IS_ENABLED(CONFIG_ACPI)
2019-10-22 18:48:07 -05:00
static bool find_slave ( struct sdw_bus * bus ,
struct acpi_device * adev ,
struct sdw_slave_id * id )
{
2021-03-02 15:51:03 +08:00
u64 addr ;
2019-10-22 18:48:07 -05:00
unsigned int link_id ;
acpi_status status ;
status = acpi_evaluate_integer ( adev - > handle ,
METHOD_NAME__ADR , NULL , & addr ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_err ( bus - > dev , " _ADR resolution failed: %x \n " ,
status ) ;
return false ;
}
2021-03-02 15:51:03 +08:00
if ( bus - > ops - > override_adr )
addr = bus - > ops - > override_adr ( bus , addr ) ;
if ( ! addr )
return false ;
2019-10-22 18:48:07 -05:00
/* Extract link id from ADR, Bit 51 to 48 (included) */
2020-09-03 17:14:58 +05:30
link_id = SDW_DISCO_LINK_ID ( addr ) ;
2019-10-22 18:48:07 -05:00
/* Check for link_id match */
if ( link_id ! = bus - > link_id )
return false ;
sdw_extract_slave_id ( bus , addr , id ) ;
return true ;
}
2017-12-14 11:19:34 +05:30
/*
* sdw_acpi_find_slaves ( ) - Find Slave devices in Master ACPI node
* @ bus : SDW bus instance
*
* Scans Master ACPI node for SDW child Slave devices and registers it .
*/
int sdw_acpi_find_slaves ( struct sdw_bus * bus )
{
struct acpi_device * adev , * parent ;
2019-10-22 18:48:08 -05:00
struct acpi_device * adev2 , * parent2 ;
2017-12-14 11:19:34 +05:30
parent = ACPI_COMPANION ( bus - > dev ) ;
if ( ! parent ) {
dev_err ( bus - > dev , " Can't find parent for acpi bind \n " ) ;
return - ENODEV ;
}
list_for_each_entry ( adev , & parent - > children , node ) {
struct sdw_slave_id id ;
2019-10-22 18:48:08 -05:00
struct sdw_slave_id id2 ;
bool ignore_unique_id = true ;
2017-12-14 11:19:34 +05:30
2019-10-22 18:48:07 -05:00
if ( ! find_slave ( bus , adev , & id ) )
2017-12-14 11:19:34 +05:30
continue ;
2019-10-22 18:48:08 -05:00
/* brute-force O(N^2) search for duplicates */
parent2 = parent ;
list_for_each_entry ( adev2 , & parent2 - > children , node ) {
if ( adev = = adev2 )
continue ;
if ( ! find_slave ( bus , adev2 , & id2 ) )
continue ;
if ( id . sdw_version ! = id2 . sdw_version | |
id . mfg_id ! = id2 . mfg_id | |
id . part_id ! = id2 . part_id | |
id . class_id ! = id2 . class_id )
continue ;
if ( id . unique_id ! = id2 . unique_id ) {
dev_dbg ( bus - > dev ,
2021-01-15 13:37:34 +08:00
" Valid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x \n " ,
id . unique_id , id2 . unique_id , id . mfg_id , id . part_id ) ;
2019-10-22 18:48:08 -05:00
ignore_unique_id = false ;
} else {
dev_err ( bus - > dev ,
2021-01-15 13:37:34 +08:00
" Invalid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x \n " ,
id . unique_id , id2 . unique_id , id . mfg_id , id . part_id ) ;
2019-10-22 18:48:08 -05:00
return - ENODEV ;
}
}
if ( ignore_unique_id )
id . unique_id = SDW_IGNORED_UNIQUE_ID ;
2017-12-14 11:19:34 +05:30
/*
* don ' t error check for sdw_slave_add as we want to continue
* adding Slaves
*/
sdw_slave_add ( bus , & id , acpi_fwnode_handle ( adev ) ) ;
}
return 0 ;
}
# endif
2019-08-29 17:35:12 +01:00
/*
* sdw_of_find_slaves ( ) - Find Slave devices in master device tree node
* @ bus : SDW bus instance
*
* Scans Master DT node for SDW child Slave devices and registers it .
*/
int sdw_of_find_slaves ( struct sdw_bus * bus )
{
struct device * dev = bus - > dev ;
struct device_node * node ;
for_each_child_of_node ( bus - > dev - > of_node , node ) {
2019-10-22 18:31:47 -05:00
int link_id , ret , len ;
unsigned int sdw_version ;
2019-08-29 17:35:12 +01:00
const char * compat = NULL ;
struct sdw_slave_id id ;
const __be32 * addr ;
compat = of_get_property ( node , " compatible " , NULL ) ;
if ( ! compat )
continue ;
ret = sscanf ( compat , " sdw%01x%04hx%04hx%02hhx " , & sdw_version ,
& id . mfg_id , & id . part_id , & id . class_id ) ;
if ( ret ! = 4 ) {
dev_err ( dev , " Invalid compatible string found %s \n " ,
compat ) ;
continue ;
}
addr = of_get_property ( node , " reg " , & len ) ;
if ( ! addr | | ( len < 2 * sizeof ( u32 ) ) ) {
dev_err ( dev , " Invalid Link and Instance ID \n " ) ;
continue ;
}
link_id = be32_to_cpup ( addr + + ) ;
id . unique_id = be32_to_cpup ( addr ) ;
id . sdw_version = sdw_version ;
/* Check for link_id match */
if ( link_id ! = bus - > link_id )
continue ;
sdw_slave_add ( bus , & id , of_fwnode_handle ( node ) ) ;
}
return 0 ;
}