From e3152c690732fb141500379f32fbe2203fa47059 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 6 Aug 2022 00:48:38 -0500 Subject: [PATCH 87/90] sunxi: Add temporary RISC-V version of board code Signed-off-by: Samuel Holland --- board/sunxi/Makefile | 3 +- board/sunxi/board-riscv.c | 687 +++++++++++++++++++++++++++++++++ include/configs/sunxi-common.h | 1 - 3 files changed, 689 insertions(+), 2 deletions(-) create mode 100644 board/sunxi/board-riscv.c --- a/board/sunxi/Makefile +++ b/board/sunxi/Makefile @@ -6,7 +6,8 @@ # # (C) Copyright 2000-2003 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -obj-y += board.o +obj-$(CONFIG_ARM) += board.o +obj-$(CONFIG_RISCV) += board-riscv.o obj-$(CONFIG_SUN7I_GMAC) += gmac.o obj-$(CONFIG_MACH_SUN4I) += dram_sun4i_auto.o obj-$(CONFIG_MACH_SUN5I) += dram_sun5i_auto.o --- /dev/null +++ b/board/sunxi/board-riscv.c @@ -0,0 +1,687 @@ +// 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; + + /* 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 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -12,7 +12,6 @@ #ifndef _SUNXI_COMMON_CONFIG_H #define _SUNXI_COMMON_CONFIG_H -#include #include /* Serial & console */