407 lines
8.9 KiB
C
407 lines
8.9 KiB
C
|
/*
|
|||
|
* (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
|
|||
|
|