sdk-hwV1.3/lichee/brandy-2.0/spl/board/sun8iw21p1/board.c

407 lines
8.9 KiB
C
Raw Normal View History

2024-05-07 10:09:20 +00:00
/*
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* Some init for sunxi platform.
*
*/
#include <common.h>
#include <libfdt.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <linux/compiler.h>
#include <linux/errno.h>
#include <private_boot0.h>
#include <arch/clock.h>
#include <arch/uart.h>
#include <arch/rtc.h>
#include <arch/gpio.h>
#ifdef CFG_BOOT0_LOAD_KERNEL
void rtc_set_vccio_det_spare(void)
{
u32 val = 0;
val = readl(SUNXI_RTC_BASE + 0x1f4);
val &= ~(0xff << 4);
val |= (VCCIO_THRESHOLD_VOLTAGE_2_9 | FORCE_DETECTER_OUTPUT);
val &= ~VCCIO_DET_BYPASS_EN;
writel(val, SUNXI_RTC_BASE + 0x1f4);
}
#endif
#ifndef FPGA_PLATFORM
int sunxi_board_init(void)
{
/*
* When waking up, don't change the voltage, frequency,
* cpu is easy to hang. We restore it in atf.
*/
/* set_pll_voltage(CONFIG_SUNXI_CORE_VOL); */
#ifdef CFG_SUNXI_POWER
if ((axp_init(0) >= 0) && (!super_standby_mode())) {
int sys_vol = (get_power_mode() & 0x3f);
int grain_size = get_power_mode() >> 6;
if (sys_vol != 0) {
switch (grain_size) {
case 0:
sys_vol = 500 + sys_vol * 10;
break;
case 1:
sys_vol = 500 + sys_vol * 20;
break;
case 2:
sys_vol = 500 + sys_vol * 50;
break;
case 3:
sys_vol = 500 + sys_vol * 100;
break;
}
if (sys_vol < 900)
sys_vol = 900;
else if (sys_vol > 1200)
sys_vol = 1200;
set_sys_voltage(sys_vol);
}
}
#endif
sunxi_board_pll_init();
printf("board init ok\n");
return 0;
}
#else
int sunxi_board_init(void)
{
printf("FPGA board init ok\n");
return 0;
}
#endif
int sunxi_assert_arisc(void)
{
printf("set arisc reset to assert state\n");
{
volatile unsigned long value;
value = readl(SUNXI_RCPUCFG_BASE + 0x0);
value &= ~1;
writel(value, SUNXI_RCPUCFG_BASE + 0x0);
}
return 0;
}
int sunxi_deassert_arisc(void)
{
printf("set arisc reset to de-assert state\n");
{
volatile unsigned long value;
value = readl(SUNXI_RCPUCFG_BASE + 0x0);
value |= 1;
writel(value, SUNXI_RCPUCFG_BASE + 0x0);
}
return 0;
}
uint8_t audioldo_check(void)
{
uint reg_val = 0;
uint roughtrim_val = 0, finetrim_val = 0;
/* reset */
reg_val = readl(CCMU_AUDIO_CODEC_BGR_REG);
reg_val &= ~(1 << 16);
writel(reg_val, CCMU_AUDIO_CODEC_BGR_REG);
udelay(2);
reg_val |= (1 << 16);
writel(reg_val, CCMU_AUDIO_CODEC_BGR_REG);
/* enable AUDIO gating */
reg_val = readl(CCMU_AUDIO_CODEC_BGR_REG);
reg_val |= (1 << 0);
writel(reg_val, CCMU_AUDIO_CODEC_BGR_REG);
/* enable pcrm CTRL */
reg_val = readl(ANA_PWR_RST_REG);
reg_val &= ~(1 << 0);
writel(reg_val, ANA_PWR_RST_REG);
/* read efuse */
printf("audio:avcc calibration\n");
reg_val = readl(SUNXI_SID_SRAM_BASE + 0x28);
roughtrim_val = (reg_val >> 0)& 0xF;
reg_val = readl(SUNXI_SID_SRAM_BASE + 0x24);
finetrim_val = (reg_val >> 16)& 0xFF;
if (roughtrim_val == 0 && finetrim_val == 0) {
reg_val = readl(SYNXI_VER_REG);
reg_val = (reg_val >> 0)& 0x7;
if (reg_val) {
/* printf("audio:chip not version A\n"); */
return 0;
} else {
roughtrim_val = 0x5;
finetrim_val = 0x19;
/* printf("audio:chip version A\n"); */
}
}
reg_val = readl(AUDIO_POWER_REG);
reg_val &= ~(0xF<<8|0xFF);
reg_val |= roughtrim_val<<8|finetrim_val;
writel(reg_val, AUDIO_POWER_REG);
return 0;
}
uint8_t sunxi_board_late_init(void)
{
#ifdef CFG_BOOT0_LOAD_KERNEL
rtc_set_vccio_det_spare();
#endif
audioldo_check();
return 0;
}
int sunxi_board_exit(void)
{
return 0;
}
#ifdef CFG_UPDATA_IRQ_TAB
#ifdef CFG_UPDATA_IRQ_TAB_DEBUG
#define irq_debug printf
#else
#define irq_debug(...) do { } while (0)
#endif
/*
* Table Format
* The first 16 byte is reserved for _table_head
* The last CPU_BANK_NUM word is resource for gpio mask
*
* ----------------
* | _table_head |
* ----------------
* | _table_entry |
* ----------------
* | ... |
* ----------------
* | GPIOA Mask |
* ----------------
* | ... |
* ---------------
*/
struct _table_head {
uint32_t tag;
uint32_t len;
uint32_t banks_off;
uint32_t banks_num;
} __packed;
struct _table_entry {
uint32_t status;
uint32_t type;
uint32_t flags;
uint32_t arch_irq;
} __packed;
#define CPU_BANK_NUM (16)
#define READY_TAG (('R' << 24) | ('E' << 16) | ('D' << 8) | 'Y')
static int update_irq_head_info(const void *fdt, int nodeoffset,
void *buf, int buf_len)
{
int len, i;
struct _table_head *head;
const uint32_t *data;
uint32_t *gpio_tab;
head = buf;
data = fdt_getprop(fdt, nodeoffset, "share-irq", &len);
if (len < 0) {
irq_debug("updata %s share-irq table failed,"
"not find share-irq property.\n",
fdt_get_name(fdt, nodeoffset, NULL));
return -ENODEV;
}
if ((len % (3 * 4)) != 0) {
irq_debug("%s share-irq table invalid format.\n",
fdt_get_name(fdt, nodeoffset, NULL));
return -ENODEV;
}
irq_debug("share_irq_table len=%d\n", len);
/*
* current we only support update gpio bank info.
* GPIO Table Format: major type flags
* type = 0 -> nomal interrupt
* type = 1 -> GPIOA
* type = 2 -> GPIOB
* ...
*
* flags = bank_mask
*/
gpio_tab = buf + buf_len - CPU_BANK_NUM * sizeof(uint32_t);
memset(gpio_tab, 0, CPU_BANK_NUM * sizeof(uint32_t));
for (i = 0; i < (len / 4); i += 3) {
uint32_t type = fdt32_to_cpu(data[i + 1]);
if (type == 0 || type >= CPU_BANK_NUM)
continue;
irq_debug("\t: GPIO%c = 0x%x\n", 'A' + type - 1, fdt32_to_cpu(data[i + 2]));
gpio_tab[type - 1] = fdt32_to_cpu(data[i + 2]);
}
head->tag = READY_TAG;
head->len = buf_len;
head->banks_off = (uint32_t)((void *)gpio_tab - buf);
head->banks_num = CPU_BANK_NUM;
return 0;
}
static int sunxi_find_irq_tab_rserved(const void *fdt,
int nodeoffset, uint32_t *addr, uint32_t *size)
{
int len;
const void *data;
data = fdt_getprop(fdt, nodeoffset, "memory-region", &len);
if (len < 0) {
irq_debug("find %s memory-region property failed",
fdt_get_name(fdt, nodeoffset, NULL));
return -ENODEV;
}
nodeoffset = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*(uint32_t *)data));
if (nodeoffset < 0) {
irq_debug("parse %s memory-region property failed, ret=%s",
fdt_get_name(fdt, nodeoffset, NULL),
fdt_strerror(nodeoffset));
return -ENODEV;
}
data = fdt_getprop(fdt, nodeoffset, "reg", &len);
if (len < 0) {
irq_debug("find %s memory-region property failed",
fdt_get_name(fdt, nodeoffset, NULL));
return -ENODEV;
}
if ((len % 4) != 0) {
irq_debug("%s reg invalid format, len=%d.\n", fdt_get_name(fdt, nodeoffset, NULL), len);
return -ENODEV;
}
/* ignore high word */
*addr = fdt32_to_cpu(*((uint32_t *)data + 1));
*size = fdt32_to_cpu(*((uint32_t *)data + 3));
irq_debug("irq table addr:0x%x len:0x%x\n", *addr, *size);
return 0;
}
void update_riscv_irq_tab(unsigned long elf_base, unsigned long fdt_addr, int idx)
{
int i, ret, nodeoffset, node;
const void *fdt = (void *)fdt_addr;
uint32_t *tab_info;
uint32_t addr, len;
ret = fdt_check_header(fdt);
if (ret < 0) {
printf("FDT: %s.\n", fdt_strerror(ret));
return;
}
node = fdt_path_offset(fdt, "/reserved-irq");
if (node < 0) {
printf("FDT: /reserved-irq node not found.\n");
return;
}
i = 0;
for (nodeoffset = fdt_first_subnode(fdt, node);
nodeoffset > 0;
nodeoffset = fdt_next_subnode(fdt, nodeoffset)) {
if (i++ != idx)
continue;
ret = sunxi_find_irq_tab_rserved(fdt, nodeoffset, &addr, &len);
if (ret)
break;
tab_info = elf_find_segment_offset(elf_base, ".share_irq_table");
if (!tab_info) {
irq_debug("firmware not support share-irq.\n");
return;
}
tab_info[0] = addr;
tab_info[1] = len;
irq_debug("0x%x -> 0x%x\n", addr, (uint32_t)tab_info);
update_irq_head_info(fdt, nodeoffset, (void *)addr, len);
break;
}
}
#endif
#ifdef CFG_RISCV_E907
void sunxi_e907_clock_init(uint32_t addr);
void sunxi_e907_clock_reset(void);
void boot_e907(phys_addr_t base, unsigned long fdt_addr)
{
uint32_t run_addr = 0;
(void)fdt_addr;
/* ISP needs, after boot0 runs successfully,
* you need to set the flag on rtc, and clear
* it in advance to prevent the small core from
* reading in advance
*/
#if CFG_BOOT0_LOAD_ISPPARM
/* Specific to fastboot*/
#ifdef CFG_SUNXI_SDMMC
int (*flash_read)(uint, uint, void *);
flash_read = mmc_bread_ext;
#elif CFG_SUNXI_SPINOR
int (*flash_read)(uint, uint, void *);
flash_read = spinor_read;
#endif /*sdmmc*/
if (load_isp_param(flash_read) != 0) {
printf("read isp param failed!!!\n");
}
extern void update_isp_param(void);
update_isp_param();
#endif
#ifdef CFG_BOOT0_WIRTE_RTC_TO_ISP
rtc_clear_isp_flag();
#endif
#ifdef CFG_UPDATA_IRQ_TAB
irq_debug("Try to Update Interrupt Table.\n");
update_riscv_irq_tab(base, fdt_addr, 0);
#endif
/* assert e907 */
sunxi_e907_clock_reset();
/* get boot address */
run_addr = elf_get_entry_addr(base);
/* load elf to target address */
load_elf_image(base);
/* de-assert e907 */
sunxi_e907_clock_init(run_addr);
}
#endif