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
|
||
|