2019-05-29 17:18:02 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-08-19 20:05:35 +03:00
/*
* Copyright ( c ) 2016 , NVIDIA CORPORATION . All rights reserved .
*/
# include <linux/clk/tegra.h>
# include <linux/genalloc.h>
# include <linux/mailbox_client.h>
2020-04-22 01:00:53 +03:00
# include <linux/module.h>
2016-08-19 20:05:35 +03:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
2018-09-21 13:08:56 +03:00
# include <linux/pm.h>
2016-08-19 20:05:35 +03:00
# include <linux/semaphore.h>
2017-02-01 18:36:40 +03:00
# include <linux/sched/clock.h>
2016-08-19 20:05:35 +03:00
# include <soc/tegra/bpmp.h>
# include <soc/tegra/bpmp-abi.h>
# include <soc/tegra/ivc.h>
2019-01-24 20:03:53 +03:00
# include "bpmp-private.h"
2016-08-19 20:05:35 +03:00
# define MSG_ACK BIT(0)
# define MSG_RING BIT(1)
2018-10-22 16:19:38 +03:00
# define TAG_SZ 32
2016-08-19 20:05:35 +03:00
static inline struct tegra_bpmp *
mbox_client_to_bpmp ( struct mbox_client * client )
{
return container_of ( client , struct tegra_bpmp , mbox . client ) ;
}
2019-01-24 20:03:53 +03:00
static inline const struct tegra_bpmp_ops *
channel_to_ops ( struct tegra_bpmp_channel * channel )
{
struct tegra_bpmp * bpmp = channel - > bpmp ;
return bpmp - > soc - > ops ;
}
2016-08-19 20:05:35 +03:00
struct tegra_bpmp * tegra_bpmp_get ( struct device * dev )
{
struct platform_device * pdev ;
struct tegra_bpmp * bpmp ;
struct device_node * np ;
np = of_parse_phandle ( dev - > of_node , " nvidia,bpmp " , 0 ) ;
if ( ! np )
return ERR_PTR ( - ENOENT ) ;
pdev = of_find_device_by_node ( np ) ;
if ( ! pdev ) {
bpmp = ERR_PTR ( - ENODEV ) ;
goto put ;
}
bpmp = platform_get_drvdata ( pdev ) ;
if ( ! bpmp ) {
bpmp = ERR_PTR ( - EPROBE_DEFER ) ;
put_device ( & pdev - > dev ) ;
goto put ;
}
put :
of_node_put ( np ) ;
return bpmp ;
}
EXPORT_SYMBOL_GPL ( tegra_bpmp_get ) ;
void tegra_bpmp_put ( struct tegra_bpmp * bpmp )
{
if ( bpmp )
put_device ( bpmp - > dev ) ;
}
EXPORT_SYMBOL_GPL ( tegra_bpmp_put ) ;
static int
tegra_bpmp_channel_get_thread_index ( struct tegra_bpmp_channel * channel )
{
struct tegra_bpmp * bpmp = channel - > bpmp ;
2018-02-20 14:58:06 +03:00
unsigned int count ;
2016-08-19 20:05:35 +03:00
int index ;
count = bpmp - > soc - > channels . thread . count ;
2018-02-20 14:58:06 +03:00
index = channel - channel - > bpmp - > threaded_channels ;
if ( index < 0 | | index > = count )
2016-08-19 20:05:35 +03:00
return - EINVAL ;
2018-02-20 14:58:06 +03:00
return index ;
2016-08-19 20:05:35 +03:00
}
static bool tegra_bpmp_message_valid ( const struct tegra_bpmp_message * msg )
{
return ( msg - > tx . size < = MSG_DATA_MIN_SZ ) & &
( msg - > rx . size < = MSG_DATA_MIN_SZ ) & &
( msg - > tx . size = = 0 | | msg - > tx . data ) & &
( msg - > rx . size = = 0 | | msg - > rx . data ) ;
}
2019-01-24 20:03:52 +03:00
static bool tegra_bpmp_is_response_ready ( struct tegra_bpmp_channel * channel )
2016-08-19 20:05:35 +03:00
{
2019-01-24 20:03:53 +03:00
const struct tegra_bpmp_ops * ops = channel_to_ops ( channel ) ;
2016-08-19 20:05:35 +03:00
2019-01-24 20:03:53 +03:00
return ops - > is_response_ready ( channel ) ;
2016-08-19 20:05:35 +03:00
}
2019-01-24 20:03:52 +03:00
static bool tegra_bpmp_is_request_ready ( struct tegra_bpmp_channel * channel )
{
2019-01-24 20:03:53 +03:00
const struct tegra_bpmp_ops * ops = channel_to_ops ( channel ) ;
return ops - > is_request_ready ( channel ) ;
2019-01-24 20:03:52 +03:00
}
static int tegra_bpmp_wait_response ( struct tegra_bpmp_channel * channel )
2016-08-19 20:05:35 +03:00
{
unsigned long timeout = channel - > bpmp - > soc - > channels . cpu_tx . timeout ;
ktime_t end ;
end = ktime_add_us ( ktime_get ( ) , timeout ) ;
do {
2019-01-24 20:03:52 +03:00
if ( tegra_bpmp_is_response_ready ( channel ) )
2016-08-19 20:05:35 +03:00
return 0 ;
} while ( ktime_before ( ktime_get ( ) , end ) ) ;
return - ETIMEDOUT ;
}
2019-01-24 20:03:52 +03:00
static int tegra_bpmp_ack_response ( struct tegra_bpmp_channel * channel )
{
2019-01-24 20:03:53 +03:00
const struct tegra_bpmp_ops * ops = channel_to_ops ( channel ) ;
return ops - > ack_response ( channel ) ;
2019-01-24 20:03:52 +03:00
}
static int tegra_bpmp_ack_request ( struct tegra_bpmp_channel * channel )
{
2019-01-24 20:03:53 +03:00
const struct tegra_bpmp_ops * ops = channel_to_ops ( channel ) ;
return ops - > ack_request ( channel ) ;
2019-01-24 20:03:52 +03:00
}
static bool
tegra_bpmp_is_request_channel_free ( struct tegra_bpmp_channel * channel )
2016-08-19 20:05:35 +03:00
{
2019-01-24 20:03:53 +03:00
const struct tegra_bpmp_ops * ops = channel_to_ops ( channel ) ;
2016-08-19 20:05:35 +03:00
2019-01-24 20:03:53 +03:00
return ops - > is_request_channel_free ( channel ) ;
2016-08-19 20:05:35 +03:00
}
2019-01-24 20:03:52 +03:00
static bool
tegra_bpmp_is_response_channel_free ( struct tegra_bpmp_channel * channel )
{
2019-01-24 20:03:53 +03:00
const struct tegra_bpmp_ops * ops = channel_to_ops ( channel ) ;
return ops - > is_response_channel_free ( channel ) ;
2019-01-24 20:03:52 +03:00
}
static int
tegra_bpmp_wait_request_channel_free ( struct tegra_bpmp_channel * channel )
2016-08-19 20:05:35 +03:00
{
unsigned long timeout = channel - > bpmp - > soc - > channels . cpu_tx . timeout ;
ktime_t start , now ;
start = ns_to_ktime ( local_clock ( ) ) ;
do {
2019-01-24 20:03:52 +03:00
if ( tegra_bpmp_is_request_channel_free ( channel ) )
2016-08-19 20:05:35 +03:00
return 0 ;
now = ns_to_ktime ( local_clock ( ) ) ;
} while ( ktime_us_delta ( now , start ) < timeout ) ;
return - ETIMEDOUT ;
}
2019-01-24 20:03:52 +03:00
static int tegra_bpmp_post_request ( struct tegra_bpmp_channel * channel )
{
2019-01-24 20:03:53 +03:00
const struct tegra_bpmp_ops * ops = channel_to_ops ( channel ) ;
return ops - > post_request ( channel ) ;
2019-01-24 20:03:52 +03:00
}
static int tegra_bpmp_post_response ( struct tegra_bpmp_channel * channel )
{
2019-01-24 20:03:53 +03:00
const struct tegra_bpmp_ops * ops = channel_to_ops ( channel ) ;
return ops - > post_response ( channel ) ;
2019-01-24 20:03:52 +03:00
}
static int tegra_bpmp_ring_doorbell ( struct tegra_bpmp * bpmp )
{
2019-01-24 20:03:53 +03:00
return bpmp - > soc - > ops - > ring_doorbell ( bpmp ) ;
2019-01-24 20:03:52 +03:00
}
2016-08-19 20:05:35 +03:00
static ssize_t __tegra_bpmp_channel_read ( struct tegra_bpmp_channel * channel ,
2017-09-07 12:31:01 +03:00
void * data , size_t size , int * ret )
2016-08-19 20:05:35 +03:00
{
2017-09-07 12:31:01 +03:00
int err ;
2016-08-19 20:05:35 +03:00
if ( data & & size > 0 )
memcpy ( data , channel - > ib - > data , size ) ;
2019-01-24 20:03:52 +03:00
err = tegra_bpmp_ack_response ( channel ) ;
2017-09-07 12:31:01 +03:00
if ( err < 0 )
return err ;
* ret = channel - > ib - > code ;
return 0 ;
2016-08-19 20:05:35 +03:00
}
static ssize_t tegra_bpmp_channel_read ( struct tegra_bpmp_channel * channel ,
2017-09-07 12:31:01 +03:00
void * data , size_t size , int * ret )
2016-08-19 20:05:35 +03:00
{
struct tegra_bpmp * bpmp = channel - > bpmp ;
unsigned long flags ;
ssize_t err ;
int index ;
index = tegra_bpmp_channel_get_thread_index ( channel ) ;
2017-05-05 08:37:23 +03:00
if ( index < 0 ) {
err = index ;
goto unlock ;
}
2016-08-19 20:05:35 +03:00
spin_lock_irqsave ( & bpmp - > lock , flags ) ;
2017-09-07 12:31:01 +03:00
err = __tegra_bpmp_channel_read ( channel , data , size , ret ) ;
2016-08-19 20:05:35 +03:00
clear_bit ( index , bpmp - > threaded . allocated ) ;
spin_unlock_irqrestore ( & bpmp - > lock , flags ) ;
2017-05-05 08:37:23 +03:00
unlock :
2016-08-19 20:05:35 +03:00
up ( & bpmp - > threaded . lock ) ;
return err ;
}
static ssize_t __tegra_bpmp_channel_write ( struct tegra_bpmp_channel * channel ,
unsigned int mrq , unsigned long flags ,
const void * data , size_t size )
{
channel - > ob - > code = mrq ;
channel - > ob - > flags = flags ;
if ( data & & size > 0 )
memcpy ( channel - > ob - > data , data , size ) ;
2019-01-24 20:03:52 +03:00
return tegra_bpmp_post_request ( channel ) ;
2016-08-19 20:05:35 +03:00
}
static struct tegra_bpmp_channel *
tegra_bpmp_write_threaded ( struct tegra_bpmp * bpmp , unsigned int mrq ,
const void * data , size_t size )
{
unsigned long timeout = bpmp - > soc - > channels . thread . timeout ;
unsigned int count = bpmp - > soc - > channels . thread . count ;
struct tegra_bpmp_channel * channel ;
unsigned long flags ;
unsigned int index ;
int err ;
err = down_timeout ( & bpmp - > threaded . lock , usecs_to_jiffies ( timeout ) ) ;
if ( err < 0 )
return ERR_PTR ( err ) ;
spin_lock_irqsave ( & bpmp - > lock , flags ) ;
index = find_first_zero_bit ( bpmp - > threaded . allocated , count ) ;
if ( index = = count ) {
2017-05-05 08:37:23 +03:00
err = - EBUSY ;
2016-08-19 20:05:35 +03:00
goto unlock ;
}
2018-02-20 14:58:06 +03:00
channel = & bpmp - > threaded_channels [ index ] ;
2016-08-19 20:05:35 +03:00
2019-01-24 20:03:52 +03:00
if ( ! tegra_bpmp_is_request_channel_free ( channel ) ) {
2017-05-05 08:37:23 +03:00
err = - EBUSY ;
2016-08-19 20:05:35 +03:00
goto unlock ;
}
set_bit ( index , bpmp - > threaded . allocated ) ;
err = __tegra_bpmp_channel_write ( channel , mrq , MSG_ACK | MSG_RING ,
data , size ) ;
2017-05-05 08:37:23 +03:00
if ( err < 0 )
goto clear_allocated ;
2016-08-19 20:05:35 +03:00
set_bit ( index , bpmp - > threaded . busy ) ;
spin_unlock_irqrestore ( & bpmp - > lock , flags ) ;
return channel ;
2017-05-05 08:37:23 +03:00
clear_allocated :
clear_bit ( index , bpmp - > threaded . allocated ) ;
unlock :
spin_unlock_irqrestore ( & bpmp - > lock , flags ) ;
up ( & bpmp - > threaded . lock ) ;
return ERR_PTR ( err ) ;
2016-08-19 20:05:35 +03:00
}
static ssize_t tegra_bpmp_channel_write ( struct tegra_bpmp_channel * channel ,
unsigned int mrq , unsigned long flags ,
const void * data , size_t size )
{
int err ;
2019-01-24 20:03:52 +03:00
err = tegra_bpmp_wait_request_channel_free ( channel ) ;
2016-08-19 20:05:35 +03:00
if ( err < 0 )
return err ;
return __tegra_bpmp_channel_write ( channel , mrq , flags , data , size ) ;
}
int tegra_bpmp_transfer_atomic ( struct tegra_bpmp * bpmp ,
struct tegra_bpmp_message * msg )
{
struct tegra_bpmp_channel * channel ;
int err ;
if ( WARN_ON ( ! irqs_disabled ( ) ) )
return - EPERM ;
if ( ! tegra_bpmp_message_valid ( msg ) )
return - EINVAL ;
2018-02-20 14:58:06 +03:00
channel = bpmp - > tx_channel ;
spin_lock ( & bpmp - > atomic_tx_lock ) ;
2016-08-19 20:05:35 +03:00
err = tegra_bpmp_channel_write ( channel , msg - > mrq , MSG_ACK ,
msg - > tx . data , msg - > tx . size ) ;
2018-02-20 14:58:06 +03:00
if ( err < 0 ) {
spin_unlock ( & bpmp - > atomic_tx_lock ) ;
2016-08-19 20:05:35 +03:00
return err ;
2018-02-20 14:58:06 +03:00
}
spin_unlock ( & bpmp - > atomic_tx_lock ) ;
2016-08-19 20:05:35 +03:00
2019-01-24 20:03:52 +03:00
err = tegra_bpmp_ring_doorbell ( bpmp ) ;
2016-08-19 20:05:35 +03:00
if ( err < 0 )
return err ;
2019-01-24 20:03:52 +03:00
err = tegra_bpmp_wait_response ( channel ) ;
2016-08-19 20:05:35 +03:00
if ( err < 0 )
return err ;
2017-09-07 12:31:01 +03:00
return __tegra_bpmp_channel_read ( channel , msg - > rx . data , msg - > rx . size ,
& msg - > rx . ret ) ;
2016-08-19 20:05:35 +03:00
}
EXPORT_SYMBOL_GPL ( tegra_bpmp_transfer_atomic ) ;
int tegra_bpmp_transfer ( struct tegra_bpmp * bpmp ,
struct tegra_bpmp_message * msg )
{
struct tegra_bpmp_channel * channel ;
unsigned long timeout ;
int err ;
if ( WARN_ON ( irqs_disabled ( ) ) )
return - EPERM ;
if ( ! tegra_bpmp_message_valid ( msg ) )
return - EINVAL ;
channel = tegra_bpmp_write_threaded ( bpmp , msg - > mrq , msg - > tx . data ,
msg - > tx . size ) ;
if ( IS_ERR ( channel ) )
return PTR_ERR ( channel ) ;
2019-01-24 20:03:52 +03:00
err = tegra_bpmp_ring_doorbell ( bpmp ) ;
2016-08-19 20:05:35 +03:00
if ( err < 0 )
return err ;
timeout = usecs_to_jiffies ( bpmp - > soc - > channels . thread . timeout ) ;
err = wait_for_completion_timeout ( & channel - > completion , timeout ) ;
if ( err = = 0 )
return - ETIMEDOUT ;
2017-09-07 12:31:01 +03:00
return tegra_bpmp_channel_read ( channel , msg - > rx . data , msg - > rx . size ,
& msg - > rx . ret ) ;
2016-08-19 20:05:35 +03:00
}
EXPORT_SYMBOL_GPL ( tegra_bpmp_transfer ) ;
static struct tegra_bpmp_mrq * tegra_bpmp_find_mrq ( struct tegra_bpmp * bpmp ,
unsigned int mrq )
{
struct tegra_bpmp_mrq * entry ;
list_for_each_entry ( entry , & bpmp - > mrqs , list )
if ( entry - > mrq = = mrq )
return entry ;
return NULL ;
}
2017-07-24 19:29:16 +03:00
void tegra_bpmp_mrq_return ( struct tegra_bpmp_channel * channel , int code ,
const void * data , size_t size )
2016-08-19 20:05:35 +03:00
{
unsigned long flags = channel - > ib - > flags ;
struct tegra_bpmp * bpmp = channel - > bpmp ;
int err ;
if ( WARN_ON ( size > MSG_DATA_MIN_SZ ) )
return ;
2019-01-24 20:03:52 +03:00
err = tegra_bpmp_ack_request ( channel ) ;
2016-08-19 20:05:35 +03:00
if ( WARN_ON ( err < 0 ) )
return ;
if ( ( flags & MSG_ACK ) = = 0 )
return ;
2019-01-24 20:03:52 +03:00
if ( WARN_ON ( ! tegra_bpmp_is_response_channel_free ( channel ) ) )
2016-08-19 20:05:35 +03:00
return ;
2019-01-24 20:03:52 +03:00
channel - > ob - > code = code ;
2016-08-19 20:05:35 +03:00
if ( data & & size > 0 )
2019-01-24 20:03:52 +03:00
memcpy ( channel - > ob - > data , data , size ) ;
2016-08-19 20:05:35 +03:00
2019-01-24 20:03:52 +03:00
err = tegra_bpmp_post_response ( channel ) ;
2016-08-19 20:05:35 +03:00
if ( WARN_ON ( err < 0 ) )
return ;
if ( flags & MSG_RING ) {
2019-01-24 20:03:52 +03:00
err = tegra_bpmp_ring_doorbell ( bpmp ) ;
2016-08-19 20:05:35 +03:00
if ( WARN_ON ( err < 0 ) )
return ;
}
}
2017-07-24 19:29:16 +03:00
EXPORT_SYMBOL_GPL ( tegra_bpmp_mrq_return ) ;
2016-08-19 20:05:35 +03:00
static void tegra_bpmp_handle_mrq ( struct tegra_bpmp * bpmp ,
unsigned int mrq ,
struct tegra_bpmp_channel * channel )
{
struct tegra_bpmp_mrq * entry ;
u32 zero = 0 ;
spin_lock ( & bpmp - > lock ) ;
entry = tegra_bpmp_find_mrq ( bpmp , mrq ) ;
if ( ! entry ) {
spin_unlock ( & bpmp - > lock ) ;
tegra_bpmp_mrq_return ( channel , - EINVAL , & zero , sizeof ( zero ) ) ;
return ;
}
entry - > handler ( mrq , channel , entry - > data ) ;
spin_unlock ( & bpmp - > lock ) ;
}
int tegra_bpmp_request_mrq ( struct tegra_bpmp * bpmp , unsigned int mrq ,
tegra_bpmp_mrq_handler_t handler , void * data )
{
struct tegra_bpmp_mrq * entry ;
unsigned long flags ;
if ( ! handler )
return - EINVAL ;
entry = devm_kzalloc ( bpmp - > dev , sizeof ( * entry ) , GFP_KERNEL ) ;
if ( ! entry )
return - ENOMEM ;
spin_lock_irqsave ( & bpmp - > lock , flags ) ;
entry - > mrq = mrq ;
entry - > handler = handler ;
entry - > data = data ;
list_add ( & entry - > list , & bpmp - > mrqs ) ;
spin_unlock_irqrestore ( & bpmp - > lock , flags ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( tegra_bpmp_request_mrq ) ;
void tegra_bpmp_free_mrq ( struct tegra_bpmp * bpmp , unsigned int mrq , void * data )
{
struct tegra_bpmp_mrq * entry ;
unsigned long flags ;
spin_lock_irqsave ( & bpmp - > lock , flags ) ;
entry = tegra_bpmp_find_mrq ( bpmp , mrq ) ;
if ( ! entry )
goto unlock ;
list_del ( & entry - > list ) ;
devm_kfree ( bpmp - > dev , entry ) ;
unlock :
spin_unlock_irqrestore ( & bpmp - > lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( tegra_bpmp_free_mrq ) ;
2018-10-22 16:19:36 +03:00
bool tegra_bpmp_mrq_is_supported ( struct tegra_bpmp * bpmp , unsigned int mrq )
{
struct mrq_query_abi_request req = { . mrq = cpu_to_le32 ( mrq ) } ;
struct mrq_query_abi_response resp ;
struct tegra_bpmp_message msg = {
. mrq = MRQ_QUERY_ABI ,
. tx = {
. data = & req ,
. size = sizeof ( req ) ,
} ,
. rx = {
. data = & resp ,
. size = sizeof ( resp ) ,
} ,
} ;
2020-07-12 13:01:15 +03:00
int err ;
2018-10-22 16:19:36 +03:00
2020-07-12 13:01:15 +03:00
err = tegra_bpmp_transfer ( bpmp , & msg ) ;
if ( err | | msg . rx . ret )
2018-10-22 16:19:36 +03:00
return false ;
return resp . status = = 0 ;
}
EXPORT_SYMBOL_GPL ( tegra_bpmp_mrq_is_supported ) ;
2016-08-19 20:05:35 +03:00
static void tegra_bpmp_mrq_handle_ping ( unsigned int mrq ,
struct tegra_bpmp_channel * channel ,
void * data )
{
struct mrq_ping_request * request ;
struct mrq_ping_response response ;
request = ( struct mrq_ping_request * ) channel - > ib - > data ;
memset ( & response , 0 , sizeof ( response ) ) ;
response . reply = request - > challenge < < 1 ;
tegra_bpmp_mrq_return ( channel , 0 , & response , sizeof ( response ) ) ;
}
static int tegra_bpmp_ping ( struct tegra_bpmp * bpmp )
{
struct mrq_ping_response response ;
struct mrq_ping_request request ;
struct tegra_bpmp_message msg ;
unsigned long flags ;
ktime_t start , end ;
int err ;
memset ( & request , 0 , sizeof ( request ) ) ;
request . challenge = 1 ;
memset ( & response , 0 , sizeof ( response ) ) ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . mrq = MRQ_PING ;
msg . tx . data = & request ;
msg . tx . size = sizeof ( request ) ;
msg . rx . data = & response ;
msg . rx . size = sizeof ( response ) ;
local_irq_save ( flags ) ;
start = ktime_get ( ) ;
err = tegra_bpmp_transfer_atomic ( bpmp , & msg ) ;
end = ktime_get ( ) ;
local_irq_restore ( flags ) ;
if ( ! err )
dev_dbg ( bpmp - > dev ,
" ping ok: challenge: %u, response: %u, time: %lld \n " ,
request . challenge , response . reply ,
ktime_to_us ( ktime_sub ( end , start ) ) ) ;
return err ;
}
2018-10-22 16:19:40 +03:00
/* deprecated version of tag query */
static int tegra_bpmp_get_firmware_tag_old ( struct tegra_bpmp * bpmp , char * tag ,
size_t size )
2016-08-19 20:05:35 +03:00
{
struct mrq_query_tag_request request ;
struct tegra_bpmp_message msg ;
unsigned long flags ;
dma_addr_t phys ;
void * virt ;
int err ;
2018-10-22 16:19:38 +03:00
if ( size ! = TAG_SZ )
return - EINVAL ;
virt = dma_alloc_coherent ( bpmp - > dev , TAG_SZ , & phys ,
2016-08-19 20:05:35 +03:00
GFP_KERNEL | GFP_DMA32 ) ;
if ( ! virt )
return - ENOMEM ;
memset ( & request , 0 , sizeof ( request ) ) ;
request . addr = phys ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . mrq = MRQ_QUERY_TAG ;
msg . tx . data = & request ;
msg . tx . size = sizeof ( request ) ;
local_irq_save ( flags ) ;
err = tegra_bpmp_transfer_atomic ( bpmp , & msg ) ;
local_irq_restore ( flags ) ;
if ( err = = 0 )
2018-10-22 16:19:38 +03:00
memcpy ( tag , virt , TAG_SZ ) ;
2016-08-19 20:05:35 +03:00
2018-10-22 16:19:38 +03:00
dma_free_coherent ( bpmp - > dev , TAG_SZ , virt , phys ) ;
2016-08-19 20:05:35 +03:00
return err ;
}
2018-10-22 16:19:40 +03:00
static int tegra_bpmp_get_firmware_tag ( struct tegra_bpmp * bpmp , char * tag ,
size_t size )
{
if ( tegra_bpmp_mrq_is_supported ( bpmp , MRQ_QUERY_FW_TAG ) ) {
struct mrq_query_fw_tag_response resp ;
struct tegra_bpmp_message msg = {
. mrq = MRQ_QUERY_FW_TAG ,
. rx = {
. data = & resp ,
. size = sizeof ( resp ) ,
} ,
} ;
int err ;
if ( size ! = sizeof ( resp . tag ) )
return - EINVAL ;
err = tegra_bpmp_transfer ( bpmp , & msg ) ;
if ( err )
return err ;
if ( msg . rx . ret < 0 )
return - EINVAL ;
memcpy ( tag , resp . tag , sizeof ( resp . tag ) ) ;
return 0 ;
}
return tegra_bpmp_get_firmware_tag_old ( bpmp , tag , size ) ;
}
2016-08-19 20:05:35 +03:00
static void tegra_bpmp_channel_signal ( struct tegra_bpmp_channel * channel )
{
unsigned long flags = channel - > ob - > flags ;
if ( ( flags & MSG_RING ) = = 0 )
return ;
complete ( & channel - > completion ) ;
}
2019-01-24 20:03:53 +03:00
void tegra_bpmp_handle_rx ( struct tegra_bpmp * bpmp )
2016-08-19 20:05:35 +03:00
{
struct tegra_bpmp_channel * channel ;
unsigned int i , count ;
unsigned long * busy ;
2018-02-20 14:58:06 +03:00
channel = bpmp - > rx_channel ;
2016-08-19 20:05:35 +03:00
count = bpmp - > soc - > channels . thread . count ;
busy = bpmp - > threaded . busy ;
2019-01-24 20:03:52 +03:00
if ( tegra_bpmp_is_request_ready ( channel ) )
2016-08-19 20:05:35 +03:00
tegra_bpmp_handle_mrq ( bpmp , channel - > ib - > code , channel ) ;
spin_lock ( & bpmp - > lock ) ;
for_each_set_bit ( i , busy , count ) {
struct tegra_bpmp_channel * channel ;
2018-02-20 14:58:06 +03:00
channel = & bpmp - > threaded_channels [ i ] ;
2016-08-19 20:05:35 +03:00
2019-01-24 20:03:52 +03:00
if ( tegra_bpmp_is_response_ready ( channel ) ) {
2016-08-19 20:05:35 +03:00
tegra_bpmp_channel_signal ( channel ) ;
clear_bit ( i , busy ) ;
}
}
spin_unlock ( & bpmp - > lock ) ;
}
static int tegra_bpmp_probe ( struct platform_device * pdev )
{
struct tegra_bpmp * bpmp ;
2018-10-22 16:19:38 +03:00
char tag [ TAG_SZ ] ;
2016-08-19 20:05:35 +03:00
size_t size ;
int err ;
bpmp = devm_kzalloc ( & pdev - > dev , sizeof ( * bpmp ) , GFP_KERNEL ) ;
if ( ! bpmp )
return - ENOMEM ;
bpmp - > soc = of_device_get_match_data ( & pdev - > dev ) ;
bpmp - > dev = & pdev - > dev ;
INIT_LIST_HEAD ( & bpmp - > mrqs ) ;
spin_lock_init ( & bpmp - > lock ) ;
bpmp - > threaded . count = bpmp - > soc - > channels . thread . count ;
sema_init ( & bpmp - > threaded . lock , bpmp - > threaded . count ) ;
size = BITS_TO_LONGS ( bpmp - > threaded . count ) * sizeof ( long ) ;
bpmp - > threaded . allocated = devm_kzalloc ( & pdev - > dev , size , GFP_KERNEL ) ;
2019-01-24 20:03:53 +03:00
if ( ! bpmp - > threaded . allocated )
return - ENOMEM ;
2016-08-19 20:05:35 +03:00
bpmp - > threaded . busy = devm_kzalloc ( & pdev - > dev , size , GFP_KERNEL ) ;
2019-01-24 20:03:53 +03:00
if ( ! bpmp - > threaded . busy )
return - ENOMEM ;
2016-08-19 20:05:35 +03:00
2018-02-20 14:58:06 +03:00
spin_lock_init ( & bpmp - > atomic_tx_lock ) ;
bpmp - > tx_channel = devm_kzalloc ( & pdev - > dev , sizeof ( * bpmp - > tx_channel ) ,
GFP_KERNEL ) ;
2019-01-24 20:03:53 +03:00
if ( ! bpmp - > tx_channel )
return - ENOMEM ;
2016-08-19 20:05:35 +03:00
2018-02-20 14:58:06 +03:00
bpmp - > rx_channel = devm_kzalloc ( & pdev - > dev , sizeof ( * bpmp - > rx_channel ) ,
GFP_KERNEL ) ;
2019-01-24 20:03:53 +03:00
if ( ! bpmp - > rx_channel )
return - ENOMEM ;
2016-08-19 20:05:35 +03:00
2018-02-20 14:58:06 +03:00
bpmp - > threaded_channels = devm_kcalloc ( & pdev - > dev , bpmp - > threaded . count ,
sizeof ( * bpmp - > threaded_channels ) ,
GFP_KERNEL ) ;
2019-01-24 20:03:53 +03:00
if ( ! bpmp - > threaded_channels )
return - ENOMEM ;
2018-02-20 14:58:06 +03:00
2019-01-24 20:03:53 +03:00
err = bpmp - > soc - > ops - > init ( bpmp ) ;
2018-02-20 14:58:06 +03:00
if ( err < 0 )
2019-01-24 20:03:53 +03:00
return err ;
2016-08-19 20:05:35 +03:00
err = tegra_bpmp_request_mrq ( bpmp , MRQ_PING ,
tegra_bpmp_mrq_handle_ping , bpmp ) ;
if ( err < 0 )
2019-01-24 20:03:53 +03:00
goto deinit ;
2016-08-19 20:05:35 +03:00
err = tegra_bpmp_ping ( bpmp ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to ping BPMP: %d \n " , err ) ;
goto free_mrq ;
}
2018-10-22 16:19:38 +03:00
err = tegra_bpmp_get_firmware_tag ( bpmp , tag , sizeof ( tag ) ) ;
2016-08-19 20:05:35 +03:00
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to get firmware tag: %d \n " , err ) ;
goto free_mrq ;
}
2018-10-22 16:19:38 +03:00
dev_info ( & pdev - > dev , " firmware: %.*s \n " , ( int ) sizeof ( tag ) , tag ) ;
2016-08-19 20:05:35 +03:00
2017-08-17 12:49:11 +03:00
platform_set_drvdata ( pdev , bpmp ) ;
2016-08-19 20:05:35 +03:00
err = of_platform_default_populate ( pdev - > dev . of_node , NULL , & pdev - > dev ) ;
if ( err < 0 )
goto free_mrq ;
2019-01-24 20:03:54 +03:00
if ( of_find_property ( pdev - > dev . of_node , " #clock-cells " , NULL ) ) {
err = tegra_bpmp_init_clocks ( bpmp ) ;
if ( err < 0 )
goto free_mrq ;
}
2016-08-19 20:05:35 +03:00
2019-01-24 20:03:54 +03:00
if ( of_find_property ( pdev - > dev . of_node , " #reset-cells " , NULL ) ) {
err = tegra_bpmp_init_resets ( bpmp ) ;
if ( err < 0 )
goto free_mrq ;
}
2016-08-19 20:05:35 +03:00
2019-01-24 20:03:54 +03:00
if ( of_find_property ( pdev - > dev . of_node , " #power-domain-cells " , NULL ) ) {
err = tegra_bpmp_init_powergates ( bpmp ) ;
if ( err < 0 )
goto free_mrq ;
}
2017-03-29 19:34:52 +03:00
2017-10-03 09:12:13 +03:00
err = tegra_bpmp_init_debugfs ( bpmp ) ;
if ( err < 0 )
dev_err ( & pdev - > dev , " debugfs initialization failed: %d \n " , err ) ;
2016-08-19 20:05:35 +03:00
return 0 ;
free_mrq :
tegra_bpmp_free_mrq ( bpmp , MRQ_PING , bpmp ) ;
2019-01-24 20:03:53 +03:00
deinit :
if ( bpmp - > soc - > ops - > deinit )
bpmp - > soc - > ops - > deinit ( bpmp ) ;
2018-02-20 14:58:06 +03:00
2016-08-19 20:05:35 +03:00
return err ;
}
2018-09-21 13:08:56 +03:00
static int __maybe_unused tegra_bpmp_resume ( struct device * dev )
{
struct tegra_bpmp * bpmp = dev_get_drvdata ( dev ) ;
2019-01-24 20:03:53 +03:00
if ( bpmp - > soc - > ops - > resume )
return bpmp - > soc - > ops - > resume ( bpmp ) ;
else
return 0 ;
2018-09-21 13:08:56 +03:00
}
2019-06-14 15:31:39 +03:00
static const struct dev_pm_ops tegra_bpmp_pm_ops = {
2019-08-02 09:17:27 +03:00
. resume_noirq = tegra_bpmp_resume ,
2019-06-14 15:31:39 +03:00
} ;
2018-09-21 13:08:56 +03:00
2019-02-07 14:50:06 +03:00
# if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \
IS_ENABLED ( CONFIG_ARCH_TEGRA_194_SOC )
2016-08-19 20:05:35 +03:00
static const struct tegra_bpmp_soc tegra186_soc = {
. channels = {
. cpu_tx = {
2018-02-20 14:58:06 +03:00
. offset = 3 ,
2016-08-19 20:05:35 +03:00
. timeout = 60 * USEC_PER_SEC ,
} ,
. thread = {
2018-02-20 14:58:06 +03:00
. offset = 0 ,
. count = 3 ,
2016-08-19 20:05:35 +03:00
. timeout = 600 * USEC_PER_SEC ,
} ,
. cpu_rx = {
. offset = 13 ,
. timeout = 0 ,
} ,
} ,
2019-01-24 20:03:53 +03:00
. ops = & tegra186_bpmp_ops ,
2016-08-19 20:05:35 +03:00
. num_resets = 193 ,
} ;
2019-02-07 14:50:05 +03:00
# endif
2016-08-19 20:05:35 +03:00
2019-02-07 14:50:05 +03:00
# if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
2019-01-24 20:03:54 +03:00
static const struct tegra_bpmp_soc tegra210_soc = {
. channels = {
. cpu_tx = {
. offset = 0 ,
. count = 1 ,
. timeout = 60 * USEC_PER_SEC ,
} ,
. thread = {
. offset = 4 ,
. count = 1 ,
. timeout = 600 * USEC_PER_SEC ,
} ,
. cpu_rx = {
. offset = 8 ,
. count = 1 ,
. timeout = 0 ,
} ,
} ,
. ops = & tegra210_bpmp_ops ,
} ;
2019-02-07 14:50:05 +03:00
# endif
2019-01-24 20:03:54 +03:00
2016-08-19 20:05:35 +03:00
static const struct of_device_id tegra_bpmp_match [ ] = {
2019-02-07 14:50:06 +03:00
# if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \
IS_ENABLED ( CONFIG_ARCH_TEGRA_194_SOC )
2016-08-19 20:05:35 +03:00
{ . compatible = " nvidia,tegra186-bpmp " , . data = & tegra186_soc } ,
2019-02-07 14:50:05 +03:00
# endif
# if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
2019-01-24 20:03:54 +03:00
{ . compatible = " nvidia,tegra210-bpmp " , . data = & tegra210_soc } ,
2019-02-07 14:50:05 +03:00
# endif
2016-08-19 20:05:35 +03:00
{ }
} ;
static struct platform_driver tegra_bpmp_driver = {
. driver = {
. name = " tegra-bpmp " ,
. of_match_table = tegra_bpmp_match ,
2018-09-21 13:08:56 +03:00
. pm = & tegra_bpmp_pm_ops ,
2020-04-22 01:00:53 +03:00
. suppress_bind_attrs = true ,
2016-08-19 20:05:35 +03:00
} ,
. probe = tegra_bpmp_probe ,
} ;
2020-04-22 01:00:53 +03:00
builtin_platform_driver ( tegra_bpmp_driver ) ;