2020-03-08 21:24:39 +08:00
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface ( SCMI ) Message SMC / HVC
* Transport driver
*
* Copyright 2020 NXP
*/
# include <linux/arm-smccc.h>
# include <linux/device.h>
# include <linux/err.h>
2020-12-22 09:56:03 -05:00
# include <linux/interrupt.h>
2020-03-08 21:24:39 +08:00
# include <linux/mutex.h>
# include <linux/of.h>
# include <linux/of_address.h>
2020-12-22 09:56:03 -05:00
# include <linux/of_irq.h>
2020-03-08 21:24:39 +08:00
# include <linux/slab.h>
# include "common.h"
/**
* struct scmi_smc - Structure representing a SCMI smc transport
*
* @ cinfo : SCMI channel info
* @ shmem : Transmit / Receive shared memory area
2020-07-09 16:31:55 +01:00
* @ shmem_lock : Lock to protect access to Tx / Rx shared memory area
2020-03-08 21:24:39 +08:00
* @ func_id : smc / hvc call function id
2020-12-22 09:56:03 -05:00
* @ irq : Optional ; employed when platforms indicates msg completion by intr .
* @ tx_complete : Optional , employed only when irq is valid .
2020-03-08 21:24:39 +08:00
*/
struct scmi_smc {
struct scmi_chan_info * cinfo ;
struct scmi_shared_mem __iomem * shmem ;
2020-03-27 16:36:51 +00:00
struct mutex shmem_lock ;
2020-03-08 21:24:39 +08:00
u32 func_id ;
2020-12-22 09:56:03 -05:00
int irq ;
struct completion tx_complete ;
2020-03-08 21:24:39 +08:00
} ;
2020-12-22 09:56:03 -05:00
static irqreturn_t smc_msg_done_isr ( int irq , void * data )
{
struct scmi_smc * scmi_info = data ;
complete ( & scmi_info - > tx_complete ) ;
return IRQ_HANDLED ;
}
2020-03-08 21:24:39 +08:00
static bool smc_chan_available ( struct device * dev , int idx )
{
2020-03-27 16:36:53 +00:00
struct device_node * np = of_parse_phandle ( dev - > of_node , " shmem " , 0 ) ;
if ( ! np )
return false ;
of_node_put ( np ) ;
2020-03-08 21:24:39 +08:00
return true ;
}
static int smc_chan_setup ( struct scmi_chan_info * cinfo , struct device * dev ,
bool tx )
{
struct device * cdev = cinfo - > dev ;
struct scmi_smc * scmi_info ;
resource_size_t size ;
struct resource res ;
struct device_node * np ;
u32 func_id ;
2020-12-22 09:56:03 -05:00
int ret , irq ;
2020-03-08 21:24:39 +08:00
if ( ! tx )
return - ENODEV ;
scmi_info = devm_kzalloc ( dev , sizeof ( * scmi_info ) , GFP_KERNEL ) ;
if ( ! scmi_info )
return - ENOMEM ;
np = of_parse_phandle ( cdev - > of_node , " shmem " , 0 ) ;
2021-06-02 08:38:51 +01:00
if ( ! of_device_is_compatible ( np , " arm,scmi-shmem " ) )
return - ENXIO ;
2020-03-08 21:24:39 +08:00
ret = of_address_to_resource ( np , 0 , & res ) ;
of_node_put ( np ) ;
if ( ret ) {
dev_err ( cdev , " failed to get SCMI Tx shared memory \n " ) ;
return ret ;
}
size = resource_size ( & res ) ;
scmi_info - > shmem = devm_ioremap ( dev , res . start , size ) ;
if ( ! scmi_info - > shmem ) {
dev_err ( dev , " failed to ioremap SCMI Tx shared memory \n " ) ;
return - EADDRNOTAVAIL ;
}
ret = of_property_read_u32 ( dev - > of_node , " arm,smc-id " , & func_id ) ;
if ( ret < 0 )
return ret ;
2020-12-22 09:56:03 -05:00
/*
* If there is an interrupt named " a2p " , then the service and
* completion of a message is signaled by an interrupt rather than by
* the return of the SMC call .
*/
irq = of_irq_get_byname ( cdev - > of_node , " a2p " ) ;
if ( irq > 0 ) {
ret = devm_request_irq ( dev , irq , smc_msg_done_isr ,
IRQF_NO_SUSPEND ,
dev_name ( dev ) , scmi_info ) ;
if ( ret ) {
dev_err ( dev , " failed to setup SCMI smc irq \n " ) ;
return ret ;
}
init_completion ( & scmi_info - > tx_complete ) ;
scmi_info - > irq = irq ;
}
2020-03-08 21:24:39 +08:00
scmi_info - > func_id = func_id ;
scmi_info - > cinfo = cinfo ;
2020-03-27 16:36:51 +00:00
mutex_init ( & scmi_info - > shmem_lock ) ;
2020-03-08 21:24:39 +08:00
cinfo - > transport_info = scmi_info ;
return 0 ;
}
static int smc_chan_free ( int id , void * p , void * data )
{
struct scmi_chan_info * cinfo = p ;
struct scmi_smc * scmi_info = cinfo - > transport_info ;
cinfo - > transport_info = NULL ;
scmi_info - > cinfo = NULL ;
scmi_free_channel ( cinfo , data , id ) ;
return 0 ;
}
static int smc_send_message ( struct scmi_chan_info * cinfo ,
struct scmi_xfer * xfer )
{
struct scmi_smc * scmi_info = cinfo - > transport_info ;
struct arm_smccc_res res ;
2020-03-27 16:36:51 +00:00
mutex_lock ( & scmi_info - > shmem_lock ) ;
2020-03-08 21:24:39 +08:00
shmem_tx_prepare ( scmi_info - > shmem , xfer ) ;
2020-12-22 09:56:03 -05:00
if ( scmi_info - > irq )
reinit_completion ( & scmi_info - > tx_complete ) ;
2020-03-08 21:24:39 +08:00
arm_smccc_1_1_invoke ( scmi_info - > func_id , 0 , 0 , 0 , 0 , 0 , 0 , 0 , & res ) ;
2020-12-22 09:56:03 -05:00
if ( scmi_info - > irq )
wait_for_completion ( & scmi_info - > tx_complete ) ;
2021-08-03 14:10:23 +01:00
scmi_rx_callback ( scmi_info - > cinfo ,
shmem_read_header ( scmi_info - > shmem ) , NULL ) ;
2020-03-08 21:24:39 +08:00
2020-03-27 16:36:51 +00:00
mutex_unlock ( & scmi_info - > shmem_lock ) ;
2020-03-08 21:24:39 +08:00
2020-04-17 11:32:32 +01:00
/* Only SMCCC_RET_NOT_SUPPORTED is valid error code */
if ( res . a0 )
return - EOPNOTSUPP ;
return 0 ;
2020-03-08 21:24:39 +08:00
}
static void smc_fetch_response ( struct scmi_chan_info * cinfo ,
struct scmi_xfer * xfer )
{
struct scmi_smc * scmi_info = cinfo - > transport_info ;
shmem_fetch_response ( scmi_info - > shmem , xfer ) ;
}
static bool
smc_poll_done ( struct scmi_chan_info * cinfo , struct scmi_xfer * xfer )
{
struct scmi_smc * scmi_info = cinfo - > transport_info ;
return shmem_poll_done ( scmi_info - > shmem , xfer ) ;
}
2020-09-07 01:04:52 +02:00
static const struct scmi_transport_ops scmi_smc_ops = {
2020-03-08 21:24:39 +08:00
. chan_available = smc_chan_available ,
. chan_setup = smc_chan_setup ,
. chan_free = smc_chan_free ,
. send_message = smc_send_message ,
. fetch_response = smc_fetch_response ,
. poll_done = smc_poll_done ,
} ;
const struct scmi_desc scmi_smc_desc = {
. ops = & scmi_smc_ops ,
. max_rx_timeout_ms = 30 ,
2020-10-08 16:37:21 +02:00
. max_msg = 20 ,
2020-03-08 21:24:39 +08:00
. max_msg_size = 128 ,
} ;