2021-10-11 12:20:14 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2021-10-13 05:39:09 +03:00
/* Copyright 2021 NXP */
2021-10-11 12:20:14 +03:00
# include <dt-bindings/firmware/imx/rsrc.h>
# include <linux/arm-smccc.h>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/firmware.h>
# include <linux/firmware/imx/sci.h>
# include <linux/interrupt.h>
# include <linux/kernel.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>
2021-10-11 12:20:14 +03:00
# include <linux/of_reserved_mem.h>
# include <linux/platform_device.h>
# include <linux/pm_domain.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <linux/remoteproc.h>
# include <linux/slab.h>
# include "imx_rproc.h"
# include "remoteproc_elf_helpers.h"
# include "remoteproc_internal.h"
# define DSP_RPROC_CLK_MAX 5
2023-02-17 12:41:24 +03:00
/*
* Module parameters
*/
static unsigned int no_mailboxes ;
module_param_named ( no_mailboxes , no_mailboxes , int , 0644 ) ;
MODULE_PARM_DESC ( no_mailboxes ,
" There is no mailbox between cores, so ignore remote proc reply after start, default is 0 (off). " ) ;
2021-10-11 12:20:14 +03:00
# define REMOTE_IS_READY BIT(0)
# define REMOTE_READY_WAIT_MAX_RETRIES 500
/* att flags */
/* DSP own area */
# define ATT_OWN BIT(31)
/* DSP instruction area */
# define ATT_IRAM BIT(30)
/* Definitions for i.MX8MP */
/* DAP registers */
# define IMX8M_DAP_DEBUG 0x28800000
# define IMX8M_DAP_DEBUG_SIZE (64 * 1024)
# define IMX8M_DAP_PWRCTL (0x4000 + 0x3020)
# define IMX8M_PWRCTL_CORERESET BIT(16)
/* DSP audio mix registers */
# define IMX8M_AudioDSP_REG0 0x100
# define IMX8M_AudioDSP_REG1 0x104
# define IMX8M_AudioDSP_REG2 0x108
# define IMX8M_AudioDSP_REG3 0x10c
# define IMX8M_AudioDSP_REG2_RUNSTALL BIT(5)
# define IMX8M_AudioDSP_REG2_PWAITMODE BIT(1)
/* Definitions for i.MX8ULP */
# define IMX8ULP_SIM_LPAV_REG_SYSCTRL0 0x8
# define IMX8ULP_SYSCTRL0_DSP_DBG_RST BIT(25)
# define IMX8ULP_SYSCTRL0_DSP_PLAT_CLK_EN BIT(19)
# define IMX8ULP_SYSCTRL0_DSP_PBCLK_EN BIT(18)
# define IMX8ULP_SYSCTRL0_DSP_CLK_EN BIT(17)
# define IMX8ULP_SYSCTRL0_DSP_RST BIT(16)
# define IMX8ULP_SYSCTRL0_DSP_OCD_HALT BIT(14)
# define IMX8ULP_SYSCTRL0_DSP_STALL BIT(13)
# define IMX8ULP_SIP_HIFI_XRDC 0xc200000e
/*
* enum - Predefined Mailbox Messages
*
* @ RP_MBOX_SUSPEND_SYSTEM : system suspend request for the remote processor
*
* @ RP_MBOX_SUSPEND_ACK : successful response from remote processor for a
* suspend request
*
* @ RP_MBOX_RESUME_SYSTEM : system resume request for the remote processor
*
* @ RP_MBOX_RESUME_ACK : successful response from remote processor for a
* resume request
*/
enum imx_dsp_rp_mbox_messages {
RP_MBOX_SUSPEND_SYSTEM = 0xFF11 ,
RP_MBOX_SUSPEND_ACK = 0xFF12 ,
RP_MBOX_RESUME_SYSTEM = 0xFF13 ,
RP_MBOX_RESUME_ACK = 0xFF14 ,
} ;
/**
* struct imx_dsp_rproc - DSP remote processor state
* @ regmap : regmap handler
* @ rproc : rproc handler
* @ dsp_dcfg : device configuration pointer
* @ clks : clocks needed by this device
* @ cl : mailbox client to request the mailbox channel
* @ cl_rxdb : mailbox client to request the mailbox channel for doorbell
* @ tx_ch : mailbox tx channel handle
* @ rx_ch : mailbox rx channel handle
* @ rxdb_ch : mailbox rx doorbell channel handle
* @ pd_dev : power domain device
* @ pd_dev_link : power domain device link
* @ ipc_handle : System Control Unit ipc handle
* @ rproc_work : work for processing virtio interrupts
* @ pm_comp : completion primitive to sync for suspend response
* @ num_domains : power domain number
* @ flags : control flags
*/
struct imx_dsp_rproc {
struct regmap * regmap ;
struct rproc * rproc ;
const struct imx_dsp_rproc_dcfg * dsp_dcfg ;
struct clk_bulk_data clks [ DSP_RPROC_CLK_MAX ] ;
struct mbox_client cl ;
struct mbox_client cl_rxdb ;
struct mbox_chan * tx_ch ;
struct mbox_chan * rx_ch ;
struct mbox_chan * rxdb_ch ;
struct device * * pd_dev ;
struct device_link * * pd_dev_link ;
struct imx_sc_ipc * ipc_handle ;
struct work_struct rproc_work ;
struct completion pm_comp ;
int num_domains ;
u32 flags ;
} ;
/**
* struct imx_dsp_rproc_dcfg - DSP remote processor configuration
* @ dcfg : imx_rproc_dcfg handler
* @ reset : reset callback function
*/
struct imx_dsp_rproc_dcfg {
const struct imx_rproc_dcfg * dcfg ;
int ( * reset ) ( struct imx_dsp_rproc * priv ) ;
} ;
static const struct imx_rproc_att imx_dsp_rproc_att_imx8qm [ ] = {
/* dev addr , sys addr , size , flags */
{ 0x596e8000 , 0x556e8000 , 0x00008000 , ATT_OWN } ,
{ 0x596f0000 , 0x556f0000 , 0x00008000 , ATT_OWN } ,
{ 0x596f8000 , 0x556f8000 , 0x00000800 , ATT_OWN | ATT_IRAM } ,
{ 0x55700000 , 0x55700000 , 0x00070000 , ATT_OWN } ,
/* DDR (Data) */
{ 0x80000000 , 0x80000000 , 0x60000000 , 0 } ,
} ;
static const struct imx_rproc_att imx_dsp_rproc_att_imx8qxp [ ] = {
/* dev addr , sys addr , size , flags */
{ 0x596e8000 , 0x596e8000 , 0x00008000 , ATT_OWN } ,
{ 0x596f0000 , 0x596f0000 , 0x00008000 , ATT_OWN } ,
{ 0x596f8000 , 0x596f8000 , 0x00000800 , ATT_OWN | ATT_IRAM } ,
{ 0x59700000 , 0x59700000 , 0x00070000 , ATT_OWN } ,
/* DDR (Data) */
{ 0x80000000 , 0x80000000 , 0x60000000 , 0 } ,
} ;
static const struct imx_rproc_att imx_dsp_rproc_att_imx8mp [ ] = {
/* dev addr , sys addr , size , flags */
{ 0x3b6e8000 , 0x3b6e8000 , 0x00008000 , ATT_OWN } ,
{ 0x3b6f0000 , 0x3b6f0000 , 0x00008000 , ATT_OWN } ,
{ 0x3b6f8000 , 0x3b6f8000 , 0x00000800 , ATT_OWN | ATT_IRAM } ,
{ 0x3b700000 , 0x3b700000 , 0x00040000 , ATT_OWN } ,
/* DDR (Data) */
{ 0x40000000 , 0x40000000 , 0x80000000 , 0 } ,
} ;
static const struct imx_rproc_att imx_dsp_rproc_att_imx8ulp [ ] = {
/* dev addr , sys addr , size , flags */
{ 0x21170000 , 0x21170000 , 0x00010000 , ATT_OWN | ATT_IRAM } ,
{ 0x21180000 , 0x21180000 , 0x00010000 , ATT_OWN } ,
/* DDR (Data) */
{ 0x0c000000 , 0x80000000 , 0x10000000 , 0 } ,
{ 0x30000000 , 0x90000000 , 0x10000000 , 0 } ,
} ;
2023-02-17 12:41:24 +03:00
/* Initialize the mailboxes between cores, if exists */
static int ( * imx_dsp_rproc_mbox_init ) ( struct imx_dsp_rproc * priv ) ;
2021-10-11 12:20:14 +03:00
/* Reset function for DSP on i.MX8MP */
static int imx8mp_dsp_reset ( struct imx_dsp_rproc * priv )
{
void __iomem * dap = ioremap_wc ( IMX8M_DAP_DEBUG , IMX8M_DAP_DEBUG_SIZE ) ;
int pwrctl ;
/* Put DSP into reset and stall */
pwrctl = readl ( dap + IMX8M_DAP_PWRCTL ) ;
pwrctl | = IMX8M_PWRCTL_CORERESET ;
writel ( pwrctl , dap + IMX8M_DAP_PWRCTL ) ;
/* Keep reset asserted for 10 cycles */
usleep_range ( 1 , 2 ) ;
regmap_update_bits ( priv - > regmap , IMX8M_AudioDSP_REG2 ,
IMX8M_AudioDSP_REG2_RUNSTALL ,
IMX8M_AudioDSP_REG2_RUNSTALL ) ;
/* Take the DSP out of reset and keep stalled for FW loading */
pwrctl = readl ( dap + IMX8M_DAP_PWRCTL ) ;
pwrctl & = ~ IMX8M_PWRCTL_CORERESET ;
writel ( pwrctl , dap + IMX8M_DAP_PWRCTL ) ;
iounmap ( dap ) ;
return 0 ;
}
/* Reset function for DSP on i.MX8ULP */
static int imx8ulp_dsp_reset ( struct imx_dsp_rproc * priv )
{
struct arm_smccc_res res ;
/* Put DSP into reset and stall */
regmap_update_bits ( priv - > regmap , IMX8ULP_SIM_LPAV_REG_SYSCTRL0 ,
IMX8ULP_SYSCTRL0_DSP_RST , IMX8ULP_SYSCTRL0_DSP_RST ) ;
regmap_update_bits ( priv - > regmap , IMX8ULP_SIM_LPAV_REG_SYSCTRL0 ,
IMX8ULP_SYSCTRL0_DSP_STALL ,
IMX8ULP_SYSCTRL0_DSP_STALL ) ;
/* Configure resources of DSP through TFA */
arm_smccc_smc ( IMX8ULP_SIP_HIFI_XRDC , 0 , 0 , 0 , 0 , 0 , 0 , 0 , & res ) ;
/* Take the DSP out of reset and keep stalled for FW loading */
regmap_update_bits ( priv - > regmap , IMX8ULP_SIM_LPAV_REG_SYSCTRL0 ,
IMX8ULP_SYSCTRL0_DSP_RST , 0 ) ;
regmap_update_bits ( priv - > regmap , IMX8ULP_SIM_LPAV_REG_SYSCTRL0 ,
IMX8ULP_SYSCTRL0_DSP_DBG_RST , 0 ) ;
return 0 ;
}
/* Specific configuration for i.MX8MP */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8mp = {
. src_reg = IMX8M_AudioDSP_REG2 ,
. src_mask = IMX8M_AudioDSP_REG2_RUNSTALL ,
. src_start = 0 ,
. src_stop = IMX8M_AudioDSP_REG2_RUNSTALL ,
. att = imx_dsp_rproc_att_imx8mp ,
. att_size = ARRAY_SIZE ( imx_dsp_rproc_att_imx8mp ) ,
. method = IMX_RPROC_MMIO ,
} ;
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8mp = {
. dcfg = & dsp_rproc_cfg_imx8mp ,
. reset = imx8mp_dsp_reset ,
} ;
/* Specific configuration for i.MX8ULP */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8ulp = {
. src_reg = IMX8ULP_SIM_LPAV_REG_SYSCTRL0 ,
. src_mask = IMX8ULP_SYSCTRL0_DSP_STALL ,
. src_start = 0 ,
. src_stop = IMX8ULP_SYSCTRL0_DSP_STALL ,
. att = imx_dsp_rproc_att_imx8ulp ,
. att_size = ARRAY_SIZE ( imx_dsp_rproc_att_imx8ulp ) ,
. method = IMX_RPROC_MMIO ,
} ;
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8ulp = {
. dcfg = & dsp_rproc_cfg_imx8ulp ,
. reset = imx8ulp_dsp_reset ,
} ;
/* Specific configuration for i.MX8QXP */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8qxp = {
. att = imx_dsp_rproc_att_imx8qxp ,
. att_size = ARRAY_SIZE ( imx_dsp_rproc_att_imx8qxp ) ,
. method = IMX_RPROC_SCU_API ,
} ;
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8qxp = {
. dcfg = & dsp_rproc_cfg_imx8qxp ,
} ;
/* Specific configuration for i.MX8QM */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8qm = {
. att = imx_dsp_rproc_att_imx8qm ,
. att_size = ARRAY_SIZE ( imx_dsp_rproc_att_imx8qm ) ,
. method = IMX_RPROC_SCU_API ,
} ;
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8qm = {
. dcfg = & dsp_rproc_cfg_imx8qm ,
} ;
static int imx_dsp_rproc_ready ( struct rproc * rproc )
{
struct imx_dsp_rproc * priv = rproc - > priv ;
int i ;
if ( ! priv - > rxdb_ch )
return 0 ;
for ( i = 0 ; i < REMOTE_READY_WAIT_MAX_RETRIES ; i + + ) {
if ( priv - > flags & REMOTE_IS_READY )
return 0 ;
usleep_range ( 100 , 200 ) ;
}
return - ETIMEDOUT ;
}
/*
* Start function for rproc_ops
*
* There is a handshake for start procedure : when DSP starts , it
* will send a doorbell message to this driver , then the
* REMOTE_IS_READY flags is set , then driver will kick
* a message to DSP .
*/
static int imx_dsp_rproc_start ( struct rproc * rproc )
{
struct imx_dsp_rproc * priv = rproc - > priv ;
const struct imx_dsp_rproc_dcfg * dsp_dcfg = priv - > dsp_dcfg ;
const struct imx_rproc_dcfg * dcfg = dsp_dcfg - > dcfg ;
struct device * dev = rproc - > dev . parent ;
int ret ;
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_SCU_API :
ret = imx_sc_pm_cpu_start ( priv - > ipc_handle ,
IMX_SC_R_DSP ,
true ,
rproc - > bootaddr ) ;
break ;
default :
return - EOPNOTSUPP ;
}
if ( ret )
dev_err ( dev , " Failed to enable remote core! \n " ) ;
else
ret = imx_dsp_rproc_ready ( rproc ) ;
return ret ;
}
/*
* Stop function for rproc_ops
* It clears the REMOTE_IS_READY flags
*/
static int imx_dsp_rproc_stop ( struct rproc * rproc )
{
struct imx_dsp_rproc * priv = rproc - > priv ;
const struct imx_dsp_rproc_dcfg * dsp_dcfg = priv - > dsp_dcfg ;
const struct imx_rproc_dcfg * dcfg = dsp_dcfg - > dcfg ;
struct device * dev = rproc - > dev . parent ;
int ret = 0 ;
if ( rproc - > state = = RPROC_CRASHED ) {
priv - > flags & = ~ REMOTE_IS_READY ;
return 0 ;
}
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_SCU_API :
ret = imx_sc_pm_cpu_start ( priv - > ipc_handle ,
IMX_SC_R_DSP ,
false ,
rproc - > bootaddr ) ;
break ;
default :
return - EOPNOTSUPP ;
}
if ( ret )
dev_err ( dev , " Failed to stop remote core \n " ) ;
else
priv - > flags & = ~ REMOTE_IS_READY ;
return ret ;
}
/**
* imx_dsp_rproc_sys_to_da ( ) - internal memory translation helper
* @ priv : private data pointer
* @ sys : system address ( DDR address )
* @ len : length of the memory buffer
* @ da : device address to translate
*
* Convert system address ( DDR address ) to device address ( DSP )
* for there may be memory remap for device .
*/
static int imx_dsp_rproc_sys_to_da ( struct imx_dsp_rproc * priv , u64 sys ,
size_t len , u64 * da )
{
const struct imx_dsp_rproc_dcfg * dsp_dcfg = priv - > dsp_dcfg ;
const struct imx_rproc_dcfg * dcfg = dsp_dcfg - > 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 ( sys > = att - > sa & & sys + len < = att - > sa + att - > size ) {
unsigned int offset = sys - att - > sa ;
* da = att - > da + offset ;
return 0 ;
}
}
return - ENOENT ;
}
/* Main virtqueue message work function
*
* This function is executed upon scheduling of the i . MX DSP remoteproc
* driver ' s workqueue . The workqueue is scheduled by the mailbox rx
* handler .
*
* This work function processes both the Tx and Rx virtqueue indices on
* every invocation . The rproc_vq_interrupt function can detect if there
* are new unprocessed messages or not ( returns IRQ_NONE vs IRQ_HANDLED ) ,
* but there is no need to check for these return values . The index 0
* triggering will process all pending Rx buffers , and the index 1 triggering
* will process all newly available Tx buffers and will wakeup any potentially
* blocked senders .
*
* NOTE :
* The current logic is based on an inherent design assumption of supporting
* only 2 vrings , but this can be changed if needed .
*/
static void imx_dsp_rproc_vq_work ( struct work_struct * work )
{
struct imx_dsp_rproc * priv = container_of ( work , struct imx_dsp_rproc ,
rproc_work ) ;
2022-09-30 10:50:16 +03:00
struct rproc * rproc = priv - > rproc ;
mutex_lock ( & rproc - > lock ) ;
if ( rproc - > state ! = RPROC_RUNNING )
goto unlock_mutex ;
2021-10-11 12:20:14 +03:00
rproc_vq_interrupt ( priv - > rproc , 0 ) ;
rproc_vq_interrupt ( priv - > rproc , 1 ) ;
2022-09-30 10:50:16 +03:00
unlock_mutex :
mutex_unlock ( & rproc - > lock ) ;
2021-10-11 12:20:14 +03:00
}
/**
* imx_dsp_rproc_rx_tx_callback ( ) - inbound mailbox message handler
* @ cl : mailbox client pointer used for requesting the mailbox channel
* @ data : mailbox payload
*
* This handler is invoked by mailbox driver whenever a mailbox
* message is received . Usually , the SUSPEND and RESUME related messages
* are handled in this function , other messages are handled by remoteproc core
*/
static void imx_dsp_rproc_rx_tx_callback ( struct mbox_client * cl , void * data )
{
struct rproc * rproc = dev_get_drvdata ( cl - > dev ) ;
struct imx_dsp_rproc * priv = rproc - > priv ;
struct device * dev = rproc - > dev . parent ;
u32 message = ( u32 ) ( * ( u32 * ) data ) ;
dev_dbg ( dev , " mbox msg: 0x%x \n " , message ) ;
switch ( message ) {
case RP_MBOX_SUSPEND_ACK :
complete ( & priv - > pm_comp ) ;
break ;
case RP_MBOX_RESUME_ACK :
complete ( & priv - > pm_comp ) ;
break ;
default :
schedule_work ( & priv - > rproc_work ) ;
break ;
}
}
/**
* imx_dsp_rproc_rxdb_callback ( ) - inbound mailbox message handler
* @ cl : mailbox client pointer used for requesting the mailbox channel
* @ data : mailbox payload
*
* For doorbell , there is no message specified , just set REMOTE_IS_READY
* flag .
*/
static void imx_dsp_rproc_rxdb_callback ( struct mbox_client * cl , void * data )
{
struct rproc * rproc = dev_get_drvdata ( cl - > dev ) ;
struct imx_dsp_rproc * priv = rproc - > priv ;
/* Remote is ready after firmware is loaded and running */
priv - > flags | = REMOTE_IS_READY ;
}
/**
2023-02-17 12:41:24 +03:00
* imx_dsp_rproc_mbox_alloc ( ) - request mailbox channels
2021-10-11 12:20:14 +03:00
* @ priv : private data pointer
*
* Request three mailbox channels ( tx , rx , rxdb ) .
*/
2023-02-17 12:41:24 +03:00
static int imx_dsp_rproc_mbox_alloc ( struct imx_dsp_rproc * priv )
2021-10-11 12:20:14 +03:00
{
struct device * dev = priv - > rproc - > dev . parent ;
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_dsp_rproc_rx_tx_callback ;
/* Channel for sending message */
priv - > tx_ch = mbox_request_channel_byname ( cl , " tx " ) ;
if ( IS_ERR ( priv - > tx_ch ) ) {
ret = PTR_ERR ( priv - > tx_ch ) ;
dev_dbg ( cl - > dev , " failed to request tx mailbox channel: %d \n " ,
ret ) ;
2023-04-06 23:00:24 +03:00
return ret ;
2021-10-11 12:20:14 +03:00
}
/* Channel for receiving message */
priv - > rx_ch = mbox_request_channel_byname ( cl , " rx " ) ;
if ( IS_ERR ( priv - > rx_ch ) ) {
ret = PTR_ERR ( priv - > rx_ch ) ;
dev_dbg ( cl - > dev , " failed to request rx mailbox channel: %d \n " ,
ret ) ;
2023-04-06 23:00:24 +03:00
goto free_channel_tx ;
2021-10-11 12:20:14 +03:00
}
cl = & priv - > cl_rxdb ;
cl - > dev = dev ;
cl - > rx_callback = imx_dsp_rproc_rxdb_callback ;
/*
* RX door bell is used to receive the ready signal from remote
* after firmware loaded .
*/
priv - > rxdb_ch = mbox_request_channel_byname ( cl , " rxdb " ) ;
if ( IS_ERR ( priv - > rxdb_ch ) ) {
ret = PTR_ERR ( priv - > rxdb_ch ) ;
dev_dbg ( cl - > dev , " failed to request mbox chan rxdb, ret %d \n " ,
ret ) ;
2023-04-06 23:00:24 +03:00
goto free_channel_rx ;
2021-10-11 12:20:14 +03:00
}
return 0 ;
2023-04-06 23:00:24 +03:00
free_channel_rx :
mbox_free_channel ( priv - > rx_ch ) ;
free_channel_tx :
mbox_free_channel ( priv - > tx_ch ) ;
2021-10-11 12:20:14 +03:00
return ret ;
}
2023-02-17 12:41:24 +03:00
/*
* imx_dsp_rproc_mbox_no_alloc ( )
*
* Empty function for no mailbox between cores
*
* Always return 0
*/
static int imx_dsp_rproc_mbox_no_alloc ( struct imx_dsp_rproc * priv )
{
return 0 ;
}
2021-10-11 12:20:14 +03:00
static void imx_dsp_rproc_free_mbox ( struct imx_dsp_rproc * priv )
{
mbox_free_channel ( priv - > tx_ch ) ;
mbox_free_channel ( priv - > rx_ch ) ;
mbox_free_channel ( priv - > rxdb_ch ) ;
}
/**
* imx_dsp_rproc_add_carveout ( ) - request mailbox channels
* @ priv : private data pointer
*
* This function registers specified memory entry in @ rproc carveouts list
* The carveouts can help to mapping the memory address for DSP .
*/
static int imx_dsp_rproc_add_carveout ( struct imx_dsp_rproc * priv )
{
const struct imx_dsp_rproc_dcfg * dsp_dcfg = priv - > dsp_dcfg ;
const struct imx_rproc_dcfg * dcfg = dsp_dcfg - > dcfg ;
struct rproc * rproc = priv - > rproc ;
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 ;
void __iomem * cpu_addr ;
int a ;
u64 da ;
/* 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 ;
if ( imx_dsp_rproc_sys_to_da ( priv , att - > sa , att - > size , & da ) )
return - EINVAL ;
cpu_addr = devm_ioremap_wc ( dev , att - > sa , att - > size ) ;
if ( ! cpu_addr ) {
dev_err ( dev , " failed to map memory %p \n " , & att - > sa ) ;
return - ENOMEM ;
}
/* Register memory region */
2022-08-15 15:43:18 +03:00
mem = rproc_mem_entry_init ( dev , ( void __force * ) cpu_addr , ( dma_addr_t ) att - > sa ,
2021-10-11 12:20:14 +03:00
att - > size , da , NULL , NULL , " dsp_mem " ) ;
if ( mem )
rproc_coredump_add_segment ( rproc , da , att - > size ) ;
else
return - ENOMEM ;
rproc_add_carveout ( rproc , mem ) ;
}
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 ) {
2023-03-21 01:18:26 +03:00
of_node_put ( it . node ) ;
2021-10-11 12:20:14 +03:00
dev_err ( dev , " unable to acquire memory-region \n " ) ;
return - EINVAL ;
}
2023-03-21 01:18:26 +03:00
if ( imx_dsp_rproc_sys_to_da ( priv , rmem - > base , rmem - > size , & da ) ) {
of_node_put ( it . node ) ;
2021-10-11 12:20:14 +03:00
return - EINVAL ;
2023-03-21 01:18:26 +03:00
}
2021-10-11 12:20:14 +03:00
cpu_addr = devm_ioremap_wc ( dev , rmem - > base , rmem - > size ) ;
if ( ! cpu_addr ) {
2023-03-21 01:18:26 +03:00
of_node_put ( it . node ) ;
2021-10-11 12:20:14 +03:00
dev_err ( dev , " failed to map memory %p \n " , & rmem - > base ) ;
return - ENOMEM ;
}
/* Register memory region */
2022-08-15 15:43:18 +03:00
mem = rproc_mem_entry_init ( dev , ( void __force * ) cpu_addr , ( dma_addr_t ) rmem - > base ,
2021-10-11 12:20:14 +03:00
rmem - > size , da , NULL , NULL , it . node - > name ) ;
2023-03-21 01:18:26 +03:00
if ( mem ) {
2021-10-11 12:20:14 +03:00
rproc_coredump_add_segment ( rproc , da , rmem - > size ) ;
2023-03-21 01:18:26 +03:00
} else {
of_node_put ( it . node ) ;
2021-10-11 12:20:14 +03:00
return - ENOMEM ;
2023-03-21 01:18:26 +03:00
}
2021-10-11 12:20:14 +03:00
rproc_add_carveout ( rproc , mem ) ;
}
return 0 ;
}
/* Prepare function for rproc_ops */
static int imx_dsp_rproc_prepare ( struct rproc * rproc )
{
struct imx_dsp_rproc * priv = rproc - > priv ;
struct device * dev = rproc - > dev . parent ;
struct rproc_mem_entry * carveout ;
int ret ;
ret = imx_dsp_rproc_add_carveout ( priv ) ;
if ( ret ) {
dev_err ( dev , " failed on imx_dsp_rproc_add_carveout \n " ) ;
return ret ;
}
pm_runtime_get_sync ( dev ) ;
/*
* Clear buffers after pm rumtime for internal ocram is not
* accessible if power and clock are not enabled .
*/
list_for_each_entry ( carveout , & rproc - > carveouts , node ) {
if ( carveout - > va )
memset ( carveout - > va , 0 , carveout - > len ) ;
}
return 0 ;
}
/* Unprepare function for rproc_ops */
static int imx_dsp_rproc_unprepare ( struct rproc * rproc )
{
pm_runtime_put_sync ( rproc - > dev . parent ) ;
return 0 ;
}
/* Kick function for rproc_ops */
static void imx_dsp_rproc_kick ( struct rproc * rproc , int vqid )
{
struct imx_dsp_rproc * priv = rproc - > priv ;
struct device * dev = rproc - > dev . parent ;
int err ;
__u32 mmsg ;
if ( ! priv - > tx_ch ) {
dev_err ( 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 ;
err = mbox_send_message ( priv - > tx_ch , ( void * ) & mmsg ) ;
if ( err < 0 )
dev_err ( dev , " %s: failed (%d, err:%d) \n " , __func__ , vqid , err ) ;
}
2023-02-21 20:03:56 +03:00
/*
* Custom memory copy implementation for i . MX DSP Cores
*
* The IRAM is part of the HiFi DSP .
* According to hw specs only 32 - bits writes are allowed .
*/
2023-04-07 19:14:29 +03:00
static int imx_dsp_rproc_memcpy ( void * dst , const void * src , size_t size )
2023-02-21 20:03:56 +03:00
{
2023-04-07 19:14:29 +03:00
void __iomem * dest = ( void __iomem * ) dst ;
2023-02-21 20:03:56 +03:00
const u8 * src_byte = src ;
const u32 * source = src ;
u32 affected_mask ;
int i , q , r ;
u32 tmp ;
/* destination must be 32bit aligned */
if ( ! IS_ALIGNED ( ( uintptr_t ) dest , 4 ) )
return - EINVAL ;
q = size / 4 ;
r = size % 4 ;
/* copy data in units of 32 bits at a time */
for ( i = 0 ; i < q ; i + + )
2023-04-07 19:14:29 +03:00
writel ( source [ i ] , dest + i * 4 ) ;
2023-02-21 20:03:56 +03:00
if ( r ) {
affected_mask = GENMASK ( 8 * r , 0 ) ;
/*
* first read the 32 bit data of dest , then change affected
* bytes , and write back to dest .
* For unaffected bytes , it should not be changed
*/
tmp = readl ( dest + q * 4 ) ;
tmp & = ~ affected_mask ;
/* avoid reading after end of source */
for ( i = 0 ; i < r ; i + + )
tmp | = ( src_byte [ q * 4 + i ] < < ( 8 * i ) ) ;
writel ( tmp , dest + q * 4 ) ;
}
return 0 ;
}
/*
* Custom memset implementation for i . MX DSP Cores
*
* The IRAM is part of the HiFi DSP .
* According to hw specs only 32 - bits writes are allowed .
*/
static int imx_dsp_rproc_memset ( void * addr , u8 value , size_t size )
{
2023-04-07 19:14:29 +03:00
void __iomem * tmp_dst = ( void __iomem * ) addr ;
2023-02-21 20:03:56 +03:00
u32 tmp_val = value ;
u32 affected_mask ;
int q , r ;
u32 tmp ;
/* destination must be 32bit aligned */
if ( ! IS_ALIGNED ( ( uintptr_t ) addr , 4 ) )
return - EINVAL ;
tmp_val | = tmp_val < < 8 ;
tmp_val | = tmp_val < < 16 ;
q = size / 4 ;
r = size % 4 ;
while ( q - - )
writel ( tmp_val , tmp_dst + + ) ;
if ( r ) {
affected_mask = GENMASK ( 8 * r , 0 ) ;
/*
* first read the 32 bit data of addr , then change affected
* bytes , and write back to addr .
* For unaffected bytes , it should not be changed
*/
tmp = readl ( tmp_dst ) ;
tmp & = ~ affected_mask ;
tmp | = ( tmp_val & affected_mask ) ;
writel ( tmp , tmp_dst ) ;
}
return 0 ;
}
/*
* imx_dsp_rproc_elf_load_segments ( ) - load firmware segments to memory
* @ rproc : remote processor which will be booted using these fw segments
* @ fw : the ELF firmware image
*
* This function loads the firmware segments to memory , where the remote
* processor expects them .
*
* Return : 0 on success and an appropriate error code otherwise
*/
static int imx_dsp_rproc_elf_load_segments ( struct rproc * rproc , const struct firmware * fw )
{
struct device * dev = & rproc - > dev ;
const void * ehdr , * phdr ;
int i , ret = 0 ;
u16 phnum ;
const u8 * elf_data = fw - > data ;
u8 class = fw_elf_get_class ( fw ) ;
u32 elf_phdr_get_size = elf_size_of_phdr ( class ) ;
ehdr = elf_data ;
phnum = elf_hdr_get_e_phnum ( class , ehdr ) ;
phdr = elf_data + elf_hdr_get_e_phoff ( class , ehdr ) ;
/* go through the available ELF segments */
for ( i = 0 ; i < phnum ; i + + , phdr + = elf_phdr_get_size ) {
u64 da = elf_phdr_get_p_paddr ( class , phdr ) ;
u64 memsz = elf_phdr_get_p_memsz ( class , phdr ) ;
u64 filesz = elf_phdr_get_p_filesz ( class , phdr ) ;
u64 offset = elf_phdr_get_p_offset ( class , phdr ) ;
u32 type = elf_phdr_get_p_type ( class , phdr ) ;
void * ptr ;
if ( type ! = PT_LOAD | | ! memsz )
continue ;
dev_dbg ( dev , " phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx \n " ,
type , da , memsz , filesz ) ;
if ( filesz > memsz ) {
dev_err ( dev , " bad phdr filesz 0x%llx memsz 0x%llx \n " ,
filesz , memsz ) ;
ret = - EINVAL ;
break ;
}
if ( offset + filesz > fw - > size ) {
dev_err ( dev , " truncated fw: need 0x%llx avail 0x%zx \n " ,
offset + filesz , fw - > size ) ;
ret = - EINVAL ;
break ;
}
if ( ! rproc_u64_fit_in_size_t ( memsz ) ) {
dev_err ( dev , " size (%llx) does not fit in size_t type \n " ,
memsz ) ;
ret = - EOVERFLOW ;
break ;
}
/* grab the kernel address for this device address */
ptr = rproc_da_to_va ( rproc , da , memsz , NULL ) ;
if ( ! ptr ) {
dev_err ( dev , " bad phdr da 0x%llx mem 0x%llx \n " , da ,
memsz ) ;
ret = - EINVAL ;
break ;
}
/* put the segment where the remote processor expects it */
if ( filesz ) {
ret = imx_dsp_rproc_memcpy ( ptr , elf_data + offset , filesz ) ;
if ( ret ) {
dev_err ( dev , " memory copy failed for da 0x%llx memsz 0x%llx \n " ,
da , memsz ) ;
break ;
}
}
/* zero out remaining memory for this segment */
if ( memsz > filesz ) {
ret = imx_dsp_rproc_memset ( ptr + filesz , 0 , memsz - filesz ) ;
if ( ret ) {
dev_err ( dev , " memset failed for da 0x%llx memsz 0x%llx \n " ,
da , memsz ) ;
break ;
}
}
}
return ret ;
}
2022-03-31 13:32:37 +03:00
static int imx_dsp_rproc_parse_fw ( struct rproc * rproc , const struct firmware * fw )
{
if ( rproc_elf_load_rsc_table ( rproc , fw ) )
dev_warn ( & rproc - > dev , " no resource table found for this firmware \n " ) ;
return 0 ;
}
2021-10-11 12:20:14 +03:00
static const struct rproc_ops imx_dsp_rproc_ops = {
. prepare = imx_dsp_rproc_prepare ,
. unprepare = imx_dsp_rproc_unprepare ,
. start = imx_dsp_rproc_start ,
. stop = imx_dsp_rproc_stop ,
. kick = imx_dsp_rproc_kick ,
2023-02-21 20:03:56 +03:00
. load = imx_dsp_rproc_elf_load_segments ,
2022-03-31 13:32:37 +03:00
. parse_fw = imx_dsp_rproc_parse_fw ,
2021-10-11 12:20:14 +03:00
. sanity_check = rproc_elf_sanity_check ,
. get_boot_addr = rproc_elf_get_boot_addr ,
} ;
/**
* imx_dsp_attach_pm_domains ( ) - attach the power domains
* @ priv : private data pointer
*
* On i . MX8QM and i . MX8QXP there is multiple power domains
* required , so need to link them .
*/
static int imx_dsp_attach_pm_domains ( struct imx_dsp_rproc * priv )
{
struct device * dev = priv - > rproc - > dev . parent ;
int ret , i ;
priv - > num_domains = of_count_phandle_with_args ( dev - > of_node ,
" power-domains " ,
" #power-domain-cells " ) ;
/* If only one domain, then no need to link the device */
if ( priv - > num_domains < = 1 )
return 0 ;
priv - > pd_dev = devm_kmalloc_array ( dev , priv - > num_domains ,
sizeof ( * priv - > pd_dev ) ,
GFP_KERNEL ) ;
if ( ! priv - > pd_dev )
return - ENOMEM ;
priv - > pd_dev_link = devm_kmalloc_array ( dev , priv - > num_domains ,
sizeof ( * priv - > pd_dev_link ) ,
GFP_KERNEL ) ;
if ( ! priv - > pd_dev_link )
return - ENOMEM ;
for ( i = 0 ; i < priv - > num_domains ; i + + ) {
priv - > pd_dev [ i ] = dev_pm_domain_attach_by_id ( dev , i ) ;
if ( IS_ERR ( priv - > pd_dev [ i ] ) ) {
ret = PTR_ERR ( priv - > pd_dev [ i ] ) ;
goto detach_pm ;
}
/*
* device_link_add will check priv - > pd_dev [ i ] , if it is
* NULL , then will break .
*/
priv - > pd_dev_link [ i ] = device_link_add ( dev ,
priv - > pd_dev [ i ] ,
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME ) ;
if ( ! priv - > pd_dev_link [ i ] ) {
dev_pm_domain_detach ( priv - > pd_dev [ i ] , false ) ;
ret = - EINVAL ;
goto detach_pm ;
}
}
return 0 ;
detach_pm :
while ( - - i > = 0 ) {
device_link_del ( priv - > pd_dev_link [ i ] ) ;
dev_pm_domain_detach ( priv - > pd_dev [ i ] , false ) ;
}
return ret ;
}
static int imx_dsp_detach_pm_domains ( struct imx_dsp_rproc * priv )
{
int i ;
if ( priv - > num_domains < = 1 )
return 0 ;
for ( i = 0 ; i < priv - > num_domains ; i + + ) {
device_link_del ( priv - > pd_dev_link [ i ] ) ;
dev_pm_domain_detach ( priv - > pd_dev [ i ] , false ) ;
}
return 0 ;
}
/**
* imx_dsp_rproc_detect_mode ( ) - detect DSP control mode
* @ priv : private data pointer
*
* Different platform has different control method for DSP , which depends
* on how the DSP is integrated in platform .
*
* For i . MX8QXP and i . MX8QM , DSP should be started and stopped by System
* Control Unit .
* For i . MX8MP and i . MX8ULP , DSP should be started and stopped by system
* integration module .
*/
static int imx_dsp_rproc_detect_mode ( struct imx_dsp_rproc * priv )
{
const struct imx_dsp_rproc_dcfg * dsp_dcfg = priv - > dsp_dcfg ;
struct device * dev = priv - > rproc - > dev . parent ;
struct regmap * regmap ;
int ret = 0 ;
switch ( dsp_dcfg - > dcfg - > method ) {
case IMX_RPROC_SCU_API :
ret = imx_scu_get_handle ( & priv - > ipc_handle ) ;
if ( ret )
return ret ;
break ;
case IMX_RPROC_MMIO :
regmap = syscon_regmap_lookup_by_phandle ( dev - > of_node , " fsl,dsp-ctrl " ) ;
if ( IS_ERR ( regmap ) ) {
dev_err ( dev , " failed to find syscon \n " ) ;
return PTR_ERR ( regmap ) ;
}
priv - > regmap = regmap ;
break ;
default :
ret = - EOPNOTSUPP ;
break ;
}
return ret ;
}
static const char * imx_dsp_clks_names [ DSP_RPROC_CLK_MAX ] = {
/* DSP clocks */
" core " , " ocram " , " debug " , " ipg " , " mu " ,
} ;
static int imx_dsp_rproc_clk_get ( struct imx_dsp_rproc * priv )
{
struct device * dev = priv - > rproc - > dev . parent ;
struct clk_bulk_data * clks = priv - > clks ;
int i ;
for ( i = 0 ; i < DSP_RPROC_CLK_MAX ; i + + )
clks [ i ] . id = imx_dsp_clks_names [ i ] ;
return devm_clk_bulk_get_optional ( dev , DSP_RPROC_CLK_MAX , clks ) ;
}
static int imx_dsp_rproc_probe ( struct platform_device * pdev )
{
const struct imx_dsp_rproc_dcfg * dsp_dcfg ;
struct device * dev = & pdev - > dev ;
struct imx_dsp_rproc * priv ;
struct rproc * rproc ;
const char * fw_name ;
int ret ;
dsp_dcfg = of_device_get_match_data ( dev ) ;
if ( ! dsp_dcfg )
return - ENODEV ;
ret = rproc_of_parse_firmware ( dev , 0 , & fw_name ) ;
if ( ret ) {
dev_err ( dev , " failed to parse firmware-name property, ret = %d \n " ,
ret ) ;
return ret ;
}
rproc = rproc_alloc ( dev , " imx-dsp-rproc " , & imx_dsp_rproc_ops , fw_name ,
sizeof ( * priv ) ) ;
if ( ! rproc )
return - ENOMEM ;
priv = rproc - > priv ;
priv - > rproc = rproc ;
priv - > dsp_dcfg = dsp_dcfg ;
2023-02-17 12:41:24 +03:00
if ( no_mailboxes )
imx_dsp_rproc_mbox_init = imx_dsp_rproc_mbox_no_alloc ;
else
imx_dsp_rproc_mbox_init = imx_dsp_rproc_mbox_alloc ;
2021-10-11 12:20:14 +03:00
dev_set_drvdata ( dev , rproc ) ;
INIT_WORK ( & priv - > rproc_work , imx_dsp_rproc_vq_work ) ;
ret = imx_dsp_rproc_detect_mode ( priv ) ;
if ( ret ) {
dev_err ( dev , " failed on imx_dsp_rproc_detect_mode \n " ) ;
goto err_put_rproc ;
}
/* There are multiple power domains required by DSP on some platform */
ret = imx_dsp_attach_pm_domains ( priv ) ;
if ( ret ) {
dev_err ( dev , " failed on imx_dsp_attach_pm_domains \n " ) ;
goto err_put_rproc ;
}
/* Get clocks */
ret = imx_dsp_rproc_clk_get ( priv ) ;
if ( ret ) {
dev_err ( dev , " failed on imx_dsp_rproc_clk_get \n " ) ;
goto err_detach_domains ;
}
init_completion ( & priv - > pm_comp ) ;
rproc - > auto_boot = false ;
ret = rproc_add ( rproc ) ;
if ( ret ) {
dev_err ( dev , " rproc_add failed \n " ) ;
goto err_detach_domains ;
}
pm_runtime_enable ( dev ) ;
return 0 ;
err_detach_domains :
imx_dsp_detach_pm_domains ( priv ) ;
err_put_rproc :
rproc_free ( rproc ) ;
return ret ;
}
2023-05-04 22:44:37 +03:00
static void imx_dsp_rproc_remove ( struct platform_device * pdev )
2021-10-11 12:20:14 +03:00
{
struct rproc * rproc = platform_get_drvdata ( pdev ) ;
struct imx_dsp_rproc * priv = rproc - > priv ;
pm_runtime_disable ( & pdev - > dev ) ;
rproc_del ( rproc ) ;
imx_dsp_detach_pm_domains ( priv ) ;
rproc_free ( rproc ) ;
}
/* pm runtime functions */
static int imx_dsp_runtime_resume ( struct device * dev )
{
struct rproc * rproc = dev_get_drvdata ( dev ) ;
struct imx_dsp_rproc * priv = rproc - > priv ;
const struct imx_dsp_rproc_dcfg * dsp_dcfg = priv - > dsp_dcfg ;
int ret ;
/*
* There is power domain attached with mailbox , if setup mailbox
* in probe ( ) , then the power of mailbox is always enabled ,
* the power can ' t be saved .
* So move setup of mailbox to runtime resume .
*/
ret = imx_dsp_rproc_mbox_init ( priv ) ;
if ( ret ) {
dev_err ( dev , " failed on imx_dsp_rproc_mbox_init \n " ) ;
return ret ;
}
ret = clk_bulk_prepare_enable ( DSP_RPROC_CLK_MAX , priv - > clks ) ;
if ( ret ) {
dev_err ( dev , " failed on clk_bulk_prepare_enable \n " ) ;
return ret ;
}
/* Reset DSP if needed */
if ( dsp_dcfg - > reset )
dsp_dcfg - > reset ( priv ) ;
return 0 ;
}
static int imx_dsp_runtime_suspend ( struct device * dev )
{
struct rproc * rproc = dev_get_drvdata ( dev ) ;
struct imx_dsp_rproc * priv = rproc - > priv ;
clk_bulk_disable_unprepare ( DSP_RPROC_CLK_MAX , priv - > clks ) ;
imx_dsp_rproc_free_mbox ( priv ) ;
return 0 ;
}
static void imx_dsp_load_firmware ( const struct firmware * fw , void * context )
{
struct rproc * rproc = context ;
int ret ;
/*
* Same flow as start procedure .
* Load the ELF segments to memory firstly .
*/
ret = rproc_load_segments ( rproc , fw ) ;
if ( ret )
goto out ;
/* Start the remote processor */
ret = rproc - > ops - > start ( rproc ) ;
if ( ret )
goto out ;
rproc - > ops - > kick ( rproc , 0 ) ;
out :
release_firmware ( fw ) ;
}
2023-04-21 00:36:04 +03:00
static int imx_dsp_suspend ( struct device * dev )
2021-10-11 12:20:14 +03:00
{
struct rproc * rproc = dev_get_drvdata ( dev ) ;
struct imx_dsp_rproc * priv = rproc - > priv ;
__u32 mmsg = RP_MBOX_SUSPEND_SYSTEM ;
int ret ;
if ( rproc - > state ! = RPROC_RUNNING )
goto out ;
reinit_completion ( & priv - > pm_comp ) ;
/* Tell DSP that suspend is happening */
ret = mbox_send_message ( priv - > tx_ch , ( void * ) & mmsg ) ;
if ( ret < 0 ) {
dev_err ( dev , " PM mbox_send_message failed: %d \n " , ret ) ;
return ret ;
}
/*
* DSP need to save the context at suspend .
* Here waiting the response for DSP , then power can be disabled .
*/
if ( ! wait_for_completion_timeout ( & priv - > pm_comp , msecs_to_jiffies ( 100 ) ) )
return - EBUSY ;
out :
/*
* The power of DSP is disabled in suspend , so force pm runtime
* to be suspend , then we can reenable the power and clocks at
* resume stage .
*/
return pm_runtime_force_suspend ( dev ) ;
}
2023-04-21 00:36:04 +03:00
static int imx_dsp_resume ( struct device * dev )
2021-10-11 12:20:14 +03:00
{
struct rproc * rproc = dev_get_drvdata ( dev ) ;
int ret = 0 ;
ret = pm_runtime_force_resume ( dev ) ;
if ( ret )
return ret ;
if ( rproc - > state ! = RPROC_RUNNING )
return 0 ;
/*
* The power of DSP is disabled at suspend , the memory of dsp
* is reset , the image segments are lost . So need to reload
* firmware and restart the DSP if it is in running state .
*/
ret = request_firmware_nowait ( THIS_MODULE , FW_ACTION_UEVENT ,
rproc - > firmware , dev , GFP_KERNEL ,
rproc , imx_dsp_load_firmware ) ;
if ( ret < 0 ) {
dev_err ( dev , " load firmware failed: %d \n " , ret ) ;
goto err ;
}
return 0 ;
err :
pm_runtime_force_suspend ( dev ) ;
return ret ;
}
static const struct dev_pm_ops imx_dsp_rproc_pm_ops = {
2023-04-21 00:36:04 +03:00
SYSTEM_SLEEP_PM_OPS ( imx_dsp_suspend , imx_dsp_resume )
RUNTIME_PM_OPS ( imx_dsp_runtime_suspend , imx_dsp_runtime_resume , NULL )
2021-10-11 12:20:14 +03:00
} ;
static const struct of_device_id imx_dsp_rproc_of_match [ ] = {
{ . compatible = " fsl,imx8qxp-hifi4 " , . data = & imx_dsp_rproc_cfg_imx8qxp } ,
{ . compatible = " fsl,imx8qm-hifi4 " , . data = & imx_dsp_rproc_cfg_imx8qm } ,
{ . compatible = " fsl,imx8mp-hifi4 " , . data = & imx_dsp_rproc_cfg_imx8mp } ,
{ . compatible = " fsl,imx8ulp-hifi4 " , . data = & imx_dsp_rproc_cfg_imx8ulp } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , imx_dsp_rproc_of_match ) ;
static struct platform_driver imx_dsp_rproc_driver = {
. probe = imx_dsp_rproc_probe ,
2023-05-04 22:44:37 +03:00
. remove_new = imx_dsp_rproc_remove ,
2021-10-11 12:20:14 +03:00
. driver = {
. name = " imx-dsp-rproc " ,
. of_match_table = imx_dsp_rproc_of_match ,
2023-04-21 00:36:04 +03:00
. pm = pm_ptr ( & imx_dsp_rproc_pm_ops ) ,
2021-10-11 12:20:14 +03:00
} ,
} ;
module_platform_driver ( imx_dsp_rproc_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " i.MX HiFi Core Remote Processor Control Driver " ) ;
MODULE_AUTHOR ( " Shengjiu Wang <shengjiu.wang@nxp.com> " ) ;