2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-08-17 09:15:26 +02:00
/*
* Copyright ( c ) 2017 Pengutronix , Oleksij Rempel < kernel @ pengutronix . de >
*/
2021-05-06 12:08:43 +08:00
# include <linux/arm-smccc.h>
2017-08-17 09:15:26 +02:00
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
2021-03-06 19:24:25 +08:00
# include <linux/mailbox_client.h>
2017-08-17 09:15:26 +02:00
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/of_address.h>
2021-03-06 19:24:22 +08:00
# include <linux/of_reserved_mem.h>
2017-08-17 09:15:26 +02:00
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/remoteproc.h>
2021-03-06 19:24:25 +08:00
# include <linux/workqueue.h>
# include "remoteproc_internal.h"
2017-08-17 09:15:26 +02:00
# define IMX7D_SRC_SCR 0x0C
# define IMX7D_ENABLE_M4 BIT(3)
# define IMX7D_SW_M4P_RST BIT(2)
# define IMX7D_SW_M4C_RST BIT(1)
# define IMX7D_SW_M4C_NON_SCLR_RST BIT(0)
# define IMX7D_M4_RST_MASK (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
| IMX7D_SW_M4C_RST \
| IMX7D_SW_M4C_NON_SCLR_RST )
# define IMX7D_M4_START (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
| IMX7D_SW_M4C_RST )
# define IMX7D_M4_STOP IMX7D_SW_M4C_NON_SCLR_RST
/* Address: 0x020D8000 */
# define IMX6SX_SRC_SCR 0x00
# define IMX6SX_ENABLE_M4 BIT(22)
# define IMX6SX_SW_M4P_RST BIT(12)
# define IMX6SX_SW_M4C_NON_SCLR_RST BIT(4)
# define IMX6SX_SW_M4C_RST BIT(3)
# define IMX6SX_M4_START (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
| IMX6SX_SW_M4C_RST )
# define IMX6SX_M4_STOP IMX6SX_SW_M4C_NON_SCLR_RST
# define IMX6SX_M4_RST_MASK (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
| IMX6SX_SW_M4C_NON_SCLR_RST \
| IMX6SX_SW_M4C_RST )
2021-04-08 09:44:47 +08:00
# define IMX_RPROC_MEM_MAX 32
2017-08-17 09:15:26 +02:00
2021-05-06 12:08:43 +08:00
# define IMX_SIP_RPROC 0xC2000005
# define IMX_SIP_RPROC_START 0x00
# define IMX_SIP_RPROC_STARTED 0x01
# define IMX_SIP_RPROC_STOP 0x02
2017-08-17 09:15:26 +02:00
/**
* struct imx_rproc_mem - slim internal memory structure
* @ cpu_addr : MPU virtual address of the memory region
* @ sys_addr : Bus address used to access the memory region
* @ size : Size of the memory region
*/
struct imx_rproc_mem {
void __iomem * cpu_addr ;
phys_addr_t sys_addr ;
size_t size ;
} ;
/* att flags */
/* M4 own area. Can be mapped at probe */
# define ATT_OWN BIT(1)
/* address translation table */
struct imx_rproc_att {
u32 da ; /* device address (From Cortex M4 view)*/
u32 sa ; /* system bus address */
u32 size ; /* size of reg range */
int flags ;
} ;
2021-05-06 12:08:40 +08:00
/* Remote core start/stop method */
enum imx_rproc_method {
IMX_RPROC_NONE ,
/* Through syscon regmap */
IMX_RPROC_MMIO ,
/* Through ARM SMCCC */
IMX_RPROC_SMC ,
} ;
2017-08-17 09:15:26 +02:00
struct imx_rproc_dcfg {
u32 src_reg ;
u32 src_mask ;
u32 src_start ;
u32 src_stop ;
const struct imx_rproc_att * att ;
size_t att_size ;
2021-05-06 12:08:40 +08:00
enum imx_rproc_method method ;
2017-08-17 09:15:26 +02:00
} ;
struct imx_rproc {
struct device * dev ;
struct regmap * regmap ;
struct rproc * rproc ;
const struct imx_rproc_dcfg * dcfg ;
2021-04-08 09:44:47 +08:00
struct imx_rproc_mem mem [ IMX_RPROC_MEM_MAX ] ;
2017-08-17 09:15:26 +02:00
struct clk * clk ;
2021-03-06 19:24:25 +08:00
struct mbox_client cl ;
struct mbox_chan * tx_ch ;
struct mbox_chan * rx_ch ;
struct work_struct rproc_work ;
struct workqueue_struct * workqueue ;
2021-04-08 09:44:49 +08:00
void __iomem * rsc_table ;
2017-08-17 09:15:26 +02:00
} ;
2021-05-06 12:08:43 +08:00
static const struct imx_rproc_att imx_rproc_att_imx8mn [ ] = {
/* dev addr , sys addr , size , flags */
/* ITCM */
{ 0x00000000 , 0x007E0000 , 0x00020000 , ATT_OWN } ,
/* OCRAM_S */
{ 0x00180000 , 0x00180000 , 0x00009000 , 0 } ,
/* OCRAM */
{ 0x00900000 , 0x00900000 , 0x00020000 , 0 } ,
/* OCRAM */
{ 0x00920000 , 0x00920000 , 0x00020000 , 0 } ,
/* OCRAM */
{ 0x00940000 , 0x00940000 , 0x00050000 , 0 } ,
/* QSPI Code - alias */
{ 0x08000000 , 0x08000000 , 0x08000000 , 0 } ,
/* DDR (Code) - alias */
{ 0x10000000 , 0x40000000 , 0x0FFE0000 , 0 } ,
/* DTCM */
{ 0x20000000 , 0x00800000 , 0x00020000 , ATT_OWN } ,
/* OCRAM_S - alias */
{ 0x20180000 , 0x00180000 , 0x00008000 , ATT_OWN } ,
/* OCRAM */
{ 0x20200000 , 0x00900000 , 0x00020000 , ATT_OWN } ,
/* OCRAM */
{ 0x20220000 , 0x00920000 , 0x00020000 , ATT_OWN } ,
/* OCRAM */
{ 0x20240000 , 0x00940000 , 0x00040000 , ATT_OWN } ,
/* DDR (Data) */
{ 0x40000000 , 0x40000000 , 0x80000000 , 0 } ,
} ;
2021-03-06 19:24:23 +08:00
static const struct imx_rproc_att imx_rproc_att_imx8mq [ ] = {
/* dev addr , sys addr , size , flags */
/* TCML - alias */
{ 0x00000000 , 0x007e0000 , 0x00020000 , 0 } ,
/* OCRAM_S */
{ 0x00180000 , 0x00180000 , 0x00008000 , 0 } ,
/* OCRAM */
{ 0x00900000 , 0x00900000 , 0x00020000 , 0 } ,
/* OCRAM */
{ 0x00920000 , 0x00920000 , 0x00020000 , 0 } ,
/* QSPI Code - alias */
{ 0x08000000 , 0x08000000 , 0x08000000 , 0 } ,
/* DDR (Code) - alias */
{ 0x10000000 , 0x80000000 , 0x0FFE0000 , 0 } ,
/* TCML */
{ 0x1FFE0000 , 0x007E0000 , 0x00020000 , ATT_OWN } ,
/* TCMU */
{ 0x20000000 , 0x00800000 , 0x00020000 , ATT_OWN } ,
/* OCRAM_S */
{ 0x20180000 , 0x00180000 , 0x00008000 , ATT_OWN } ,
/* OCRAM */
{ 0x20200000 , 0x00900000 , 0x00020000 , ATT_OWN } ,
/* OCRAM */
{ 0x20220000 , 0x00920000 , 0x00020000 , ATT_OWN } ,
/* DDR (Data) */
{ 0x40000000 , 0x40000000 , 0x80000000 , 0 } ,
} ;
2021-06-22 14:01:48 +08:00
static const struct imx_rproc_att imx_rproc_att_imx8ulp [ ] = {
{ 0x1FFC0000 , 0x1FFC0000 , 0xC0000 , ATT_OWN } ,
{ 0x21000000 , 0x21000000 , 0x10000 , ATT_OWN } ,
{ 0x80000000 , 0x80000000 , 0x60000000 , 0 }
} ;
2021-05-06 12:08:42 +08:00
static const struct imx_rproc_att imx_rproc_att_imx7ulp [ ] = {
{ 0x1FFD0000 , 0x1FFD0000 , 0x30000 , ATT_OWN } ,
{ 0x20000000 , 0x20000000 , 0x10000 , ATT_OWN } ,
{ 0x2F000000 , 0x2F000000 , 0x20000 , ATT_OWN } ,
{ 0x2F020000 , 0x2F020000 , 0x20000 , ATT_OWN } ,
{ 0x60000000 , 0x60000000 , 0x40000000 , 0 }
} ;
2017-08-17 09:15:26 +02:00
static const struct imx_rproc_att imx_rproc_att_imx7d [ ] = {
/* dev addr , sys addr , size , flags */
/* OCRAM_S (M4 Boot code) - alias */
{ 0x00000000 , 0x00180000 , 0x00008000 , 0 } ,
/* OCRAM_S (Code) */
{ 0x00180000 , 0x00180000 , 0x00008000 , ATT_OWN } ,
/* OCRAM (Code) - alias */
{ 0x00900000 , 0x00900000 , 0x00020000 , 0 } ,
/* OCRAM_EPDC (Code) - alias */
{ 0x00920000 , 0x00920000 , 0x00020000 , 0 } ,
/* OCRAM_PXP (Code) - alias */
{ 0x00940000 , 0x00940000 , 0x00008000 , 0 } ,
/* TCML (Code) */
{ 0x1FFF8000 , 0x007F8000 , 0x00008000 , ATT_OWN } ,
/* DDR (Code) - alias, first part of DDR (Data) */
{ 0x10000000 , 0x80000000 , 0x0FFF0000 , 0 } ,
/* TCMU (Data) */
{ 0x20000000 , 0x00800000 , 0x00008000 , ATT_OWN } ,
/* OCRAM (Data) */
{ 0x20200000 , 0x00900000 , 0x00020000 , 0 } ,
/* OCRAM_EPDC (Data) */
{ 0x20220000 , 0x00920000 , 0x00020000 , 0 } ,
/* OCRAM_PXP (Data) */
{ 0x20240000 , 0x00940000 , 0x00008000 , 0 } ,
/* DDR (Data) */
{ 0x80000000 , 0x80000000 , 0x60000000 , 0 } ,
} ;
static const struct imx_rproc_att imx_rproc_att_imx6sx [ ] = {
/* dev addr , sys addr , size , flags */
/* TCML (M4 Boot Code) - alias */
{ 0x00000000 , 0x007F8000 , 0x00008000 , 0 } ,
/* OCRAM_S (Code) */
{ 0x00180000 , 0x008F8000 , 0x00004000 , 0 } ,
/* OCRAM_S (Code) - alias */
{ 0x00180000 , 0x008FC000 , 0x00004000 , 0 } ,
/* TCML (Code) */
{ 0x1FFF8000 , 0x007F8000 , 0x00008000 , ATT_OWN } ,
/* DDR (Code) - alias, first part of DDR (Data) */
{ 0x10000000 , 0x80000000 , 0x0FFF8000 , 0 } ,
/* TCMU (Data) */
{ 0x20000000 , 0x00800000 , 0x00008000 , ATT_OWN } ,
/* OCRAM_S (Data) - alias? */
{ 0x208F8000 , 0x008F8000 , 0x00004000 , 0 } ,
/* DDR (Data) */
{ 0x80000000 , 0x80000000 , 0x60000000 , 0 } ,
} ;
2021-05-06 12:08:43 +08:00
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn = {
. att = imx_rproc_att_imx8mn ,
. att_size = ARRAY_SIZE ( imx_rproc_att_imx8mn ) ,
. method = IMX_RPROC_SMC ,
} ;
2021-03-06 19:24:23 +08:00
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mq = {
. src_reg = IMX7D_SRC_SCR ,
. src_mask = IMX7D_M4_RST_MASK ,
. src_start = IMX7D_M4_START ,
. src_stop = IMX7D_M4_STOP ,
. att = imx_rproc_att_imx8mq ,
. att_size = ARRAY_SIZE ( imx_rproc_att_imx8mq ) ,
2021-05-06 12:08:40 +08:00
. method = IMX_RPROC_MMIO ,
2021-03-06 19:24:23 +08:00
} ;
2021-06-22 14:01:48 +08:00
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8ulp = {
. att = imx_rproc_att_imx8ulp ,
. att_size = ARRAY_SIZE ( imx_rproc_att_imx8ulp ) ,
. method = IMX_RPROC_NONE ,
} ;
2021-05-06 12:08:42 +08:00
static const struct imx_rproc_dcfg imx_rproc_cfg_imx7ulp = {
. att = imx_rproc_att_imx7ulp ,
. att_size = ARRAY_SIZE ( imx_rproc_att_imx7ulp ) ,
. method = IMX_RPROC_NONE ,
} ;
2017-08-17 09:15:26 +02:00
static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = {
. src_reg = IMX7D_SRC_SCR ,
. src_mask = IMX7D_M4_RST_MASK ,
. src_start = IMX7D_M4_START ,
. src_stop = IMX7D_M4_STOP ,
. att = imx_rproc_att_imx7d ,
. att_size = ARRAY_SIZE ( imx_rproc_att_imx7d ) ,
2021-05-06 12:08:40 +08:00
. method = IMX_RPROC_MMIO ,
2017-08-17 09:15:26 +02:00
} ;
static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = {
. src_reg = IMX6SX_SRC_SCR ,
. src_mask = IMX6SX_M4_RST_MASK ,
. src_start = IMX6SX_M4_START ,
. src_stop = IMX6SX_M4_STOP ,
. att = imx_rproc_att_imx6sx ,
. att_size = ARRAY_SIZE ( imx_rproc_att_imx6sx ) ,
2021-05-06 12:08:40 +08:00
. method = IMX_RPROC_MMIO ,
2017-08-17 09:15:26 +02:00
} ;
static int imx_rproc_start ( struct rproc * rproc )
{
struct imx_rproc * priv = rproc - > priv ;
const struct imx_rproc_dcfg * dcfg = priv - > dcfg ;
struct device * dev = priv - > dev ;
2021-05-06 12:08:43 +08:00
struct arm_smccc_res res ;
2017-08-17 09:15:26 +02:00
int ret ;
2021-05-06 12:08:43 +08:00
switch ( dcfg - > method ) {
case IMX_RPROC_MMIO :
ret = regmap_update_bits ( priv - > regmap , dcfg - > src_reg , dcfg - > src_mask ,
dcfg - > src_start ) ;
break ;
case IMX_RPROC_SMC :
arm_smccc_smc ( IMX_SIP_RPROC , IMX_SIP_RPROC_START , 0 , 0 , 0 , 0 , 0 , 0 , & res ) ;
ret = res . a0 ;
break ;
default :
return - EOPNOTSUPP ;
}
2017-08-17 09:15:26 +02:00
if ( ret )
2021-05-06 12:08:43 +08:00
dev_err ( dev , " Failed to enable remote core! \n " ) ;
2017-08-17 09:15:26 +02:00
return ret ;
}
static int imx_rproc_stop ( struct rproc * rproc )
{
struct imx_rproc * priv = rproc - > priv ;
const struct imx_rproc_dcfg * dcfg = priv - > dcfg ;
struct device * dev = priv - > dev ;
2021-05-06 12:08:43 +08:00
struct arm_smccc_res res ;
2017-08-17 09:15:26 +02:00
int ret ;
2021-05-06 12:08:43 +08:00
switch ( dcfg - > method ) {
case IMX_RPROC_MMIO :
ret = regmap_update_bits ( priv - > regmap , dcfg - > src_reg , dcfg - > src_mask ,
dcfg - > src_stop ) ;
break ;
case IMX_RPROC_SMC :
arm_smccc_smc ( IMX_SIP_RPROC , IMX_SIP_RPROC_STOP , 0 , 0 , 0 , 0 , 0 , 0 , & res ) ;
ret = res . a0 ;
if ( res . a1 )
dev_info ( dev , " Not in wfi, force stopped \n " ) ;
break ;
default :
2021-05-06 12:08:42 +08:00
return - EOPNOTSUPP ;
2021-05-06 12:08:43 +08:00
}
2021-05-06 12:08:42 +08:00
2017-08-17 09:15:26 +02:00
if ( ret )
2021-05-06 12:08:43 +08:00
dev_err ( dev , " Failed to stop remote core \n " ) ;
2017-08-17 09:15:26 +02:00
return ret ;
}
static int imx_rproc_da_to_sys ( struct imx_rproc * priv , u64 da ,
2020-03-02 10:38:55 +01:00
size_t len , u64 * sys )
2017-08-17 09:15:26 +02:00
{
const struct imx_rproc_dcfg * dcfg = priv - > dcfg ;
int i ;
/* parse address translation table */
for ( i = 0 ; i < dcfg - > att_size ; i + + ) {
const struct imx_rproc_att * att = & dcfg - > att [ i ] ;
if ( da > = att - > da & & da + len < att - > da + att - > size ) {
unsigned int offset = da - att - > da ;
* sys = att - > sa + offset ;
return 0 ;
}
}
2020-03-02 10:38:55 +01:00
dev_warn ( priv - > dev , " Translation failed: da = 0x%llx len = 0x%zx \n " ,
2017-08-17 09:15:26 +02:00
da , len ) ;
return - ENOENT ;
}
2021-03-06 19:24:19 +08:00
static void * imx_rproc_da_to_va ( struct rproc * rproc , u64 da , size_t len , bool * is_iomem )
2017-08-17 09:15:26 +02:00
{
struct imx_rproc * priv = rproc - > priv ;
void * va = NULL ;
u64 sys ;
int i ;
2020-03-02 10:38:55 +01:00
if ( len = = 0 )
2017-08-17 09:15:26 +02:00
return NULL ;
/*
* On device side we have many aliases , so we need to convert device
* address ( M4 ) to system bus address first .
*/
if ( imx_rproc_da_to_sys ( priv , da , len , & sys ) )
return NULL ;
2021-04-08 09:44:47 +08:00
for ( i = 0 ; i < IMX_RPROC_MEM_MAX ; i + + ) {
2017-08-17 09:15:26 +02:00
if ( sys > = priv - > mem [ i ] . sys_addr & & sys + len <
priv - > mem [ i ] . sys_addr + priv - > mem [ i ] . size ) {
unsigned int offset = sys - priv - > mem [ i ] . sys_addr ;
/* __force to make sparse happy with type conversion */
va = ( __force void * ) ( priv - > mem [ i ] . cpu_addr + offset ) ;
break ;
}
}
2020-03-02 10:38:55 +01:00
dev_dbg ( & rproc - > dev , " da = 0x%llx len = 0x%zx va = 0x%p \n " ,
da , len , va ) ;
2017-08-17 09:15:26 +02:00
return va ;
}
2021-03-06 19:24:22 +08:00
static int imx_rproc_mem_alloc ( struct rproc * rproc ,
struct rproc_mem_entry * mem )
{
struct device * dev = rproc - > dev . parent ;
void * va ;
dev_dbg ( dev , " map memory: %p+%zx \n " , & mem - > dma , mem - > len ) ;
va = ioremap_wc ( mem - > dma , mem - > len ) ;
if ( IS_ERR_OR_NULL ( va ) ) {
dev_err ( dev , " Unable to map memory region: %p+%zx \n " ,
& mem - > dma , mem - > len ) ;
return - ENOMEM ;
}
/* Update memory entry va */
mem - > va = va ;
return 0 ;
}
static int imx_rproc_mem_release ( struct rproc * rproc ,
struct rproc_mem_entry * mem )
{
dev_dbg ( rproc - > dev . parent , " unmap memory: %pa \n " , & mem - > dma ) ;
iounmap ( mem - > va ) ;
return 0 ;
}
2021-04-08 09:44:48 +08:00
static int imx_rproc_prepare ( struct rproc * rproc )
2021-03-06 19:24:22 +08:00
{
struct imx_rproc * priv = rproc - > priv ;
struct device_node * np = priv - > dev - > of_node ;
struct of_phandle_iterator it ;
struct rproc_mem_entry * mem ;
struct reserved_mem * rmem ;
u32 da ;
/* Register associated reserved memory regions */
of_phandle_iterator_init ( & it , np , " memory-region " , NULL , 0 ) ;
while ( of_phandle_iterator_next ( & it ) = = 0 ) {
/*
* Ignore the first memory region which will be used vdev buffer .
* No need to do extra handlings , rproc_add_virtio_dev will handle it .
*/
if ( ! strcmp ( it . node - > name , " vdev0buffer " ) )
continue ;
rmem = of_reserved_mem_lookup ( it . node ) ;
if ( ! rmem ) {
dev_err ( priv - > dev , " unable to acquire memory-region \n " ) ;
return - EINVAL ;
}
/* No need to translate pa to da, i.MX use same map */
da = rmem - > base ;
/* Register memory region */
mem = rproc_mem_entry_init ( priv - > dev , NULL , ( dma_addr_t ) rmem - > base , rmem - > size , da ,
imx_rproc_mem_alloc , imx_rproc_mem_release ,
it . node - > name ) ;
if ( mem )
rproc_coredump_add_segment ( rproc , da , rmem - > size ) ;
else
return - ENOMEM ;
rproc_add_carveout ( rproc , mem ) ;
}
return 0 ;
}
static int imx_rproc_parse_fw ( struct rproc * rproc , const struct firmware * fw )
{
2021-04-08 09:44:48 +08:00
int ret ;
2021-03-06 19:24:22 +08:00
ret = rproc_elf_load_rsc_table ( rproc , fw ) ;
if ( ret )
dev_info ( & rproc - > dev , " No resource table in elf \n " ) ;
return 0 ;
}
2021-03-06 19:24:25 +08:00
static void imx_rproc_kick ( struct rproc * rproc , int vqid )
{
struct imx_rproc * priv = rproc - > priv ;
int err ;
__u32 mmsg ;
if ( ! priv - > tx_ch ) {
dev_err ( priv - > dev , " No initialized mbox tx channel \n " ) ;
return ;
}
/*
* Send the index of the triggered virtqueue as the mu payload .
* Let remote processor know which virtqueue is used .
*/
mmsg = vqid < < 16 ;
err = mbox_send_message ( priv - > tx_ch , ( void * ) & mmsg ) ;
if ( err < 0 )
dev_err ( priv - > dev , " %s: failed (%d, err:%d) \n " ,
__func__ , vqid , err ) ;
}
2021-04-08 09:44:49 +08:00
static int imx_rproc_attach ( struct rproc * rproc )
{
return 0 ;
}
static struct resource_table * imx_rproc_get_loaded_rsc_table ( struct rproc * rproc , size_t * table_sz )
{
struct imx_rproc * priv = rproc - > priv ;
/* The resource table has already been mapped in imx_rproc_addr_init */
if ( ! priv - > rsc_table )
return NULL ;
* table_sz = SZ_1K ;
return ( struct resource_table * ) priv - > rsc_table ;
}
2017-08-17 09:15:26 +02:00
static const struct rproc_ops imx_rproc_ops = {
2021-04-08 09:44:48 +08:00
. prepare = imx_rproc_prepare ,
2021-04-08 09:44:49 +08:00
. attach = imx_rproc_attach ,
2017-08-17 09:15:26 +02:00
. start = imx_rproc_start ,
. stop = imx_rproc_stop ,
2021-03-06 19:24:25 +08:00
. kick = imx_rproc_kick ,
2017-08-17 09:15:26 +02:00
. da_to_va = imx_rproc_da_to_va ,
2021-03-06 19:24:22 +08:00
. load = rproc_elf_load_segments ,
. parse_fw = imx_rproc_parse_fw ,
. find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table ,
2021-04-08 09:44:49 +08:00
. get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table ,
2021-03-06 19:24:22 +08:00
. sanity_check = rproc_elf_sanity_check ,
. get_boot_addr = rproc_elf_get_boot_addr ,
2017-08-17 09:15:26 +02:00
} ;
static int imx_rproc_addr_init ( struct imx_rproc * priv ,
struct platform_device * pdev )
{
const struct imx_rproc_dcfg * dcfg = priv - > dcfg ;
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
int a , b = 0 , err , nph ;
/* remap required addresses */
for ( a = 0 ; a < dcfg - > att_size ; a + + ) {
const struct imx_rproc_att * att = & dcfg - > att [ a ] ;
if ( ! ( att - > flags & ATT_OWN ) )
continue ;
2021-04-08 09:44:47 +08:00
if ( b > = IMX_RPROC_MEM_MAX )
2017-08-17 09:15:26 +02:00
break ;
priv - > mem [ b ] . cpu_addr = devm_ioremap ( & pdev - > dev ,
att - > sa , att - > size ) ;
2017-10-11 10:48:44 +00:00
if ( ! priv - > mem [ b ] . cpu_addr ) {
2021-03-06 19:24:20 +08:00
dev_err ( dev , " failed to remap %#x bytes from %#x \n " , att - > size , att - > sa ) ;
2017-10-11 10:48:44 +00:00
return - ENOMEM ;
2017-08-17 09:15:26 +02:00
}
priv - > mem [ b ] . sys_addr = att - > sa ;
priv - > mem [ b ] . size = att - > size ;
b + + ;
}
/* memory-region is optional property */
nph = of_count_phandle_with_args ( np , " memory-region " , NULL ) ;
if ( nph < = 0 )
return 0 ;
/* remap optional addresses */
for ( a = 0 ; a < nph ; a + + ) {
struct device_node * node ;
struct resource res ;
node = of_parse_phandle ( np , " memory-region " , a ) ;
2021-03-06 19:24:24 +08:00
/* Not map vdev region */
if ( ! strcmp ( node - > name , " vdev " ) )
continue ;
2017-08-17 09:15:26 +02:00
err = of_address_to_resource ( node , 0 , & res ) ;
if ( err ) {
dev_err ( dev , " unable to resolve memory region \n " ) ;
return err ;
}
2021-04-08 09:44:46 +08:00
of_node_put ( node ) ;
2021-04-08 09:44:47 +08:00
if ( b > = IMX_RPROC_MEM_MAX )
2017-08-17 09:15:26 +02:00
break ;
2021-03-06 19:24:21 +08:00
/* Not use resource version, because we might share region */
priv - > mem [ b ] . cpu_addr = devm_ioremap ( & pdev - > dev , res . start , resource_size ( & res ) ) ;
2021-03-12 08:04:20 +00:00
if ( ! priv - > mem [ b ] . cpu_addr ) {
2021-03-06 19:24:20 +08:00
dev_err ( dev , " failed to remap %pr \n " , & res ) ;
2021-03-12 08:04:20 +00:00
return - ENOMEM ;
2017-08-17 09:15:26 +02:00
}
priv - > mem [ b ] . sys_addr = res . start ;
priv - > mem [ b ] . size = resource_size ( & res ) ;
2021-04-08 09:44:49 +08:00
if ( ! strcmp ( node - > name , " rsc_table " ) )
priv - > rsc_table = priv - > mem [ b ] . cpu_addr ;
2017-08-17 09:15:26 +02:00
b + + ;
}
return 0 ;
}
2021-03-06 19:24:25 +08:00
static void imx_rproc_vq_work ( struct work_struct * work )
{
struct imx_rproc * priv = container_of ( work , struct imx_rproc ,
rproc_work ) ;
rproc_vq_interrupt ( priv - > rproc , 0 ) ;
rproc_vq_interrupt ( priv - > rproc , 1 ) ;
}
static void imx_rproc_rx_callback ( struct mbox_client * cl , void * msg )
{
struct rproc * rproc = dev_get_drvdata ( cl - > dev ) ;
struct imx_rproc * priv = rproc - > priv ;
queue_work ( priv - > workqueue , & priv - > rproc_work ) ;
}
static int imx_rproc_xtr_mbox_init ( struct rproc * rproc )
{
struct imx_rproc * priv = rproc - > priv ;
struct device * dev = priv - > dev ;
struct mbox_client * cl ;
int ret ;
if ( ! of_get_property ( dev - > of_node , " mbox-names " , NULL ) )
return 0 ;
cl = & priv - > cl ;
cl - > dev = dev ;
cl - > tx_block = true ;
cl - > tx_tout = 100 ;
cl - > knows_txdone = false ;
cl - > rx_callback = imx_rproc_rx_callback ;
priv - > tx_ch = mbox_request_channel_byname ( cl , " tx " ) ;
if ( IS_ERR ( priv - > tx_ch ) ) {
ret = PTR_ERR ( priv - > tx_ch ) ;
return dev_err_probe ( cl - > dev , ret ,
" failed to request tx mailbox channel: %d \n " , ret ) ;
}
priv - > rx_ch = mbox_request_channel_byname ( cl , " rx " ) ;
if ( IS_ERR ( priv - > rx_ch ) ) {
mbox_free_channel ( priv - > tx_ch ) ;
ret = PTR_ERR ( priv - > rx_ch ) ;
return dev_err_probe ( cl - > dev , ret ,
" failed to request rx mailbox channel: %d \n " , ret ) ;
}
return 0 ;
}
static void imx_rproc_free_mbox ( struct rproc * rproc )
{
struct imx_rproc * priv = rproc - > priv ;
mbox_free_channel ( priv - > tx_ch ) ;
mbox_free_channel ( priv - > rx_ch ) ;
}
2021-04-08 09:44:49 +08:00
static int imx_rproc_detect_mode ( struct imx_rproc * priv )
{
2021-05-06 12:08:42 +08:00
struct regmap_config config = { . name = " imx-rproc " } ;
2021-04-08 09:44:49 +08:00
const struct imx_rproc_dcfg * dcfg = priv - > dcfg ;
struct device * dev = priv - > dev ;
2021-05-06 12:08:42 +08:00
struct regmap * regmap ;
2021-05-06 12:08:43 +08:00
struct arm_smccc_res res ;
2021-04-08 09:44:49 +08:00
int ret ;
u32 val ;
2021-05-06 12:08:42 +08:00
switch ( dcfg - > method ) {
case IMX_RPROC_NONE :
priv - > rproc - > state = RPROC_DETACHED ;
return 0 ;
2021-05-06 12:08:43 +08:00
case IMX_RPROC_SMC :
arm_smccc_smc ( IMX_SIP_RPROC , IMX_SIP_RPROC_STARTED , 0 , 0 , 0 , 0 , 0 , 0 , & res ) ;
if ( res . a0 )
priv - > rproc - > state = RPROC_DETACHED ;
return 0 ;
2021-05-06 12:08:42 +08:00
default :
break ;
}
regmap = syscon_regmap_lookup_by_phandle ( dev - > of_node , " syscon " ) ;
if ( IS_ERR ( regmap ) ) {
dev_err ( dev , " failed to find syscon \n " ) ;
return PTR_ERR ( regmap ) ;
}
priv - > regmap = regmap ;
regmap_attach_dev ( dev , regmap , & config ) ;
ret = regmap_read ( regmap , dcfg - > src_reg , & val ) ;
2021-04-08 09:44:49 +08:00
if ( ret ) {
dev_err ( dev , " Failed to read src \n " ) ;
return ret ;
}
if ( ! ( val & dcfg - > src_stop ) )
priv - > rproc - > state = RPROC_DETACHED ;
return 0 ;
}
2021-05-06 12:08:41 +08:00
static int imx_rproc_clk_enable ( struct imx_rproc * priv )
{
const struct imx_rproc_dcfg * dcfg = priv - > dcfg ;
struct device * dev = priv - > dev ;
int ret ;
/* Remote core is not under control of Linux */
if ( dcfg - > method = = IMX_RPROC_NONE )
return 0 ;
priv - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( priv - > clk ) ) {
dev_err ( dev , " Failed to get clock \n " ) ;
return PTR_ERR ( priv - > clk ) ;
}
/*
* clk for M4 block including memory . Should be
* enabled before . start for FW transfer .
*/
ret = clk_prepare_enable ( priv - > clk ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable clock \n " ) ;
return ret ;
}
return 0 ;
}
2017-08-17 09:15:26 +02:00
static int imx_rproc_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct imx_rproc * priv ;
struct rproc * rproc ;
const struct imx_rproc_dcfg * dcfg ;
int ret ;
/* set some other name then imx */
rproc = rproc_alloc ( dev , " imx-rproc " , & imx_rproc_ops ,
NULL , sizeof ( * priv ) ) ;
2018-03-14 20:56:39 +01:00
if ( ! rproc )
return - ENOMEM ;
2017-08-17 09:15:26 +02:00
dcfg = of_device_get_match_data ( dev ) ;
2018-03-14 20:56:37 +01:00
if ( ! dcfg ) {
ret = - EINVAL ;
goto err_put_rproc ;
}
2017-08-17 09:15:26 +02:00
priv = rproc - > priv ;
priv - > rproc = rproc ;
priv - > dcfg = dcfg ;
priv - > dev = dev ;
dev_set_drvdata ( dev , rproc ) ;
2021-03-06 19:24:25 +08:00
priv - > workqueue = create_workqueue ( dev_name ( dev ) ) ;
if ( ! priv - > workqueue ) {
dev_err ( dev , " cannot create workqueue \n " ) ;
ret = - ENOMEM ;
goto err_put_rproc ;
}
ret = imx_rproc_xtr_mbox_init ( rproc ) ;
if ( ret )
goto err_put_wkq ;
2017-08-17 09:15:26 +02:00
ret = imx_rproc_addr_init ( priv , pdev ) ;
if ( ret ) {
2019-06-03 20:46:28 -03:00
dev_err ( dev , " failed on imx_rproc_addr_init \n " ) ;
2021-03-06 19:24:25 +08:00
goto err_put_mbox ;
2017-08-17 09:15:26 +02:00
}
2021-04-08 09:44:49 +08:00
ret = imx_rproc_detect_mode ( priv ) ;
if ( ret )
goto err_put_mbox ;
2021-05-06 12:08:41 +08:00
ret = imx_rproc_clk_enable ( priv ) ;
if ( ret )
2021-03-06 19:24:25 +08:00
goto err_put_mbox ;
2017-08-17 09:15:26 +02:00
2021-03-06 19:24:25 +08:00
INIT_WORK ( & priv - > rproc_work , imx_rproc_vq_work ) ;
2021-05-06 12:08:39 +08:00
if ( rproc - > state ! = RPROC_DETACHED )
rproc - > auto_boot = of_property_read_bool ( np , " fsl,auto-boot " ) ;
2017-08-17 09:15:26 +02:00
ret = rproc_add ( rproc ) ;
if ( ret ) {
dev_err ( dev , " rproc_add failed \n " ) ;
goto err_put_clk ;
}
2018-03-14 20:56:39 +01:00
return 0 ;
2017-08-17 09:15:26 +02:00
err_put_clk :
clk_disable_unprepare ( priv - > clk ) ;
2021-03-06 19:24:25 +08:00
err_put_mbox :
imx_rproc_free_mbox ( rproc ) ;
err_put_wkq :
destroy_workqueue ( priv - > workqueue ) ;
2017-08-17 09:15:26 +02:00
err_put_rproc :
rproc_free ( rproc ) ;
2018-03-14 20:56:39 +01:00
2017-08-17 09:15:26 +02:00
return ret ;
}
static int imx_rproc_remove ( struct platform_device * pdev )
{
struct rproc * rproc = platform_get_drvdata ( pdev ) ;
struct imx_rproc * priv = rproc - > priv ;
clk_disable_unprepare ( priv - > clk ) ;
rproc_del ( rproc ) ;
2021-03-06 19:24:25 +08:00
imx_rproc_free_mbox ( rproc ) ;
2017-08-17 09:15:26 +02:00
rproc_free ( rproc ) ;
return 0 ;
}
static const struct of_device_id imx_rproc_of_match [ ] = {
2021-05-06 12:08:42 +08:00
{ . compatible = " fsl,imx7ulp-cm4 " , . data = & imx_rproc_cfg_imx7ulp } ,
2017-08-17 09:15:26 +02:00
{ . compatible = " fsl,imx7d-cm4 " , . data = & imx_rproc_cfg_imx7d } ,
{ . compatible = " fsl,imx6sx-cm4 " , . data = & imx_rproc_cfg_imx6sx } ,
2021-03-06 19:24:23 +08:00
{ . compatible = " fsl,imx8mq-cm4 " , . data = & imx_rproc_cfg_imx8mq } ,
{ . compatible = " fsl,imx8mm-cm4 " , . data = & imx_rproc_cfg_imx8mq } ,
2021-05-06 12:08:43 +08:00
{ . compatible = " fsl,imx8mn-cm7 " , . data = & imx_rproc_cfg_imx8mn } ,
{ . compatible = " fsl,imx8mp-cm7 " , . data = & imx_rproc_cfg_imx8mn } ,
2021-06-22 14:01:48 +08:00
{ . compatible = " fsl,imx8ulp-cm33 " , . data = & imx_rproc_cfg_imx8ulp } ,
2017-08-17 09:15:26 +02:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , imx_rproc_of_match ) ;
static struct platform_driver imx_rproc_driver = {
. probe = imx_rproc_probe ,
. remove = imx_rproc_remove ,
. driver = {
. name = " imx-rproc " ,
. of_match_table = imx_rproc_of_match ,
} ,
} ;
module_platform_driver ( imx_rproc_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2021-03-06 19:24:23 +08:00
MODULE_DESCRIPTION ( " i.MX remote processor control driver " ) ;
2017-08-17 09:15:26 +02:00
MODULE_AUTHOR ( " Oleksij Rempel <o.rempel@pengutronix.de> " ) ;