2019-05-30 02:57:50 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-04-16 20:01:02 +04:00
/*
* HSI core .
*
* Copyright ( C ) 2010 Nokia Corporation . All rights reserved .
*
* Contact : Carlos Chinea < carlos . chinea @ nokia . com >
*/
# include <linux/hsi/hsi.h>
# include <linux/compiler.h>
# include <linux/list.h>
# include <linux/kobject.h>
# include <linux/slab.h>
# include <linux/string.h>
2012-04-11 11:55:53 +04:00
# include <linux/notifier.h>
2014-03-29 01:59:43 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2010-04-16 20:01:02 +04:00
# include "hsi_core.h"
static ssize_t modalias_show ( struct device * dev ,
struct device_attribute * a __maybe_unused , char * buf )
{
return sprintf ( buf , " hsi:%s \n " , dev_name ( dev ) ) ;
}
2013-10-07 10:55:50 +04:00
static DEVICE_ATTR_RO ( modalias ) ;
2010-04-16 20:01:02 +04:00
2013-10-07 10:55:50 +04:00
static struct attribute * hsi_bus_dev_attrs [ ] = {
& dev_attr_modalias . attr ,
NULL ,
2010-04-16 20:01:02 +04:00
} ;
2013-10-07 10:55:50 +04:00
ATTRIBUTE_GROUPS ( hsi_bus_dev ) ;
2010-04-16 20:01:02 +04:00
static int hsi_bus_uevent ( struct device * dev , struct kobj_uevent_env * env )
{
2012-04-10 16:11:24 +04:00
add_uevent_var ( env , " MODALIAS=hsi:%s " , dev_name ( dev ) ) ;
2010-04-16 20:01:02 +04:00
return 0 ;
}
static int hsi_bus_match ( struct device * dev , struct device_driver * driver )
{
2014-03-29 01:59:43 +04:00
if ( of_driver_match_device ( dev , driver ) )
return true ;
if ( strcmp ( dev_name ( dev ) , driver - > name ) = = 0 )
return true ;
return false ;
2010-04-16 20:01:02 +04:00
}
static struct bus_type hsi_bus_type = {
. name = " hsi " ,
2013-10-07 10:55:50 +04:00
. dev_groups = hsi_bus_dev_groups ,
2010-04-16 20:01:02 +04:00
. match = hsi_bus_match ,
. uevent = hsi_bus_uevent ,
} ;
static void hsi_client_release ( struct device * dev )
{
2014-03-29 01:48:23 +04:00
struct hsi_client * cl = to_hsi_client ( dev ) ;
kfree ( cl - > tx_cfg . channels ) ;
kfree ( cl - > rx_cfg . channels ) ;
kfree ( cl ) ;
2010-04-16 20:01:02 +04:00
}
2014-03-29 01:54:25 +04:00
struct hsi_client * hsi_new_client ( struct hsi_port * port ,
struct hsi_board_info * info )
2010-04-16 20:01:02 +04:00
{
struct hsi_client * cl ;
2014-03-29 01:48:23 +04:00
size_t size ;
2010-04-16 20:01:02 +04:00
cl = kzalloc ( sizeof ( * cl ) , GFP_KERNEL ) ;
if ( ! cl )
2015-10-17 22:25:07 +03:00
goto err ;
2014-03-29 01:48:23 +04:00
2010-04-16 20:01:02 +04:00
cl - > tx_cfg = info - > tx_cfg ;
2014-03-29 01:48:23 +04:00
if ( cl - > tx_cfg . channels ) {
size = cl - > tx_cfg . num_channels * sizeof ( * cl - > tx_cfg . channels ) ;
2016-05-19 14:09:28 +03:00
cl - > tx_cfg . channels = kmemdup ( info - > tx_cfg . channels , size ,
GFP_KERNEL ) ;
2015-10-17 22:25:07 +03:00
if ( ! cl - > tx_cfg . channels )
goto err_tx ;
2014-03-29 01:48:23 +04:00
}
2010-04-16 20:01:02 +04:00
cl - > rx_cfg = info - > rx_cfg ;
2014-03-29 01:48:23 +04:00
if ( cl - > rx_cfg . channels ) {
size = cl - > rx_cfg . num_channels * sizeof ( * cl - > rx_cfg . channels ) ;
2016-05-19 14:09:28 +03:00
cl - > rx_cfg . channels = kmemdup ( info - > rx_cfg . channels , size ,
GFP_KERNEL ) ;
2015-10-17 22:25:07 +03:00
if ( ! cl - > rx_cfg . channels )
goto err_rx ;
2014-03-29 01:48:23 +04:00
}
2010-04-16 20:01:02 +04:00
cl - > device . bus = & hsi_bus_type ;
cl - > device . parent = & port - > device ;
cl - > device . release = hsi_client_release ;
2013-07-04 02:04:56 +04:00
dev_set_name ( & cl - > device , " %s " , info - > name ) ;
2010-04-16 20:01:02 +04:00
cl - > device . platform_data = info - > platform_data ;
if ( info - > archdata )
cl - > device . archdata = * info - > archdata ;
if ( device_register ( & cl - > device ) < 0 ) {
pr_err ( " hsi: failed to register client: %s \n " , info - > name ) ;
2012-04-11 12:01:11 +04:00
put_device ( & cl - > device ) ;
2010-04-16 20:01:02 +04:00
}
2014-03-29 01:54:25 +04:00
return cl ;
2015-10-17 22:25:07 +03:00
err_rx :
kfree ( cl - > tx_cfg . channels ) ;
err_tx :
kfree ( cl ) ;
err :
return NULL ;
2010-04-16 20:01:02 +04:00
}
2014-03-29 01:54:25 +04:00
EXPORT_SYMBOL_GPL ( hsi_new_client ) ;
2010-04-16 20:01:02 +04:00
static void hsi_scan_board_info ( struct hsi_controller * hsi )
{
struct hsi_cl_info * cl_info ;
struct hsi_port * p ;
list_for_each_entry ( cl_info , & hsi_board_list , list )
if ( cl_info - > info . hsi_id = = hsi - > id ) {
p = hsi_find_port_num ( hsi , cl_info - > info . port ) ;
if ( ! p )
continue ;
hsi_new_client ( p , & cl_info - > info ) ;
}
}
2014-03-29 01:59:43 +04:00
# ifdef CONFIG_OF
static struct hsi_board_info hsi_char_dev_info = {
. name = " hsi_char " ,
} ;
static int hsi_of_property_parse_mode ( struct device_node * client , char * name ,
unsigned int * result )
{
const char * mode ;
int err ;
err = of_property_read_string ( client , name , & mode ) ;
if ( err < 0 )
return err ;
if ( strcmp ( mode , " stream " ) = = 0 )
* result = HSI_MODE_STREAM ;
else if ( strcmp ( mode , " frame " ) = = 0 )
* result = HSI_MODE_FRAME ;
else
return - EINVAL ;
return 0 ;
}
static int hsi_of_property_parse_flow ( struct device_node * client , char * name ,
unsigned int * result )
{
const char * flow ;
int err ;
err = of_property_read_string ( client , name , & flow ) ;
if ( err < 0 )
return err ;
if ( strcmp ( flow , " synchronized " ) = = 0 )
* result = HSI_FLOW_SYNC ;
else if ( strcmp ( flow , " pipeline " ) = = 0 )
* result = HSI_FLOW_PIPE ;
else
return - EINVAL ;
return 0 ;
}
static int hsi_of_property_parse_arb_mode ( struct device_node * client ,
char * name , unsigned int * result )
{
const char * arb_mode ;
int err ;
err = of_property_read_string ( client , name , & arb_mode ) ;
if ( err < 0 )
return err ;
if ( strcmp ( arb_mode , " round-robin " ) = = 0 )
* result = HSI_ARB_RR ;
else if ( strcmp ( arb_mode , " priority " ) = = 0 )
* result = HSI_ARB_PRIO ;
else
return - EINVAL ;
return 0 ;
}
static void hsi_add_client_from_dt ( struct hsi_port * port ,
struct device_node * client )
{
struct hsi_client * cl ;
struct hsi_channel channel ;
struct property * prop ;
char name [ 32 ] ;
int length , cells , err , i , max_chan , mode ;
cl = kzalloc ( sizeof ( * cl ) , GFP_KERNEL ) ;
if ( ! cl )
return ;
err = of_modalias_node ( client , name , sizeof ( name ) ) ;
if ( err )
goto err ;
err = hsi_of_property_parse_mode ( client , " hsi-mode " , & mode ) ;
if ( err ) {
err = hsi_of_property_parse_mode ( client , " hsi-rx-mode " ,
& cl - > rx_cfg . mode ) ;
if ( err )
goto err ;
err = hsi_of_property_parse_mode ( client , " hsi-tx-mode " ,
& cl - > tx_cfg . mode ) ;
if ( err )
goto err ;
} else {
cl - > rx_cfg . mode = mode ;
cl - > tx_cfg . mode = mode ;
}
err = of_property_read_u32 ( client , " hsi-speed-kbps " ,
& cl - > tx_cfg . speed ) ;
if ( err )
goto err ;
cl - > rx_cfg . speed = cl - > tx_cfg . speed ;
err = hsi_of_property_parse_flow ( client , " hsi-flow " ,
& cl - > rx_cfg . flow ) ;
if ( err )
goto err ;
err = hsi_of_property_parse_arb_mode ( client , " hsi-arb-mode " ,
& cl - > rx_cfg . arb_mode ) ;
if ( err )
goto err ;
prop = of_find_property ( client , " hsi-channel-ids " , & length ) ;
if ( ! prop ) {
err = - EINVAL ;
goto err ;
}
cells = length / sizeof ( u32 ) ;
cl - > rx_cfg . num_channels = cells ;
cl - > tx_cfg . num_channels = cells ;
2017-04-25 15:17:50 +03:00
cl - > rx_cfg . channels = kcalloc ( cells , sizeof ( channel ) , GFP_KERNEL ) ;
2014-03-29 01:59:43 +04:00
if ( ! cl - > rx_cfg . channels ) {
err = - ENOMEM ;
goto err ;
}
2017-04-25 15:17:50 +03:00
cl - > tx_cfg . channels = kcalloc ( cells , sizeof ( channel ) , GFP_KERNEL ) ;
2014-03-29 01:59:43 +04:00
if ( ! cl - > tx_cfg . channels ) {
err = - ENOMEM ;
goto err2 ;
}
max_chan = 0 ;
for ( i = 0 ; i < cells ; i + + ) {
err = of_property_read_u32_index ( client , " hsi-channel-ids " , i ,
& channel . id ) ;
if ( err )
goto err3 ;
err = of_property_read_string_index ( client , " hsi-channel-names " ,
i , & channel . name ) ;
if ( err )
channel . name = NULL ;
if ( channel . id > max_chan )
max_chan = channel . id ;
cl - > rx_cfg . channels [ i ] = channel ;
cl - > tx_cfg . channels [ i ] = channel ;
}
cl - > rx_cfg . num_hw_channels = max_chan + 1 ;
cl - > tx_cfg . num_hw_channels = max_chan + 1 ;
cl - > device . bus = & hsi_bus_type ;
cl - > device . parent = & port - > device ;
cl - > device . release = hsi_client_release ;
cl - > device . of_node = client ;
2021-04-09 14:08:17 +03:00
dev_set_name ( & cl - > device , " %s " , name ) ;
2014-03-29 01:59:43 +04:00
if ( device_register ( & cl - > device ) < 0 ) {
pr_err ( " hsi: failed to register client: %s \n " , name ) ;
put_device ( & cl - > device ) ;
}
return ;
err3 :
kfree ( cl - > tx_cfg . channels ) ;
err2 :
kfree ( cl - > rx_cfg . channels ) ;
err :
kfree ( cl ) ;
pr_err ( " hsi client: missing or incorrect of property: err=%d \n " , err ) ;
}
void hsi_add_clients_from_dt ( struct hsi_port * port , struct device_node * clients )
{
struct device_node * child ;
/* register hsi-char device */
hsi_new_client ( port , & hsi_char_dev_info ) ;
for_each_available_child_of_node ( clients , child )
hsi_add_client_from_dt ( port , child ) ;
}
EXPORT_SYMBOL_GPL ( hsi_add_clients_from_dt ) ;
# endif
2014-03-29 01:54:25 +04:00
int hsi_remove_client ( struct device * dev , void * data __maybe_unused )
2010-04-16 20:01:02 +04:00
{
device_unregister ( dev ) ;
return 0 ;
}
2014-03-29 01:54:25 +04:00
EXPORT_SYMBOL_GPL ( hsi_remove_client ) ;
2010-04-16 20:01:02 +04:00
static int hsi_remove_port ( struct device * dev , void * data __maybe_unused )
{
device_for_each_child ( dev , NULL , hsi_remove_client ) ;
device_unregister ( dev ) ;
return 0 ;
}
2012-04-04 15:11:45 +04:00
static void hsi_controller_release ( struct device * dev )
2010-04-16 20:01:02 +04:00
{
2012-04-04 15:11:45 +04:00
struct hsi_controller * hsi = to_hsi_controller ( dev ) ;
kfree ( hsi - > port ) ;
kfree ( hsi ) ;
2010-04-16 20:01:02 +04:00
}
2012-04-04 15:11:45 +04:00
static void hsi_port_release ( struct device * dev )
2010-04-16 20:01:02 +04:00
{
2012-04-04 15:11:45 +04:00
kfree ( to_hsi_port ( dev ) ) ;
2010-04-16 20:01:02 +04:00
}
2013-10-06 22:23:49 +04:00
/**
2020-12-01 15:08:54 +03:00
* hsi_port_unregister_clients - Unregister an HSI port
2013-10-06 22:23:49 +04:00
* @ port : The HSI port to unregister
*/
void hsi_port_unregister_clients ( struct hsi_port * port )
{
device_for_each_child ( & port - > device , NULL , hsi_remove_client ) ;
}
EXPORT_SYMBOL_GPL ( hsi_port_unregister_clients ) ;
2010-04-16 20:01:02 +04:00
/**
* hsi_unregister_controller - Unregister an HSI controller
* @ hsi : The HSI controller to register
*/
void hsi_unregister_controller ( struct hsi_controller * hsi )
{
device_for_each_child ( & hsi - > device , NULL , hsi_remove_port ) ;
device_unregister ( & hsi - > device ) ;
}
EXPORT_SYMBOL_GPL ( hsi_unregister_controller ) ;
/**
* hsi_register_controller - Register an HSI controller and its ports
* @ hsi : The HSI controller to register
*
* Returns - errno on failure , 0 on success .
*/
int hsi_register_controller ( struct hsi_controller * hsi )
{
unsigned int i ;
int err ;
2012-04-04 15:11:45 +04:00
err = device_add ( & hsi - > device ) ;
2010-04-16 20:01:02 +04:00
if ( err < 0 )
return err ;
for ( i = 0 ; i < hsi - > num_ports ; i + + ) {
2012-04-04 15:11:45 +04:00
hsi - > port [ i ] - > device . parent = & hsi - > device ;
err = device_add ( & hsi - > port [ i ] - > device ) ;
2010-04-16 20:01:02 +04:00
if ( err < 0 )
goto out ;
}
/* Populate HSI bus with HSI clients */
hsi_scan_board_info ( hsi ) ;
return 0 ;
out :
2012-04-04 15:11:45 +04:00
while ( i - - > 0 )
device_del ( & hsi - > port [ i ] - > device ) ;
device_del ( & hsi - > device ) ;
2010-04-16 20:01:02 +04:00
return err ;
}
EXPORT_SYMBOL_GPL ( hsi_register_controller ) ;
/**
* hsi_register_client_driver - Register an HSI client to the HSI bus
* @ drv : HSI client driver to register
*
* Returns - errno on failure , 0 on success .
*/
int hsi_register_client_driver ( struct hsi_client_driver * drv )
{
drv - > driver . bus = & hsi_bus_type ;
return driver_register ( & drv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( hsi_register_client_driver ) ;
static inline int hsi_dummy_msg ( struct hsi_msg * msg __maybe_unused )
{
return 0 ;
}
static inline int hsi_dummy_cl ( struct hsi_client * cl __maybe_unused )
{
return 0 ;
}
2012-04-04 15:11:45 +04:00
/**
* hsi_put_controller - Free an HSI controller
*
* @ hsi : Pointer to the HSI controller to freed
*
* HSI controller drivers should only use this function if they need
* to free their allocated hsi_controller structures before a successful
* call to hsi_register_controller . Other use is not allowed .
*/
void hsi_put_controller ( struct hsi_controller * hsi )
{
unsigned int i ;
if ( ! hsi )
return ;
for ( i = 0 ; i < hsi - > num_ports ; i + + )
if ( hsi - > port & & hsi - > port [ i ] )
put_device ( & hsi - > port [ i ] - > device ) ;
put_device ( & hsi - > device ) ;
}
EXPORT_SYMBOL_GPL ( hsi_put_controller ) ;
2010-04-16 20:01:02 +04:00
/**
* hsi_alloc_controller - Allocate an HSI controller and its ports
* @ n_ports : Number of ports on the HSI controller
* @ flags : Kernel allocation flags
*
* Return NULL on failure or a pointer to an hsi_controller on success .
*/
struct hsi_controller * hsi_alloc_controller ( unsigned int n_ports , gfp_t flags )
{
struct hsi_controller * hsi ;
2012-04-04 15:11:45 +04:00
struct hsi_port * * port ;
2010-04-16 20:01:02 +04:00
unsigned int i ;
if ( ! n_ports )
return NULL ;
hsi = kzalloc ( sizeof ( * hsi ) , flags ) ;
if ( ! hsi )
2012-04-04 15:11:45 +04:00
return NULL ;
2017-04-25 15:17:50 +03:00
port = kcalloc ( n_ports , sizeof ( * port ) , flags ) ;
2012-04-04 15:11:45 +04:00
if ( ! port ) {
kfree ( hsi ) ;
return NULL ;
2010-04-16 20:01:02 +04:00
}
hsi - > num_ports = n_ports ;
hsi - > port = port ;
2012-04-04 15:11:45 +04:00
hsi - > device . release = hsi_controller_release ;
device_initialize ( & hsi - > device ) ;
for ( i = 0 ; i < n_ports ; i + + ) {
port [ i ] = kzalloc ( sizeof ( * * port ) , flags ) ;
if ( port [ i ] = = NULL )
goto out ;
port [ i ] - > num = i ;
port [ i ] - > async = hsi_dummy_msg ;
port [ i ] - > setup = hsi_dummy_cl ;
port [ i ] - > flush = hsi_dummy_cl ;
port [ i ] - > start_tx = hsi_dummy_cl ;
port [ i ] - > stop_tx = hsi_dummy_cl ;
port [ i ] - > release = hsi_dummy_cl ;
mutex_init ( & port [ i ] - > lock ) ;
2016-04-30 19:08:06 +03:00
BLOCKING_INIT_NOTIFIER_HEAD ( & port [ i ] - > n_head ) ;
2012-04-04 15:11:45 +04:00
dev_set_name ( & port [ i ] - > device , " port%d " , i ) ;
hsi - > port [ i ] - > device . release = hsi_port_release ;
device_initialize ( & hsi - > port [ i ] - > device ) ;
}
2010-04-16 20:01:02 +04:00
return hsi ;
out :
2012-04-04 15:11:45 +04:00
hsi_put_controller ( hsi ) ;
2010-04-16 20:01:02 +04:00
return NULL ;
}
EXPORT_SYMBOL_GPL ( hsi_alloc_controller ) ;
/**
* hsi_free_msg - Free an HSI message
* @ msg : Pointer to the HSI message
*
* Client is responsible to free the buffers pointed by the scatterlists .
*/
void hsi_free_msg ( struct hsi_msg * msg )
{
if ( ! msg )
return ;
sg_free_table ( & msg - > sgt ) ;
kfree ( msg ) ;
}
EXPORT_SYMBOL_GPL ( hsi_free_msg ) ;
/**
* hsi_alloc_msg - Allocate an HSI message
* @ nents : Number of memory entries
* @ flags : Kernel allocation flags
*
* nents can be 0. This mainly makes sense for read transfer .
* In that case , HSI drivers will call the complete callback when
* there is data to be read without consuming it .
*
* Return NULL on failure or a pointer to an hsi_msg on success .
*/
struct hsi_msg * hsi_alloc_msg ( unsigned int nents , gfp_t flags )
{
struct hsi_msg * msg ;
int err ;
msg = kzalloc ( sizeof ( * msg ) , flags ) ;
if ( ! msg )
return NULL ;
if ( ! nents )
return msg ;
err = sg_alloc_table ( & msg - > sgt , nents , flags ) ;
if ( unlikely ( err ) ) {
kfree ( msg ) ;
msg = NULL ;
}
return msg ;
}
EXPORT_SYMBOL_GPL ( hsi_alloc_msg ) ;
/**
* hsi_async - Submit an HSI transfer to the controller
* @ cl : HSI client sending the transfer
* @ msg : The HSI transfer passed to controller
*
* The HSI message must have the channel , ttype , complete and destructor
* fields set beforehand . If nents > 0 then the client has to initialize
* also the scatterlists to point to the buffers to write to or read from .
*
* HSI controllers relay on pre - allocated buffers from their clients and they
* do not allocate buffers on their own .
*
* Once the HSI message transfer finishes , the HSI controller calls the
* complete callback with the status and actual_len fields of the HSI message
* updated . The complete callback can be called before returning from
* hsi_async .
*
* Returns - errno on failure or 0 on success
*/
int hsi_async ( struct hsi_client * cl , struct hsi_msg * msg )
{
struct hsi_port * port = hsi_get_port ( cl ) ;
if ( ! hsi_port_claimed ( cl ) )
return - EACCES ;
WARN_ON_ONCE ( ! msg - > destructor | | ! msg - > complete ) ;
msg - > cl = cl ;
return port - > async ( msg ) ;
}
EXPORT_SYMBOL_GPL ( hsi_async ) ;
/**
* hsi_claim_port - Claim the HSI client ' s port
* @ cl : HSI client that wants to claim its port
* @ share : Flag to indicate if the client wants to share the port or not .
*
* Returns - errno on failure , 0 on success .
*/
int hsi_claim_port ( struct hsi_client * cl , unsigned int share )
{
struct hsi_port * port = hsi_get_port ( cl ) ;
int err = 0 ;
mutex_lock ( & port - > lock ) ;
if ( ( port - > claimed ) & & ( ! port - > shared | | ! share ) ) {
err = - EBUSY ;
goto out ;
}
if ( ! try_module_get ( to_hsi_controller ( port - > device . parent ) - > owner ) ) {
err = - ENODEV ;
goto out ;
}
port - > claimed + + ;
port - > shared = ! ! share ;
cl - > pclaimed = 1 ;
out :
mutex_unlock ( & port - > lock ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( hsi_claim_port ) ;
/**
* hsi_release_port - Release the HSI client ' s port
* @ cl : HSI client which previously claimed its port
*/
void hsi_release_port ( struct hsi_client * cl )
{
struct hsi_port * port = hsi_get_port ( cl ) ;
mutex_lock ( & port - > lock ) ;
/* Allow HW driver to do some cleanup */
port - > release ( cl ) ;
if ( cl - > pclaimed )
port - > claimed - - ;
BUG_ON ( port - > claimed < 0 ) ;
cl - > pclaimed = 0 ;
if ( ! port - > claimed )
port - > shared = 0 ;
module_put ( to_hsi_controller ( port - > device . parent ) - > owner ) ;
mutex_unlock ( & port - > lock ) ;
}
EXPORT_SYMBOL_GPL ( hsi_release_port ) ;
2012-04-11 11:55:53 +04:00
static int hsi_event_notifier_call ( struct notifier_block * nb ,
unsigned long event , void * data __maybe_unused )
2010-04-16 20:01:02 +04:00
{
2012-04-11 11:55:53 +04:00
struct hsi_client * cl = container_of ( nb , struct hsi_client , nb ) ;
( * cl - > ehandler ) ( cl , event ) ;
2010-04-16 20:01:02 +04:00
return 0 ;
}
2012-04-11 11:55:53 +04:00
/**
* hsi_register_port_event - Register a client to receive port events
* @ cl : HSI client that wants to receive port events
2013-03-01 23:11:47 +04:00
* @ handler : Event handler callback
2012-04-11 11:55:53 +04:00
*
* Clients should register a callback to be able to receive
* events from the ports . Registration should happen after
* claiming the port .
* The handler can be called in interrupt context .
*
* Returns - errno on error , or 0 on success .
*/
int hsi_register_port_event ( struct hsi_client * cl ,
void ( * handler ) ( struct hsi_client * , unsigned long ) )
2010-04-16 20:01:02 +04:00
{
2012-04-11 11:55:53 +04:00
struct hsi_port * port = hsi_get_port ( cl ) ;
2010-04-16 20:01:02 +04:00
2012-04-11 11:55:53 +04:00
if ( ! handler | | cl - > ehandler )
return - EINVAL ;
if ( ! hsi_port_claimed ( cl ) )
return - EACCES ;
cl - > ehandler = handler ;
cl - > nb . notifier_call = hsi_event_notifier_call ;
2016-04-30 19:08:06 +03:00
return blocking_notifier_chain_register ( & port - > n_head , & cl - > nb ) ;
2010-04-16 20:01:02 +04:00
}
2012-04-11 11:55:53 +04:00
EXPORT_SYMBOL_GPL ( hsi_register_port_event ) ;
2010-04-16 20:01:02 +04:00
2012-04-11 11:55:53 +04:00
/**
* hsi_unregister_port_event - Stop receiving port events for a client
* @ cl : HSI client that wants to stop receiving port events
*
* Clients should call this function before releasing their associated
* port .
*
* Returns - errno on error , or 0 on success .
*/
int hsi_unregister_port_event ( struct hsi_client * cl )
2010-04-16 20:01:02 +04:00
{
2012-04-11 11:55:53 +04:00
struct hsi_port * port = hsi_get_port ( cl ) ;
int err ;
2010-04-16 20:01:02 +04:00
2012-04-11 11:55:53 +04:00
WARN_ON ( ! hsi_port_claimed ( cl ) ) ;
2010-04-16 20:01:02 +04:00
2016-04-30 19:08:06 +03:00
err = blocking_notifier_chain_unregister ( & port - > n_head , & cl - > nb ) ;
2012-04-11 11:55:53 +04:00
if ( ! err )
cl - > ehandler = NULL ;
return err ;
2010-04-16 20:01:02 +04:00
}
2012-04-11 11:55:53 +04:00
EXPORT_SYMBOL_GPL ( hsi_unregister_port_event ) ;
2010-04-16 20:01:02 +04:00
/**
2014-03-29 01:59:43 +04:00
* hsi_event - Notifies clients about port events
2010-04-16 20:01:02 +04:00
* @ port : Port where the event occurred
* @ event : The event type
*
* Clients should not be concerned about wake line behavior . However , due
* to a race condition in HSI HW protocol , clients need to be notified
* about wake line changes , so they can implement a workaround for it .
*
* Events :
* HSI_EVENT_START_RX - Incoming wake line high
* HSI_EVENT_STOP_RX - Incoming wake line down
2012-04-11 11:55:53 +04:00
*
* Returns - errno on error , or 0 on success .
2010-04-16 20:01:02 +04:00
*/
2012-04-11 11:55:53 +04:00
int hsi_event ( struct hsi_port * port , unsigned long event )
2010-04-16 20:01:02 +04:00
{
2016-04-30 19:08:06 +03:00
return blocking_notifier_call_chain ( & port - > n_head , event , NULL ) ;
2010-04-16 20:01:02 +04:00
}
EXPORT_SYMBOL_GPL ( hsi_event ) ;
2014-03-29 01:48:23 +04:00
/**
* hsi_get_channel_id_by_name - acquire channel id by channel name
* @ cl : HSI client , which uses the channel
* @ name : name the channel is known under
*
* Clients can call this function to get the hsi channel ids similar to
* requesting IRQs or GPIOs by name . This function assumes the same
* channel configuration is used for RX and TX .
*
* Returns - errno on error or channel id on success .
*/
int hsi_get_channel_id_by_name ( struct hsi_client * cl , char * name )
{
int i ;
if ( ! cl - > rx_cfg . channels )
return - ENOENT ;
for ( i = 0 ; i < cl - > rx_cfg . num_channels ; i + + )
if ( ! strcmp ( cl - > rx_cfg . channels [ i ] . name , name ) )
return cl - > rx_cfg . channels [ i ] . id ;
return - ENXIO ;
}
EXPORT_SYMBOL_GPL ( hsi_get_channel_id_by_name ) ;
2010-04-16 20:01:02 +04:00
static int __init hsi_init ( void )
{
return bus_register ( & hsi_bus_type ) ;
}
postcore_initcall ( hsi_init ) ;
static void __exit hsi_exit ( void )
{
bus_unregister ( & hsi_bus_type ) ;
}
module_exit ( hsi_exit ) ;
MODULE_AUTHOR ( " Carlos Chinea <carlos.chinea@nokia.com> " ) ;
MODULE_DESCRIPTION ( " High-speed Synchronous Serial Interface (HSI) framework " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;