2020-05-18 21:02:22 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2019 , Linaro Limited , All rights reserved .
* Author : Mike Leach < mike . leach @ linaro . org >
*/
# include <linux/device.h>
# include <linux/kernel.h>
# include "coresight-priv.h"
/*
* Connections group - links attribute .
* Count of created links between coresight components in the group .
*/
static ssize_t nr_links_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct coresight_device * csdev = to_coresight_device ( dev ) ;
return sprintf ( buf , " %d \n " , csdev - > nr_links ) ;
}
static DEVICE_ATTR_RO ( nr_links ) ;
static struct attribute * coresight_conns_attrs [ ] = {
& dev_attr_nr_links . attr ,
NULL ,
} ;
static struct attribute_group coresight_conns_group = {
. attrs = coresight_conns_attrs ,
. name = " connections " ,
} ;
/*
* Create connections group for CoreSight devices .
* This group will then be used to collate the sysfs links between
* devices .
*/
int coresight_create_conns_sysfs_group ( struct coresight_device * csdev )
{
int ret = 0 ;
if ( ! csdev )
return - EINVAL ;
ret = sysfs_create_group ( & csdev - > dev . kobj , & coresight_conns_group ) ;
if ( ret )
return ret ;
csdev - > has_conns_grp = true ;
return ret ;
}
void coresight_remove_conns_sysfs_group ( struct coresight_device * csdev )
{
if ( ! csdev )
return ;
if ( csdev - > has_conns_grp ) {
sysfs_remove_group ( & csdev - > dev . kobj , & coresight_conns_group ) ;
csdev - > has_conns_grp = false ;
}
}
int coresight_add_sysfs_link ( struct coresight_sysfs_link * info )
{
int ret = 0 ;
if ( ! info )
return - EINVAL ;
if ( ! info - > orig | | ! info - > target | |
! info - > orig_name | | ! info - > target_name )
return - EINVAL ;
if ( ! info - > orig - > has_conns_grp | | ! info - > target - > has_conns_grp )
return - EINVAL ;
/* first link orig->target */
ret = sysfs_add_link_to_group ( & info - > orig - > dev . kobj ,
coresight_conns_group . name ,
& info - > target - > dev . kobj ,
info - > orig_name ) ;
if ( ret )
return ret ;
/* second link target->orig */
ret = sysfs_add_link_to_group ( & info - > target - > dev . kobj ,
coresight_conns_group . name ,
& info - > orig - > dev . kobj ,
info - > target_name ) ;
/* error in second link - remove first - otherwise inc counts */
if ( ret ) {
sysfs_remove_link_from_group ( & info - > orig - > dev . kobj ,
coresight_conns_group . name ,
info - > orig_name ) ;
} else {
info - > orig - > nr_links + + ;
info - > target - > nr_links + + ;
}
return ret ;
}
2020-09-28 19:34:53 +03:00
EXPORT_SYMBOL_GPL ( coresight_add_sysfs_link ) ;
2020-05-18 21:02:22 +03:00
void coresight_remove_sysfs_link ( struct coresight_sysfs_link * info )
{
if ( ! info )
return ;
if ( ! info - > orig | | ! info - > target | |
! info - > orig_name | | ! info - > target_name )
return ;
sysfs_remove_link_from_group ( & info - > orig - > dev . kobj ,
coresight_conns_group . name ,
info - > orig_name ) ;
sysfs_remove_link_from_group ( & info - > target - > dev . kobj ,
coresight_conns_group . name ,
info - > target_name ) ;
info - > orig - > nr_links - - ;
info - > target - > nr_links - - ;
}
2020-09-28 19:34:53 +03:00
EXPORT_SYMBOL_GPL ( coresight_remove_sysfs_link ) ;
2020-05-18 21:02:23 +03:00
/*
* coresight_make_links : Make a link for a connection from a @ orig
* device to @ target , represented by @ conn .
*
* e . g , for devOrig [ output_X ] - > devTarget [ input_Y ] is represented
* as two symbolic links :
*
* / sys / . . . / devOrig / out : X - > / sys / . . . / devTarget /
* / sys / . . . / devTarget / in : Y - > / sys / . . . / devOrig /
*
* The link names are allocated for a device where it appears . i . e , the
* " out " link on the master and " in " link on the slave device .
* The link info is stored in the connection record for avoiding
* the reconstruction of names for removal .
*/
int coresight_make_links ( struct coresight_device * orig ,
struct coresight_connection * conn ,
struct coresight_device * target )
{
int ret = - ENOMEM ;
char * outs = NULL , * ins = NULL ;
struct coresight_sysfs_link * link = NULL ;
do {
outs = devm_kasprintf ( & orig - > dev , GFP_KERNEL ,
" out:%d " , conn - > outport ) ;
if ( ! outs )
break ;
ins = devm_kasprintf ( & target - > dev , GFP_KERNEL ,
" in:%d " , conn - > child_port ) ;
if ( ! ins )
break ;
link = devm_kzalloc ( & orig - > dev ,
sizeof ( struct coresight_sysfs_link ) ,
GFP_KERNEL ) ;
if ( ! link )
break ;
link - > orig = orig ;
link - > target = target ;
link - > orig_name = outs ;
link - > target_name = ins ;
ret = coresight_add_sysfs_link ( link ) ;
if ( ret )
break ;
conn - > link = link ;
/*
* Install the device connection . This also indicates that
* the links are operational on both ends .
*/
conn - > child_dev = target ;
return 0 ;
} while ( 0 ) ;
return ret ;
}
/*
* coresight_remove_links : Remove the sysfs links for a given connection @ conn ,
* from @ orig device to @ target device . See coresight_make_links ( ) for more
* details .
*/
void coresight_remove_links ( struct coresight_device * orig ,
struct coresight_connection * conn )
{
if ( ! orig | | ! conn - > link )
return ;
coresight_remove_sysfs_link ( conn - > link ) ;
devm_kfree ( & conn - > child_dev - > dev , conn - > link - > target_name ) ;
devm_kfree ( & orig - > dev , conn - > link - > orig_name ) ;
devm_kfree ( & orig - > dev , conn - > link ) ;
conn - > link = NULL ;
conn - > child_dev = NULL ;
}