2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2020-01-08 00:04:10 +03:00
/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved.
2015-09-12 00:01:16 +03:00
*/
# include <linux/io.h>
# include <linux/errno.h>
2016-06-04 02:25:26 +03:00
# include <linux/delay.h>
# include <linux/mutex.h>
# include <linux/slab.h>
# include <linux/types.h>
2015-09-12 00:01:16 +03:00
# include <linux/qcom_scm.h>
2016-06-04 02:25:26 +03:00
# include <linux/arm-smccc.h>
# include <linux/dma-mapping.h>
# include "qcom_scm.h"
2020-01-08 00:04:16 +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 ] ;
} ;
2016-06-04 02:25:26 +03:00
static DEFINE_MUTEX ( qcom_scm_lock ) ;
# define QCOM_SCM_EBUSY_WAIT_MS 30
# define QCOM_SCM_EBUSY_MAX_RETRY 20
2020-01-08 00:04:13 +03:00
# define SCM_SMC_N_REG_ARGS 4
# define SCM_SMC_FIRST_EXT_IDX (SCM_SMC_N_REG_ARGS - 1)
# define SCM_SMC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SCM_SMC_N_REG_ARGS + 1)
2020-01-08 00:04:16 +03:00
# define SCM_SMC_FIRST_REG_IDX 2
# define SCM_SMC_LAST_REG_IDX (SCM_SMC_FIRST_REG_IDX + SCM_SMC_N_REG_ARGS - 1)
2016-06-04 02:25:26 +03:00
2020-01-08 00:04:16 +03:00
static void __scm_smc_do_quirk ( const struct arm_smccc_args * smc ,
struct arm_smccc_res * res )
2019-09-20 11:04:27 +03:00
{
2020-01-08 00:04:16 +03:00
unsigned long a0 = smc - > args [ 0 ] ;
2019-09-20 11:04:27 +03:00
struct arm_smccc_quirk quirk = { . id = ARM_SMCCC_QUIRK_QCOM_A6 } ;
quirk . state . a6 = 0 ;
do {
2020-01-08 00:04:16 +03:00
arm_smccc_smc_quirk ( a0 , smc - > args [ 1 ] , smc - > args [ 2 ] ,
smc - > args [ 3 ] , smc - > args [ 4 ] , smc - > args [ 5 ] ,
quirk . state . a6 , smc - > args [ 7 ] , res , & quirk ) ;
2019-09-20 11:04:27 +03:00
if ( res - > a0 = = QCOM_SCM_INTERRUPTED )
2020-01-08 00:04:16 +03:00
a0 = res - > a0 ;
2019-09-20 11:04:27 +03:00
} while ( res - > a0 = = QCOM_SCM_INTERRUPTED ) ;
}
2020-01-08 00:04:16 +03:00
static void __scm_smc_do ( const struct arm_smccc_args * smc ,
struct arm_smccc_res * res , bool atomic )
2019-09-20 11:04:27 +03:00
{
int retry_count = 0 ;
if ( atomic ) {
2020-01-08 00:04:16 +03:00
__scm_smc_do_quirk ( smc , res ) ;
2019-09-20 11:04:27 +03:00
return ;
}
do {
mutex_lock ( & qcom_scm_lock ) ;
2020-01-08 00:04:16 +03:00
__scm_smc_do_quirk ( smc , res ) ;
2019-09-20 11:04:27 +03:00
mutex_unlock ( & qcom_scm_lock ) ;
if ( res - > a0 = = QCOM_SCM_V2_EBUSY ) {
if ( retry_count + + > QCOM_SCM_EBUSY_MAX_RETRY )
break ;
msleep ( QCOM_SCM_EBUSY_WAIT_MS ) ;
}
} while ( res - > a0 = = QCOM_SCM_V2_EBUSY ) ;
}
2021-02-24 00:45:35 +03:00
int __scm_smc_call ( struct device * dev , const struct qcom_scm_desc * desc ,
enum qcom_scm_convention qcom_convention ,
struct qcom_scm_res * res , bool atomic )
2016-06-04 02:25:26 +03:00
{
int arglen = desc - > arginfo & 0xf ;
2019-09-20 11:04:27 +03:00
int i ;
2016-06-04 02:25:26 +03:00
dma_addr_t args_phys = 0 ;
void * args_virt = NULL ;
size_t alloc_len ;
2019-09-20 11:04:27 +03:00
gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL ;
2020-01-08 00:04:16 +03:00
u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL ;
2021-02-24 00:45:35 +03:00
u32 qcom_smccc_convention = ( qcom_convention = = SMC_CONVENTION_ARM_32 ) ?
ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64 ;
2020-01-08 00:04:15 +03:00
struct arm_smccc_res smc_res ;
2020-01-08 00:04:16 +03:00
struct arm_smccc_args smc = { 0 } ;
smc . args [ 0 ] = ARM_SMCCC_CALL_VAL (
smccc_call_type ,
qcom_smccc_convention ,
desc - > owner ,
SCM_SMC_FNID ( desc - > svc , desc - > cmd ) ) ;
smc . args [ 1 ] = desc - > arginfo ;
for ( i = 0 ; i < SCM_SMC_N_REG_ARGS ; i + + )
smc . args [ i + SCM_SMC_FIRST_REG_IDX ] = desc - > args [ i ] ;
2016-06-04 02:25:26 +03:00
2020-01-08 00:04:10 +03:00
if ( unlikely ( arglen > SCM_SMC_N_REG_ARGS ) ) {
alloc_len = SCM_SMC_N_EXT_ARGS * sizeof ( u64 ) ;
2019-09-20 11:04:27 +03:00
args_virt = kzalloc ( PAGE_ALIGN ( alloc_len ) , flag ) ;
2016-06-04 02:25:26 +03:00
if ( ! args_virt )
return - ENOMEM ;
if ( qcom_smccc_convention = = ARM_SMCCC_SMC_32 ) {
__le32 * args = args_virt ;
2020-01-08 00:04:10 +03:00
for ( i = 0 ; i < SCM_SMC_N_EXT_ARGS ; i + + )
2016-06-04 02:25:26 +03:00
args [ i ] = cpu_to_le32 ( desc - > args [ i +
2020-01-08 00:04:10 +03:00
SCM_SMC_FIRST_EXT_IDX ] ) ;
2016-06-04 02:25:26 +03:00
} else {
__le64 * args = args_virt ;
2020-01-08 00:04:10 +03:00
for ( i = 0 ; i < SCM_SMC_N_EXT_ARGS ; i + + )
2016-06-04 02:25:26 +03:00
args [ i ] = cpu_to_le64 ( desc - > args [ i +
2020-01-08 00:04:10 +03:00
SCM_SMC_FIRST_EXT_IDX ] ) ;
2016-06-04 02:25:26 +03:00
}
args_phys = dma_map_single ( dev , args_virt , alloc_len ,
DMA_TO_DEVICE ) ;
if ( dma_mapping_error ( dev , args_phys ) ) {
kfree ( args_virt ) ;
return - ENOMEM ;
}
2020-01-08 00:04:16 +03:00
smc . args [ SCM_SMC_LAST_REG_IDX ] = args_phys ;
2016-06-04 02:25:26 +03:00
}
2020-01-08 00:04:16 +03:00
__scm_smc_do ( & smc , & smc_res , atomic ) ;
2016-06-04 02:25:26 +03:00
if ( args_virt ) {
dma_unmap_single ( dev , args_phys , alloc_len , DMA_TO_DEVICE ) ;
kfree ( args_virt ) ;
}
2020-01-08 00:04:15 +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:26 +03:00
2020-01-08 00:04:15 +03:00
return ( long ) smc_res . a0 ? qcom_scm_remap_error ( smc_res . a0 ) : 0 ;
2021-02-24 00:45:35 +03:00
2016-06-04 02:25:26 +03:00
}