2018-09-24 14:07:50 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Qualcomm Technology Inc . ADSP Peripheral Image Loader for SDM845 .
* Copyright ( c ) 2018 , The Linux Foundation . All rights reserved .
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/firmware.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/kernel.h>
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/of_address.h>
# include <linux/of_device.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/reset.h>
# include <linux/soc/qcom/mdt_loader.h>
# include <linux/soc/qcom/smem.h>
# include <linux/soc/qcom/smem_state.h>
# include "qcom_common.h"
2020-06-22 22:19:40 +03:00
# include "qcom_pil_info.h"
2018-09-24 14:07:50 +03:00
# include "qcom_q6v5.h"
# include "remoteproc_internal.h"
/* time out value */
# define ACK_TIMEOUT 1000
2022-02-01 13:52:04 +03:00
# define ACK_TIMEOUT_US 1000000
2018-09-24 14:07:50 +03:00
# define BOOT_FSM_TIMEOUT 10000
/* mask values */
# define EVB_MASK GENMASK(27, 4)
/*QDSP6SS register offsets*/
# define RST_EVB_REG 0x10
# define CORE_START_REG 0x400
# define BOOT_CMD_REG 0x404
# define BOOT_STATUS_REG 0x408
# define RET_CFG_REG 0x1C
/*TCSR register offsets*/
# define LPASS_MASTER_IDLE_REG 0x8
# define LPASS_HALTACK_REG 0x4
# define LPASS_PWR_ON_REG 0x10
# define LPASS_HALTREQ_REG 0x0
2019-05-10 07:34:15 +03:00
# define QDSP6SS_XO_CBCR 0x38
# define QDSP6SS_CORE_CBCR 0x20
# define QDSP6SS_SLEEP_CBCR 0x3c
2018-09-24 14:07:50 +03:00
2022-02-01 13:52:04 +03:00
# define QCOM_Q6V5_RPROC_PROXY_PD_MAX 3
2018-09-24 14:07:50 +03:00
struct adsp_pil_data {
int crash_reason_smem ;
const char * firmware_name ;
const char * ssr_name ;
const char * sysmon_name ;
int ssctl_id ;
2022-02-01 13:52:04 +03:00
bool is_wpss ;
2022-09-28 15:27:48 +03:00
bool has_iommu ;
2022-02-01 13:52:04 +03:00
bool auto_boot ;
2019-05-10 07:34:15 +03:00
const char * * clk_ids ;
int num_clks ;
2022-02-01 13:52:04 +03:00
const char * * proxy_pd_names ;
const char * load_state ;
2018-09-24 14:07:50 +03:00
} ;
struct qcom_adsp {
struct device * dev ;
struct rproc * rproc ;
struct qcom_q6v5 q6v5 ;
struct clk * xo ;
int num_clks ;
struct clk_bulk_data * clks ;
void __iomem * qdsp6ss_base ;
struct reset_control * pdc_sync_reset ;
2019-05-10 07:34:15 +03:00
struct reset_control * restart ;
2018-09-24 14:07:50 +03:00
struct regmap * halt_map ;
unsigned int halt_lpass ;
int crash_reason_smem ;
2020-06-22 22:19:40 +03:00
const char * info_name ;
2018-09-24 14:07:50 +03:00
struct completion start_done ;
struct completion stop_done ;
phys_addr_t mem_phys ;
phys_addr_t mem_reloc ;
void * mem_region ;
size_t mem_size ;
2022-09-28 15:27:48 +03:00
bool has_iommu ;
2018-09-24 14:07:50 +03:00
2022-02-01 13:52:04 +03:00
struct device * proxy_pds [ QCOM_Q6V5_RPROC_PROXY_PD_MAX ] ;
size_t proxy_pd_count ;
2018-09-24 14:07:50 +03:00
struct qcom_rproc_glink glink_subdev ;
struct qcom_rproc_ssr ssr_subdev ;
struct qcom_sysmon * sysmon ;
2022-02-01 13:52:04 +03:00
int ( * shutdown ) ( struct qcom_adsp * adsp ) ;
2018-09-24 14:07:50 +03:00
} ;
2022-02-01 13:52:04 +03:00
static int qcom_rproc_pds_attach ( struct device * dev , struct qcom_adsp * adsp ,
const char * * pd_names )
{
struct device * * devs = adsp - > proxy_pds ;
size_t num_pds = 0 ;
int ret ;
int i ;
if ( ! pd_names )
return 0 ;
/* Handle single power domain */
if ( dev - > pm_domain ) {
devs [ 0 ] = dev ;
pm_runtime_enable ( dev ) ;
return 1 ;
}
while ( pd_names [ num_pds ] )
num_pds + + ;
if ( num_pds > ARRAY_SIZE ( adsp - > proxy_pds ) )
return - E2BIG ;
for ( i = 0 ; i < num_pds ; i + + ) {
devs [ i ] = dev_pm_domain_attach_by_name ( dev , pd_names [ i ] ) ;
if ( IS_ERR_OR_NULL ( devs [ i ] ) ) {
ret = PTR_ERR ( devs [ i ] ) ? : - ENODATA ;
goto unroll_attach ;
}
}
return num_pds ;
unroll_attach :
for ( i - - ; i > = 0 ; i - - )
dev_pm_domain_detach ( devs [ i ] , false ) ;
return ret ;
}
static void qcom_rproc_pds_detach ( struct qcom_adsp * adsp , struct device * * pds ,
size_t pd_count )
{
struct device * dev = adsp - > dev ;
int i ;
/* Handle single power domain */
if ( dev - > pm_domain & & pd_count ) {
pm_runtime_disable ( dev ) ;
return ;
}
for ( i = 0 ; i < pd_count ; i + + )
dev_pm_domain_detach ( pds [ i ] , false ) ;
}
static int qcom_rproc_pds_enable ( struct qcom_adsp * adsp , struct device * * pds ,
size_t pd_count )
{
int ret ;
int i ;
for ( i = 0 ; i < pd_count ; i + + ) {
dev_pm_genpd_set_performance_state ( pds [ i ] , INT_MAX ) ;
2022-04-28 09:45:45 +03:00
ret = pm_runtime_resume_and_get ( pds [ i ] ) ;
2022-02-01 13:52:04 +03:00
if ( ret < 0 ) {
dev_pm_genpd_set_performance_state ( pds [ i ] , 0 ) ;
goto unroll_pd_votes ;
}
}
return 0 ;
unroll_pd_votes :
for ( i - - ; i > = 0 ; i - - ) {
dev_pm_genpd_set_performance_state ( pds [ i ] , 0 ) ;
pm_runtime_put ( pds [ i ] ) ;
}
return ret ;
}
static void qcom_rproc_pds_disable ( struct qcom_adsp * adsp , struct device * * pds ,
size_t pd_count )
{
int i ;
for ( i = 0 ; i < pd_count ; i + + ) {
dev_pm_genpd_set_performance_state ( pds [ i ] , 0 ) ;
pm_runtime_put ( pds [ i ] ) ;
}
}
static int qcom_wpss_shutdown ( struct qcom_adsp * adsp )
{
unsigned int val ;
regmap_write ( adsp - > halt_map , adsp - > halt_lpass + LPASS_HALTREQ_REG , 1 ) ;
/* Wait for halt ACK from QDSP6 */
regmap_read_poll_timeout ( adsp - > halt_map ,
adsp - > halt_lpass + LPASS_HALTACK_REG , val ,
val , 1000 , ACK_TIMEOUT_US ) ;
/* Assert the WPSS PDC Reset */
reset_control_assert ( adsp - > pdc_sync_reset ) ;
/* Place the WPSS processor into reset */
reset_control_assert ( adsp - > restart ) ;
/* wait after asserting subsystem restart from AOSS */
usleep_range ( 200 , 205 ) ;
/* Remove the WPSS reset */
reset_control_deassert ( adsp - > restart ) ;
/* De-assert the WPSS PDC Reset */
reset_control_deassert ( adsp - > pdc_sync_reset ) ;
usleep_range ( 100 , 105 ) ;
clk_bulk_disable_unprepare ( adsp - > num_clks , adsp - > clks ) ;
regmap_write ( adsp - > halt_map , adsp - > halt_lpass + LPASS_HALTREQ_REG , 0 ) ;
/* Wait for halt ACK from QDSP6 */
regmap_read_poll_timeout ( adsp - > halt_map ,
adsp - > halt_lpass + LPASS_HALTACK_REG , val ,
! val , 1000 , ACK_TIMEOUT_US ) ;
return 0 ;
}
2018-09-24 14:07:50 +03:00
static int qcom_adsp_shutdown ( struct qcom_adsp * adsp )
{
unsigned long timeout ;
unsigned int val ;
int ret ;
/* Reset the retention logic */
val = readl ( adsp - > qdsp6ss_base + RET_CFG_REG ) ;
val | = 0x1 ;
writel ( val , adsp - > qdsp6ss_base + RET_CFG_REG ) ;
clk_bulk_disable_unprepare ( adsp - > num_clks , adsp - > clks ) ;
/* QDSP6 master port needs to be explicitly halted */
ret = regmap_read ( adsp - > halt_map ,
adsp - > halt_lpass + LPASS_PWR_ON_REG , & val ) ;
if ( ret | | ! val )
goto reset ;
ret = regmap_read ( adsp - > halt_map ,
adsp - > halt_lpass + LPASS_MASTER_IDLE_REG ,
& val ) ;
if ( ret | | val )
goto reset ;
regmap_write ( adsp - > halt_map ,
adsp - > halt_lpass + LPASS_HALTREQ_REG , 1 ) ;
/* Wait for halt ACK from QDSP6 */
timeout = jiffies + msecs_to_jiffies ( ACK_TIMEOUT ) ;
for ( ; ; ) {
ret = regmap_read ( adsp - > halt_map ,
adsp - > halt_lpass + LPASS_HALTACK_REG , & val ) ;
if ( ret | | val | | time_after ( jiffies , timeout ) )
break ;
usleep_range ( 1000 , 1100 ) ;
}
ret = regmap_read ( adsp - > halt_map ,
adsp - > halt_lpass + LPASS_MASTER_IDLE_REG , & val ) ;
if ( ret | | ! val )
dev_err ( adsp - > dev , " port failed halt \n " ) ;
reset :
/* Assert the LPASS PDC Reset */
reset_control_assert ( adsp - > pdc_sync_reset ) ;
/* Place the LPASS processor into reset */
2019-05-10 07:34:15 +03:00
reset_control_assert ( adsp - > restart ) ;
2018-09-24 14:07:50 +03:00
/* wait after asserting subsystem restart from AOSS */
usleep_range ( 200 , 300 ) ;
/* Clear the halt request for the AXIM and AHBM for Q6 */
regmap_write ( adsp - > halt_map , adsp - > halt_lpass + LPASS_HALTREQ_REG , 0 ) ;
/* De-assert the LPASS PDC Reset */
reset_control_deassert ( adsp - > pdc_sync_reset ) ;
/* Remove the LPASS reset */
2019-05-10 07:34:15 +03:00
reset_control_deassert ( adsp - > restart ) ;
2018-09-24 14:07:50 +03:00
/* wait after de-asserting subsystem restart from AOSS */
usleep_range ( 200 , 300 ) ;
return 0 ;
}
static int adsp_load ( struct rproc * rproc , const struct firmware * fw )
{
struct qcom_adsp * adsp = ( struct qcom_adsp * ) rproc - > priv ;
2020-06-22 22:19:40 +03:00
int ret ;
ret = qcom_mdt_load_no_init ( adsp - > dev , fw , rproc - > firmware , 0 ,
adsp - > mem_region , adsp - > mem_phys ,
adsp - > mem_size , & adsp - > mem_reloc ) ;
if ( ret )
return ret ;
qcom_pil_info_store ( adsp - > info_name , adsp - > mem_phys , adsp - > mem_size ) ;
2018-09-24 14:07:50 +03:00
2020-06-22 22:19:40 +03:00
return 0 ;
2018-09-24 14:07:50 +03:00
}
static int adsp_start ( struct rproc * rproc )
{
struct qcom_adsp * adsp = ( struct qcom_adsp * ) rproc - > priv ;
int ret ;
unsigned int val ;
2021-09-16 16:59:21 +03:00
ret = qcom_q6v5_prepare ( & adsp - > q6v5 ) ;
if ( ret )
return ret ;
2018-09-24 14:07:50 +03:00
ret = clk_prepare_enable ( adsp - > xo ) ;
if ( ret )
goto disable_irqs ;
2022-02-01 13:52:04 +03:00
ret = qcom_rproc_pds_enable ( adsp , adsp - > proxy_pds ,
adsp - > proxy_pd_count ) ;
if ( ret < 0 )
2018-09-24 14:07:50 +03:00
goto disable_xo_clk ;
ret = clk_bulk_prepare_enable ( adsp - > num_clks , adsp - > clks ) ;
if ( ret ) {
dev_err ( adsp - > dev , " adsp clk_enable failed \n " ) ;
goto disable_power_domain ;
}
2019-05-10 07:34:15 +03:00
/* Enable the XO clock */
writel ( 1 , adsp - > qdsp6ss_base + QDSP6SS_XO_CBCR ) ;
/* Enable the QDSP6SS sleep clock */
writel ( 1 , adsp - > qdsp6ss_base + QDSP6SS_SLEEP_CBCR ) ;
/* Enable the QDSP6 core clock */
writel ( 1 , adsp - > qdsp6ss_base + QDSP6SS_CORE_CBCR ) ;
2018-09-24 14:07:50 +03:00
/* Program boot address */
writel ( adsp - > mem_phys > > 4 , adsp - > qdsp6ss_base + RST_EVB_REG ) ;
/* De-assert QDSP6 stop core. QDSP6 will execute after out of reset */
writel ( 0x1 , adsp - > qdsp6ss_base + CORE_START_REG ) ;
/* Trigger boot FSM to start QDSP6 */
writel ( 0x1 , adsp - > qdsp6ss_base + BOOT_CMD_REG ) ;
/* Wait for core to come out of reset */
ret = readl_poll_timeout ( adsp - > qdsp6ss_base + BOOT_STATUS_REG ,
val , ( val & BIT ( 0 ) ) ! = 0 , 10 , BOOT_FSM_TIMEOUT ) ;
if ( ret ) {
dev_err ( adsp - > dev , " failed to bootup adsp \n " ) ;
goto disable_adsp_clks ;
}
ret = qcom_q6v5_wait_for_start ( & adsp - > q6v5 , msecs_to_jiffies ( 5 * HZ ) ) ;
if ( ret = = - ETIMEDOUT ) {
dev_err ( adsp - > dev , " start timed out \n " ) ;
goto disable_adsp_clks ;
}
return 0 ;
disable_adsp_clks :
clk_bulk_disable_unprepare ( adsp - > num_clks , adsp - > clks ) ;
disable_power_domain :
2022-02-01 13:52:04 +03:00
qcom_rproc_pds_disable ( adsp , adsp - > proxy_pds , adsp - > proxy_pd_count ) ;
2018-09-24 14:07:50 +03:00
disable_xo_clk :
clk_disable_unprepare ( adsp - > xo ) ;
disable_irqs :
qcom_q6v5_unprepare ( & adsp - > q6v5 ) ;
return ret ;
}
static void qcom_adsp_pil_handover ( struct qcom_q6v5 * q6v5 )
{
struct qcom_adsp * adsp = container_of ( q6v5 , struct qcom_adsp , q6v5 ) ;
clk_disable_unprepare ( adsp - > xo ) ;
2022-02-01 13:52:04 +03:00
qcom_rproc_pds_disable ( adsp , adsp - > proxy_pds , adsp - > proxy_pd_count ) ;
2018-09-24 14:07:50 +03:00
}
static int adsp_stop ( struct rproc * rproc )
{
struct qcom_adsp * adsp = ( struct qcom_adsp * ) rproc - > priv ;
int handover ;
int ret ;
2020-11-22 08:41:34 +03:00
ret = qcom_q6v5_request_stop ( & adsp - > q6v5 , adsp - > sysmon ) ;
2018-09-24 14:07:50 +03:00
if ( ret = = - ETIMEDOUT )
dev_err ( adsp - > dev , " timed out on wait \n " ) ;
2022-02-01 13:52:04 +03:00
ret = adsp - > shutdown ( adsp ) ;
2018-09-24 14:07:50 +03:00
if ( ret )
dev_err ( adsp - > dev , " failed to shutdown: %d \n " , ret ) ;
handover = qcom_q6v5_unprepare ( & adsp - > q6v5 ) ;
if ( handover )
qcom_adsp_pil_handover ( & adsp - > q6v5 ) ;
return ret ;
}
2021-03-06 14:24:19 +03:00
static void * adsp_da_to_va ( struct rproc * rproc , u64 da , size_t len , bool * is_iomem )
2018-09-24 14:07:50 +03:00
{
struct qcom_adsp * adsp = ( struct qcom_adsp * ) rproc - > priv ;
int offset ;
offset = da - adsp - > mem_reloc ;
if ( offset < 0 | | offset + len > adsp - > mem_size )
return NULL ;
return adsp - > mem_region + offset ;
}
2022-09-28 15:27:50 +03:00
static int adsp_parse_firmware ( struct rproc * rproc , const struct firmware * fw )
{
struct qcom_adsp * adsp = rproc - > priv ;
int ret ;
ret = qcom_register_dump_segments ( rproc , fw ) ;
if ( ret ) {
dev_err ( & rproc - > dev , " Error in registering dump segments \n " ) ;
return ret ;
}
if ( adsp - > has_iommu ) {
ret = rproc_elf_load_rsc_table ( rproc , fw ) ;
if ( ret ) {
dev_err ( & rproc - > dev , " Error in loading resource table \n " ) ;
return ret ;
}
}
return 0 ;
}
2020-03-24 08:29:04 +03:00
static unsigned long adsp_panic ( struct rproc * rproc )
{
struct qcom_adsp * adsp = rproc - > priv ;
return qcom_q6v5_panic ( & adsp - > q6v5 ) ;
}
2018-09-24 14:07:50 +03:00
static const struct rproc_ops adsp_ops = {
. start = adsp_start ,
. stop = adsp_stop ,
. da_to_va = adsp_da_to_va ,
2022-09-28 15:27:50 +03:00
. parse_fw = adsp_parse_firmware ,
2018-09-24 14:07:50 +03:00
. load = adsp_load ,
2020-03-24 08:29:04 +03:00
. panic = adsp_panic ,
2018-09-24 14:07:50 +03:00
} ;
2019-05-10 07:34:15 +03:00
static int adsp_init_clock ( struct qcom_adsp * adsp , const char * * clk_ids )
2018-09-24 14:07:50 +03:00
{
2019-05-10 07:34:15 +03:00
int num_clks = 0 ;
2018-09-24 14:07:50 +03:00
int i , ret ;
adsp - > xo = devm_clk_get ( adsp - > dev , " xo " ) ;
if ( IS_ERR ( adsp - > xo ) ) {
ret = PTR_ERR ( adsp - > xo ) ;
if ( ret ! = - EPROBE_DEFER )
dev_err ( adsp - > dev , " failed to get xo clock " ) ;
return ret ;
}
2019-05-10 07:34:15 +03:00
for ( i = 0 ; clk_ids [ i ] ; i + + )
num_clks + + ;
adsp - > num_clks = num_clks ;
2018-09-24 14:07:50 +03:00
adsp - > clks = devm_kcalloc ( adsp - > dev , adsp - > num_clks ,
sizeof ( * adsp - > clks ) , GFP_KERNEL ) ;
2018-10-10 06:59:39 +03:00
if ( ! adsp - > clks )
return - ENOMEM ;
2018-09-24 14:07:50 +03:00
for ( i = 0 ; i < adsp - > num_clks ; i + + )
2019-05-10 07:34:15 +03:00
adsp - > clks [ i ] . id = clk_ids [ i ] ;
2018-09-24 14:07:50 +03:00
return devm_clk_bulk_get ( adsp - > dev , adsp - > num_clks , adsp - > clks ) ;
}
static int adsp_init_reset ( struct qcom_adsp * adsp )
{
2019-05-10 07:34:15 +03:00
adsp - > pdc_sync_reset = devm_reset_control_get_optional_exclusive ( adsp - > dev ,
2018-09-24 14:07:50 +03:00
" pdc_sync " ) ;
if ( IS_ERR ( adsp - > pdc_sync_reset ) ) {
dev_err ( adsp - > dev , " failed to acquire pdc_sync reset \n " ) ;
return PTR_ERR ( adsp - > pdc_sync_reset ) ;
}
2019-05-10 07:34:15 +03:00
adsp - > restart = devm_reset_control_get_optional_exclusive ( adsp - > dev , " restart " ) ;
/* Fall back to the old "cc_lpass" if "restart" is absent */
if ( ! adsp - > restart )
adsp - > restart = devm_reset_control_get_exclusive ( adsp - > dev , " cc_lpass " ) ;
if ( IS_ERR ( adsp - > restart ) ) {
dev_err ( adsp - > dev , " failed to acquire restart \n " ) ;
return PTR_ERR ( adsp - > restart ) ;
2018-09-24 14:07:50 +03:00
}
return 0 ;
}
static int adsp_init_mmio ( struct qcom_adsp * adsp ,
struct platform_device * pdev )
{
struct device_node * syscon ;
int ret ;
remoteproc: qcom: Fix potential NULL dereference in adsp_init_mmio()
platform_get_resource() may fail and in this case a NULL dereference
will occur.
Fix it to use devm_platform_ioremap_resource() instead of calling
platform_get_resource() and devm_ioremap().
This is detected by Coccinelle semantic patch.
@@
expression pdev, res, n, t, e, e1, e2;
@@
res = \(platform_get_resource\|platform_get_resource_byname\)(pdev, t,
n);
+ if (!res)
+ return -EINVAL;
... when != res == NULL
e = devm_ioremap(e1, res->start, e2);
Fixes: dc160e449122 ("remoteproc: qcom: Introduce Non-PAS ADSP PIL driver")
Signed-off-by: Zhang Changzhong <zhangchangzhong@huawei.com>
Link: https://lore.kernel.org/r/1607392460-20516-1-git-send-email-zhangchangzhong@huawei.com
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2020-12-08 04:54:20 +03:00
adsp - > qdsp6ss_base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( adsp - > qdsp6ss_base ) ) {
2018-09-24 14:07:50 +03:00
dev_err ( adsp - > dev , " failed to map QDSP6SS registers \n " ) ;
remoteproc: qcom: Fix potential NULL dereference in adsp_init_mmio()
platform_get_resource() may fail and in this case a NULL dereference
will occur.
Fix it to use devm_platform_ioremap_resource() instead of calling
platform_get_resource() and devm_ioremap().
This is detected by Coccinelle semantic patch.
@@
expression pdev, res, n, t, e, e1, e2;
@@
res = \(platform_get_resource\|platform_get_resource_byname\)(pdev, t,
n);
+ if (!res)
+ return -EINVAL;
... when != res == NULL
e = devm_ioremap(e1, res->start, e2);
Fixes: dc160e449122 ("remoteproc: qcom: Introduce Non-PAS ADSP PIL driver")
Signed-off-by: Zhang Changzhong <zhangchangzhong@huawei.com>
Link: https://lore.kernel.org/r/1607392460-20516-1-git-send-email-zhangchangzhong@huawei.com
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2020-12-08 04:54:20 +03:00
return PTR_ERR ( adsp - > qdsp6ss_base ) ;
2018-09-24 14:07:50 +03:00
}
syscon = of_parse_phandle ( pdev - > dev . of_node , " qcom,halt-regs " , 0 ) ;
if ( ! syscon ) {
dev_err ( & pdev - > dev , " failed to parse qcom,halt-regs \n " ) ;
return - EINVAL ;
}
adsp - > halt_map = syscon_node_to_regmap ( syscon ) ;
of_node_put ( syscon ) ;
if ( IS_ERR ( adsp - > halt_map ) )
return PTR_ERR ( adsp - > halt_map ) ;
ret = of_property_read_u32_index ( pdev - > dev . of_node , " qcom,halt-regs " ,
1 , & adsp - > halt_lpass ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " no offset in syscon \n " ) ;
return ret ;
}
return 0 ;
}
static int adsp_alloc_memory_region ( struct qcom_adsp * adsp )
{
struct device_node * node ;
struct resource r ;
int ret ;
node = of_parse_phandle ( adsp - > dev - > of_node , " memory-region " , 0 ) ;
if ( ! node ) {
dev_err ( adsp - > dev , " no memory-region specified \n " ) ;
return - EINVAL ;
}
ret = of_address_to_resource ( node , 0 , & r ) ;
2022-03-08 06:12:19 +03:00
of_node_put ( node ) ;
2018-09-24 14:07:50 +03:00
if ( ret )
return ret ;
adsp - > mem_phys = adsp - > mem_reloc = r . start ;
adsp - > mem_size = resource_size ( & r ) ;
adsp - > mem_region = devm_ioremap_wc ( adsp - > dev ,
adsp - > mem_phys , adsp - > mem_size ) ;
if ( ! adsp - > mem_region ) {
dev_err ( adsp - > dev , " unable to map memory region: %pa+%zx \n " ,
& r . start , adsp - > mem_size ) ;
return - EBUSY ;
}
return 0 ;
}
static int adsp_probe ( struct platform_device * pdev )
{
const struct adsp_pil_data * desc ;
2022-02-01 13:52:04 +03:00
const char * firmware_name ;
2018-09-24 14:07:50 +03:00
struct qcom_adsp * adsp ;
struct rproc * rproc ;
int ret ;
desc = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! desc )
return - EINVAL ;
2022-02-01 13:52:04 +03:00
firmware_name = desc - > firmware_name ;
ret = of_property_read_string ( pdev - > dev . of_node , " firmware-name " ,
& firmware_name ) ;
if ( ret < 0 & & ret ! = - EINVAL ) {
dev_err ( & pdev - > dev , " unable to read firmware-name \n " ) ;
return ret ;
}
2018-09-24 14:07:50 +03:00
rproc = rproc_alloc ( & pdev - > dev , pdev - > name , & adsp_ops ,
2022-02-01 13:52:04 +03:00
firmware_name , sizeof ( * adsp ) ) ;
2018-09-24 14:07:50 +03:00
if ( ! rproc ) {
dev_err ( & pdev - > dev , " unable to allocate remoteproc \n " ) ;
return - ENOMEM ;
}
2022-02-01 13:52:04 +03:00
rproc - > auto_boot = desc - > auto_boot ;
2022-09-28 15:27:48 +03:00
rproc - > has_iommu = desc - > has_iommu ;
2020-04-10 13:24:33 +03:00
rproc_coredump_set_elf_info ( rproc , ELFCLASS32 , EM_NONE ) ;
2018-09-24 14:07:50 +03:00
adsp = ( struct qcom_adsp * ) rproc - > priv ;
adsp - > dev = & pdev - > dev ;
adsp - > rproc = rproc ;
2020-06-22 22:19:40 +03:00
adsp - > info_name = desc - > sysmon_name ;
2022-09-28 15:27:48 +03:00
adsp - > has_iommu = desc - > has_iommu ;
2018-09-24 14:07:50 +03:00
platform_set_drvdata ( pdev , adsp ) ;
2022-02-01 13:52:04 +03:00
if ( desc - > is_wpss )
adsp - > shutdown = qcom_wpss_shutdown ;
else
adsp - > shutdown = qcom_adsp_shutdown ;
2018-09-24 14:07:50 +03:00
ret = adsp_alloc_memory_region ( adsp ) ;
if ( ret )
goto free_rproc ;
2019-05-10 07:34:15 +03:00
ret = adsp_init_clock ( adsp , desc - > clk_ids ) ;
2018-09-24 14:07:50 +03:00
if ( ret )
goto free_rproc ;
2022-02-01 13:52:04 +03:00
ret = qcom_rproc_pds_attach ( adsp - > dev , adsp ,
desc - > proxy_pd_names ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Failed to attach proxy power domains \n " ) ;
goto free_rproc ;
}
adsp - > proxy_pd_count = ret ;
2018-09-24 14:07:50 +03:00
ret = adsp_init_reset ( adsp ) ;
if ( ret )
goto disable_pm ;
ret = adsp_init_mmio ( adsp , pdev ) ;
if ( ret )
goto disable_pm ;
2022-02-01 13:52:04 +03:00
ret = qcom_q6v5_init ( & adsp - > q6v5 , pdev , rproc , desc - > crash_reason_smem ,
desc - > load_state , qcom_adsp_pil_handover ) ;
2018-09-24 14:07:50 +03:00
if ( ret )
goto disable_pm ;
2020-04-23 03:37:33 +03:00
qcom_add_glink_subdev ( rproc , & adsp - > glink_subdev , desc - > ssr_name ) ;
2018-09-24 14:07:50 +03:00
qcom_add_ssr_subdev ( rproc , & adsp - > ssr_subdev , desc - > ssr_name ) ;
adsp - > sysmon = qcom_add_sysmon_subdev ( rproc ,
desc - > sysmon_name ,
desc - > ssctl_id ) ;
2019-01-08 13:23:43 +03:00
if ( IS_ERR ( adsp - > sysmon ) ) {
ret = PTR_ERR ( adsp - > sysmon ) ;
goto disable_pm ;
}
2018-09-24 14:07:50 +03:00
ret = rproc_add ( rproc ) ;
if ( ret )
goto disable_pm ;
return 0 ;
disable_pm :
2022-02-01 13:52:04 +03:00
qcom_rproc_pds_detach ( adsp , adsp - > proxy_pds , adsp - > proxy_pd_count ) ;
2018-09-24 14:07:50 +03:00
free_rproc :
rproc_free ( rproc ) ;
return ret ;
}
static int adsp_remove ( struct platform_device * pdev )
{
struct qcom_adsp * adsp = platform_get_drvdata ( pdev ) ;
rproc_del ( adsp - > rproc ) ;
2021-09-16 16:59:21 +03:00
qcom_q6v5_deinit ( & adsp - > q6v5 ) ;
2018-09-24 14:07:50 +03:00
qcom_remove_glink_subdev ( adsp - > rproc , & adsp - > glink_subdev ) ;
qcom_remove_sysmon_subdev ( adsp - > sysmon ) ;
qcom_remove_ssr_subdev ( adsp - > rproc , & adsp - > ssr_subdev ) ;
2022-02-01 13:52:04 +03:00
qcom_rproc_pds_detach ( adsp , adsp - > proxy_pds , adsp - > proxy_pd_count ) ;
2018-09-24 14:07:50 +03:00
rproc_free ( adsp - > rproc ) ;
return 0 ;
}
static const struct adsp_pil_data adsp_resource_init = {
. crash_reason_smem = 423 ,
. firmware_name = " adsp.mdt " ,
. ssr_name = " lpass " ,
. sysmon_name = " adsp " ,
. ssctl_id = 0x14 ,
2022-02-01 13:52:04 +03:00
. is_wpss = false ,
. auto_boot = true ,
2019-05-10 07:34:15 +03:00
. clk_ids = ( const char * [ ] ) {
" sway_cbcr " , " lpass_ahbs_aon_cbcr " , " lpass_ahbm_aon_cbcr " ,
" qdsp6ss_xo " , " qdsp6ss_sleep " , " qdsp6ss_core " , NULL
} ,
. num_clks = 7 ,
2022-02-01 13:52:04 +03:00
. proxy_pd_names = ( const char * [ ] ) {
" cx " , NULL
} ,
2019-05-10 07:34:15 +03:00
} ;
2022-09-28 15:27:49 +03:00
static const struct adsp_pil_data adsp_sc7280_resource_init = {
. crash_reason_smem = 423 ,
. firmware_name = " adsp.pbn " ,
. load_state = " adsp " ,
. ssr_name = " lpass " ,
. sysmon_name = " adsp " ,
. ssctl_id = 0x14 ,
. has_iommu = true ,
. auto_boot = true ,
. clk_ids = ( const char * [ ] ) {
" gcc_cfg_noc_lpass " , NULL
} ,
. num_clks = 1 ,
} ;
2019-05-10 07:34:15 +03:00
static const struct adsp_pil_data cdsp_resource_init = {
. crash_reason_smem = 601 ,
. firmware_name = " cdsp.mdt " ,
. ssr_name = " cdsp " ,
. sysmon_name = " cdsp " ,
. ssctl_id = 0x17 ,
2022-02-01 13:52:04 +03:00
. is_wpss = false ,
. auto_boot = true ,
2019-05-10 07:34:15 +03:00
. clk_ids = ( const char * [ ] ) {
" sway " , " tbu " , " bimc " , " ahb_aon " , " q6ss_slave " , " q6ss_master " ,
" q6_axim " , NULL
} ,
. num_clks = 7 ,
2022-02-01 13:52:04 +03:00
. proxy_pd_names = ( const char * [ ] ) {
" cx " , NULL
} ,
} ;
static const struct adsp_pil_data wpss_resource_init = {
. crash_reason_smem = 626 ,
. firmware_name = " wpss.mdt " ,
. ssr_name = " wpss " ,
. sysmon_name = " wpss " ,
. ssctl_id = 0x19 ,
. is_wpss = true ,
. auto_boot = false ,
. load_state = " wpss " ,
. clk_ids = ( const char * [ ] ) {
" ahb_bdg " , " ahb " , " rscp " , NULL
} ,
. num_clks = 3 ,
. proxy_pd_names = ( const char * [ ] ) {
" cx " , " mx " , NULL
} ,
2018-09-24 14:07:50 +03:00
} ;
static const struct of_device_id adsp_of_match [ ] = {
2019-05-10 07:34:15 +03:00
{ . compatible = " qcom,qcs404-cdsp-pil " , . data = & cdsp_resource_init } ,
2022-09-28 15:27:49 +03:00
{ . compatible = " qcom,sc7280-adsp-pil " , . data = & adsp_sc7280_resource_init } ,
2022-02-01 13:52:04 +03:00
{ . compatible = " qcom,sc7280-wpss-pil " , . data = & wpss_resource_init } ,
2018-09-24 14:07:50 +03:00
{ . compatible = " qcom,sdm845-adsp-pil " , . data = & adsp_resource_init } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , adsp_of_match ) ;
static struct platform_driver adsp_pil_driver = {
. probe = adsp_probe ,
. remove = adsp_remove ,
. driver = {
. name = " qcom_q6v5_adsp " ,
. of_match_table = adsp_of_match ,
} ,
} ;
module_platform_driver ( adsp_pil_driver ) ;
MODULE_DESCRIPTION ( " QTI SDM845 ADSP Peripheral Image Loader " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;