2022-04-22 23:52:13 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2021 - 2022 NVIDIA Corporation
*
* Author : Dipen Patel < dipenp @ nvidia . com >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/of.h>
# include <linux/mutex.h>
# include <linux/uaccess.h>
# include <linux/hte.h>
# include <linux/delay.h>
# include <linux/debugfs.h>
2023-07-14 20:46:00 +03:00
# include <linux/device.h>
2022-04-22 23:52:13 +03:00
/* Global list of the HTE devices */
static DEFINE_SPINLOCK ( hte_lock ) ;
static LIST_HEAD ( hte_devices ) ;
enum {
HTE_TS_REGISTERED ,
HTE_TS_REQ ,
HTE_TS_DISABLE ,
HTE_TS_QUEUE_WK ,
} ;
/**
* struct hte_ts_info - Information related to requested timestamp .
*
* @ xlated_id : Timestamp ID as understood between HTE subsys and HTE provider ,
* See xlate callback API .
* @ flags : Flags holding state information .
* @ hte_cb_flags : Callback related flags .
* @ seq : Timestamp sequence counter .
* @ line_name : HTE allocated line name .
* @ free_attr_name : If set , free the attr name .
* @ cb : A nonsleeping callback function provided by clients .
* @ tcb : A secondary sleeping callback function provided by clients .
* @ dropped_ts : Dropped timestamps .
* @ slock : Spin lock to synchronize between disable / enable ,
* request / release APIs .
* @ cb_work : callback workqueue , used when tcb is specified .
* @ req_mlock : Lock during timestamp request / release APIs .
* @ ts_dbg_root : Root for the debug fs .
* @ gdev : HTE abstract device that this timestamp information belongs to .
* @ cl_data : Client specific data .
*/
struct hte_ts_info {
u32 xlated_id ;
unsigned long flags ;
unsigned long hte_cb_flags ;
u64 seq ;
char * line_name ;
bool free_attr_name ;
hte_ts_cb_t cb ;
hte_ts_sec_cb_t tcb ;
atomic_t dropped_ts ;
spinlock_t slock ;
struct work_struct cb_work ;
struct mutex req_mlock ;
struct dentry * ts_dbg_root ;
struct hte_device * gdev ;
void * cl_data ;
} ;
/**
* struct hte_device - HTE abstract device
* @ nlines : Number of entities this device supports .
* @ ts_req : Total number of entities requested .
* @ sdev : Device used at various debug prints .
* @ dbg_root : Root directory for debug fs .
* @ list : List node to store hte_device for each provider .
* @ chip : HTE chip providing this HTE device .
* @ owner : helps prevent removal of modules when in use .
* @ ei : Timestamp information .
*/
struct hte_device {
u32 nlines ;
atomic_t ts_req ;
struct device * sdev ;
struct dentry * dbg_root ;
struct list_head list ;
struct hte_chip * chip ;
struct module * owner ;
2023-09-22 20:53:45 +03:00
struct hte_ts_info ei [ ] __counted_by ( nlines ) ;
2022-04-22 23:52:13 +03:00
} ;
# ifdef CONFIG_DEBUG_FS
static struct dentry * hte_root ;
static int __init hte_subsys_dbgfs_init ( void )
{
/* creates /sys/kernel/debug/hte/ */
hte_root = debugfs_create_dir ( " hte " , NULL ) ;
return 0 ;
}
subsys_initcall ( hte_subsys_dbgfs_init ) ;
static void hte_chip_dbgfs_init ( struct hte_device * gdev )
{
const struct hte_chip * chip = gdev - > chip ;
const char * name = chip - > name ? chip - > name : dev_name ( chip - > dev ) ;
gdev - > dbg_root = debugfs_create_dir ( name , hte_root ) ;
debugfs_create_atomic_t ( " ts_requested " , 0444 , gdev - > dbg_root ,
& gdev - > ts_req ) ;
debugfs_create_u32 ( " total_ts " , 0444 , gdev - > dbg_root ,
& gdev - > nlines ) ;
}
static void hte_ts_dbgfs_init ( const char * name , struct hte_ts_info * ei )
{
if ( ! ei - > gdev - > dbg_root | | ! name )
return ;
ei - > ts_dbg_root = debugfs_create_dir ( name , ei - > gdev - > dbg_root ) ;
debugfs_create_atomic_t ( " dropped_timestamps " , 0444 , ei - > ts_dbg_root ,
& ei - > dropped_ts ) ;
}
# else
static void hte_chip_dbgfs_init ( struct hte_device * gdev )
{
}
static void hte_ts_dbgfs_init ( const char * name , struct hte_ts_info * ei )
{
}
# endif
/**
* hte_ts_put ( ) - Release and disable timestamp for the given desc .
*
* @ desc : timestamp descriptor .
*
* Context : debugfs_remove_recursive ( ) function call may use sleeping locks ,
* not suitable from atomic context .
* Returns : 0 on success or a negative error code on failure .
*/
int hte_ts_put ( struct hte_ts_desc * desc )
{
int ret = 0 ;
unsigned long flag ;
struct hte_device * gdev ;
struct hte_ts_info * ei ;
if ( ! desc )
return - EINVAL ;
ei = desc - > hte_data ;
if ( ! ei | | ! ei - > gdev )
return - EINVAL ;
gdev = ei - > gdev ;
mutex_lock ( & ei - > req_mlock ) ;
if ( unlikely ( ! test_bit ( HTE_TS_REQ , & ei - > flags ) & &
! test_bit ( HTE_TS_REGISTERED , & ei - > flags ) ) ) {
dev_info ( gdev - > sdev , " id:%d is not requested \n " ,
desc - > attr . line_id ) ;
ret = - EINVAL ;
goto unlock ;
}
if ( unlikely ( ! test_bit ( HTE_TS_REQ , & ei - > flags ) & &
test_bit ( HTE_TS_REGISTERED , & ei - > flags ) ) ) {
dev_info ( gdev - > sdev , " id:%d is registered but not requested \n " ,
desc - > attr . line_id ) ;
ret = - EINVAL ;
goto unlock ;
}
if ( test_bit ( HTE_TS_REQ , & ei - > flags ) & &
! test_bit ( HTE_TS_REGISTERED , & ei - > flags ) ) {
clear_bit ( HTE_TS_REQ , & ei - > flags ) ;
desc - > hte_data = NULL ;
ret = 0 ;
goto mod_put ;
}
ret = gdev - > chip - > ops - > release ( gdev - > chip , desc , ei - > xlated_id ) ;
if ( ret ) {
dev_err ( gdev - > sdev , " id: %d free failed \n " ,
desc - > attr . line_id ) ;
goto unlock ;
}
kfree ( ei - > line_name ) ;
if ( ei - > free_attr_name )
kfree_const ( desc - > attr . name ) ;
debugfs_remove_recursive ( ei - > ts_dbg_root ) ;
spin_lock_irqsave ( & ei - > slock , flag ) ;
if ( test_bit ( HTE_TS_QUEUE_WK , & ei - > flags ) ) {
spin_unlock_irqrestore ( & ei - > slock , flag ) ;
flush_work ( & ei - > cb_work ) ;
spin_lock_irqsave ( & ei - > slock , flag ) ;
}
atomic_dec ( & gdev - > ts_req ) ;
atomic_set ( & ei - > dropped_ts , 0 ) ;
ei - > seq = 1 ;
ei - > flags = 0 ;
desc - > hte_data = NULL ;
spin_unlock_irqrestore ( & ei - > slock , flag ) ;
ei - > cb = NULL ;
ei - > tcb = NULL ;
ei - > cl_data = NULL ;
mod_put :
module_put ( gdev - > owner ) ;
unlock :
mutex_unlock ( & ei - > req_mlock ) ;
dev_dbg ( gdev - > sdev , " release id: %d \n " , desc - > attr . line_id ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( hte_ts_put ) ;
static int hte_ts_dis_en_common ( struct hte_ts_desc * desc , bool en )
{
u32 ts_id ;
struct hte_device * gdev ;
struct hte_ts_info * ei ;
int ret ;
unsigned long flag ;
if ( ! desc )
return - EINVAL ;
ei = desc - > hte_data ;
if ( ! ei | | ! ei - > gdev )
return - EINVAL ;
gdev = ei - > gdev ;
ts_id = desc - > attr . line_id ;
mutex_lock ( & ei - > req_mlock ) ;
if ( ! test_bit ( HTE_TS_REGISTERED , & ei - > flags ) ) {
dev_dbg ( gdev - > sdev , " id:%d is not registered " , ts_id ) ;
ret = - EUSERS ;
goto out ;
}
spin_lock_irqsave ( & ei - > slock , flag ) ;
if ( en ) {
if ( ! test_bit ( HTE_TS_DISABLE , & ei - > flags ) ) {
ret = 0 ;
goto out_unlock ;
}
spin_unlock_irqrestore ( & ei - > slock , flag ) ;
ret = gdev - > chip - > ops - > enable ( gdev - > chip , ei - > xlated_id ) ;
if ( ret ) {
dev_warn ( gdev - > sdev , " id: %d enable failed \n " ,
ts_id ) ;
goto out ;
}
spin_lock_irqsave ( & ei - > slock , flag ) ;
clear_bit ( HTE_TS_DISABLE , & ei - > flags ) ;
} else {
if ( test_bit ( HTE_TS_DISABLE , & ei - > flags ) ) {
ret = 0 ;
goto out_unlock ;
}
spin_unlock_irqrestore ( & ei - > slock , flag ) ;
ret = gdev - > chip - > ops - > disable ( gdev - > chip , ei - > xlated_id ) ;
if ( ret ) {
dev_warn ( gdev - > sdev , " id: %d disable failed \n " ,
ts_id ) ;
goto out ;
}
spin_lock_irqsave ( & ei - > slock , flag ) ;
set_bit ( HTE_TS_DISABLE , & ei - > flags ) ;
}
out_unlock :
spin_unlock_irqrestore ( & ei - > slock , flag ) ;
out :
mutex_unlock ( & ei - > req_mlock ) ;
return ret ;
}
/**
* hte_disable_ts ( ) - Disable timestamp on given descriptor .
*
* The API does not release any resources associated with desc .
*
* @ desc : ts descriptor , this is the same as returned by the request API .
*
* Context : Holds mutex lock , not suitable from atomic context .
* Returns : 0 on success or a negative error code on failure .
*/
int hte_disable_ts ( struct hte_ts_desc * desc )
{
return hte_ts_dis_en_common ( desc , false ) ;
}
EXPORT_SYMBOL_GPL ( hte_disable_ts ) ;
/**
* hte_enable_ts ( ) - Enable timestamp on given descriptor .
*
* @ desc : ts descriptor , this is the same as returned by the request API .
*
* Context : Holds mutex lock , not suitable from atomic context .
* Returns : 0 on success or a negative error code on failure .
*/
int hte_enable_ts ( struct hte_ts_desc * desc )
{
return hte_ts_dis_en_common ( desc , true ) ;
}
EXPORT_SYMBOL_GPL ( hte_enable_ts ) ;
static void hte_do_cb_work ( struct work_struct * w )
{
unsigned long flag ;
struct hte_ts_info * ei = container_of ( w , struct hte_ts_info , cb_work ) ;
if ( unlikely ( ! ei - > tcb ) )
return ;
ei - > tcb ( ei - > cl_data ) ;
spin_lock_irqsave ( & ei - > slock , flag ) ;
clear_bit ( HTE_TS_QUEUE_WK , & ei - > flags ) ;
spin_unlock_irqrestore ( & ei - > slock , flag ) ;
}
static int __hte_req_ts ( struct hte_ts_desc * desc , hte_ts_cb_t cb ,
hte_ts_sec_cb_t tcb , void * data )
{
int ret ;
struct hte_device * gdev ;
struct hte_ts_info * ei = desc - > hte_data ;
gdev = ei - > gdev ;
/*
* There is a chance that multiple consumers requesting same entity ,
* lock here .
*/
mutex_lock ( & ei - > req_mlock ) ;
if ( test_bit ( HTE_TS_REGISTERED , & ei - > flags ) | |
! test_bit ( HTE_TS_REQ , & ei - > flags ) ) {
dev_dbg ( gdev - > chip - > dev , " id:%u req failed \n " ,
desc - > attr . line_id ) ;
ret = - EUSERS ;
goto unlock ;
}
ei - > cb = cb ;
ei - > tcb = tcb ;
if ( tcb )
INIT_WORK ( & ei - > cb_work , hte_do_cb_work ) ;
ret = gdev - > chip - > ops - > request ( gdev - > chip , desc , ei - > xlated_id ) ;
if ( ret < 0 ) {
dev_err ( gdev - > chip - > dev , " ts request failed \n " ) ;
goto unlock ;
}
ei - > cl_data = data ;
ei - > seq = 1 ;
atomic_inc ( & gdev - > ts_req ) ;
2023-10-10 17:11:23 +03:00
if ( desc - > attr . name )
ei - > line_name = NULL ;
else
ei - > line_name = kasprintf ( GFP_KERNEL , " ts_%u " , desc - > attr . line_id ) ;
2022-04-22 23:52:13 +03:00
hte_ts_dbgfs_init ( desc - > attr . name = = NULL ?
ei - > line_name : desc - > attr . name , ei ) ;
set_bit ( HTE_TS_REGISTERED , & ei - > flags ) ;
dev_dbg ( gdev - > chip - > dev , " id: %u, xlated id:%u " ,
desc - > attr . line_id , ei - > xlated_id ) ;
ret = 0 ;
unlock :
mutex_unlock ( & ei - > req_mlock ) ;
return ret ;
}
static int hte_bind_ts_info_locked ( struct hte_ts_info * ei ,
struct hte_ts_desc * desc , u32 x_id )
{
int ret = 0 ;
mutex_lock ( & ei - > req_mlock ) ;
if ( test_bit ( HTE_TS_REQ , & ei - > flags ) ) {
dev_dbg ( ei - > gdev - > chip - > dev , " id:%u is already requested \n " ,
desc - > attr . line_id ) ;
ret = - EUSERS ;
goto out ;
}
set_bit ( HTE_TS_REQ , & ei - > flags ) ;
desc - > hte_data = ei ;
ei - > xlated_id = x_id ;
out :
mutex_unlock ( & ei - > req_mlock ) ;
return ret ;
}
static struct hte_device * of_node_to_htedevice ( struct device_node * np )
{
struct hte_device * gdev ;
spin_lock ( & hte_lock ) ;
list_for_each_entry ( gdev , & hte_devices , list )
if ( gdev - > chip & & gdev - > chip - > dev & &
2022-11-17 10:17:29 +03:00
device_match_of_node ( gdev - > chip - > dev , np ) ) {
2022-04-22 23:52:13 +03:00
spin_unlock ( & hte_lock ) ;
return gdev ;
}
spin_unlock ( & hte_lock ) ;
return ERR_PTR ( - ENODEV ) ;
}
static struct hte_device * hte_find_dev_from_linedata ( struct hte_ts_desc * desc )
{
struct hte_device * gdev ;
spin_lock ( & hte_lock ) ;
list_for_each_entry ( gdev , & hte_devices , list )
if ( gdev - > chip & & gdev - > chip - > match_from_linedata ) {
if ( ! gdev - > chip - > match_from_linedata ( gdev - > chip , desc ) )
continue ;
spin_unlock ( & hte_lock ) ;
return gdev ;
}
spin_unlock ( & hte_lock ) ;
return ERR_PTR ( - ENODEV ) ;
}
/**
* of_hte_req_count - Return the number of entities to timestamp .
*
* The function returns the total count of the requested entities to timestamp
* by parsing device tree .
*
* @ dev : The HTE consumer .
*
* Returns : Positive number on success , - ENOENT if no entries ,
* - EINVAL for other errors .
*/
int of_hte_req_count ( struct device * dev )
{
int count ;
if ( ! dev | | ! dev - > of_node )
return - EINVAL ;
count = of_count_phandle_with_args ( dev - > of_node , " timestamps " ,
" #timestamp-cells " ) ;
return count ? count : - ENOENT ;
}
EXPORT_SYMBOL_GPL ( of_hte_req_count ) ;
static inline struct hte_device * hte_get_dev ( struct hte_ts_desc * desc )
{
return hte_find_dev_from_linedata ( desc ) ;
}
static struct hte_device * hte_of_get_dev ( struct device * dev ,
struct hte_ts_desc * desc ,
int index ,
struct of_phandle_args * args ,
bool * free_name )
{
int ret ;
struct device_node * np ;
char * temp ;
if ( ! dev - > of_node )
return ERR_PTR ( - EINVAL ) ;
np = dev - > of_node ;
2023-03-10 17:47:06 +03:00
if ( ! of_property_present ( np , " timestamp-names " ) ) {
2022-04-22 23:52:13 +03:00
/* Let hte core construct it during request time */
desc - > attr . name = NULL ;
} else {
ret = of_property_read_string_index ( np , " timestamp-names " ,
index , & desc - > attr . name ) ;
if ( ret ) {
pr_err ( " can't parse \" timestamp-names \" property \n " ) ;
return ERR_PTR ( ret ) ;
}
* free_name = false ;
if ( desc - > attr . name ) {
temp = skip_spaces ( desc - > attr . name ) ;
if ( ! * temp )
desc - > attr . name = NULL ;
}
}
ret = of_parse_phandle_with_args ( np , " timestamps " , " #timestamp-cells " ,
index , args ) ;
if ( ret ) {
pr_err ( " %s(): can't parse \" timestamps \" property \n " ,
__func__ ) ;
return ERR_PTR ( ret ) ;
}
of_node_put ( args - > np ) ;
return of_node_to_htedevice ( args - > np ) ;
}
/**
* hte_ts_get ( ) - The function to initialize and obtain HTE desc .
*
* The function initializes the consumer provided HTE descriptor . If consumer
* has device tree node , index is used to parse the line id and other details .
* The function needs to be called before using any request APIs .
*
* @ dev : HTE consumer / client device , used in case of parsing device tree node .
* @ desc : Pre - allocated timestamp descriptor .
* @ index : The index will be used as an index to parse line_id from the
* device tree node if node is present .
*
* Context : Holds mutex lock .
* Returns : Returns 0 on success or negative error code on failure .
*/
int hte_ts_get ( struct device * dev , struct hte_ts_desc * desc , int index )
{
struct hte_device * gdev ;
struct hte_ts_info * ei ;
const struct fwnode_handle * fwnode ;
struct of_phandle_args args ;
u32 xlated_id ;
int ret ;
2022-05-06 17:54:31 +03:00
bool free_name = false ;
2022-04-22 23:52:13 +03:00
if ( ! desc )
return - EINVAL ;
fwnode = dev ? dev_fwnode ( dev ) : NULL ;
if ( is_of_node ( fwnode ) )
gdev = hte_of_get_dev ( dev , desc , index , & args , & free_name ) ;
else
gdev = hte_get_dev ( desc ) ;
if ( IS_ERR ( gdev ) ) {
pr_err ( " %s() no hte dev found \n " , __func__ ) ;
return PTR_ERR ( gdev ) ;
}
if ( ! try_module_get ( gdev - > owner ) )
return - ENODEV ;
if ( ! gdev - > chip ) {
pr_err ( " %s(): requested id does not have provider \n " ,
__func__ ) ;
ret = - ENODEV ;
goto put ;
}
if ( is_of_node ( fwnode ) ) {
if ( ! gdev - > chip - > xlate_of )
ret = - EINVAL ;
else
ret = gdev - > chip - > xlate_of ( gdev - > chip , & args ,
desc , & xlated_id ) ;
} else {
if ( ! gdev - > chip - > xlate_plat )
ret = - EINVAL ;
else
ret = gdev - > chip - > xlate_plat ( gdev - > chip , desc ,
& xlated_id ) ;
}
if ( ret < 0 )
goto put ;
ei = & gdev - > ei [ xlated_id ] ;
ret = hte_bind_ts_info_locked ( ei , desc , xlated_id ) ;
if ( ret )
goto put ;
ei - > free_attr_name = free_name ;
return 0 ;
put :
module_put ( gdev - > owner ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( hte_ts_get ) ;
static void __devm_hte_release_ts ( void * res )
{
hte_ts_put ( res ) ;
}
/**
* hte_request_ts_ns ( ) - The API to request and enable hardware timestamp in
* nanoseconds .
*
* The entity is provider specific for example , GPIO lines , signals , buses
* etc . . . The API allocates necessary resources and enables the timestamp .
*
* @ desc : Pre - allocated and initialized timestamp descriptor .
* @ cb : Callback to push the timestamp data to consumer .
* @ tcb : Optional callback . If its provided , subsystem initializes
* workqueue . It is called when cb returns HTE_RUN_SECOND_CB .
* @ data : Client data , used during cb and tcb callbacks .
*
* Context : Holds mutex lock .
* Returns : Returns 0 on success or negative error code on failure .
*/
int hte_request_ts_ns ( struct hte_ts_desc * desc , hte_ts_cb_t cb ,
hte_ts_sec_cb_t tcb , void * data )
{
int ret ;
struct hte_ts_info * ei ;
if ( ! desc | | ! desc - > hte_data | | ! cb )
return - EINVAL ;
ei = desc - > hte_data ;
if ( ! ei | | ! ei - > gdev )
return - EINVAL ;
ret = __hte_req_ts ( desc , cb , tcb , data ) ;
if ( ret < 0 ) {
dev_err ( ei - > gdev - > chip - > dev ,
" failed to request id: %d \n " , desc - > attr . line_id ) ;
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( hte_request_ts_ns ) ;
/**
* devm_hte_request_ts_ns ( ) - Resource managed API to request and enable
* hardware timestamp in nanoseconds .
*
* The entity is provider specific for example , GPIO lines , signals , buses
* etc . . . The API allocates necessary resources and enables the timestamp . It
* deallocates and disables automatically when the consumer exits .
*
* @ dev : HTE consumer / client device .
* @ desc : Pre - allocated and initialized timestamp descriptor .
* @ cb : Callback to push the timestamp data to consumer .
* @ tcb : Optional callback . If its provided , subsystem initializes
* workqueue . It is called when cb returns HTE_RUN_SECOND_CB .
* @ data : Client data , used during cb and tcb callbacks .
*
* Context : Holds mutex lock .
* Returns : Returns 0 on success or negative error code on failure .
*/
int devm_hte_request_ts_ns ( struct device * dev , struct hte_ts_desc * desc ,
hte_ts_cb_t cb , hte_ts_sec_cb_t tcb ,
void * data )
{
int err ;
if ( ! dev )
return - EINVAL ;
err = hte_request_ts_ns ( desc , cb , tcb , data ) ;
if ( err )
return err ;
err = devm_add_action_or_reset ( dev , __devm_hte_release_ts , desc ) ;
if ( err )
return err ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( devm_hte_request_ts_ns ) ;
/**
* hte_init_line_attr ( ) - Initialize line attributes .
*
* Zeroes out line attributes and initializes with provided arguments .
* The function needs to be called before calling any consumer facing
* functions .
*
* @ desc : Pre - allocated timestamp descriptor .
* @ line_id : line id .
* @ edge_flags : edge flags related to line_id .
* @ name : name of the line .
* @ data : line data related to line_id .
*
* Context : Any .
* Returns : 0 on success or negative error code for the failure .
*/
int hte_init_line_attr ( struct hte_ts_desc * desc , u32 line_id ,
unsigned long edge_flags , const char * name , void * data )
{
if ( ! desc )
return - EINVAL ;
memset ( & desc - > attr , 0 , sizeof ( desc - > attr ) ) ;
desc - > attr . edge_flags = edge_flags ;
desc - > attr . line_id = line_id ;
desc - > attr . line_data = data ;
if ( name ) {
name = kstrdup_const ( name , GFP_KERNEL ) ;
if ( ! name )
return - ENOMEM ;
}
desc - > attr . name = name ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( hte_init_line_attr ) ;
/**
* hte_get_clk_src_info ( ) - Get the clock source information for a ts
* descriptor .
*
* @ desc : ts descriptor , same as returned from request API .
* @ ci : The API fills this structure with the clock information data .
*
* Context : Any context .
* Returns : 0 on success else negative error code on failure .
*/
int hte_get_clk_src_info ( const struct hte_ts_desc * desc ,
struct hte_clk_info * ci )
{
struct hte_chip * chip ;
struct hte_ts_info * ei ;
if ( ! desc | | ! desc - > hte_data | | ! ci ) {
pr_debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
return - EINVAL ;
}
ei = desc - > hte_data ;
if ( ! ei - > gdev | | ! ei - > gdev - > chip )
return - EINVAL ;
chip = ei - > gdev - > chip ;
if ( ! chip - > ops - > get_clk_src_info )
return - EOPNOTSUPP ;
return chip - > ops - > get_clk_src_info ( chip , ci ) ;
}
EXPORT_SYMBOL_GPL ( hte_get_clk_src_info ) ;
/**
* hte_push_ts_ns ( ) - Push timestamp data in nanoseconds .
*
* It is used by the provider to push timestamp data .
*
* @ chip : The HTE chip , used during the registration .
* @ xlated_id : entity id understood by both subsystem and provider , this is
* obtained from xlate callback during request API .
* @ data : timestamp data .
*
* Returns : 0 on success or a negative error code on failure .
*/
int hte_push_ts_ns ( const struct hte_chip * chip , u32 xlated_id ,
struct hte_ts_data * data )
{
enum hte_return ret ;
int st = 0 ;
struct hte_ts_info * ei ;
unsigned long flag ;
if ( ! chip | | ! data | | ! chip - > gdev )
return - EINVAL ;
2022-05-06 17:53:52 +03:00
if ( xlated_id > = chip - > nlines )
2022-04-22 23:52:13 +03:00
return - EINVAL ;
ei = & chip - > gdev - > ei [ xlated_id ] ;
spin_lock_irqsave ( & ei - > slock , flag ) ;
/* timestamp sequence counter */
data - > seq = ei - > seq + + ;
if ( ! test_bit ( HTE_TS_REGISTERED , & ei - > flags ) | |
test_bit ( HTE_TS_DISABLE , & ei - > flags ) ) {
dev_dbg ( chip - > dev , " Unknown timestamp push \n " ) ;
atomic_inc ( & ei - > dropped_ts ) ;
st = - EINVAL ;
goto unlock ;
}
ret = ei - > cb ( data , ei - > cl_data ) ;
if ( ret = = HTE_RUN_SECOND_CB & & ei - > tcb ) {
queue_work ( system_unbound_wq , & ei - > cb_work ) ;
set_bit ( HTE_TS_QUEUE_WK , & ei - > flags ) ;
}
unlock :
spin_unlock_irqrestore ( & ei - > slock , flag ) ;
return st ;
}
EXPORT_SYMBOL_GPL ( hte_push_ts_ns ) ;
static int hte_register_chip ( struct hte_chip * chip )
{
struct hte_device * gdev ;
u32 i ;
if ( ! chip | | ! chip - > dev | | ! chip - > dev - > of_node )
return - EINVAL ;
if ( ! chip - > ops | | ! chip - > ops - > request | | ! chip - > ops - > release ) {
dev_err ( chip - > dev , " Driver needs to provide ops \n " ) ;
return - EINVAL ;
}
gdev = kzalloc ( struct_size ( gdev , ei , chip - > nlines ) , GFP_KERNEL ) ;
if ( ! gdev )
return - ENOMEM ;
gdev - > chip = chip ;
chip - > gdev = gdev ;
gdev - > nlines = chip - > nlines ;
gdev - > sdev = chip - > dev ;
for ( i = 0 ; i < chip - > nlines ; i + + ) {
gdev - > ei [ i ] . gdev = gdev ;
mutex_init ( & gdev - > ei [ i ] . req_mlock ) ;
spin_lock_init ( & gdev - > ei [ i ] . slock ) ;
}
if ( chip - > dev - > driver )
gdev - > owner = chip - > dev - > driver - > owner ;
else
gdev - > owner = THIS_MODULE ;
of_node_get ( chip - > dev - > of_node ) ;
INIT_LIST_HEAD ( & gdev - > list ) ;
spin_lock ( & hte_lock ) ;
list_add_tail ( & gdev - > list , & hte_devices ) ;
spin_unlock ( & hte_lock ) ;
hte_chip_dbgfs_init ( gdev ) ;
dev_dbg ( chip - > dev , " Added hte chip \n " ) ;
return 0 ;
}
static int hte_unregister_chip ( struct hte_chip * chip )
{
struct hte_device * gdev ;
if ( ! chip )
return - EINVAL ;
gdev = chip - > gdev ;
spin_lock ( & hte_lock ) ;
list_del ( & gdev - > list ) ;
spin_unlock ( & hte_lock ) ;
gdev - > chip = NULL ;
of_node_put ( chip - > dev - > of_node ) ;
debugfs_remove_recursive ( gdev - > dbg_root ) ;
kfree ( gdev ) ;
dev_dbg ( chip - > dev , " Removed hte chip \n " ) ;
return 0 ;
}
static void _hte_devm_unregister_chip ( void * chip )
{
hte_unregister_chip ( chip ) ;
}
/**
* devm_hte_register_chip ( ) - Resource managed API to register HTE chip .
*
* It is used by the provider to register itself with the HTE subsystem .
* The unregistration is done automatically when the provider exits .
*
* @ chip : the HTE chip to add to subsystem .
*
* Returns : 0 on success or a negative error code on failure .
*/
int devm_hte_register_chip ( struct hte_chip * chip )
{
int err ;
err = hte_register_chip ( chip ) ;
if ( err )
return err ;
err = devm_add_action_or_reset ( chip - > dev , _hte_devm_unregister_chip ,
chip ) ;
if ( err )
return err ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( devm_hte_register_chip ) ;