2019-05-29 17:12:41 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2020-01-08 00:04:10 +03:00
/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
2015-03-12 00:28:10 +03:00
* Copyright ( C ) 2015 Linaro Ltd .
*/
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/qcom_scm.h>
2020-01-08 00:04:18 +03:00
# include <linux/arm-smccc.h>
2016-06-04 02:25:25 +03:00
# include <linux/dma-mapping.h>
2015-03-12 00:28:10 +03:00
# include "qcom_scm.h"
static DEFINE_MUTEX ( qcom_scm_lock ) ;
2020-01-08 00:04:20 +03:00
2020-01-08 00:04:21 +03:00
/**
* struct arm_smccc_args
* @ args : The array of values used in registers in smc instruction
*/
struct arm_smccc_args {
unsigned long args [ 8 ] ;
} ;
2020-01-08 00:04:19 +03:00
2015-03-12 00:28:10 +03:00
/**
2020-01-08 00:04:10 +03:00
* struct scm_legacy_command - one SCM command buffer
2015-03-12 00:28:10 +03:00
* @ len : total available memory for command and response
* @ buf_offset : start of command buffer
* @ resp_hdr_offset : start of response buffer
* @ id : command to be executed
2020-01-08 00:04:10 +03:00
* @ buf : buffer returned from scm_legacy_get_command_buffer ( )
2015-03-12 00:28:10 +03:00
*
* An SCM command is laid out in memory as follows :
*
2020-01-08 00:04:10 +03:00
* - - - - - - - - - - - - - - - - - - - < - - - struct scm_legacy_command
2015-03-12 00:28:10 +03:00
* | command header |
2020-01-08 00:04:10 +03:00
* - - - - - - - - - - - - - - - - - - - < - - - scm_legacy_get_command_buffer ( )
2015-03-12 00:28:10 +03:00
* | command buffer |
2020-01-08 00:04:10 +03:00
* - - - - - - - - - - - - - - - - - - - < - - - struct scm_legacy_response and
* | response header | scm_legacy_command_to_response ( )
* - - - - - - - - - - - - - - - - - - - < - - - scm_legacy_get_response_buffer ( )
2015-03-12 00:28:10 +03:00
* | response buffer |
* - - - - - - - - - - - - - - - - - - -
*
* There can be arbitrary padding between the headers and buffers so
2020-01-08 00:04:10 +03:00
* you should always use the appropriate scm_legacy_get_ * _buffer ( ) routines
2015-03-12 00:28:10 +03:00
* to access the buffers in a safe manner .
*/
2020-01-08 00:04:10 +03:00
struct scm_legacy_command {
2015-03-12 00:28:10 +03:00
__le32 len ;
__le32 buf_offset ;
__le32 resp_hdr_offset ;
__le32 id ;
2020-05-09 00:08:05 +03:00
__le32 buf [ ] ;
2015-03-12 00:28:10 +03:00
} ;
/**
2020-01-08 00:04:10 +03:00
* struct scm_legacy_response - one SCM response buffer
2015-03-12 00:28:10 +03:00
* @ len : total available memory for response
2020-01-08 00:04:10 +03:00
* @ buf_offset : start of response data relative to start of scm_legacy_response
2015-03-12 00:28:10 +03:00
* @ is_complete : indicates if the command has finished processing
*/
2020-01-08 00:04:10 +03:00
struct scm_legacy_response {
2015-03-12 00:28:10 +03:00
__le32 len ;
__le32 buf_offset ;
__le32 is_complete ;
} ;
/**
2020-01-08 00:04:10 +03:00
* scm_legacy_command_to_response ( ) - Get a pointer to a scm_legacy_response
2015-03-12 00:28:10 +03:00
* @ cmd : command
*
* Returns a pointer to a response for a command .
*/
2020-01-08 00:04:10 +03:00
static inline struct scm_legacy_response * scm_legacy_command_to_response (
const struct scm_legacy_command * cmd )
2015-03-12 00:28:10 +03:00
{
return ( void * ) cmd + le32_to_cpu ( cmd - > resp_hdr_offset ) ;
}
/**
2020-01-08 00:04:10 +03:00
* scm_legacy_get_command_buffer ( ) - Get a pointer to a command buffer
2015-03-12 00:28:10 +03:00
* @ cmd : command
*
* Returns a pointer to the command buffer of a command .
*/
2020-01-08 00:04:10 +03:00
static inline void * scm_legacy_get_command_buffer (
const struct scm_legacy_command * cmd )
2015-03-12 00:28:10 +03:00
{
return ( void * ) cmd - > buf ;
}
/**
2020-01-08 00:04:10 +03:00
* scm_legacy_get_response_buffer ( ) - Get a pointer to a response buffer
2015-03-12 00:28:10 +03:00
* @ rsp : response
*
* Returns a pointer to a response buffer of a response .
*/
2020-01-08 00:04:10 +03:00
static inline void * scm_legacy_get_response_buffer (
const struct scm_legacy_response * rsp )
2015-03-12 00:28:10 +03:00
{
return ( void * ) rsp + le32_to_cpu ( rsp - > buf_offset ) ;
}
2020-01-08 00:04:21 +03:00
static void __scm_legacy_do ( const struct arm_smccc_args * smc ,
struct arm_smccc_res * res )
2015-03-12 00:28:10 +03:00
{
do {
2020-01-08 00:04:21 +03:00
arm_smccc_smc ( smc - > args [ 0 ] , smc - > args [ 1 ] , smc - > args [ 2 ] ,
smc - > args [ 3 ] , smc - > args [ 4 ] , smc - > args [ 5 ] ,
smc - > args [ 6 ] , smc - > args [ 7 ] , res ) ;
} while ( res - > a0 = = QCOM_SCM_INTERRUPTED ) ;
2015-03-12 00:28:10 +03:00
}
/**
2021-02-24 00:45:38 +03:00
* scm_legacy_call ( ) - Sends a command to the SCM and waits for the command to
2020-01-08 00:04:20 +03:00
* finish processing .
2022-05-19 10:34:10 +03:00
* @ dev : device
* @ desc : descriptor structure containing arguments and return values
* @ res : results from SMC call
2015-03-12 00:28:10 +03:00
*
* A note on cache maintenance :
* Note that any buffers that are expected to be accessed by the secure world
* must be flushed before invoking qcom_scm_call and invalidated in the cache
* immediately after qcom_scm_call returns . Cache maintenance on the command
* and response buffers is taken care of by qcom_scm_call ; however , callers are
* responsible for any other cached buffers passed over to the secure world .
*/
2020-01-08 00:04:26 +03:00
int scm_legacy_call ( struct device * dev , const struct qcom_scm_desc * desc ,
struct qcom_scm_res * res )
2015-03-12 00:28:10 +03:00
{
2020-01-08 00:04:20 +03:00
u8 arglen = desc - > arginfo & 0xf ;
2020-01-08 00:04:21 +03:00
int ret = 0 , context_id ;
2020-01-08 00:04:20 +03:00
unsigned int i ;
2020-01-08 00:04:10 +03:00
struct scm_legacy_command * cmd ;
struct scm_legacy_response * rsp ;
2020-01-08 00:04:21 +03:00
struct arm_smccc_args smc = { 0 } ;
struct arm_smccc_res smc_res ;
2020-01-08 00:04:20 +03:00
const size_t cmd_len = arglen * sizeof ( __le32 ) ;
const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof ( __le32 ) ;
2016-06-04 02:25:25 +03:00
size_t alloc_len = sizeof ( * cmd ) + cmd_len + sizeof ( * rsp ) + resp_len ;
dma_addr_t cmd_phys ;
2020-01-08 00:04:20 +03:00
__le32 * arg_buf ;
const __le32 * res_buf ;
2015-03-12 00:28:10 +03:00
2016-06-04 02:25:25 +03:00
cmd = kzalloc ( PAGE_ALIGN ( alloc_len ) , GFP_KERNEL ) ;
2015-03-12 00:28:10 +03:00
if ( ! cmd )
return - ENOMEM ;
2016-06-04 02:25:25 +03:00
cmd - > len = cpu_to_le32 ( alloc_len ) ;
cmd - > buf_offset = cpu_to_le32 ( sizeof ( * cmd ) ) ;
cmd - > resp_hdr_offset = cpu_to_le32 ( sizeof ( * cmd ) + cmd_len ) ;
2020-01-08 00:04:20 +03:00
cmd - > id = cpu_to_le32 ( SCM_LEGACY_FNID ( desc - > svc , desc - > cmd ) ) ;
2016-06-04 02:25:25 +03:00
2020-01-08 00:04:20 +03:00
arg_buf = scm_legacy_get_command_buffer ( cmd ) ;
for ( i = 0 ; i < arglen ; i + + )
arg_buf [ i ] = cpu_to_le32 ( desc - > args [ i ] ) ;
2015-03-12 00:28:10 +03:00
2020-01-08 00:04:10 +03:00
rsp = scm_legacy_command_to_response ( cmd ) ;
2016-06-04 02:25:25 +03:00
cmd_phys = dma_map_single ( dev , cmd , alloc_len , DMA_TO_DEVICE ) ;
if ( dma_mapping_error ( dev , cmd_phys ) ) {
kfree ( cmd ) ;
return - ENOMEM ;
}
2020-01-08 00:04:21 +03:00
smc . args [ 0 ] = 1 ;
smc . args [ 1 ] = ( unsigned long ) & context_id ;
smc . args [ 2 ] = cmd_phys ;
2015-03-12 00:28:10 +03:00
mutex_lock ( & qcom_scm_lock ) ;
2020-01-08 00:04:21 +03:00
__scm_legacy_do ( & smc , & smc_res ) ;
if ( smc_res . a0 )
ret = qcom_scm_remap_error ( smc_res . a0 ) ;
2015-03-12 00:28:10 +03:00
mutex_unlock ( & qcom_scm_lock ) ;
if ( ret )
goto out ;
do {
2016-06-04 02:25:25 +03:00
dma_sync_single_for_cpu ( dev , cmd_phys + sizeof ( * cmd ) + cmd_len ,
sizeof ( * rsp ) , DMA_FROM_DEVICE ) ;
2015-03-12 00:28:10 +03:00
} while ( ! rsp - > is_complete ) ;
2020-01-08 00:04:20 +03:00
dma_sync_single_for_cpu ( dev , cmd_phys + sizeof ( * cmd ) + cmd_len +
le32_to_cpu ( rsp - > buf_offset ) ,
resp_len , DMA_FROM_DEVICE ) ;
if ( res ) {
res_buf = scm_legacy_get_response_buffer ( rsp ) ;
for ( i = 0 ; i < MAX_QCOM_SCM_RETS ; i + + )
res - > result [ i ] = le32_to_cpu ( res_buf [ i ] ) ;
2016-06-04 02:25:25 +03:00
}
2015-03-12 00:28:10 +03:00
out :
2016-06-04 02:25:25 +03:00
dma_unmap_single ( dev , cmd_phys , alloc_len , DMA_TO_DEVICE ) ;
kfree ( cmd ) ;
2015-03-12 00:28:10 +03:00
return ret ;
}
2020-01-08 00:04:22 +03:00
# define SCM_LEGACY_ATOMIC_N_REG_ARGS 5
# define SCM_LEGACY_ATOMIC_FIRST_REG_IDX 2
2020-01-08 00:04:10 +03:00
# define SCM_LEGACY_CLASS_REGISTER (0x2 << 8)
# define SCM_LEGACY_MASK_IRQS BIT(5)
# define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \
2020-01-08 00:04:19 +03:00
( ( SCM_LEGACY_FNID ( svc , cmd ) < < 12 ) | \
2020-01-08 00:04:10 +03:00
SCM_LEGACY_CLASS_REGISTER | \
SCM_LEGACY_MASK_IRQS | \
2015-03-12 00:28:10 +03:00
( n & 0xf ) )
/**
2021-02-24 00:45:38 +03:00
* scm_legacy_call_atomic ( ) - Send an atomic SCM command with up to 5 arguments
2020-01-08 00:04:22 +03:00
* and 3 return values
2022-05-19 10:34:10 +03:00
* @ unused : device , legacy argument , not used , can be NULL
2020-01-08 00:04:22 +03:00
* @ desc : SCM call descriptor containing arguments
* @ res : SCM call return values
2015-03-12 00:28:10 +03:00
*
* This shall only be used with commands that are guaranteed to be
* uninterruptable , atomic and SMP safe .
*/
2020-01-08 00:04:26 +03:00
int scm_legacy_call_atomic ( struct device * unused ,
const struct qcom_scm_desc * desc ,
struct qcom_scm_res * res )
2015-03-12 00:28:10 +03:00
{
int context_id ;
2020-01-08 00:04:22 +03:00
struct arm_smccc_res smc_res ;
size_t arglen = desc - > arginfo & 0xf ;
2020-01-08 00:04:18 +03:00
2020-01-08 00:04:22 +03:00
BUG_ON ( arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS ) ;
2015-03-12 00:28:10 +03:00
2020-01-08 00:04:22 +03:00
arm_smccc_smc ( SCM_LEGACY_ATOMIC_ID ( desc - > svc , desc - > cmd , arglen ) ,
( unsigned long ) & context_id ,
desc - > args [ 0 ] , desc - > args [ 1 ] , desc - > args [ 2 ] ,
desc - > args [ 3 ] , desc - > args [ 4 ] , 0 , & smc_res ) ;
2015-03-12 00:28:10 +03:00
2020-01-08 00:04:22 +03:00
if ( res ) {
res - > result [ 0 ] = smc_res . a1 ;
res - > result [ 1 ] = smc_res . a2 ;
res - > result [ 2 ] = smc_res . a3 ;
}
2016-06-04 02:25:23 +03:00
2020-01-08 00:04:22 +03:00
return smc_res . a0 ;
2016-06-04 02:25:23 +03:00
}