SmartAudio/lichee/brandy/u-boot-2014.07/drivers/arisc/arisc.c

298 lines
10 KiB
C
Executable File

/*
* drivers/arisc/arisc.c
*
* Copyright (c) 2012 Allwinner.
* 2012-05-01 Written by sunny (sunny@allwinnertech.com).
* 2012-10-01 Written by superm (superm@allwinnertech.com).
*
* 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; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "arisc_i.h"
#include <smc.h>
#include <sunxi_board.h>
//if all value are zero , compile will not allocate space to this global variable.
//use them before uboot Relocation will cause error.
struct dts_cfg dts_cfg =
{
.dram_para = {0x1,0x1}
};
struct dts_cfg_64 dts_cfg_64 =
{
.dram_para = {0x1,0x1}
};
unsigned int arisc_debug_level = 2;
#if 0 //boot load scp,so not need this param
static int get_image_addr_size(phys_addr_t *image_addr, size_t *image_size)
{
struct spare_boot_head_t *header;
phys_addr_t offset;
size_t size;
/* Obtain a reference to the image by querying the platform layer */
header = &uboot_spare_head;
offset = header->boot_ext[2].data[0];
size = header->boot_ext[2].data[1];
if(0 == offset || 0 == size)
{
printf("arisc image file not found\n");
return -1;
}
*image_addr = (ptrdiff_t)CONFIG_SYS_TEXT_BASE + offset;
*image_size = size;
return 0;
}
#endif
int arisc_dvfs_cfg_vf_table(void)
{
u32 value = 0;
int index = 0;
int nodeoffset;
u32 vf_table_size = 0;
char vf_table_main_key[64];
char vf_table_sub_key[64];
u32 vf_table_count = 0;
u32 vf_table_type = 0;
//nodeoffset = fdt_node_offset_by_compatible(working_fdt, -1, "allwinner,dvfs_table");
nodeoffset = fdt_path_offset(working_fdt, "/dvfs_table");
if (IS_ERR_VALUE(nodeoffset)) {
ARISC_ERR("get [allwinner,dvfs_table] device node error\n");
return -EINVAL;
}
if (IS_ERR_VALUE(fdt_getprop_u32(working_fdt, nodeoffset, "vf_table_count", &vf_table_count))) {
ARISC_LOG("%s: support only one vf_table\n", __func__);
sprintf(vf_table_main_key, "%s", "/dvfs_table");
} else {
//vf_table_type = sunxi_get_soc_bin();
sprintf(vf_table_main_key, "%s%d", "/vf_table", vf_table_type);
}
ARISC_INF("%s: vf table type [%d=%s]\n", __func__, vf_table_type, vf_table_main_key);
//nodeoffset = fdt_node_offset_by_compatible(working_fdt, -1, vf_table_main_key);
nodeoffset = fdt_path_offset(working_fdt, vf_table_main_key);
if (IS_ERR_VALUE(nodeoffset)) {
ARISC_ERR("get [%s] device node error\n", vf_table_main_key);
return -EINVAL;
}
/* parse system config v-f table information */
if (IS_ERR_VALUE(fdt_getprop_u32(working_fdt, nodeoffset, "lv_count", &vf_table_size))) {
ARISC_ERR("parse system config dvfs_table size fail\n");
return -EINVAL;
}
for (index = 0; index < vf_table_size; index++) {
sprintf(vf_table_sub_key, "lv%d_freq", index + 1);
if (!IS_ERR_VALUE(fdt_getprop_u32(working_fdt, nodeoffset, vf_table_sub_key, &value))) {
dts_cfg.vf[index].freq = value;
}
ARISC_INF("%s: freq [%s-%d=%d]\n", __func__, vf_table_sub_key, index, value);
sprintf(vf_table_sub_key, "lv%d_volt", index + 1);
if (!IS_ERR_VALUE(fdt_getprop_u32(working_fdt, nodeoffset, vf_table_sub_key, &value))) {
dts_cfg.vf[index].voltage = value;
}
ARISC_INF("%s: volt [%s-%d=%d]\n", __func__, vf_table_sub_key, index, value);
}
return 0;
}
static int sunxi_arisc_parse_cfg(void)
{
u32 value[4] = {0, 0, 0, 0};
int nodeoffset;
/* parse arisc_space node */
nodeoffset = fdt_path_offset(working_fdt, "/soc/arisc_space");
if (IS_ERR_VALUE(nodeoffset)) {
ARISC_ERR("get [allwinner,arisc_space] device node error\n");
return -EINVAL;
}
if (IS_ERR_VALUE(fdt_getprop_u32(working_fdt, nodeoffset, "space4", value))) {
ARISC_ERR("get arisc_space space4 error\n");
return -EINVAL;
}
dts_cfg.space.msgpool_dst = (phys_addr_t)value[0];
dts_cfg.space.msgpool_offset = (phys_addr_t)value[1];
dts_cfg.space.msgpool_size = (size_t)value[2];
ARISC_INF("arisc_space space4 msgpool_dst:0x%p, msgpool_offset:0x%p, msgpool_size:0x%zx, \n",
(void *)dts_cfg.space.msgpool_dst, (void *)dts_cfg.space.msgpool_offset, dts_cfg.space.msgpool_size);
/* parse msgbox node */
nodeoffset = fdt_path_offset(working_fdt, "/soc/msgbox");
if (IS_ERR_VALUE(nodeoffset)) {
ARISC_ERR("get [allwinner,msgbox] device node error\n");
return -EINVAL;
}
if (IS_ERR_VALUE(fdt_getprop_u32(working_fdt, nodeoffset, "reg", value))) {
ARISC_ERR("get msgbox reg error\n");
return -EINVAL;
}
dts_cfg.msgbox.base = (phys_addr_t)value[1];
dts_cfg.msgbox.size = (size_t)value[3];
dts_cfg.msgbox.status = fdtdec_get_is_enabled(working_fdt, nodeoffset);
ARISC_INF("msgbox base:0x%p, size:0x%zx, status:%u\n",
(void *)dts_cfg.msgbox.base, dts_cfg.msgbox.size, dts_cfg.msgbox.status);
/* parse hwspinlock node */
//nodeoffset = fdt_node_offset_by_compatible(working_fdt, -1, "allwinner,sunxi-hwspinlock");
nodeoffset = fdt_path_offset(working_fdt, "/soc/hwspinlock");
if (IS_ERR_VALUE(nodeoffset)) {
ARISC_ERR("get [allwinner,sunxi-hwspinlock] device node error\n");
return -EINVAL;
}
if (IS_ERR_VALUE(fdt_getprop_u32(working_fdt, nodeoffset, "reg", value))) {
ARISC_ERR("get sunxi-hwspinlock reg error\n");
return -EINVAL;
}
dts_cfg.hwspinlock.base = (phys_addr_t)value[1];
dts_cfg.hwspinlock.size = (size_t)value[3];
dts_cfg.hwspinlock.status = fdtdec_get_is_enabled(working_fdt, nodeoffset);
ARISC_INF("hwspinlock base:0x%p, size:0x%zx, status:%u\n",
(void *)dts_cfg.hwspinlock.base, dts_cfg.hwspinlock.size, dts_cfg.hwspinlock.status);
return 0;
}
static void sunxi_arisc_transform_cfg(void)
{
memcpy((void *)&dts_cfg_64.dram_para, (const void *)&dts_cfg.dram_para, sizeof(struct dram_para));
memcpy((void *)&dts_cfg_64.vf, (const void *)&dts_cfg.vf, sizeof(struct arisc_freq_voltage) * ARISC_DVFS_VF_TABLE_MAX);
dts_cfg_64.space.sram_dst = (u64)dts_cfg.space.sram_dst;
dts_cfg_64.space.sram_offset = (u64)dts_cfg.space.sram_offset;
dts_cfg_64.space.sram_size = (u64)dts_cfg.space.sram_size;
dts_cfg_64.space.dram_dst = (u64)dts_cfg.space.dram_dst;
dts_cfg_64.space.dram_offset = (u64)dts_cfg.space.dram_offset;
dts_cfg_64.space.dram_size = (u64)dts_cfg.space.dram_size;
dts_cfg_64.space.para_dst = (u64)dts_cfg.space.para_dst;
dts_cfg_64.space.para_offset = (u64)dts_cfg.space.para_offset;
dts_cfg_64.space.para_size = (u64)dts_cfg.space.para_size;
dts_cfg_64.space.msgpool_dst = (u64)dts_cfg.space.msgpool_dst;
dts_cfg_64.space.msgpool_offset = (u64)dts_cfg.space.msgpool_offset;
dts_cfg_64.space.msgpool_size = (u64)dts_cfg.space.msgpool_size;
dts_cfg_64.space.standby_dst = (u64)dts_cfg.space.standby_dst;
dts_cfg_64.space.standby_offset = (u64)dts_cfg.space.standby_offset;
dts_cfg_64.space.standby_size = (u64)dts_cfg.space.standby_size;
dts_cfg_64.image.base = (u64)dts_cfg.image.base;
dts_cfg_64.image.size = (u64)dts_cfg.image.size;
dts_cfg_64.prcm.base = (u64)dts_cfg.prcm.base;
dts_cfg_64.prcm.size = (u64)dts_cfg.prcm.size;
dts_cfg_64.cpuscfg.base = (u64)dts_cfg.cpuscfg.base;
dts_cfg_64.cpuscfg.size = (u64)dts_cfg.cpuscfg.size;
dts_cfg_64.msgbox.base = (u64)dts_cfg.msgbox.base;
dts_cfg_64.msgbox.size = (u64)dts_cfg.msgbox.size;
dts_cfg_64.msgbox.irq = dts_cfg.msgbox.irq;
dts_cfg_64.msgbox.status = dts_cfg.msgbox.status;
dts_cfg_64.hwspinlock.base = (u64)dts_cfg.hwspinlock.base;
dts_cfg_64.hwspinlock.size = (u64)dts_cfg.hwspinlock.size;
dts_cfg_64.hwspinlock.irq = dts_cfg.hwspinlock.irq;
dts_cfg_64.hwspinlock.status = dts_cfg.hwspinlock.status;
dts_cfg_64.s_uart.base = (u64)dts_cfg.s_uart.base;
dts_cfg_64.s_uart.size = (u64)dts_cfg.s_uart.size;
dts_cfg_64.s_uart.irq = dts_cfg.s_uart.irq;
dts_cfg_64.s_uart.status = dts_cfg.s_uart.status;
#if defined CONFIG_ARCH_SUN50IW2P1
dts_cfg_64.s_twi.base = (u64)dts_cfg.s_twi.base;
dts_cfg_64.s_twi.size = (u64)dts_cfg.s_twi.size;
dts_cfg_64.s_twi.irq = dts_cfg.s_twi.irq;
dts_cfg_64.s_twi.status = dts_cfg.s_twi.status;
#else
dts_cfg_64.s_rsb.base = (u64)dts_cfg.s_rsb.base;
dts_cfg_64.s_rsb.size = (u64)dts_cfg.s_rsb.size;
dts_cfg_64.s_rsb.irq = dts_cfg.s_rsb.irq;
dts_cfg_64.s_rsb.status = dts_cfg.s_rsb.status;
#endif
dts_cfg_64.s_jtag.base = (u64)dts_cfg.s_jtag.base;
dts_cfg_64.s_jtag.size = (u64)dts_cfg.s_jtag.size;
dts_cfg_64.s_jtag.irq = dts_cfg.s_jtag.irq;
dts_cfg_64.s_jtag.status = dts_cfg.s_jtag.status;
dts_cfg_64.s_cir.base = (u64)dts_cfg.s_cir.base;
dts_cfg_64.s_cir.size = (u64)dts_cfg.s_cir.size;
dts_cfg_64.s_cir.irq = dts_cfg.s_cir.irq;
dts_cfg_64.s_cir.status = dts_cfg.s_cir.status;
memcpy(&dts_cfg_64.s_cir.ir_key, &dts_cfg.s_cir.ir_key, sizeof(ir_key_t));
memcpy(&dts_cfg_64.start_os, &dts_cfg.start_os, sizeof(box_start_os_cfg_t));
memcpy((void *)&dts_cfg_64.pmu, (const void *)&dts_cfg.pmu, sizeof(struct pmu_cfg));
memcpy((void *)&dts_cfg_64.power, (const void *)&dts_cfg.power, sizeof(struct power_cfg));
flush_dcache_range((unsigned long)&dts_cfg_64, ((unsigned long)&dts_cfg_64 + sizeof(struct dts_cfg_64)));
}
int sunxi_arisc_probe(void)
{
if(!sunxi_probe_secure_monitor())
{
return 0;
}
ARISC_LOG("arisc initialize\n");
sunxi_arisc_parse_cfg();
/* because the uboot is arch32,
* and the runtime server is arch64,
* should transform the dts_cfg to dts_cfg_64.
*/
sunxi_arisc_transform_cfg();
ARISC_LOG("arisc para ok\n");
if (arm_svc_arisc_startup((ulong)&dts_cfg_64)) {
/* arisc initialize failed */
ARISC_ERR("sunxi-arisc driver startup failed\n");
} else {
/* arisc initialize succeeded */
ARISC_LOG("sunxi-arisc driver startup succeeded\n");
}
return 0;
}
int sunxi_arisc_wait_ready(void)
{
if(get_boot_work_mode()!= WORK_MODE_BOOT)
return 0;
arm_svc_arisc_wait_ready();
return 0;
}
int sunxi_arisc_fake_poweroff(void)
{
arm_svc_arisc_fake_poweroff();
return 0;
}