2019-11-12 14:03:25 +03:00
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2019 MediaTek Inc.
# include <asm/barrier.h>
# include <linux/clk.h>
# include <linux/dma-mapping.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include <linux/of_reserved_mem.h>
# include <linux/platform_device.h>
# include <linux/remoteproc.h>
# include <linux/remoteproc/mtk_scp.h>
2019-11-12 14:03:26 +03:00
# include <linux/rpmsg/mtk_rpmsg.h>
2019-11-12 14:03:25 +03:00
# include "mtk_common.h"
# include "remoteproc_internal.h"
# define MAX_CODE_SIZE 0x500000
2020-12-02 07:46:09 +03:00
# define SECTION_NAME_IPI_BUFFER ".ipi_buffer"
2019-11-12 14:03:25 +03:00
/**
* scp_get ( ) - get a reference to SCP .
*
* @ pdev : the platform device of the module requesting SCP platform
* device for using SCP API .
*
* Return : Return NULL if failed . otherwise reference to SCP .
* */
struct mtk_scp * scp_get ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * scp_node ;
struct platform_device * scp_pdev ;
scp_node = of_parse_phandle ( dev - > of_node , " mediatek,scp " , 0 ) ;
if ( ! scp_node ) {
dev_err ( dev , " can't get SCP node \n " ) ;
return NULL ;
}
scp_pdev = of_find_device_by_node ( scp_node ) ;
of_node_put ( scp_node ) ;
if ( WARN_ON ( ! scp_pdev ) ) {
dev_err ( dev , " SCP pdev failed \n " ) ;
return NULL ;
}
return platform_get_drvdata ( scp_pdev ) ;
}
EXPORT_SYMBOL_GPL ( scp_get ) ;
/**
* scp_put ( ) - " free " the SCP
*
* @ scp : mtk_scp structure from scp_get ( ) .
* */
void scp_put ( struct mtk_scp * scp )
{
put_device ( scp - > dev ) ;
}
EXPORT_SYMBOL_GPL ( scp_put ) ;
static void scp_wdt_handler ( struct mtk_scp * scp , u32 scp_to_host )
{
dev_err ( scp - > dev , " SCP watchdog timeout! 0x%x " , scp_to_host ) ;
rproc_report_crash ( scp - > rproc , RPROC_WATCHDOG ) ;
}
static void scp_init_ipi_handler ( void * data , unsigned int len , void * priv )
{
struct mtk_scp * scp = ( struct mtk_scp * ) priv ;
struct scp_run * run = ( struct scp_run * ) data ;
scp - > run . signaled = run - > signaled ;
strscpy ( scp - > run . fw_ver , run - > fw_ver , SCP_FW_VER_LEN ) ;
scp - > run . dec_capability = run - > dec_capability ;
scp - > run . enc_capability = run - > enc_capability ;
wake_up_interruptible ( & scp - > run . wq ) ;
}
static void scp_ipi_handler ( struct mtk_scp * scp )
{
struct mtk_share_obj __iomem * rcv_obj = scp - > recv_buf ;
struct scp_ipi_desc * ipi_desc = scp - > ipi_desc ;
u8 tmp_data [ SCP_SHARE_BUFFER_SIZE ] ;
scp_ipi_handler_t handler ;
u32 id = readl ( & rcv_obj - > id ) ;
u32 len = readl ( & rcv_obj - > len ) ;
if ( len > SCP_SHARE_BUFFER_SIZE ) {
dev_err ( scp - > dev , " ipi message too long (len %d, max %d) " , len ,
SCP_SHARE_BUFFER_SIZE ) ;
return ;
}
if ( id > = SCP_IPI_MAX ) {
dev_err ( scp - > dev , " No such ipi id = %d \n " , id ) ;
return ;
}
scp_ipi_lock ( scp , id ) ;
handler = ipi_desc [ id ] . handler ;
if ( ! handler ) {
dev_err ( scp - > dev , " No such ipi id = %d \n " , id ) ;
scp_ipi_unlock ( scp , id ) ;
return ;
}
memcpy_fromio ( tmp_data , & rcv_obj - > share_buf , len ) ;
handler ( tmp_data , len , ipi_desc [ id ] . priv ) ;
scp_ipi_unlock ( scp , id ) ;
scp - > ipi_id_ack [ id ] = true ;
wake_up ( & scp - > ack_wq ) ;
}
2020-12-02 07:46:09 +03:00
static int scp_elf_read_ipi_buf_addr ( struct mtk_scp * scp ,
const struct firmware * fw ,
size_t * offset ) ;
static int scp_ipi_init ( struct mtk_scp * scp , const struct firmware * fw )
2019-11-12 14:03:25 +03:00
{
2020-12-02 07:46:09 +03:00
int ret ;
size_t offset ;
/* read the ipi buf addr from FW itself first */
ret = scp_elf_read_ipi_buf_addr ( scp , fw , & offset ) ;
if ( ret ) {
/* use default ipi buf addr if the FW doesn't have it */
offset = scp - > data - > ipi_buf_offset ;
if ( ! offset )
return ret ;
}
dev_info ( scp - > dev , " IPI buf addr %#010zx \n " , offset ) ;
2019-11-12 14:03:25 +03:00
2020-12-02 07:46:09 +03:00
scp - > recv_buf = ( struct mtk_share_obj __iomem * )
( scp - > sram_base + offset ) ;
scp - > send_buf = ( struct mtk_share_obj __iomem * )
( scp - > sram_base + offset + sizeof ( * scp - > recv_buf ) ) ;
2020-05-09 11:42:37 +03:00
memset_io ( scp - > recv_buf , 0 , sizeof ( * scp - > recv_buf ) ) ;
memset_io ( scp - > send_buf , 0 , sizeof ( * scp - > send_buf ) ) ;
2019-11-12 14:03:25 +03:00
return 0 ;
}
2020-09-21 12:48:46 +03:00
static void mt8183_scp_reset_assert ( struct mtk_scp * scp )
2019-11-12 14:03:25 +03:00
{
u32 val ;
val = readl ( scp - > reg_base + MT8183_SW_RSTN ) ;
val & = ~ MT8183_SW_RSTN_BIT ;
writel ( val , scp - > reg_base + MT8183_SW_RSTN ) ;
}
2020-09-21 12:48:46 +03:00
static void mt8183_scp_reset_deassert ( struct mtk_scp * scp )
2019-11-12 14:03:25 +03:00
{
u32 val ;
val = readl ( scp - > reg_base + MT8183_SW_RSTN ) ;
val | = MT8183_SW_RSTN_BIT ;
writel ( val , scp - > reg_base + MT8183_SW_RSTN ) ;
}
2020-09-21 12:48:46 +03:00
static void mt8192_scp_reset_assert ( struct mtk_scp * scp )
2019-11-12 14:03:25 +03:00
{
2020-09-21 12:48:46 +03:00
writel ( 1 , scp - > reg_base + MT8192_CORE0_SW_RSTN_SET ) ;
}
2019-11-12 14:03:25 +03:00
2020-09-21 12:48:46 +03:00
static void mt8192_scp_reset_deassert ( struct mtk_scp * scp )
{
writel ( 1 , scp - > reg_base + MT8192_CORE0_SW_RSTN_CLR ) ;
}
static void mt8183_scp_irq_handler ( struct mtk_scp * scp )
{
u32 scp_to_host ;
2019-11-12 14:03:25 +03:00
scp_to_host = readl ( scp - > reg_base + MT8183_SCP_TO_HOST ) ;
if ( scp_to_host & MT8183_SCP_IPC_INT_BIT )
scp_ipi_handler ( scp ) ;
else
scp_wdt_handler ( scp , scp_to_host ) ;
/* SCP won't send another interrupt until we set SCP_TO_HOST to 0. */
writel ( MT8183_SCP_IPC_INT_BIT | MT8183_SCP_WDT_INT_BIT ,
scp - > reg_base + MT8183_SCP_TO_HOST ) ;
2020-09-21 12:48:46 +03:00
}
static void mt8192_scp_irq_handler ( struct mtk_scp * scp )
{
u32 scp_to_host ;
scp_to_host = readl ( scp - > reg_base + MT8192_SCP2APMCU_IPC_SET ) ;
2021-01-27 11:20:46 +03:00
if ( scp_to_host & MT8192_SCP_IPC_INT_BIT ) {
2020-09-21 12:48:46 +03:00
scp_ipi_handler ( scp ) ;
2021-01-27 11:20:46 +03:00
/*
* SCP won ' t send another interrupt until we clear
* MT8192_SCP2APMCU_IPC .
*/
writel ( MT8192_SCP_IPC_INT_BIT ,
scp - > reg_base + MT8192_SCP2APMCU_IPC_CLR ) ;
} else {
scp_wdt_handler ( scp , scp_to_host ) ;
writel ( 1 , scp - > reg_base + MT8192_CORE0_WDT_IRQ ) ;
}
2020-09-21 12:48:46 +03:00
}
static irqreturn_t scp_irq_handler ( int irq , void * priv )
{
struct mtk_scp * scp = priv ;
int ret ;
ret = clk_prepare_enable ( scp - > clk ) ;
if ( ret ) {
dev_err ( scp - > dev , " failed to enable clocks \n " ) ;
return IRQ_NONE ;
}
scp - > data - > scp_irq_handler ( scp ) ;
2019-11-12 14:03:25 +03:00
clk_disable_unprepare ( scp - > clk ) ;
return IRQ_HANDLED ;
}
static int scp_elf_load_segments ( struct rproc * rproc , const struct firmware * fw )
{
struct device * dev = & rproc - > dev ;
struct elf32_hdr * ehdr ;
struct elf32_phdr * phdr ;
int i , ret = 0 ;
const u8 * elf_data = fw - > data ;
ehdr = ( struct elf32_hdr * ) elf_data ;
phdr = ( struct elf32_phdr * ) ( elf_data + ehdr - > e_phoff ) ;
/* go through the available ELF segments */
for ( i = 0 ; i < ehdr - > e_phnum ; i + + , phdr + + ) {
u32 da = phdr - > p_paddr ;
u32 memsz = phdr - > p_memsz ;
u32 filesz = phdr - > p_filesz ;
u32 offset = phdr - > p_offset ;
void __iomem * ptr ;
dev_dbg ( dev , " phdr: type %d da 0x%x memsz 0x%x filesz 0x%x \n " ,
phdr - > p_type , da , memsz , filesz ) ;
2020-11-16 11:44:12 +03:00
if ( phdr - > p_type ! = PT_LOAD )
continue ;
if ( ! filesz )
continue ;
2019-11-12 14:03:25 +03:00
if ( filesz > memsz ) {
dev_err ( dev , " bad phdr filesz 0x%x memsz 0x%x \n " ,
filesz , memsz ) ;
ret = - EINVAL ;
break ;
}
if ( offset + filesz > fw - > size ) {
dev_err ( dev , " truncated fw: need 0x%x avail 0x%zx \n " ,
offset + filesz , fw - > size ) ;
ret = - EINVAL ;
break ;
}
/* grab the kernel address for this device address */
2021-03-06 14:24:19 +03:00
ptr = ( void __iomem * ) rproc_da_to_va ( rproc , da , memsz , NULL ) ;
2019-11-12 14:03:25 +03:00
if ( ! ptr ) {
dev_err ( dev , " bad phdr da 0x%x mem 0x%x \n " , da , memsz ) ;
ret = - EINVAL ;
break ;
}
/* put the segment where the remote processor expects it */
2020-11-16 11:44:12 +03:00
scp_memcpy_aligned ( ptr , elf_data + phdr - > p_offset , filesz ) ;
2019-11-12 14:03:25 +03:00
}
return ret ;
}
2020-12-02 07:46:09 +03:00
static int scp_elf_read_ipi_buf_addr ( struct mtk_scp * scp ,
const struct firmware * fw ,
size_t * offset )
{
struct elf32_hdr * ehdr ;
struct elf32_shdr * shdr , * shdr_strtab ;
int i ;
const u8 * elf_data = fw - > data ;
const char * strtab ;
ehdr = ( struct elf32_hdr * ) elf_data ;
shdr = ( struct elf32_shdr * ) ( elf_data + ehdr - > e_shoff ) ;
shdr_strtab = shdr + ehdr - > e_shstrndx ;
strtab = ( const char * ) ( elf_data + shdr_strtab - > sh_offset ) ;
for ( i = 0 ; i < ehdr - > e_shnum ; i + + , shdr + + ) {
if ( strcmp ( strtab + shdr - > sh_name ,
SECTION_NAME_IPI_BUFFER ) = = 0 ) {
* offset = shdr - > sh_addr ;
return 0 ;
}
}
return - ENOENT ;
}
2021-09-24 06:39:33 +03:00
static int mt8183_scp_clk_get ( struct mtk_scp * scp )
{
struct device * dev = scp - > dev ;
int ret = 0 ;
scp - > clk = devm_clk_get ( dev , " main " ) ;
if ( IS_ERR ( scp - > clk ) ) {
dev_err ( dev , " Failed to get clock \n " ) ;
ret = PTR_ERR ( scp - > clk ) ;
}
return ret ;
}
static int mt8192_scp_clk_get ( struct mtk_scp * scp )
{
return mt8183_scp_clk_get ( scp ) ;
}
static int mt8195_scp_clk_get ( struct mtk_scp * scp )
{
scp - > clk = NULL ;
return 0 ;
}
2020-09-21 12:48:46 +03:00
static int mt8183_scp_before_load ( struct mtk_scp * scp )
2019-11-12 14:03:25 +03:00
{
2020-09-21 12:48:46 +03:00
/* Clear SCP to host interrupt */
writel ( MT8183_SCP_IPC_INT_BIT , scp - > reg_base + MT8183_SCP_TO_HOST ) ;
2019-11-12 14:03:25 +03:00
/* Reset clocks before loading FW */
writel ( 0x0 , scp - > reg_base + MT8183_SCP_CLK_SW_SEL ) ;
writel ( 0x0 , scp - > reg_base + MT8183_SCP_CLK_DIV_SEL ) ;
/* Initialize TCM before loading FW. */
writel ( 0x0 , scp - > reg_base + MT8183_SCP_L1_SRAM_PD ) ;
writel ( 0x0 , scp - > reg_base + MT8183_SCP_TCM_TAIL_SRAM_PD ) ;
/* Turn on the power of SCP's SRAM before using it. */
writel ( 0x0 , scp - > reg_base + MT8183_SCP_SRAM_PDN ) ;
/*
* Set I - cache and D - cache size before loading SCP FW .
* SCP SRAM logical address may change when cache size setting differs .
*/
writel ( MT8183_SCP_CACHE_CON_WAYEN | MT8183_SCP_CACHESIZE_8KB ,
scp - > reg_base + MT8183_SCP_CACHE_CON ) ;
writel ( MT8183_SCP_CACHESIZE_8KB , scp - > reg_base + MT8183_SCP_DCACHE_CON ) ;
2020-09-21 12:48:46 +03:00
return 0 ;
}
2022-03-21 09:03:40 +03:00
static void scp_sram_power_on ( void __iomem * addr , u32 reserved_mask )
2020-09-21 12:48:46 +03:00
{
int i ;
for ( i = 31 ; i > = 0 ; i - - )
2022-03-21 09:03:40 +03:00
writel ( GENMASK ( i , 0 ) & ~ reserved_mask , addr ) ;
2020-09-21 12:48:46 +03:00
writel ( 0 , addr ) ;
}
2022-03-21 09:03:40 +03:00
static void scp_sram_power_off ( void __iomem * addr , u32 reserved_mask )
2020-09-21 12:48:46 +03:00
{
int i ;
writel ( 0 , addr ) ;
for ( i = 0 ; i < 32 ; i + + )
2022-03-21 09:03:40 +03:00
writel ( GENMASK ( i , 0 ) & ~ reserved_mask , addr ) ;
2020-09-21 12:48:46 +03:00
}
2022-02-25 16:27:47 +03:00
static int mt8186_scp_before_load ( struct mtk_scp * scp )
{
/* Clear SCP to host interrupt */
writel ( MT8183_SCP_IPC_INT_BIT , scp - > reg_base + MT8183_SCP_TO_HOST ) ;
/* Reset clocks before loading FW */
writel ( 0x0 , scp - > reg_base + MT8183_SCP_CLK_SW_SEL ) ;
writel ( 0x0 , scp - > reg_base + MT8183_SCP_CLK_DIV_SEL ) ;
/* Turn on the power of SCP's SRAM before using it. Enable 1 block per time*/
2022-03-21 09:03:40 +03:00
scp_sram_power_on ( scp - > reg_base + MT8183_SCP_SRAM_PDN , 0 ) ;
2022-02-25 16:27:47 +03:00
/* Initialize TCM before loading FW. */
writel ( 0x0 , scp - > reg_base + MT8183_SCP_L1_SRAM_PD ) ;
writel ( 0x0 , scp - > reg_base + MT8183_SCP_TCM_TAIL_SRAM_PD ) ;
writel ( 0x0 , scp - > reg_base + MT8186_SCP_L1_SRAM_PD_P1 ) ;
writel ( 0x0 , scp - > reg_base + MT8186_SCP_L1_SRAM_PD_p2 ) ;
2022-07-01 15:12:29 +03:00
/*
* Set I - cache and D - cache size before loading SCP FW .
* SCP SRAM logical address may change when cache size setting differs .
*/
writel ( MT8183_SCP_CACHE_CON_WAYEN | MT8183_SCP_CACHESIZE_8KB ,
scp - > reg_base + MT8183_SCP_CACHE_CON ) ;
writel ( MT8183_SCP_CACHESIZE_8KB , scp - > reg_base + MT8183_SCP_DCACHE_CON ) ;
2022-02-25 16:27:47 +03:00
return 0 ;
}
2020-09-21 12:48:46 +03:00
static int mt8192_scp_before_load ( struct mtk_scp * scp )
{
/* clear SPM interrupt, SCP2SPM_IPC_CLR */
writel ( 0xff , scp - > reg_base + MT8192_SCP2SPM_IPC_CLR ) ;
writel ( 1 , scp - > reg_base + MT8192_CORE0_SW_RSTN_SET ) ;
/* enable SRAM clock */
2022-03-21 09:03:40 +03:00
scp_sram_power_on ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_0 , 0 ) ;
scp_sram_power_on ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_1 , 0 ) ;
scp_sram_power_on ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_2 , 0 ) ;
scp_sram_power_on ( scp - > reg_base + MT8192_L1TCM_SRAM_PDN , 0 ) ;
scp_sram_power_on ( scp - > reg_base + MT8192_CPU0_SRAM_PD , 0 ) ;
/* enable MPU for all memory regions */
writel ( 0xff , scp - > reg_base + MT8192_CORE0_MEM_ATT_PREDEF ) ;
return 0 ;
}
static int mt8195_scp_before_load ( struct mtk_scp * scp )
{
/* clear SPM interrupt, SCP2SPM_IPC_CLR */
writel ( 0xff , scp - > reg_base + MT8192_SCP2SPM_IPC_CLR ) ;
writel ( 1 , scp - > reg_base + MT8192_CORE0_SW_RSTN_SET ) ;
/* enable SRAM clock */
scp_sram_power_on ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_0 , 0 ) ;
scp_sram_power_on ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_1 , 0 ) ;
scp_sram_power_on ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_2 , 0 ) ;
scp_sram_power_on ( scp - > reg_base + MT8192_L1TCM_SRAM_PDN ,
MT8195_L1TCM_SRAM_PDN_RESERVED_RSI_BITS ) ;
scp_sram_power_on ( scp - > reg_base + MT8192_CPU0_SRAM_PD , 0 ) ;
2020-09-21 12:48:46 +03:00
2021-01-27 11:31:34 +03:00
/* enable MPU for all memory regions */
writel ( 0xff , scp - > reg_base + MT8192_CORE0_MEM_ATT_PREDEF ) ;
2020-09-21 12:48:46 +03:00
return 0 ;
}
static int scp_load ( struct rproc * rproc , const struct firmware * fw )
{
struct mtk_scp * scp = rproc - > priv ;
struct device * dev = scp - > dev ;
int ret ;
ret = clk_prepare_enable ( scp - > clk ) ;
if ( ret ) {
dev_err ( dev , " failed to enable clocks \n " ) ;
return ret ;
}
/* Hold SCP in reset while loading FW. */
scp - > data - > scp_reset_assert ( scp ) ;
ret = scp - > data - > scp_before_load ( scp ) ;
if ( ret < 0 )
2020-12-03 18:59:14 +03:00
goto leave ;
2020-09-21 12:48:46 +03:00
2019-11-12 14:03:25 +03:00
ret = scp_elf_load_segments ( rproc , fw ) ;
2020-12-03 18:59:14 +03:00
leave :
2019-11-12 14:03:25 +03:00
clk_disable_unprepare ( scp - > clk ) ;
return ret ;
}
2020-12-02 07:46:09 +03:00
static int scp_parse_fw ( struct rproc * rproc , const struct firmware * fw )
{
struct mtk_scp * scp = rproc - > priv ;
struct device * dev = scp - > dev ;
int ret ;
ret = clk_prepare_enable ( scp - > clk ) ;
if ( ret ) {
dev_err ( dev , " failed to enable clocks \n " ) ;
return ret ;
}
ret = scp_ipi_init ( scp , fw ) ;
clk_disable_unprepare ( scp - > clk ) ;
return ret ;
}
2019-11-12 14:03:25 +03:00
static int scp_start ( struct rproc * rproc )
{
struct mtk_scp * scp = ( struct mtk_scp * ) rproc - > priv ;
struct device * dev = scp - > dev ;
struct scp_run * run = & scp - > run ;
int ret ;
ret = clk_prepare_enable ( scp - > clk ) ;
if ( ret ) {
dev_err ( dev , " failed to enable clocks \n " ) ;
return ret ;
}
run - > signaled = false ;
2020-09-21 12:48:46 +03:00
scp - > data - > scp_reset_deassert ( scp ) ;
2019-11-12 14:03:25 +03:00
ret = wait_event_interruptible_timeout (
run - > wq ,
run - > signaled ,
msecs_to_jiffies ( 2000 ) ) ;
if ( ret = = 0 ) {
dev_err ( dev , " wait SCP initialization timeout! \n " ) ;
ret = - ETIME ;
goto stop ;
}
if ( ret = = - ERESTARTSYS ) {
dev_err ( dev , " wait SCP interrupted by a signal! \n " ) ;
goto stop ;
}
2020-09-21 12:48:46 +03:00
2019-11-12 14:03:25 +03:00
clk_disable_unprepare ( scp - > clk ) ;
dev_info ( dev , " SCP is ready. FW version %s \n " , run - > fw_ver ) ;
return 0 ;
stop :
2020-09-21 12:48:46 +03:00
scp - > data - > scp_reset_assert ( scp ) ;
2019-11-12 14:03:25 +03:00
clk_disable_unprepare ( scp - > clk ) ;
return ret ;
}
2021-01-27 11:31:36 +03:00
static void * mt8183_scp_da_to_va ( struct mtk_scp * scp , u64 da , size_t len )
2019-11-12 14:03:25 +03:00
{
int offset ;
if ( da < scp - > sram_size ) {
offset = da ;
2020-11-16 11:44:11 +03:00
if ( offset > = 0 & & ( offset + len ) < = scp - > sram_size )
2019-11-12 14:03:25 +03:00
return ( void __force * ) scp - > sram_base + offset ;
2020-09-21 12:48:46 +03:00
} else if ( scp - > dram_size ) {
2020-04-08 18:54:29 +03:00
offset = da - scp - > dma_addr ;
2020-11-16 11:44:11 +03:00
if ( offset > = 0 & & ( offset + len ) < = scp - > dram_size )
2020-11-16 11:25:37 +03:00
return scp - > cpu_addr + offset ;
2019-11-12 14:03:25 +03:00
}
return NULL ;
}
2021-01-27 11:31:36 +03:00
static void * mt8192_scp_da_to_va ( struct mtk_scp * scp , u64 da , size_t len )
{
int offset ;
if ( da > = scp - > sram_phys & &
( da + len ) < = scp - > sram_phys + scp - > sram_size ) {
offset = da - scp - > sram_phys ;
return ( void __force * ) scp - > sram_base + offset ;
}
/* optional memory region */
if ( scp - > l1tcm_size & &
da > = scp - > l1tcm_phys & &
( da + len ) < = scp - > l1tcm_phys + scp - > l1tcm_size ) {
offset = da - scp - > l1tcm_phys ;
return ( void __force * ) scp - > l1tcm_base + offset ;
}
/* optional memory region */
if ( scp - > dram_size & &
da > = scp - > dma_addr & &
( da + len ) < = scp - > dma_addr + scp - > dram_size ) {
offset = da - scp - > dma_addr ;
return scp - > cpu_addr + offset ;
}
return NULL ;
}
2021-03-06 14:24:19 +03:00
static void * scp_da_to_va ( struct rproc * rproc , u64 da , size_t len , bool * is_iomem )
2021-01-27 11:31:36 +03:00
{
struct mtk_scp * scp = ( struct mtk_scp * ) rproc - > priv ;
return scp - > data - > scp_da_to_va ( scp , da , len ) ;
}
2020-09-21 12:48:46 +03:00
static void mt8183_scp_stop ( struct mtk_scp * scp )
{
/* Disable SCP watchdog */
writel ( 0 , scp - > reg_base + MT8183_WDT_CFG ) ;
}
static void mt8192_scp_stop ( struct mtk_scp * scp )
{
/* Disable SRAM clock */
2022-03-21 09:03:40 +03:00
scp_sram_power_off ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_0 , 0 ) ;
scp_sram_power_off ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_1 , 0 ) ;
scp_sram_power_off ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_2 , 0 ) ;
scp_sram_power_off ( scp - > reg_base + MT8192_L1TCM_SRAM_PDN , 0 ) ;
scp_sram_power_off ( scp - > reg_base + MT8192_CPU0_SRAM_PD , 0 ) ;
/* Disable SCP watchdog */
writel ( 0 , scp - > reg_base + MT8192_CORE0_WDT_CFG ) ;
}
static void mt8195_scp_stop ( struct mtk_scp * scp )
{
/* Disable SRAM clock */
scp_sram_power_off ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_0 , 0 ) ;
scp_sram_power_off ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_1 , 0 ) ;
scp_sram_power_off ( scp - > reg_base + MT8192_L2TCM_SRAM_PD_2 , 0 ) ;
scp_sram_power_off ( scp - > reg_base + MT8192_L1TCM_SRAM_PDN ,
MT8195_L1TCM_SRAM_PDN_RESERVED_RSI_BITS ) ;
scp_sram_power_off ( scp - > reg_base + MT8192_CPU0_SRAM_PD , 0 ) ;
2020-09-21 12:48:46 +03:00
/* Disable SCP watchdog */
writel ( 0 , scp - > reg_base + MT8192_CORE0_WDT_CFG ) ;
}
2019-11-12 14:03:25 +03:00
static int scp_stop ( struct rproc * rproc )
{
struct mtk_scp * scp = ( struct mtk_scp * ) rproc - > priv ;
int ret ;
ret = clk_prepare_enable ( scp - > clk ) ;
if ( ret ) {
dev_err ( scp - > dev , " failed to enable clocks \n " ) ;
return ret ;
}
2020-09-21 12:48:46 +03:00
scp - > data - > scp_reset_assert ( scp ) ;
scp - > data - > scp_stop ( scp ) ;
2019-11-12 14:03:25 +03:00
clk_disable_unprepare ( scp - > clk ) ;
return 0 ;
}
static const struct rproc_ops scp_ops = {
. start = scp_start ,
. stop = scp_stop ,
. load = scp_load ,
. da_to_va = scp_da_to_va ,
2020-12-02 07:46:09 +03:00
. parse_fw = scp_parse_fw ,
2019-11-12 14:03:25 +03:00
} ;
/**
* scp_get_device ( ) - get device struct of SCP
*
* @ scp : mtk_scp structure
* */
struct device * scp_get_device ( struct mtk_scp * scp )
{
return scp - > dev ;
}
EXPORT_SYMBOL_GPL ( scp_get_device ) ;
/**
* scp_get_rproc ( ) - get rproc struct of SCP
*
* @ scp : mtk_scp structure
* */
struct rproc * scp_get_rproc ( struct mtk_scp * scp )
{
return scp - > rproc ;
}
EXPORT_SYMBOL_GPL ( scp_get_rproc ) ;
/**
* scp_get_vdec_hw_capa ( ) - get video decoder hardware capability
*
* @ scp : mtk_scp structure
*
* Return : video decoder hardware capability
* */
unsigned int scp_get_vdec_hw_capa ( struct mtk_scp * scp )
{
return scp - > run . dec_capability ;
}
EXPORT_SYMBOL_GPL ( scp_get_vdec_hw_capa ) ;
/**
* scp_get_venc_hw_capa ( ) - get video encoder hardware capability
*
* @ scp : mtk_scp structure
*
* Return : video encoder hardware capability
* */
unsigned int scp_get_venc_hw_capa ( struct mtk_scp * scp )
{
return scp - > run . enc_capability ;
}
EXPORT_SYMBOL_GPL ( scp_get_venc_hw_capa ) ;
/**
* scp_mapping_dm_addr ( ) - Mapping SRAM / DRAM to kernel virtual address
*
* @ scp : mtk_scp structure
* @ mem_addr : SCP views memory address
*
* Mapping the SCP ' s SRAM address /
* DMEM ( Data Extended Memory ) memory address /
* Working buffer memory address to
* kernel virtual address .
*
* Return : Return ERR_PTR ( - EINVAL ) if mapping failed ,
* otherwise the mapped kernel virtual address
* */
void * scp_mapping_dm_addr ( struct mtk_scp * scp , u32 mem_addr )
{
void * ptr ;
2021-03-06 14:24:19 +03:00
ptr = scp_da_to_va ( scp - > rproc , mem_addr , 0 , NULL ) ;
2019-11-12 14:03:25 +03:00
if ( ! ptr )
return ERR_PTR ( - EINVAL ) ;
return ptr ;
}
EXPORT_SYMBOL_GPL ( scp_mapping_dm_addr ) ;
static int scp_map_memory_region ( struct mtk_scp * scp )
{
int ret ;
ret = of_reserved_mem_device_init ( scp - > dev ) ;
2020-09-21 12:48:46 +03:00
/* reserved memory is optional. */
if ( ret = = - ENODEV ) {
dev_info ( scp - > dev , " skipping reserved memory initialization. " ) ;
return 0 ;
}
2019-11-12 14:03:25 +03:00
if ( ret ) {
dev_err ( scp - > dev , " failed to assign memory-region: %d \n " , ret ) ;
return - ENOMEM ;
}
/* Reserved SCP code size */
scp - > dram_size = MAX_CODE_SIZE ;
scp - > cpu_addr = dma_alloc_coherent ( scp - > dev , scp - > dram_size ,
2020-04-08 18:54:29 +03:00
& scp - > dma_addr , GFP_KERNEL ) ;
2019-11-12 14:03:25 +03:00
if ( ! scp - > cpu_addr )
return - ENOMEM ;
return 0 ;
}
static void scp_unmap_memory_region ( struct mtk_scp * scp )
{
2020-09-21 12:48:46 +03:00
if ( scp - > dram_size = = 0 )
return ;
2019-11-12 14:03:25 +03:00
dma_free_coherent ( scp - > dev , scp - > dram_size , scp - > cpu_addr ,
2020-04-08 18:54:29 +03:00
scp - > dma_addr ) ;
2019-11-12 14:03:25 +03:00
of_reserved_mem_device_release ( scp - > dev ) ;
}
2019-11-12 14:03:26 +03:00
static int scp_register_ipi ( struct platform_device * pdev , u32 id ,
ipi_handler_t handler , void * priv )
{
struct mtk_scp * scp = platform_get_drvdata ( pdev ) ;
return scp_ipi_register ( scp , id , handler , priv ) ;
}
static void scp_unregister_ipi ( struct platform_device * pdev , u32 id )
{
struct mtk_scp * scp = platform_get_drvdata ( pdev ) ;
scp_ipi_unregister ( scp , id ) ;
}
static int scp_send_ipi ( struct platform_device * pdev , u32 id , void * buf ,
unsigned int len , unsigned int wait )
{
struct mtk_scp * scp = platform_get_drvdata ( pdev ) ;
return scp_ipi_send ( scp , id , buf , len , wait ) ;
}
static struct mtk_rpmsg_info mtk_scp_rpmsg_info = {
. send_ipi = scp_send_ipi ,
. register_ipi = scp_register_ipi ,
. unregister_ipi = scp_unregister_ipi ,
. ns_ipi_id = SCP_IPI_NS_SERVICE ,
} ;
static void scp_add_rpmsg_subdev ( struct mtk_scp * scp )
{
scp - > rpmsg_subdev =
mtk_rpmsg_create_rproc_subdev ( to_platform_device ( scp - > dev ) ,
& mtk_scp_rpmsg_info ) ;
if ( scp - > rpmsg_subdev )
rproc_add_subdev ( scp - > rproc , scp - > rpmsg_subdev ) ;
}
static void scp_remove_rpmsg_subdev ( struct mtk_scp * scp )
{
if ( scp - > rpmsg_subdev ) {
rproc_remove_subdev ( scp - > rproc , scp - > rpmsg_subdev ) ;
mtk_rpmsg_destroy_rproc_subdev ( scp - > rpmsg_subdev ) ;
scp - > rpmsg_subdev = NULL ;
}
}
2019-11-12 14:03:25 +03:00
static int scp_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct mtk_scp * scp ;
struct rproc * rproc ;
struct resource * res ;
2022-04-19 15:33:31 +03:00
const char * fw_name = " scp.img " ;
2019-11-12 14:03:25 +03:00
int ret , i ;
2022-04-19 15:33:31 +03:00
ret = rproc_of_parse_firmware ( dev , 0 , & fw_name ) ;
if ( ret < 0 & & ret ! = - EINVAL )
return ret ;
2022-01-24 15:09:13 +03:00
rproc = devm_rproc_alloc ( dev , np - > name , & scp_ops , fw_name , sizeof ( * scp ) ) ;
2022-01-24 15:09:15 +03:00
if ( ! rproc )
return dev_err_probe ( dev , - ENOMEM , " unable to allocate remoteproc \n " ) ;
2019-11-12 14:03:25 +03:00
scp = ( struct mtk_scp * ) rproc - > priv ;
scp - > rproc = rproc ;
scp - > dev = dev ;
2020-09-21 12:48:46 +03:00
scp - > data = of_device_get_match_data ( dev ) ;
2019-11-12 14:03:25 +03:00
platform_set_drvdata ( pdev , scp ) ;
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " sram " ) ;
scp - > sram_base = devm_ioremap_resource ( dev , res ) ;
2022-01-24 15:09:15 +03:00
if ( IS_ERR ( scp - > sram_base ) )
return dev_err_probe ( dev , PTR_ERR ( scp - > sram_base ) ,
" Failed to parse and map sram memory \n " ) ;
2019-11-12 14:03:25 +03:00
scp - > sram_size = resource_size ( res ) ;
2021-01-27 11:31:36 +03:00
scp - > sram_phys = res - > start ;
/* l1tcm is an optional memory region */
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " l1tcm " ) ;
scp - > l1tcm_base = devm_ioremap_resource ( dev , res ) ;
2022-01-24 15:09:15 +03:00
if ( IS_ERR ( scp - > l1tcm_base ) ) {
ret = PTR_ERR ( scp - > l1tcm_base ) ;
2021-01-27 11:31:36 +03:00
if ( ret ! = - EINVAL ) {
2022-01-24 15:09:15 +03:00
return dev_err_probe ( dev , ret , " Failed to map l1tcm memory \n " ) ;
2021-01-27 11:31:36 +03:00
}
} else {
scp - > l1tcm_size = resource_size ( res ) ;
scp - > l1tcm_phys = res - > start ;
}
2019-11-12 14:03:25 +03:00
2021-01-27 11:31:33 +03:00
scp - > reg_base = devm_platform_ioremap_resource_byname ( pdev , " cfg " ) ;
2022-01-24 15:09:15 +03:00
if ( IS_ERR ( scp - > reg_base ) )
return dev_err_probe ( dev , PTR_ERR ( scp - > reg_base ) ,
" Failed to parse and map cfg memory \n " ) ;
2019-11-12 14:03:25 +03:00
2022-01-24 15:09:14 +03:00
ret = scp - > data - > scp_clk_get ( scp ) ;
2019-11-12 14:03:25 +03:00
if ( ret )
2022-01-24 15:09:14 +03:00
return ret ;
2019-11-12 14:03:25 +03:00
2022-01-24 15:09:14 +03:00
ret = scp_map_memory_region ( scp ) ;
2021-09-24 06:39:33 +03:00
if ( ret )
2022-01-24 15:09:14 +03:00
return ret ;
mutex_init ( & scp - > send_lock ) ;
for ( i = 0 ; i < SCP_IPI_MAX ; i + + )
mutex_init ( & scp - > ipi_desc [ i ] . lock ) ;
2019-11-12 14:03:25 +03:00
/* register SCP initialization IPI */
ret = scp_ipi_register ( scp , SCP_IPI_INIT , scp_init_ipi_handler , scp ) ;
if ( ret ) {
dev_err ( dev , " Failed to register IPI_SCP_INIT \n " ) ;
goto release_dev_mem ;
}
init_waitqueue_head ( & scp - > run . wq ) ;
init_waitqueue_head ( & scp - > ack_wq ) ;
2019-11-12 14:03:26 +03:00
scp_add_rpmsg_subdev ( scp ) ;
2019-11-12 14:03:25 +03:00
ret = devm_request_threaded_irq ( dev , platform_get_irq ( pdev , 0 ) , NULL ,
scp_irq_handler , IRQF_ONESHOT ,
pdev - > name , scp ) ;
if ( ret ) {
dev_err ( dev , " failed to request irq \n " ) ;
2019-11-12 14:03:26 +03:00
goto remove_subdev ;
2019-11-12 14:03:25 +03:00
}
ret = rproc_add ( rproc ) ;
if ( ret )
2019-11-12 14:03:26 +03:00
goto remove_subdev ;
2019-11-12 14:03:25 +03:00
2019-11-12 14:03:26 +03:00
return 0 ;
2019-11-12 14:03:25 +03:00
2019-11-12 14:03:26 +03:00
remove_subdev :
scp_remove_rpmsg_subdev ( scp ) ;
2019-11-12 14:03:25 +03:00
scp_ipi_unregister ( scp , SCP_IPI_INIT ) ;
release_dev_mem :
scp_unmap_memory_region ( scp ) ;
for ( i = 0 ; i < SCP_IPI_MAX ; i + + )
mutex_destroy ( & scp - > ipi_desc [ i ] . lock ) ;
mutex_destroy ( & scp - > send_lock ) ;
return ret ;
}
static int scp_remove ( struct platform_device * pdev )
{
struct mtk_scp * scp = platform_get_drvdata ( pdev ) ;
int i ;
rproc_del ( scp - > rproc ) ;
2019-11-12 14:03:26 +03:00
scp_remove_rpmsg_subdev ( scp ) ;
2019-11-12 14:03:25 +03:00
scp_ipi_unregister ( scp , SCP_IPI_INIT ) ;
scp_unmap_memory_region ( scp ) ;
for ( i = 0 ; i < SCP_IPI_MAX ; i + + )
mutex_destroy ( & scp - > ipi_desc [ i ] . lock ) ;
mutex_destroy ( & scp - > send_lock ) ;
return 0 ;
}
2020-09-21 12:48:46 +03:00
static const struct mtk_scp_of_data mt8183_of_data = {
2021-09-24 06:39:33 +03:00
. scp_clk_get = mt8183_scp_clk_get ,
2020-09-21 12:48:46 +03:00
. scp_before_load = mt8183_scp_before_load ,
. scp_irq_handler = mt8183_scp_irq_handler ,
. scp_reset_assert = mt8183_scp_reset_assert ,
. scp_reset_deassert = mt8183_scp_reset_deassert ,
. scp_stop = mt8183_scp_stop ,
2021-01-27 11:31:36 +03:00
. scp_da_to_va = mt8183_scp_da_to_va ,
2020-09-21 12:48:46 +03:00
. host_to_scp_reg = MT8183_HOST_TO_SCP ,
. host_to_scp_int_bit = MT8183_HOST_IPC_INT_BIT ,
2020-12-02 07:46:09 +03:00
. ipi_buf_offset = 0x7bdb0 ,
2020-09-21 12:48:46 +03:00
} ;
2022-02-25 16:27:47 +03:00
static const struct mtk_scp_of_data mt8186_of_data = {
. scp_clk_get = mt8195_scp_clk_get ,
. scp_before_load = mt8186_scp_before_load ,
. scp_irq_handler = mt8183_scp_irq_handler ,
. scp_reset_assert = mt8183_scp_reset_assert ,
. scp_reset_deassert = mt8183_scp_reset_deassert ,
. scp_stop = mt8183_scp_stop ,
. scp_da_to_va = mt8183_scp_da_to_va ,
. host_to_scp_reg = MT8183_HOST_TO_SCP ,
. host_to_scp_int_bit = MT8183_HOST_IPC_INT_BIT ,
2022-07-01 15:12:29 +03:00
. ipi_buf_offset = 0x3bdb0 ,
2022-02-25 16:27:47 +03:00
} ;
2022-07-15 08:18:21 +03:00
static const struct mtk_scp_of_data mt8188_of_data = {
. scp_clk_get = mt8195_scp_clk_get ,
. scp_before_load = mt8192_scp_before_load ,
. scp_irq_handler = mt8192_scp_irq_handler ,
. scp_reset_assert = mt8192_scp_reset_assert ,
. scp_reset_deassert = mt8192_scp_reset_deassert ,
. scp_stop = mt8192_scp_stop ,
. scp_da_to_va = mt8192_scp_da_to_va ,
. host_to_scp_reg = MT8192_GIPC_IN_SET ,
. host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT ,
} ;
2020-09-21 12:48:46 +03:00
static const struct mtk_scp_of_data mt8192_of_data = {
2021-09-24 06:39:33 +03:00
. scp_clk_get = mt8192_scp_clk_get ,
. scp_before_load = mt8192_scp_before_load ,
. scp_irq_handler = mt8192_scp_irq_handler ,
. scp_reset_assert = mt8192_scp_reset_assert ,
. scp_reset_deassert = mt8192_scp_reset_deassert ,
. scp_stop = mt8192_scp_stop ,
. scp_da_to_va = mt8192_scp_da_to_va ,
. host_to_scp_reg = MT8192_GIPC_IN_SET ,
. host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT ,
} ;
static const struct mtk_scp_of_data mt8195_of_data = {
. scp_clk_get = mt8195_scp_clk_get ,
2022-03-21 09:03:40 +03:00
. scp_before_load = mt8195_scp_before_load ,
2020-09-21 12:48:46 +03:00
. scp_irq_handler = mt8192_scp_irq_handler ,
. scp_reset_assert = mt8192_scp_reset_assert ,
. scp_reset_deassert = mt8192_scp_reset_deassert ,
2022-03-21 09:03:40 +03:00
. scp_stop = mt8195_scp_stop ,
2021-01-27 11:31:36 +03:00
. scp_da_to_va = mt8192_scp_da_to_va ,
2020-09-21 12:48:46 +03:00
. host_to_scp_reg = MT8192_GIPC_IN_SET ,
. host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT ,
} ;
2019-11-12 14:03:25 +03:00
static const struct of_device_id mtk_scp_of_match [ ] = {
2020-09-21 12:48:46 +03:00
{ . compatible = " mediatek,mt8183-scp " , . data = & mt8183_of_data } ,
2022-02-25 16:27:47 +03:00
{ . compatible = " mediatek,mt8186-scp " , . data = & mt8186_of_data } ,
2022-07-15 08:18:21 +03:00
{ . compatible = " mediatek,mt8188-scp " , . data = & mt8188_of_data } ,
2020-09-21 12:48:46 +03:00
{ . compatible = " mediatek,mt8192-scp " , . data = & mt8192_of_data } ,
2021-09-24 06:39:33 +03:00
{ . compatible = " mediatek,mt8195-scp " , . data = & mt8195_of_data } ,
2019-11-12 14:03:25 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , mtk_scp_of_match ) ;
static struct platform_driver mtk_scp_driver = {
. probe = scp_probe ,
. remove = scp_remove ,
. driver = {
. name = " mtk-scp " ,
2020-11-28 00:50:55 +03:00
. of_match_table = mtk_scp_of_match ,
2019-11-12 14:03:25 +03:00
} ,
} ;
module_platform_driver ( mtk_scp_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " MediaTek SCP control driver " ) ;