From ab3b53f30f348a1f2a31109464d75c5fd4860653 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Wed, 18 Nov 2020 10:27:00 +0000 Subject: [PATCH 01/10] sunxi: add PineCube board PineCube is an IP camera development kit released by Pine64. It comes with the following compoents: - A mainboard with Sochip S3 SoC, a 16MByte SPI Flash, AXP209 PMIC, a power-only microUSB connector, a USB Type-A connector, a 10/100Mbps Ethernet port and FPC connectors for camera and daughter board. - An OV5640-based camera module which is connected to the parallel CSI bus of the mainboard. - A daughterboard with several buttons, a SD slot, some IR LEDs, a microphone and a speaker connector. As the device tree is synchronized in a previous commit, just add it to Makefile, create a new MAINTAINER item and provide a defconfig. Signed-off-by: Icenowy Zheng Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- arch/arm/dts/Makefile | 1 + board/sunxi/MAINTAINERS | 5 +++++ configs/pinecube_defconfig | 15 +++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 configs/pinecube_defconfig diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index fd47e408f82..1a62e4c0708 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -590,6 +590,7 @@ dtb-$(CONFIG_MACH_SUN8I_R40) += \ sun8i-r40-bananapi-m2-ultra.dtb \ sun8i-v40-bananapi-m2-berry.dtb dtb-$(CONFIG_MACH_SUN8I_V3S) += \ + sun8i-s3-pinecube.dtb \ sun8i-v3s-licheepi-zero.dtb dtb-$(CONFIG_MACH_SUN50I_H5) += \ sun50i-h5-bananapi-m2-plus.dtb \ diff --git a/board/sunxi/MAINTAINERS b/board/sunxi/MAINTAINERS index d3755ae41a9..735801ae1d8 100644 --- a/board/sunxi/MAINTAINERS +++ b/board/sunxi/MAINTAINERS @@ -440,6 +440,11 @@ M: Vasily Khoruzhick S: Maintained F: configs/pinebook_defconfig +PINECUBE BOARD: +M: Icenowy Zheng +S: Maintained +F: configs/pinecube_defconfig + PINE64 BOARDS M: Andre Przywara S: Maintained diff --git a/configs/pinecube_defconfig b/configs/pinecube_defconfig new file mode 100644 index 00000000000..a8c404f6b1c --- /dev/null +++ b/configs/pinecube_defconfig @@ -0,0 +1,15 @@ +CONFIG_ARM=y +CONFIG_ARCH_SUNXI=y +CONFIG_SPL=y +CONFIG_MACH_SUN8I_V3S=y +CONFIG_SUNXI_DRAM_DDR3_1333=y +CONFIG_DRAM_CLK=504 +CONFIG_DRAM_ODT_EN=y +CONFIG_I2C0_ENABLE=y +CONFIG_DEFAULT_DEVICE_TREE="sun8i-s3-pinecube" +CONFIG_SPL_I2C_SUPPORT=y +# CONFIG_NETDEVICES is not set +CONFIG_AXP209_POWER=y +CONFIG_AXP_DCDC2_VOLT=1250 +CONFIG_AXP_DCDC3_VOLT=3300 +CONFIG_CONS_INDEX=3 From 23c0ee83aa09c484df3d2c19a8550d31782fd42d Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 17 Nov 2020 23:36:05 +0000 Subject: [PATCH 02/10] sunxi: Factor out eGON BROM header description To be able to easily share the Allwinner eGON BROM header structure between the tools and the SPL code, move the struct definition into a separate header file. Signed-off-by: Andre Przywara Reviewed-by: Jernej Skrabec Reviewed-by: Samuel Holland --- arch/arm/include/asm/arch-sunxi/spl.h | 65 +-------------------- include/sunxi_image.h | 81 +++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 64 deletions(-) create mode 100644 include/sunxi_image.h diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h index 8c916e8c752..7b99c1aa12d 100644 --- a/arch/arm/include/asm/arch-sunxi/spl.h +++ b/arch/arm/include/asm/arch-sunxi/spl.h @@ -7,19 +7,7 @@ #ifndef _ASM_ARCH_SPL_H_ #define _ASM_ARCH_SPL_H_ -#define BOOT0_MAGIC "eGON.BT0" -#define SPL_SIGNATURE "SPL" /* marks "sunxi" SPL header */ -#define SPL_MAJOR_BITS 3 -#define SPL_MINOR_BITS 5 -#define SPL_VERSION(maj, min) \ - ((((maj) & ((1U << SPL_MAJOR_BITS) - 1)) << SPL_MINOR_BITS) | \ - ((min) & ((1U << SPL_MINOR_BITS) - 1))) - -#define SPL_HEADER_VERSION SPL_VERSION(0, 2) - -#define SPL_ENV_HEADER_VERSION SPL_VERSION(0, 1) -#define SPL_DT_HEADER_VERSION SPL_VERSION(0, 2) -#define SPL_DRAM_HEADER_VERSION SPL_VERSION(0, 3) +#include #define SPL_ADDR CONFIG_SUNXI_SRAM_ADDRESS @@ -31,57 +19,6 @@ #define SUNXI_BOOTED_FROM_MMC0_HIGH 0x10 #define SUNXI_BOOTED_FROM_MMC2_HIGH 0x12 -/* boot head definition from sun4i boot code */ -struct boot_file_head { - uint32_t b_instruction; /* one intruction jumping to real code */ - uint8_t magic[8]; /* ="eGON.BT0" or "eGON.BT1", not C-style str */ - uint32_t check_sum; /* generated by PC */ - uint32_t length; /* generated by PC */ - /* - * We use a simplified header, only filling in what is needed - * by the boot ROM. To be compatible with Allwinner tools we - * would need to implement the proper fields here instead of - * padding. - * - * Actually we want the ability to recognize our "sunxi" variant - * of the SPL. To do so, let's place a special signature into the - * "pub_head_size" field. We can reasonably expect Allwinner's - * boot0 to always have the upper 16 bits of this set to 0 (after - * all the value shouldn't be larger than the limit imposed by - * SRAM size). - * If the signature is present (at 0x14), then we know it's safe - * to use the remaining 8 bytes (at 0x18) for our own purposes. - * (E.g. sunxi-tools "fel" utility can pass information there.) - */ - union { - uint32_t pub_head_size; - uint8_t spl_signature[4]; - }; - uint32_t fel_script_address; /* since v0.1, set by sunxi-fel */ - /* - * If the fel_uEnv_length member below is set to a non-zero value, - * it specifies the size (byte count) of data at fel_script_address. - * At the same time this indicates that the data is in uEnv.txt - * compatible format, ready to be imported via "env import -t". - */ - uint32_t fel_uEnv_length; /* since v0.1, set by sunxi-fel */ - /* - * Offset of an ASCIIZ string (relative to the SPL header), which - * contains the default device tree name (CONFIG_DEFAULT_DEVICE_TREE). - * This is optional and may be set to NULL. Is intended to be used - * by flash programming tools for providing nice informative messages - * to the users. - */ - uint32_t dt_name_offset; /* since v0.2, set by mksunxiboot */ - uint32_t dram_size; /* in MiB, since v0.3, set by SPL */ - uint32_t boot_media; /* written here by the boot ROM */ - /* A padding area (may be used for storing text strings) */ - uint32_t string_pool[13]; /* since v0.2, filled by mksunxiboot */ - /* The header must be a multiple of 32 bytes (for VBAR alignment) */ -}; - -/* Compile time check to assure proper alignment of structure */ -typedef char boot_file_head_not_multiple_of_32[1 - 2*(sizeof(struct boot_file_head) % 32)]; #define is_boot0_magic(addr) (memcmp((void *)addr, BOOT0_MAGIC, 8) == 0) diff --git a/include/sunxi_image.h b/include/sunxi_image.h new file mode 100644 index 00000000000..d0d1290eb72 --- /dev/null +++ b/include/sunxi_image.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. + * Tom Cubie + * + * Constants and data structures used in Allwinner "eGON" images, as + * parsed by the Boot-ROM. + * + * Shared between mkimage and the SPL. + */ +#ifndef SUNXI_IMAGE_H +#define SUNXI_IMAGE_H + +#define BOOT0_MAGIC "eGON.BT0" +#define SPL_SIGNATURE "SPL" /* marks "sunxi" SPL header */ +#define SPL_MAJOR_BITS 3 +#define SPL_MINOR_BITS 5 +#define SPL_VERSION(maj, min) \ + ((((maj) & ((1U << SPL_MAJOR_BITS) - 1)) << SPL_MINOR_BITS) | \ + ((min) & ((1U << SPL_MINOR_BITS) - 1))) + +#define SPL_HEADER_VERSION SPL_VERSION(0, 2) + +#define SPL_ENV_HEADER_VERSION SPL_VERSION(0, 1) +#define SPL_DT_HEADER_VERSION SPL_VERSION(0, 2) +#define SPL_DRAM_HEADER_VERSION SPL_VERSION(0, 3) + +/* boot head definition from sun4i boot code */ +struct boot_file_head { + uint32_t b_instruction; /* one intruction jumping to real code */ + uint8_t magic[8]; /* ="eGON.BT0" or "eGON.BT1", not C-style str */ + uint32_t check_sum; /* generated by PC */ + uint32_t length; /* generated by PC */ + /* + * We use a simplified header, only filling in what is needed + * by the boot ROM. To be compatible with Allwinner tools we + * would need to implement the proper fields here instead of + * padding. + * + * Actually we want the ability to recognize our "sunxi" variant + * of the SPL. To do so, let's place a special signature into the + * "pub_head_size" field. We can reasonably expect Allwinner's + * boot0 to always have the upper 16 bits of this set to 0 (after + * all the value shouldn't be larger than the limit imposed by + * SRAM size). + * If the signature is present (at 0x14), then we know it's safe + * to use the remaining 8 bytes (at 0x18) for our own purposes. + * (E.g. sunxi-tools "fel" utility can pass information there.) + */ + union { + uint32_t pub_head_size; + uint8_t spl_signature[4]; + }; + uint32_t fel_script_address; /* since v0.1, set by sunxi-fel */ + /* + * If the fel_uEnv_length member below is set to a non-zero value, + * it specifies the size (byte count) of data at fel_script_address. + * At the same time this indicates that the data is in uEnv.txt + * compatible format, ready to be imported via "env import -t". + */ + uint32_t fel_uEnv_length; /* since v0.1, set by sunxi-fel */ + /* + * Offset of an ASCIIZ string (relative to the SPL header), which + * contains the default device tree name (CONFIG_DEFAULT_DEVICE_TREE). + * This is optional and may be set to NULL. Is intended to be used + * by flash programming tools for providing nice informative messages + * to the users. + */ + uint32_t dt_name_offset; /* since v0.2, set by mksunxiboot */ + uint32_t dram_size; /* in MiB, since v0.3, set by SPL */ + uint32_t boot_media; /* written here by the boot ROM */ + /* A padding area (may be used for storing text strings) */ + uint32_t string_pool[13]; /* since v0.2, filled by mksunxiboot */ + /* The header must be a multiple of 32 bytes (for VBAR alignment) */ +}; + +/* Compile time check to assure proper alignment of structure */ +typedef char boot_file_head_not_multiple_of_32[1 - 2*(sizeof(struct boot_file_head) % 32)]; + +#endif From 6d295099cc96ed39cd2229dad376ab00baba9a5d Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 20 Dec 2018 01:15:18 +0000 Subject: [PATCH 03/10] tools: mkimage: Add Allwinner eGON support So far we used the separate mksunxiboot tool for generating a bootable image for Allwinner SPLs, probably just for historical reasons. Use the mkimage framework to generate a so called eGON image the Allwinner BROM expects. The new image type is called "sunxi_egon", to differentiate it from the (still to be implemented) secure boot TOC0 image. Signed-off-by: Andre Przywara Reviewed-by: Jernej Skrabec Reviewed-by: Samuel Holland Reviewed-by: Simon Glass --- common/image.c | 1 + include/image.h | 1 + include/sunxi_image.h | 1 + tools/Makefile | 1 + tools/sunxi_egon.c | 136 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+) create mode 100644 tools/sunxi_egon.c diff --git a/common/image.c b/common/image.c index 451fc689a89..6923dac7c07 100644 --- a/common/image.c +++ b/common/image.c @@ -189,6 +189,7 @@ static const table_entry_t uimage_type[] = { { IH_TYPE_STM32IMAGE, "stm32image", "STMicroelectronics STM32 Image" }, { IH_TYPE_MTKIMAGE, "mtk_image", "MediaTek BootROM loadable Image" }, { IH_TYPE_COPRO, "copro", "Coprocessor Image"}, + { IH_TYPE_SUNXI_EGON, "sunxi_egon", "Allwinner eGON Boot Image" }, { -1, "", "", }, }; diff --git a/include/image.h b/include/image.h index 41473dbb9cd..856bc3e1b29 100644 --- a/include/image.h +++ b/include/image.h @@ -308,6 +308,7 @@ enum { IH_TYPE_IMX8MIMAGE, /* Freescale IMX8MBoot Image */ IH_TYPE_IMX8IMAGE, /* Freescale IMX8Boot Image */ IH_TYPE_COPRO, /* Coprocessor Image for remoteproc*/ + IH_TYPE_SUNXI_EGON, /* Allwinner eGON Boot Image */ IH_TYPE_COUNT, /* Number of image types */ }; diff --git a/include/sunxi_image.h b/include/sunxi_image.h index d0d1290eb72..5b2055c0af3 100644 --- a/include/sunxi_image.h +++ b/include/sunxi_image.h @@ -13,6 +13,7 @@ #define SUNXI_IMAGE_H #define BOOT0_MAGIC "eGON.BT0" +#define BROM_STAMP_VALUE 0x5f0a6c39 #define SPL_SIGNATURE "SPL" /* marks "sunxi" SPL header */ #define SPL_MAJOR_BITS 3 #define SPL_MINOR_BITS 5 diff --git a/tools/Makefile b/tools/Makefile index 14fb0ed98f6..9b1aa51b10a 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -104,6 +104,7 @@ dumpimage-mkimage-objs := aisimage.o \ stm32image.o \ $(ROCKCHIP_OBS) \ socfpgaimage.o \ + sunxi_egon.o \ lib/crc16.o \ lib/sha1.o \ lib/sha256.o \ diff --git a/tools/sunxi_egon.c b/tools/sunxi_egon.c new file mode 100644 index 00000000000..a5299eb6a11 --- /dev/null +++ b/tools/sunxi_egon.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 Arm Ltd. + */ + +#include "imagetool.h" +#include + +#include + +/* + * NAND requires 8K padding. SD/eMMC gets away with 512 bytes, + * but let's use the larger padding to cover both. + */ +#define PAD_SIZE 8192 + +static int egon_check_params(struct image_tool_params *params) +{ + /* We just need a binary image file. */ + return !params->dflag; +} + +static int egon_verify_header(unsigned char *ptr, int image_size, + struct image_tool_params *params) +{ + const struct boot_file_head *header = (void *)ptr; + uint32_t length; + + /* First 4 bytes must be an ARM branch instruction. */ + if ((le32_to_cpu(header->b_instruction) & 0xff000000) != 0xea000000) + return EXIT_FAILURE; + + if (memcmp(header->magic, BOOT0_MAGIC, sizeof(header->magic))) + return EXIT_FAILURE; + + length = le32_to_cpu(header->length); + /* Must be at least 512 byte aligned. */ + if (length & 511) + return EXIT_FAILURE; + + /* + * Image could also contain U-Boot proper, so could be bigger. + * But it must not be shorter. + */ + if (image_size < length) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static void egon_print_header(const void *buf) +{ + const struct boot_file_head *header = buf; + + printf("Allwinner eGON image, size: %d bytes\n", + le32_to_cpu(header->length)); + + if (memcmp(header->spl_signature, SPL_SIGNATURE, 3)) + return; + + printf("\tSPL header version %d.%d\n", + header->spl_signature[3] >> SPL_MINOR_BITS, + header->spl_signature[3] & ((1U << SPL_MINOR_BITS) - 1)); + if (header->spl_signature[3] >= SPL_DT_HEADER_VERSION) { + uint32_t dt_name_offs = le32_to_cpu(header->dt_name_offset); + + if (dt_name_offs > 0) + printf("\tDT name: %s\n", (char *)buf + dt_name_offs); + } +} + +static void egon_set_header(void *buf, struct stat *sbuf, int infd, + struct image_tool_params *params) +{ + struct boot_file_head *header = buf; + uint32_t *buf32 = buf; + uint32_t checksum = 0, value; + int i; + + /* Generate an ARM branch instruction to jump over the header. */ + value = 0xea000000 | (sizeof(struct boot_file_head) / 4 - 2); + header->b_instruction = cpu_to_le32(value); + + memcpy(header->magic, BOOT0_MAGIC, sizeof(header->magic)); + header->check_sum = cpu_to_le32(BROM_STAMP_VALUE); + header->length = cpu_to_le32(params->file_size); + + memcpy(header->spl_signature, SPL_SIGNATURE, 3); + header->spl_signature[3] = SPL_ENV_HEADER_VERSION; + + /* If an image name has been provided, use it as the DT name. */ + if (params->imagename && params->imagename[0]) { + if (strlen(params->imagename) > sizeof(header->string_pool) - 1) + printf("WARNING: DT name too long for SPL header!\n"); + else { + strcpy((char *)header->string_pool, params->imagename); + value = offsetof(struct boot_file_head, string_pool); + header->dt_name_offset = cpu_to_le32(value); + header->spl_signature[3] = SPL_DT_HEADER_VERSION; + } + } + + /* Calculate the checksum. Yes, it's that simple. */ + for (i = 0; i < sbuf->st_size / 4; i++) + checksum += le32_to_cpu(buf32[i]); + header->check_sum = cpu_to_le32(checksum); +} + +static int egon_check_image_type(uint8_t type) +{ + return type == IH_TYPE_SUNXI_EGON ? 0 : 1; +} + +static int egon_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams) +{ + tparams->hdr = calloc(sizeof(struct boot_file_head), 1); + + /* Return padding to 8K blocks. */ + return ALIGN(params->file_size, PAD_SIZE) - params->file_size; +} + +U_BOOT_IMAGE_TYPE( + sunxi_egon, + "Allwinner eGON Boot Image support", + sizeof(struct boot_file_head), + NULL, + egon_check_params, + egon_verify_header, + egon_print_header, + egon_set_header, + NULL, + egon_check_image_type, + NULL, + egon_vrec_header +); From acd832cd8c3c981cc1c52d85edabf00a462680b4 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 20 Dec 2018 15:41:34 +0000 Subject: [PATCH 04/10] sunxi: Use mkimage for SPL boot image generation Switch the SPL boot image generation from using mksunxiboot to the new sunxi_egon format of mkimage. Verified to create identical results for all 152 Allwinner boards. Signed-off-by: Andre Przywara Reviewed-by: Jernej Skrabec Reviewed-by: Samuel Holland Reviewed-by: Simon Glass Tested-by: Samuel Holland --- scripts/Makefile.spl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 87021e22e55..ea4e045769c 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -386,11 +386,11 @@ endif $(obj)/$(SPL_BIN).sfp: $(obj)/$(SPL_BIN).bin FORCE $(call if_changed,mkimage) -quiet_cmd_mksunxiboot = MKSUNXI $@ -cmd_mksunxiboot = $(objtree)/tools/mksunxiboot \ - --default-dt $(CONFIG_DEFAULT_DEVICE_TREE) $< $@ +MKIMAGEFLAGS_sunxi-spl.bin = -T sunxi_egon \ + -n $(CONFIG_DEFAULT_DEVICE_TREE) + $(obj)/sunxi-spl.bin: $(obj)/$(SPL_BIN).bin FORCE - $(call if_changed,mksunxiboot) + $(call if_changed,mkimage) quiet_cmd_sunxi_spl_image_builder = SUNXI_SPL_IMAGE_BUILDER $@ cmd_sunxi_spl_image_builder = $(objtree)/tools/sunxi-spl-image-builder \ From 00f8e9c2da2a4aaeb762652e4f029d796eec3c86 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Wed, 4 Nov 2020 01:00:31 +0000 Subject: [PATCH 05/10] sunxi: Fix is_boot0_magic macro The is_boot0_magic macro is missing parentheses around the macro argument, breaking any usage with a more complex argument. Signed-off-by: Andre Przywara --- arch/arm/include/asm/arch-sunxi/spl.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h index 7b99c1aa12d..58cdf806d9a 100644 --- a/arch/arm/include/asm/arch-sunxi/spl.h +++ b/arch/arm/include/asm/arch-sunxi/spl.h @@ -19,8 +19,7 @@ #define SUNXI_BOOTED_FROM_MMC0_HIGH 0x10 #define SUNXI_BOOTED_FROM_MMC2_HIGH 0x12 - -#define is_boot0_magic(addr) (memcmp((void *)addr, BOOT0_MAGIC, 8) == 0) +#define is_boot0_magic(addr) (memcmp((void *)(addr), BOOT0_MAGIC, 8) == 0) uint32_t sunxi_get_boot_device(void); From 0e4d5db4e0379da9ca4eee00dbca9773d8718e8e Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 23 Aug 2016 22:19:30 +0100 Subject: [PATCH 06/10] sunxi: Add arm64 FEL support So far we did not support the BootROM based FEL USB debug mode on the 64-bit builds for Allwinner SoCs: The BootROM is using AArch32, but the SPL runs in AArch64. Returning back to AArch32 was not working as expected, since the RMR reset into 32-bit mode always starts execution in the BootROM, but not in the FEL routine. After some debug and research and with help via IRC, the CPU hotplug mechanism emerged as a solution: If a certain R_CPUCFG register contains some magic, the BootROM will immediately branch to an address stored in some other register. This works well for our purposes. Enable the FEL feature by providing early AArch32 code to first save the FEL state, *before* initially entering AArch64. If we eventually determine that we should return to FEL, we reset back into AArch32, and use the CPU hotplug mechanism to run some small AArch32 code snippet that restores the initially saved FEL state. That allows the normal AArch64 SPL build to be loaded via the sunxi-fel tool, with it returning into FEL mode, so that other payloads can be transferred via FEL as well. Tested on A64, H5 and H6. Signed-off-by: Andre Przywara Tested-by: Icenowy Zheng Acked-by: Jagan Teki Tested-by: Priit Laes (on Olimex A64-Olinuxino) --- arch/arm/cpu/armv8/Makefile | 2 + arch/arm/cpu/armv8/fel_utils.S | 78 +++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/boot0.h | 14 +++++ include/configs/sunxi-common.h | 2 - 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 arch/arm/cpu/armv8/fel_utils.S diff --git a/arch/arm/cpu/armv8/Makefile b/arch/arm/cpu/armv8/Makefile index 93d26f98568..f7b4a5ee46c 100644 --- a/arch/arm/cpu/armv8/Makefile +++ b/arch/arm/cpu/armv8/Makefile @@ -27,6 +27,8 @@ obj-$(CONFIG_ARM_SMCCC) += smccc-call.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_ARMV8_SPIN_TABLE) += spin_table.o spin_table_v8.o +else +obj-$(CONFIG_ARCH_SUNXI) += fel_utils.o endif obj-$(CONFIG_$(SPL_)ARMV8_SEC_FIRMWARE_SUPPORT) += sec_firmware.o sec_firmware_asm.o diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S new file mode 100644 index 00000000000..9510dcd9e4c --- /dev/null +++ b/arch/arm/cpu/armv8/fel_utils.S @@ -0,0 +1,78 @@ +/* + * Utility functions for FEL mode, when running SPL in AArch64. + * + * Copyright (c) 2017 Arm Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* + * We don't overwrite save_boot_params() here, to save the FEL state upon + * entry, since this would run *after* the RMR reset, which clobbers that + * state. + * Instead we store the state _very_ early in the boot0 hook, *before* + * resetting to AArch64. + */ + +/* + * The FEL routines in BROM run in AArch32. + * Reset back into 32-bit mode here and restore the saved FEL state + * afterwards. + * Resetting back into AArch32/EL3 using the RMR always enters the BROM, + * but we can use the CPU hotplug mechanism to branch back to our code + * immediately. + */ +ENTRY(return_to_fel) + /* + * the RMR reset will clear all registers, so save the arguments + * (LR and SP) in the fel_stash structure, which we read anyways later + */ + adr x2, fel_stash + str w0, [x2] + str w1, [x2, #4] + + adr x1, fel_stash_addr // to find the fel_stash address in AA32 + str w2, [x1] + + ldr x0, =0xfa50392f // CPU hotplug magic +#ifdef CONFIG_MACH_SUN50I_H6 + ldr x2, =(SUNXI_RTC_BASE + 0x1b8) // BOOT_CPU_HP_FLAG_REG + str w0, [x2], #0x4 +#else + ldr x2, =(SUNXI_CPUCFG_BASE + 0x1a4) // offset for CPU hotplug base + str w0, [x2, #0x8] +#endif + adr x0, back_in_32 + str w0, [x2] + + dsb sy + isb sy + mov x0, #2 // RMR reset into AArch32 + dsb sy + msr RMR_EL3, x0 + isb sy +1: wfi + b 1b + +/* AArch32 code to restore the state from fel_stash and return back to FEL. */ +back_in_32: + .word 0xe59f0028 // ldr r0, [pc, #40] ; load fel_stash address + .word 0xe5901008 // ldr r1, [r0, #8] + .word 0xe129f001 // msr CPSR_fc, r1 + .word 0xf57ff06f // isb + .word 0xe590d000 // ldr sp, [r0] + .word 0xe590e004 // ldr lr, [r0, #4] + .word 0xe5901010 // ldr r1, [r0, #16] + .word 0xee0c1f10 // mcr 15, 0, r1, cr12, cr0, {0} ; VBAR + .word 0xe590100c // ldr r1, [r0, #12] + .word 0xee011f10 // mcr 15, 0, r1, cr1, cr0, {0} ; SCTLR + .word 0xf57ff06f // isb + .word 0xe12fff1e // bx lr ; return to FEL +fel_stash_addr: + .word 0x00000000 // receives fel_stash addr, by AA64 code above +ENDPROC(return_to_fel) diff --git a/arch/arm/include/asm/arch-sunxi/boot0.h b/arch/arm/include/asm/arch-sunxi/boot0.h index 54c144afd8c..46d0f0666c2 100644 --- a/arch/arm/include/asm/arch-sunxi/boot0.h +++ b/arch/arm/include/asm/arch-sunxi/boot0.h @@ -15,6 +15,19 @@ tst x0, x0 // this is "b #0x84" in ARM b reset .space 0x7c + + .word 0xe28f0058 // add r0, pc, #88 + .word 0xe59f1054 // ldr r1, [pc, #84] + .word 0xe0800001 // add r0, r0, r1 + .word 0xe580d000 // str sp, [r0] + .word 0xe580e004 // str lr, [r0, #4] + .word 0xe10fe000 // mrs lr, CPSR + .word 0xe580e008 // str lr, [r0, #8] + .word 0xee11ef10 // mrc 15, 0, lr, cr1, cr0, {0} + .word 0xe580e00c // str lr, [r0, #12] + .word 0xee1cef10 // mrc 15, 0, lr, cr12, cr0, {0} + .word 0xe580e010 // str lr, [r0, #16] + .word 0xe59f1024 // ldr r1, [pc, #36] ; 0x170000a0 .word 0xe59f0024 // ldr r0, [pc, #36] ; CONFIG_*_TEXT_BASE .word 0xe5810000 // str r0, [r1] @@ -36,6 +49,7 @@ #else .word CONFIG_SYS_TEXT_BASE #endif + .word fel_stash - . #else /* normal execution */ b reset diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index a6a4879523a..203cb10fbab 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -163,9 +163,7 @@ #define CONFIG_SYS_MONITOR_LEN (768 << 10) /* 768 KiB */ -#ifndef CONFIG_ARM64 /* AArch64 FEL support is not ready yet */ #define CONFIG_SPL_BOARD_LOAD_IMAGE -#endif /* * We cannot use expressions here, because expressions won't be evaluated in From 92600edb431b03df7149ee30c1508bbc3b5ebb43 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 8 Dec 2020 17:45:31 +0200 Subject: [PATCH 07/10] sunxi: board: Move USB ethernet initialization to board_late_init() For the sake of consistency (*) and order of initialization, i.e. after we have got the ethernet address, interrupt and timer initialized, try to initialize USB ethernet gadget. *) for example, zynqmp uses same order. Signed-off-by: Andy Shevchenko Reviewed-by: Andre Przywara Tested-by: Andre Przywara Signed-off-by: Andre Przywara --- arch/arm/Kconfig | 1 + board/sunxi/board.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index fbe90875ae4..54762ce03a6 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1045,6 +1045,7 @@ config ARCH_SUNXI select SPL_USE_TINY_PRINTF select USE_PREBOOT select SYS_RELOC_GD_ENV_ADDR + imply BOARD_LATE_INIT imply CMD_DM imply CMD_GPT imply CMD_UBI if MTD_RAW_NAND diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 708a27ed78e..77e46471884 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -890,6 +890,11 @@ int misc_init_r(void) setup_environment(gd->fdt_blob); + return 0; +} + +int board_late_init(void) +{ #ifdef CONFIG_USB_ETHER usb_ether_init(); #endif From ec9cdaaa13d5eacb66d711db0f995d1fe4acc09b Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Thu, 12 Mar 2020 17:46:00 +0000 Subject: [PATCH 08/10] sunxi: dram: h6: Improve DDR3 config detection It turns out that in rare cases, current analytical approach to detect correct DRAM bus width and rank on H6 doesn't work. On some TV boxes with DDR3, incorrect DRAM configuration triggers write leveling error which immediately stops initialization process. Exact reason why this error appears isn't known. However, if correct configuration is used, initalization works without problem. In order to fix this issue, simply try another configuration when any kind of error appears during initialization, not just those related to rank and bus width. Signed-off-by: Jernej Skrabec Tested-by: Thomas Graichen Reviewed-by: Andre Przywara Tested-by: Andre Przywara Signed-off-by: Andre Przywara --- arch/arm/mach-sunxi/dram_sun50i_h6.c | 100 +++++++++++++++------------ 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c index 9e34da47479..32ec0bc4cd4 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h6.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -37,9 +37,9 @@ static void mctl_sys_init(struct dram_para *para); static void mctl_com_init(struct dram_para *para); -static void mctl_channel_init(struct dram_para *para); +static bool mctl_channel_init(struct dram_para *para); -static void mctl_core_init(struct dram_para *para) +static bool mctl_core_init(struct dram_para *para) { mctl_sys_init(para); mctl_com_init(para); @@ -51,7 +51,7 @@ static void mctl_core_init(struct dram_para *para) default: panic("Unsupported DRAM type!"); }; - mctl_channel_init(para); + return mctl_channel_init(para); } /* PHY initialisation */ @@ -411,7 +411,7 @@ static void mctl_bit_delay_set(struct dram_para *para) } } -static void mctl_channel_init(struct dram_para *para) +static bool mctl_channel_init(struct dram_para *para) { struct sunxi_mctl_com_reg * const mctl_com = (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; @@ -528,46 +528,15 @@ static void mctl_channel_init(struct dram_para *para) clrbits_le32(&mctl_phy->dx[i].gcr[3], ~0x3ffff); udelay(10); - if (readl(&mctl_phy->pgsr[0]) & 0x400000) - { - /* Check for single rank and optionally half DQ. */ - if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 2 && - (readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 2) { - para->ranks = 1; - - if ((readl(&mctl_phy->dx[2].rsr[0]) & 0x3) != 2 || - (readl(&mctl_phy->dx[3].rsr[0]) & 0x3) != 2) - para->bus_full_width = 0; - - /* Restart DRAM initialization from scratch. */ - mctl_core_init(para); - return; - } - - /* - * Check for dual rank and half DQ. NOTE: This combination - * is highly unlikely and was not tested. Condition is the - * same as in libdram, though. - */ - if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 0 && - (readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 0) { - para->bus_full_width = 0; - - /* Restart DRAM initialization from scratch. */ - mctl_core_init(para); - return; - } - - panic("This DRAM setup is currently not supported.\n"); - } - if (readl(&mctl_phy->pgsr[0]) & 0xff00000) { /* Oops! There's something wrong! */ debug("PLL = %x\n", readl(0x3001010)); debug("DRAM PHY PGSR0 = %x\n", readl(&mctl_phy->pgsr[0])); for (i = 0; i < 4; i++) debug("DRAM PHY DX%dRSR0 = %x\n", i, readl(&mctl_phy->dx[i].rsr[0])); - panic("Error while initializing DRAM PHY!\n"); + debug("Error while initializing DRAM PHY!\n"); + + return false; } if (sunxi_dram_is_lpddr(para->type)) @@ -582,13 +551,59 @@ static void mctl_channel_init(struct dram_para *para) writel(0xffffffff, &mctl_com->maer0); writel(0x7ff, &mctl_com->maer1); writel(0xffff, &mctl_com->maer2); + + return true; +} + +static void mctl_auto_detect_rank_width(struct dram_para *para) +{ + /* this is minimum size that it's supported */ + para->cols = 8; + para->rows = 13; + + /* + * Previous versions of this driver tried to auto detect the rank + * and width by looking at controller registers. However this proved + * to be not reliable, so this approach here is the more robust + * solution. Check the git history for details. + * + * Strategy here is to test most demanding combination first and least + * demanding last, otherwise HW might not be fully utilized. For + * example, half bus width and rank = 1 combination would also work + * on HW with full bus width and rank = 2, but only 1/4 RAM would be + * visible. + */ + + debug("testing 32-bit width, rank = 2\n"); + para->bus_full_width = 1; + para->ranks = 2; + if (mctl_core_init(para)) + return; + + debug("testing 32-bit width, rank = 1\n"); + para->bus_full_width = 1; + para->ranks = 1; + if (mctl_core_init(para)) + return; + + debug("testing 16-bit width, rank = 2\n"); + para->bus_full_width = 0; + para->ranks = 2; + if (mctl_core_init(para)) + return; + + debug("testing 16-bit width, rank = 1\n"); + para->bus_full_width = 0; + para->ranks = 1; + if (mctl_core_init(para)) + return; + + panic("This DRAM setup is currently not supported.\n"); } static void mctl_auto_detect_dram_size(struct dram_para *para) { /* TODO: non-(LP)DDR3 */ - /* Detect rank number and half DQ by the code in mctl_channel_init. */ - mctl_core_init(para); /* detect row address bits */ para->cols = 8; @@ -652,10 +667,6 @@ unsigned long sunxi_dram_init(void) (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; struct dram_para para = { .clk = CONFIG_DRAM_CLK, - .ranks = 2, - .cols = 11, - .rows = 14, - .bus_full_width = 1, #ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3 .type = SUNXI_DRAM_TYPE_LPDDR3, .dx_read_delays = SUN50I_H6_LPDDR3_DX_READ_DELAYS, @@ -673,6 +684,7 @@ unsigned long sunxi_dram_init(void) setbits_le32(0x7010310, BIT(8)); clrbits_le32(0x7010318, 0x3f); + mctl_auto_detect_rank_width(¶); mctl_auto_detect_dram_size(¶); mctl_core_init(¶); From 928f4f48682a5277ca4efd0f3c4e79671dcc8e29 Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Fri, 1 Oct 2021 19:29:00 +0100 Subject: [PATCH 09/10] sunxi: board: extract creating a unique sid into a helper function Refactor setup_environment() so we can use the created sid for a Bluetooth address too. Signed-off-by: Andre Heider Acked-by: Maxime Ripard Reviewed-by: Andre Przywara [rebased] Signed-off-by: Jernej Skrabec Signed-off-by: Andre Przywara --- board/sunxi/board.c | 105 ++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 77e46471884..b7f527a0faa 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -789,6 +789,38 @@ static void parse_spl_header(const uint32_t spl_addr) 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. @@ -799,61 +831,40 @@ static void setup_environment(const void *fdt) unsigned int sid[4]; uint8_t mac_addr[6]; char ethaddr[16]; - int i, ret; + int i; - ret = sunxi_get_sid(sid); - if (ret == 0 && sid[0] != 0) { - /* - * 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 + if (!get_unique_sid(sid)) + return; - /* Ensure the NIC specific bytes of the mac are not all 0 */ - if ((sid[3] & 0xffffff) == 0) - sid[3] |= 0x800000; + for (i = 0; i < 4; i++) { + sprintf(ethaddr, "ethernet%d", i); + if (!fdt_get_alias(fdt, ethaddr)) + continue; - 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 (i == 0) - strcpy(ethaddr, "ethaddr"); - else - sprintf(ethaddr, "eth%daddr", i); + if (env_get(ethaddr)) + continue; - 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; - /* 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); + } - eth_env_set_enetaddr(ethaddr, mac_addr); - } + if (!env_get("serial#")) { + snprintf(serial_string, sizeof(serial_string), + "%08x%08x", sid[0], sid[3]); - if (!env_get("serial#")) { - snprintf(serial_string, sizeof(serial_string), - "%08x%08x", sid[0], sid[3]); - - env_set("serial#", serial_string); - } + env_set("serial#", serial_string); } } From 9267ff89ee27a624ddb5d8eb7d90830ee0ff6f77 Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Fri, 1 Oct 2021 19:29:00 +0100 Subject: [PATCH 10/10] sunxi: board: add a config option to fixup a Bluetooth address Some Bluetooth controllers, like the BCM4345C5 of the Orange Pi 3, ship with the controller default address. Add a config option to fix it up so it can function properly. Signed-off-by: Andre Heider Tested-by: Ondrej Jirman Acked-by: Maxime Ripard [rebased] Signed-off-by: Jernej Skrabec Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- arch/arm/mach-sunxi/Kconfig | 11 +++++++++++ board/sunxi/board.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 49ef217f08c..11e64451927 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -1016,4 +1016,15 @@ config PINEPHONE_DT_SELECTION Enable this option to automatically select the device tree for the correct PinePhone hardware revision during boot. +config BLUETOOTH_DT_DEVICE_FIXUP + string "Fixup the Bluetooth controller address" + default "" + help + This option specifies the DT compatible name of the Bluetooth + controller for which to set the "local-bd-address" property. + Set this option if your device ships with the Bluetooth controller + default address. + The used address is "bdaddr" if set, and "ethaddr" with the LSB + flipped elsewise. + endif diff --git a/board/sunxi/board.c b/board/sunxi/board.c index b7f527a0faa..4f058952b5b 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -913,6 +914,38 @@ int board_late_init(void) 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; @@ -923,6 +956,8 @@ int ft_board_setup(void *blob, struct bd_info *bd) */ setup_environment(blob); + bluetooth_dt_fixup(blob); + #ifdef CONFIG_VIDEO_DT_SIMPLEFB r = sunxi_simplefb_setup(blob); if (r)