sdk-hwV1.3/lichee/brandy-2.0/u-boot-2018/drivers/dsp/sun50iw11/dsp.c

944 lines
27 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* drivers/dsp/dsp.c
*
* Copyright (c) 2020 Allwinner.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program;
*/
#include <asm/arch-sunxi/cpu_ncat.h>
#include <asm/io.h>
#include <common.h>
#include "elf.h"
#include "fdt_support.h"
#include <sys_config.h>
#include <sunxi_image_verifier.h>
#include "dsp_i.h"
#ifdef CONFIG_SUNXI_IMAGE_HEADER
#include <sunxi_image_header.h>
#endif
#include <asm/arch-sunxi/efuse.h>
#define ROUND_DOWN(a, b) ((a) & ~((b)-1))
#define ROUND_UP(a, b) (((a) + (b)-1) & ~((b)-1))
#define ROUND_DOWN_CACHE(a) ROUND_DOWN(a, CONFIG_SYS_CACHELINE_SIZE)
#define ROUND_UP_CACHE(a) ROUND_UP(a, CONFIG_SYS_CACHELINE_SIZE)
#define DSP_AHBS_CLK_CONFIG 0x07010000
#define DSP_FREQ_CONFIG_REG DSP_AHBS_CLK_CONFIG
#define AHBS_CLK_SRC_OFFSET 24
#define AHBS_CLK_SRC_DCXO24M (0 << AHBS_CLK_SRC_OFFSET)
#define AHBS_CLK_SRC_RTC_32K (1 << AHBS_CLK_SRC_OFFSET)
#define AHBS_CLK_SRC_RC16M (2 << AHBS_CLK_SRC_OFFSET)
#define AHBS_CLK_SRC_PLL_PERI2X (3 << AHBS_CLK_SRC_OFFSET)
#define AHBS_CLK_SRC_PLL_AUDIO0_DIV2 (4 << AHBS_CLK_SRC_OFFSET)
#define AHBS_CLK_DIV_RATIO_N_MASK 0x300
#define AHBS_CLK_FACTOR_M_MASK 0x1f
#define AHBS_CLK_DIV_RATIO_OFFSET 8
#define AHBS_CLK_DIV_RATIO_N_1 (0 << AHBS_CLK_DIV_RATIO_OFFSET)
#define AHBS_CLK_DIV_RATIO_N_2 (1 << AHBS_CLK_DIV_RATIO_OFFSET)
#define AHBS_CLK_DIV_RATIO_N_4 (2 << AHBS_CLK_DIV_RATIO_OFFSET)
#define AHBS_CLK_DIV_RATIO_N_8 (3 << AHBS_CLK_DIV_RATIO_OFFSET)
/* x must be 1 - 32 */
#define AHBS_CLK_FACTOR_M(x) (((x) - 1) << 0)
/*
* dsp need to remap addresses for some addr.
*/
struct vaddr_range_t {
unsigned long vstart;
unsigned long vend;
unsigned long offset;
};
static struct vaddr_range_t addr_mapping[] = {
{ 0x18000000, 0x1fffffff, 0x8000000 },
{ 0x38000000, 0x3fffffff, 0x8000000 },
};
unsigned long set_img_va_to_pa(unsigned long vaddr,
struct vaddr_range_t *map,
int size)
{
unsigned long paddr = vaddr;
int i;
for (i = 0; i < size; i++) {
if (vaddr >= map[i].vstart
&& vaddr <= map[i].vend) {
paddr += map[i].offset;
break;
}
}
return paddr;
}
static int dts_get_dsp_memory(ulong *start, u32 *size, u32 id)
{
struct fdt_header *dtb_base = working_fdt;
int nodeoffset;
int ret;
u32 reg_data[8];
if (id == 0) {
nodeoffset = fdt_path_offset(dtb_base, "/reserved-memory/dsp0");
if (nodeoffset < 0) {
pr_err("%s: no /reserved-memory/dsp0 in fdt\n", __func__);
return -1;
}
} else {
nodeoffset = fdt_path_offset(dtb_base, "/reserved-memory/dsp1");
if (nodeoffset < 0) {
pr_err("%s: no /reserved-memory/dsp1 in fdt\n", __func__);
return -1;
}
}
memset(reg_data, 0, sizeof(reg_data));
ret = fdt_getprop_u32(dtb_base, nodeoffset, "reg", reg_data);
if (ret < 0) {
pr_err("%s: error fdt get reg\n", __func__);
return -2;
}
*start = reg_data[1];
*size = reg_data[3];
pr_msg("start = 0x%x size =0x%x\n", (u32)*start, *size);
return 0;
}
static void sunxi_dsp_set_runstall(u32 dsp_id, u32 value)
{
u32 reg_val;
if (dsp_id == 0) { /* DSP0 */
reg_val = readl(DSP0_CFG_BASE + DSP_CTRL_REG0);
reg_val &= ~(1 << BIT_RUN_STALL);
reg_val |= (value << BIT_RUN_STALL);
writel(reg_val, DSP0_CFG_BASE + DSP_CTRL_REG0);
} else { /* DSP1 */
reg_val = readl(DSP1_CFG_BASE + DSP_CTRL_REG0);
reg_val &= ~(1 << BIT_RUN_STALL);
reg_val |= (value << BIT_RUN_STALL);
writel(reg_val, DSP1_CFG_BASE + DSP_CTRL_REG0);
}
}
static int sun50iw11_dram_set(void *head_addr)
{
struct fdt_header *dtb_base = working_fdt;
int nodeoffset;
u32 *dst_addr = ((void *)head_addr + 0x30);
nodeoffset = fdt_path_offset(dtb_base, "/dram");
if (nodeoffset < 0) {
pr_err("dam error get parameter\n");
return -1;
}
fdt_getprop_u32(dtb_base, nodeoffset, "dram_clk", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_type", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_zq", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_odt_en", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_para1", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_para2", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_mr0", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_mr1", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_mr2", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_mr3", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr0", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr1", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr2", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr3", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr4", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr5", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr6", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr7", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr8", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr9", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr10", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr11", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr12", dst_addr++);
fdt_getprop_u32(dtb_base, nodeoffset, "dram_tpr13", dst_addr++);
return 0;
}
static int sun50iw11_codec_param_set(struct fdt_header *dtb_base, int nodeoffset,
struct codec_param *codec_param)
{
unsigned int temp_val = 0;
char *string_val = NULL;
user_gpio_set_t gpio_cfg;
fdt_getprop_u32(dtb_base, nodeoffset, "mic1gain", &temp_val);
codec_param->mic_gain[0] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "mic2gain", &temp_val);
codec_param->mic_gain[1] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "mic3gain", &temp_val);
codec_param->mic_gain[2] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "mic4gain", &temp_val);
codec_param->mic_gain[3] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "mic5gain", &temp_val);
codec_param->mic_gain[4] = temp_val;
if (sunxi_efuse_get_soc_ver() == SUN50IW11P1_VERSION_C) {
fdt_getprop_u32(dtb_base, nodeoffset, "mic6gain", &temp_val);
codec_param->mic_gain[5] = temp_val;
}
fdt_getprop_u32(dtb_base, nodeoffset, "adcdrc_cfg", &temp_val);
codec_param->adcdrc_cfg = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "adchpf_cfg", &temp_val);
codec_param->adchpf_cfg = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "dacdrc_cfg", &temp_val);
codec_param->dacdrc_cfg = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "dachpf_cfg", &temp_val);
codec_param->dachpf_cfg = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "mic_num", &temp_val);
codec_param->mic_num = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "pa_msleep_time", &temp_val);
codec_param->pa_msleep_time = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "capture_cma", &temp_val);
codec_param->capture_cma = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "playback_cma", &temp_val);
codec_param->playback_cma = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "spk_vol", &temp_val);
codec_param->spk_vol = temp_val;
if (fdt_get_one_gpio_by_offset(nodeoffset,
"gpio-boost-en", &gpio_cfg) >= 0) {
codec_param->pa_gpio[0] =
(gpio_cfg.port - 1) * 32 + gpio_cfg.port_num;
}
if (fdt_get_one_gpio_by_offset(nodeoffset, "gpio-spk", &gpio_cfg) >= 0) {
codec_param->pa_gpio[1] =
(gpio_cfg.port - 1) * 32 + gpio_cfg.port_num;
}
if (fdt_get_one_gpio_by_offset(nodeoffset,
"gpio-pa-power", &gpio_cfg) >= 0) {
codec_param->pa_gpio[2] =
(gpio_cfg.port - 1) * 32 + gpio_cfg.port_num;
}
fdt_getprop_u32(dtb_base, nodeoffset, "rx_sync_en", &temp_val);
codec_param->rx_sync_en = temp_val;
fdt_getprop_string(dtb_base, nodeoffset, "status", &string_val);
if (!strcmp("okay", string_val)) {
codec_param->used = 1;
} else {
codec_param->used = 0;
}
return 0;
}
static int daudio_param_set(struct fdt_header *dtb_base, int nodeoffset,
struct daudio_param *daudio_param)
{
unsigned int temp_val = 0;
char *string_val = NULL;
fdt_getprop_u32(dtb_base, nodeoffset, "pcm_lrck_period", &temp_val);
daudio_param->pcm_lrck_period = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "daudio_master", &temp_val);
daudio_param->daudio_master = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "audio_format", &temp_val);
daudio_param->audio_format = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "signal_inversion", &temp_val);
daudio_param->signal_inversion = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "slot_width_select", &temp_val);
daudio_param->slot_width_select = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "mclk_div", &temp_val);
daudio_param->mclk_div = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "sign_extend", &temp_val);
daudio_param->sign_extend = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "tx_data_mode", &temp_val);
daudio_param->tx_data_mode = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "rx_data_mode", &temp_val);
daudio_param->rx_data_mode = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "msb_lsb_first", &temp_val);
daudio_param->msb_lsb_first = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "tdm_config", &temp_val);
daudio_param->tdm_config = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "tdm_num", &temp_val);
daudio_param->tdm_num = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "frame_type", &temp_val);
daudio_param->frame_type = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "daudio_type", &temp_val);
daudio_param->daudio_type = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "tx_num", &temp_val);
daudio_param->tx_num = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "tx_chmap0", &temp_val);
daudio_param->tx_chmap[0] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "tx_chmap1", &temp_val);
daudio_param->tx_chmap[1] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "rx_num", &temp_val);
daudio_param->rx_num = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "rx_chmap0", &temp_val);
daudio_param->rx_chmap[0] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "rx_chmap1", &temp_val);
daudio_param->rx_chmap[1] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "rx_chmap2", &temp_val);
daudio_param->rx_chmap[2] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "rx_chmap3", &temp_val);
daudio_param->rx_chmap[3] = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "capture_cma", &temp_val);
daudio_param->capture_cma = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "playback_cma", &temp_val);
daudio_param->playback_cma = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "rx_sync_en", &temp_val);
daudio_param->rx_sync_en = temp_val;
fdt_getprop_string(dtb_base, nodeoffset, "status", &string_val);
if (!strcmp("okay", string_val)) {
daudio_param->used = 1;
} else {
daudio_param->used = 0;
temp_val = daudio_param->used;
fdt_getprop_u32(dtb_base, nodeoffset, "daudio_used", &temp_val);
daudio_param->used = temp_val;
}
return 0;
}
static int dmic_param_set(struct fdt_header *dtb_base, int nodeoffset,
struct dmic_param *dmic_param)
{
unsigned int temp_val = 0;
char *string_val = NULL;
fdt_getprop_u32(dtb_base, nodeoffset, "rx_chmap", &temp_val);
dmic_param->rx_chmap = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "data_vol", &temp_val);
dmic_param->data_vol = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "capture_cma", &temp_val);
dmic_param->capture_cma = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "rx_sync_en", &temp_val);
dmic_param->rx_sync_en = temp_val;
fdt_getprop_string(dtb_base, nodeoffset, "status", &string_val);
if (!strcmp("okay", string_val)) {
dmic_param->used = 1;
} else {
dmic_param->used = 0;
}
return 0;
}
static int mad_param_set(struct fdt_header *dtb_base, int nodeoffset,
struct mad_param *mad_param)
{
unsigned int temp_val = 0;
fdt_getprop_u32(dtb_base, nodeoffset, "lpsd_th", &temp_val);
mad_param->lpsd_th = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "lpsd_rrun", &temp_val);
mad_param->lpsd_rrun = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "lpsd_rstop", &temp_val);
mad_param->lpsd_rstop = temp_val;
fdt_getprop_u32(dtb_base, nodeoffset, "lpsd_ecnt", &temp_val);
mad_param->lpsd_ecnt = temp_val;
return 0;
}
static int sun50iw11_audio_set(void *head_addr)
{
struct fdt_header *dtb_base = working_fdt;
int nodeoffset;
struct audio_param *audio_param = ((void *)head_addr + 0x30 + sizeof(struct dram_para_32));
/* set for audiocodec */
nodeoffset = fdt_path_offset(dtb_base, "/soc/codec");
if (nodeoffset < 0) {
pr_err("r_codec error get parameter\n");
} else {
sun50iw11_codec_param_set(dtb_base, nodeoffset,
&audio_param->codec_param);
}
/* set for snddaudio0 */
nodeoffset = fdt_path_offset(dtb_base, "/soc/daudio@0x07033000");
if (nodeoffset < 0) {
pr_err("r_daudio0 error get parameter\n");
} else {
daudio_param_set(dtb_base, nodeoffset, &audio_param->daudio_param[0]);
}
/* set for snddaudio1 */
nodeoffset = fdt_path_offset(dtb_base, "/soc/daudio@0x07034000");
if (nodeoffset < 0) {
pr_err("r_daudio1 error get parameter\n");
} else {
daudio_param_set(dtb_base, nodeoffset, &audio_param->daudio_param[1]);
}
/* set for snddmic */
nodeoffset = fdt_path_offset(dtb_base, "/soc/dmic-controller");
if (nodeoffset < 0) {
pr_err("r_dmic error get parameter\n");
} else
dmic_param_set(dtb_base, nodeoffset, &audio_param->dmic_param);
/* set for mad */
nodeoffset = fdt_path_offset(dtb_base, "/soc/mad@0x07097000");
if (nodeoffset < 0) {
pr_err("mad error get parameter\n");
} else
mad_param_set(dtb_base, nodeoffset, &audio_param->mad_param);
return 0;
}
#define fdt_standby_phase(_name) do {\
err = fdt_getprop_u32(dtb_base, nodeoffset, _name, &temp_val); \
if (err < 0) { \
pr_err("phase dts node(%s) failed. ret: %d, val:0x%08x\n", \
_name, err, temp_val); \
} else { \
pr_info("phase %s: 0x%08x\n", _name, temp_val); \
} } while (0);
static int sun50iw11_standby_set(void *head_addr)
{
struct fdt_header *dtb_base = working_fdt;
int nodeoffset, err = -1;
struct standby_param *standby_param = ((void *)head_addr + 0x30 + \
sizeof(struct dram_para_32) + sizeof(struct audio_param));
unsigned int temp_val = 0;
/* find standby node */
nodeoffset = fdt_path_offset(dtb_base, "/soc/standby_param");
if (nodeoffset < 0) {
pr_err("standby_param error get parameter\n");
}
/* set power for standby */
fdt_standby_phase("vdd-cpu");
standby_param->power_param.vdd_cpu = temp_val;
fdt_standby_phase("vdd-sys");
standby_param->power_param.vdd_sys = temp_val;
fdt_standby_phase("vcc-pll");
standby_param->power_param.vcc_pll = temp_val;
/* set power for standby */
fdt_standby_phase("osc24m-on");
standby_param->clk_param.osc24m_on = !!temp_val;
fdt_standby_phase("pllcpu-off");
standby_param->clk_param.pllcpu_off = !!temp_val;
fdt_standby_phase("pllperiph0-off");
standby_param->clk_param.pllperiph0_off = !!temp_val;
fdt_standby_phase("pllaudio0-off");
standby_param->clk_param.pllaudio0_off = !!temp_val;
fdt_standby_phase("pllaudio1-off");
standby_param->clk_param.pllaudio1_off = !!temp_val;
fdt_standby_phase("ahb1ahb2-to-32k");
standby_param->clk_param.ahb1ahb2_to_32k = !!temp_val;
fdt_standby_phase("apb1-to-32k");
standby_param->clk_param.apb1_to_32k = !!temp_val;
fdt_standby_phase("apb2-to-32k");
standby_param->clk_param.apb2_to_32k = !!temp_val;
fdt_standby_phase("axi-to-32k");
standby_param->clk_param.axi_to_32k = !!temp_val;
fdt_standby_phase("apbs0-to-32k");
standby_param->clk_param.apbs0_to_32k = !!temp_val;
fdt_standby_phase("apbs1-to-32k");
standby_param->clk_param.apbs1_to_32k = !!temp_val;
/* set func for standby */
fdt_standby_phase("uart-off");
standby_param->func_param.uart_off = !!temp_val;
/* set standby */
fdt_standby_phase("nmi-wakeup");
standby_param->nmi_wakeup = !!temp_val;
fdt_standby_phase("sleep-freq");
standby_param->sleep_freq = temp_val;
standby_param->flag_isupdate = 0;
return 0;
}
static int sun50iw11_head_set(u32 *head_addr)
{
sun50iw11_dram_set(head_addr);
sun50iw11_audio_set(head_addr);
sun50iw11_standby_set(head_addr);
return 0;
}
static int sun50iw11_print_version(void *head_addr)
{
/* we promise 896 offset is verson string */
char *pstr = head_addr + 896;
/* max string is 100 */
pstr[100] = 0;
printf("DSP VERSION IS %s\n", pstr);
return 0;
}
static int load_image_old(u32 img_addr, u32 *run_addr)
{
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
Elf32_Phdr *phdr; /* Program header structure pointer */
int i;
ehdr = (Elf32_Ehdr *)img_addr;
phdr = (Elf32_Phdr *)(img_addr + ehdr->e_phoff);
/* Load each program header */
for (i = 0; i < ehdr->e_phnum; ++i) {
void *dst = (void *)(uintptr_t)phdr->p_paddr;
void *src = (void *)img_addr + phdr->p_offset;
debug("Loading phdr %i to 0x%p (%i bytes)\n",
i, dst, phdr->p_filesz);
if (phdr->p_filesz)
memcpy(dst, src, phdr->p_filesz);
if (phdr->p_filesz != phdr->p_memsz)
memset(dst + phdr->p_filesz, 0x00,
phdr->p_memsz - phdr->p_filesz);
#ifdef CONFIG_MACH_SUN50IW11
/* 50iw11 have header,we set it and simple check it */
if (phdr->p_memsz <= 0x400 && i == 0) {
sun50iw11_head_set(dst);
sun50iw11_print_version(dst);
}
#endif /* CONFIG_MACH_SUN50IW11 */
flush_cache(ROUND_DOWN_CACHE((unsigned long)dst),
ROUND_UP_CACHE(phdr->p_filesz));
++phdr;
}
if (!*run_addr)
*run_addr = ehdr->e_entry;
return 0;
}
static int load_image(u32 img_addr, u32 dsp_id)
{
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
Elf32_Phdr *phdr; /* Program header structure pointer */
int i;
void *dst = NULL;
void *src = NULL;
int size = sizeof(addr_mapping) / sizeof(struct vaddr_range_t);
ulong mem_start = 0;
u32 mem_size = 0;
ehdr = (Elf32_Ehdr *)img_addr;
phdr = (Elf32_Phdr *)(img_addr + ehdr->e_phoff);
/* Load each program header */
for (i = 0; i < ehdr->e_phnum; ++i) {
//remap addresses
dst = (void *)set_img_va_to_pa((uintptr_t)phdr->p_paddr, \
addr_mapping, \
size);
src = (void *)img_addr + phdr->p_offset;
debug("Loading phdr %i to 0x%p (%i bytes)\n",
i, dst, phdr->p_filesz);
if (phdr->p_filesz)
memcpy(dst, src, phdr->p_filesz);
if (phdr->p_filesz != phdr->p_memsz)
memset(dst + phdr->p_filesz, 0x00,
phdr->p_memsz - phdr->p_filesz);
/* 50iw11 have header,we set it and simple check it */
if (phdr->p_memsz <= 0x400 && i == 0) {
sun50iw11_head_set(dst);
sun50iw11_print_version(dst);
}
//flush_cache(ROUND_DOWN_CACHE((unsigned long)dst),
// ROUND_UP_CACHE(phdr->p_filesz));
++phdr;
}
dts_get_dsp_memory(&mem_start, &mem_size, dsp_id);
if (!mem_start || !mem_size) {
pr_err("dts_get_dsp_memory fail\n");
} else {
flush_cache(ROUND_DOWN_CACHE(mem_start),
ROUND_UP_CACHE(mem_size));
}
return 0;
}
static int get_image_len(u32 img_addr)
{
Elf32_Ehdr *ehdr = NULL; /* Elf header structure pointer */
Elf32_Phdr *phdr = NULL; /* Program header structure pointer */
Elf32_Shdr *shdr = NULL;
ehdr = (Elf32_Ehdr *)img_addr;
phdr = (Elf32_Phdr *)(img_addr + ehdr->e_phoff);
shdr = (Elf32_Shdr *)(img_addr + ehdr->e_shoff
+ (ehdr->e_shstrndx * sizeof(Elf32_Shdr)));
int i = 0;
unsigned long change_addr = 0;
unsigned char *strtab = NULL;
if (shdr->sh_type == SHT_STRTAB)
strtab = (unsigned char *)(img_addr + shdr->sh_offset);
for (i = 0; i < ehdr->e_shnum; ++i) {
shdr = (Elf32_Shdr *)(img_addr + ehdr->e_shoff
+ (i * sizeof(Elf32_Shdr)));
if (!(shdr->sh_flags & SHF_ALLOC)
|| (shdr->sh_addr == 0)
|| (shdr->sh_size == 0)) {
continue;
}
if (strtab) {
char *pstr = (char *)(&strtab[shdr->sh_name]);
if (strcmp(pstr, ".oemhead.text") == 0) {
printf("find dsp section: %s ,addr = 0x%lx \n",
pstr, (unsigned long)shdr->sh_addr);
change_addr = shdr->sh_addr;
break;
}
}
}
int img_len = 0;
struct spare_rtos_head_t *prtos = NULL;
for (i = 0; i < ehdr->e_phnum; ++i) {
if (change_addr == (unsigned long)phdr->p_paddr) {
prtos = (struct spare_rtos_head_t *)(img_addr
+ phdr->p_offset);
img_len = prtos->rtos_img_hdr.image_size;
break;
}
++phdr;
}
return img_len;
}
static void dsp_freq_default_set(void)
{
u32 reg = DSP_FREQ_CONFIG_REG;
u32 val = 0;
val = AHBS_CLK_SRC_PLL_PERI2X | AHBS_CLK_DIV_RATIO_N_1 |
AHBS_CLK_FACTOR_M(3);
writel(val, reg);
}
static void sram_remap_set(int value)
{
u32 val = 0;
val = readl(SUNXI_PRCM_BASE + PRCM_DSP0_LOCALRAM_REMAP_REG);
val &= ~(1 << BIT_DSP0_LOCALRAM);
val |= (value << BIT_DSP0_LOCALRAM);
writel(val, SUNXI_PRCM_BASE + PRCM_DSP0_LOCALRAM_REMAP_REG);
}
static void ahbs_clk_set(void)
{
u32 reg = DSP_FREQ_CONFIG_REG;
u32 val = 0;
val = AHBS_CLK_SRC_PLL_PERI2X | AHBS_CLK_DIV_RATIO_N_1 |
AHBS_CLK_FACTOR_M(6);
writel(val, reg);
}
static int update_reset_vec(u32 img_addr, u32 *run_addr)
{
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
ehdr = (Elf32_Ehdr *)img_addr;
if (!*run_addr)
*run_addr = ehdr->e_entry;
return 0;
}
int sunxi_dsp_init_old(u32 img_addr, u32 run_ddr, u32 dsp_id, u32 image_len)
{
u32 reg_val;
load_image_old(img_addr, &run_ddr);
#ifdef CONFIG_MACH_SUN50IW11
dsp_freq_default_set();
#endif /* CONFIG_MACH_SUN50IW11 */
printf("DSP%d booting from 0x%x...\n", dsp_id, run_ddr);
if (dsp_id == 0) { /* DSP0 */
/* clock gating */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP0_GATING);
reg_val |= (1 << BIT_DSP0_CFG_GATING);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* reset */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP0_CFG_RST);
reg_val |= (1 << BIT_DSP0_DBG_RST);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* set external Reset Vector if needed */
if (run_ddr != DSP_DEFAULT_RST_VEC) {
writel(run_ddr, DSP0_CFG_BASE + DSP_ALT_RESET_VEC_REG);
reg_val = readl(DSP0_CFG_BASE + DSP_CTRL_REG0);
reg_val |= (1 << BIT_START_VEC_SEL);
writel(reg_val, DSP0_CFG_BASE + DSP_CTRL_REG0);
}
/* set runstall */
sunxi_dsp_set_runstall(dsp_id, 1);
/* de-assert dsp0 */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP0_RST);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* clear runstall */
sunxi_dsp_set_runstall(dsp_id, 0);
} else { /* DSP1 */
/* clock gating */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP1_GATING);
reg_val |= (1 << BIT_DSP1_CFG_GATING);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* reset */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP1_CFG_RST);
reg_val |= (1 << BIT_DSP1_DBG_RST);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* set external Reset Vector if needed */
if (run_ddr != DSP_DEFAULT_RST_VEC) {
writel(run_ddr, DSP1_CFG_BASE + DSP_ALT_RESET_VEC_REG);
reg_val = readl(DSP1_CFG_BASE + DSP_CTRL_REG0);
reg_val |= (1 << BIT_START_VEC_SEL);
writel(reg_val, DSP1_CFG_BASE + DSP_CTRL_REG0);
}
/* set runstall */
sunxi_dsp_set_runstall(dsp_id, 1);
/* de-assert dsp1 */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP1_RST);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* clear runstall */
sunxi_dsp_set_runstall(dsp_id, 0);
}
return 0;
}
int sunxi_dsp_init(u32 img_addr, u32 run_ddr, u32 dsp_id)
{
u32 reg_val = 0;
int image_len = 0;
#ifdef CONFIG_SUNXI_IMAGE_HEADER
sunxi_image_header_t *ih = (sunxi_image_header_t *)img_addr;
image_len = get_image_len((u32)((uint8_t *)ih + ih->ih_hsize));
#else
image_len = get_image_len(img_addr);
#endif
#if defined(CONFIG_SUNXI_VERIFY_DSP) && defined(CONFIG_SUNXI_IMAGE_VERIFIER)
// for IMAGE_HEADER, copy payload to img_addr
if (sunxi_verify_dsp(img_addr, image_len, dsp_id) < 0) {
return -1;
}
#else
pr_msg("no verify dsp\n");
#endif
if (sunxi_efuse_get_soc_ver() != SUN50IW11P1_VERSION_C) {
if (sunxi_dsp_init_old(img_addr, run_ddr, dsp_id, image_len) < 0) {
return -1;
}
return 0;
}
/* update run addr */
update_reset_vec(img_addr, &run_ddr);
/* set ahbs clk */
ahbs_clk_set();
if (dsp_id == 0) { /* DSP0 */
/* set uboot use local ram */
sram_remap_set(1);
/* set dsp clk value 600M */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP0_CLK_CFG_REG);
reg_val |= (1 << BIT_DSP0_SCLK_GATING);
reg_val |= (3 << BIT_DSP0_CLK_SRC_SEL);
reg_val |= (0 << BIT_DSP0_CLK_DIV_RATIO_N);
reg_val |= (1 << BIT_DSP0_FACTOR_M);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP0_CLK_CFG_REG);
/* set dsp clk value 516M
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP0_CLK_CFG_REG);
reg_val |= (1 << BIT_DSP0_SCLK_GATING);
reg_val |= (4 << BIT_DSP0_CLK_SRC_SEL);
reg_val |= (0 << BIT_DSP0_CLK_DIV_RATIO_N);
reg_val |= (0 << BIT_DSP0_FACTOR_M);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP0_CLK_CFG_REG);
*/
/* clock gating */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP0_GATING);
reg_val |= (1 << BIT_DSP0_CFG_GATING);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* reset */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP0_CFG_RST);
reg_val |= (1 << BIT_DSP0_DBG_RST);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* load image*/
load_image(img_addr, dsp_id);
/* set dsp vector*/
writel(run_ddr, DSP0_CFG_BASE + DSP_ALT_RESET_VEC_REG);
reg_val = readl(DSP0_CFG_BASE + DSP_CTRL_REG0);
reg_val |= (1 << BIT_START_VEC_SEL);
writel(reg_val, DSP0_CFG_BASE + DSP_CTRL_REG0);
/* set runstall */
sunxi_dsp_set_runstall(dsp_id, 1);
/* set dsp use local ram */
sram_remap_set(0);
/* de-assert dsp */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP0_RST);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* clear runstall */
sunxi_dsp_set_runstall(dsp_id, 0);
} else { /* DSP1 */
/* set dsp clk value 400M*/
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP1_CLK_CFG_REG);
reg_val |= (1 << BIT_DSP1_SCLK_GATING);
reg_val |= (3 << BIT_DSP1_CLK_SRC_SEL);
reg_val |= (0 << BIT_DSP1_CLK_DIV_RATIO_N);
reg_val |= (2 << BIT_DSP1_FACTOR_M);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP1_CLK_CFG_REG);
/* clock gating */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP1_GATING);
reg_val |= (1 << BIT_DSP1_CFG_GATING);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* reset */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP1_CFG_RST);
reg_val |= (1 << BIT_DSP1_DBG_RST);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* load image*/
load_image(img_addr, dsp_id);
/* set dsp vector*/
writel(run_ddr, DSP1_CFG_BASE + DSP_ALT_RESET_VEC_REG);
reg_val = readl(DSP1_CFG_BASE + DSP_CTRL_REG0);
reg_val |= (1 << BIT_START_VEC_SEL);
writel(reg_val, DSP1_CFG_BASE + DSP_CTRL_REG0);
/* set runstall */
sunxi_dsp_set_runstall(dsp_id, 1);
/* de-assert dsp */
reg_val = readl(SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
reg_val |= (1 << BIT_DSP1_RST);
writel(reg_val, SUNXI_PRCM_BASE + PRCM_DSP_BGR_REG);
/* clear runstall */
sunxi_dsp_set_runstall(dsp_id, 0);
}
printf("DSP%d start ok, img length %d, booting from 0x%x\n",
dsp_id, image_len, run_ddr);
return 0;
}