/* * (C) Copyright 2012 Henrik Nordstrom * * (C) Copyright 2007-2011 * Allwinner Technology Co., Ltd. * Tom Cubie * * Some init for sunxi platform. * */ #include #include #include #include #include #include #include #include #include #include #include #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