2019-05-14 11:26:58 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) STMicroelectronics 2018 - All Rights Reserved
* Authors : Ludovic Barre < ludovic . barre @ st . com > for STMicroelectronics .
* Fabien Dessenne < fabien . dessenne @ st . com > for STMicroelectronics .
*/
# include <linux/arm-smccc.h>
# include <linux/dma-mapping.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/mailbox_client.h>
# include <linux/mfd/syscon.h>
# include <linux/module.h>
2023-07-14 20:49:33 +03:00
# include <linux/of.h>
2019-05-14 11:26:58 +03:00
# include <linux/of_reserved_mem.h>
2023-07-14 20:49:33 +03:00
# include <linux/platform_device.h>
2019-08-26 18:38:43 +03:00
# include <linux/pm_wakeirq.h>
2019-05-14 11:26:58 +03:00
# include <linux/regmap.h>
# include <linux/remoteproc.h>
# include <linux/reset.h>
2020-04-10 00:09:35 +03:00
# include <linux/slab.h>
2019-10-25 11:56:39 +03:00
# include <linux/workqueue.h>
2019-05-14 11:26:58 +03:00
# include "remoteproc_internal.h"
# define HOLD_BOOT 0
# define RELEASE_BOOT 1
# define MBOX_NB_VQ 2
2021-03-31 10:33:47 +03:00
# define MBOX_NB_MBX 4
2019-05-14 11:26:58 +03:00
# define STM32_SMC_RCC 0x82001000
# define STM32_SMC_REG_WRITE 0x1
# define STM32_MBX_VQ0 "vq0"
2019-10-25 11:56:39 +03:00
# define STM32_MBX_VQ0_ID 0
2019-05-14 11:26:58 +03:00
# define STM32_MBX_VQ1 "vq1"
2019-10-25 11:56:39 +03:00
# define STM32_MBX_VQ1_ID 1
2019-05-14 11:26:58 +03:00
# define STM32_MBX_SHUTDOWN "shutdown"
2021-03-31 10:33:47 +03:00
# define STM32_MBX_DETACH "detach"
2019-05-14 11:26:58 +03:00
2020-07-14 23:04:43 +03:00
# define RSC_TBL_SIZE 1024
2020-07-14 23:04:40 +03:00
# define M4_STATE_OFF 0
# define M4_STATE_INI 1
# define M4_STATE_CRUN 2
# define M4_STATE_CSTOP 3
# define M4_STATE_STANDBY 4
# define M4_STATE_CRASH 5
2019-05-14 11:26:58 +03:00
struct stm32_syscon {
struct regmap * map ;
u32 reg ;
u32 mask ;
} ;
struct stm32_rproc_mem {
char name [ 20 ] ;
void __iomem * cpu_addr ;
phys_addr_t bus_addr ;
u32 dev_addr ;
size_t size ;
} ;
struct stm32_rproc_mem_ranges {
u32 dev_addr ;
u32 bus_addr ;
u32 size ;
} ;
struct stm32_mbox {
const unsigned char name [ 10 ] ;
struct mbox_chan * chan ;
struct mbox_client client ;
2019-10-25 11:56:39 +03:00
struct work_struct vq_work ;
2019-05-14 11:26:58 +03:00
int vq_id ;
} ;
struct stm32_rproc {
struct reset_control * rst ;
2023-05-12 12:39:24 +03:00
struct reset_control * hold_boot_rst ;
2019-05-14 11:26:58 +03:00
struct stm32_syscon hold_boot ;
struct stm32_syscon pdds ;
2020-07-14 23:04:39 +03:00
struct stm32_syscon m4_state ;
struct stm32_syscon rsctbl ;
2019-08-26 18:38:43 +03:00
int wdg_irq ;
2019-05-14 11:26:58 +03:00
u32 nb_rmems ;
struct stm32_rproc_mem * rmems ;
struct stm32_mbox mb [ MBOX_NB_MBX ] ;
2019-10-25 11:56:39 +03:00
struct workqueue_struct * workqueue ;
2023-05-12 12:39:24 +03:00
bool hold_boot_smc ;
2020-07-14 23:04:43 +03:00
void __iomem * rsc_va ;
2019-05-14 11:26:58 +03:00
} ;
static int stm32_rproc_pa_to_da ( struct rproc * rproc , phys_addr_t pa , u64 * da )
{
unsigned int i ;
struct stm32_rproc * ddata = rproc - > priv ;
struct stm32_rproc_mem * p_mem ;
for ( i = 0 ; i < ddata - > nb_rmems ; i + + ) {
p_mem = & ddata - > rmems [ i ] ;
if ( pa < p_mem - > bus_addr | |
pa > = p_mem - > bus_addr + p_mem - > size )
continue ;
* da = pa - p_mem - > bus_addr + p_mem - > dev_addr ;
dev_dbg ( rproc - > dev . parent , " pa %pa to da %llx \n " , & pa , * da ) ;
return 0 ;
}
return - EINVAL ;
}
static int stm32_rproc_mem_alloc ( struct rproc * rproc ,
struct rproc_mem_entry * mem )
{
struct device * dev = rproc - > dev . parent ;
void * va ;
remoteproc: stm32: use correct format strings on 64-bit
With CONFIG_ARCH_STM32 making it into arch/arm64, a couple of format
strings no longer work, since they rely on size_t being compatible
with %x, or they print an 'int' using %z:
drivers/remoteproc/stm32_rproc.c: In function 'stm32_rproc_mem_alloc':
drivers/remoteproc/stm32_rproc.c:122:22: error: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'size_t' {aka 'long unsigned int'} [-Werror=format=]
drivers/remoteproc/stm32_rproc.c:122:40: note: format string is defined here
122 | dev_dbg(dev, "map memory: %pa+%x\n", &mem->dma, mem->len);
| ~^
| |
| unsigned int
| %lx
drivers/remoteproc/stm32_rproc.c:125:30: error: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'size_t' {aka 'long unsigned int'} [-Werror=format=]
drivers/remoteproc/stm32_rproc.c:125:65: note: format string is defined here
125 | dev_err(dev, "Unable to map memory region: %pa+%x\n",
| ~^
| |
| unsigned int
| %lx
drivers/remoteproc/stm32_rproc.c: In function 'stm32_rproc_get_loaded_rsc_table':
drivers/remoteproc/stm32_rproc.c:646:30: error: format '%zx' expects argument of type 'size_t', but argument 4 has type 'int' [-Werror=format=]
drivers/remoteproc/stm32_rproc.c:646:66: note: format string is defined here
646 | dev_err(dev, "Unable to map memory region: %pa+%zx\n",
| ~~^
| |
| long unsigned int
| %x
Fix up all three instances to work across architectures, and enable
compile testing for this driver to ensure it builds everywhere.
Reviewed-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
2023-06-09 13:45:42 +03:00
dev_dbg ( dev , " map memory: %pad+%zx \n " , & mem - > dma , mem - > len ) ;
2019-05-14 11:26:58 +03:00
va = ioremap_wc ( mem - > dma , mem - > len ) ;
if ( IS_ERR_OR_NULL ( va ) ) {
remoteproc: stm32: use correct format strings on 64-bit
With CONFIG_ARCH_STM32 making it into arch/arm64, a couple of format
strings no longer work, since they rely on size_t being compatible
with %x, or they print an 'int' using %z:
drivers/remoteproc/stm32_rproc.c: In function 'stm32_rproc_mem_alloc':
drivers/remoteproc/stm32_rproc.c:122:22: error: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'size_t' {aka 'long unsigned int'} [-Werror=format=]
drivers/remoteproc/stm32_rproc.c:122:40: note: format string is defined here
122 | dev_dbg(dev, "map memory: %pa+%x\n", &mem->dma, mem->len);
| ~^
| |
| unsigned int
| %lx
drivers/remoteproc/stm32_rproc.c:125:30: error: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'size_t' {aka 'long unsigned int'} [-Werror=format=]
drivers/remoteproc/stm32_rproc.c:125:65: note: format string is defined here
125 | dev_err(dev, "Unable to map memory region: %pa+%x\n",
| ~^
| |
| unsigned int
| %lx
drivers/remoteproc/stm32_rproc.c: In function 'stm32_rproc_get_loaded_rsc_table':
drivers/remoteproc/stm32_rproc.c:646:30: error: format '%zx' expects argument of type 'size_t', but argument 4 has type 'int' [-Werror=format=]
drivers/remoteproc/stm32_rproc.c:646:66: note: format string is defined here
646 | dev_err(dev, "Unable to map memory region: %pa+%zx\n",
| ~~^
| |
| long unsigned int
| %x
Fix up all three instances to work across architectures, and enable
compile testing for this driver to ensure it builds everywhere.
Reviewed-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
2023-06-09 13:45:42 +03:00
dev_err ( dev , " Unable to map memory region: %pad+0x%zx \n " ,
2019-05-14 11:26:58 +03:00
& mem - > dma , mem - > len ) ;
return - ENOMEM ;
}
/* Update memory entry va */
mem - > va = va ;
return 0 ;
}
static int stm32_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 ;
}
2020-07-14 23:04:35 +03:00
static int stm32_rproc_of_memory_translations ( struct platform_device * pdev ,
struct stm32_rproc * ddata )
2019-05-14 11:26:58 +03:00
{
2020-07-14 23:04:35 +03:00
struct device * parent , * dev = & pdev - > dev ;
2019-05-14 11:26:58 +03:00
struct device_node * np ;
struct stm32_rproc_mem * p_mems ;
struct stm32_rproc_mem_ranges * mem_range ;
int cnt , array_size , i , ret = 0 ;
parent = dev - > parent ;
np = parent - > of_node ;
cnt = of_property_count_elems_of_size ( np , " dma-ranges " ,
sizeof ( * mem_range ) ) ;
if ( cnt < = 0 ) {
dev_err ( dev , " %s: dma-ranges property not defined \n " , __func__ ) ;
return - EINVAL ;
}
p_mems = devm_kcalloc ( dev , cnt , sizeof ( * p_mems ) , GFP_KERNEL ) ;
if ( ! p_mems )
return - ENOMEM ;
mem_range = kcalloc ( cnt , sizeof ( * mem_range ) , GFP_KERNEL ) ;
if ( ! mem_range )
return - ENOMEM ;
array_size = cnt * sizeof ( struct stm32_rproc_mem_ranges ) / sizeof ( u32 ) ;
ret = of_property_read_u32_array ( np , " dma-ranges " ,
( u32 * ) mem_range , array_size ) ;
if ( ret ) {
dev_err ( dev , " error while get dma-ranges property: %x \n " , ret ) ;
goto free_mem ;
}
for ( i = 0 ; i < cnt ; i + + ) {
p_mems [ i ] . bus_addr = mem_range [ i ] . bus_addr ;
p_mems [ i ] . dev_addr = mem_range [ i ] . dev_addr ;
p_mems [ i ] . size = mem_range [ i ] . size ;
dev_dbg ( dev , " memory range[%i]: da %#x, pa %pa, size %#zx: \n " ,
i , p_mems [ i ] . dev_addr , & p_mems [ i ] . bus_addr ,
p_mems [ i ] . size ) ;
}
ddata - > rmems = p_mems ;
ddata - > nb_rmems = cnt ;
free_mem :
kfree ( mem_range ) ;
return ret ;
}
static int stm32_rproc_mbox_idx ( struct rproc * rproc , const unsigned char * name )
{
struct stm32_rproc * ddata = rproc - > priv ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( ddata - > mb ) ; i + + ) {
if ( ! strncmp ( ddata - > mb [ i ] . name , name , strlen ( name ) ) )
return i ;
}
dev_err ( & rproc - > dev , " mailbox %s not found \n " , name ) ;
return - EINVAL ;
}
2021-03-12 19:24:43 +03:00
static int stm32_rproc_prepare ( struct rproc * rproc )
2019-05-14 11:26:58 +03:00
{
struct device * dev = rproc - > dev . parent ;
struct device_node * np = dev - > of_node ;
struct of_phandle_iterator it ;
struct rproc_mem_entry * mem ;
struct reserved_mem * rmem ;
u64 da ;
int index = 0 ;
/* Register associated reserved memory regions */
of_phandle_iterator_init ( & it , np , " memory-region " , NULL , 0 ) ;
while ( of_phandle_iterator_next ( & it ) = = 0 ) {
rmem = of_reserved_mem_lookup ( it . node ) ;
if ( ! rmem ) {
2023-03-21 01:18:22 +03:00
of_node_put ( it . node ) ;
2019-05-14 11:26:58 +03:00
dev_err ( dev , " unable to acquire memory-region \n " ) ;
return - EINVAL ;
}
if ( stm32_rproc_pa_to_da ( rproc , rmem - > base , & da ) < 0 ) {
2023-03-21 01:18:22 +03:00
of_node_put ( it . node ) ;
2019-05-14 11:26:58 +03:00
dev_err ( dev , " memory region not valid %pa \n " ,
& rmem - > base ) ;
return - EINVAL ;
}
/* No need to map vdev buffer */
if ( strcmp ( it . node - > name , " vdev0buffer " ) ) {
/* Register memory region */
mem = rproc_mem_entry_init ( dev , NULL ,
( dma_addr_t ) rmem - > base ,
rmem - > size , da ,
stm32_rproc_mem_alloc ,
stm32_rproc_mem_release ,
it . node - > name ) ;
if ( mem )
rproc_coredump_add_segment ( rproc , da ,
rmem - > size ) ;
} else {
/* Register reserved memory for vdev buffer alloc */
mem = rproc_of_resm_mem_entry_init ( dev , index ,
rmem - > size ,
rmem - > base ,
it . node - > name ) ;
}
2023-03-21 01:18:22 +03:00
if ( ! mem ) {
of_node_put ( it . node ) ;
2019-05-14 11:26:58 +03:00
return - ENOMEM ;
2023-03-21 01:18:22 +03:00
}
2019-05-14 11:26:58 +03:00
rproc_add_carveout ( rproc , mem ) ;
index + + ;
}
2020-07-14 23:04:42 +03:00
return 0 ;
}
static int stm32_rproc_parse_fw ( struct rproc * rproc , const struct firmware * fw )
{
2021-03-12 19:24:43 +03:00
if ( rproc_elf_load_rsc_table ( rproc , fw ) )
dev_warn ( & rproc - > dev , " no resource table found for this firmware \n " ) ;
2020-07-14 23:04:42 +03:00
2021-03-12 19:24:43 +03:00
return 0 ;
2019-05-14 11:26:58 +03:00
}
static irqreturn_t stm32_rproc_wdg ( int irq , void * data )
{
2020-07-14 23:04:36 +03:00
struct platform_device * pdev = data ;
struct rproc * rproc = platform_get_drvdata ( pdev ) ;
2019-05-14 11:26:58 +03:00
rproc_report_crash ( rproc , RPROC_WATCHDOG ) ;
return IRQ_HANDLED ;
}
2019-10-25 11:56:39 +03:00
static void stm32_rproc_mb_vq_work ( struct work_struct * work )
{
struct stm32_mbox * mb = container_of ( work , struct stm32_mbox , vq_work ) ;
struct rproc * rproc = dev_get_drvdata ( mb - > client . dev ) ;
2023-03-31 19:06:34 +03:00
mutex_lock ( & rproc - > lock ) ;
if ( rproc - > state ! = RPROC_RUNNING )
goto unlock_mutex ;
2019-10-25 11:56:39 +03:00
if ( rproc_vq_interrupt ( rproc , mb - > vq_id ) = = IRQ_NONE )
dev_dbg ( & rproc - > dev , " no message found in vq%d \n " , mb - > vq_id ) ;
2023-03-31 19:06:34 +03:00
unlock_mutex :
mutex_unlock ( & rproc - > lock ) ;
2019-10-25 11:56:39 +03:00
}
2019-05-14 11:26:58 +03:00
static void stm32_rproc_mb_callback ( struct mbox_client * cl , void * data )
{
struct rproc * rproc = dev_get_drvdata ( cl - > dev ) ;
struct stm32_mbox * mb = container_of ( cl , struct stm32_mbox , client ) ;
2019-10-25 11:56:39 +03:00
struct stm32_rproc * ddata = rproc - > priv ;
2019-05-14 11:26:58 +03:00
2019-10-25 11:56:39 +03:00
queue_work ( ddata - > workqueue , & mb - > vq_work ) ;
2019-05-14 11:26:58 +03:00
}
static void stm32_rproc_free_mbox ( struct rproc * rproc )
{
struct stm32_rproc * ddata = rproc - > priv ;
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( ddata - > mb ) ; i + + ) {
if ( ddata - > mb [ i ] . chan )
mbox_free_channel ( ddata - > mb [ i ] . chan ) ;
ddata - > mb [ i ] . chan = NULL ;
}
}
static const struct stm32_mbox stm32_rproc_mbox [ MBOX_NB_MBX ] = {
{
. name = STM32_MBX_VQ0 ,
2019-10-25 11:56:39 +03:00
. vq_id = STM32_MBX_VQ0_ID ,
2019-05-14 11:26:58 +03:00
. client = {
. rx_callback = stm32_rproc_mb_callback ,
. tx_block = false ,
} ,
} ,
{
. name = STM32_MBX_VQ1 ,
2019-10-25 11:56:39 +03:00
. vq_id = STM32_MBX_VQ1_ID ,
2019-05-14 11:26:58 +03:00
. client = {
. rx_callback = stm32_rproc_mb_callback ,
. tx_block = false ,
} ,
} ,
{
. name = STM32_MBX_SHUTDOWN ,
. vq_id = - 1 ,
. client = {
. tx_block = true ,
. tx_done = NULL ,
. tx_tout = 500 , /* 500 ms time out */
} ,
2021-03-31 10:33:47 +03:00
} ,
{
. name = STM32_MBX_DETACH ,
. vq_id = - 1 ,
. client = {
. tx_block = true ,
. tx_done = NULL ,
. tx_tout = 200 , /* 200 ms time out to detach should be fair enough */
} ,
2019-05-14 11:26:58 +03:00
}
} ;
2019-11-15 13:03:08 +03:00
static int stm32_rproc_request_mbox ( struct rproc * rproc )
2019-05-14 11:26:58 +03:00
{
struct stm32_rproc * ddata = rproc - > priv ;
struct device * dev = & rproc - > dev ;
unsigned int i ;
2019-11-15 13:03:08 +03:00
int j ;
2019-05-14 11:26:58 +03:00
const unsigned char * name ;
struct mbox_client * cl ;
/* Initialise mailbox structure table */
memcpy ( ddata - > mb , stm32_rproc_mbox , sizeof ( stm32_rproc_mbox ) ) ;
for ( i = 0 ; i < MBOX_NB_MBX ; i + + ) {
name = ddata - > mb [ i ] . name ;
cl = & ddata - > mb [ i ] . client ;
cl - > dev = dev - > parent ;
ddata - > mb [ i ] . chan = mbox_request_channel_byname ( cl , name ) ;
if ( IS_ERR ( ddata - > mb [ i ] . chan ) ) {
2020-12-17 17:41:25 +03:00
if ( PTR_ERR ( ddata - > mb [ i ] . chan ) = = - EPROBE_DEFER ) {
dev_err_probe ( dev - > parent ,
PTR_ERR ( ddata - > mb [ i ] . chan ) ,
" failed to request mailbox %s \n " ,
name ) ;
2019-11-15 13:03:08 +03:00
goto err_probe ;
2020-12-17 17:41:25 +03:00
}
2019-05-14 11:26:58 +03:00
dev_warn ( dev , " cannot get %s mbox \n " , name ) ;
ddata - > mb [ i ] . chan = NULL ;
}
2019-10-25 11:56:39 +03:00
if ( ddata - > mb [ i ] . vq_id > = 0 ) {
INIT_WORK ( & ddata - > mb [ i ] . vq_work ,
stm32_rproc_mb_vq_work ) ;
}
2019-05-14 11:26:58 +03:00
}
2019-11-15 13:03:08 +03:00
return 0 ;
err_probe :
for ( j = i - 1 ; j > = 0 ; j - - )
if ( ddata - > mb [ j ] . chan )
mbox_free_channel ( ddata - > mb [ j ] . chan ) ;
return - EPROBE_DEFER ;
2019-05-14 11:26:58 +03:00
}
static int stm32_rproc_set_hold_boot ( struct rproc * rproc , bool hold )
{
struct stm32_rproc * ddata = rproc - > priv ;
struct stm32_syscon hold_boot = ddata - > hold_boot ;
struct arm_smccc_res smc_res ;
int val , err ;
2023-05-12 12:39:24 +03:00
/*
* Three ways to manage the hold boot
* - using SCMI : the hold boot is managed as a reset ,
* - using Linux ( no SCMI ) : the hold boot is managed as a syscon register
* - using SMC call ( deprecated ) : use SMC reset interface
*/
2019-05-14 11:26:58 +03:00
val = hold ? HOLD_BOOT : RELEASE_BOOT ;
2023-05-12 12:39:24 +03:00
if ( ddata - > hold_boot_rst ) {
/* Use the SCMI reset controller */
if ( ! hold )
err = reset_control_deassert ( ddata - > hold_boot_rst ) ;
else
err = reset_control_assert ( ddata - > hold_boot_rst ) ;
} else if ( IS_ENABLED ( CONFIG_HAVE_ARM_SMCCC ) & & ddata - > hold_boot_smc ) {
/* Use the SMC call */
2019-05-14 11:26:58 +03:00
arm_smccc_smc ( STM32_SMC_RCC , STM32_SMC_REG_WRITE ,
hold_boot . reg , val , 0 , 0 , 0 , 0 , & smc_res ) ;
err = smc_res . a0 ;
} else {
2023-05-12 12:39:24 +03:00
/* Use syscon */
2019-05-14 11:26:58 +03:00
err = regmap_update_bits ( hold_boot . map , hold_boot . reg ,
hold_boot . mask , val ) ;
}
if ( err )
dev_err ( & rproc - > dev , " failed to set hold boot \n " ) ;
return err ;
}
static void stm32_rproc_add_coredump_trace ( struct rproc * rproc )
{
struct rproc_debug_trace * trace ;
struct rproc_dump_segment * segment ;
bool already_added ;
list_for_each_entry ( trace , & rproc - > traces , node ) {
already_added = false ;
list_for_each_entry ( segment , & rproc - > dump_segments , node ) {
if ( segment - > da = = trace - > trace_mem . da ) {
already_added = true ;
break ;
}
}
if ( ! already_added )
rproc_coredump_add_segment ( rproc , trace - > trace_mem . da ,
trace - > trace_mem . len ) ;
}
}
static int stm32_rproc_start ( struct rproc * rproc )
{
2019-08-26 18:38:06 +03:00
struct stm32_rproc * ddata = rproc - > priv ;
2019-05-14 11:26:58 +03:00
int err ;
stm32_rproc_add_coredump_trace ( rproc ) ;
2019-08-26 18:38:06 +03:00
/* clear remote proc Deep Sleep */
if ( ddata - > pdds . map ) {
err = regmap_update_bits ( ddata - > pdds . map , ddata - > pdds . reg ,
ddata - > pdds . mask , 0 ) ;
if ( err ) {
dev_err ( & rproc - > dev , " failed to clear pdds \n " ) ;
return err ;
}
}
2019-05-14 11:26:58 +03:00
err = stm32_rproc_set_hold_boot ( rproc , false ) ;
if ( err )
return err ;
return stm32_rproc_set_hold_boot ( rproc , true ) ;
}
2020-07-14 23:04:44 +03:00
static int stm32_rproc_attach ( struct rproc * rproc )
{
stm32_rproc_add_coredump_trace ( rproc ) ;
return stm32_rproc_set_hold_boot ( rproc , true ) ;
}
2021-03-31 10:33:47 +03:00
static int stm32_rproc_detach ( struct rproc * rproc )
{
struct stm32_rproc * ddata = rproc - > priv ;
2021-04-20 12:19:22 +03:00
int err , idx ;
2021-03-31 10:33:47 +03:00
/* Inform the remote processor of the detach */
idx = stm32_rproc_mbox_idx ( rproc , STM32_MBX_DETACH ) ;
if ( idx > = 0 & & ddata - > mb [ idx ] . chan ) {
2021-04-20 12:19:22 +03:00
err = mbox_send_message ( ddata - > mb [ idx ] . chan , " stop " ) ;
2021-03-31 10:33:47 +03:00
if ( err < 0 )
dev_warn ( & rproc - > dev , " warning: remote FW detach without ack \n " ) ;
}
/* Allow remote processor to auto-reboot */
return stm32_rproc_set_hold_boot ( rproc , false ) ;
}
2019-05-14 11:26:58 +03:00
static int stm32_rproc_stop ( struct rproc * rproc )
{
struct stm32_rproc * ddata = rproc - > priv ;
2021-04-20 12:19:22 +03:00
int err , idx ;
2019-05-14 11:26:58 +03:00
/* request shutdown of the remote processor */
2021-12-21 17:31:29 +03:00
if ( rproc - > state ! = RPROC_OFFLINE & & rproc - > state ! = RPROC_CRASHED ) {
2019-05-14 11:26:58 +03:00
idx = stm32_rproc_mbox_idx ( rproc , STM32_MBX_SHUTDOWN ) ;
if ( idx > = 0 & & ddata - > mb [ idx ] . chan ) {
2021-04-20 12:19:22 +03:00
err = mbox_send_message ( ddata - > mb [ idx ] . chan , " detach " ) ;
2019-05-14 11:26:58 +03:00
if ( err < 0 )
dev_warn ( & rproc - > dev , " warning: remote FW shutdown without ack \n " ) ;
}
}
err = stm32_rproc_set_hold_boot ( rproc , true ) ;
if ( err )
return err ;
err = reset_control_assert ( ddata - > rst ) ;
if ( err ) {
dev_err ( & rproc - > dev , " failed to assert the reset \n " ) ;
return err ;
}
/* to allow platform Standby power mode, set remote proc Deep Sleep */
if ( ddata - > pdds . map ) {
err = regmap_update_bits ( ddata - > pdds . map , ddata - > pdds . reg ,
ddata - > pdds . mask , 1 ) ;
if ( err ) {
dev_err ( & rproc - > dev , " failed to set pdds \n " ) ;
return err ;
}
}
2020-07-14 23:04:45 +03:00
/* update coprocessor state to OFF if available */
if ( ddata - > m4_state . map ) {
err = regmap_update_bits ( ddata - > m4_state . map ,
ddata - > m4_state . reg ,
ddata - > m4_state . mask ,
M4_STATE_OFF ) ;
if ( err ) {
dev_err ( & rproc - > dev , " failed to set copro state \n " ) ;
return err ;
}
}
2019-05-14 11:26:58 +03:00
return 0 ;
}
static void stm32_rproc_kick ( struct rproc * rproc , int vqid )
{
struct stm32_rproc * ddata = rproc - > priv ;
unsigned int i ;
int err ;
if ( WARN_ON ( vqid > = MBOX_NB_VQ ) )
return ;
for ( i = 0 ; i < MBOX_NB_MBX ; i + + ) {
if ( vqid ! = ddata - > mb [ i ] . vq_id )
continue ;
if ( ! ddata - > mb [ i ] . chan )
return ;
2021-04-20 12:19:22 +03:00
err = mbox_send_message ( ddata - > mb [ i ] . chan , " kick " ) ;
2019-05-14 11:26:58 +03:00
if ( err < 0 )
dev_err ( & rproc - > dev , " %s: failed (%s, err:%d) \n " ,
__func__ , ddata - > mb [ i ] . name , err ) ;
return ;
}
}
2021-03-12 19:24:42 +03:00
static int stm32_rproc_da_to_pa ( struct rproc * rproc ,
u64 da , phys_addr_t * pa )
{
struct stm32_rproc * ddata = rproc - > priv ;
struct device * dev = rproc - > dev . parent ;
struct stm32_rproc_mem * p_mem ;
unsigned int i ;
for ( i = 0 ; i < ddata - > nb_rmems ; i + + ) {
p_mem = & ddata - > rmems [ i ] ;
if ( da < p_mem - > dev_addr | |
da > = p_mem - > dev_addr + p_mem - > size )
continue ;
* pa = da - p_mem - > dev_addr + p_mem - > bus_addr ;
2021-04-21 17:00:40 +03:00
dev_dbg ( dev , " da %llx to pa %pap \n " , da , pa ) ;
2021-03-12 19:24:42 +03:00
return 0 ;
}
dev_err ( dev , " can't translate da %llx \n " , da ) ;
return - EINVAL ;
}
static struct resource_table *
stm32_rproc_get_loaded_rsc_table ( struct rproc * rproc , size_t * table_sz )
{
struct stm32_rproc * ddata = rproc - > priv ;
struct device * dev = rproc - > dev . parent ;
phys_addr_t rsc_pa ;
u32 rsc_da ;
int err ;
/* The resource table has already been mapped, nothing to do */
if ( ddata - > rsc_va )
goto done ;
err = regmap_read ( ddata - > rsctbl . map , ddata - > rsctbl . reg , & rsc_da ) ;
if ( err ) {
dev_err ( dev , " failed to read rsc tbl addr \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
if ( ! rsc_da )
/* no rsc table */
return ERR_PTR ( - ENOENT ) ;
err = stm32_rproc_da_to_pa ( rproc , rsc_da , & rsc_pa ) ;
if ( err )
return ERR_PTR ( err ) ;
ddata - > rsc_va = devm_ioremap_wc ( dev , rsc_pa , RSC_TBL_SIZE ) ;
if ( IS_ERR_OR_NULL ( ddata - > rsc_va ) ) {
remoteproc: stm32: use correct format strings on 64-bit
With CONFIG_ARCH_STM32 making it into arch/arm64, a couple of format
strings no longer work, since they rely on size_t being compatible
with %x, or they print an 'int' using %z:
drivers/remoteproc/stm32_rproc.c: In function 'stm32_rproc_mem_alloc':
drivers/remoteproc/stm32_rproc.c:122:22: error: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'size_t' {aka 'long unsigned int'} [-Werror=format=]
drivers/remoteproc/stm32_rproc.c:122:40: note: format string is defined here
122 | dev_dbg(dev, "map memory: %pa+%x\n", &mem->dma, mem->len);
| ~^
| |
| unsigned int
| %lx
drivers/remoteproc/stm32_rproc.c:125:30: error: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'size_t' {aka 'long unsigned int'} [-Werror=format=]
drivers/remoteproc/stm32_rproc.c:125:65: note: format string is defined here
125 | dev_err(dev, "Unable to map memory region: %pa+%x\n",
| ~^
| |
| unsigned int
| %lx
drivers/remoteproc/stm32_rproc.c: In function 'stm32_rproc_get_loaded_rsc_table':
drivers/remoteproc/stm32_rproc.c:646:30: error: format '%zx' expects argument of type 'size_t', but argument 4 has type 'int' [-Werror=format=]
drivers/remoteproc/stm32_rproc.c:646:66: note: format string is defined here
646 | dev_err(dev, "Unable to map memory region: %pa+%zx\n",
| ~~^
| |
| long unsigned int
| %x
Fix up all three instances to work across architectures, and enable
compile testing for this driver to ensure it builds everywhere.
Reviewed-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
2023-06-09 13:45:42 +03:00
dev_err ( dev , " Unable to map memory region: %pa+%x \n " ,
2021-03-12 19:24:42 +03:00
& rsc_pa , RSC_TBL_SIZE ) ;
ddata - > rsc_va = NULL ;
return ERR_PTR ( - ENOMEM ) ;
}
done :
2021-03-31 10:33:47 +03:00
/*
* Assuming the resource table fits in 1 kB is fair .
* Notice for the detach , that this 1 kB memory area has to be reserved in the coprocessor
* firmware for the resource table . On detach , the remoteproc core re - initializes this
* entire area by overwriting it with the initial values stored in rproc - > clean_table .
*/
2021-03-12 19:24:42 +03:00
* table_sz = RSC_TBL_SIZE ;
return ( struct resource_table * ) ddata - > rsc_va ;
}
2020-11-08 02:36:30 +03:00
static const struct rproc_ops st_rproc_ops = {
2021-03-12 19:24:43 +03:00
. prepare = stm32_rproc_prepare ,
2019-05-14 11:26:58 +03:00
. start = stm32_rproc_start ,
. stop = stm32_rproc_stop ,
2020-07-14 23:04:44 +03:00
. attach = stm32_rproc_attach ,
2021-03-31 10:33:47 +03:00
. detach = stm32_rproc_detach ,
2019-05-14 11:26:58 +03:00
. kick = stm32_rproc_kick ,
. load = rproc_elf_load_segments ,
. parse_fw = stm32_rproc_parse_fw ,
. find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table ,
2021-03-12 19:24:42 +03:00
. get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table ,
2020-04-22 12:30:17 +03:00
. sanity_check = rproc_elf_sanity_check ,
2019-05-14 11:26:58 +03:00
. get_boot_addr = rproc_elf_get_boot_addr ,
} ;
static const struct of_device_id stm32_rproc_match [ ] = {
{ . compatible = " st,stm32mp1-m4 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , stm32_rproc_match ) ;
static int stm32_rproc_get_syscon ( struct device_node * np , const char * prop ,
struct stm32_syscon * syscon )
{
int err = 0 ;
syscon - > map = syscon_regmap_lookup_by_phandle ( np , prop ) ;
if ( IS_ERR ( syscon - > map ) ) {
err = PTR_ERR ( syscon - > map ) ;
syscon - > map = NULL ;
goto out ;
}
err = of_property_read_u32_index ( np , prop , 1 , & syscon - > reg ) ;
if ( err )
goto out ;
err = of_property_read_u32_index ( np , prop , 2 , & syscon - > mask ) ;
out :
return err ;
}
2020-07-14 23:04:37 +03:00
static int stm32_rproc_parse_dt ( struct platform_device * pdev ,
struct stm32_rproc * ddata , bool * auto_boot )
2019-05-14 11:26:58 +03:00
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct stm32_syscon tz ;
unsigned int tzen ;
int err , irq ;
2023-08-17 11:33:36 +03:00
irq = platform_get_irq_optional ( pdev , 0 ) ;
2019-08-26 18:38:29 +03:00
if ( irq = = - EPROBE_DEFER )
2023-08-17 11:33:36 +03:00
return irq ;
2019-08-26 18:38:29 +03:00
2019-05-14 11:26:58 +03:00
if ( irq > 0 ) {
err = devm_request_irq ( dev , irq , stm32_rproc_wdg , 0 ,
2020-07-14 23:04:36 +03:00
dev_name ( dev ) , pdev ) ;
2020-12-17 17:41:25 +03:00
if ( err )
return dev_err_probe ( dev , err ,
" failed to request wdg irq \n " ) ;
2019-05-14 11:26:58 +03:00
2019-08-26 18:38:43 +03:00
ddata - > wdg_irq = irq ;
if ( of_property_read_bool ( np , " wakeup-source " ) ) {
device_init_wakeup ( dev , true ) ;
dev_pm_set_wake_irq ( dev , irq ) ;
}
2019-05-14 11:26:58 +03:00
dev_info ( dev , " wdg irq registered \n " ) ;
}
2023-05-12 12:39:24 +03:00
ddata - > rst = devm_reset_control_get_optional ( dev , " mcu_rst " ) ;
if ( ! ddata - > rst ) {
/* Try legacy fallback method: get it by index */
ddata - > rst = devm_reset_control_get_by_index ( dev , 0 ) ;
}
2020-12-17 17:41:25 +03:00
if ( IS_ERR ( ddata - > rst ) )
return dev_err_probe ( dev , PTR_ERR ( ddata - > rst ) ,
" failed to get mcu_reset \n " ) ;
2019-05-14 11:26:58 +03:00
/*
2023-05-12 12:39:24 +03:00
* Three ways to manage the hold boot
* - using SCMI : the hold boot is managed as a reset
* The DT " reset-mames " property should be defined with 2 items :
* reset - names = " mcu_rst " , " hold_boot " ;
* - using SMC call ( deprecated ) : use SMC reset interface
* The DT " reset-mames " property is optional , " st,syscfg-tz " is required
* - default ( no SCMI , no SMC ) : the hold boot is managed as a syscon register
* The DT " reset-mames " property is optional , " st,syscfg-holdboot " is required
2019-05-14 11:26:58 +03:00
*/
2023-05-12 12:39:24 +03:00
ddata - > hold_boot_rst = devm_reset_control_get_optional ( dev , " hold_boot " ) ;
if ( IS_ERR ( ddata - > hold_boot_rst ) )
2023-05-22 10:46:11 +03:00
return dev_err_probe ( dev , PTR_ERR ( ddata - > hold_boot_rst ) ,
2023-05-12 12:39:24 +03:00
" failed to get hold_boot reset \n " ) ;
if ( ! ddata - > hold_boot_rst & & IS_ENABLED ( CONFIG_HAVE_ARM_SMCCC ) ) {
/* Manage the MCU_BOOT using SMC call */
err = stm32_rproc_get_syscon ( np , " st,syscfg-tz " , & tz ) ;
if ( ! err ) {
err = regmap_read ( tz . map , tz . reg , & tzen ) ;
if ( err ) {
dev_err ( dev , " failed to read tzen \n " ) ;
return err ;
}
ddata - > hold_boot_smc = tzen & tz . mask ;
}
2019-05-14 11:26:58 +03:00
}
2023-05-12 12:39:24 +03:00
if ( ! ddata - > hold_boot_rst & & ! ddata - > hold_boot_smc ) {
/* Default: hold boot manage it through the syscon controller */
err = stm32_rproc_get_syscon ( np , " st,syscfg-holdboot " ,
& ddata - > hold_boot ) ;
if ( err ) {
dev_err ( dev , " failed to get hold boot \n " ) ;
return err ;
}
2019-05-14 11:26:58 +03:00
}
err = stm32_rproc_get_syscon ( np , " st,syscfg-pdds " , & ddata - > pdds ) ;
if ( err )
2020-02-24 20:25:19 +03:00
dev_info ( dev , " failed to get pdds \n " ) ;
2019-05-14 11:26:58 +03:00
2020-07-14 23:04:37 +03:00
* auto_boot = of_property_read_bool ( np , " st,auto-boot " ) ;
2019-05-14 11:26:58 +03:00
2020-07-14 23:04:39 +03:00
/*
* See if we can check the M4 status , i . e if it was started
* from the boot loader or not .
*/
err = stm32_rproc_get_syscon ( np , " st,syscfg-m4-state " ,
& ddata - > m4_state ) ;
if ( err ) {
/* remember this */
ddata - > m4_state . map = NULL ;
/* no coprocessor state syscon (optional) */
dev_warn ( dev , " m4 state not supported \n " ) ;
/* no need to go further */
return 0 ;
}
/* See if we can get the resource table */
err = stm32_rproc_get_syscon ( np , " st,syscfg-rsc-tbl " ,
& ddata - > rsctbl ) ;
if ( err ) {
/* no rsc table syscon (optional) */
dev_warn ( dev , " rsc tbl syscon not supported \n " ) ;
}
2020-07-14 23:04:38 +03:00
return 0 ;
2019-05-14 11:26:58 +03:00
}
2020-07-14 23:04:40 +03:00
static int stm32_rproc_get_m4_status ( struct stm32_rproc * ddata ,
unsigned int * state )
{
/* See stm32_rproc_parse_dt() */
if ( ! ddata - > m4_state . map ) {
/*
* We couldn ' t get the coprocessor ' s state , assume
* it is not running .
*/
2020-09-01 00:37:58 +03:00
* state = M4_STATE_OFF ;
2020-07-14 23:04:40 +03:00
return 0 ;
}
return regmap_read ( ddata - > m4_state . map , ddata - > m4_state . reg , state ) ;
}
2019-05-14 11:26:58 +03:00
static int stm32_rproc_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct stm32_rproc * ddata ;
struct device_node * np = dev - > of_node ;
struct rproc * rproc ;
2020-07-14 23:04:40 +03:00
unsigned int state ;
2019-05-14 11:26:58 +03:00
int ret ;
ret = dma_coerce_mask_and_coherent ( dev , DMA_BIT_MASK ( 32 ) ) ;
if ( ret )
return ret ;
rproc = rproc_alloc ( dev , np - > name , & st_rproc_ops , NULL , sizeof ( * ddata ) ) ;
if ( ! rproc )
return - ENOMEM ;
2020-07-14 23:04:37 +03:00
ddata = rproc - > priv ;
2020-04-10 13:24:33 +03:00
rproc_coredump_set_elf_info ( rproc , ELFCLASS32 , EM_NONE ) ;
2020-07-14 23:04:37 +03:00
ret = stm32_rproc_parse_dt ( pdev , ddata , & rproc - > auto_boot ) ;
if ( ret )
goto free_rproc ;
2020-07-14 23:04:38 +03:00
ret = stm32_rproc_of_memory_translations ( pdev , ddata ) ;
if ( ret )
goto free_rproc ;
2020-07-14 23:04:40 +03:00
ret = stm32_rproc_get_m4_status ( ddata , & state ) ;
if ( ret )
goto free_rproc ;
2021-03-12 19:24:43 +03:00
if ( state = = M4_STATE_CRUN )
2020-07-14 23:04:40 +03:00
rproc - > state = RPROC_DETACHED ;
2019-05-14 11:26:58 +03:00
rproc - > has_iommu = false ;
2019-10-25 11:56:39 +03:00
ddata - > workqueue = create_workqueue ( dev_name ( dev ) ) ;
if ( ! ddata - > workqueue ) {
dev_err ( dev , " cannot create workqueue \n " ) ;
ret = - ENOMEM ;
2020-07-14 23:04:42 +03:00
goto free_resources ;
2019-10-25 11:56:39 +03:00
}
2019-05-14 11:26:58 +03:00
platform_set_drvdata ( pdev , rproc ) ;
2019-11-15 13:03:08 +03:00
ret = stm32_rproc_request_mbox ( rproc ) ;
if ( ret )
2020-07-14 23:04:37 +03:00
goto free_wkq ;
2019-05-14 11:26:58 +03:00
ret = rproc_add ( rproc ) ;
if ( ret )
goto free_mb ;
return 0 ;
free_mb :
stm32_rproc_free_mbox ( rproc ) ;
2019-10-25 11:56:39 +03:00
free_wkq :
destroy_workqueue ( ddata - > workqueue ) ;
2020-07-14 23:04:42 +03:00
free_resources :
rproc_resource_cleanup ( rproc ) ;
2019-05-14 11:26:58 +03:00
free_rproc :
2019-08-26 18:38:43 +03:00
if ( device_may_wakeup ( dev ) ) {
dev_pm_clear_wake_irq ( dev ) ;
device_init_wakeup ( dev , false ) ;
}
2019-05-14 11:26:58 +03:00
rproc_free ( rproc ) ;
return ret ;
}
2023-05-04 22:44:52 +03:00
static void stm32_rproc_remove ( struct platform_device * pdev )
2019-05-14 11:26:58 +03:00
{
struct rproc * rproc = platform_get_drvdata ( pdev ) ;
2019-10-25 11:56:39 +03:00
struct stm32_rproc * ddata = rproc - > priv ;
2019-08-26 18:38:43 +03:00
struct device * dev = & pdev - > dev ;
2019-05-14 11:26:58 +03:00
if ( atomic_read ( & rproc - > power ) > 0 )
rproc_shutdown ( rproc ) ;
rproc_del ( rproc ) ;
stm32_rproc_free_mbox ( rproc ) ;
2019-10-25 11:56:39 +03:00
destroy_workqueue ( ddata - > workqueue ) ;
2019-08-26 18:38:43 +03:00
if ( device_may_wakeup ( dev ) ) {
dev_pm_clear_wake_irq ( dev ) ;
device_init_wakeup ( dev , false ) ;
}
2019-05-14 11:26:58 +03:00
rproc_free ( rproc ) ;
}
2023-07-24 22:56:49 +03:00
static int stm32_rproc_suspend ( struct device * dev )
2019-08-26 18:38:43 +03:00
{
struct rproc * rproc = dev_get_drvdata ( dev ) ;
struct stm32_rproc * ddata = rproc - > priv ;
if ( device_may_wakeup ( dev ) )
return enable_irq_wake ( ddata - > wdg_irq ) ;
return 0 ;
}
2023-07-24 22:56:49 +03:00
static int stm32_rproc_resume ( struct device * dev )
2019-08-26 18:38:43 +03:00
{
struct rproc * rproc = dev_get_drvdata ( dev ) ;
struct stm32_rproc * ddata = rproc - > priv ;
if ( device_may_wakeup ( dev ) )
return disable_irq_wake ( ddata - > wdg_irq ) ;
return 0 ;
}
2023-07-24 22:56:49 +03:00
static DEFINE_SIMPLE_DEV_PM_OPS ( stm32_rproc_pm_ops ,
stm32_rproc_suspend , stm32_rproc_resume ) ;
2019-08-26 18:38:43 +03:00
2019-05-14 11:26:58 +03:00
static struct platform_driver stm32_rproc_driver = {
. probe = stm32_rproc_probe ,
2023-05-04 22:44:52 +03:00
. remove_new = stm32_rproc_remove ,
2019-05-14 11:26:58 +03:00
. driver = {
. name = " stm32-rproc " ,
2023-07-24 22:56:49 +03:00
. pm = pm_ptr ( & stm32_rproc_pm_ops ) ,
. of_match_table = stm32_rproc_match ,
2019-05-14 11:26:58 +03:00
} ,
} ;
module_platform_driver ( stm32_rproc_driver ) ;
MODULE_DESCRIPTION ( " STM32 Remote Processor Control Driver " ) ;
MODULE_AUTHOR ( " Ludovic Barre <ludovic.barre@st.com> " ) ;
MODULE_AUTHOR ( " Fabien Dessenne <fabien.dessenne@st.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;