650 lines
16 KiB
C
650 lines
16 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Samsung Exynos SoC series dsp driver
|
||
|
*
|
||
|
* Copyright (c) 2020 Samsung Electronics Co., Ltd.
|
||
|
* http://www.samsung.com/
|
||
|
*/
|
||
|
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/version.h>
|
||
|
#include <dt-bindings/soc/samsung/exynos-dsp.h>
|
||
|
#if IS_ENABLED(CONFIG_DEBUG_SNAPSHOT) && defined(CONFIG_EXYNOS_DSP_HW_P0)
|
||
|
#include <soc/samsung/debug-snapshot.h>
|
||
|
#else
|
||
|
#define dbg_snapshot_expire_watchdog()
|
||
|
#endif
|
||
|
|
||
|
#include "dsp-log.h"
|
||
|
#include "dsp-dump.h"
|
||
|
#include "dsp-hw-common-init.h"
|
||
|
#include "dsp-device.h"
|
||
|
#include "dsp-binary.h"
|
||
|
#include "dsp-hw-p0-pm.h"
|
||
|
#include "dsp-hw-p0-clk.h"
|
||
|
#include "dsp-hw-p0-bus.h"
|
||
|
#include "dsp-hw-p0-llc.h"
|
||
|
#include "dsp-hw-p0-memory.h"
|
||
|
#include "dsp-hw-p0-interface.h"
|
||
|
#include "dsp-hw-p0-ctrl.h"
|
||
|
#include "dsp-hw-p0-mailbox.h"
|
||
|
#include "dsp-hw-p0-governor.h"
|
||
|
#include "dsp-hw-p0-imgloader.h"
|
||
|
#include "dsp-hw-p0-sysevent.h"
|
||
|
#include "dsp-hw-p0-memlogger.h"
|
||
|
#include "dsp-hw-p0-log.h"
|
||
|
#include "dsp-hw-p0-debug.h"
|
||
|
#include "dsp-hw-p0-dump.h"
|
||
|
#include "dsp-hw-common-system.h"
|
||
|
#include "dsp-hw-p0-system.h"
|
||
|
|
||
|
static void dsp_hw_p0_system_iovmm_fault_dump(struct dsp_system *sys)
|
||
|
{
|
||
|
dsp_enter();
|
||
|
dsp_hw_common_system_iovmm_fault_dump(sys);
|
||
|
dsp_leave();
|
||
|
}
|
||
|
|
||
|
static int __dsp_hw_p0_system_master_copy(struct dsp_system *sys)
|
||
|
{
|
||
|
int ret;
|
||
|
struct dsp_reserved_mem *rmem;
|
||
|
|
||
|
dsp_enter();
|
||
|
rmem = &sys->memory.reserved_mem[DSP_P0_RESERVED_MEM_FW_MASTER];
|
||
|
|
||
|
if (!sys->boot_bin_size) {
|
||
|
dsp_warn("master bin is not loaded yet\n");
|
||
|
ret = dsp_binary_load(DSP_P0_MASTER_FW_NAME, NULL,
|
||
|
DSP_P0_FW_EXTENSION, rmem->kvaddr, rmem->size,
|
||
|
&rmem->used_size);
|
||
|
if (ret)
|
||
|
goto p_err;
|
||
|
|
||
|
sys->boot_bin_size = rmem->used_size;
|
||
|
dsp_info("master binary[%zu] is loaded\n", sys->boot_bin_size);
|
||
|
}
|
||
|
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
p_err:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void __dsp_hw_p0_system_init(struct dsp_system *sys)
|
||
|
{
|
||
|
struct dsp_priv_mem *pmem;
|
||
|
unsigned int chip_id;
|
||
|
unsigned int product_id;
|
||
|
|
||
|
dsp_enter();
|
||
|
pmem = sys->memory.priv_mem;
|
||
|
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_TO_CC_INT_STATUS), 0);
|
||
|
dsp_ctrl_dhcp_writel(
|
||
|
DSP_P0_DHCP_IDX(DSP_P0_DHCP_TO_HOST_INT_STATUS), 0);
|
||
|
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_DEBUG_LAYER_START),
|
||
|
sys->layer_start);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_DEBUG_LAYER_END),
|
||
|
sys->layer_end);
|
||
|
|
||
|
memcpy(pmem[DSP_P0_PRIV_MEM_FW].kvaddr,
|
||
|
pmem[DSP_P0_PRIV_MEM_FW].bac_kvaddr,
|
||
|
pmem[DSP_P0_PRIV_MEM_FW].used_size);
|
||
|
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_FW_RESERVED_SIZE),
|
||
|
pmem[DSP_P0_PRIV_MEM_FW].size);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_IVP_PM_IOVA),
|
||
|
pmem[DSP_P0_PRIV_MEM_IVP_PM].iova);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_IVP_PM_SIZE),
|
||
|
pmem[DSP_P0_PRIV_MEM_IVP_PM].used_size);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_IVP_DM_IOVA),
|
||
|
pmem[DSP_P0_PRIV_MEM_IVP_DM].iova);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_IVP_DM_SIZE),
|
||
|
pmem[DSP_P0_PRIV_MEM_IVP_DM].used_size);
|
||
|
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_MAILBOX_VERSION), 0);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_MESSAGE_VERSION), 0);
|
||
|
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_FW_LOG_MEMORY_IOVA),
|
||
|
pmem[DSP_P0_PRIV_MEM_FW_LOG].iova);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_FW_LOG_MEMORY_SIZE),
|
||
|
pmem[DSP_P0_PRIV_MEM_FW_LOG].size);
|
||
|
dsp_ctrl_dhcp_writel(
|
||
|
DSP_P0_DHCP_IDX(DSP_P0_DHCP_TO_CC_MBOX_MEMORY_IOVA),
|
||
|
pmem[DSP_P0_PRIV_MEM_MBOX_MEMORY].iova);
|
||
|
dsp_ctrl_dhcp_writel(
|
||
|
DSP_P0_DHCP_IDX(DSP_P0_DHCP_TO_CC_MBOX_MEMORY_SIZE),
|
||
|
pmem[DSP_P0_PRIV_MEM_MBOX_MEMORY].size);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_TO_CC_MBOX_POOL_IOVA),
|
||
|
pmem[DSP_P0_PRIV_MEM_MBOX_POOL].iova);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_TO_CC_MBOX_POOL_SIZE),
|
||
|
pmem[DSP_P0_PRIV_MEM_MBOX_POOL].size);
|
||
|
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_KERNEL_MODE),
|
||
|
DSP_DYNAMIC_KERNEL);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_DL_OUT_IOVA),
|
||
|
pmem[DSP_P0_PRIV_MEM_DL_OUT].iova);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_DL_OUT_SIZE),
|
||
|
pmem[DSP_P0_PRIV_MEM_DL_OUT].size);
|
||
|
|
||
|
#ifndef ENABLE_DSP_VELOCE
|
||
|
chip_id = readl(sys->chip_id);
|
||
|
#else
|
||
|
chip_id = 0xdeadbeef;
|
||
|
#endif
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_CHIPID_REV), chip_id);
|
||
|
dsp_info("CHIPID : %#x\n", chip_id);
|
||
|
|
||
|
#ifndef ENABLE_DSP_VELOCE
|
||
|
product_id = readl(sys->product_id);
|
||
|
#else
|
||
|
product_id = 0xdeadbeef;
|
||
|
#endif
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_PRODUCT_ID),
|
||
|
product_id);
|
||
|
dsp_info("PRODUCT_ID : %#x\n", product_id);
|
||
|
|
||
|
if (sys->wait_mode == DSP_SYSTEM_WAIT_MODE_INTERRUPT)
|
||
|
dsp_ctrl_writel(DSP_P0_DNCC_NS_IRQ_MBOX_ENABLE_FR_CC, 0x11);
|
||
|
else if (sys->wait_mode == DSP_SYSTEM_WAIT_MODE_BUSY_WAITING)
|
||
|
dsp_ctrl_writel(DSP_P0_DNCC_NS_IRQ_MBOX_ENABLE_FR_CC, 0x0);
|
||
|
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_INTERRUPT_MODE),
|
||
|
sys->wait_mode);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_DRIVER_VERSION),
|
||
|
sys->dspdev->version);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_FIRMWARE_VERSION),
|
||
|
0xdeadbeef);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_RESERVED0),
|
||
|
0xdeadbeef);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_RESERVED1),
|
||
|
0xdeadbeef);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_RESERVED2),
|
||
|
0xdeadbeef);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_RESERVED3),
|
||
|
0xdeadbeef);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_RESERVED4),
|
||
|
0xdeadbeef);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_RESERVED5),
|
||
|
0xdeadbeef);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_RESERVED6),
|
||
|
0xdeadbeef);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_RESERVED7),
|
||
|
0xdeadbeef);
|
||
|
dsp_ctrl_dhcp_writel(DSP_P0_DHCP_IDX(DSP_P0_DHCP_RESERVED8),
|
||
|
0xdeadbeef);
|
||
|
|
||
|
dsp_leave();
|
||
|
}
|
||
|
|
||
|
static int __dsp_hw_p0_system_wait_boot(struct dsp_system *sys)
|
||
|
{
|
||
|
int ret;
|
||
|
unsigned int wait_time;
|
||
|
long timeout;
|
||
|
|
||
|
dsp_enter();
|
||
|
wait_time = sys->wait[DSP_SYSTEM_WAIT_BOOT];
|
||
|
#ifndef ENABLE_DSP_VELOCE
|
||
|
dsp_dbg("[boot] wait mode : %u\n", sys->wait_mode);
|
||
|
if (sys->wait_mode == DSP_SYSTEM_WAIT_MODE_INTERRUPT) {
|
||
|
timeout = wait_event_timeout(sys->system_wq,
|
||
|
sys->system_flag & BIT(DSP_SYSTEM_BOOT),
|
||
|
msecs_to_jiffies(wait_time));
|
||
|
if (!timeout) {
|
||
|
sys->interface.ops->check_irq(&sys->interface);
|
||
|
if (sys->system_flag & BIT(DSP_SYSTEM_BOOT)) {
|
||
|
timeout = 1;
|
||
|
dsp_warn("interrupt was unstable\n");
|
||
|
}
|
||
|
}
|
||
|
} else if (sys->wait_mode == DSP_SYSTEM_WAIT_MODE_BUSY_WAITING) {
|
||
|
timeout = wait_time * 1000;
|
||
|
|
||
|
while (timeout) {
|
||
|
if (sys->system_flag & BIT(DSP_SYSTEM_BOOT))
|
||
|
break;
|
||
|
|
||
|
sys->interface.ops->check_irq(&sys->interface);
|
||
|
timeout -= 10;
|
||
|
udelay(10);
|
||
|
}
|
||
|
} else {
|
||
|
ret = -EINVAL;
|
||
|
dsp_err("wait mode(%u) is invalid\n", sys->wait_mode);
|
||
|
goto p_err;
|
||
|
}
|
||
|
#else
|
||
|
while (1) {
|
||
|
if (sys->system_flag & BIT(DSP_SYSTEM_BOOT))
|
||
|
break;
|
||
|
|
||
|
dsp_ctrl_pc_dump();
|
||
|
dsp_ctrl_userdefined_dump();
|
||
|
mdelay(100);
|
||
|
}
|
||
|
timeout = 1;
|
||
|
#endif
|
||
|
if (!timeout) {
|
||
|
ret = -ETIMEDOUT;
|
||
|
dsp_err("Failed to boot DSP (wait time %ums)\n",
|
||
|
sys->wait[DSP_SYSTEM_WAIT_BOOT]);
|
||
|
if (sys->timeout_handler) {
|
||
|
sys->timeout_handler(sys, DSP_SYSTEM_WAIT_BOOT);
|
||
|
} else {
|
||
|
dsp_warn("timeout handler was not registered\n");
|
||
|
dsp_dump_ctrl();
|
||
|
dsp_dump_task_count();
|
||
|
}
|
||
|
goto p_err;
|
||
|
} else {
|
||
|
dsp_info("Completed to boot DSP\n");
|
||
|
dsp_dump_task_count();
|
||
|
}
|
||
|
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
p_err:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int __dsp_hw_p0_system_check_kernel_mode(struct dsp_system *sys)
|
||
|
{
|
||
|
int kernel_mode;
|
||
|
|
||
|
dsp_enter();
|
||
|
kernel_mode = dsp_ctrl_dhcp_readl(
|
||
|
DSP_P0_DHCP_IDX(DSP_P0_DHCP_KERNEL_MODE));
|
||
|
if (kernel_mode != DSP_DYNAMIC_KERNEL) {
|
||
|
dsp_err("static kernel is no longer available\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int dsp_hw_p0_system_boot(struct dsp_system *sys)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
dsp_enter();
|
||
|
sys->system_flag = 0x0;
|
||
|
|
||
|
sys->ctrl.ops->all_init(&sys->ctrl);
|
||
|
ret = __dsp_hw_p0_system_master_copy(sys);
|
||
|
if (ret)
|
||
|
goto p_err;
|
||
|
|
||
|
ret = sys->imgloader.ops->boot(&sys->imgloader);
|
||
|
if (ret) {
|
||
|
dsp_err("Failed to boot imgloader ops(%d/%zu)\n",
|
||
|
ret, sys->boot_bin_size);
|
||
|
goto p_err;
|
||
|
}
|
||
|
|
||
|
__dsp_hw_p0_system_init(sys);
|
||
|
sys->log.ops->start(&sys->log);
|
||
|
|
||
|
sys->pm.ops->update_devfreq_boot(&sys->pm);
|
||
|
sys->ctrl.ops->start(&sys->ctrl);
|
||
|
ret = __dsp_hw_p0_system_wait_boot(sys);
|
||
|
sys->pm.ops->update_devfreq_min(&sys->pm);
|
||
|
if (ret)
|
||
|
goto p_err_reset;
|
||
|
|
||
|
ret = __dsp_hw_p0_system_check_kernel_mode(sys);
|
||
|
if (ret)
|
||
|
goto p_err_reset;
|
||
|
|
||
|
ret = sys->mailbox.ops->start(&sys->mailbox);
|
||
|
if (ret)
|
||
|
goto p_err_reset;
|
||
|
|
||
|
sys->boot_init |= BIT(DSP_SYSTEM_DSP_INIT);
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
p_err_reset:
|
||
|
sys->ops->reset(sys);
|
||
|
p_err:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int __dsp_hw_p0_system_wait_reset(struct dsp_system *sys)
|
||
|
{
|
||
|
int ret;
|
||
|
unsigned int wait_time;
|
||
|
long timeout;
|
||
|
|
||
|
dsp_enter();
|
||
|
wait_time = sys->wait[DSP_SYSTEM_WAIT_RESET];
|
||
|
#ifndef ENABLE_DSP_VELOCE
|
||
|
dsp_dbg("[reset] wait mode : %u\n", sys->wait_mode);
|
||
|
if (sys->wait_mode == DSP_SYSTEM_WAIT_MODE_INTERRUPT) {
|
||
|
timeout = wait_event_timeout(sys->system_wq,
|
||
|
sys->system_flag & BIT(DSP_SYSTEM_RESET),
|
||
|
msecs_to_jiffies(wait_time));
|
||
|
if (!timeout) {
|
||
|
sys->interface.ops->check_irq(&sys->interface);
|
||
|
if (sys->system_flag & BIT(DSP_SYSTEM_RESET)) {
|
||
|
timeout = 1;
|
||
|
dsp_warn("interrupt was unstable\n");
|
||
|
}
|
||
|
}
|
||
|
} else if (sys->wait_mode == DSP_SYSTEM_WAIT_MODE_BUSY_WAITING) {
|
||
|
timeout = wait_time * 1000;
|
||
|
|
||
|
while (timeout) {
|
||
|
if (sys->system_flag & BIT(DSP_SYSTEM_RESET))
|
||
|
break;
|
||
|
|
||
|
sys->interface.ops->check_irq(&sys->interface);
|
||
|
timeout -= 10;
|
||
|
udelay(10);
|
||
|
}
|
||
|
} else {
|
||
|
ret = -EINVAL;
|
||
|
dsp_err("wait mode(%u) is invalid\n", sys->wait_mode);
|
||
|
goto p_err;
|
||
|
}
|
||
|
#else
|
||
|
while (1) {
|
||
|
if (sys->system_flag & BIT(DSP_SYSTEM_RESET))
|
||
|
break;
|
||
|
|
||
|
dsp_ctrl_pc_dump();
|
||
|
dsp_ctrl_userdefined_dump();
|
||
|
mdelay(100);
|
||
|
}
|
||
|
timeout = 1;
|
||
|
#endif
|
||
|
if (!timeout) {
|
||
|
ret = -ETIMEDOUT;
|
||
|
dsp_err("Failed to reset DSP (wait time %ums)\n",
|
||
|
sys->wait[DSP_SYSTEM_WAIT_RESET]);
|
||
|
if (sys->timeout_handler) {
|
||
|
sys->timeout_handler(sys, DSP_SYSTEM_WAIT_RESET);
|
||
|
} else {
|
||
|
dsp_warn("timeout handler was not registered\n");
|
||
|
dsp_dump_ctrl();
|
||
|
dsp_dump_task_count();
|
||
|
}
|
||
|
goto p_err;
|
||
|
}
|
||
|
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
p_err:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int dsp_hw_p0_system_reset(struct dsp_system *sys)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
dsp_enter();
|
||
|
sys->system_flag = 0x0;
|
||
|
sys->pm.ops->update_devfreq_boot(&sys->pm);
|
||
|
sys->interface.ops->send_irq(&sys->interface, BIT(DSP_TO_CC_INT_RESET));
|
||
|
ret = __dsp_hw_p0_system_wait_reset(sys);
|
||
|
if (ret)
|
||
|
sys->ctrl.ops->force_reset(&sys->ctrl);
|
||
|
else
|
||
|
sys->ctrl.ops->reset(&sys->ctrl);
|
||
|
sys->pm.ops->update_devfreq_min(&sys->pm);
|
||
|
|
||
|
sys->mailbox.ops->stop(&sys->mailbox);
|
||
|
sys->log.ops->stop(&sys->log);
|
||
|
sys->boot_init &= ~BIT(DSP_SYSTEM_DSP_INIT);
|
||
|
sys->imgloader.ops->shutdown(&sys->imgloader);
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __dsp_hw_p0_system_binary_load(struct dsp_system *sys)
|
||
|
{
|
||
|
int ret;
|
||
|
struct dsp_memory *mem;
|
||
|
struct dsp_priv_mem *pmem;
|
||
|
|
||
|
dsp_enter();
|
||
|
mem = &sys->memory;
|
||
|
|
||
|
pmem = &mem->priv_mem[DSP_P0_PRIV_MEM_FW];
|
||
|
ret = dsp_binary_load(DSP_P0_FW_NAME, sys->fw_postfix,
|
||
|
DSP_P0_FW_EXTENSION, pmem->bac_kvaddr, pmem->size,
|
||
|
&pmem->used_size);
|
||
|
if (ret)
|
||
|
goto p_err_load;
|
||
|
|
||
|
pmem = &mem->priv_mem[DSP_P0_PRIV_MEM_IVP_PM];
|
||
|
ret = dsp_binary_load(DSP_P0_IVP_PM_NAME, sys->fw_postfix,
|
||
|
DSP_P0_FW_EXTENSION, pmem->kvaddr, pmem->size,
|
||
|
&pmem->used_size);
|
||
|
if (ret)
|
||
|
goto p_err_load;
|
||
|
|
||
|
pmem = &mem->priv_mem[DSP_P0_PRIV_MEM_IVP_DM];
|
||
|
ret = dsp_binary_load(DSP_P0_IVP_DM_NAME, sys->fw_postfix,
|
||
|
DSP_P0_FW_EXTENSION, pmem->kvaddr, pmem->size,
|
||
|
&pmem->used_size);
|
||
|
if (ret)
|
||
|
goto p_err_load;
|
||
|
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
p_err_load:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int dsp_hw_p0_system_start(struct dsp_system *sys)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
dsp_enter();
|
||
|
ret = __dsp_hw_p0_system_binary_load(sys);
|
||
|
if (ret)
|
||
|
goto p_err;
|
||
|
|
||
|
ret = dsp_hw_common_system_start(sys);
|
||
|
if (ret) {
|
||
|
dsp_err("Failed to start common system(%d)\n", ret);
|
||
|
goto p_err;
|
||
|
}
|
||
|
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
p_err:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int dsp_hw_p0_system_timeout_handler(struct dsp_system *sys,
|
||
|
unsigned int wait_id)
|
||
|
{
|
||
|
bool check;
|
||
|
|
||
|
dsp_enter();
|
||
|
if ((sys->debug_mode & BIT(DSP_DEBUG_MODE_TASK_TIMEOUT_PANIC)) &&
|
||
|
(wait_id == DSP_SYSTEM_WAIT_MAILBOX))
|
||
|
check = true;
|
||
|
else if ((sys->debug_mode & BIT(DSP_DEBUG_MODE_BOOT_TIMEOUT_PANIC)) &&
|
||
|
(wait_id == DSP_SYSTEM_WAIT_BOOT))
|
||
|
check = true;
|
||
|
else if ((sys->debug_mode & BIT(DSP_DEBUG_MODE_RESET_TIMEOUT_PANIC)) &&
|
||
|
(wait_id == DSP_SYSTEM_WAIT_RESET))
|
||
|
check = true;
|
||
|
else
|
||
|
check = false;
|
||
|
|
||
|
if (check) {
|
||
|
dsp_dump_ctrl();
|
||
|
dsp_dump_task_count();
|
||
|
dbg_snapshot_expire_watchdog();
|
||
|
BUG();
|
||
|
} else {
|
||
|
dsp_dump_ctrl();
|
||
|
dsp_dump_task_count();
|
||
|
}
|
||
|
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void __dsp_hw_p0_system_master_load_async(const struct firmware *fw,
|
||
|
void *context)
|
||
|
{
|
||
|
int ret, idx, retry = 10;
|
||
|
struct dsp_system *sys;
|
||
|
char full_name[DSP_BINARY_NAME_SIZE];
|
||
|
struct dsp_reserved_mem *rmem;
|
||
|
|
||
|
dsp_enter();
|
||
|
sys = context;
|
||
|
rmem = &sys->memory.reserved_mem[DSP_P0_RESERVED_MEM_FW_MASTER];
|
||
|
|
||
|
snprintf(full_name, DSP_BINARY_NAME_SIZE, "%s.%s",
|
||
|
DSP_P0_MASTER_FW_NAME, DSP_P0_FW_EXTENSION);
|
||
|
|
||
|
if (!fw) {
|
||
|
for (idx = 0; idx < retry; ++idx) {
|
||
|
#if KERNEL_VERSION(4, 18, 0) > LINUX_VERSION_CODE
|
||
|
ret = request_firmware_direct(&fw, full_name, sys->dev);
|
||
|
#else
|
||
|
ret = firmware_request_nowarn(&fw, full_name, sys->dev);
|
||
|
#endif
|
||
|
if (ret >= 0)
|
||
|
break;
|
||
|
/* Wait for the file system to be mounted at boot time*/
|
||
|
msleep(500);
|
||
|
}
|
||
|
if (ret < 0) {
|
||
|
dsp_err("Failed to request binary[%s]\n", full_name);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (rmem->size < fw->size) {
|
||
|
dsp_err("master bin size is over(%zu/%zu)\n",
|
||
|
rmem->size, fw->size);
|
||
|
release_firmware(fw);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
memcpy(rmem->kvaddr, fw->data, fw->size);
|
||
|
rmem->used_size = fw->size;
|
||
|
sys->boot_bin_size = fw->size;
|
||
|
release_firmware(fw);
|
||
|
dsp_info("binary[%s/%zu] is loaded\n", full_name, sys->boot_bin_size);
|
||
|
dsp_leave();
|
||
|
}
|
||
|
|
||
|
static int dsp_hw_p0_system_probe(struct dsp_system *sys, void *dspdev)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
dsp_enter();
|
||
|
ret = dsp_hw_common_system_probe(sys, dspdev,
|
||
|
dsp_hw_p0_system_timeout_handler);
|
||
|
if (ret) {
|
||
|
dsp_err("Failed to probe common system(%d)\n", ret);
|
||
|
goto p_err;
|
||
|
}
|
||
|
|
||
|
ret = dsp_binary_load_async(DSP_P0_MASTER_FW_NAME, NULL,
|
||
|
DSP_P0_FW_EXTENSION, sys,
|
||
|
__dsp_hw_p0_system_master_load_async);
|
||
|
if (ret < 0)
|
||
|
goto p_err_bin_load;
|
||
|
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
p_err_bin_load:
|
||
|
dsp_hw_common_system_remove(sys);
|
||
|
p_err:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void dsp_hw_p0_system_remove(struct dsp_system *sys)
|
||
|
{
|
||
|
dsp_enter();
|
||
|
dsp_hw_common_system_remove(sys);
|
||
|
dsp_leave();
|
||
|
}
|
||
|
|
||
|
static const struct dsp_system_ops p0_system_ops = {
|
||
|
.request_control = dsp_hw_common_system_request_control,
|
||
|
.execute_task = dsp_hw_common_system_execute_task,
|
||
|
.iovmm_fault_dump = dsp_hw_p0_system_iovmm_fault_dump,
|
||
|
.boot = dsp_hw_p0_system_boot,
|
||
|
.reset = dsp_hw_p0_system_reset,
|
||
|
.power_active = dsp_hw_common_system_power_active,
|
||
|
.set_boot_qos = dsp_hw_common_system_set_boot_qos,
|
||
|
.runtime_resume = dsp_hw_common_system_runtime_resume,
|
||
|
.runtime_suspend = dsp_hw_common_system_runtime_suspend,
|
||
|
.resume = dsp_hw_common_system_resume,
|
||
|
.suspend = dsp_hw_common_system_suspend,
|
||
|
|
||
|
.npu_start = dsp_hw_common_system_npu_start,
|
||
|
.start = dsp_hw_p0_system_start,
|
||
|
.stop = dsp_hw_common_system_stop,
|
||
|
|
||
|
.open = dsp_hw_common_system_open,
|
||
|
.close = dsp_hw_common_system_close,
|
||
|
.probe = dsp_hw_p0_system_probe,
|
||
|
.remove = dsp_hw_p0_system_remove,
|
||
|
};
|
||
|
|
||
|
int dsp_hw_p0_system_register_ops(void)
|
||
|
{
|
||
|
int ret;
|
||
|
int (*ops_list[])(void) = {
|
||
|
dsp_hw_p0_pm_register_ops,
|
||
|
dsp_hw_p0_clk_register_ops,
|
||
|
dsp_hw_p0_bus_register_ops,
|
||
|
dsp_hw_p0_llc_register_ops,
|
||
|
dsp_hw_p0_memory_register_ops,
|
||
|
dsp_hw_p0_interface_register_ops,
|
||
|
dsp_hw_p0_ctrl_register_ops,
|
||
|
dsp_hw_p0_mailbox_register_ops,
|
||
|
dsp_hw_p0_governor_register_ops,
|
||
|
dsp_hw_p0_imgloader_register_ops,
|
||
|
dsp_hw_p0_sysevent_register_ops,
|
||
|
dsp_hw_p0_memlogger_register_ops,
|
||
|
dsp_hw_p0_log_register_ops,
|
||
|
dsp_hw_p0_debug_register_ops,
|
||
|
dsp_hw_p0_dump_register_ops,
|
||
|
};
|
||
|
size_t size, idx;
|
||
|
|
||
|
dsp_enter();
|
||
|
size = ARRAY_SIZE(ops_list);
|
||
|
if (size != DSP_HW_OPS_COUNT - 1) {
|
||
|
ret = -EINVAL;
|
||
|
dsp_err("size of ops_list is not enough(%zu/%zu)\n",
|
||
|
size, DSP_HW_OPS_COUNT - 1);
|
||
|
goto p_err;
|
||
|
}
|
||
|
|
||
|
ret = dsp_hw_common_register_ops(DSP_DEVICE_ID_P0, DSP_HW_OPS_SYSTEM,
|
||
|
&p0_system_ops, sizeof(p0_system_ops) / sizeof(void *));
|
||
|
if (ret)
|
||
|
goto p_err;
|
||
|
|
||
|
for (idx = 0; idx < size; ++idx) {
|
||
|
ret = ops_list[idx]();
|
||
|
if (ret) {
|
||
|
dsp_err("Failed to register ops at system(%zu/%zu)\n",
|
||
|
idx, size);
|
||
|
goto p_err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dsp_info("hw_p0(%u) was initilized\n", DSP_DEVICE_ID_P0);
|
||
|
dsp_leave();
|
||
|
return 0;
|
||
|
p_err:
|
||
|
return ret;
|
||
|
}
|