// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2012-2013 Henrik Nordstrom * (C) Copyright 2013 Luke Kenneth Casson Leighton * * (C) Copyright 2007-2011 * Allwinner Technology Co., Ltd. * Tom Cubie * * Some board init for the Allwinner A10-evb board. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_RISCV int board_init(void) { /* https://lore.kernel.org/u-boot/31587574-4cd1-02da-9761-0134ac82b94b@sholland.org/ */ return cpu_probe_all(); } int sunxi_get_sid(unsigned int *sid) { return -ENODEV; } #define SPL_ADDR CONFIG_SUNXI_SRAM_ADDRESS /* The low 8-bits of the 'boot_media' field in the SPL header */ #define SUNXI_BOOTED_FROM_MMC0 0 #define SUNXI_BOOTED_FROM_NAND 1 #define SUNXI_BOOTED_FROM_MMC2 2 #define SUNXI_BOOTED_FROM_SPI 3 #define SUNXI_BOOTED_FROM_MMC0_HIGH 0x10 #define SUNXI_BOOTED_FROM_MMC2_HIGH 0x12 #define SUNXI_INVALID_BOOT_SOURCE -1 static int sunxi_egon_valid(struct boot_file_head *egon_head) { return !memcmp(egon_head->magic, BOOT0_MAGIC, 8); /* eGON.BT0 */ } static int sunxi_toc0_valid(struct toc0_main_info *toc0_info) { return !memcmp(toc0_info->name, TOC0_MAIN_INFO_NAME, 8); /* TOC0.GLH */ } static int sunxi_get_boot_source(void) { struct boot_file_head *egon_head = (void *)SPL_ADDR; struct toc0_main_info *toc0_info = (void *)SPL_ADDR; /* * On the ARMv5 SoCs, the SPL header in SRAM is overwritten by the * exception vectors in U-Boot proper, so we won't find any * information there. Also the FEL stash is only valid in the SPL, * so we can't use that either. So if this is called from U-Boot * proper, just return MMC0 as a placeholder, for now. */ if (IS_ENABLED(CONFIG_MACH_SUNIV) && !IS_ENABLED(CONFIG_SPL_BUILD)) return SUNXI_BOOTED_FROM_MMC0; if (sunxi_egon_valid(egon_head)) return readb(&egon_head->boot_media); if (sunxi_toc0_valid(toc0_info)) return readb(&toc0_info->platform[0]); /* Not a valid image, so we must have been booted via FEL. */ return SUNXI_INVALID_BOOT_SOURCE; } /* The sunxi internal brom will try to loader external bootloader * from mmc0, nand flash, mmc2. */ uint32_t sunxi_get_boot_device(void) { int boot_source = sunxi_get_boot_source(); /* * When booting from the SD card or NAND memory, the "eGON.BT0" * signature is expected to be found in memory at the address 0x0004 * (see the "mksunxiboot" tool, which generates this header). * * When booting in the FEL mode over USB, this signature is patched in * memory and replaced with something else by the 'fel' tool. This other * signature is selected in such a way, that it can't be present in a * valid bootable SD card image (because the BROM would refuse to * execute the SPL in this case). * * This checks for the signature and if it is not found returns to * the FEL code in the BROM to wait and receive the main u-boot * binary over USB. If it is found, it determines where SPL was * read from. */ switch (boot_source) { case SUNXI_INVALID_BOOT_SOURCE: return BOOT_DEVICE_BOARD; case SUNXI_BOOTED_FROM_MMC0: case SUNXI_BOOTED_FROM_MMC0_HIGH: return BOOT_DEVICE_MMC1; case SUNXI_BOOTED_FROM_NAND: return BOOT_DEVICE_NAND; case SUNXI_BOOTED_FROM_MMC2: case SUNXI_BOOTED_FROM_MMC2_HIGH: return BOOT_DEVICE_MMC2; case SUNXI_BOOTED_FROM_SPI: return BOOT_DEVICE_SPI; } panic("Unknown boot source %d\n", boot_source); return -1; /* Never reached */ } uint32_t sunxi_get_spl_size(void) { struct boot_file_head *egon_head = (void *)SPL_ADDR; struct toc0_main_info *toc0_info = (void *)SPL_ADDR; if (sunxi_egon_valid(egon_head)) return readl(&egon_head->length); if (sunxi_toc0_valid(toc0_info)) return readl(&toc0_info->length); /* Not a valid image, so use the default U-Boot offset. */ return 0; } /* * The eGON SPL image can be located at 8KB or at 128KB into an SD card or * an eMMC device. The boot source has bit 4 set in the latter case. * By adding 120KB to the normal offset when booting from a "high" location * we can support both cases. * Also U-Boot proper is located at least 32KB after the SPL, but will * immediately follow the SPL if that is bigger than that. */ unsigned long spl_mmc_get_uboot_raw_sector(struct mmc *mmc, unsigned long raw_sect) { unsigned long spl_size = sunxi_get_spl_size(); unsigned long sector; sector = max(raw_sect, spl_size / 512); switch (sunxi_get_boot_source()) { case SUNXI_BOOTED_FROM_MMC0_HIGH: case SUNXI_BOOTED_FROM_MMC2_HIGH: sector += (128 - 8) * 2; break; } printf("SPL size = %lu, sector = %lu\n", spl_size, sector); return sector; } u32 spl_boot_device(void) { return sunxi_get_boot_device(); } #define CSR_MXSTATUS 0x7c0 #define CSR_MHCR 0x7c1 #define CSR_MCOR 0x7c2 #define CSR_MHINT 0x7c5 int spl_board_init_f(void) { int ret; struct udevice *dev; ret = cpu_probe_all(); if (ret) { debug("CPU init failed: %d\n", ret); } /* DDR init */ ret = uclass_get_device(UCLASS_RAM, 0, &dev); if (ret) { debug("DRAM init failed: %d\n", ret); return ret; } /* Initialize extension CSRs. */ printf("mxstatus=0x%08lx mhcr=0x%08lx mcor=0x%08lx mhint=0x%08lx\n", csr_read(CSR_MXSTATUS), csr_read(CSR_MHCR), csr_read(CSR_MCOR), csr_read(CSR_MHINT)); csr_set(CSR_MXSTATUS, 0x638000); csr_write(CSR_MCOR, 0x70013); csr_write(CSR_MHCR, 0x11ff); csr_write(CSR_MHINT, 0x16e30c); return 0; } #ifdef CONFIG_SPL_BUILD void spl_perform_fixups(struct spl_image_info *spl_image) { struct ram_info info; struct udevice *dev; int ret; ret = uclass_get_device(UCLASS_RAM, 0, &dev); if (ret) panic("No RAM device"); ret = ram_get_info(dev, &info); if (ret) panic("No RAM info"); ret = fdt_fixup_memory(spl_image->fdt_addr, info.base, info.size); if (ret) panic("Failed to update DTB"); } #endif #endif DECLARE_GLOBAL_DATA_PTR; /* * Try to use the environment from the boot source first. * For MMC, this means a FAT partition on the boot device (SD or eMMC). * If the raw MMC environment is also enabled, this is tried next. * When booting from NAND we try UBI first, then NAND directly. * SPI flash falls back to FAT (on SD card). */ enum env_location env_get_location(enum env_operation op, int prio) { if (prio > 1) return ENVL_UNKNOWN; /* NOWHERE is exclusive, no other option can be defined. */ if (IS_ENABLED(CONFIG_ENV_IS_NOWHERE)) return ENVL_NOWHERE; switch (sunxi_get_boot_device()) { case BOOT_DEVICE_MMC1: case BOOT_DEVICE_MMC2: if (prio == 0 && IS_ENABLED(CONFIG_ENV_IS_IN_FAT)) return ENVL_FAT; if (IS_ENABLED(CONFIG_ENV_IS_IN_MMC)) return ENVL_MMC; break; case BOOT_DEVICE_NAND: if (prio == 0 && IS_ENABLED(CONFIG_ENV_IS_IN_UBI)) return ENVL_UBI; if (IS_ENABLED(CONFIG_ENV_IS_IN_NAND)) return ENVL_NAND; break; case BOOT_DEVICE_SPI: if (prio == 0 && IS_ENABLED(CONFIG_ENV_IS_IN_SPI_FLASH)) return ENVL_SPI_FLASH; if (IS_ENABLED(CONFIG_ENV_IS_IN_FAT)) return ENVL_FAT; break; case BOOT_DEVICE_BOARD: break; default: break; } /* * If we come here for the first time, we *must* return a valid * environment location other than ENVL_UNKNOWN, or the setup sequence * in board_f() will silently hang. This is arguably a bug in * env_init(), but for now pick one environment for which we know for * sure to have a driver for. For all defconfigs this is either FAT * or UBI, or NOWHERE, which is already handled above. */ if (prio == 0) { if (IS_ENABLED(CONFIG_ENV_IS_IN_FAT)) return ENVL_FAT; if (IS_ENABLED(CONFIG_ENV_IS_IN_UBI)) return ENVL_UBI; } return ENVL_UNKNOWN; } /* * On older SoCs the SPL is actually at address zero, so using NULL as * an error value does not work. */ #define INVALID_SPL_HEADER ((void *)~0UL) static struct boot_file_head * get_spl_header(uint8_t req_version) { struct boot_file_head *spl = (void *)(ulong)SPL_ADDR; uint8_t spl_header_version = spl->spl_signature[3]; /* Is there really the SPL header (still) there? */ if (memcmp(spl->spl_signature, SPL_SIGNATURE, 3) != 0) return INVALID_SPL_HEADER; if (spl_header_version < req_version) { printf("sunxi SPL version mismatch: expected %u, got %u\n", req_version, spl_header_version); return INVALID_SPL_HEADER; } return spl; } static const char *get_spl_dt_name(void) { struct boot_file_head *spl = get_spl_header(SPL_DT_HEADER_VERSION); /* Check if there is a DT name stored in the SPL header. */ if (spl != INVALID_SPL_HEADER && spl->dt_name_offset) return (char *)spl + spl->dt_name_offset; return NULL; } #if CONFIG_MMC_SUNXI_SLOT_EXTRA != -1 int mmc_get_env_dev(void) { switch (sunxi_get_boot_device()) { case BOOT_DEVICE_MMC1: return 0; case BOOT_DEVICE_MMC2: return 1; default: return CONFIG_SYS_MMC_ENV_DEV; } } #endif #ifdef CONFIG_SPL_BUILD void sunxi_board_init(void) { #ifdef CONFIG_LED_STATUS if (IS_ENABLED(CONFIG_SPL_DRIVERS_MISC)) status_led_init(); #endif } #endif #ifdef CONFIG_USB_GADGET int g_dnl_board_usb_cable_connected(void) { struct udevice *dev; struct phy phy; int ret; ret = uclass_get_device(UCLASS_USB_GADGET_GENERIC, 0, &dev); if (ret) { pr_err("%s: Cannot find USB device\n", __func__); return ret; } ret = generic_phy_get_by_name(dev, "usb", &phy); if (ret) { pr_err("failed to get %s USB PHY\n", dev->name); return ret; } ret = generic_phy_init(&phy); if (ret) { pr_debug("failed to init %s USB PHY\n", dev->name); return ret; } return sun4i_usb_phy_vbus_detect(&phy); } #endif #ifdef CONFIG_SERIAL_TAG void get_board_serial(struct tag_serialnr *serialnr) { char *serial_string; unsigned long long serial; serial_string = env_get("serial#"); if (serial_string) { serial = simple_strtoull(serial_string, NULL, 16); serialnr->high = (unsigned int) (serial >> 32); serialnr->low = (unsigned int) (serial & 0xffffffff); } else { serialnr->high = 0; serialnr->low = 0; } } #endif /* * Check the SPL header for the "sunxi" variant. If found: parse values * that might have been passed by the loader ("fel" utility), and update * the environment accordingly. */ static void parse_spl_header(const uint32_t spl_addr) { struct boot_file_head *spl = get_spl_header(SPL_ENV_HEADER_VERSION); if (spl == INVALID_SPL_HEADER) return; if (!spl->fel_script_address) return; if (spl->fel_uEnv_length != 0) { /* * data is expected in uEnv.txt compatible format, so "env * import -t" the string(s) at fel_script_address right away. */ himport_r(&env_htab, (char *)(uintptr_t)spl->fel_script_address, spl->fel_uEnv_length, '\n', H_NOCLEAR, 0, 0, NULL); return; } /* otherwise assume .scr format (mkimage-type script) */ env_set_hex("fel_scriptaddr", spl->fel_script_address); } static bool get_unique_sid(unsigned int *sid) { if (sunxi_get_sid(sid) != 0) return false; if (!sid[0]) return false; /* * The single words 1 - 3 of the SID have quite a few bits * which are the same on many models, so we take a crc32 * of all 3 words, to get a more unique value. * * Note we only do this on newer SoCs as we cannot change * the algorithm on older SoCs since those have been using * fixed mac-addresses based on only using word 3 for a * long time and changing a fixed mac-address with an * u-boot update is not good. */ #if !defined(CONFIG_MACH_SUN4I) && !defined(CONFIG_MACH_SUN5I) && \ !defined(CONFIG_MACH_SUN6I) && !defined(CONFIG_MACH_SUN7I) && \ !defined(CONFIG_MACH_SUN8I_A23) && !defined(CONFIG_MACH_SUN8I_A33) sid[3] = crc32(0, (unsigned char *)&sid[1], 12); #endif /* Ensure the NIC specific bytes of the mac are not all 0 */ if ((sid[3] & 0xffffff) == 0) sid[3] |= 0x800000; return true; } /* * Note this function gets called multiple times. * It must not make any changes to env variables which already exist. */ static void setup_environment(const void *fdt) { char serial_string[17] = { 0 }; unsigned int sid[4]; uint8_t mac_addr[6]; char ethaddr[16]; int i; if (!get_unique_sid(sid)) return; for (i = 0; i < 4; i++) { sprintf(ethaddr, "ethernet%d", i); if (!fdt_get_alias(fdt, ethaddr)) continue; if (i == 0) strcpy(ethaddr, "ethaddr"); else sprintf(ethaddr, "eth%daddr", i); if (env_get(ethaddr)) continue; /* Non OUI / registered MAC address */ mac_addr[0] = (i << 4) | 0x02; mac_addr[1] = (sid[0] >> 0) & 0xff; mac_addr[2] = (sid[3] >> 24) & 0xff; mac_addr[3] = (sid[3] >> 16) & 0xff; mac_addr[4] = (sid[3] >> 8) & 0xff; mac_addr[5] = (sid[3] >> 0) & 0xff; eth_env_set_enetaddr(ethaddr, mac_addr); } if (!env_get("serial#")) { snprintf(serial_string, sizeof(serial_string), "%08x%08x", sid[0], sid[3]); env_set("serial#", serial_string); } } int misc_init_r(void) { const char *spl_dt_name; uint boot; env_set("fel_booted", NULL); env_set("fel_scriptaddr", NULL); env_set("mmc_bootdev", NULL); boot = sunxi_get_boot_device(); /* determine if we are running in FEL mode */ if (boot == BOOT_DEVICE_BOARD) { env_set("fel_booted", "1"); parse_spl_header(SPL_ADDR); /* or if we booted from MMC, and which one */ } else if (boot == BOOT_DEVICE_MMC1) { env_set("mmc_bootdev", "0"); } else if (boot == BOOT_DEVICE_MMC2) { env_set("mmc_bootdev", "1"); } /* Set fdtfile to match the FIT configuration chosen in SPL. */ spl_dt_name = get_spl_dt_name(); if (spl_dt_name) { char *prefix = IS_ENABLED(CONFIG_ARM64) ? "allwinner/" : ""; char str[64]; snprintf(str, sizeof(str), "%s%s.dtb", prefix, spl_dt_name); env_set("fdtfile", str); } setup_environment(gd->fdt_blob); return 0; } int board_late_init(void) { #ifdef CONFIG_USB_ETHER usb_ether_init(); #endif #ifdef SUNXI_SCP_BASE if (!rproc_load(0, SUNXI_SCP_BASE, SUNXI_SCP_MAX_SIZE)) { puts("Starting SCP...\n"); rproc_start(0); } #endif return 0; } static void bluetooth_dt_fixup(void *blob) { /* Some devices ship with a Bluetooth controller default address. * Set a valid address through the device tree. */ uchar tmp[ETH_ALEN], bdaddr[ETH_ALEN]; unsigned int sid[4]; int i; if (!CONFIG_BLUETOOTH_DT_DEVICE_FIXUP[0]) return; if (eth_env_get_enetaddr("bdaddr", tmp)) { /* Convert between the binary formats of the corresponding stacks */ for (i = 0; i < ETH_ALEN; ++i) bdaddr[i] = tmp[ETH_ALEN - i - 1]; } else { if (!get_unique_sid(sid)) return; bdaddr[0] = ((sid[3] >> 0) & 0xff) ^ 1; bdaddr[1] = (sid[3] >> 8) & 0xff; bdaddr[2] = (sid[3] >> 16) & 0xff; bdaddr[3] = (sid[3] >> 24) & 0xff; bdaddr[4] = (sid[0] >> 0) & 0xff; bdaddr[5] = 0x02; } do_fixup_by_compat(blob, CONFIG_BLUETOOTH_DT_DEVICE_FIXUP, "local-bd-address", bdaddr, ETH_ALEN, 1); } int ft_board_setup(void *blob, struct bd_info *bd) { int __maybe_unused r; /* * Call setup_environment and fdt_fixup_ethernet again * in case the boot fdt has ethernet aliases the u-boot * copy does not have. */ setup_environment(blob); fdt_fixup_ethernet(blob); bluetooth_dt_fixup(blob); #ifdef CONFIG_VIDEO_DT_SIMPLEFB r = sunxi_simplefb_setup(blob); if (r) return r; #endif return 0; } #ifdef CONFIG_SPL_LOAD_FIT static void set_spl_dt_name(const char *name) { struct boot_file_head *spl = get_spl_header(SPL_ENV_HEADER_VERSION); if (spl == INVALID_SPL_HEADER) return; /* Promote the header version for U-Boot proper, if needed. */ if (spl->spl_signature[3] < SPL_DT_HEADER_VERSION) spl->spl_signature[3] = SPL_DT_HEADER_VERSION; strcpy((char *)&spl->string_pool, name); spl->dt_name_offset = offsetof(struct boot_file_head, string_pool); } int board_fit_config_name_match(const char *name) { const char *best_dt_name = get_spl_dt_name(); int ret; #ifdef CONFIG_DEFAULT_DEVICE_TREE if (best_dt_name == NULL) best_dt_name = CONFIG_DEFAULT_DEVICE_TREE; #endif if (best_dt_name == NULL) { /* No DT name was provided, so accept the first config. */ return 0; } #ifdef CONFIG_PINE64_DT_SELECTION if (strstr(best_dt_name, "-pine64-plus")) { /* Differentiate the Pine A64 boards by their DRAM size. */ if ((gd->ram_size == 512 * 1024 * 1024)) best_dt_name = "sun50i-a64-pine64"; } #endif #ifdef CONFIG_PINEPHONE_DT_SELECTION if (strstr(best_dt_name, "-pinephone")) { /* Differentiate the PinePhone revisions by GPIO inputs. */ prcm_apb0_enable(PRCM_APB0_GATE_PIO); sunxi_gpio_set_pull(SUNXI_GPL(6), SUNXI_GPIO_PULL_UP); sunxi_gpio_set_cfgpin(SUNXI_GPL(6), SUNXI_GPIO_INPUT); udelay(100); /* PL6 is pulled low by the modem on v1.2. */ if (gpio_get_value(SUNXI_GPL(6)) == 0) best_dt_name = "sun50i-a64-pinephone-1.2"; else best_dt_name = "sun50i-a64-pinephone-1.1"; sunxi_gpio_set_cfgpin(SUNXI_GPL(6), SUNXI_GPIO_DISABLE); sunxi_gpio_set_pull(SUNXI_GPL(6), SUNXI_GPIO_PULL_DISABLE); prcm_apb0_disable(PRCM_APB0_GATE_PIO); } #endif ret = strcmp(name, best_dt_name); /* * If one of the FIT configurations matches the most accurate DT name, * update the SPL header to provide that DT name to U-Boot proper. */ if (ret == 0) set_spl_dt_name(best_dt_name); return ret; } #endif