/* * Copyright (c) Hisilicon Technologies Co., Ltd. 2020-2020. All rights reserved. * Description: asr driver * Author: audio * Create: 2020/9/11 * Notes: * History: 2020/9/11 init. */ #include "reg_crg_ext.h" #include "drv_sys_ext.h" #include "td_type.h" #include "drv_asr_elf.h" #include "drv_asr_defines.h" #include "drv_asr_ext.h" #include "osal_ext.h" #include "reg_asr_ext.h" #include "drv_file_ext.h" #ifdef __cplusplus #if __cplusplus extern "C" { #endif #endif static td_uchar* g_pc_elf_buf = TD_NULL; static td_uchar* g_pc_overlay_buf = TD_NULL; static osal_semaphore g_dsp_mutex = {0}; static osal_task *g_asr_thread = TD_NULL; static volatile dsp_regs_type* g_reg_asr_secure = TD_NULL; static volatile sysctrl_asr_cfg_0* g_reg_asr_sysctl = TD_NULL; static volatile sc_crg_ctrl_0* g_reg_asr_sc_crg_ctrl = TD_NULL; static volatile td_u32* g_reg_asr_sc_epll_crg_2 = TD_NULL; static volatile U_PERI_CRG182* g_reg_asr_crg = TD_NULL; static volatile reg_sys_ctl_asr_idle_mask* g_reg_sysctl_asr_int_mask = TD_NULL; static volatile reg_sys_ctl_asr_idle_sta* g_reg_sysctl_asr_int_sta = TD_NULL; static volatile reg_sys_ctl_asr_idle_mask_sta* g_reg_sysctl_asr_int_mask_sta = TD_NULL; static volatile td_u32* g_overlay_start_entry = TD_NULL; static volatile reg_asr_start_vec *g_reg_asr_start_vec = TD_NULL; static td_void asr_reg_unmap(td_void); #define DSP_OCRAM_SIZE 0x8000 #define REG_START 0 #define REG_END (0x10000000 - 0x4) #define DDR_ADDR_START 0x10000000 /* ASR */ #define REG_ASR 0x00980000 #define DSP_SECUE_REG_ADDR 0x00982000 /* system ctrl reg */ #define ASR_CRG_CTRL_0_ADDR 0x00840050 #define ASR_CRG_0_ADDR 0x00840070 #define ASR_SC_EPLL_CRG_2_ADDR 0x00840088 /* SC_EPLL_CRG_2 */ #define ASR_PWRUP_CTRL_ADDR 0x00840190 #define ASR_START_VEC 0x009802f8 #define ASR_INT_MASK_ADDR 0x00840B80 #define ASR_INT_STATE_ADDR 0x00840B88 #define ASR_INT_MASK_STATE_ADDR 0x00840B8c #define ASR_CRG182_REG_ADDR 0x00A002D8 #define ASR_OVERLAY_ENTRY_ADDR 0x02819000 static td_s32 asr_check_firmware_file(td_void) { td_s32 ret; td_u32 i; td_u32 path_num; td_void *fp_fw = TD_NULL; td_void *fp_overlay = TD_NULL; td_u8 fw_name[128]; /* 128: len */ td_u8 overlay_name[128]; td_u8 *path[] = { "/lib/firmware/", "/vendor/firmware/" }; path_num = sizeof(path) / sizeof(path[0]); for (i = 0; i < path_num; i++) { ret = snprintf_s(fw_name, sizeof(fw_name), sizeof(fw_name) - 1, "%s%s", path[i], ASR_BIN_PATH); if (ret < 0) { soc_err_print_call_fun_err(snprintf_s, ret); return ret; } ret = snprintf_s(overlay_name, sizeof(overlay_name), sizeof(overlay_name) - 1, "%s%s", path[i], ASR_OVERLAY_PATH); if (ret < 0) { soc_err_print_call_fun_err(snprintf_s, ret); return ret; } fp_fw = osal_klib_fopen(fw_name, OSAL_O_RDONLY, 0); fp_overlay = osal_klib_fopen(overlay_name, OSAL_O_RDONLY, 0); if (fp_fw == TD_NULL || fp_overlay == TD_NULL) { continue; } osal_klib_fclose(fp_fw); osal_klib_fclose(fp_overlay); return TD_SUCCESS; } return TD_FAILURE; } static td_s32 asr_load_asr_code(td_void) { td_s32 ret; osal_firmware *fw = TD_NULL; osal_firmware *overlay = TD_NULL; ret = asr_check_firmware_file(); if (ret != TD_SUCCESS) { return ret; } ret = osal_request_firmware_into_buf(&fw, ASR_BIN_PATH, g_pc_elf_buf, ELF_SIZE_MAX); if (ret != 0) { soc_err_print_call_fun_err(osal_request_firmware_into_buf, ret); return ret; } ret = osal_request_firmware_into_buf(&overlay, ASR_OVERLAY_PATH, g_pc_overlay_buf, OVERLAY_SIZE_MAX); if (ret != 0) { soc_err_print_call_fun_err(osal_request_firmware_into_buf, ret); return ret; } osal_release_firmware(fw); osal_release_firmware(overlay); return TD_SUCCESS; } static td_s32 asr_load_firmware_thread(td_void *data) { td_s32 ret; while (!osal_kthread_should_stop()) { osal_msleep(500); /* 500 ms */ ret = asr_load_asr_code(); if (ret != TD_SUCCESS) { continue; } break; } return TD_SUCCESS; } static td_s32 asr_try_loading_firmware(td_void) { td_s32 ret; ret = asr_load_asr_code(); if (ret == TD_SUCCESS) { return TD_SUCCESS; } g_asr_thread = osal_kthread_create(asr_load_firmware_thread, TD_NULL, "asr_loading_thread", 0); if (g_asr_thread == TD_NULL) { soc_err_print_call_fun_err(osal_kthread_create, TD_FAILURE); return TD_FAILURE; } return TD_SUCCESS; } static td_s32 asr_firmware_buf_init(td_void) { g_pc_elf_buf = osal_vmalloc(ELF_SIZE_MAX); if (g_pc_elf_buf == TD_NULL) { soc_log_err("g_pc_elf_buf SOC_VMALLOC fail\n"); return TD_FAILURE; } g_pc_overlay_buf = osal_vmalloc(OVERLAY_SIZE_MAX); if (g_pc_overlay_buf == TD_NULL) { soc_log_err("g_pc_elf_buf SOC_VMALLOC fail\n"); osal_vfree(g_pc_elf_buf); g_pc_elf_buf = TD_NULL; return TD_FAILURE; } memset_s(g_pc_elf_buf, ELF_SIZE_MAX, 0, ELF_SIZE_MAX); /* reset firmware area to all 0. */ memset_s(g_pc_overlay_buf, OVERLAY_SIZE_MAX, 0, OVERLAY_SIZE_MAX); return TD_SUCCESS; } static td_void asr_firmware_buf_deinit(td_void) { if (g_pc_elf_buf != TD_NULL) { osal_vfree(g_pc_elf_buf); g_pc_elf_buf = TD_NULL; } asr_reg_unmap(); return; } td_s32 asr_firmware_init(td_void) { td_s32 ret; ret = asr_firmware_buf_init(); if (ret != TD_SUCCESS) { soc_log_err("asr_firmware_buf_init fail\n"); return TD_FAILURE; } ret = asr_try_loading_firmware(); if (ret != TD_SUCCESS) { soc_log_err("asr_try_loading_firmware fail\n"); return TD_FAILURE; } return TD_SUCCESS; } td_void asr_firmware_deinit(td_void) { if (g_asr_thread != TD_NULL) { osal_kthread_destroy(g_asr_thread, 1); g_asr_thread = TD_NULL; } asr_firmware_buf_deinit(); return; } static td_s32 asr_load_elf(td_void) { td_s32 ret; td_u32 start_entry; if (g_pc_elf_buf == TD_NULL) { soc_log_err("no asr firmware, load asr fail!\n"); return TD_FAILURE; } asr_copy_elf_section(g_pc_overlay_buf); ret = asr_check_elf_paser(g_pc_overlay_buf); if (ret != TD_SUCCESS) { soc_log_err("call asr_check_elf_paser failed(0x%x)\n", ret); osal_vfree(g_pc_overlay_buf); g_pc_overlay_buf = TD_NULL; return ret; } asr_get_elf_entry(g_pc_overlay_buf, &start_entry); *g_overlay_start_entry = start_entry; asr_copy_elf_section(g_pc_elf_buf); ret = asr_check_elf_paser(g_pc_elf_buf); if (ret != TD_SUCCESS) { soc_log_err("call asr_check_elf_paser failed(0x%x)\n", ret); return ret; } return ret; } td_s32 asr_sem_init(td_void) { td_s32 ret; ret = osal_sem_init(&g_dsp_mutex, 1); if (ret != TD_SUCCESS) { soc_log_err("call osal_sem_init fail: 0x%x\n", ret); return ret; } return TD_SUCCESS; } td_void asr_sem_deinit(td_void) { osal_sem_destroy(&g_dsp_mutex); } static td_void asr_reg_unmap1(td_void) { if (g_reg_asr_secure != TD_NULL) { osal_iounmap((td_void *)g_reg_asr_secure, sizeof(dsp_regs_type)); g_reg_asr_secure = TD_NULL; } if (g_reg_asr_sysctl != TD_NULL) { osal_iounmap((td_void *)g_reg_asr_sysctl, sizeof(sysctrl_asr_cfg_0)); g_reg_asr_sysctl = TD_NULL; } if (g_reg_asr_sc_crg_ctrl != TD_NULL) { osal_iounmap((td_void *)g_reg_asr_sc_crg_ctrl, sizeof(sc_crg_ctrl_0)); g_reg_asr_sc_crg_ctrl = TD_NULL; } if (g_reg_asr_sc_epll_crg_2 != TD_NULL) { osal_iounmap((td_void *)g_reg_asr_sc_epll_crg_2, sizeof(td_u32)); g_reg_asr_sc_epll_crg_2 = TD_NULL; } if (g_reg_asr_crg != TD_NULL) { osal_iounmap((td_void *)g_reg_asr_crg, sizeof(U_PERI_CRG182)); g_reg_asr_crg = TD_NULL; } if (g_overlay_start_entry != TD_NULL) { osal_iounmap((td_void *)g_overlay_start_entry, sizeof(td_u32)); g_overlay_start_entry = TD_NULL; } } static td_void asr_reg_unmap2(td_void) { if (g_reg_sysctl_asr_int_mask != TD_NULL) { osal_iounmap((td_void *)g_reg_sysctl_asr_int_mask, sizeof(reg_sys_ctl_asr_idle_mask)); g_reg_sysctl_asr_int_mask = TD_NULL; } if (g_reg_sysctl_asr_int_sta != TD_NULL) { osal_iounmap((td_void *)g_reg_sysctl_asr_int_sta, sizeof(reg_sys_ctl_asr_idle_sta)); g_reg_sysctl_asr_int_sta = TD_NULL; } if (g_reg_sysctl_asr_int_mask_sta != TD_NULL) { osal_iounmap((td_void *)g_reg_sysctl_asr_int_mask_sta, sizeof(reg_sys_ctl_asr_idle_mask_sta)); g_reg_sysctl_asr_int_mask_sta = TD_NULL; } if (g_reg_asr_start_vec != TD_NULL) { osal_iounmap((td_void *)g_reg_asr_start_vec, sizeof(reg_asr_start_vec)); g_reg_asr_start_vec = TD_NULL; } } static td_void asr_reg_unmap(td_void) { asr_reg_unmap1(); asr_reg_unmap2(); } static td_void asr_reg_map1(td_void) { g_reg_asr_secure = (volatile dsp_regs_type*)osal_ioremap_nocache(DSP_SECUE_REG_ADDR, sizeof(dsp_regs_type)); if (g_reg_asr_secure == TD_NULL) { soc_log_err("osal_ioremap_nocache dsp_regs_type failed!\n"); goto err; } g_reg_asr_sysctl = (volatile sysctrl_asr_cfg_0*)osal_ioremap_nocache(ASR_CRG_0_ADDR, sizeof(sysctrl_asr_cfg_0)); if (g_reg_asr_sysctl == TD_NULL) { soc_log_err("osal_ioremap_nocache sysctrl_asr_cfg_0 failed!\n"); goto err; } g_reg_asr_sc_crg_ctrl = (volatile sc_crg_ctrl_0*)osal_ioremap_nocache(ASR_CRG_CTRL_0_ADDR, sizeof(sc_crg_ctrl_0)); if (g_reg_asr_sc_crg_ctrl == TD_NULL) { soc_log_err("osal_ioremap_nocache sc_crg_ctrl_0 failed!\n"); goto err; } g_reg_asr_sc_epll_crg_2 = (volatile td_u32*)osal_ioremap_nocache(ASR_SC_EPLL_CRG_2_ADDR, sizeof(td_u32)); if (g_reg_asr_sc_epll_crg_2 == TD_NULL) { soc_log_err("osal_ioremap_nocache sc_crg_ctrl_0 failed!\n"); goto err; } g_reg_asr_crg = (volatile U_PERI_CRG182*)osal_ioremap_nocache(ASR_CRG182_REG_ADDR, sizeof(U_PERI_CRG182)); if (g_reg_asr_crg == TD_NULL) { soc_log_err("osal_ioremap_nocache U_PERI_CRG182 failed!\n"); goto err; } g_overlay_start_entry = (volatile td_u32*)osal_ioremap_nocache(ASR_OVERLAY_ENTRY_ADDR, sizeof(td_u32)); if (g_overlay_start_entry == TD_NULL) { soc_log_info("osal_ioremap_nocache sc_crg_ctrl_0 failed!\n"); goto err; } return; err: asr_reg_unmap1(); } static td_void asr_reg_map2(td_void) { g_reg_sysctl_asr_int_mask = (volatile reg_sys_ctl_asr_idle_mask*)osal_ioremap_nocache(ASR_INT_MASK_ADDR, sizeof(reg_sys_ctl_asr_idle_mask)); if (g_reg_sysctl_asr_int_mask == TD_NULL) { soc_log_err("osal_ioremap_nocache reg_sys_ctl_asr_idle_maskfailed!\n"); goto err; } g_reg_sysctl_asr_int_sta = (volatile reg_sys_ctl_asr_idle_sta*)osal_ioremap_nocache(ASR_INT_STATE_ADDR, sizeof(reg_sys_ctl_asr_idle_sta)); if (g_reg_sysctl_asr_int_sta == TD_NULL) { soc_log_err("osal_ioremap_nocache reg_sys_ctl_asr_idle_sta!\n"); goto err; } g_reg_sysctl_asr_int_mask_sta = (volatile reg_sys_ctl_asr_idle_mask_sta*) osal_ioremap_nocache(ASR_INT_MASK_STATE_ADDR, sizeof(reg_sys_ctl_asr_idle_mask_sta)); if (g_reg_sysctl_asr_int_mask_sta == TD_NULL) { soc_log_err("osal_ioremap_nocache reg_sys_ctl_asr_idle_sta!\n"); goto err; } g_reg_asr_start_vec = (volatile reg_asr_start_vec *)osal_ioremap_nocache(ASR_START_VEC, sizeof(reg_asr_start_vec)); if (g_reg_asr_start_vec == TD_NULL) { soc_log_info("osal_ioremap_nocache g_reg_asr_start_vec failed!\n"); goto err; } return; err: asr_reg_unmap(); } static td_void asr_reg_map(td_void) { asr_reg_map1(); asr_reg_map2(); } static td_void asr_select_clk(td_void) { volatile U_PERI_CRG182 crg; crg.u32 = g_reg_asr_crg->u32; crg.bits.dsp0_cksel = 0x0; /* 0-12M, 1-600M, 2-784M, 3-392M, 4-50M ,5-25M, 7-710M */ g_reg_asr_crg->u32 = crg.u32; } static td_void asr_sc_crg_ctrl_init(td_void) { volatile sc_crg_ctrl_0 crg; crg.u32 = g_reg_asr_sc_crg_ctrl->u32; crg.bits.sys_aclk_asr_cken = 1; /* [20] */ g_reg_asr_sc_crg_ctrl->u32 = crg.u32; } static td_void asr_sc_epll_crg_2_init(td_void) { *g_reg_asr_sc_epll_crg_2 = 0x100000; /* EPLL enable */ } static void asr_disable(td_void) { volatile dsp_ctrl_info dsp_ctrl; dsp_ctrl.u32 = g_reg_asr_secure->dsp_ctrl.u32; dsp_ctrl.bits.dsp_en = 0; g_reg_asr_secure->dsp_ctrl.u32 = dsp_ctrl.u32; osal_msleep(1); } static void asr_enable(td_void) { volatile dsp_ctrl_info dsp_ctrl; dsp_ctrl.u32 = g_reg_asr_secure->dsp_ctrl.u32; dsp_ctrl.bits.dsp_en = 1; g_reg_asr_secure->dsp_ctrl.u32 = dsp_ctrl.u32; osal_msleep(1); } static void asr_sysctrl_reset(td_void) { volatile sysctrl_asr_cfg_0 crg; crg.u32 = g_reg_asr_sysctl->u32; crg.bits.sys_dsp0_srst_req = 1; crg.bits.sys_dsp0_debug_srst_req = 1; g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); } static void asr_sysctrl_unreset(td_void) { volatile sysctrl_asr_cfg_0 crg; crg.u32 = g_reg_asr_sysctl->u32; crg.bits.sys_dsp0_srst_req = 0; crg.bits.sys_dsp0_debug_srst_req = 0; g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); } static void asr_start_addr_set(td_s32 val) { volatile reg_asr_start_vec start_vec; start_vec.u32 = g_reg_asr_start_vec->u32; start_vec.bits.statvectorsel = val; g_reg_asr_start_vec->u32 = start_vec.u32; osal_msleep(1); /* wait 1 ms */ } static td_void asr_clk_set(td_void) { volatile sysctrl_asr_cfg_0 crg; crg.u32 = g_reg_asr_sysctl->u32; /* * DSP0 work clk * 00: crystal oscillator 24M * 01: clk_dsp0_occ --> 600M * 10: clk_12d288m_int --> 394M * 11: clk_49d1m_int --> 524M */ crg.bits.sys_asr0_clk_sel = 2; /* 2: 349M */ /* * DSP0 bus AXI clk * 00: crystal oscillator 24M * 01: aclk_asr_occ * 10: clk_49d1m_ini --> 349M * 11: clk_12d288m_ini --> 394M */ crg.bits.sys_asr0_axi_clk_sel = 2; /* 2: 349M */ g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); /* * ocram clk choice * 0: crystal oscillator 24M * 1: choice 394MHz occ clk */ crg.u32 = g_reg_asr_sysctl->u32; crg.bits.sys_ocram_work_cksel = 1; g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); crg.u32 = g_reg_asr_sysctl->u32; crg.bits.sys_ocram_cken = 1; g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); asr_select_clk(); crg.u32 = g_reg_asr_sysctl->u32; crg.bits.sys_dsp0_cken = 1; g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); } static td_void asr_clk_restore(td_void) { volatile sysctrl_asr_cfg_0 crg; crg.u32 = g_reg_asr_sysctl->u32; /* * DSP0 work clk * 00: crystal oscillator 24M * 01: clk_dsp0_occ --> 600M * 10: clk_12d288m_int --> 394M * 11: clk_49d1m_int --> 524M */ crg.bits.sys_asr0_clk_sel = 0; /* * DSP0 bus AXI clk * 00: crystal oscillator 24M * 01: aclk_asr_occ * 10: clk_49d1m_ini --> 349M * 11: clk_12d288m_ini --> 394M */ crg.bits.sys_asr0_axi_clk_sel = 0; g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); /* * ocram clk choice * 0: crystal oscillator 24M * 1: choice 394MHz occ clk */ crg.u32 = g_reg_asr_sysctl->u32; crg.bits.sys_ocram_work_cksel = 0; g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); crg.u32 = g_reg_asr_sysctl->u32; crg.bits.sys_ocram_cken = 1; g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); asr_select_clk(); crg.u32 = g_reg_asr_sysctl->u32; crg.bits.sys_dsp0_cken = 1; g_reg_asr_sysctl->u32 = crg.u32; osal_msleep(1); } static td_s32 dsp_start(td_void) { td_u32 ret; asr_sc_epll_crg_2_init(); asr_sc_crg_ctrl_init(); asr_disable(); asr_sysctrl_reset(); asr_start_addr_set(1); asr_clk_set(); asr_reset_elf_section(ASR_IMG_START_ADDR, ASR_IMG_SIZE); /* Clear .text area before cancel reset. */ g_reg_asr_secure->dsp_work_on.bits.dsp_work_on = 0x0; g_reg_sysctl_asr_int_sta->bits.sys_asr_idle_int = 0x0; g_reg_sysctl_asr_int_mask_sta->bits.sys_asr_idle_mask_int = 0x0; asr_sysctrl_unreset(); ret = asr_load_elf(); if (ret != TD_SUCCESS) { soc_err_print_call_fun_err(asr_load_elf, ret); return ret; } osal_msleep(1); ret = asr_flush_cache_area(ASR_IMG_START_ADDR, ASR_IMG_SIZE); if (ret != TD_SUCCESS) { soc_err_print_call_fun_err(asr_flush_cache_area, ret); return ret; } asr_sysctrl_reset(); asr_start_addr_set(0); asr_sysctrl_unreset(); asr_enable(); return TD_SUCCESS; } static td_s32 dsp_check_running(td_void) { td_u8 work_on; td_u32 wait_ms = 0; while (wait_ms++ < 30) { /* try 30 times */ work_on = g_reg_asr_secure->dsp_work_on.bits.dsp_work_on; if (work_on == 1) { g_reg_asr_secure->dsp_work_on.bits.dsp_work_on = 0x0; return TD_SUCCESS; } osal_msleep(100); /* wait 100 ms */ if (wait_ms % 50 == 0) { /* 50 for not printf frequently */ soc_log_err("wait %d\n", (wait_ms / 10)); /* 10 means every 1s */ } } return TD_FAILURE; } td_s32 dsp_check_idle(td_void) { td_u32 cnt = 0; while (cnt < 10) { /* try 10 times */ if (g_reg_sysctl_asr_int_sta->bits.sys_asr_idle_int == 1) { return TD_SUCCESS; } if (g_reg_sysctl_asr_int_mask_sta->bits.sys_asr_idle_mask_int == 1) { return TD_SUCCESS; } cnt++; osal_msleep(100); /* wait 100 ms */ if (cnt % 10 == 0) { /* wait 10 is cnt */ soc_log_err("dsp_check_idle times : %d\n", cnt); } } return TD_FAILURE; } static td_s32 dsp_wait_running(td_void) { td_s32 ret; ret = dsp_check_running(); if (ret != TD_SUCCESS) { soc_log_err("chk runnig fail\n"); return ret; } ret = dsp_check_idle(); if (ret != TD_SUCCESS) { soc_log_err("dsp_check_idle fail\n"); return ret; } return TD_SUCCESS; } static td_s32 asr_reset_toboot(td_void) { td_s32 ret; ret = dsp_start(); if (ret != TD_SUCCESS) { soc_log_err("dsp start failed(ret:0x%x)\n", ret); return ret; } return dsp_wait_running(); } td_void asr_resume_reset(td_void) { asr_sc_crg_ctrl_init(); asr_clk_set(); } td_void asr_restore_default_clk(td_void) { asr_sysctrl_reset(); asr_clk_restore(); } td_s32 asr_reset(td_void) { td_s32 ret; osal_sem_down_interruptible(&g_dsp_mutex); asr_reg_map(); osal_sem_up(&g_dsp_mutex); ret = asr_reset_toboot(); if (ret != TD_SUCCESS) { soc_log_err("asr_reset_toboot failed\n"); asr_reg_unmap(); return ret; } osal_msleep(100); /* delay 100ms */ return TD_SUCCESS; } #ifdef __cplusplus #if __cplusplus } #endif #endif /* __cplusplus */