From 6b36c152cc647e6bb40915c69aaca6c761ed2630 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov <7954832+dimerr@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:20:06 +0000 Subject: [PATCH 1/8] openipc: add the OpenIPC boot layer The firmware scan, recovery and boot-limit failsafe of the openipc-hisilicon branch, for the Allwinner V831/V833: common/openipc.c and include/openipc.h as on openipc-novatek and openipc-anyka, unchanged. CONFIG_OPENIPC builds it; PRODUCT_SOC and PRODUCT_SOCMODEL name the target in the environment. --- .gitignore | 3 + common/Kconfig | 21 ++ common/Makefile | 2 + common/openipc.c | 765 ++++++++++++++++++++++++++++++++++++++++++++++ include/openipc.h | 7 + 5 files changed, 798 insertions(+) create mode 100644 common/openipc.c create mode 100644 include/openipc.h diff --git a/.gitignore b/.gitignore index eb769f144c3d..0183116964e3 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,6 @@ __pycache__ # pylint files /pylint.cur /pylint.out/ + +# OpenIPC build outputs +/output/ diff --git a/common/Kconfig b/common/Kconfig index ebee856e5676..b6f754d9fdf8 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -1006,3 +1006,24 @@ config FDT_SIMPLEFB These functions can be used by board to indicate to the OS the presence of the simple frame buffer with associated reserved memory + +config OPENIPC + bool "OpenIPC boot layer" + help + The OpenIPC firmware scan, SD/USB recovery and boot-limit failsafe + (common/openipc.c), driven from the board's misc_init_r(), with the + default environment in include/configs/openipc-common.h. + +config PRODUCT_SOC + string "Product SoC name" + default SYS_CONFIG_NAME + help + Target SoC name exposed as 'soc=' (and 'board=') in the default + environment. Set per target by the build system. + +config PRODUCT_SOCMODEL + string "Product SoC model" + default "" + help + Optional board/binning model exposed as 'socmodel=' in the default + environment. diff --git a/common/Makefile b/common/Makefile index 2ed8672c3ac1..d725fc328029 100644 --- a/common/Makefile +++ b/common/Makefile @@ -112,3 +112,5 @@ obj-$(CONFIG_$(SPL_TPL_)STACKPROTECTOR) += stackprot.o obj-$(CONFIG_SCP03) += scp03.o obj-$(CONFIG_QFW) += qfw.o + +obj-$(CONFIG_OPENIPC) += openipc.o diff --git a/common/openipc.c b/common/openipc.c new file mode 100644 index 000000000000..002a3206f14f --- /dev/null +++ b/common/openipc.c @@ -0,0 +1,765 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_USB +#include +#endif +#ifdef CONFIG_MMC +#include +#endif +#ifdef CONFIG_ENV_IS_IN_NAND +#include +#endif +#ifdef CONFIG_ENV_IS_IN_NAND +#include +#include +#include +#include +#include +#include +#endif + +#define SCAN_FROM CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE +#define SCAN_STEP 0x10000 +#define SCAN_LIMIT 0x400000 + +#define UBI_MAGIC_ASCII "UBI#" +#define FDT_MAGIC 0xd00dfeed + +#define SQSH_SIZE_OFF 0x58 +#define SQSH_MAGIC_BE 0x73717368 +#define SQSH_MAGIC_LE 0x68737173 + +static int check_squashfs(void *buf) { + uint32_t magic = get_unaligned_be32(buf); + if (magic == SQSH_MAGIC_BE || magic == SQSH_MAGIC_LE) + return 1; + return 0; +} + +#ifdef CONFIG_ENV_IS_IN_NAND +#define UBIFS_SB_MAGIC 0x06101831 + +static int check_ubifs(void *buf) { + return get_unaligned_le32(buf) == UBIFS_SB_MAGIC; +} + +/* Check for the UBI marker without attaching: attaching an empty MTD + * would create a fresh UBI on flash. */ +static int ubi_marker_present(void) { + struct mtd_info *mtd; + u8 buf[4]; + size_t retlen; + int ret; + + /* Register the mtdparts partitions before looking one up by name */ + mtd_probe_devices(); + mtd = get_mtd_device_nm("ubi"); + if (IS_ERR(mtd)) + return 0; + + ret = mtd_read(mtd, 0, sizeof(buf), &retlen, buf); + put_mtd_device(mtd); + + return !ret && retlen == sizeof(buf) && + get_unaligned_be32(buf) == UBI_EC_HDR_MAGIC; +} +#endif + +static int check_kernel(void *buf) { + struct image_header *hdr = (struct image_header *)buf; + if (image_check_magic(hdr)) { + return 1; + } + + if (fdt_magic(buf) == FDT_MAGIC) { + if (fdt_check_header(buf) == 0) { + return 1; + } + } + + return 0; +} + +#ifndef CONFIG_ENV_IS_IN_NAND + +static uint64_t get_squashfs_size(void *buf) { + uint32_t magic = get_unaligned_be32(buf); + uint64_t size; + + if (magic == SQSH_MAGIC_BE) { + size = get_unaligned_be64((u8 *)buf + SQSH_SIZE_OFF); + } else { + size = get_unaligned_le64((u8 *)buf + SQSH_SIZE_OFF); + } + + return size; +} + +static int check_rootfs(void *buf, uint64_t *rootfs_size) { + if (check_squashfs(buf)) { + *rootfs_size = get_squashfs_size(buf); + return 1; + } + if (memcmp(buf, UBI_MAGIC_ASCII, 4) == 0) { + *rootfs_size = 0; + return 1; + } + return 0; +} + +static int scan_spi_device(struct spi_flash *flash, ulong *kernel_off, ulong *rootfs_off, uint64_t *kernel_size, + uint64_t *rootfs_size) { + u8 *buf; + ulong offset; + ulong erase_size; + int ret; + + buf = malloc_cache_aligned(SCAN_STEP); + if (!buf) + return -1; + +#ifdef CONFIG_SPI_FLASH + erase_size = flash->sector_size; +#else + erase_size = 0x10000; +#endif + + printf("Scanning 0x%x-0x%x ...\n", SCAN_FROM, SCAN_LIMIT); + + for (offset = SCAN_FROM; offset < SCAN_LIMIT && offset < flash->size; offset += SCAN_STEP) { + ret = spi_flash_read(flash, offset, SCAN_STEP, buf); + if (ret != 0) + continue; + + /* Skip completely empty regions */ + if (buf[0] == 0x00 || buf[0] == 0xFF) { + int i, uniform = 1; + for (i = 1; i < 32; i++) { + if (buf[i] != buf[0]) { + uniform = 0; + break; + } + } + if (uniform) + continue; + } + + if (!*kernel_off && check_kernel(buf)) { + *kernel_off = offset; + printf("Found Kernel at 0x%08lx\n", offset); + } + + if (!*rootfs_off && check_rootfs(buf, rootfs_size)) { + *rootfs_off = offset; + printf("Found RootFS at 0x%08lx (%lldk)\n", offset, *rootfs_size / 1024); + } + + if (*kernel_off && *rootfs_off) + break; + } + + if (*kernel_off && *rootfs_off) { + *kernel_size = *rootfs_off - *kernel_off; + } + + /* Align rootfs size to erase block boundary */ + if (*rootfs_size > 0) { + ulong aligned_size = (*rootfs_size + erase_size - 1) & ~(erase_size - 1); + *rootfs_size = aligned_size; + } + + free(buf); + return 0; +} + +#endif /* !CONFIG_ENV_IS_IN_NAND */ + +#if (defined(CONFIG_MMC) || defined(CONFIG_USB_STORAGE)) && defined(CONFIG_FS_FAT) +#define RECOVERY_KERNEL 0 +#define RECOVERY_ROOTFS 1 + +/* The medium recovery_mount() found. Every fs_*() call closes the + * filesystem when it returns, so each one has to select it again. */ +static const char *recovery_if, *recovery_part; + +static int recovery_select(void) { + return fs_set_blk_dev(recovery_if, recovery_part, FS_TYPE_FAT); +} + +static int recovery_mount(const char *ifname) { + static const char *const parts[] = { "0:1", "0" }; + int i; + + for (i = 0; i < ARRAY_SIZE(parts); i++) { + if (!fs_set_blk_dev(ifname, parts[i], FS_TYPE_FAT)) { + recovery_if = ifname; + recovery_part = parts[i]; + return 0; + } + } + return -1; +} + +#ifdef CONFIG_ENV_IS_IN_NAND +static int recovery_ensure_volume(const char *name, const char *size) { + struct ubi_volume_desc *desc; + char cmd[64]; + + desc = ubi_open_volume_nm(0, name, UBI_READONLY); + if (!IS_ERR(desc)) { + ubi_close_volume(desc); + return 0; + } + + printf("Recovery: creating volume %s\n", name); + snprintf(cmd, sizeof(cmd), "ubi create %s %s d", name, size); + + return run_command(cmd, 0); +} + +/* Recreate a system volume so it always has the expected size; the old + * one is removed first because UBI volumes cannot be resized. With + * `liberate' set, a create that runs out of space removes rootfs_data + * and retries: growing the rootfs is the only way to need its PEBs. */ +static int recovery_recreate_volume(const char *name, const char *size, + bool liberate) { + struct ubi_volume_desc *desc; + char cmd[64]; + int ret; + + desc = ubi_open_volume_nm(0, name, UBI_READONLY); + if (!IS_ERR(desc)) { + ubi_close_volume(desc); + printf("Recovery: recreating volume %s\n", name); + snprintf(cmd, sizeof(cmd), "ubi remove %s", name); + run_command(cmd, 0); + } else { + printf("Recovery: creating volume %s\n", name); + } + + snprintf(cmd, sizeof(cmd), "ubi create %s %s d", name, size); + ret = run_command(cmd, 0); + + if (ret && liberate) { + printf("Recovery: no room for %s, dropping rootfs_data\n", name); + run_command("ubi remove rootfs_data", 0); + ret = run_command(cmd, 0); + } + + return ret; +} + +static int recovery_write(const char *name, int type) { + struct image_header hdr; + loff_t actread; + ulong size; + int ret; + + if (recovery_select() || !fs_exists(name)) + return 0; + + if (type == RECOVERY_ROOTFS) { + /* Raw ubifs image, written into the rootfs volume */ + loff_t fsize; + + ret = recovery_select() ? -1 : fs_size(name, &fsize); + if (ret || fsize == 0) { + printf("Recovery: failed to stat %s\n", name); + return -1; + } + size = fsize; + } else { + /* Kernel: uImage or FIT, written to the kernel volume */ + ret = recovery_select() ? -1 : fs_read(name, (ulong)&hdr, 0, sizeof(hdr), &actread); + if (ret || actread != sizeof(hdr)) { + printf("Recovery: failed to read %s\n", name); + return -1; + } + + if (image_check_magic(&hdr)) { + size = sizeof(hdr) + image_get_data_size(&hdr); + } else if (fdt_magic(&hdr) == FDT_MAGIC && fdt_check_header(&hdr) == 0) { + size = fdt_totalsize(&hdr); + } else { + printf("Recovery: no valid uImage/FIT header in %s\n", name); + return -1; + } + } + + ret = recovery_select() ? -1 : fs_read(name, CONFIG_SYS_LOAD_ADDR, 0, size, &actread); + if (ret || actread != size) { + printf("Recovery: failed to read %s\n", name); + return -1; + } + + /* Attach only now that there is something to write */ + if (ubi_part("ubi", NULL)) { + printf("Recovery: cannot attach UBI\n"); + return -1; + } + + if (type == RECOVERY_ROOTFS) { + char size_str[24]; + + /* The volume is sized to the image; if it does not fit, the data + * volume is dropped (UBI volumes cannot be resized) */ + snprintf(size_str, sizeof(size_str), "0x%lx", size); + ret = recovery_recreate_volume("rootfs", size_str, true); + if (ret) { + printf("Recovery: cannot recreate rootfs volume (%d)\n", ret); + return -1; + } + + ret = ubi_volume_write("rootfs", (void *)CONFIG_SYS_LOAD_ADDR, size); + if (ret) { + printf("Recovery: failed to write rootfs volume (%d)\n", ret); + return -1; + } + + /* The data volume takes the remaining space; keep it if present */ + recovery_ensure_volume("rootfs_data", "0"); + + printf("Recovery: wrote %s (%lu bytes) to rootfs volume\n", name, size); + } else { + ret = recovery_recreate_volume("kernel", "0x300000", false); + if (ret) { + printf("Recovery: cannot recreate kernel volume (%d)\n", ret); + return -1; + } + + ret = ubi_volume_write("kernel", (void *)CONFIG_SYS_LOAD_ADDR, size); + if (ret) { + printf("Recovery: failed to write kernel volume (%d)\n", ret); + return -1; + } + + printf("Recovery: wrote %s (%lu bytes) to kernel volume\n", name, size); + } + + return 1; +} +#else +static struct spi_flash *recovery_flash; + +/* Whether the flash at addr already holds these bytes */ +static int recovery_same(ulong addr, const u8 *data, ulong size) { + const ulong chunk = 0x10000; + u8 *buf = malloc(chunk); + ulong off, n; + int same = buf != NULL; + + for (off = 0; same && off < size; off += n) { + n = min(chunk, size - off); + same = !spi_flash_read(recovery_flash, addr + off, n, buf) && + !memcmp(buf, data + off, n); + } + free(buf); + return same; +} + +static ulong recovery_env(const char *name, ulong def) { + const char *str = env_get(name); + + return str ? hextoul(str, NULL) : def; +} + +/* + * A uImage on the card is flashed where its header's load address says, + * without the header (mkimage -a ); a FIT goes to kernaddr. + * A card left in reflashes nothing: an image the flash already holds is + * skipped. + */ +static int recovery_write(const char *name, int type) { + struct image_header hdr; + loff_t actread; + ulong addr, offset = 0, size, erase_len; + int ret; + + if (recovery_select() || !fs_exists(name)) + return 0; + + ret = recovery_select() ? -1 : fs_read(name, (ulong)&hdr, 0, sizeof(hdr), &actread); + if (ret || actread != sizeof(hdr)) { + printf("Recovery: failed to read %s\n", name); + return -1; + } + + if (image_check_magic(&hdr)) { + addr = image_get_load(&hdr); + offset = sizeof(hdr); + size = image_get_data_size(&hdr); + } else if (type == RECOVERY_KERNEL && fdt_magic(&hdr) == FDT_MAGIC && + fdt_check_header(&hdr) == 0) { + addr = recovery_env("kernaddr", OPENIPC_KERNADDR); + size = fdt_totalsize(&hdr); + } else { + printf("Recovery: no valid uImage/FIT header in %s\n", name); + return -1; + } + + ret = recovery_select() ? -1 : fs_read(name, CONFIG_SYS_LOAD_ADDR, offset, size, &actread); + if (ret || actread != size) { + printf("Recovery: failed to read %s\n", name); + return -1; + } + + if (recovery_same(addr, (u8 *)CONFIG_SYS_LOAD_ADDR, size)) { + printf("Recovery: %s already in flash at 0x%lx\n", name, addr); + return 1; + } + + erase_len = (size + recovery_flash->sector_size - 1) & + ~(recovery_flash->sector_size - 1); + if (spi_flash_erase(recovery_flash, addr, erase_len) || + spi_flash_write(recovery_flash, addr, size, + (void *)CONFIG_SYS_LOAD_ADDR)) { + printf("Recovery: failed to write %s to flash at 0x%lx\n", name, addr); + return -1; + } + + printf("Recovery: wrote %s (%lu bytes) to flash at 0x%lx\n", name, size, + addr); + + return 1; +} +#endif + +/* If a script.txt is present on the medium, execute it (uImage script or + * plain text) instead of searching for kernel/rootfs images. */ +static int recovery_run_script(const char *name) { + loff_t size, actread; + int ret; + char cmd[32]; + + if (recovery_select() || !fs_exists(name)) + return 0; + + ret = recovery_select() ? -1 : fs_size(name, &size); + if (ret || size == 0) { + printf("Recovery: failed to stat %s\n", name); + return 1; + } + + ret = recovery_select() ? -1 : fs_read(name, CONFIG_SYS_LOAD_ADDR, 0, size, &actread); + if (ret || actread != size) { + printf("Recovery: failed to read %s\n", name); + return 1; + } + + if (image_check_magic((void *)CONFIG_SYS_LOAD_ADDR)) { + /* uImage-wrapped script (mkimage -A arm -T script -d ...) */ + sprintf(cmd, "source 0x%lx", (ulong)CONFIG_SYS_LOAD_ADDR); + ret = run_command(cmd, 0); + } else { + /* Plain text script */ + ret = run_command_list((char *)CONFIG_SYS_LOAD_ADDR, size, 0); + } + + printf("Recovery: executed %s (%lld bytes)\n", name, size); + + return 1; +} + +static int recovery_try(void) { + char name[64]; + int ret, found = 0; + + /* script.txt takes precedence over the kernel/rootfs search */ + if (recovery_run_script("script.txt")) + return 1; + +#ifndef CONFIG_ENV_IS_IN_NAND + /* Kernel: uImage preferred, FIT fallback */ + sprintf(name, "uImage.%s.img", CONFIG_PRODUCT_SOC); + ret = recovery_write(name, RECOVERY_KERNEL); + if (ret > 0) { + found = 1; + } else if (ret == 0) { + sprintf(name, "fitImage.%s.img", CONFIG_PRODUCT_SOC); + ret = recovery_write(name, RECOVERY_KERNEL); + if (ret > 0) + found = 1; + } +#endif + + /* Rootfs. On NAND it carries the kernel (/boot), its volume is sized + * to the image and rootfs_data takes the rest. */ +#ifdef CONFIG_ENV_IS_IN_NAND + sprintf(name, "rootfs.ubifs.%s.img", CONFIG_PRODUCT_SOC); +#else + sprintf(name, "rootfs.squashfs.%s.img", CONFIG_PRODUCT_SOC); +#endif + ret = recovery_write(name, RECOVERY_ROOTFS); + if (ret > 0) + found = 1; + + return found; +} + +int firmware_recovery(void) { + int found = 0; + +#ifndef CONFIG_ENV_IS_IN_NAND + recovery_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, + CONFIG_SF_DEFAULT_MODE); + if (!recovery_flash) + return -1; +#endif + +#ifdef CONFIG_MMC + { + struct mmc *mmc = find_mmc_device(0); + + if (mmc && !mmc_init(mmc) && !recovery_mount("mmc")) + found |= recovery_try(); + } +#endif + +#ifdef CONFIG_USB_STORAGE + if (!usb_init() && usb_stor_scan(1) == 0 && !recovery_mount("usb")) + found |= recovery_try(); +#endif + + fs_close(); +#ifndef CONFIG_ENV_IS_IN_NAND + spi_flash_free(recovery_flash); +#endif + + return found ? 0 : -1; +} +#else +int firmware_recovery(void) { + return -1; +} +#endif + +int firmware_scan(void) { +#ifdef CONFIG_ENV_IS_IN_NAND + u8 buf[64]; + int ret; + + printf("Checking UBI firmware...\n"); + + /* Only attach when the UBI actually exists, so we never create one */ + if (!ubi_marker_present()) { + printf("Firmware is absent/corrupt (no UBI)\n"); + env_set("bootfail", "1"); + return 0; + } + + if (ubi_part("ubi", NULL)) { + printf("Firmware is absent/corrupt (cannot attach UBI)\n"); + env_set("bootfail", "1"); + return 0; + } + + /* The kernel is a file in the UBIFS rootfs (/boot/fitImage or + * /boot/uImage): there is no kernel volume to check. */ + ret = ubi_volume_read("rootfs", (char *)buf, sizeof(buf)); + if (ret || !check_ubifs(buf)) { + printf("Firmware is absent/corrupt (rootfs volume)\n"); + env_set("bootfail", "1"); + return 0; + } + + /* A rootfs without a kernel in it boots nothing: hand it to recovery + * like a missing one. loadkernel mounts the volume again itself. */ + if (cmd_ubifs_mount("ubi0:rootfs")) { + printf("Firmware is absent/corrupt (cannot mount rootfs)\n"); + env_set("bootfail", "1"); + return 0; + } + ret = ubifs_exists("/boot/fitImage") || ubifs_exists("/boot/uImage"); + cmd_ubifs_umount(); + if (!ret) { + printf("Firmware is absent/corrupt (no /boot/fitImage or /boot/uImage)\n"); + env_set("bootfail", "1"); + return 0; + } + + env_set("bootfail", NULL); + + return 0; +#else + struct spi_flash *flash; + ulong kernel_off = 0; + ulong rootfs_off = 0; + uint64_t kernel_size = 0; + uint64_t rootfs_size = 0; + + printf("Automatic flash layout detection...\n"); + + flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); + if (!flash) { + printf("Failed to probe SPI flash at %u:%u\n", CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS); + return -1; + } + + scan_spi_device(flash, &kernel_off, &rootfs_off, &kernel_size, &rootfs_size); + +#ifndef CONFIG_TARGET_HI3516CV610_FAMILY + if (kernel_size <= 0x200000) + kernel_size = 0x200000; + if (rootfs_size < 0x500000) + rootfs_size = 0x500000; + else if (rootfs_size > 0x500000 && flash->size > 0x800000) + rootfs_size = 0xa00000; +#endif + + spi_flash_free(flash); + + if (kernel_off) { + char str[32]; + sprintf(str, "0x%08lx", kernel_off); + env_set("kernaddr", str); + + sprintf(str, "0x%llx", kernel_size); + env_set("kernsize", str); + } + + if (rootfs_off) { + char str[32]; + sprintf(str, "0x%08lx", rootfs_off); + env_set("rootaddr", str); + + sprintf(str, "0x%llx", rootfs_size); + env_set("rootsize", str); + + char mtds[128], mtdparts[160]; + const char *mtdids = env_get("mtdids"); + /* + * An environment saved by an earlier OpenIPC U-Boot has no mtdids: its + * bootargs carried "mtdparts=${mtdparts}" with the controller name + * spelled out. Without one the kernel gets "mtdparts=:...", matches no + * flash, and cannot mount /dev/mtdblock3. Name the controller this + * build drives, as its own default environment does. + */ +#ifdef SFC + if (!mtdids || !*mtdids) + mtdids = SFC; +#endif + sprintf(mtds, "%dk(u-boot),%dk(env),%lldk(kernel),%lldk(rootfs),%lldk@0x%lx(firmware),-(rootfs_data)", + CONFIG_ENV_OFFSET / 1024, CONFIG_ENV_SIZE / 1024, kernel_size / 1024, rootfs_size / 1024, + (kernel_size + rootfs_size) / 1024, kernel_off); + env_set("mtds", mtds); + sprintf(mtdparts, "%s:%s", mtdids ? mtdids : "", mtds); + env_set("mtdparts", mtdparts); + } + + if (kernel_off && rootfs_off) { + env_set("bootfail", NULL); + } else { + printf("Firmware is absent/corrupt (kernel=0x%lx, rootfs=0x%lx)\n", kernel_off, rootfs_off); + env_set("bootfail", "1"); + } + + return 0; +#endif +} + +#ifndef OPENIPC_RAM_MAX_SIZE +#ifdef PHYS_SDRAM_1_SIZE +#define OPENIPC_RAM_MAX_SIZE PHYS_SDRAM_1_SIZE +#endif +#endif + +#if defined(OPENIPC_RAM_MAX_SIZE) && defined(CONFIG_SYS_SDRAM_BASE) +ulong openipc_ram_size(void) +{ + return get_ram_size((long *)CONFIG_SYS_SDRAM_BASE, OPENIPC_RAM_MAX_SIZE); +} +#endif + +/* Check whether the device environment area is still blank (donor-style + * CRC check): only then it is safe to write the default environment. */ +static int env_crc_is_blank(void) { + u32 crc; + +#if defined(CONFIG_ENV_IS_IN_SPI_FLASH) + struct spi_flash *flash; + u8 *buf; + + flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); + if (!flash) + return 0; + + /* Cache-aligned: the DMA path invalidates cache lines around the + * buffer and a plain malloc buffer can share lines with heap metadata */ + buf = malloc_cache_aligned(CONFIG_ENV_SIZE); + if (!buf) { + spi_flash_free(flash); + return 0; + } + + if (spi_flash_read(flash, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf)) { + free(buf); + spi_flash_free(flash); + return 0; + } + + memcpy(&crc, buf, sizeof(crc)); + free(buf); + spi_flash_free(flash); +#elif defined(CONFIG_ENV_IS_IN_NAND) + struct mtd_info *mtd = get_nand_dev_by_index(0); + size_t len = sizeof(crc); + + if (!mtd) + return 0; + + if (nand_read_skip_bad(mtd, CONFIG_ENV_OFFSET, &len, NULL, mtd->size, + (u_char *)&crc)) + return 0; + + if (len != sizeof(crc)) + return 0; +#else + return 0; +#endif + + return crc == 0xffffffff; +} + +/* An environment saved by an earlier build of this tree carries the retired + * boot-limit command, which reflashes instead of entering failsafe (see + * OPENIPC_ALTBOOTCMD). Replace exactly that one, once; a command anyone + * edited is left alone. */ +static void altbootcmd_migrate(void) { + const char *s = env_get("altbootcmd"); + + if (!s || strcmp(s, OPENIPC_ALTBOOTCMD_RETIRED)) + return; + printf("altbootcmd: retired reflash command replaced with failsafe\n"); + env_set("altbootcmd", OPENIPC_ALTBOOTCMD); + env_save(); +} + +int openipc_helper(void) { + char msize[16]; + sprintf(msize, "%ldM", gd->ram_size / 1024 / 1024); + env_set("totalmem", msize); + firmware_recovery(); + firmware_scan(); + altbootcmd_migrate(); + + /* Persist the default environment on first start (or after a bad env); + * only if the device env area is still blank so we never overwrite + * foreign data (e.g. a RAM-booted U-Boot on another device). */ + if ((gd->flags & GD_FLG_ENV_DEFAULT) && env_crc_is_blank()) + env_save(); + + return 0; +} diff --git a/include/openipc.h b/include/openipc.h new file mode 100644 index 000000000000..b8ccfe058431 --- /dev/null +++ b/include/openipc.h @@ -0,0 +1,7 @@ +#ifndef _OPENIPC_H +#define _OPENIPC_H + +unsigned long openipc_ram_size(void); +int openipc_helper(void); + +#endif \ No newline at end of file From e02d97a41ee6a815247ff3b5f741beec408b35b5 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov <7954832+dimerr@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:20:06 +0000 Subject: [PATCH 2/8] lzma: build LzmaTools for the SPL too LzmaTools.c was guarded by CONFIG_LZMA, so with SPL_LZMA alone the SPL's spl_load_legacy_img() did not link (undefined lzmaBuffToBuffDecompress). CONFIG_IS_ENABLED(LZMA), as later mainline has it. --- lib/lzma/LzmaTools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lzma/LzmaTools.c b/lib/lzma/LzmaTools.c index af88900d315f..e5d19295e34d 100644 --- a/lib/lzma/LzmaTools.c +++ b/lib/lzma/LzmaTools.c @@ -22,7 +22,7 @@ #include #include -#ifdef CONFIG_LZMA +#if CONFIG_IS_ENABLED(LZMA) #define LZMA_PROPERTIES_OFFSET 0 #define LZMA_SIZE_OFFSET LZMA_PROPS_SIZE From f644f9d1e7af229668b94225e69dd4101c12d934 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov <7954832+dimerr@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:20:06 +0000 Subject: [PATCH 3/8] arm: sunxi: add the Allwinner V831/V833 (sun8iw19) One Cortex-A7 with the H6's peripherals, clocks and memory map, and the DDR in the package (V831: 64 MiB DDR2, V833: 128 MiB DDR3): a new SoC, MACH_SUN8I_V831, selecting SUN50I_GEN_H6, with its own SPL in place of the vendor's boot0, OP-TEE and boot package. - DRAM: dram_sun8i_v831.c is the vendor boot0's DRAM library (libdram, shipped as a binary object), translated to C function for function and register write for register write. It drives the D1/T113 controller of mainline's dram_sun20i_d1.c, at 0x04002000/0x04003000, in an older revision of the library (timings computed and stored back, DDR2/DDR3 scan). Checked under QEMU against the vendor's boot0 on a camera's flash: with the V831's and with the V833's parameters every DRAM controller, PHY, PLL_DDR, MBUS, DRAM clock and ZQ register is equal after both. The size it finds is checked with get_ram_size(). - Clocks as boot0 sets them: PLL_PERI0 1.2 GHz (2X), AHB 200, APB1 100 MHz, the MBUS reset released (its clock follows the DRAM's), CPU 816 MHz after the DDR, as the vendor U-Boot sets it. - SPL from SPI NOR: the H6's SPI0 on the H3's pins (PC0-PC3, function 4), and an LZMA legacy image (U-Boot does not fit the 256 KiB boot partition unpacked). The images boot from SPI NOR alone, so a valid eGON header means SPI: QEMU's boot ROM does not write the boot-media byte. - UART0 on PH9/PH10 (function 5). The SPL's BSS inside 64 MiB. - PSCI 1.0 for the vendor kernels, which expect OP-TEE's: version, features, CPU_OFF, SYSTEM_RESET (the watchdog). One core, no CPU_ON. - U-Boot's device tree (sun8i-v831.dtsi): the CCU is the H6's for every gate U-Boot uses (vendor clk-sun8iw19.h), so it maps to the H6 table; a V831 pinctrl table; SPI as the H3's, MMC as the H6's, the EMAC as the A64's (no internal PHY). - No binman for this SoC: it needs pylibfdt, which current setuptools no longer builds for 2022.10; build.sh puts the image together. --- arch/arm/Kconfig | 2 +- arch/arm/cpu/armv7/sunxi/Makefile | 4 + arch/arm/cpu/armv7/sunxi/psci_v831.c | 80 ++ arch/arm/dts/Makefile | 2 + arch/arm/dts/sun8i-v831-openipc.dts | 75 ++ arch/arm/dts/sun8i-v831.dtsi | 209 ++++ .../include/asm/arch-sunxi/clock_sun50i_h6.h | 12 + arch/arm/include/asm/arch-sunxi/dram.h | 2 + .../include/asm/arch-sunxi/dram_sun8i_v831.h | 15 + arch/arm/include/asm/arch-sunxi/gpio.h | 1 + arch/arm/mach-sunxi/Kconfig | 110 +- arch/arm/mach-sunxi/Makefile | 1 + arch/arm/mach-sunxi/board.c | 13 + arch/arm/mach-sunxi/clock_sun50i_h6.c | 16 +- arch/arm/mach-sunxi/cpu_info.c | 2 + arch/arm/mach-sunxi/dram_sun8i_v831.c | 1069 +++++++++++++++++ arch/arm/mach-sunxi/spl_spi_sunxi.c | 38 +- common/spl/Kconfig | 1 + drivers/clk/sunxi/Kconfig | 2 +- drivers/clk/sunxi/clk_sunxi.c | 2 + drivers/pinctrl/sunxi/Kconfig | 5 + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 23 + 22 files changed, 1668 insertions(+), 16 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/psci_v831.c create mode 100644 arch/arm/dts/sun8i-v831-openipc.dts create mode 100644 arch/arm/dts/sun8i-v831.dtsi create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun8i_v831.h create mode 100644 arch/arm/mach-sunxi/dram_sun8i_v831.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 82cd456f51f1..c119552ad539 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1144,7 +1144,7 @@ config ARCH_SOCFPGA config ARCH_SUNXI bool "Support sunxi (Allwinner) SoCs" - select BINMAN + select BINMAN if !MACH_SUN8I_V831 select CMD_GPIO select CMD_MMC if MMC select CMD_USB if DISTRO_DEFAULTS && USB_HOST diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 3e975b366c43..a2be669cad63 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -13,8 +13,12 @@ obj-$(CONFIG_MACH_SUN6I) += sram.o obj-$(CONFIG_MACH_SUN8I) += sram.o ifndef CONFIG_SPL_BUILD +ifdef CONFIG_MACH_SUN8I_V831 +obj-$(CONFIG_ARMV7_PSCI) += psci_v831.o +else obj-$(CONFIG_ARMV7_PSCI) += psci.o endif +endif ifdef CONFIG_SPL_BUILD obj-y += fel_utils.o diff --git a/arch/arm/cpu/armv7/sunxi/psci_v831.c b/arch/arm/cpu/armv7/sunxi/psci_v831.c new file mode 100644 index 000000000000..faab1848e480 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/psci_v831.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PSCI for the Allwinner V831/V833: one core, so no CPU_ON. What the + * vendor kernels ask for is the version, SYSTEM_RESET (the watchdog) and, + * for poweroff, SYSTEM_OFF (the core parks; there is no PMIC to cut). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +u32 __secure psci_version(void) +{ + return ARM_PSCI_VER_1_0; +} + +s32 __secure psci_features(u32 function_id, u32 psci_fid) +{ + switch (psci_fid) { + case ARM_PSCI_0_2_FN_PSCI_VERSION: + case ARM_PSCI_0_2_FN_CPU_OFF: + case ARM_PSCI_0_2_FN_AFFINITY_INFO: + case ARM_PSCI_0_2_FN_MIGRATE_INFO_TYPE: + case ARM_PSCI_0_2_FN_SYSTEM_OFF: + case ARM_PSCI_0_2_FN_SYSTEM_RESET: + return 0x0; + } + return ARM_PSCI_RET_NI; +} + +s32 __secure psci_affinity_info(u32 function_id, u32 target_affinity, + u32 lowest_affinity_level) +{ + /* the one core, MPIDR affinity 0 */ + if (lowest_affinity_level > 0 || (target_affinity & 0xffffff)) + return ARM_PSCI_RET_INVAL; + + return 0; /* ON */ +} + +u32 __secure psci_migrate_info_type(void) +{ + return 2; /* no trusted OS */ +} + +s32 __secure psci_cpu_off(void) +{ + psci_cpu_off_common(); + + while (1) + wfi(); +} + +void __secure psci_system_reset(void) +{ + struct sunxi_wdog *wdog = + ((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->wdog; + + writel(WDT_CFG_RESET, &wdog->cfg); + writel(WDT_MODE_EN, &wdog->mode); + writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->ctl); + + while (1) + wfi(); +} + +void __secure psci_system_off(void) +{ + while (1) + wfi(); +} + +void __secure psci_arch_init(void) +{ +} diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 965895bc2a3c..c83f4fe32e0e 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -686,6 +686,8 @@ dtb-$(CONFIG_MACH_SUN8I_V3S) += \ sun8i-s3-pinecube.dtb \ sun8i-v3-sl631-imx179.dtb \ sun8i-v3s-licheepi-zero.dtb +dtb-$(CONFIG_MACH_SUN8I_V831) += \ + sun8i-v831-openipc.dtb dtb-$(CONFIG_MACH_SUN50I_H5) += \ sun50i-h5-bananapi-m2-plus.dtb \ sun50i-h5-emlid-neutis-n5-devboard.dtb \ diff --git a/arch/arm/dts/sun8i-v831-openipc.dts b/arch/arm/dts/sun8i-v831-openipc.dts new file mode 100644 index 000000000000..2bac4eb2f1de --- /dev/null +++ b/arch/arm/dts/sun8i-v831-openipc.dts @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * A V831/V833 IP camera as the vendor's reference design wires it: + * console on UART0 (PH9/PH10), SPI NOR on SPI0 (PC0-PC3), the SD card on + * SMHC0 (PF0-PF5), an RMII PHY on the EMAC (PD), clocked by the SoC's + * 25 MHz output and reset on PD19. + */ + +/dts-v1/; + +#include "sun8i-v831.dtsi" + +#include + +/ { + model = "OpenIPC V831/V833"; + compatible = "openipc,v83x", "allwinner,sun8i-v831"; + + aliases { + ethernet0 = &emac; + serial0 = &uart0; + spi0 = &spi0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; +}; + +&emac { + pinctrl-names = "default"; + pinctrl-0 = <&rmii_pins>; + phy-mode = "rmii"; + phy-handle = <&rmii_phy>; + allwinner,tx-delay-ps = <700>; + allwinner,rx-delay-ps = <3100>; + snps,reset-gpio = <&pio 3 19 GPIO_ACTIVE_HIGH>; /* PD19 */ + snps,reset-active-low; + snps,reset-delays-us = <0 10000 50000>; + status = "okay"; +}; + +&mdio { + /* + * No address: the boards strap their PHYs differently, so the MDIO + * bus is scanned, as the vendor driver does + */ + rmii_phy: ethernet-phy { + compatible = "ethernet-phy-ieee802.3-c22"; + }; +}; + +&mmc0 { + bus-width = <4>; + broken-cd; + status = "okay"; +}; + +&spi0 { + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <50000000>; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_ph_pins>; + status = "okay"; +}; diff --git a/arch/arm/dts/sun8i-v831.dtsi b/arch/arm/dts/sun8i-v831.dtsi new file mode 100644 index 000000000000..8f402c8767fd --- /dev/null +++ b/arch/arm/dts/sun8i-v831.dtsi @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Allwinner V831/V833 (sun8iw19p1): one Cortex-A7 with the H6's + * peripherals, clocks and memory map. The clock and reset indices are the + * H6's: the V831's CCU has the same bus gates at the same offsets (the + * vendor kernel's clk-sun8iw19.h). + */ + +#include +#include +#include +#include +#include + +/ { + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,cortex-a7"; + device_type = "cpu"; + reg = <0>; + }; + }; + + osc24M: osc24M_clk { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "osc24M"; + }; + + osc32k: osc32k_clk { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "osc32k"; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupts = , + , + , + ; + clock-frequency = <24000000>; + arm,cpu-registers-not-fw-configured; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + syscon: syscon@3000000 { + compatible = "allwinner,sun8i-v831-system-control", + "allwinner,sun50i-a64-system-control"; + reg = <0x03000000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + }; + + ccu: clock@3001000 { + compatible = "allwinner,sun8i-v831-ccu"; + reg = <0x03001000 0x1000>; + clocks = <&osc24M>, <&osc32k>; + clock-names = "hosc", "losc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + pio: pinctrl@300b000 { + compatible = "allwinner,sun8i-v831-pinctrl"; + reg = <0x0300b000 0x400>; + clocks = <&ccu CLK_APB1>, <&osc24M>, <&osc32k>; + clock-names = "apb", "hosc", "losc"; + gpio-controller; + #gpio-cells = <3>; + interrupt-controller; + #interrupt-cells = <3>; + + mmc0_pins: mmc0-pins { + pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; + function = "mmc0"; + drive-strength = <30>; + bias-pull-up; + }; + + rmii_pins: rmii-pins { + pins = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", + "PD7", "PD8", "PD18", "PD20", "PD21"; + function = "emac"; + drive-strength = <20>; + }; + + spi0_pins: spi0-pins { + pins = "PC0", "PC1", "PC2", "PC3"; + function = "spi0"; + }; + + uart0_ph_pins: uart0-ph-pins { + pins = "PH9", "PH10"; + function = "uart0"; + }; + }; + + gic: interrupt-controller@3021000 { + compatible = "arm,gic-400"; + reg = <0x03021000 0x1000>, + <0x03022000 0x2000>, + <0x03024000 0x2000>, + <0x03026000 0x2000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + }; + + mmc0: mmc@4020000 { + compatible = "allwinner,sun8i-v831-mmc", + "allwinner,sun50i-h6-mmc"; + reg = <0x04020000 0x1000>; + clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC0>; + reset-names = "ahb"; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins>; + max-frequency = <150000000>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + uart0: serial@5000000 { + compatible = "snps,dw-apb-uart"; + reg = <0x05000000 0x400>; + interrupts = ; + reg-shift = <2>; + reg-io-width = <4>; + clocks = <&ccu CLK_BUS_UART0>; + resets = <&ccu RST_BUS_UART0>; + status = "disabled"; + }; + + spi0: spi@5010000 { + compatible = "allwinner,sun8i-v831-spi", + "allwinner,sun8i-h3-spi"; + reg = <0x05010000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; + clock-names = "ahb", "mod"; + resets = <&ccu RST_BUS_SPI0>; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + emac: ethernet@5020000 { + compatible = "allwinner,sun8i-v831-emac", + "allwinner,sun50i-a64-emac"; + syscon = <&syscon>; + reg = <0x05020000 0x10000>; + interrupts = ; + interrupt-names = "macirq"; + resets = <&ccu RST_BUS_EMAC>; + reset-names = "stmmaceth"; + clocks = <&ccu CLK_BUS_EMAC>; + clock-names = "stmmaceth"; + status = "disabled"; + + mdio: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + r_ccu: clock@7010000 { + compatible = "allwinner,sun50i-h6-r-ccu"; + reg = <0x07010000 0x400>; + clocks = <&osc24M>, <&osc32k>, <&osc32k>, + <&ccu CLK_PLL_PERIPH0>; + clock-names = "hosc", "losc", "iosc", "pll-periph"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + r_pio: pinctrl@7022000 { + compatible = "allwinner,sun50i-h6-r-pinctrl"; + reg = <0x07022000 0x400>; + clocks = <&r_ccu CLK_R_APB1>, <&osc24M>, <&osc32k>; + clock-names = "apb", "hosc", "losc"; + gpio-controller; + #gpio-cells = <3>; + interrupt-controller; + #interrupt-cells = <3>; + }; + }; +}; diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h index 37df4410eaae..b21c0bccaa44 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h @@ -274,6 +274,18 @@ struct sunxi_ccm_reg { /* apb1 bit field */ #define CCM_APB1_DEFAULT 0x03000102 +#elif CONFIG_MACH_SUN8I_V831 +/* PLL_PERI0(2X) 1200 MHz, as boot0 sets it */ +#define CCM_PLL6_DEFAULT 0xa0003100 + +/* psi_ahb1_ahb2 bit field: PLL_PERI0(1X) / 3 = 200 MHz */ +#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 + +/* ahb3 bit field: 200 MHz */ +#define CCM_AHB3_DEFAULT 0x03000002 + +/* apb1 bit field: 100 MHz */ +#define CCM_APB1_DEFAULT 0x03000102 #elif CONFIG_MACH_SUN50I_H616 #define CCM_PLL6_DEFAULT 0xa8003100 diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 682daae6b1ae..8eb3646edb18 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -33,6 +33,8 @@ #include #elif defined(CONFIG_MACH_SUNIV) #include +#elif defined(CONFIG_MACH_SUN8I_V831) +#include #else #include #endif diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun8i_v831.h b/arch/arm/include/asm/arch-sunxi/dram_sun8i_v831.h new file mode 100644 index 000000000000..ed72b5b0274c --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dram_sun8i_v831.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Allwinner V831/V833 (sun8iw19p1) DRAM controller + */ + +#ifndef _SUNXI_DRAM_SUN8I_V831_H +#define _SUNXI_DRAM_SUN8I_V831_H + +/* dram_type, as the vendor's parameters give it */ +#define SUNXI_DRAM_V831_DDR2 2 +#define SUNXI_DRAM_V831_DDR3 3 +#define SUNXI_DRAM_V831_LPDDR2 6 +#define SUNXI_DRAM_V831_LPDDR3 7 + +#endif /* _SUNXI_DRAM_SUN8I_V831_H */ diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 437e86479ced..a287307701a5 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -187,6 +187,7 @@ enum sunxi_gpio_number { #define SUN9I_GPH_UART0 2 #define SUN50I_H6_GPH_UART0 2 #define SUN50I_H616_GPH_UART0 2 +#define SUN8I_V831_GPH_UART0 5 #define SUNXI_GPI_SDC3 2 diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 571257618442..fb930a0ccf89 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -51,6 +51,11 @@ config DRAM_SUN50I_H616 Select this dram controller driver for some sun50i platforms, like H616. +config DRAM_SUN8I_V831 + bool + help + Select this dram controller driver for the V831/V833 (sun8iw19). + if DRAM_SUN50I_H616 config DRAM_SUN50I_H616_WRITE_LEVELING bool "H616 DRAM write leveling" @@ -130,8 +135,8 @@ config SUNXI_GEN_SUN6I config SUN50I_GEN_H6 bool - select FIT - select SPL_LOAD_FIT + select FIT if ARM64 + select SPL_LOAD_FIT if ARM64 select MMC_SUNXI_HAS_NEW_MODE select SUPPORT_SPL ---help--- @@ -312,6 +317,20 @@ config MACH_SUN8I_V3S select SUPPORT_SPL select ARMV7_BOOT_SEC_DEFAULT if OLD_SUNXI_KERNEL_COMPAT +config MACH_SUN8I_V831 + bool "sun8i (Allwinner V831/V833)" + select CPU_V7A + select CPU_V7_HAS_NONSEC + select CPU_V7_HAS_VIRT + select ARCH_SUPPORT_PSCI + select SPL_ARMV7_SET_CORTEX_SMPEN + select DRAM_SUN8I_V831 + select SUN50I_GEN_H6 + help + The V831 and V833 (sun8iw19): one Cortex-A7 and the H6's + peripherals, clocks and memory map, with DDR2 (V831, 64 MiB) or + DDR3 (V833, 128 MiB) in the package. + config MACH_SUN9I bool "sun9i (Allwinner A80)" select CPU_V7A @@ -461,6 +480,7 @@ config DRAM_TYPE config DRAM_CLK int "sunxi dram clock speed" default 792 if MACH_SUN9I + default 528 if MACH_SUN8I_V831 default 648 if MACH_SUN8I_R40 default 312 if MACH_SUN6I || MACH_SUN8I default 360 if MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || \ @@ -489,6 +509,7 @@ config DRAM_ZQ MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_A83T default 127 if MACH_SUN7I default 14779 if MACH_SUN8I_V3S + default 3065 if MACH_SUN8I_V831 default 3881979 if MACH_SUNXI_H3_H5 || MACH_SUN8I_R40 || MACH_SUN50I_H6 default 4145117 if MACH_SUN9I default 3881915 if MACH_SUN50I @@ -506,6 +527,86 @@ config DRAM_ODT_EN ---help--- Select this to enable dram odt (on die termination). +if DRAM_SUN8I_V831 +# The vendor's dram_para words (boot0 header, sys_config.fex [dram_para]). +# The defaults are the V831's (DDR2 in the package); a V833 board config +# overrides them with the V833's (DDR3). +config DRAM_SUN8I_V831_TYPE + int "DRAM type (2: DDR2, 3: DDR3)" + default 2 + +config DRAM_SUN8I_V831_ODT_EN + hex "dram_odt_en" + default 0x0 + +config DRAM_SUN8I_V831_PARA1 + hex "dram_para1" + default 0xd2 + +config DRAM_SUN8I_V831_PARA2 + hex "dram_para2" + default 0x0 + +config DRAM_SUN8I_V831_MR0 + hex "dram_mr0" + default 0xe73 + +config DRAM_SUN8I_V831_MR1 + hex "dram_mr1" + default 0x2 + +config DRAM_SUN8I_V831_MR2 + hex "dram_mr2" + default 0x0 + +config DRAM_SUN8I_V831_MR3 + hex "dram_mr3" + default 0x0 + +config DRAM_SUN8I_V831_TPR0 + hex "dram_tpr0" + default 0x00471992 + +config DRAM_SUN8I_V831_TPR1 + hex "dram_tpr1" + default 0x0131a10c + +config DRAM_SUN8I_V831_TPR2 + hex "dram_tpr2" + default 0x00057041 + +config DRAM_SUN8I_V831_TPR3 + hex "dram_tpr3" + default 0xb4787896 + +config DRAM_SUN8I_V831_TPR5 + hex "dram_tpr5" + default 0x48484848 + +config DRAM_SUN8I_V831_TPR6 + hex "dram_tpr6" + default 0x48 + +config DRAM_SUN8I_V831_TPR7 + hex "dram_tpr7" + default 0x1621121e + +config DRAM_SUN8I_V831_TPR11 + hex "dram_tpr11" + default 0x00120000 + +config DRAM_SUN8I_V831_TPR12 + hex "dram_tpr12" + default 0x23 + +config DRAM_SUN8I_V831_TPR13 + hex "dram_tpr13" + default 0x34000903 + help + Feature bits of the vendor DRAM library. Bit 0 set: the geometry + in dram_para1/2 is used as it is, no scan. +endif + if MACH_SUN4I || MACH_SUN5I || MACH_SUN7I config DRAM_EMR1 int "sunxi dram emr1 value" @@ -592,6 +693,7 @@ config SYS_CLK_FREQ default 1008000000 if MACH_SUN6I default 912000000 if MACH_SUN7I default 816000000 if MACH_SUN50I || MACH_SUN50I_H5 + default 816000000 if MACH_SUN8I_V831 default 1008000000 if MACH_SUN8I default 1008000000 if MACH_SUN9I default 888000000 if MACH_SUN50I_H6 @@ -604,6 +706,7 @@ config SYS_CONFIG_NAME default "sun6i" if MACH_SUN6I default "sun7i" if MACH_SUN7I default "sun8i" if MACH_SUN8I + default "v83x" if MACH_SUN8I_V831 default "sun9i" if MACH_SUN9I default "sun50i" if MACH_SUN50I default "sun50i" if MACH_SUN50I_H6 @@ -992,13 +1095,14 @@ config SPL_STACK_R_ADDR default 0x4fe00000 if MACH_SUN6I default 0x4fe00000 if MACH_SUN7I default 0x4fe00000 if MACH_SUN8I + default 0x43e00000 if MACH_SUN8I_V831 default 0x2fe00000 if MACH_SUN9I default 0x4fe00000 if MACH_SUN50I default 0x4fe00000 if SUN50I_GEN_H6 config SPL_SPI_SUNXI bool "Support for SPI Flash on Allwinner SoCs in SPL" - depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 || MACH_SUN50I || MACH_SUN8I_R40 || MACH_SUN50I_H6 || MACH_SUNIV + depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 || MACH_SUN50I || MACH_SUN8I_R40 || MACH_SUN50I_H6 || MACH_SUNIV || MACH_SUN8I_V831 help Enable support for SPI Flash. This option allows SPL to read from sunxi SPI Flash. It uses the same method as the boot ROM, so does diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile index 58f807cb82d5..a5610de8ccc5 100644 --- a/arch/arm/mach-sunxi/Makefile +++ b/arch/arm/mach-sunxi/Makefile @@ -45,4 +45,5 @@ obj-$(CONFIG_DRAM_SUN50I_H6) += dram_sun50i_h6.o obj-$(CONFIG_DRAM_SUN50I_H6) += dram_timings/ obj-$(CONFIG_DRAM_SUN50I_H616) += dram_sun50i_h616.o obj-$(CONFIG_DRAM_SUN50I_H616) += dram_timings/ +obj-$(CONFIG_DRAM_SUN8I_V831) += dram_sun8i_v831.o endif diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index 8f7c894286dc..c71deb1b1107 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -135,6 +135,10 @@ static int gpio_init(void) sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H616_GPH_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H616_GPH_UART0); sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_V831) + sunxi_gpio_set_cfgpin(SUNXI_GPH(9), SUN8I_V831_GPH_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPH(10), SUN8I_V831_GPH_UART0); + sunxi_gpio_set_pull(SUNXI_GPH(10), SUNXI_GPIO_PULL_UP); #elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A83T) sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_A83T_GPB_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_A83T_GPB_UART0); @@ -278,6 +282,15 @@ uint32_t sunxi_get_boot_device(void) * binary over USB. If it is found, it determines where SPL was * read from. */ + /* + * The V831/V833 images boot from SPI NOR alone: an SPL with a valid + * header came from there, whatever the boot-media byte says (the + * boot ROM's stand-in in QEMU does not write it). + */ + if (IS_ENABLED(CONFIG_MACH_SUN8I_V831) && !IS_ENABLED(CONFIG_SPL_MMC) && + boot_source != SUNXI_INVALID_BOOT_SOURCE) + return BOOT_DEVICE_SPI; + switch (boot_source) { case SUNXI_INVALID_BOOT_SOURCE: return BOOT_DEVICE_BOARD; diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c index 7926394cf762..b97bc9515467 100644 --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c @@ -18,6 +18,13 @@ void clock_init_safe(void) setbits_le32(&prcm->res_cal_ctrl, 2); } + if (IS_ENABLED(CONFIG_MACH_SUN8I_V831)) { + /* boot0: the analog (ADDA) supply and the calibration circuit */ + setbits_le32(&prcm->sys_pwroff_gating, 0x10); + /* boot0: PLL_CPUX tuning step */ + clrsetbits_le32(SUNXI_CCM_BASE + 0x400, 0x7 << 28, 0x1 << 28); + } + clrbits_le32(&prcm->res_cal_ctrl, 1); setbits_le32(&prcm->res_cal_ctrl, 1); @@ -35,12 +42,19 @@ void clock_init_safe(void) ; clrsetbits_le32(&ccm->cpu_axi_cfg, CCM_CPU_AXI_APB_MASK | CCM_CPU_AXI_AXI_MASK, + IS_ENABLED(CONFIG_MACH_SUN8I_V831) ? 0x302 : CCM_CPU_AXI_DEFAULT_FACTORS); writel(CCM_PSI_AHB1_AHB2_DEFAULT, &ccm->psi_ahb1_ahb2_cfg); writel(CCM_AHB3_DEFAULT, &ccm->ahb3_cfg); writel(CCM_APB1_DEFAULT, &ccm->apb1_cfg); + if (IS_ENABLED(CONFIG_MACH_SUN8I_V831)) { + /* the MBUS runs at a quarter of the DRAM clock: no mux */ + writel(MBUS_RESET, &ccm->mbus_cfg); + return; + } + /* * The mux and factor are set, but the clock will be enabled in * DRAM initialization code. @@ -105,7 +119,7 @@ unsigned int clock_get_pll6(void) { struct sunxi_ccm_reg *const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - int m = IS_ENABLED(CONFIG_MACH_SUN50I_H6) ? 4 : 2; + int m = IS_ENABLED(CONFIG_MACH_SUN50I_H6) ? 4 : 2; /* H616, V831: 2X */ uint32_t rval = readl(&ccm->pll6_cfg); int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT) + 1; diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c index 7eef178859b8..afc238de82a1 100644 --- a/arch/arm/mach-sunxi/cpu_info.c +++ b/arch/arm/mach-sunxi/cpu_info.c @@ -103,6 +103,8 @@ int print_cpuinfo(void) puts("CPU: Allwinner H6 (SUN50I)\n"); #elif defined CONFIG_MACH_SUN50I_H616 puts("CPU: Allwinner H616 (SUN50I)\n"); +#elif defined CONFIG_MACH_SUN8I_V831 + puts("CPU: Allwinner V831/V833 (SUN8IW19)\n"); #else #warning Please update cpu_info.c with correct CPU information puts("CPU: SUNXI Family\n"); diff --git a/arch/arm/mach-sunxi/dram_sun8i_v831.c b/arch/arm/mach-sunxi/dram_sun8i_v831.c new file mode 100644 index 000000000000..7a4269586ef5 --- /dev/null +++ b/arch/arm/mach-sunxi/dram_sun8i_v831.c @@ -0,0 +1,1069 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Allwinner V831/V833 (sun8iw19p1) DRAM initialisation + * + * There is no documentation for this memory controller. This is a + * translation of the vendor's boot0 DRAM library (libdram) for the + * sun8iw19p1, function for function and register write for register write; + * the names are the library's own. The controller is the one of the later + * D1/T113 (see drivers/ram/sunxi/dram_sun20i_d1.c in mainline), at + * 0x04002000 (MC_COM) and 0x04003000 (MCTL + PHY), but this library is an + * older revision: the timing values are computed in auto_set_timing_para() + * and stored back into the parameters, and it can tell DDR2 from DDR3 by + * itself (auto_scan_dram_type()). + * + * Left out: the vendor's chip binding check (bond_id_check(), which makes + * the library refuse to run on a chip whose efuse does not match), the + * PMIC DRAM supply setting (dram_vol_set(); these packages have no PMIC + * on the reference boards) and the super standby resume path, which only + * runs when the PRCM says the SoC is waking from it. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MC_COM 0x04002000 +#define MCTL 0x04003000 +#define CCM SUNXI_CCM_BASE + +#define CCM_PLL_DDR (CCM + 0x010) +#define CCM_PLL_DDR_PAT (CCM + 0x110) +#define CCM_MBUS (CCM + 0x540) +#define CCM_DRAM_CLK (CCM + 0x800) +#define CCM_DRAM_BGR (CCM + 0x80c) + +/* the vendor's parameter block, as in boot0's header (dram_para[32]) */ +struct dram_para { + u32 clk; + u32 type; + u32 zq; + u32 odt_en; + u32 para1; + u32 para2; + u32 mr0; + u32 mr1; + u32 mr2; + u32 mr3; + u32 tpr0; + u32 tpr1; + u32 tpr2; + u32 tpr3; + u32 tpr4; + u32 tpr5; + u32 tpr6; + u32 tpr7; + u32 tpr8; + u32 tpr9; + u32 tpr10; + u32 tpr11; + u32 tpr12; + u32 tpr13; +}; + +static void dram_enable_all_master(void) +{ + writel(0xffffffff, MC_COM + 0x20); + writel(0x7f, MC_COM + 0x24); + writel(0xffff, MC_COM + 0x28); +} + +static void dram_disable_all_master(void) +{ + writel(1, MC_COM + 0x20); + writel(0, MC_COM + 0x24); + writel(0, MC_COM + 0x28); +} + +static void eye_delay_compensation(struct dram_para *para) +{ + u32 delay; + ulong ptr; + + /* DATn0IOCR, n = 0..7 */ + delay = ((para->tpr11 << 9) & 0x1e00) | ((para->tpr12 << 1) & 0x1e); + for (ptr = MCTL + 0x310; ptr != MCTL + 0x334; ptr += 4) + setbits_le32(ptr, delay); + + /* DATn1IOCR, n = 0..7 */ + delay = ((para->tpr11 << 5) & 0x1e00) | ((para->tpr12 >> 3) & 0x1e); + for (ptr = MCTL + 0x390; ptr != MCTL + 0x3b4; ptr += 4) + setbits_le32(ptr, delay); + + /* PGCR0: assert the AC loopback FIFO reset */ + clrbits_le32(MCTL + 0x100, BIT(26)); + + delay = (((para->tpr11 >> 16) << 9) & 0x1e00) | + ((para->tpr12 >> 15) & 0x1e); + setbits_le32(MCTL + 0x334, delay); + setbits_le32(MCTL + 0x338, delay); + + delay = (((para->tpr11 >> 20) << 9) & 0x1e00) | + ((para->tpr12 >> 19) & 0x1e); + setbits_le32(MCTL + 0x3b4, delay); + setbits_le32(MCTL + 0x3b8, delay); + + setbits_le32(MCTL + 0x33c, ((para->tpr11 >> 16) << 25) & 0x1e000000); + setbits_le32(MCTL + 0x3bc, ((para->tpr11 >> 20) << 25) & 0x1e000000); + + /* PGCR0: release the AC loopback FIFO reset */ + setbits_le32(MCTL + 0x100, BIT(26)); + + delay = (para->tpr10 << 4) & 0xf00; + for (ptr = MCTL + 0x240; ptr != MCTL + 0x27c; ptr += 4) + setbits_le32(ptr, delay); + + setbits_le32(MCTL + 0x218, (para->tpr10 << 8) & 0xf00); + setbits_le32(MCTL + 0x21c, para->tpr10 & 0xf00); + setbits_le32(MCTL + 0x280, (para->tpr10 >> 4) & 0xf00); +} + +/* cycles of the controller clock (DRAM clock / 2) in ns, rounded up */ +static u32 auto_cal_timing(u32 time_ns, u32 clk) +{ + u32 value = time_ns * clk; + + return value / 1000 + (value % 1000 ? 1 : 0); +} + +static u32 refresh_interval(u32 clk, u32 ns) +{ + u32 t = auto_cal_timing(ns, clk); + + return (t >> 5) + ((t & 0x1f) ? 1 : 0); +} + +static void auto_set_timing_para(struct dram_para *para) +{ + u32 clk = para->clk; + u32 ctrl_freq = clk / 2; + u32 type = para->type; + u32 tccd, trcd, tfaw, trrd, trc; + u32 txp, twtr, twr, trtp, trp, tras; + u32 trfc, trefi; + u32 tcksrx, tckesr, tcke, trd2wr, twr2rd, trasmax, twtp; + u32 tmod, tmrd, tmrw, tcl, tcwl, t_rdata_en, wr_latency; + u32 mr0, mr1, mr2, mr3; + u32 tdinit0, tdinit1, tdinit2, tdinit3; + u32 val; + + if (para->tpr13 & BIT(1)) { + /* timings given in tpr0..tpr2 */ + tccd = (para->tpr0 >> 21) & 0x7; + trcd = (para->tpr0 >> 6) & 0x1f; + tfaw = (para->tpr0 >> 15) & 0x3f; + trrd = (para->tpr0 >> 11) & 0xf; + trc = para->tpr0 & 0x3f; + txp = (para->tpr1 >> 23) & 0x1f; + twtr = (para->tpr1 >> 20) & 0x7; + twr = (para->tpr1 >> 11) & 0xf; + trtp = (para->tpr1 >> 15) & 0x1f; + trp = (para->tpr1 >> 6) & 0x1f; + tras = para->tpr1 & 0x3f; + trfc = (para->tpr2 >> 12) & 0x1ff; + trefi = para->tpr2 & 0xfff; + } else { + switch (type) { + case SUNXI_DRAM_V831_DDR3: + twtr = max(auto_cal_timing(8, ctrl_freq), 2U); + trcd = auto_cal_timing(15, ctrl_freq); + twr = max(trcd, 2U); + trefi = refresh_interval(ctrl_freq, 7800); + trfc = auto_cal_timing(350, ctrl_freq); + if (clk <= 800) { + tfaw = auto_cal_timing(50, ctrl_freq); + trrd = max(auto_cal_timing(10, ctrl_freq), 2U); + trc = auto_cal_timing(53, ctrl_freq); + tras = auto_cal_timing(38, ctrl_freq); + txp = twtr; + } else { + tfaw = auto_cal_timing(35, ctrl_freq); + trrd = max(auto_cal_timing(6, ctrl_freq), 2U); + trcd = auto_cal_timing(14, ctrl_freq); + trc = auto_cal_timing(48, ctrl_freq); + tras = auto_cal_timing(34, ctrl_freq); + txp = trrd; + } + trp = trcd; + break; + case SUNXI_DRAM_V831_DDR2: + tfaw = auto_cal_timing(50, ctrl_freq); + trrd = auto_cal_timing(10, ctrl_freq); + trcd = auto_cal_timing(20, ctrl_freq); + trc = auto_cal_timing(65, ctrl_freq); + twtr = auto_cal_timing(8, ctrl_freq); + trp = auto_cal_timing(15, ctrl_freq); + twr = trp; + tras = auto_cal_timing(45, ctrl_freq); + trefi = refresh_interval(ctrl_freq, 7800); + trfc = auto_cal_timing(328, ctrl_freq); + txp = 2; + break; + case SUNXI_DRAM_V831_LPDDR2: + tfaw = max(auto_cal_timing(50, ctrl_freq), 4U); + trrd = auto_cal_timing(10, ctrl_freq); + if (trrd == 0) + trrd = 1; + trcd = max(auto_cal_timing(24, ctrl_freq), 2U); + trc = auto_cal_timing(70, ctrl_freq); + txp = auto_cal_timing(8, ctrl_freq); + if (txp == 0) { + txp = 1; + twtr = 2; + } else { + twtr = max(txp, 2U); + } + twr = max(auto_cal_timing(15, ctrl_freq), 2U); + trp = auto_cal_timing(17, ctrl_freq); + tras = auto_cal_timing(42, ctrl_freq); + trefi = refresh_interval(ctrl_freq, 3900); + trfc = auto_cal_timing(210, ctrl_freq); + break; + case SUNXI_DRAM_V831_LPDDR3: + tfaw = max(auto_cal_timing(50, ctrl_freq), 4U); + trrd = auto_cal_timing(10, ctrl_freq); + if (trrd == 0) + trrd = 1; + trcd = max(auto_cal_timing(24, ctrl_freq), 2U); + trc = auto_cal_timing(70, ctrl_freq); + twtr = max(auto_cal_timing(8, ctrl_freq), 2U); + twr = max(auto_cal_timing(15, ctrl_freq), 2U); + trp = auto_cal_timing(17, ctrl_freq); + tras = auto_cal_timing(42, ctrl_freq); + trefi = refresh_interval(ctrl_freq, 3900); + trfc = auto_cal_timing(210, ctrl_freq); + txp = twtr; + break; + default: + trfc = 128; + trefi = 98; + txp = 10; + tfaw = 16; + tras = 14; + twtr = 3; + trp = 6; + twr = 8; + trc = 20; + trcd = 6; + trrd = 3; + break; + } + tccd = 2; + trtp = twtr; + + /* the library stores what it computed */ + para->tpr0 = trc | (trcd << 6) | (trrd << 11) | (tfaw << 15) | + (tccd << 21); + para->tpr1 = tras | (trp << 6) | (twr << 11) | (trtp << 15) | + (twtr << 20) | (txp << 23); + para->tpr2 = trefi | (trfc << 12); + } + + switch (type) { + case SUNXI_DRAM_V831_DDR2: + tmrd = 2; + tcwl = 3; + tcke = 3; + tckesr = 4; + trd2wr = 4; + tcksrx = 5; + tmrw = 0; + tmod = 12; + wr_latency = 1; + trasmax = clk / 30; + if (ctrl_freq < 205) { + t_rdata_en = 1; + tcl = 3; + mr0 = 0xa63; + } else { + t_rdata_en = 2; + tcl = 4; + mr0 = 0xe73; + } + mr1 = para->mr1; + mr2 = 0; + mr3 = 0; + tdinit0 = 400 * clk + 1; + tdinit1 = 500 * clk / 1000 + 1; + tdinit2 = 200 * clk + 1; + tdinit3 = clk + 1; + twtp = twr + 5; + twr2rd = twtr + 5; + break; + case SUNXI_DRAM_V831_DDR3: + tcwl = clk > 800 ? 5 : 4; + t_rdata_en = tcwl; + twr += 2; + twtr += 2; + tckesr = 4; + tcke = 3; + trasmax = clk / 30; + tcl = clk > 800 ? 7 : 6; + wr_latency = clk > 800 ? 3 : 2; + mr2 = clk > 800 ? 32 : 24; + mr0 = clk > 800 ? 0x1e14 : 0x1c70; + tdinit0 = 500 * clk + 1; + tdinit1 = 360 * clk / 1000 + 1; + tdinit2 = 200 * clk + 1; + tdinit3 = clk + 1; + twtp = twr + tcwl; + twr2rd = twtr + tcwl; + tcksrx = 5; + mr1 = para->mr1; + if (((para->tpr13 >> 2) & 0x3) == 1 || clk <= 912) + trd2wr = 5; + else + trd2wr = 6; + tmrd = 4; + tmod = 12; + mr3 = 0; + tmrw = 0; + break; + case SUNXI_DRAM_V831_LPDDR2: + mr2 = 6; + tcke = 2; + trd2wr = 10; + tcwl = 2; + tcl = 4; + wr_latency = 1; + trasmax = clk / 60; + tdinit0 = 200 * clk + 1; + tdinit1 = 100 * clk / 1000 + 1; + tcksrx = 5; + tckesr = 5; + tmrd = 5; + tmod = 5; + tdinit2 = 11 * clk + 1; + tdinit3 = clk + 1; + twtp = twr + 5; + twr2rd = twtr + 5; + mr3 = para->mr3; + tmrw = 3; + t_rdata_en = 3; + mr1 = 0xc3; + mr0 = 0; + break; + case SUNXI_DRAM_V831_LPDDR3: + tcwl = clk > 800 ? 4 : 3; + t_rdata_en = clk > 800 ? 6 : 5; + twr += 5; + twtr += 5; + trd2wr = 13; + trasmax = clk / 60; + wr_latency = clk > 800 ? 3 : 2; + mr2 = clk > 800 ? 12 : 10; + tdinit0 = 200 * clk + 1; + tdinit1 = 100 * clk / 1000 + 1; + tcksrx = 5; + tckesr = 5; + tdinit2 = 11 * clk + 1; + tdinit3 = clk + 1; + twtp = twr + tcwl; + twr2rd = twtr + tcwl; + tcl = clk > 800 ? 7 : 6; + tcke = 3; + mr3 = para->mr3; + tmrd = 5; + tmod = 12; + tmrw = 5; + mr1 = 0xc3; + mr0 = 0; + break; + default: + trasmax = 27; + twr2rd = 8; + twtp = 12; + tcke = 2; + tmod = 6; + tckesr = 3; + t_rdata_en = 1; + tcksrx = 4; + tmrd = 2; + tmrw = 0; + trd2wr = 4; + tcwl = 3; + tcl = 3; + wr_latency = 1; + mr0 = 0; + mr1 = 0; + mr2 = 0; + mr3 = 0; + tdinit0 = 0; + tdinit1 = 0; + tdinit2 = 0; + tdinit3 = 0; + break; + } + + if (trtp + trp < tcl + 2) + trtp = tcl + 2 - trp; + + /* a value given with bits set in the upper half wins */ + if (!(para->mr0 >> 16)) + para->mr0 = mr0; + if (!(para->mr1 >> 16)) + para->mr1 = mr1; + if (!(para->mr2 >> 16)) + para->mr2 = mr2; + if (!(para->mr3 >> 16)) + para->mr3 = mr3; + + writel(para->mr0 & 0xffff, MCTL + 0x030); + writel(para->mr1 & 0xffff, MCTL + 0x034); + writel(para->mr2 & 0xffff, MCTL + 0x038); + writel(para->mr3 & 0xffff, MCTL + 0x03c); + writel((para->odt_en >> 4) & 0x3, MCTL + 0x02c); + + /* DRAMTMG0..5 */ + writel(tras | (tfaw << 16) | (twtp << 24) | (trasmax << 8), + MCTL + 0x058); + writel(trc | (txp << 16) | (trtp << 8), MCTL + 0x05c); + writel(twr2rd | (tcl << 16) | (tcwl << 24) | (trd2wr << 8), + MCTL + 0x060); + writel(tmod | (tmrd << 12) | (tmrw << 16), MCTL + 0x064); + writel(trp | (trrd << 8) | (tccd << 16) | (trcd << 24), MCTL + 0x068); + writel(tcke | (tckesr << 8) | (tcksrx << 16) | (tcksrx << 24), + MCTL + 0x06c); + + /* dual rank timing */ + val = (readl(MCTL + 0x078) & 0x0fff0000) | 0xf0000010; + val |= clk <= 800 ? 0x6600 : 0x7600; + writel(val, MCTL + 0x078); + + /* PITMG0, PTR3, PTR4 */ + writel(wr_latency | BIT(8) | (t_rdata_en << 16) | (2 << 24), + MCTL + 0x080); + writel(tdinit0 | (tdinit1 << 20), MCTL + 0x050); + writel(tdinit2 | (tdinit3 << 20), MCTL + 0x054); + + /* refresh */ + writel(trfc | (trefi << 16), MCTL + 0x090); + writel((trefi << 15) & 0x0fff0000, MCTL + 0x094); +} + +static void ccm_set_pll_ddr_sscg(struct dram_para *para) +{ + u32 pat; + + if (readl(CCM_PLL_DDR) & BIT(1)) + setbits_le32(CCM_PLL_DDR_PAT, BIT(19)); + + switch ((para->tpr13 >> 20) & 0x7) { + case 1: + pat = 0xe486cccc; + break; + case 2: + pat = 0xe9069999; + break; + case 3: + pat = 0xed866666; + break; + case 5: + pat = 0xf5860000; + break; + default: + pat = 0xf2063333; + break; + } + writel(pat, CCM_PLL_DDR_PAT); + setbits_le32(CCM_PLL_DDR, BIT(24)); +} + +static u32 ccm_set_pll_ddr_clk(struct dram_para *para) +{ + u32 clk, n, val; + + clk = (para->tpr13 & BIT(6)) ? para->tpr9 : para->clk; + n = clk * 2 / 24; + + val = readl(CCM_PLL_DDR) & ~0x7f03; + val |= (max(n, 12U) - 1) << 8; + writel(val | BIT(31), CCM_PLL_DDR); + val &= ~BIT(29); + writel(val | BIT(31), CCM_PLL_DDR); + writel(val | BIT(31) | BIT(29), CCM_PLL_DDR); + while (!(readl(CCM_PLL_DDR) & BIT(28))) + ; + + if (para->tpr13 & BIT(23)) + ccm_set_pll_ddr_sscg(para); + + return n * 24; +} + +static void mctl_sys_init(struct dram_para *para) +{ + /* MBUS reset and gate */ + clrbits_le32(CCM_MBUS, BIT(31)); + clrbits_le32(CCM_MBUS, BIT(30)); + /* DRAM bus gate and reset */ + clrbits_le32(CCM_DRAM_BGR, BIT(0)); + clrbits_le32(CCM_DRAM_BGR, BIT(16)); + /* PLL_DDR off, its LDO on */ + clrsetbits_le32(CCM_PLL_DDR, BIT(31), BIT(30)); + clrbits_le32(CCM_DRAM_CLK, BIT(30)); + + para->clk = ccm_set_pll_ddr_clk(para) / 2; + + /* DRAM clock from PLL_DDR, M = 1, update */ + clrbits_le32(CCM_DRAM_CLK, 0x3000000); + clrbits_le32(CCM_DRAM_CLK, 0x3); + setbits_le32(CCM_DRAM_CLK, BIT(27)); + + setbits_le32(CCM_DRAM_BGR, BIT(16)); + setbits_le32(CCM_DRAM_BGR, BIT(0)); + + dram_disable_all_master(); + + setbits_le32(CCM_MBUS, BIT(30)); + setbits_le32(CCM_MBUS, BIT(31)); + setbits_le32(CCM_DRAM_CLK, BIT(30)); + + writel(0x8000, MCTL + 0x00c); +} + +static void mctl_com_init(struct dram_para *para) +{ + u32 val, n, i; + + clrsetbits_le32(MC_COM + 0x008, 0x3f00, 0x2000); + + val = readl(MC_COM + 0x000) & ~0x00fff000; + val |= BIT(22); + val |= (para->type << 16) & 0x70000; + if (!(para->para2 & BIT(0))) + val |= BIT(12); + if (para->type == SUNXI_DRAM_V831_LPDDR2 || + para->type == SUNXI_DRAM_V831_LPDDR3) + val |= BIT(19); + else + val |= (para->tpr13 << 14) & BIT(19); + writel(val, MC_COM + 0x000); + + /* rank0 and, with two different ranks, rank1 */ + if ((para->para2 & BIT(8)) && ((para->para2 >> 12) & 0xf) == 1) + n = 2; + else + n = 1; + + for (i = 0; i < n; i++) { + u32 sh = i * 16; + ulong reg = MC_COM + 0x000 + i * 4; + + val = readl(reg) & 0xfffff000; + val |= (para->para2 >> 12) & 0x3; + val |= ((para->para1 >> (sh + 12)) << 2) & 0x4; + val |= (((para->para1 >> (sh + 4)) - 1) << 4) & 0xff; + switch ((para->para1 >> sh) & 0xf) { + case 1: + val |= 0x700; + break; + case 2: + val |= 0x800; + break; + case 4: + val |= 0x900; + break; + case 8: + val |= 0xa00; + break; + default: + val |= 0x600; + break; + } + writel(val, reg); + } + + writel((readl(MC_COM + 0x000) & BIT(0)) ? 0x303 : 0x201, + MCTL + 0x120); + + if (para->para2 & BIT(0)) + writel(0, MCTL + 0x3c4); + + if (para->tpr4) { + setbits_le32(MC_COM + 0x000, (para->tpr4 << 25) & 0x06000000); + setbits_le32(MC_COM + 0x004, (para->tpr4 << 10) & 0x001ff000); + } +} + +static void mctl_phy_ac_remapping(struct dram_para *para) +{ + if (para->type != SUNXI_DRAM_V831_DDR3) + return; + + writel(0x00f4b0c0, MC_COM + 0x500); + writel(0x04e50267, MC_COM + 0x504); + writel(0x00ba5461, MC_COM + 0x508); + writel(0x24db4080, MC_COM + 0x50c); + writel(0x00f4b0c1, MC_COM + 0x500); +} + +static void mctl_vrefzq_init(struct dram_para *para) +{ + if (para->tpr13 & BIT(17)) + return; + + clrsetbits_le32(MCTL + 0x110, 0x7f7f7f7f, para->tpr5); + if (!(para->tpr13 & BIT(16))) + clrsetbits_le32(MCTL + 0x114, 0x7f, para->tpr6 & 0x7f); +} + +static int mctl_channel_init(struct dram_para *para) +{ + u32 dqs_gating_mode = (para->tpr13 >> 2) & 0x3; + u32 odt, val; + int ret; + + clrsetbits_le32(MC_COM + 0x00c, 0xfff, para->clk >> 1); + clrsetbits_le32(MCTL + 0x108, 0xf00, 0x300); + + odt = (~para->odt_en << 5) & BIT(5); + + /* DX0GCR0, DX1GCR0 */ + val = (readl(MCTL + 0x344) & ~0x30) | odt; + if (para->clk > 672) + val = (val & ~0xf61e) | 0x400; + else + val &= ~0xf01e; + writel(val, MCTL + 0x344); + + val = (readl(MCTL + 0x3c4) & ~0x30) | odt; + if (para->clk > 672) + val = (val & ~0xf61e) | 0x400; + else + val &= ~0xf01e; + writel(val, MCTL + 0x3c4); + + setbits_le32(MCTL + 0x208, BIT(1)); + + eye_delay_compensation(para); + + if (dqs_gating_mode == 1) { + clrbits_le32(MCTL + 0x108, 0xc0); + clrbits_le32(MCTL + 0x0bc, 0x107); + } else if (dqs_gating_mode == 2) { + clrsetbits_le32(MCTL + 0x108, 0xc0, 0x80); + clrsetbits_le32(MCTL + 0x0bc, 0x107, + (((readl(MCTL + 0x060) >> 16) & 0x1f) - 2) | + 0x100); + clrsetbits_le32(MCTL + 0x11c, BIT(31), BIT(27)); + } else { + clrbits_le32(MCTL + 0x108, 0x40); + setbits_le32(MCTL + 0x108, 0xc0); + } + + if (para->type == SUNXI_DRAM_V831_LPDDR2 || + para->type == SUNXI_DRAM_V831_LPDDR3) { + if (dqs_gating_mode == 1) + clrsetbits_le32(MCTL + 0x11c, 0x080000c0, BIT(31)); + else + clrsetbits_le32(MCTL + 0x11c, 0x77000000, 0x22000000); + } + + clrsetbits_le32(MCTL + 0x0c0, 0x0fffffff, + ((para->para2 & BIT(12)) ? 0x03000000 : 0x01000000) | + 1); + + /* ZQ */ + clrsetbits_le32(MCTL + 0x140, 0x03ffffff, + BIT(25) | (para->zq & 0x00ffffff)); + + if (dqs_gating_mode == 1) { + writel(0x52, MCTL + 0x000); + writel(0x53, MCTL + 0x000); + while (!(readl(MCTL + 0x010) & BIT(0))) + ; + val = para->type == SUNXI_DRAM_V831_DDR3 ? 0x5a0 : 0x520; + } else { + val = para->type == SUNXI_DRAM_V831_DDR3 ? 0x1f2 : 0x172; + } + /* PIR: the training steps, then go */ + writel(val, MCTL + 0x000); + writel(val | BIT(0), MCTL + 0x000); + while (!(readl(MCTL + 0x010) & BIT(0))) + ; + + /* PGSR0 error bits; ZQ calibration failing is fatal at once */ + val = readl(MCTL + 0x010); + if (val & BIT(20)) + return 0; + ret = !(val & 0x0ff00000); + + while (!(readl(MCTL + 0x018) & BIT(0))) + ; + + setbits_le32(MCTL + 0x08c, BIT(31)); + clrbits_le32(MCTL + 0x08c, BIT(31)); + setbits_le32(MC_COM + 0x014, BIT(31)); + clrbits_le32(MCTL + 0x10c, 0x06000000); + if (dqs_gating_mode == 1) + clrsetbits_le32(MCTL + 0x11c, 0xc0, 0x40); + + return ret; +} + +static int mctl_core_init(struct dram_para *para) +{ + mctl_sys_init(para); + mctl_vrefzq_init(para); + mctl_com_init(para); + mctl_phy_ac_remapping(para); + auto_set_timing_para(para); + + return mctl_channel_init(para); +} + +static u32 rank_size_mb(u32 reg) +{ + return 1U << (((reg >> 8) & 0xf) + ((reg >> 4) & 0xf) + + ((reg >> 2) & 0x3) - 14); +} + +static u32 dramc_get_dram_size(void) +{ + u32 reg0 = readl(MC_COM + 0x000); + u32 reg1; + u32 size0 = rank_size_mb(reg0); + + if (!(reg0 & 0x3)) + return size0; + + reg1 = readl(MC_COM + 0x004); + if (!(reg1 & 0x3)) + return size0 * 2; + + return size0 + rank_size_mb(reg1); +} + +/* after a training run in DQS probe mode: ranks and DQ width */ +static int dqs_gate_detect(struct dram_para *para) +{ + u32 dx0, dx1; + + if (!(readl(MCTL + 0x010) & BIT(22))) { + /* dual rank, full DQ */ + para->para2 = (para->para2 & ~0xf) | BIT(12); + return 1; + } + + dx0 = (readl(MCTL + 0x348) >> 24) & 0x3; + dx1 = (readl(MCTL + 0x3c8) >> 24) & 0x3; + + if (dx0 == 2) { + /* single rank, full or half DQ */ + para->para2 &= ~0xf00f; + if (dx1 != 2) + para->para2 |= BIT(0); + return 1; + } + + if (dx0 == 0) { + /* dual rank, half DQ */ + para->para2 = (para->para2 & ~0xf) | 0x1001; + return 1; + } + + return 0; +} + +static int dramc_simple_wr_test(u32 mem_mb, u32 len) +{ + ulong half = (mem_mb >> 1) << 20; + ulong base = CONFIG_SYS_SDRAM_BASE; + u32 i; + + for (i = 0; i < len; i++) { + writel(0x01234567 + i, base + i * 4); + writel(0xfedcba98 + i, base + half + i * 4); + } + + for (i = 0; i < len; i++) { + if (readl(base + half + i * 4) != 0xfedcba98 + i || + readl(base + i * 4) != 0x01234567 + i) { + printf("DRAM: simple test FAIL at %lx\n", base + i * 4); + return 1; + } + } + + return 0; +} + +static u32 scan_pattern(int odd, ulong addr) +{ + return odd ? addr : ~addr; +} + +/* 64 words at base + off read back as the pattern written at pat */ +static int scan_match(ulong addr, ulong pat) +{ + int i; + + for (i = 0; i < 64; i++) { + if (readl(addr + i * 4) != scan_pattern(i & 1, pat + i * 4)) + return 0; + } + + return 1; +} + +/* + * Rows, banks and page size per rank: set the address mapping wide, then + * see where the pattern at the start of the rank shows up again. + */ +static int auto_scan_dram_size(struct dram_para *para) +{ + u32 para2 = para->para2; + u32 maxrank = ((para2 >> 12) & 0xf) == 1 ? 2 : 1; + ulong pat = CONFIG_SYS_SDRAM_BASE, base = CONFIG_SYS_SDRAM_BASE; + u32 shift = 12, rank, i, pg; + ulong reg; + + if (!mctl_core_init(para)) + return 0; + + for (rank = 0; rank < maxrank; rank++) { + reg = MC_COM + 0x000 + rank * 4; + + for (i = 0; i < 64; i++) + writel(scan_pattern(i & 1, base + i * 4), + base + i * 4); + + /* row mode */ + clrsetbits_le32(reg, 0xf0c, 0x6f0); + for (i = 11; i < 17; i++) { + if (scan_match(base + (1UL << (i + 11)), pat)) + break; + } + if (i == 17) + i = 16; + para->para1 = (para->para1 & ~(0xff << (shift - 8))) | + (i << (shift - 8)); + + /* bank mode */ + if (rank == 1) { + clrsetbits_le32(MC_COM + 0x000, 0xffc, 0x6a4); + base = CONFIG_SYS_SDRAM_BASE + 0x800000; + } + clrsetbits_le32(reg, 0xffc, 0x6a4); + i = scan_match(base + 0x800, pat) ? 0 : 1; + para->para1 = (para->para1 & ~(0xf << shift)) | (i << shift); + + /* page mode */ + if (rank == 1) { + clrsetbits_le32(MC_COM + 0x000, 0xffc, 0xaa0); + base = CONFIG_SYS_SDRAM_BASE + 0x4000000; + } + clrsetbits_le32(reg, 0xffc, 0xaa0); + for (i = 9; i < 14; i++) { + if (scan_match(base + (1UL << i), pat)) + break; + } + if (i == 14) + i = 13; + pg = i == 9 ? 0 : 1 << (i - 10); + para->para1 = (para->para1 & ~(0xf << (rank * 16))) | + (pg << (rank * 16)); + + if (rank + 1 == maxrank) + break; + + /* on to rank1, at 128 MiB */ + clrsetbits_le32(MC_COM + 0x000, 0xffc, 0x6f0); + clrsetbits_le32(MC_COM + 0x004, 0xffc, 0x6f1); + pat = base = CONFIG_SYS_SDRAM_BASE + 0x8000000; + shift += 16; + } + + if (maxrank == 2) { + para2 &= ~0xf00; + if ((para->para1 & 0xffff) != (para->para1 >> 16)) + para2 |= BIT(8); + para->para2 = para2; + } + + return 1; +} + +static int auto_scan_dram_rank_width(struct dram_para *para) +{ + u32 tpr13 = para->tpr13; + u32 para1 = para->para1; + + para->para1 = 0x00b000b0; + para->para2 = (para->para2 & ~0xf) | BIT(12); + /* DQS probe mode */ + para->tpr13 = (tpr13 & ~BIT(3)) | BIT(2) | BIT(0); + + mctl_core_init(para); + if (readl(MCTL + 0x010) & BIT(20)) + return 0; + if (!dqs_gate_detect(para)) + return 0; + + para->tpr13 = tpr13; + para->para1 = para1; + + return 1; +} + +/* DDR3 first, then DDR2; the clocks for either are in tpr7 */ +static int auto_scan_dram_type(struct dram_para *para) +{ + u32 tpr13 = para->tpr13; + u32 para1 = para->para1; + u32 para2 = para->para2; + u32 clk = para->clk; + + para->para1 = 0x00b000b0; + para->para2 = 0; + para->tpr13 = (tpr13 & ~BIT(3)) | BIT(2) | BIT(0); + + if (!clk) + para->clk = 24 * ((para->tpr7 >> 16) & 0xff); + para->type = SUNXI_DRAM_V831_DDR3; + para->zq = 0x3bfb; + mctl_core_init(para); + if (!dqs_gate_detect(para)) { + if (!clk) + para->clk = 24 * ((para->tpr7 >> 24) & 0xff); + para->type = SUNXI_DRAM_V831_DDR2; + para->zq = 0x3bf9; + mctl_core_init(para); + if (!dqs_gate_detect(para)) + return 0; + } + + para->tpr13 = tpr13; + para->para1 = para1; + para->para2 = para2; + + return 1; +} + +static int auto_scan_dram_config(struct dram_para *para) +{ + if (!(para->tpr13 & BIT(13)) && !auto_scan_dram_type(para)) + return 0; + if (!(para->tpr13 & BIT(14)) && !auto_scan_dram_rank_width(para)) + return 0; + if (!(para->tpr13 & BIT(0)) && !auto_scan_dram_size(para)) + return 0; + if (!(para->tpr13 & BIT(15))) + para->tpr13 |= BIT(14) | BIT(13) | BIT(1) | BIT(0); + + return 1; +} + +static u32 init_dram(struct dram_para *para) +{ + u32 size_mb; + + if (!(para->tpr13 & BIT(16))) { + /* external ZQ resistor */ + writel(0x10, SUNXI_PRCM_BASE + 0x250); + writel(0x330000, SUNXI_PRCM_BASE + 0x310); + writel(0x330003, SUNXI_PRCM_BASE + 0x310); + } else { + setbits_le32(SUNXI_PRCM_BASE + 0x310, BIT(8)); + writel(0, SUNXI_PRCM_BASE + 0x318); + } + + if (!(para->tpr13 & BIT(0)) && !auto_scan_dram_config(para)) { + printf("DRAM: auto scan failed\n"); + return 0; + } + + if (!mctl_core_init(para)) { + printf("DRAM: initialisation failed\n"); + return 0; + } + + if (para->para2 & BIT(31)) { + size_mb = (para->para2 >> 16) & 0x7fff; + } else { + size_mb = dramc_get_dram_size(); + para->para2 = (para->para2 & 0xffff) | (size_mb << 16); + } + + /* auto self refresh */ + if (para->tpr13 & BIT(30)) { + writel(para->tpr8 ? para->tpr8 : 0x10000200, MCTL + 0x0a0); + writel(0x40a, MCTL + 0x09c); + setbits_le32(MCTL + 0x004, BIT(0)); + } else { + clrbits_le32(MCTL + 0x0a0, 0xffff); + clrbits_le32(MCTL + 0x004, BIT(0)); + } + + if ((para->tpr13 & BIT(9)) || para->type == SUNXI_DRAM_V831_LPDDR2) + clrsetbits_le32(MCTL + 0x100, 0xf000, 0x5000); + else + clrbits_le32(MCTL + 0x100, 0xf000); + + setbits_le32(MCTL + 0x140, BIT(31)); + if (para->tpr13 & BIT(8)) + setbits_le32(MCTL + 0x0b8, 0x300); + + if (para->tpr13 & BIT(26)) + clrbits_le32(MCTL + 0x108, BIT(13)); + else + setbits_le32(MCTL + 0x108, BIT(13)); + + if (para->type == SUNXI_DRAM_V831_LPDDR3) + clrsetbits_le32(MCTL + 0x07c, 0xf0000, 0x10000); + + dram_enable_all_master(); + + if ((para->tpr13 & BIT(28)) && dramc_simple_wr_test(size_mb, 4096)) + return 0; + + return size_mb; +} + +unsigned long sunxi_dram_init(void) +{ + struct dram_para para = { + .clk = CONFIG_DRAM_CLK, + .type = CONFIG_DRAM_SUN8I_V831_TYPE, + .zq = CONFIG_DRAM_ZQ, + .odt_en = CONFIG_DRAM_SUN8I_V831_ODT_EN, + .para1 = CONFIG_DRAM_SUN8I_V831_PARA1, + .para2 = CONFIG_DRAM_SUN8I_V831_PARA2, + .mr0 = CONFIG_DRAM_SUN8I_V831_MR0, + .mr1 = CONFIG_DRAM_SUN8I_V831_MR1, + .mr2 = CONFIG_DRAM_SUN8I_V831_MR2, + .mr3 = CONFIG_DRAM_SUN8I_V831_MR3, + .tpr0 = CONFIG_DRAM_SUN8I_V831_TPR0, + .tpr1 = CONFIG_DRAM_SUN8I_V831_TPR1, + .tpr2 = CONFIG_DRAM_SUN8I_V831_TPR2, + .tpr3 = CONFIG_DRAM_SUN8I_V831_TPR3, + .tpr5 = CONFIG_DRAM_SUN8I_V831_TPR5, + .tpr6 = CONFIG_DRAM_SUN8I_V831_TPR6, + .tpr7 = CONFIG_DRAM_SUN8I_V831_TPR7, + .tpr11 = CONFIG_DRAM_SUN8I_V831_TPR11, + .tpr12 = CONFIG_DRAM_SUN8I_V831_TPR12, + .tpr13 = CONFIG_DRAM_SUN8I_V831_TPR13, + }; + + unsigned long size_mb = init_dram(¶); + + if (!size_mb) + return 0; + + /* + * The geometry the library found, checked against the DRAM itself: + * past the part's size the addresses alias. (Under QEMU, which does + * not model the controller's address mapping, the library's scan, + * like the vendor's boot0, reports 4160 MiB.) + */ + size_mb = min(size_mb, (unsigned long)(SZ_2G >> 20)); + + return get_ram_size((long *)CONFIG_SYS_SDRAM_BASE, size_mb << 20); +} diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index de9aa68c4ac2..e1a80dbf3b58 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -78,7 +78,7 @@ #define CCM_AHB_GATING0 (0x01C20000 + 0x60) #define CCM_H6_SPI_BGR_REG (0x03001000 + 0x96c) -#ifdef CONFIG_MACH_SUN50I_H6 +#if defined(CONFIG_MACH_SUN50I_H6) || defined(CONFIG_MACH_SUN8I_V831) #define CCM_SPI0_CLK (0x03001000 + 0x940) #else #define CCM_SPI0_CLK (0x01C20000 + 0xA0) @@ -99,6 +99,13 @@ * from SPI Flash, everything else is using pins PC0,PC1,PC2,PC3. * The H6 uses PC0, PC2, PC3, PC5. */ + +/* The V831/V833 have the H6's SPI controller, at the H6's address */ +static bool is_h6_spi(void) +{ + return IS_ENABLED(CONFIG_MACH_SUN50I_H6) || + IS_ENABLED(CONFIG_MACH_SUN8I_V831); +} static void spi0_pinmux_setup(unsigned int pin_function) { /* All chips use PC0 and PC2. */ @@ -121,8 +128,7 @@ static void spi0_pinmux_setup(unsigned int pin_function) static bool is_sun6i_gen_spi(void) { - return IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) || - IS_ENABLED(CONFIG_MACH_SUN50I_H6); + return IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) || is_h6_spi(); } static uintptr_t spi0_base_address(void) @@ -130,7 +136,7 @@ static uintptr_t spi0_base_address(void) if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) return 0x01C05000; - if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + if (is_h6_spi()) return 0x05010000; if (!is_sun6i_gen_spi() || @@ -148,14 +154,14 @@ static void spi0_enable_clock(void) uintptr_t base = spi0_base_address(); /* Deassert SPI0 reset on SUN6I */ - if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + if (is_h6_spi()) setbits_le32(CCM_H6_SPI_BGR_REG, (1U << 16) | 0x1); else if (is_sun6i_gen_spi()) setbits_le32(SUN6I_BUS_SOFT_RST_REG0, (1 << AHB_RESET_SPI0_SHIFT)); /* Open the SPI0 gate */ - if (!IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + if (!is_h6_spi()) setbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0)); if (IS_ENABLED(CONFIG_MACH_SUNIV)) { @@ -202,11 +208,11 @@ static void spi0_disable_clock(void) writel(0, CCM_SPI0_CLK); /* Close the SPI0 gate */ - if (!IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + if (!is_h6_spi()) clrbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0)); /* Assert SPI0 reset on SUN6I */ - if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + if (is_h6_spi()) clrbits_le32(CCM_H6_SPI_BGR_REG, (1U << 16) | 0x1); else if (is_sun6i_gen_spi()) clrbits_le32(SUN6I_BUS_SOFT_RST_REG0, @@ -217,8 +223,7 @@ static void spi0_init(void) { unsigned int pin_function = SUNXI_GPC_SPI0; - if (IS_ENABLED(CONFIG_MACH_SUN50I) || - IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + if (IS_ENABLED(CONFIG_MACH_SUN50I) || is_h6_spi()) pin_function = SUN50I_GPC_SPI0; else if (IS_ENABLED(CONFIG_MACH_SUNIV)) pin_function = SUNIV_GPC_SPI0; @@ -357,6 +362,19 @@ static int spl_spi_load_image(struct spl_image_info *spl_image, load.read = spi_load_read; ret = spl_load_simple_fit(spl_image, &load, load_offset, header); + } else if (IS_ENABLED(CONFIG_SPL_LZMA) && + image_get_magic(header) == IH_MAGIC && + image_get_comp(header) == IH_COMP_LZMA) { + /* a compressed legacy image: unpacked to its load address */ + struct spl_load_info load; + + load.dev = NULL; + load.priv = NULL; + load.filename = NULL; + load.bl_len = 1; + load.read = spi_load_read; + ret = spl_load_legacy_img(spl_image, bootdev, &load, + load_offset); } else { ret = spl_parse_image_header(spl_image, bootdev, header); if (ret) diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 70d97815f0a1..9ab484ee655f 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -117,6 +117,7 @@ config SPL_BSS_START_ADDR default 0x18200000 if ARCH_MX6 && !(MX6SX || MX6SL || MX6UL || MX6ULL) default 0x80a00000 if ARCH_OMAP2PLUS default 0x81f80000 if ARCH_SUNXI && MACH_SUNIV + default 0x43f80000 if ARCH_SUNXI && MACH_SUN8I_V831 default 0x4ff80000 if ARCH_SUNXI && !(MACH_SUN9I || MACH_SUNIV) default 0x2ff80000 if ARCH_SUNXI && MACH_SUN9I default 0x1000 if ARCH_ZYNQMP diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig index bf11fad6eefc..27753aefb92c 100644 --- a/drivers/clk/sunxi/Kconfig +++ b/drivers/clk/sunxi/Kconfig @@ -89,7 +89,7 @@ config CLK_SUN8I_H3 config CLK_SUN50I_H6 bool "Clock driver for Allwinner H6" - default MACH_SUN50I_H6 + default MACH_SUN50I_H6 || MACH_SUN8I_V831 help This enables common clock driver support for platforms based on Allwinner H6 SoC. diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c index ec02a2d0370d..d08602f9e673 100644 --- a/drivers/clk/sunxi/clk_sunxi.c +++ b/drivers/clk/sunxi/clk_sunxi.c @@ -198,6 +198,8 @@ static const struct udevice_id sunxi_clk_ids[] = { #ifdef CONFIG_CLK_SUN50I_H6 { .compatible = "allwinner,sun50i-h6-ccu", .data = (ulong)&h6_ccu_desc }, + { .compatible = "allwinner,sun8i-v831-ccu", + .data = (ulong)&h6_ccu_desc }, #endif #ifdef CONFIG_CLK_SUN50I_H6_R { .compatible = "allwinner,sun50i-h6-r-ccu", diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig index 77da90836b6b..e02b6fe0b40e 100644 --- a/drivers/pinctrl/sunxi/Kconfig +++ b/drivers/pinctrl/sunxi/Kconfig @@ -109,6 +109,11 @@ config PINCTRL_SUN50I_H6 default MACH_SUN50I_H6 select PINCTRL_SUNXI +config PINCTRL_SUN8I_V831 + bool "Support for the Allwinner V831/V833 PIO" + default MACH_SUN8I_V831 + select PINCTRL_SUNXI + config PINCTRL_SUN50I_H6_R bool "Support for the Allwinner H6 R-PIO" default MACH_SUN50I_H6 diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 9ce2bc1b3afb..add1f4c632a3 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -689,6 +689,23 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h6_r_pinctrl_desc = .num_banks = 2, }; +static const struct sunxi_pinctrl_function sun8i_v831_pinctrl_functions[] = { + { "emac", 5 }, /* PD1-PD21 */ + { "gpio_in", 0 }, + { "gpio_out", 1 }, + { "mmc0", 2 }, /* PF0-PF5 */ + { "mmc1", 2 }, /* PG0-PG5 */ + { "spi0", 4 }, /* PC0-PC3 */ + { "uart0", 5 }, /* PH9-PH10 */ +}; + +static const struct sunxi_pinctrl_desc __maybe_unused sun8i_v831_pinctrl_desc = { + .functions = sun8i_v831_pinctrl_functions, + .num_functions = ARRAY_SIZE(sun8i_v831_pinctrl_functions), + .first_bank = SUNXI_GPIO_A, + .num_banks = 9, +}; + static const struct sunxi_pinctrl_function sun50i_h616_pinctrl_functions[] = { { "emac0", 2 }, /* PI0-PI16 */ { "gpio_in", 0 }, @@ -871,6 +888,12 @@ static const struct udevice_id sunxi_pinctrl_ids[] = { .data = (ulong)&sun50i_h6_r_pinctrl_desc, }, #endif +#ifdef CONFIG_PINCTRL_SUN8I_V831 + { + .compatible = "allwinner,sun8i-v831-pinctrl", + .data = (ulong)&sun8i_v831_pinctrl_desc, + }, +#endif #ifdef CONFIG_PINCTRL_SUN50I_H616 { .compatible = "allwinner,sun50i-h616-pinctrl", From 471064a507f92eab70db59e8292daa13da24ad2c Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov <7954832+dimerr@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:20:06 +0000 Subject: [PATCH 4/8] sunxi: V831/V833: boot the vendor kernels with their device tree OpenIPC's V83x kernels (Linux 4.9) carry no device tree; the vendor U-Boot gives them its own. v83x-kernel.dts is that tree: the Tina V83x SDK's reference board over the kernel's sun8iw19p1.dtsi, flattened, with what the vendor U-Boot changes in it before booting from SPI NOR, traced under QEMU from a camera's flash: SPI0 enabled (the flash, the root filesystem); OP-TEE's memreserve dropped. It is gzipped into U-Boot (12 KiB), unpacked at misc_init_r() to ${fdtaddr}, and a plain uImage boots with it (boot/image-fdt.c, as on openipc-novatek). ft_board_setup() sets /dram/dram_para2 to the size, as the vendor does. The board also clocks the RMII PHY (EPHY_25M, the vendor tree's use_ephy25m) and runs the OpenIPC helper from misc_init_r(). Under QEMU an OpenIPC allwinner-v83x kernel boots on it to a shell: PSCIv1.0, HYP mode, cmdlinepart on spi0.0, SquashFS root on mtdblock3, sunxi-gmac pings the host, reboot comes back through the SPL. --- board/sunxi/Makefile | 16 + board/sunxi/board.c | 18 + board/sunxi/v83x-kernel.dts | 3227 +++++++++++++++++++++++++++++++++ board/sunxi/v83x.c | 65 + board/sunxi/v83x_kernel_dtb.S | 10 + boot/image-fdt.c | 13 + 6 files changed, 3349 insertions(+) create mode 100644 board/sunxi/v83x-kernel.dts create mode 100644 board/sunxi/v83x.c create mode 100644 board/sunxi/v83x_kernel_dtb.S diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile index d96b7897b6c6..0f6b1e8fb115 100644 --- a/board/sunxi/Makefile +++ b/board/sunxi/Makefile @@ -12,3 +12,19 @@ obj-$(CONFIG_MACH_SUN4I) += dram_sun4i_auto.o obj-$(CONFIG_MACH_SUN5I) += dram_sun5i_auto.o obj-$(CONFIG_MACH_SUN7I) += dram_sun5i_auto.o obj-$(CONFIG_CHIP_DIP_SCAN) += chip.o + +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_MACH_SUN8I_V831) += v83x.o v83x_kernel_dtb.o + +# The vendor kernel's device tree, gzipped into U-Boot (v83x.c) +AFLAGS_v83x_kernel_dtb.o += -DV83X_KERNEL_DTB_GZ=\"$(obj)/v83x-kernel.dtb.gz\" +$(obj)/v83x_kernel_dtb.o: $(obj)/v83x-kernel.dtb.gz + +quiet_cmd_dtbgz = DTBGZ $@ + cmd_dtbgz = $(DTC) -q -I dts -O dtb -o - $< | gzip -9n > $@ + +$(obj)/v83x-kernel.dtb.gz: $(src)/v83x-kernel.dts FORCE + $(call if_changed,dtbgz) + +targets += v83x-kernel.dtb.gz +endif diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 21a2407e062f..fcddedab1dc5 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -49,6 +49,11 @@ #include #include #include +#include + +void v83x_board_init(void); +void v83x_kernel_fdt(void); +void v83x_ft_board_setup(void *blob); DECLARE_GLOBAL_DATA_PTR; @@ -261,6 +266,9 @@ int board_init(void) eth_init_board(); + if (IS_ENABLED(CONFIG_MACH_SUN8I_V831)) + v83x_board_init(); + return 0; } @@ -860,6 +868,13 @@ int misc_init_r(void) setup_environment(gd->fdt_blob); + if (IS_ENABLED(CONFIG_MACH_SUN8I_V831)) + v83x_kernel_fdt(); + +#ifdef CONFIG_OPENIPC + openipc_helper(); +#endif + return 0; } @@ -918,6 +933,9 @@ int ft_board_setup(void *blob, struct bd_info *bd) bluetooth_dt_fixup(blob); + if (IS_ENABLED(CONFIG_MACH_SUN8I_V831)) + v83x_ft_board_setup(blob); + #ifdef CONFIG_VIDEO_DT_SIMPLEFB r = sunxi_simplefb_setup(blob); if (r) diff --git a/board/sunxi/v83x-kernel.dts b/board/sunxi/v83x-kernel.dts new file mode 100644 index 000000000000..140e91add868 --- /dev/null +++ b/board/sunxi/v83x-kernel.dts @@ -0,0 +1,3227 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The device tree the V831/V833 vendor kernels (Linux 4.9, as OpenIPC + * builds them) boot with, as the vendor's U-Boot would hand it over: the + * Tina V83x SDK's reference board (device/config/chips/v831/configs/base/ + * board.dts) over the kernel's arch/arm/boot/dts/sun8iw19p1.dtsi, + * preprocessed and flattened, with what the vendor U-Boot changes in it + * before it boots a kernel from SPI NOR (traced under QEMU from a camera's + * flash): SPI0, the flash, enabled; and without the memory OP-TEE took. + * /memory, /chosen and /dram/dram_para2 (the DRAM size) are set at boot. + */ + +/dts-v1/; + +/ { + model = "sun8iw19"; + compatible = "allwinner,sun8iw19p1"; + interrupt-parent = <0x01>; + #address-cells = <0x02>; + #size-cells = <0x02>; + + clocks { + compatible = "allwinner,clk-init"; + device_type = "clocks"; + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + reg = <0x00 0x3001000 0x00 0x1000 0x00 0x7010000 0x00 0x400 0x00 0x7000000 0x00 0x200>; + + losc { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-clock"; + clock-frequency = <0x8000>; + clock-output-names = "losc"; + phandle = <0x10>; + }; + + iosc { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-clock"; + clock-frequency = <0xf42400>; + clock-output-names = "iosc"; + }; + + hosc { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-clock"; + clock-frequency = <0x16e3600>; + clock-output-names = "hosc"; + phandle = <0x08>; + }; + + pll_periph0div25m { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-clock"; + clock-frequency = <0x17d7840>; + clock-output-names = "pll_periph0div25m"; + }; + + osc48m { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-clock"; + clock-frequency = <0x2dc6c00>; + clock-output-names = "osc48m"; + phandle = <0x09>; + }; + + pll_cpu { + #clock-cells = <0x00>; + compatible = "allwinner,pll-clock"; + lock-mode = "new"; + clock-output-names = "pll_cpu"; + phandle = <0xa1>; + }; + + pll_ddr0 { + #clock-cells = <0x00>; + compatible = "allwinner,pll-clock"; + lock-mode = "new"; + clock-output-names = "pll_ddr0"; + phandle = <0x07>; + }; + + pll_periph0 { + #clock-cells = <0x00>; + compatible = "allwinner,pll-clock"; + assigned-clock-rates = <0x23c34600>; + lock-mode = "new"; + clock-output-names = "pll_periph0"; + phandle = <0x04>; + }; + + pll_uni { + #clock-cells = <0x00>; + compatible = "allwinner,pll-clock"; + assigned-clock-rates = <0x23c34600>; + lock-mode = "new"; + clock-output-names = "pll_uni"; + phandle = <0x05>; + }; + + pll_video0 { + #clock-cells = <0x00>; + compatible = "allwinner,pll-clock"; + lock-mode = "new"; + clock-output-names = "pll_video0"; + phandle = <0x06>; + }; + + pll_audio { + #clock-cells = <0x00>; + compatible = "allwinner,pll-clock"; + assigned-clocks = <0x02>; + assigned-clock-rates = <0x1588800>; + lock-mode = "new"; + clock-output-names = "pll_audio"; + phandle = <0x02>; + }; + + pll_csi { + #clock-cells = <0x00>; + compatible = "allwinner,pll-clock"; + assigned-clocks = <0x03>; + assigned-clock-rates = <0x202fbf00>; + lock-mode = "new"; + clock-output-names = "pll_csi"; + phandle = <0x03>; + }; + + pll_periph0x2 { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-factor-clock"; + clocks = <0x04>; + clock-mult = <0x02>; + clock-div = <0x01>; + clock-output-names = "pll_periph0x2"; + phandle = <0x0d>; + }; + + periph32k { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-factor-clock"; + clocks = <0x04>; + clock-mult = <0x02>; + clock-div = <0x8f0d>; + clock-output-names = "periph32k"; + }; + + pll_unix2 { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-factor-clock"; + clocks = <0x05>; + clock-mult = <0x02>; + clock-div = <0x01>; + clock-output-names = "pll_unix2"; + phandle = <0x51>; + }; + + pll_audiox4 { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-factor-clock"; + clocks = <0x02>; + clock-mult = <0x04>; + clock-div = <0x01>; + clock-output-names = "pll_audiox4"; + }; + + pll_audiox2 { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-factor-clock"; + clocks = <0x02>; + clock-mult = <0x02>; + clock-div = <0x01>; + clock-output-names = "pll_audiox2"; + }; + + pll_video0x4 { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-factor-clock"; + clocks = <0x06>; + clock-mult = <0x04>; + clock-div = <0x01>; + clock-output-names = "pll_video0x4"; + }; + + mbus { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-factor-clock"; + clocks = <0x07>; + clock-mult = <0x01>; + clock-div = <0x04>; + clock-output-names = "mbus"; + }; + + hoscd2 { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-factor-clock"; + clocks = <0x08>; + clock-mult = <0x01>; + clock-div = <0x02>; + clock-output-names = "hoscd2"; + }; + + osc48md4 { + #clock-cells = <0x00>; + compatible = "allwinner,fixed-factor-clock"; + clocks = <0x09>; + clock-mult = <0x01>; + clock-div = <0x04>; + clock-output-names = "osc48md4"; + phandle = <0x39>; + }; + + cpu { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "cpu"; + }; + + axi { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "axi"; + }; + + cpuapb { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "cpuapb"; + }; + + psi { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "psi"; + }; + + ahb1 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "ahb1"; + }; + + ahb2 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "ahb2"; + }; + + ahb3 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "ahb3"; + }; + + apb1 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "apb1"; + }; + + apb2 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "apb2"; + }; + + de { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + assigned-clocks = <0x0a>; + assigned-clock-rates = <0x11e1a300>; + clock-output-names = "de"; + phandle = <0x0a>; + }; + + g2d { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + assigned-clock-rates = <0x11e1a300>; + assigned-clocks = <0x0b>; + clock-output-names = "g2d"; + phandle = <0x0b>; + }; + + ve { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "ve"; + phandle = <0x13>; + }; + + ce { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "ce"; + phandle = <0x93>; + }; + + eise { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "eise"; + phandle = <0x15>; + }; + + nna { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + assigned-clocks = <0x0c>; + assigned-clock-parents = <0x0d>; + clock-output-names = "nna"; + phandle = <0x0c>; + }; + + nna_rts { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "nna_rst"; + phandle = <0x66>; + }; + + isp { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "isp"; + phandle = <0x89>; + }; + + dma { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "dma"; + phandle = <0x12>; + }; + + hstimer { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "hstimer"; + }; + + avs { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "avs"; + }; + + dbgsys { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "dbgsys"; + }; + + pwm { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "pwm"; + phandle = <0x67>; + }; + + iommu { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "iommu"; + phandle = <0xa7>; + }; + + sdram { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdram"; + }; + + sdmmc0_mod { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdmmc0_mod"; + phandle = <0x58>; + }; + + sdmmc0_bus { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdmmc0_bus"; + phandle = <0x59>; + }; + + sdmmc0_rst { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdmmc0_rst"; + phandle = <0x5a>; + }; + + sdmmc1_mod { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdmmc1_mod"; + phandle = <0x5c>; + }; + + sdmmc1_bus { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdmmc1_bus"; + phandle = <0x5d>; + }; + + sdmmc1_rst { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdmmc1_rst"; + phandle = <0x5e>; + }; + + sdmmc2_mod { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdmmc2_mod"; + phandle = <0x52>; + }; + + sdmmc2_bus { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdmmc2_bus"; + phandle = <0x53>; + }; + + sdmmc2_rst { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "sdmmc2_rst"; + phandle = <0x54>; + }; + + uart0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "uart0"; + phandle = <0x16>; + }; + + uart1 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "uart1"; + phandle = <0x19>; + }; + + uart2 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "uart2"; + phandle = <0x1c>; + }; + + uart3 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "uart3"; + phandle = <0x1f>; + }; + + twi0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "twi0"; + phandle = <0x25>; + }; + + twi1 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "twi1"; + phandle = <0x28>; + }; + + twi2 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "twi2"; + phandle = <0x2b>; + }; + + twi3 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "twi3"; + phandle = <0x2e>; + }; + + spi0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "spi0"; + phandle = <0x45>; + }; + + spi1 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "spi1"; + phandle = <0x49>; + }; + + spi2 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "spi2"; + phandle = <0x4d>; + }; + + ephy_25m { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "ephy_25m"; + phandle = <0x9d>; + }; + + gmac { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "gmac"; + phandle = <0x9c>; + }; + + gpadc { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "gpadc"; + phandle = <0x9b>; + }; + + ths { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "ths"; + phandle = <0x94>; + }; + + i2s0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "i2s0"; + phandle = <0x3a>; + }; + + i2s1 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "i2s1"; + phandle = <0x3d>; + }; + + codec_1x { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "codec_1x"; + phandle = <0x40>; + }; + + codec_4x { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "codec_4x"; + }; + + usbphy0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "usbphy0"; + phandle = <0x34>; + }; + + usbohci0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "usbohci0"; + phandle = <0x37>; + }; + + usbohci0_12m { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "usbohci0_12m"; + phandle = <0x38>; + }; + + usbehci0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "usbehci0"; + phandle = <0x36>; + }; + + usbotg { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "usbotg"; + phandle = <0x35>; + }; + + mipi_dphy0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "mipi_dphy0"; + phandle = <0x63>; + }; + + mipi_host0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + assigned-clocks = <0x0e>; + assigned-clock-parents = <0x08>; + clock-output-names = "mipi_host0"; + phandle = <0x0e>; + }; + + display_top { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "display_top"; + phandle = <0x61>; + }; + + tcon_lcd { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "tcon_lcd"; + phandle = <0x62>; + }; + + csi_top { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "csi_top"; + phandle = <0x86>; + }; + + csi_master0 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "csi_master0"; + phandle = <0x87>; + }; + + csi_master1 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "csi_master1"; + phandle = <0x88>; + }; + + dspo { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "dspo"; + }; + + pio { + #clock-cells = <0x00>; + compatible = "allwinner,periph-clock"; + clock-output-names = "pio"; + phandle = <0x11>; + }; + + cpurowc { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "cpurowc"; + phandle = <0x22>; + }; + + stwi { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "stwi"; + phandle = <0x31>; + }; + + losc_out { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "losc_out"; + phandle = <0xa8>; + }; + + cpurcpus_pll { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "cpurcpus_pll"; + }; + + cpurcpus { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "cpurcpus"; + }; + + cpurahbs { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "cpurahbs"; + }; + + cpurapbs1 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "cpurapbs1"; + }; + + cpurapbs2_pll { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "cpurapbs2_pll"; + }; + + cpurapbs2 { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "cpurapbs2"; + }; + + cpurpio { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "cpurpio"; + phandle = <0x0f>; + }; + + spwm { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "spwm"; + }; + + dcxo_out { + #clock-cells = <0x00>; + compatible = "allwinner,periph-cpus-clock"; + clock-output-names = "dcxo_out"; + }; + }; + + soc@03000000 { + compatible = "simple-bus"; + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + device_type = "soc"; + + pinctrl@07022000 { + compatible = "allwinner,sun8iw19p1-r-pinctrl"; + reg = <0x00 0x7022000 0x00 0x400>; + interrupts = <0x00 0x6a 0x04>; + clocks = <0x0f 0x10 0x08>; + device_type = "r_pio"; + gpio-controller; + interrupt-controller; + #interrupt-cells = <0x03>; + #size-cells = <0x00>; + #gpio-cells = <0x06>; + + s_twi0@0 { + allwinner,pins = "PL0", "PL1"; + allwinner,function = "s_twi0"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x32>; + }; + + s_twi0@1 { + allwinner,pins = "PL0", "PL1"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x33>; + }; + }; + + pinctrl@0300b000 { + compatible = "allwinner,sun8iw19p1-pinctrl"; + reg = <0x00 0x300b000 0x00 0x400>; + interrupts = <0x00 0x43 0x04 0x00 0x44 0x04 0x00 0x45 0x04 0x00 0x46 0x04 0x00 0x47 0x04 0x00 0x48 0x04 0x00 0x49 0x04>; + device_type = "pio"; + clocks = <0x11 0x10 0x08>; + gpio-controller; + interrupt-controller; + #interrupt-cells = <0x03>; + #size-cells = <0x00>; + #gpio-cells = <0x06>; + input-debounce = <0x00 0x00 0x00 0x01 0x00 0x00 0x00>; + phandle = <0x41>; + + vdevice@0 { + allwinner,pins = "PC0", "PC1"; + allwinner,function = "vdevice"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x92>; + }; + + uart0@0 { + allwinner,pins = "PH9", "PH10"; + allwinner,pname = "uart0_tx", "uart0_rx"; + allwinner,function = "uart0"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x17>; + }; + + uart0@1 { + allwinner,pins = "PH9", "PH10"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x18>; + }; + + uart1@0 { + allwinner,pins = "PG6", "PG7"; + allwinner,pname = "uart1_tx", "uart1_rx"; + allwinner,function = "uart1"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x1a>; + }; + + uart1@1 { + allwinner,pins = "PG6", "PG7"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x1b>; + }; + + uart2@0 { + allwinner,pins = "PE18", "PE19", "PE20", "PE21"; + allwinner,pname = "uart2_tx", "uart2_rx", "uart2_rts", "uart2_cts"; + allwinner,function = "uart2"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x1d>; + }; + + uart2@1 { + allwinner,pins = "PE18", "PE19", "PE20", "PE21"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x1e>; + }; + + uart3@0 { + allwinner,pins = "PH0", "PH1", "PH2", "PH3"; + allwinner,pname = "uart3_tx", "uart3_rx", "uart3_rts", "uart3_cts"; + allwinner,function = "uart3"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x20>; + }; + + uart3@1 { + allwinner,pins = "PH0", "PH1", "PH2", "PH3"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x21>; + }; + + twi0@0 { + allwinner,pins = "PI3", "PI4"; + allwinner,pname = "twi0_scl", "twi0_sda"; + allwinner,function = "twi0"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x26>; + }; + + twi0@1 { + allwinner,pins = "PI3", "PI4"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x27>; + }; + + twi1@0 { + allwinner,pins = "PI1", "PI2"; + allwinner,pname = "twi1_scl", "twi1_sda"; + allwinner,function = "twi1"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x29>; + }; + + twi1@1 { + allwinner,pins = "PI1", "PI2"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x2a>; + }; + + twi2@0 { + allwinner,pins = "PH5", "PH6"; + allwinner,pname = "twi2_scl", "twi2_sda"; + allwinner,function = "twi2"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x2c>; + }; + + twi2@1 { + allwinner,pins = "PH5", "PH6"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x2d>; + }; + + twi3@0 { + allwinner,pins = "PH13", "PH14"; + allwinner,pname = "twi3_scl", "twi3_sda"; + allwinner,function = "twi3"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x2f>; + }; + + twi3@1 { + allwinner,pins = "PH13", "PH14"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x30>; + }; + + pwm0@0 { + allwinner,pins = "PD1"; + allwinner,pname = "pwm0_positive"; + allwinner,function = "pwm0"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x72>; + }; + + pwm0@1 { + allwinner,pins = "PD1"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x73>; + }; + + pwm1@0 { + allwinner,pins = "PD2"; + allwinner,pname = "pwm1_positive"; + allwinner,function = "pwm1"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x74>; + }; + + pwm1@1 { + allwinner,pins = "PD2"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x75>; + }; + + pwm2@0 { + allwinner,pins = "PD3"; + allwinner,pname = "pwm2_positive"; + allwinner,function = "pwm2"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x76>; + }; + + pwm2@1 { + allwinner,pins = "PD3"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x77>; + }; + + pwm3@0 { + allwinner,pins = "PD4"; + allwinner,pname = "pwm3_positive"; + allwinner,function = "pwm3"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x78>; + }; + + pwm3@1 { + allwinner,pins = "PD4"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x79>; + }; + + pwm4@0 { + allwinner,pins = "PD5"; + allwinner,pname = "pwm4_positive"; + allwinner,function = "pwm4"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x7a>; + }; + + pwm4@1 { + allwinner,pins = "PD5"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x7b>; + }; + + pwm5@0 { + allwinner,pins = "PD6"; + allwinner,pname = "pwm5_positive"; + allwinner,function = "pwm5"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x7c>; + }; + + pwm5@1 { + allwinner,pins = "PD6"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x7d>; + }; + + pwm6@0 { + allwinner,pins = "PD7"; + allwinner,pname = "pwm6_positive"; + allwinner,function = "pwm6"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x7e>; + }; + + pwm6@1 { + allwinner,pins = "PD7"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x7f>; + }; + + pwm7@0 { + allwinner,pins = "PD8"; + allwinner,pname = "pwm7_positive"; + allwinner,function = "pwm7"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x80>; + }; + + pwm7@1 { + allwinner,pins = "PD8"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x81>; + }; + + pwm8@0 { + allwinner,pins = "PH8"; + allwinner,pname = "pwm8_positive"; + allwinner,function = "pwm8"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x82>; + }; + + pwm8@1 { + allwinner,pins = "PH8"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x83>; + }; + + pwm9@0 { + allwinner,pins = "PD22"; + allwinner,pname = "pwm9_positive"; + allwinner,function = "pwm9"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x84>; + }; + + pwm9@1 { + allwinner,pins = "PD22"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x85>; + }; + + ts0@0 { + allwinner,pins = "PE0", "PE1", "PE2", "PE3", "PE4", "PE5", "PE6", "PE7", "PE8", "PE9", "PE10", "PE11"; + allwinner,pname = "ts0_clk", "ts0_err", "ts0_sync", "ts0_dvld", "ts0_d0", "ts0_d1", "ts0_d2", "ts0_d3", "ts0_d4", "ts0_d5", "ts0_d6", "ts0_d7"; + allwinner,function = "ts0"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + ts0_sleep@0 { + allwinner,pins = "PE0", "PE1", "PE2", "PE3", "PE4", "PE5", "PE6", "PE7", "PE8", "PE9", "PE10", "PE11"; + allwinner,pname = "ts0_clk", "ts0_err", "ts0_sync", "ts0_dvld", "ts0_d0", "ts0_d1", "ts0_d2", "ts0_d3", "ts0_d4", "ts0_d5", "ts0_d6", "ts0_d7"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + ts1@0 { + allwinner,pins = "PE7", "PE8", "PE9", "PE10", "PE11"; + allwinner,pname = "ts1_clk", "ts1_err", "ts1_sync", "ts1_dvld", "ts1_d0"; + allwinner,function = "ts1"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + ts1_sleep@0 { + allwinner,pins = "PE7", "PE8", "PE9", "PE10", "PE11"; + allwinner,pname = "ts1_clk", "ts1_err", "ts1_sync", "ts1_dvld", "ts1_d0"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + ts2@0 { + allwinner,pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD9", "PD10", "PD11"; + allwinner,pname = "ts2_clk", "ts2_err", "ts2_sync", "ts2_dvld", "ts2_d0", "ts2_d1", "ts2_d2", "ts2_d3", "ts2_d4", "ts2_d5", "ts2_d6", "ts2_d7"; + allwinner,function = "ts2"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + ts2_sleep@0 { + allwinner,pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD9", "PD10", "PD11"; + allwinner,pname = "ts2_clk", "ts2_err", "ts2_sync", "ts2_dvld", "ts2_d0", "ts2_d1", "ts2_d2", "ts2_d3", "ts2_d4", "ts2_d5", "ts2_d6", "ts2_d7"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + ts3@0 { + allwinner,pins = "PD7", "PD8", "PD9", "PD10", "PD11"; + allwinner,pname = "ts3_clk", "ts3_err", "ts3_sync", "ts3_dvld", "ts3_d0"; + allwinner,function = "ts3"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + ts3_sleep@0 { + allwinner,pins = "PD7", "PD8", "PD9", "PD10", "PD11"; + allwinner,pname = "ts3_clk", "ts3_err", "ts3_sync", "ts3_dvld", "ts3_d0"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + spi0@0 { + allwinner,pins = "PC0", "PC2", "PC3", "PC5", "PC4"; + allwinner,pname = "spi0_sclk", "spi0_mosi", "spi0_miso", "spi0_hold", "spi0_wp"; + allwinner,function = "spi0"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x46>; + }; + + spi0@1 { + allwinner,pins = "PC1", "PC6"; + allwinner,pname = "spi0_cs0", "spi0_cs1"; + allwinner,function = "spi0"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x47>; + }; + + spi0@2 { + allwinner,pins = "PC0", "PC1", "PC2", "PC3", "PC4", "PC5", "PC6"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x48>; + }; + + spi1@0 { + allwinner,pins = "PH11", "PH12", "PH13"; + allwinner,pname = "spi1_sclk", "spi1_mosi", "spi1_miso"; + allwinner,function = "spi1"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x4a>; + }; + + spi1@1 { + allwinner,pins = "PH14", "PH15"; + allwinner,pname = "spi1_cs0", "spi1_cs1"; + allwinner,function = "spi1"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x4b>; + }; + + spi1@2 { + allwinner,pins = "PH11", "PH12", "PH13", "PH14", "PH15"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x4c>; + }; + + spi2@0 { + allwinner,pins = "PE18", "PE19", "PE20"; + allwinner,pname = "spi2_sclk", "spi2_mosi", "spi2_miso"; + allwinner,function = "spi2"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x4e>; + }; + + spi2@1 { + allwinner,pins = "PE21"; + allwinner,pname = "spi2_cs0"; + allwinner,function = "spi2"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x4f>; + }; + + spi2@2 { + allwinner,pins = "PE18", "PE19", "PE20", "PW21"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x50>; + }; + + spi3@0 { + allwinner,pins = "PI3", "PI4", "PI5"; + allwinner,pname = "spi3_sclk", "spi3_mosi", "spi3_miso"; + allwinner,function = "spi3"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + spi3@1 { + allwinner,pins = "PI6"; + allwinner,pname = "spi3_cs0"; + allwinner,function = "spi3"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + }; + + spi3@2 { + allwinner,pins = "PI3", "PI4", "PI5", "PI6"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + sdc0@0 { + allwinner,pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; + allwinner,function = "sdc0"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x03>; + allwinner,pull = <0x01>; + phandle = <0x5b>; + }; + + sdc1@0 { + allwinner,pins = "PG0", "PG1", "PG2", "PG3", "PG4", "PG5"; + allwinner,function = "sdc1"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x03>; + allwinner,pull = <0x01>; + phandle = <0x5f>; + }; + + sdc1@1 { + allwinner,pins = "PG0", "PG1", "PG2", "PG3", "PG4", "PG5"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x60>; + }; + + sdc2@0 { + allwinner,pins = "PC1", "PC4", "PC5", "PC6", "PC7", "PC8", "PC9", "PC11", "PC12", "PC13", "PC14", "PC0"; + allwinner,function = "sdc2"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x02>; + allwinner,pull = <0x01>; + phandle = <0x55>; + }; + + sdc2@1 { + allwinner,pins = "PC1", "PC4", "PC5", "PC6", "PC7", "PC8", "PC9", "PC10", "PC11", "PC12", "PC13", "PC14"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + phandle = <0x57>; + }; + + sdc2@2 { + allwinner,pins = "PC0"; + allwinner,function = "sdc2"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x02>; + allwinner,pull = <0x02>; + phandle = <0x56>; + }; + + aif2@0 { + allwinner,pins = "PH1", "PH2", "PH3", "PH4"; + allwinner,function = "aif2"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + aif3@0 { + allwinner,pins = "PG10", "PG11", "PG12", "PG13"; + allwinner,function = "aif3"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + aif2_sleep@0 { + allwinner,pins = "PH1", "PH2", "PH3", "PH4"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + aif3_sleep@0 { + allwinner,pins = "PG10", "PG11", "PG12", "PG13"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + owc0@0 { + allwinner,pins = "PH4"; + allwinner,pname = "owc0"; + allwinner,function = "owc0"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x23>; + }; + + owc0_sleep@0 { + allwinner,pins = "PH4"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x24>; + }; + + daudio0@0 { + allwinner,pins = "PH0", "PH1", "PH2", "PH3", "PH4"; + allwinner,function = "i2s0"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x3b>; + }; + + daudio0_sleep@0 { + allwinner,pins = "PH0", "PH1", "PH2", "PH3", "PH4"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x3c>; + }; + + daudio1@0 { + allwinner,pins = "PD10", "PD11", "PD12", "PD13", "PD14", "PD15", "PD16", "PD17"; + allwinner,function = "i2s1"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x3e>; + }; + + daudio1_sleep@0 { + allwinner,pins = "PD10", "PD11", "PD12", "PD13", "PD14", "PD15", "PD16", "PD17"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x3f>; + }; + + csi_mclk0@0 { + allwinner,pins = "PI0"; + allwinner,pname = "csi_mclk0"; + allwinner,function = "csi_mclk0"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x02>; + allwinner,pull = <0x00>; + phandle = <0x8a>; + }; + + csi_mclk0@1 { + allwinner,pins = "PI0"; + allwinner,pname = "csi_mclk0"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x02>; + allwinner,pull = <0x00>; + phandle = <0x8b>; + }; + + csi_mclk1@0 { + allwinner,pins = "PE1"; + allwinner,pname = "csi_mclk1"; + allwinner,function = "csi_mclk1"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x02>; + allwinner,pull = <0x00>; + phandle = <0x8c>; + }; + + csi_mclk1@1 { + allwinner,pins = "PE1"; + allwinner,pname = "csi_mclk1"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x02>; + allwinner,pull = <0x00>; + phandle = <0x8d>; + }; + + csi1@0 { + allwinner,pins = "PE0", "PE2", "PE3", "PE4", "PE5", "PE6", "PE7", "PE8", "PE9", "PE10", "PE11", "PE12", "PE13", "PE14", "PE15", "PE18", "PE19", "PE20", "PE21"; + allwinner,pname = "csi1_pck", "csi1_hsync", "csi1_vsync", "csi1_d0", "csi1_d1", "csi1_d2", "csi1_d3", "csi1_d4", "csi1_d5", "csi1_d6", "csi1_d7", "csi1_d8", "csi1_d9", "csi1_d10", "csi1_d11", "csi1_d12", "csi1_d13", "csi1_d14", "csi1_d15"; + allwinner,function = "csi1"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x8e>; + }; + + csi1@1 { + allwinner,pins = "PE0", "PE2", "PE3", "PE4", "PE5", "PE6", "PE7", "PE8", "PE9", "PE10", "PE11", "PE12", "PE13", "PE14", "PE15", "PE18", "PE19", "PE20", "PE21"; + allwinner,pname = "csi1_pck", "csi1_hsync", "csi1_vsync", "csi1_d0", "csi1_d1", "csi1_d2", "csi1_d3", "csi1_d4", "csi1_d5", "csi1_d6", "csi1_d7", "csi1_d8", "csi1_d9", "csi1_d10", "csi1_d11", "csi1_d12", "csi1_d13", "csi1_d14", "csi1_d15"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x8f>; + }; + + scr0@0 { + allwinner,pins = "PH21", "PH24", "PH25", "PH26", "PH27"; + allwinner,pname = "scr0_rst", "scr0_det", "scr0_vccen", "scr0_sck", "scr0_sda"; + allwinner,function = "sim0"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x00>; + allwinner,pull = <0x01>; + }; + + scr0@1 { + allwinner,pins = "PH22", "PH23"; + allwinner,pname = "scr0_vppen", "scr0_vppp"; + allwinner,function = "sim0"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x00>; + allwinner,pull = <0x01>; + }; + + scr0@2 { + allwinner,pins = "PH21", "PH22", "PH23", "PH24", "PH25", "PH26", "P27"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x00>; + allwinner,pull = <0x00>; + }; + + scr1@0 { + allwinner,pins = "PD15", "PD16", "PD12", "PD13", "PD14"; + allwinner,pname = "scr1_rst", "scr1_det", "scr1_vccen", "scr1_sck", "scr1_sda"; + allwinner,function = "sim1"; + allwinner,muxsel = <0x04>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + }; + + scr1@1 { + allwinner,pins = "PE14", "PE15"; + allwinner,pname = "scr1_vppen", "scr1_vppp"; + allwinner,function = "sim1"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + }; + + scr1@2 { + allwinner,pins = "PD15", "PD16", "PD12", "PD13", "PD14", "PE14", "PE15"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + nand0@0 { + allwinner,pins = "PC0", "PC1", "PC2", "PC4", "PC6", "PC7", "PC8", "PC9", "PC10", "PC11", "PC12", "PC13", "PC14"; + allwinner,pname = "nand0_we", "nand0_ale", "nand0_cle", "nand0_nre", "nand0_d7", "nand0_d6", "nand0_d5", "nand0_d4", "nand0_ndqs", "nand0_d3", "nand0_d2", "nand0_d1", "nand0_d0"; + allwinner,function = "nand0"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + nand0@1 { + allwinner,pins = "PC3", "PC5"; + allwinner,pname = "nand0_ce0", "nand0_rb0"; + allwinner,function = "nand0"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x01>; + allwinner,pull = <0x01>; + }; + + nand0@2 { + allwinner,pins = "PC0", "PC1", "PC2", "PC3", "PC4", "PC5", "PC6", "PC7", "PC8", "PC9", "PC10", "PC11", "PC12", "PC13", "PC14"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + hdmi@0 { + allwinner,pins = "PH13", "PH14"; + allwinner,function = "ddc"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + hdmi@1 { + allwinner,pins = "PH13", "PH14"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + hdmi@2 { + allwinner,pins = "PH15"; + allwinner,function = "hcec"; + allwinner,muxsel = <0x03>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + hdmi@3 { + allwinner,pins = "PH15"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + }; + + gmac@0 { + allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "PD20", "PD21"; + allwinner,function = "gmac0"; + allwinner,muxsel = <0x05>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x9e>; + }; + + gmac@1 { + allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "PD20", "PD21"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x9f>; + }; + + sdc0@1 { + allwinner,pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0xa0>; + }; + + rgb8@0 { + allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "PD19", "PD20", "PD21"; + allwinner,pname = "lcdd3", "lcdd4", "lcdd5", "lcdd6", "lcdd7", "lcdd10", "lcdd11", "lcdd12", "lcdckl", "lcdde", "lcdhsync", "lcdvsync"; + allwinner,function = "rgb8"; + allwinner,muxsel = <0x02>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x64>; + }; + + rgb8@1 { + allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "PD19", "PD20", "PD21"; + allwinner,function = "io_disabled"; + allwinner,muxsel = <0x07>; + allwinner,drive = <0x01>; + allwinner,pull = <0x00>; + phandle = <0x65>; + }; + }; + + dma-controller@03002000 { + compatible = "allwinner,sun8i-dma"; + reg = <0x00 0x3002000 0x00 0x1000>; + interrupts = <0x00 0x0a 0x04>; + clocks = <0x12>; + #dma-cells = <0x01>; + }; + + timer@03009000 { + compatible = "allwinner,sun4i-a10-timer"; + device_type = "soc_timer"; + reg = <0x00 0x3009000 0x00 0x90>; + interrupts = <0x00 0x3c 0x04>; + clocks = <0x08 0x10>; + }; + + rtc@07000000 { + compatible = "allwinner,sun8i-rtc", "allwinner,sunxi-rtc"; + device_type = "rtc"; + reg = <0x00 0x7000000 0x00 0x400>; + interrupts = <0x00 0x69 0x04>; + auto_switch; + wakeup-source; + gpr_offset = <0x100>; + gpr_len = <0x08>; + }; + + watchdog@030090a0 { + compatible = "allwinner,sun50i-wdt"; + reg = <0x00 0x30090a0 0x00 0x20>; + interrupts = <0x00 0x0e 0x04>; + }; + + ve@01c0e000 { + compatible = "allwinner,sunxi-cedar-ve"; + reg = <0x00 0x1c0e000 0x00 0x1000 0x00 0x3000000 0x00 0x10 0x00 0x3001000 0x00 0x800 0x00 0x7010000 0x00 0x300>; + interrupts = <0x00 0x19 0x04>; + clocks = <0x05 0x13>; + iommus = <0x14 0x03 0x01>; + }; + + ise@01c10000 { + compatible = "allwinner,sunxi-ise"; + reg = <0x00 0x1c10000 0x00 0x1000 0x00 0x3001000 0x00 0x800>; + interrupts = <0x00 0x18 0x04>; + }; + + eise@0x02300000 { + compatible = "allwinner,sunxi-eise"; + reg = <0x00 0x2300000 0x00 0x1000 0x00 0x3001000 0x00 0x800>; + interrupts = <0x00 0x1a 0x04>; + clocks = <0x05 0x15>; + iommus = <0x14 0x01 0x01>; + }; + + uart@05000000 { + compatible = "allwinner,sun8i-uart"; + device_type = "uart0"; + reg = <0x00 0x5000000 0x00 0x400>; + interrupts = <0x00 0x31 0x04>; + clocks = <0x16>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x17>; + pinctrl-1 = <0x18>; + uart0_port = <0x00>; + uart0_type = <0x02>; + status = "okay"; + }; + + uart@05000400 { + compatible = "allwinner,sun8i-uart"; + device_type = "uart1"; + reg = <0x00 0x5000400 0x00 0x400>; + interrupts = <0x00 0x32 0x04>; + clocks = <0x19>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x1a>; + pinctrl-1 = <0x1b>; + uart1_port = <0x01>; + uart1_type = <0x04>; + status = "disabled"; + }; + + uart@05000800 { + compatible = "allwinner,sun8i-uart"; + device_type = "uart2"; + reg = <0x00 0x5000800 0x00 0x400>; + interrupts = <0x00 0x33 0x04>; + clocks = <0x1c>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x1d>; + pinctrl-1 = <0x1e>; + uart2_port = <0x02>; + uart2_type = <0x04>; + status = "disabled"; + }; + + uart@05000c00 { + compatible = "allwinner,sun8i-uart"; + device_type = "uart3"; + reg = <0x00 0x5000c00 0x00 0x400>; + interrupts = <0x00 0x34 0x04>; + clocks = <0x1f>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x20>; + pinctrl-1 = <0x21>; + uart3_port = <0x03>; + uart3_type = <0x04>; + status = "disabled"; + }; + + s_owc@07040400 { + compatible = "allwinner,sunxi-owc"; + device_type = "s_owc0"; + reg = <0x00 0x7040400 0x00 0x30>; + interrupts = <0x00 0x72 0x04>; + clocks = <0x08 0x22>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x23>; + pinctrl-1 = <0x24>; + status = "okay"; + }; + + twi@0x05002000 { + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "allwinner,sun8i-twi"; + device_type = "twi0"; + reg = <0x00 0x5002000 0x00 0x400>; + interrupts = <0x00 0x29 0x04>; + clocks = <0x25>; + clock-frequency = <0x61a80>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x26>; + pinctrl-1 = <0x27>; + twi_drv_used = <0x01>; + status = "disable"; + }; + + twi@0x05002400 { + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "allwinner,sun8i-twi"; + device_type = "twi1"; + reg = <0x00 0x5002400 0x00 0x400>; + interrupts = <0x00 0x2a 0x04>; + clocks = <0x28>; + clock-frequency = <0x61a80>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x29>; + pinctrl-1 = <0x2a>; + twi_drv_used = <0x01>; + status = "okay"; + }; + + twi@0x05002800 { + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "allwinner,sun8i-twi"; + device_type = "twi2"; + reg = <0x00 0x5002800 0x00 0x400>; + interrupts = <0x00 0x2b 0x04>; + clocks = <0x2b>; + clock-frequency = <0x30d40>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x2c>; + pinctrl-1 = <0x2d>; + twi_drv_used = <0x01>; + status = "disable"; + }; + + twi@0x05002c00 { + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "allwinner,sun8i-twi"; + device_type = "twi3"; + reg = <0x00 0x5002c00 0x00 0x400>; + interrupts = <0x00 0x2c 0x04>; + clocks = <0x2e>; + clock-frequency = <0x30d40>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x2f>; + pinctrl-1 = <0x30>; + twi_drv_used = <0x01>; + status = "disable"; + }; + + twi@0x07081400 { + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "allwinner,sun8i-twi"; + device_type = "twi4"; + reg = <0x00 0x7081400 0x00 0x400>; + interrupts = <0x00 0x6b 0x04>; + clocks = <0x31>; + clock-frequency = <0x30d40>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x32>; + pinctrl-1 = <0x33>; + status = "disabled"; + }; + + usbc0@0 { + device_type = "usbc0"; + compatible = "allwinner,sunxi-otg-manager"; + usb_port_type = <0x00>; + usb_detect_type = <0x00>; + usb_id_gpio; + usb_det_vbus_gpio; + usb_drv_vbus_gpio; + usb_host_init_state = <0x00>; + usb_regulator_io = "nocare"; + usb_wakeup_suspend = <0x00>; + usb_luns = <0x03>; + usb_serial_unique = <0x00>; + usb_serial_number = "20080411"; + rndis_wceis = <0x01>; + status = "okay"; + usb_detect_mode = <0x00>; + }; + + udc-controller@0x05100000 { + compatible = "allwinner,sunxi-udc"; + reg = <0x00 0x5100000 0x00 0x1000 0x00 0x00 0x00 0x100>; + interrupts = <0x00 0x40 0x04>; + clocks = <0x34 0x35>; + status = "okay"; + }; + + ehci0-controller@0x05101000 { + compatible = "allwinner,sunxi-ehci0"; + reg = <0x00 0x5101000 0x00 0xfff 0x00 0x00 0x00 0x100 0x00 0x5100000 0x00 0x1000>; + interrupts = <0x00 0x41 0x04>; + clocks = <0x34 0x36>; + hci_ctrl_no = <0x00>; + status = "okay"; + }; + + ohci0-controller@0x05101400 { + compatible = "allwinner,sunxi-ohci0"; + reg = <0x00 0x5101000 0x00 0xfff 0x00 0x00 0x00 0x100 0x00 0x5100000 0x00 0x1000>; + interrupts = <0x00 0x42 0x04>; + clocks = <0x34 0x37 0x38 0x39 0x08 0x10>; + hci_ctrl_no = <0x00>; + status = "okay"; + }; + + daudio@0x05090000 { + compatible = "allwinner,sunxi-daudio"; + reg = <0x00 0x5090000 0x00 0x7c>; + clocks = <0x02 0x3a>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x3b>; + pinctrl-1 = <0x3c>; + device_type = "daudio0"; + tdm_num = <0x00>; + status = "okay"; + rate_max = <0x2ee00>; + channels_max = <0x08>; + buffer_bytes_max = <0x20000>; + period_bytes_max = <0x10000>; + periods_max = <0x08>; + fifo_size = <0x80>; + mclk_div = <0x00>; + frametype = <0x00>; + tdm_config = <0x01>; + sign_extend = <0x00>; + tx_data_mode = <0x00>; + rx_data_mode = <0x00>; + msb_lsb_first = <0x00>; + pcm_lrck_period = <0x80>; + slot_width_select = <0x20>; + phandle = <0x43>; + }; + + daudio@0x05091000 { + compatible = "allwinner,sunxi-daudio"; + reg = <0x00 0x5091000 0x00 0x7c>; + clocks = <0x02 0x3d>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x3e>; + pinctrl-1 = <0x3f>; + device_type = "daudio1"; + tdm_num = <0x01>; + status = "disabled"; + rate_max = <0x2ee00>; + channels_max = <0x08>; + buffer_bytes_max = <0x20000>; + period_bytes_max = <0x10000>; + periods_max = <0x08>; + fifo_size = <0x80>; + mclk_div = <0x01>; + frametype = <0x00>; + tdm_config = <0x01>; + sign_extend = <0x00>; + tx_data_mode = <0x00>; + rx_data_mode = <0x00>; + msb_lsb_first = <0x00>; + pcm_lrck_period = <0x80>; + slot_width_select = <0x20>; + phandle = <0x44>; + }; + + codec@0x05096000 { + compatible = "allwinner,sunxi-internal-codec"; + reg = <0x00 0x5096000 0x00 0x320>; + clocks = <0x02 0x40>; + device_type = "codec"; + status = "okay"; + rate_max = <0x2ee00>; + channels_max = <0x08>; + buffer_bytes_max = <0x20000>; + period_bytes_max = <0x10000>; + periods_max = <0x08>; + fifo_size = <0x80>; + pa_level = <0x01>; + main_gain = <0x17>; + adcdrc_cfg = <0x00>; + adchpf_cfg = <0x01>; + dacdrc_cfg = <0x01>; + dachpf_cfg = <0x00>; + digital_vol = <0x00>; + lineout_vol = <0x1f>; + pa_msleep_time = <0xa0>; + gpio-spk = <0x41 0x07 0x0e 0x01 0x01 0x01 0x01>; + phandle = <0x42>; + }; + + sound@0 { + compatible = "allwinner,sunxi-codec-machine"; + interrupts = <0x00 0x1c 0x04>; + sunxi,audio-codec = <0x42>; + device_type = "sndcodec"; + status = "okay"; + }; + + sound@1 { + compatible = "allwinner,sunxi-daudio0-machine"; + sunxi,daudio-controller = <0x43>; + device_type = "snddaudio0"; + status = "okay"; + audio_format = <0x01>; + daudio_master = <0x04>; + signal_inversion = <0x01>; + }; + + sound@2 { + compatible = "allwinner,sunxi-daudio1-machine"; + sunxi,daudio-controller = <0x44>; + device_type = "snddaudio1"; + status = "disabled"; + audio_format = <0x01>; + daudio_master = <0x04>; + signal_inversion = <0x01>; + }; + + spi@05010000 { + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "allwinner,sun8i-spi"; + device_type = "spi0"; + reg = <0x00 0x5010000 0x00 0x1000>; + interrupts = <0x00 0x36 0x04>; + clocks = <0x04 0x45>; + clock-frequency = <0x11e1a300>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x46 0x47>; + pinctrl-1 = <0x48>; + spi0_cs_number = <0x02>; + spi0_cs_bitmap = <0x03>; + status = "okay"; + + spi_board0 { + device_type = "spi_board0"; + compatible = "m25p80"; + spi-max-frequency = <0x5f5e100>; + reg = <0x00>; + spi-rx-bus-width = <0x01>; + spi-tx-bus-width = <0x01>; + }; + }; + + spi@05011000 { + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "allwinner,sun8i-spi"; + device_type = "spi1"; + reg = <0x00 0x5011000 0x00 0x1000>; + interrupts = <0x00 0x37 0x04>; + clocks = <0x04 0x49>; + clock-frequency = <0xbebc200>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x4a 0x4b>; + pinctrl-1 = <0x4c>; + spi1_cs_number = <0x02>; + spi1_cs_bitmap = <0x03>; + status = "disabled"; + }; + + spi@05012000 { + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "allwinner,sun8i-spi"; + device_type = "spi2"; + reg = <0x00 0x5012000 0x00 0x1000>; + interrupts = <0x00 0x38 0x04>; + clocks = <0x04 0x4d>; + clock-frequency = <0xbebc200>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x4e 0x4f>; + pinctrl-1 = <0x50>; + spi2_cs_number = <0x01>; + spi2_cs_bitmap = <0x01>; + status = "disabled"; + }; + + auto_print { + device_type = "auto_print"; + status = "okay"; + }; + + sdmmc@04022000 { + compatible = "allwinner,sunxi-mmc-v4p5x"; + device_type = "sdc2"; + reg = <0x00 0x4022000 0x00 0x1000>; + interrupts = <0x00 0x30 0x04>; + clocks = <0x08 0x51 0x52 0x53 0x54>; + clock-names = "osc24m", "pll_periph", "mmc", "ahb", "rst"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x55 0x56>; + pinctrl-1 = <0x57>; + bus-width = <0x08>; + cap-sd-highspeed; + cap-mmc-highspeed; + cap-wait-while-busy; + mmc-high-capacity-erase-size; + cap-erase; + max-frequency = <0x2faf080>; + sdc_tm4_sm0_freq0 = <0x00>; + sdc_tm4_sm0_freq1 = <0x00>; + sdc_tm4_sm1_freq0 = <0x00>; + sdc_tm4_sm1_freq1 = <0x00>; + sdc_tm4_sm2_freq0 = <0x00>; + sdc_tm4_sm2_freq1 = <0x00>; + sdc_tm4_sm3_freq0 = <0x5000000>; + sdc_tm4_sm3_freq1 = <0x05>; + sdc_tm4_sm4_freq0 = <0x50000>; + sdc_tm4_sm4_freq1 = <0x04>; + status = "disable"; + }; + + sdmmc@04020000 { + compatible = "allwinner,sunxi-mmc-v4p1x"; + device_type = "sdc0"; + reg = <0x00 0x4020000 0x00 0x1000>; + interrupts = <0x00 0x2e 0x04>; + clocks = <0x08 0x51 0x58 0x59 0x5a>; + clock-names = "osc24m", "pll_periph", "mmc", "ahb", "rst"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x5b>; + max-frequency = <0x8f0d180>; + bus-width = <0x04>; + cap-sd-highspeed; + cap-mmc-highspeed; + cap-wait-while-busy; + status = "okay"; + cd-gpios = <0x41 0x05 0x06 0x00 0x01 0x03 0xffffffff>; + card-pwr-gpios = <0x41 0x02 0x04 0x01 0x01 0x02 0xffffffff>; + sunxi-power-save-mode; + sunxi-signal-vol-sw-without-pmu; + sd-uhs-sdr50; + sd-uhs-ddr50; + sd-uhs-sdr104; + min-frequency = <0x186a0>; + cd-used-24M; + no-sdio; + ctl-spec-caps = <0x08>; + }; + + sdmmc@04021000 { + compatible = "allwinner,sunxi-mmc-v4p1x"; + device_type = "sdc1"; + reg = <0x00 0x4021000 0x00 0x1000>; + interrupts = <0x00 0x2f 0x04>; + clocks = <0x08 0x51 0x5c 0x5d 0x5e>; + clock-names = "osc24m", "pll_periph", "mmc", "ahb", "rst"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x5f>; + pinctrl-1 = <0x60>; + max-frequency = <0x2faf080>; + bus-width = <0x04>; + cap-sd-highspeed; + cap-mmc-highspeed; + cap-wait-while-busy; + sunxi-dly-52M-ddr4 = <0x01 0x00 0x00 0x00 0x02>; + sunxi-dly-104M = <0x01 0x00 0x00 0x00 0x01>; + sunxi-dly-208M = <0x01 0x00 0x00 0x00 0x01>; + status = "okay"; + cap-sdio-irq; + keep-power-in-suspend; + ignore-pm-notify; + }; + + disp@01000000 { + compatible = "allwinner,sunxi-disp"; + reg = <0x00 0x1000000 0x00 0x3fffff 0x00 0x6510000 0x00 0xfff 0x00 0x6511000 0x00 0xfff 0x00 0x6504000 0x00 0x2000>; + interrupts = <0x00 0x3e 0x04 0x00 0x3f 0x04 0x00 0x2d 0x04>; + clocks = <0x0a 0x61 0x62 0x63 0x0e>; + boot_disp = <0x00>; + fb_base = <0x00>; + iommus = <0x14 0x00 0x00>; + status = "okay"; + disp_init_enable = <0x01>; + disp_mode = <0x00>; + screen0_output_type = <0x01>; + screen0_output_mode = <0x04>; + disp_rotation_used = <0x01>; + degree0 = <0x01>; + fb0_format = <0x00>; + fb0_width = <0x140>; + fb0_height = <0xf0>; + lcd0_backlight = <0x32>; + lcd0_bright = <0x32>; + lcd0_contrast = <0x32>; + lcd0_saturation = <0x39>; + lcd0_hue = <0x32>; + }; + + lcd0@01c0c000 { + compatible = "allwinner,sunxi-lcd0"; + pinctrl-names = "active", "sleep"; + status = "okay"; + lcd_used = <0x01>; + lcd_driver_name = "st7789v_cpu"; + lcd_backlight = <0x96>; + lcd_if = <0x01>; + lcd_x = <0xf0>; + lcd_y = <0x140>; + lcd_width = <0x6c>; + lcd_height = <0x40>; + lcd_dclk_freq = <0x12>; + lcd_pwm_used = <0x01>; + lcd_pwm_ch = <0x08>; + lcd_pwm_freq = <0xc350>; + lcd_pwm_pol = <0x01>; + lcd_pwm_max_limit = <0xff>; + lcd_hbp = <0x3c>; + lcd_ht = <0x12a>; + lcd_hspw = <0x1e>; + lcd_vbp = <0x154>; + lcd_vt = <0x150>; + lcd_vspw = <0x02>; + lcd_frm = <0x01>; + lcd_io_phase = <0x00>; + lcd_hv_clk_phase = <0x00>; + lcd_hv_sync_polarity = <0x00>; + lcd_hv_data_polarity = <0x01>; + lcd_gamma_en = <0x00>; + lcd_bright_curve_en = <0x00>; + lcd_cmap_en = <0x00>; + lcdgamma4iep = <0x16>; + lcd_cpu_mode = <0x01>; + lcd_cpu_te = <0x01>; + lcd_cpu_if = <0x0c>; + lcd_hv_if = <0x00>; + lcd_hv_srgb_seq = <0x00>; + lcd_rb_swap = <0x00>; + lcd_gpio_0 = <0x41 0x04 0x10 0x01 0x00 0x03 0x00>; + lcd_gpio_1 = <0x41 0x04 0x11 0x01 0x00 0x03 0x00>; + pinctrl-0 = <0x64>; + pinctrl-1 = <0x65>; + }; + + tr@01000000 { + compatible = "allwinner,sun50i-tr"; + reg = <0x00 0x1000000 0x00 0x200bc>; + interrupts = <0x00 0x60 0x04>; + clocks = <0x0a>; + status = "okay"; + }; + + g2d@01480000 { + compatible = "allwinner,sunxi-g2d"; + reg = <0x00 0x1480000 0x00 0xbffff>; + interrupts = <0x00 0x15 0x04>; + clocks = <0x0b>; + iommus = <0x14 0x06 0x01>; + status = "okay"; + }; + + nna@02400000 { + compatible = "allwinner,sunxi-nna"; + reg = <0x00 0x2400000 0x00 0x1ffff>; + interrupts = <0x00 0x14 0x04>; + clocks = <0x0c 0x66>; + iommus = <0x14 0x02 0x01>; + status = "okay"; + }; + + pwm@0300a000 { + compatible = "allwinner,sunxi-pwm"; + reg = <0x00 0x300a000 0x00 0x400>; + status = "okay"; + clocks = <0x67>; + pwm-number = <0x0a>; + pwm-base = <0x00>; + pwms = <0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f 0x70 0x71>; + }; + + pwm0@0300a000 { + compatible = "allwinner,sunxi-pwm0"; + pinctrl-names = "active", "sleep"; + pinctrl-0 = <0x72>; + pinctrl-1 = <0x73>; + status = "okay"; + reg_base = <0x300a000>; + phandle = <0x68>; + }; + + pwm1@0300a000 { + compatible = "allwinner,sunxi-pwm1"; + pinctrl-names = "active", "sleep"; + reg_base = <0x300a000>; + pinctrl-0 = <0x74>; + pinctrl-1 = <0x75>; + status = "okay"; + phandle = <0x69>; + }; + + pwm2@0300a000 { + compatible = "allwinner,sunxi-pwm2"; + pinctrl-names = "active", "sleep"; + reg_base = <0x300a000>; + pinctrl-0 = <0x76>; + pinctrl-1 = <0x77>; + status = "okay"; + phandle = <0x6a>; + }; + + pwm3@0300a000 { + compatible = "allwinner,sunxi-pwm3"; + pinctrl-names = "active", "sleep"; + reg_base = <0x300a000>; + pinctrl-0 = <0x78>; + pinctrl-1 = <0x79>; + status = "okay"; + phandle = <0x6b>; + }; + + pwm4@0300a000 { + compatible = "allwinner,sunxi-pwm4"; + pinctrl-names = "active", "sleep"; + reg_base = <0x300a000>; + pinctrl-0 = <0x7a>; + pinctrl-1 = <0x7b>; + status = "okay"; + phandle = <0x6c>; + }; + + pwm5@0300a000 { + compatible = "allwinner,sunxi-pwm5"; + pinctrl-names = "active", "sleep"; + reg_base = <0x300a000>; + pinctrl-0 = <0x7c>; + pinctrl-1 = <0x7d>; + status = "okay"; + phandle = <0x6d>; + }; + + pwm6@0300a000 { + compatible = "allwinner,sunxi-pwm6"; + pinctrl-names = "active", "sleep"; + reg_base = <0x300a000>; + pinctrl-0 = <0x7e>; + pinctrl-1 = <0x7f>; + status = "okay"; + phandle = <0x6e>; + }; + + pwm7@0300a000 { + compatible = "allwinner,sunxi-pwm7"; + pinctrl-names = "active", "sleep"; + reg_base = <0x300a000>; + pinctrl-0 = <0x80>; + pinctrl-1 = <0x81>; + status = "okay"; + phandle = <0x6f>; + }; + + pwm8@0300a000 { + compatible = "allwinner,sunxi-pwm8"; + pinctrl-names = "active", "sleep"; + reg_base = <0x300a000>; + pinctrl-0 = <0x82>; + pinctrl-1 = <0x83>; + status = "okay"; + phandle = <0x70>; + }; + + pwm9@0300a000 { + compatible = "allwinner,sunxi-pwm9"; + pinctrl-names = "active", "sleep"; + reg_base = <0x300a000>; + pinctrl-0 = <0x84>; + pinctrl-1 = <0x85>; + status = "okay"; + phandle = <0x71>; + }; + + boot_disp { + compatible = "allwinner,boot_disp"; + }; + + vind@0 { + compatible = "allwinner,sunxi-vin-media", "simple-bus"; + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + device_id = <0x00>; + vind0_clk = <0x1017df80>; + vind0_isp = <0xbebc200>; + reg = <0x00 0x6600800 0x00 0x200 0x00 0x6600000 0x00 0x800>; + interrupts = <0x00 0x5c 0x04>; + clocks = <0x86 0x03 0x87 0x08 0x03 0x88 0x08 0x03 0x89 0x05>; + pinctrl-names = "mclk0-default", "mclk0-sleep", "mclk1-default", "mclk1-sleep"; + pinctrl-0 = <0x8a>; + pinctrl-1 = <0x8b>; + pinctrl-2 = <0x8c>; + pinctrl-3 = <0x8d>; + status = "okay"; + + csi@0 { + device_type = "csi0"; + compatible = "allwinner,sunxi-csi"; + reg = <0x00 0x6601000 0x00 0x1000>; + interrupts = <0x00 0x4e 0x04>; + device_id = <0x00>; + iommus = <0x14 0x04 0x01>; + status = "okay"; + }; + + csi@1 { + device_type = "csi1"; + compatible = "allwinner,sunxi-csi"; + reg = <0x00 0x6602000 0x00 0x1000>; + interrupts = <0x00 0x4f 0x04>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x8e>; + pinctrl-1 = <0x8f>; + device_id = <0x01>; + iommus = <0x14 0x04 0x01>; + status = "okay"; + }; + + mipi@0 { + compatible = "allwinner,sunxi-mipi"; + reg = <0x00 0x660c000 0x00 0x1000>; + interrupts = <0x00 0x52 0x04>; + device_id = <0x00>; + status = "okay"; + }; + + isp@0 { + compatible = "allwinner,sunxi-isp"; + reg = <0x00 0x2100000 0x00 0x800>; + interrupts = <0x00 0x21 0x04>; + device_id = <0x00>; + iommus = <0x14 0x05 0x01>; + status = "okay"; + }; + + isp@1 { + compatible = "allwinner,sunxi-isp"; + device_id = <0x01>; + status = "okay"; + }; + + scaler@0 { + compatible = "allwinner,sunxi-scaler"; + reg = <0x00 0x2104000 0x00 0x400>; + device_id = <0x00>; + iommus = <0x14 0x05 0x01>; + status = "okay"; + }; + + scaler@1 { + compatible = "allwinner,sunxi-scaler"; + reg = <0x00 0x2104400 0x00 0x400>; + device_id = <0x01>; + iommus = <0x14 0x05 0x01>; + status = "okay"; + }; + + scaler@2 { + compatible = "allwinner,sunxi-scaler"; + reg = <0x00 0x2104800 0x00 0x400>; + device_id = <0x02>; + iommus = <0x14 0x05 0x01>; + status = "okay"; + }; + + scaler@3 { + compatible = "allwinner,sunxi-scaler"; + reg = <0x00 0x2104c00 0x00 0x400>; + device_id = <0x03>; + iommus = <0x14 0x05 0x01>; + status = "okay"; + }; + + actuator@0 { + device_type = "actuator0"; + compatible = "allwinner,sunxi-actuator"; + actuator0_name = "ad5820_act"; + actuator0_slave = <0x18>; + actuator0_af_pwdn; + actuator0_afvdd = "afvcc-csi"; + actuator0_afvdd_vol = <0x2ab980>; + status = "disabled"; + phandle = <0x91>; + }; + + flash@0 { + device_type = "flash0"; + compatible = "allwinner,sunxi-flash"; + flash0_type = <0x02>; + flash0_en; + flash0_mode; + flash0_flvdd = [00]; + flash0_flvdd_vol; + device_id = <0x00>; + status = "disabled"; + phandle = <0x90>; + }; + + sensor@0 { + device_type = "sensor0"; + compatible = "allwinner,sunxi-sensor"; + clocks = <0x87 0x08 0x03>; + pinctrl-names = "mclk0-default", "mclk0-sleep"; + pinctrl-0 = <0x8a>; + pinctrl-1 = <0x8b>; + sensor0_mname = "sc2232_mipi"; + sensor0_twi_cci_id = <0x01>; + sensor0_twi_addr = <0x60>; + sensor0_mclk_id = <0x00>; + sensor0_pos = "rear"; + sensor0_isp_used = <0x01>; + sensor0_fmt = <0x01>; + sensor0_stby_mode = <0x00>; + sensor0_vflip = <0x00>; + sensor0_hflip = <0x00>; + sensor0_iovdd-supply; + sensor0_iovdd_vol = <0x1b7740>; + sensor0_avdd-supply; + sensor0_avdd_vol = <0x2ab980>; + sensor0_dvdd-supply; + sensor0_dvdd_vol = <0x124f80>; + sensor0_power_en; + sensor0_reset = <0x41 0x08 0x03 0x01 0x00 0x01 0x00>; + sensor0_pwdn = <0x41 0x08 0x04 0x01 0x00 0x01 0x00>; + flash_handle = <0x90>; + act_handle = <0x91>; + device_id = <0x00>; + status = "okay"; + }; + + sensor@1 { + device_type = "sensor1"; + compatible = "allwinner,sunxi-sensor"; + clocks = <0x88 0x08 0x03>; + pinctrl-names = "mclk1-default", "mclk1-sleep"; + pinctrl-0 = <0x8c>; + pinctrl-1 = <0x8d>; + sensor1_mname = "ov5647"; + sensor1_twi_cci_id = <0x00>; + sensor1_twi_addr = <0x6c>; + sensor1_mclk_id = <0x01>; + sensor1_pos = "front"; + sensor1_isp_used = <0x00>; + sensor1_fmt = <0x00>; + sensor1_stby_mode = <0x00>; + sensor1_vflip = <0x00>; + sensor1_hflip = <0x00>; + sensor1_iovdd-supply; + sensor1_iovdd_vol = <0x2ab980>; + sensor1_avdd-supply; + sensor1_avdd_vol = <0x2ab980>; + sensor1_dvdd-supply; + sensor1_dvdd_vol = <0x16e360>; + sensor1_power_en; + sensor1_reset = <0x41 0x08 0x03 0x01 0x00 0x01 0x00>; + sensor1_pwdn = <0x41 0x08 0x04 0x01 0x00 0x01 0x00>; + flash_handle; + act_handle; + device_id = <0x01>; + status = "disabled"; + }; + + vinc@0 { + device_type = "vinc0"; + compatible = "allwinner,sunxi-vin-core"; + reg = <0x00 0x6609000 0x00 0x200>; + interrupts = <0x00 0x4a 0x04>; + vinc0_csi_sel = <0x00>; + vinc0_mipi_sel = <0x00>; + vinc0_isp_sel = <0x00>; + vinc0_rear_sensor_sel = <0x00>; + vinc0_front_sensor_sel = <0x00>; + vinc0_sensor_list = <0x00>; + device_id = <0x00>; + iommus = <0x14 0x04 0x01>; + status = "okay"; + vinc0_isp_tx_ch = <0x00>; + }; + + vinc@1 { + device_type = "vinc1"; + compatible = "allwinner,sunxi-vin-core"; + reg = <0x00 0x6609200 0x00 0x200>; + interrupts = <0x00 0x4b 0x04>; + vinc1_csi_sel = <0x00>; + vinc1_mipi_sel = <0x00>; + vinc1_isp_sel = <0x00>; + vinc1_rear_sensor_sel = <0x00>; + vinc1_front_sensor_sel = <0x00>; + vinc1_sensor_list = <0x00>; + device_id = <0x01>; + iommus = <0x14 0x04 0x01>; + status = "okay"; + vinc1_isp_tx_ch = <0x00>; + }; + + vinc@2 { + device_type = "vinc2"; + compatible = "allwinner,sunxi-vin-core"; + reg = <0x00 0x6609400 0x00 0x200>; + interrupts = <0x00 0x4c 0x04>; + vinc2_csi_sel = <0x00>; + vinc2_mipi_sel = <0x00>; + vinc2_isp_sel = <0x00>; + vinc2_rear_sensor_sel = <0x00>; + vinc2_front_sensor_sel = <0x00>; + vinc2_sensor_list = <0x00>; + device_id = <0x02>; + iommus = <0x14 0x04 0x01>; + status = "okay"; + vinc2_isp_tx_ch = <0x00>; + }; + + vinc@3 { + device_type = "vinc3"; + compatible = "allwinner,sunxi-vin-core"; + reg = <0x00 0x6609600 0x00 0x200>; + interrupts = <0x00 0x4d 0x04>; + vinc3_csi_sel = <0x01>; + vinc3_mipi_sel = <0xff>; + vinc3_isp_sel = <0x01>; + vinc3_rear_sensor_sel = <0x01>; + vinc3_front_sensor_sel = <0x01>; + vinc3_sensor_list = <0x00>; + device_id = <0x03>; + iommus = <0x14 0x04 0x01>; + status = "disabled"; + vinc3_isp_tx_ch = <0x00>; + }; + }; + + vdevice@0 { + compatible = "allwinner,sun8i-vdevice"; + device_type = "Vdevice"; + interrupt-parent = <0x41>; + interrupts = <0x03 0x03 0x04>; + pinctrl-names = "default"; + pinctrl-0 = <0x92>; + test-gpios = <0x41 0x02 0x03 0x01 0x02 0x02 0x01>; + status = "disabled"; + }; + + ce@1904000 { + compatible = "allwinner,sunxi-ce"; + device_name = "ce"; + reg = <0x00 0x1904000 0x00 0xa0 0x00 0x1904800 0x00 0xa0>; + interrupts = <0x00 0x23 0x01 0x00 0x24 0x01>; + clock-frequency = <0x11e1a300>; + clocks = <0x93 0x0d>; + }; + + nmi@0x01f00c00 { + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "allwinner,sunxi-nmi"; + reg = <0x00 0x1f00c00 0x00 0x50>; + nmi_irq_ctrl = <0x0c>; + nmi_irq_en = <0x40>; + nmi_irq_status = <0x10>; + nmi_irq_mask = <0x50>; + status = "okay"; + }; + + thermal_sensor { + compatible = "allwinner,thermal_sensor"; + reg = <0x00 0x5070400 0x00 0x400>; + interrupts = <0x00 0x01 0x00>; + clocks = <0x08 0x94>; + sensor_num = <0x04>; + combine_num = <0x04>; + shut_temp = <0x1c138>; + status = "okay"; + + ths_combine0 { + compatible = "allwinner,ths_combine"; + #thermal-sensor-cells = <0x01>; + combine_sensor_num = <0x01>; + combine_sensor_type = "CPU"; + combine_sensor_temp_type = "max"; + combine_sensor_id = <0x00>; + phandle = <0x95>; + }; + + ths_combine1 { + compatible = "allwinner,ths_combine"; + #thermal-sensor-cells = <0x01>; + combine_sensor_num = <0x01>; + combine_sensor_type = "VE"; + combine_sensor_temp_type = "max"; + combine_sensor_id = <0x01>; + phandle = <0x98>; + }; + + ths_combine2 { + compatible = "allwinner,ths_combine"; + #thermal-sensor-cells = <0x01>; + combine_sensor_num = <0x01>; + combine_sensor_type = "ISP"; + combine_sensor_temp_type = "max"; + combine_sensor_id = <0x02>; + phandle = <0x99>; + }; + + ths_combine3 { + compatible = "allwinner,ths_combine"; + #thermal-sensor-cells = <0x01>; + combine_sensor_num = <0x01>; + combine_sensor_type = "DDR"; + combine_sensor_temp_type = "max"; + combine_sensor_id = <0x03>; + phandle = <0x9a>; + }; + }; + + cpu_budget_cool { + compatible = "allwinner,budget_cooling"; + device_type = "cpu_budget_cooling"; + #cooling-cells = <0x02>; + status = "okay"; + state_cnt = <0x02>; + cluster_num = <0x01>; + state0 = <0x10d880 0x01>; + state1 = <0x927c0 0x01>; + phandle = <0x97>; + }; + + thermal-zones { + + cpu_thermal_zone { + polling-delay-passive = <0x3e8>; + polling-delay = <0x3e8>; + thermal-sensors = <0x95 0x00>; + + trips { + + t0 { + temperature = <0x13880>; + type = "passive"; + hysteresis = <0x00>; + phandle = <0x96>; + }; + + t2 { + temperature = <0x1c138>; + type = "critical"; + hysteresis = <0x00>; + }; + }; + + cooling-maps { + + bind0 { + contribution = <0x00>; + trip = <0x96>; + cooling-device = <0x97 0x01 0x01>; + }; + }; + }; + + ve_thermal_zone { + polling-delay-passive = <0x3e8>; + polling-delay = <0x2710>; + thermal-sensors = <0x98 0x01>; + + trips { + + t0 { + temperature = <0x1c138>; + type = "critical"; + hysteresis = <0x00>; + }; + }; + }; + + isp_thermal_zone { + polling-delay-passive = <0x3e8>; + polling-delay = <0x2710>; + thermal-sensors = <0x99 0x02>; + + trips { + + t0 { + temperature = <0x1c138>; + type = "critical"; + hysteresis = <0x00>; + }; + }; + }; + + ddr_thermal_zone { + polling-delay-passive = <0x3e8>; + polling-delay = <0x2710>; + thermal-sensors = <0x9a 0x03>; + + trips { + + t0 { + temperature = <0x1c138>; + type = "critical"; + hysteresis = <0x00>; + }; + }; + }; + }; + + gpadc { + compatible = "allwinner,sunxi-gpadc"; + reg = <0x00 0x5070000 0x00 0x400>; + interrupts = <0x00 0x00 0x00>; + clocks = <0x9b>; + status = "disable"; + channel_num = <0x01>; + channel_select = <0x01>; + channel_data_select = <0x00>; + channel_compare_select = <0x01>; + channel_cld_select = <0x01>; + channel_chd_select = <0x01>; + channel0_compare_lowdata = <0x19f0a0>; + channel0_compare_higdata = <0x124f80>; + key_cnt = <0x01>; + key0_vol = <0x320>; + key0_val = <0x73>; + }; + + eth@05020000 { + compatible = "allwinner,sunxi-gmac"; + reg = <0x00 0x5020000 0x00 0xffff 0x00 0x3000030 0x00 0x04>; + interrupts = <0x00 0x1b 0x04>; + interrupt-names = "gmacirq"; + clocks = <0x9c 0x9d>; + clock-names = "gmac", "ephy"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <0x9e>; + pinctrl-1 = <0x9f>; + phy-mode = "rmii"; + use_ephy25m = <0x01>; + tx-delay = <0x07>; + rx-delay = <0x1f>; + phy-rst = <0x41 0x03 0x13 0x01 0x01 0x01 0x00>; + gmac-power0; + gmac-power1; + gmac-power2; + status = "okay"; + }; + + card0_boot_para { + device_type = "card0_boot_para"; + card_ctrl = <0x00>; + card_high_speed = <0x01>; + card_line = <0x04>; + time_pwroff_ms = <0xc8>; + pinctrl-1 = <0xa0>; + card-pwr-gpios = <0x41 0x02 0x04 0x01 0x01 0x02 0xffffffff>; + status = "okay"; + }; + }; + + aliases { + serial0 = "/soc@03000000/uart@05000000"; + serial1 = "/soc@03000000/uart@05000400"; + serial2 = "/soc@03000000/uart@05000800"; + serial3 = "/soc@03000000/uart@05000c00"; + twi0 = "/soc@03000000/twi@0x05002000"; + twi1 = "/soc@03000000/twi@0x05002400"; + twi2 = "/soc@03000000/twi@0x05002800"; + twi3 = "/soc@03000000/twi@0x05002c00"; + twi4 = "/soc@03000000/twi@0x07081400"; + spi0 = "/soc@03000000/spi@05010000"; + spi1 = "/soc@03000000/spi@05011000"; + spi2 = "/soc@03000000/spi@05012000"; + gmac0 = "/soc@03000000/eth@05020000"; + global_timer0 = "/soc@03000000/timer@03009000"; + mmc0 = "/soc@03000000/sdmmc@04020000"; + mmc2 = "/soc@03000000/sdmmc@04022000"; + disp = "/soc@03000000/disp@01000000"; + lcd0 = "/soc@03000000/lcd0@01c0c000"; + pwm = "/soc@03000000/pwm@0300a000"; + pwm0 = "/soc@03000000/pwm0@0300a000"; + pwm1 = "/soc@03000000/pwm1@0300a000"; + pwm2 = "/soc@03000000/pwm2@0300a000"; + pwm3 = "/soc@03000000/pwm3@0300a000"; + pwm4 = "/soc@03000000/pwm4@0300a000"; + pwm5 = "/soc@03000000/pwm5@0300a000"; + pwm6 = "/soc@03000000/pwm6@0300a000"; + pwm7 = "/soc@03000000/pwm7@0300a000"; + pwm8 = "/soc@03000000/pwm8@0300a000"; + pwm9 = "/soc@03000000/pwm9@0300a000"; + boot_disp = "/soc@03000000/boot_disp"; + }; + + chosen { + bootargs = "earlyprintk=sunxi-uart,0x05000000 loglevel=8 initcall_debug=0 console=ttyS0 init=/init"; + linux,initrd-start = <0x00 0x00>; + linux,initrd-end = <0x00 0x00>; + }; + + cpus { + enable-method = "allwinner,sun8iw19p1"; + #address-cells = <0x01>; + #size-cells = <0x00>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x00>; + enable-method = "psci"; + clocks = <0xa1>; + clock-frequency = <0x3c14dc00>; + clock-latency = <0x1e8480>; + operating-points-v2 = <0xa2>; + cpu-idle-states = <0xa3 0xa4 0xa5>; + cooling-min-level = <0x05>; + cooling-max-level = <0x00>; + #cooling-cells = <0x02>; + dynamic-power-coefficient = <0x96>; + }; + + idle-states { + entry-method = "arm,psci"; + + cpu-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x10000>; + entry-latency-us = <0x2d>; + exit-latency-us = <0x5a>; + min-residency-us = <0xbb8>; + local-timer-stop; + phandle = <0xa3>; + }; + + cluster-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x1010000>; + entry-latency-us = <0x37>; + exit-latency-us = <0x23a>; + min-residency-us = <0x4268>; + local-timer-stop; + phandle = <0xa4>; + }; + + sys-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x2010000>; + entry-latency-us = <0xffffffff>; + exit-latency-us = <0xffffffff>; + min-residency-us = <0xffffffff>; + local-timer-stop; + phandle = <0xa5>; + }; + }; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + n_brom { + compatible = "allwinner,n-brom"; + reg = <0x00 0x00 0x00 0xc000>; + }; + + s_brom { + compatible = "allwinner,s-brom"; + reg = <0x00 0x00 0x00 0x10000>; + }; + + sram_ctrl { + device_type = "sram_ctrl"; + compatible = "allwinner,sram_ctrl"; + reg = <0x00 0x3000000 0x00 0x100>; + }; + + sram_a1 { + compatible = "allwinner,sram_a1"; + reg = <0x00 0x10000 0x00 0x10000>; + }; + + sram_a2 { + compatible = "allwinner,sram_a2"; + reg = <0x00 0x40000 0x00 0x14000>; + }; + + prcm { + compatible = "allwinner,prcm"; + reg = <0x00 0x1f01400 0x00 0x400>; + }; + + cpuscfg { + compatible = "allwinner,cpuscfg"; + reg = <0x00 0x1f01c00 0x00 0x400>; + }; + + ion { + compatible = "allwinner,sunxi-ion"; + + heap_sys_user@0 { + compatible = "allwinner,sys_user"; + heap-name = "sys_user"; + heap-id = <0x00>; + heap-base = <0x00>; + heap-size = <0x00>; + heap-type = "ion_system"; + }; + + heap_sys_contig@0 { + compatible = "allwinner,sys_contig"; + heap-name = "sys_contig"; + heap-id = <0x01>; + heap-base = <0x00>; + heap-size = <0x00>; + heap-type = "ion_contig"; + }; + + heap_cma@0 { + compatible = "allwinner,cma"; + heap-name = "cma"; + heap-id = <0x04>; + heap-base = <0x00>; + heap-size = <0x00>; + heap-type = "ion_cma"; + }; + }; + + dram { + compatible = "allwinner,dram"; + device_type = "dram"; + clocks = <0x07>; + clock-names = "pll_ddr0"; + dram_clk = <0x1e0>; + dram_type = <0x03>; + dram_zq = <0x3bfb>; + dram_odt_en = <0x01>; + dram_para1 = <0x4310f2>; + dram_para2 = <0x2000000>; + dram_mr0 = <0x1c70>; + dram_mr1 = <0x00>; + dram_mr2 = <0x18>; + dram_mr3 = <0x00>; + dram_tpr0 = <0x46190d>; + dram_tpr1 = <0x121210a>; + dram_tpr2 = <0x5403b>; + dram_tpr3 = <0x2006>; + dram_tpr4 = <0x5040405>; + dram_tpr5 = <0x5050302>; + dram_tpr6 = <0x90006644>; + dram_tpr7 = <0x42c21590>; + dram_tpr8 = <0xd05612c0>; + dram_tpr9 = <0x83def>; + dram_tpr10 = <0x00>; + dram_tpr11 = <0x210000>; + dram_tpr12 = <0x55>; + dram_tpr13 = <0x6003>; + }; + + memory@40000000 { + device_type = "memory"; + reg = <0x00 0x40000000 0x00 0x20000000>; + }; + + interrupt-controller@03020000 { + compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <0x03>; + #address-cells = <0x00>; + device_type = "gic"; + interrupt-controller; + reg = <0x00 0x3021000 0x00 0x1000 0x00 0x3022000 0x00 0x2000 0x00 0x3024000 0x00 0x2000 0x00 0x3026000 0x00 0x2000>; + interrupts = <0x01 0x09 0xf04>; + interrupt-parent = <0xa6>; + phandle = <0xa6>; + }; + + intc-nmi@07010320 { + compatible = "allwinner,sun8i-nmi"; + interrupt-parent = <0xa6>; + #interrupt-cells = <0x02>; + #address-cells = <0x00>; + interrupt-controller; + reg = <0x00 0x7010320 0x00 0x0c>; + pad-control-v1 = <0x7000208>; + interrupts = <0x00 0x68 0x04>; + }; + + interrupt-controller@0 { + compatible = "allwinner,sunxi-wakeupgen"; + interrupt-controller; + #interrupt-cells = <0x03>; + interrupt-parent = <0xa6>; + phandle = <0x01>; + }; + + sunxi-sid@03006000 { + compatible = "allwinner,sunxi-sid"; + device_type = "sid"; + reg = <0x00 0x3006000 0x00 0x200>; + }; + + sunxi-chipid@03006200 { + compatible = "allwinner,sunxi-chipid"; + device_type = "chipid"; + reg = <0x00 0x3006200 0x00 0xc0>; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupts = <0x01 0x0d 0x308 0x01 0x0e 0x308 0x01 0x0b 0x308 0x01 0x0a 0x308>; + clock-frequency = <0x16e3600>; + arm,cpu-registers-not-fw-configured; + interrupt-parent = <0xa6>; + }; + + pmu { + compatible = "arm,cortex-a7-pmu"; + interrupts = <0x00 0x8c 0x04>; + }; + + dramfreq { + compatible = "allwinner,sunxi-dramfreq"; + reg = <0x00 0x1c62000 0x00 0x1000 0x00 0x1c63000 0x00 0x1000 0x00 0x1c20000 0x00 0x800>; + interrupts = <0x00 0x45 0x04>; + status = "okay"; + }; + + uboot { + }; + + iommu@030f0000 { + compatible = "allwinner,sunxi-iommu"; + reg = <0x00 0x30f0000 0x00 0x1000>; + interrupts = <0x00 0x5f 0x04>; + interrupt-names = "iommu-irq"; + clocks = <0xa7>; + clock-names = "iommu"; + #iommu-cells = <0x02>; + status = "okay"; + phandle = <0x14>; + }; + + mbus-controller@047FA000 { + compatible = "allwinner,sun8i-mbus"; + reg = <0x00 0x4002000 0x00 0x1000>; + #mbus-cells = <0x01>; + }; + + ac200 { + compatible = "allwinner,sunxi-ac200"; + pinctrl-names = "active", "sleep"; + status = "okay"; + }; + + ramoops@0x48106000 { + compatible = "ramoops"; + reg = <0x00 0x48106000 0x00 0x60000>; + record-size = <0x20000>; + console-size = <0x20000>; + pmsg-size = <0x20000>; + }; + + cpucfg@09010000 { + compatible = "allwinner,sunxi-cpucfg"; + reg = <0x00 0x9010000 0x00 0xc8>; + }; + + opp_dvfs_table { + cluster_num = <0x01>; + opp_table_count = <0x01>; + + opp_l_table0 { + compatible = "allwinner,opp_l_table0"; + opp_count = <0x02>; + opp-shared; + phandle = <0xa2>; + + opp00 { + opp-hz = <0x00 0x23c34600>; + opp-microvolt = <0xdbba0>; + axi-bus-divide-ratio = <0x03>; + clock-latency-ns = <0x1e8480>; + }; + + opp01 { + opp-hz = <0x00 0x30a32c00>; + opp-microvolt = <0xdbba0>; + axi-bus-divide-ratio = <0x03>; + clock-latency-ns = <0x1e8480>; + }; + }; + }; + + wlan { + compatible = "allwinner,sunxi-wlan"; + clocks = <0xa8>; + wlan_busnum = <0x01>; + wlan_usbnum = <0x03>; + wakeup_source = <0x00>; + wlan_regon = <0x41 0x06 0x06 0x01 0x00 0x01 0x00>; + wlan_hostwake = <0x41 0x06 0x07 0x00 0x00 0x01 0x00>; + status = "okay"; + }; +}; diff --git a/board/sunxi/v83x.c b/board/sunxi/v83x.c new file mode 100644 index 000000000000..1434b836572a --- /dev/null +++ b/board/sunxi/v83x.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Allwinner V831/V833 boards, as OpenIPC boots them + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* The vendor kernel device trees are about 60 KiB */ +#define V83X_KERNEL_DTB_MAX 0x20000 + +extern const unsigned char v83x_kernel_dtb_gz[]; +extern const unsigned char v83x_kernel_dtb_gz_end[]; + +void v83x_board_init(void) +{ + /* + * The external RMII PHY takes its 25 MHz from the SoC (EPHY_25M, + * PLL_PERI0 / 24): the vendor device tree's use_ephy25m + */ + setbits_le32(SUNXI_CCM_BASE + 0x970, BIT(31) | BIT(30)); +} + +/* + * The kernels carry no device tree and the vendor U-Boot hands them its + * own: unpack the one built in for bootm (boot/image-fdt.c boots a plain + * uImage with the tree at ${fdtaddr}). + */ +void v83x_kernel_fdt(void) +{ + unsigned long len = v83x_kernel_dtb_gz_end - v83x_kernel_dtb_gz; + void *fdt = malloc(V83X_KERNEL_DTB_MAX); + + if (!fdt) + return; + + if (gunzip(fdt, V83X_KERNEL_DTB_MAX, (uchar *)v83x_kernel_dtb_gz, + &len) || fdt_check_header(fdt)) { + printf("V83x: built-in kernel device tree is bad\n"); + free(fdt); + return; + } + + env_set_hex("fdtaddr", (ulong)fdt); +} + +/* What the vendor U-Boot sets at boot besides /memory and /chosen */ +void v83x_ft_board_setup(void *blob) +{ + int node = fdt_path_offset(blob, "/dram"); + + /* the DRAM size, in MiB, in the upper half */ + if (node >= 0) + fdt_setprop_u32(blob, node, "dram_para2", + (gd->ram_size >> 20) << 16); +} diff --git a/board/sunxi/v83x_kernel_dtb.S b/board/sunxi/v83x_kernel_dtb.S new file mode 100644 index 000000000000..09b9ad8e69af --- /dev/null +++ b/board/sunxi/v83x_kernel_dtb.S @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* The kernel's device tree (v83x-kernel.dts), gzipped, see v83x.c */ + + .section .rodata.v83x_kernel_dtb, "a" + .balign 4 + .global v83x_kernel_dtb_gz +v83x_kernel_dtb_gz: + .incbin V83X_KERNEL_DTB_GZ + .global v83x_kernel_dtb_gz_end +v83x_kernel_dtb_gz_end: diff --git a/boot/image-fdt.c b/boot/image-fdt.c index e75d051c8721..271be3893975 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -560,6 +560,19 @@ int boot_get_fdt(int flag, int argc, char *const argv[], uint8_t arch, debug("## Using FDT at ${fdtaddr}=Ox%lx\n", fdt_addr); } +#endif +#ifdef CONFIG_MACH_SUN8I_V831 + } else if (images->legacy_hdr_valid && env_get_hex("fdtaddr", 0)) { + /* + * A plain uImage boots with the device tree the board keeps + * at ${fdtaddr}: on the V831/V833, the vendor kernel's, built + * into U-Boot (board/sunxi/v83x.c). + */ + fdt_addr = env_get_hex("fdtaddr", 0); + fdt_blob = map_sysmem(fdt_addr, 0); + if (fdt_check_header(fdt_blob)) + goto no_fdt; + printf(" Booting using the fdt blob at %#08lx\n", fdt_addr); #endif } else { debug("## No Flattened Device Tree\n"); From ff3573f8039def460097a0ce52a789fdeec488a4 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov <7954832+dimerr@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:20:06 +0000 Subject: [PATCH 5/8] openipc: build the V831/V833 NOR images build.sh builds u-boot-v831-nor.bin and u-boot-v833-nor.bin: the SPL (eGON.BT0) padded to 0x8000, then U-Boot as an LZMA legacy image, ~170 KiB in all; it fails a build over the 256 KiB boot partition or with an SPL over 32 KiB. The configuration is openipc/openipc_config plus the SoC's fragment, which carries the vendor's dram_para words: the V831 reference boards' (DDR2, 528 MHz, fixed geometry) and the V833 lindenis board's (DDR3, 792 MHz, scanned). include/configs/openipc-common.h is openipc-novatek's environment with this family's parameters: vendor "allwinner", mem= the whole DDR, the kernel staged at 0x41000000, the flash's MTD name spi0.0, console ttyS0. Under QEMU (qemu-bench -M v831/v833) both images reach the autoboot prompt, and a second boot loads the environment the first saved. --- build.sh | 96 +++++++++++++++++ include/configs/openipc-common.h | 135 +++++++++++++++++++++++ include/configs/v83x.h | 30 ++++++ openipc/openipc_config | 178 +++++++++++++++++++++++++++++++ openipc/v831_config | 16 +++ openipc/v833_config | 17 +++ 6 files changed, 472 insertions(+) create mode 100755 build.sh create mode 100644 include/configs/openipc-common.h create mode 100644 include/configs/v83x.h create mode 100644 openipc/openipc_config create mode 100644 openipc/v831_config create mode 100644 openipc/v833_config diff --git a/build.sh b/build.sh new file mode 100755 index 000000000000..acb8f232fd71 --- /dev/null +++ b/build.sh @@ -0,0 +1,96 @@ +#!/bin/sh +# +# Build U-Boot for the SoCs named on the command line, or for every +# supported SoC when invoked without arguments. Failures are collected: +# every requested SoC is attempted and the script exits non-zero if any +# of them failed. + +cd "$(dirname "$0")" + +export ARCH=arm +export CROSS_COMPILE=${CROSS_COMPILE:-arm-linux-gnueabi-} + +BOOT_SRCDIR=$(pwd) + +# The boot partition: the environment starts at 0x40000 +BOOT_PART_SIZE=262144 +# U-Boot proper's offset in the image, where the SPL reads it from +UBOOT_OFFSET=32768 + +mkdir -p output + +# Concatenate the config fragments into the build directory's .config; +# later fragments override earlier ones. +mkconfig() { + builddir=$1 + shift + mkdir -p "${builddir}" + cat "$@" > "${builddir}/.config" +} + +# Allwinner V831/V833: the boot ROM loads the SPL (eGON.BT0) from SPI NOR +# offset 0 into SRAM; the SPL brings up the DDR and unpacks U-Boot, an +# LZMA-compressed legacy image, from offset 0x8000 to its link address. +build_v83x() { + soc=$1 + builddir=${BOOT_SRCDIR}/output/build-${soc}-nor + make_opts="-C ${BOOT_SRCDIR} O=${builddir} -j$(nproc)" + out=output/u-boot-${soc}-nor.bin + + rm -rf "${builddir}" + mkconfig "${builddir}" openipc/openipc_config openipc/${soc}_config && \ + make ${make_opts} olddefconfig /dev/null && \ + make ${make_opts} &2 + return 1 + fi + + text=$(sed -n 's/^CONFIG_SYS_TEXT_BASE=//p' "${builddir}/.config") + xz --format=lzma -9 -c "${builddir}/u-boot.bin" \ + > "${builddir}/u-boot.bin.lzma" || return 1 + "${builddir}/tools/mkimage" -A arm -O u-boot -T firmware -C lzma \ + -a "${text}" -e "${text}" -n "U-Boot ${soc}" \ + -d "${builddir}/u-boot.bin.lzma" "${builddir}/u-boot-lzma.img" \ + >/dev/null || return 1 + + # SPL, padded with 0xff to U-Boot's offset, then U-Boot + { cat "${spl}" + head -c $((UBOOT_OFFSET - $(stat -c %s "${spl}"))) /dev/zero | \ + tr '\0' '\377' + cat "${builddir}/u-boot-lzma.img"; } > "${out}" || return 1 + + size=$(stat -c %s "${out}") + if [ "${size}" -gt ${BOOT_PART_SIZE} ]; then + echo "${soc}: ${out} is ${size} bytes, over the" \ + "${BOOT_PART_SIZE}-byte boot partition" >&2 + rm -f "${out}" + return 1 + fi + echo "${out}: ${size} bytes" +} + +build_soc() { + soc=$1 + case "${soc}" in + v831|v833) + build_v83x "${soc}" ;; + *) + echo "unsupported SoC: ${soc}" >&2 + return 1 ;; + esac +} + +ALL_SOCS="v831 v833" + +SOCS="$@" +[ -z "${SOCS}" ] && SOCS="${ALL_SOCS}" + +rc=0 +for soc in ${SOCS}; do + build_soc "${soc}" || { echo "build failed for ${soc}" >&2; rc=1; } +done + +exit ${rc} diff --git a/include/configs/openipc-common.h b/include/configs/openipc-common.h new file mode 100644 index 000000000000..db8bc7a60c22 --- /dev/null +++ b/include/configs/openipc-common.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common OpenIPC configuration for the Allwinner V831/V833 targets. + * + * The single place for the default environment, kept in step with + * include/configs/openipc-common.h on the openipc-hisilicon branch: the + * update, recovery and boot-limit commands are the same, only the per-SoC + * parameters below differ. Per-SoC config headers include this last and must + * not define CONFIG_EXTRA_ENV_SETTINGS, CONFIG_BOOTARGS or + * CONFIG_BOOTCOMMAND themselves. + */ + +#ifndef __OPENIPC_COMMON_H +#define __OPENIPC_COMMON_H + +#include + +/* Default network configuration */ +#define CONFIG_IPADDR 192.168.1.10 +#define CONFIG_SERVERIP 192.168.1.107 +#define CONFIG_NETMASK 255.255.255.0 +#define CONFIG_GATEWAYIP 192.168.1.1 + +#define VENDOR "allwinner" + +/* + * Kernel memory (mem=): all of the DDR in the package, as the vendor + * U-Boot leaves it (it passes no mem=); the vendor media drivers take + * their buffers from the kernel's CMA. DDR3 is the V833's 128 MiB, DDR2 + * the V831's 64 MiB. + */ +#if CONFIG_DRAM_SUN8I_V831_TYPE == 3 +#define OPENIPC_OSMEM "128M" +#else +#define OPENIPC_OSMEM "64M" +#endif + +/* + * Clear of the kernel at 0x40008000 as it unpacks, and of U-Boot at the + * top of DDR: 16 MiB in + */ +#define OPENIPC_BASEADDR 0x41000000 + +/* NOR layout, shared with every OpenIPC NOR camera */ +#define OPENIPC_KERNADDR 0x50000 +#define OPENIPC_KERNSIZE 0x200000 +#define OPENIPC_ROOTADDR 0x250000 +#define OPENIPC_ROOTSIZE 0x500000 + +#define OPENIPC_FW_FILE "firmware.${soc}" + +#define OPENIPC_BOOTARGSNFS "mem=\\${osmem} console=ttyS0,115200 panic=20 root=/dev/nfs rootfstype=nfs ip=${ipaddr}:::255.255.255.0::eth0 nfsroot=${serverip}:${nfsroot},v3,nolock rw \\${extras}" + +/* NOR partition list; the reserved 'mtdparts' variable holds the full + * ":" string, the bootargs compose it from mtdids/mtds. */ +#define OPENIPC_MTDPARTS "256k(boot),64k(env),2048k(kernel),5120k(rootfs),7168k@0x50000(firmware),-(rootfs_data)" + +/* + * Past bootlimit -- a boot loop: the image is intact, but userspace never + * comes up healthy, so nothing clears the DRAM counter -- boot the same + * kernel with "failsafe". openipc-failsafe then brings up network and SSH + * only, so the camera stays reachable. Firmware without that package ignores + * the word and boots as usual. Firmware the scan found broken (bootfail) + * goes to recovery first, as bootflash does. A kernel that does not load + * leaves the camera at this prompt rather than in a reset loop. + * OPENIPC_ALTBOOTCMD_RETIRED is the reflash this replaced on the + * openipc-hisilicon branch; openipc_helper() migrates an environment that + * still carries it. + */ +#define OPENIPC_ALTBOOTCMD "run fwrecovery; echo \"*** Boot limit exceeded -- entering FAILSAFE (network + SSH only) ***\"; setenv setargs setenv bootargs ${bootargs} failsafe; run setargs; if " OPENIPC_LOADKERNEL "; then bootm ${baseaddr}; fi" +#define OPENIPC_ALTBOOTCMD_RETIRED "echo \"*** Boot limit exceeded -- forcing firmware recovery ***\"; run fwupd" + +/* Environment shared by all boot media: identity, network boot, update + * source selection and the medium-agnostic update commands. The selected + * medium provides ubfile/ubwrite/ukwrite/urwrite/loadkernel/mtdids/ + * mtdparts/bootargs. */ +#define OPENIPC_ENV_COMMON \ + "baseaddr=" __stringify(OPENIPC_BASEADDR) "\0" \ + "osmem=" OPENIPC_OSMEM "\0" \ + "board_name\0" \ + "board=" CONFIG_PRODUCT_SOC "\0" \ + "vendor=" VENDOR "\0" \ + "soc=" CONFIG_PRODUCT_SOC "\0" \ + "socmodel=" CONFIG_PRODUCT_SOCMODEL "\0" \ + "nfsroot=/srv/nfs/" CONFIG_PRODUCT_SOC "\0" \ + "bootargsnfs=" OPENIPC_BOOTARGSNFS "\0" \ + "bootnfs=setenv setargs setenv bootargs ${bootargsnfs}; run setargs; ${source} ${baseaddr} uImage.${soc}; bootm ${baseaddr}\0" \ + "source=tftpboot\0" \ + "srcnet=setenv source tftpboot\0" \ + "srcmmc=setenv source fatload mmc 0\0" \ + "srcusb=usb start; setenv source fatload usb 0\0" \ + "ub=${source} ${baseaddr} ${ubfile} && run ubwrite\0" \ + "uk=if ${source} ${baseaddr} uImage.${soc}; then run ukwrite; elif ${source} ${baseaddr} fitImage.${soc}; then run ukwrite; fi\0" \ + "ur=${source} ${baseaddr} ${urfile} && run urwrite\0" \ + OPENIPC_FWUPD \ + "bootlimit=2\0" \ + "altbootcmd=" OPENIPC_ALTBOOTCMD "\0" \ + "fwrecovery=if env exists bootfail; then run fwupd; sleep 5; reset; fi\0" \ + "bootflash=run fwrecovery; setenv setargs setenv bootargs ${bootargs}; run setargs; run loadkernel; bootm ${baseaddr}; reset\0" + +/* The flash's MTD name in the vendor kernels: the SPI device's */ +#define SFC "spi0.0" + +#define CONFIG_BOOTARGS "mem=\\${osmem} console=ttyS0,115200 panic=20 root=/dev/mtdblock3 rootfstype=squashfs init=/init mtdparts=\\${mtdparts} \\${extras}" + +/* Recovery: the rootfs is fetched first so its size selects the NOR schema */ +#define OPENIPC_FWUPD "fwupd=run ur; run uk\0" + +#define OPENIPC_LOADKERNEL "sf probe 0; sf read ${baseaddr} ${kernaddr} ${kernsize}" +#define OPENIPC_ENV_MEDIUM \ + "ubfile=u-boot-" CONFIG_PRODUCT_SOC "-nor.bin\0" \ + "urfile=rootfs.squashfs." CONFIG_PRODUCT_SOC "\0" \ + "kernaddr=" __stringify(OPENIPC_KERNADDR) "\0" \ + "kernsize=" __stringify(OPENIPC_KERNSIZE) "\0" \ + "rootaddr=" __stringify(OPENIPC_ROOTADDR) "\0" \ + "rootsize=" __stringify(OPENIPC_ROOTSIZE) "\0" \ + "mtdslite=setenv kernsize 0x200000; setenv rootaddr 0x250000; setenv rootsize 0x500000\0" \ + "mtdsultimate=setenv kernsize 0x300000; setenv rootaddr 0x350000; setenv rootsize 0xa00000\0" \ + "ubwrite=sf probe 0; sf erase 0x0 ${kernaddr}; sf write ${baseaddr} 0x0 ${filesize}\0" \ + "ukwrite=sf probe 0; sf erase ${kernaddr} ${kernsize}; sf write ${baseaddr} ${kernaddr} ${filesize}\0" \ + "urwrite=sf probe 0; if itest ${filesize} -gt 500000; then run mtdsultimate; fi; sf erase ${rootaddr} ${rootsize}; sf write ${baseaddr} ${rootaddr} ${filesize}\0" \ + "fwwrite=sf probe 0; sf erase ${kernaddr} ${filesize}; sf write ${baseaddr} ${kernaddr} ${filesize}\0" \ + "loadkernel=" OPENIPC_LOADKERNEL "\0" \ + "mtdids=" SFC "\0" \ + "mtds=" OPENIPC_MTDPARTS "\0" \ + "mtdparts=" SFC ":" OPENIPC_MTDPARTS "\0" \ + "bootargs=" CONFIG_BOOTARGS + +#define CONFIG_BOOTCOMMAND "run bootflash" + +#define CONFIG_EXTRA_ENV_SETTINGS \ + OPENIPC_ENV_COMMON \ + OPENIPC_ENV_MEDIUM + +#endif /* __OPENIPC_COMMON_H */ diff --git a/include/configs/v83x.h b/include/configs/v83x.h new file mode 100644 index 000000000000..a863d52cb2ff --- /dev/null +++ b/include/configs/v83x.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Allwinner V831/V833 (sun8iw19p1) with the OpenIPC environment + */ +#ifndef __CONFIG_H +#define __CONFIG_H + +#include + +/* + * The nonsec switch finds the GIC through CBAR otherwise; name it, as + * on the arndale: QEMU's Cortex-A7 reads CBAR as 0 + */ +#define CONFIG_ARM_GIC_BASE_ADDRESS SUNXI_GIC400_BASE + +/* DRAM in the package: 64 MiB of DDR2 (V831) or 128 MiB of DDR3 (V833) */ +#if CONFIG_DRAM_SUN8I_V831_TYPE == 3 +#define PHYS_SDRAM_1_SIZE 0x08000000 +#else +#define PHYS_SDRAM_1_SIZE 0x04000000 +#endif + +/* The OpenIPC environment instead of sunxi-common's distro one */ +#ifdef CONFIG_OPENIPC +#undef CONFIG_EXTRA_ENV_SETTINGS +#undef CONFIG_BOOTCOMMAND +#include "openipc-common.h" +#endif + +#endif /* __CONFIG_H */ diff --git a/openipc/openipc_config b/openipc/openipc_config new file mode 100644 index 000000000000..1f3494e5aab7 --- /dev/null +++ b/openipc/openipc_config @@ -0,0 +1,178 @@ +# OpenIPC base configuration for the Allwinner V831/V833 targets. build.sh +# appends the per-SoC fragment and runs olddefconfig, so a later line +# overrides an earlier one. +CONFIG_ARM=y +CONFIG_ARCH_SUNXI=y +CONFIG_MACH_SUN8I_V831=y +CONFIG_DEFAULT_DEVICE_TREE="sun8i-v831-openipc" +CONFIG_IDENT_STRING=" Allwinner V83x" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# 32 MiB into the DDR: below what the smallest part (64 MiB) has +CONFIG_SYS_TEXT_BASE=0x42000000 +CONFIG_SYS_LOAD_ADDR=0x41000000 +CONFIG_SYS_MALLOC_F_LEN=0x2000 +CONFIG_SYS_MALLOC_LEN=0x400000 +CONFIG_NR_DRAM_BANKS=1 +CONFIG_DISPLAY_CPUINFO=y +CONFIG_DISPLAY_BOARDINFO=y +CONFIG_MISC_INIT_R=y +# CONFIG_BOARD_LATE_INIT is not set + +# SPL: the boot ROM loads it from SPI NOR offset 0 (eGON.BT0) into SRAM; +# it brings up clocks and DDR, then loads U-Boot from offset 0x8000 +CONFIG_SPL=y +# end of SRAM A2, as on the H6 +CONFIG_SPL_STACK=0x118000 +CONFIG_SPL_SPI_SUNXI=y +CONFIG_SYS_SPI_U_BOOT_OFFS=0x8000 +# U-Boot is LZMA-packed (build.sh), to fit the 256 KiB boot partition +CONFIG_SPL_LZMA=y +# CONFIG_SPL_MMC is not set +# CONFIG_SPL_FS_FAT is not set +# CONFIG_SPL_LIBDISK_SUPPORT is not set +# CONFIG_SPL_POWER is not set +# CONFIG_SPL_I2C is not set +# CONFIG_SPL_DOS_PARTITION is not set +# CONFIG_SPL_EFI_PARTITION is not set + +# Kernel hand-off: non-secure, PSCI 1.0 from U-Boot's own monitor, as +# the vendor kernels expect from OP-TEE +CONFIG_ARMV7_NONSEC=y +CONFIG_ARMV7_PSCI=y +CONFIG_ARMV7_PSCI_1_0=y +CONFIG_ARMV7_PSCI_NR_CPUS=1 + +# Console: UART0 +CONFIG_CONS_INDEX=1 +CONFIG_BAUDRATE=115200 +CONFIG_SYS_PROMPT="OpenIPC # " +CONFIG_SILENT_CONSOLE=y +CONFIG_SILENT_CONSOLE_UPDATE_ON_RELOC=y +CONFIG_HUSH_PARSER=y +CONFIG_CMDLINE_EDITING=y +CONFIG_AUTO_COMPLETE=y + +# Autoboot: Ctrl-C only +CONFIG_BOOTDELAY=1 +CONFIG_AUTOBOOT=y +CONFIG_AUTOBOOT_KEYED=y +CONFIG_AUTOBOOT_PROMPT="Hit Ctrl-C to stop autoboot: %2d\n" +CONFIG_AUTOBOOT_STOP_STR="\x03" +CONFIG_AUTOBOOT_KEYED_CTRLC=y +# CONFIG_DISTRO_DEFAULTS is not set +# CONFIG_USE_PREBOOT is not set + +# OpenIPC boot layer: firmware scan, recovery, boot-limit failsafe +CONFIG_OPENIPC=y +CONFIG_BOOTCOUNT_LIMIT=y +CONFIG_BOOTCOUNT_GENERIC=y +CONFIG_SYS_BOOTCOUNT_SINGLEWORD=y +# DRAM + 0x1f20000, as on the other targets +CONFIG_SYS_BOOTCOUNT_ADDR=0x41F20000 +CONFIG_LEGACY_IMAGE_FORMAT=y +# CONFIG_FIT is not set + +# Kernels boot with the vendor device tree U-Boot carries (board/sunxi) +CONFIG_OF_LIBFDT=y +# CONFIG_OF_LIBFDT_OVERLAY is not set + +# SD card (SMHC0, 4-bit): recovery from it in common/openipc.c, fatload +CONFIG_MMC=y +CONFIG_MMC_SUNXI=y +CONFIG_CMD_MMC=y +CONFIG_CMD_FAT=y +CONFIG_FS_FAT=y +CONFIG_DOS_PARTITION=y +# CONFIG_MMC_HW_PARTITIONING is not set +# CONFIG_SUPPORT_EMMC_BOOT is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_FAT_WRITE is not set + +# SPI NOR, and the environment in it +CONFIG_SPI=y +CONFIG_SPI_SUNXI=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_BAR=y +CONFIG_SPI_FLASH_EON=y +CONFIG_SPI_FLASH_GIGADEVICE=y +CONFIG_SPI_FLASH_ISSI=y +CONFIG_SPI_FLASH_MACRONIX=y +CONFIG_SPI_FLASH_SPANSION=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_SPI_FLASH_XMC=y +CONFIG_SPI_FLASH_XTX=y +CONFIG_CMD_SF=y +CONFIG_ENV_IS_IN_SPI_FLASH=y +# CONFIG_ENV_IS_IN_FAT is not set +# CONFIG_ENV_IS_IN_EXT4 is not set +# CONFIG_ENV_IS_IN_MMC is not set +# CONFIG_ENV_IS_NOWHERE is not set +CONFIG_ENV_OFFSET=0x40000 +CONFIG_ENV_SIZE=0x10000 +CONFIG_ENV_SECT_SIZE=0x10000 +# SPL, U-Boot and its device trees must end before the environment: +# build.sh fails the build, not the board +# CONFIG_HAS_BOARD_SIZE_LIMIT is not set + +# Commands +CONFIG_CMD_BOOTM=y +CONFIG_CMD_GO=y +CONFIG_CMD_RUN=y +CONFIG_CMD_ECHO=y +CONFIG_CMD_ITEST=y +CONFIG_CMD_SOURCE=y +CONFIG_CMD_SETEXPR=y +CONFIG_CMD_EDITENV=y +CONFIG_CMD_SAVEENV=y +CONFIG_CMD_ENV_EXISTS=y +CONFIG_CMD_MEMORY=y +CONFIG_CMD_CRC32=y +CONFIG_CMD_SLEEP=y +CONFIG_CMD_FDT=y +# Not used by the OpenIPC flows +# CONFIG_CMD_BOOTEFI is not set +# CONFIG_EFI_LOADER is not set +# CONFIG_CMD_BOOTZ is not set +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_XIMG is not set +# CONFIG_CMD_RANDOM is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_NFS is not set +# CONFIG_CMD_GPT is not set +# CONFIG_CMD_GPIO is not set +# CONFIG_CMD_DM is not set +# CONFIG_CMD_PXE is not set +# CONFIG_CMD_EXTENSION is not set +# CONFIG_CMD_IMI is not set +# CONFIG_CMD_FPGA is not set +# CONFIG_CMD_LZMADEC is not set +# CONFIG_CMD_UNZIP is not set +# CONFIG_CMD_BOOTDEV is not set + +# Not on these boards +# CONFIG_USB is not set +# CONFIG_USB_GADGET is not set +# CONFIG_VIDEO is not set +# CONFIG_DM_VIDEO is not set +# CONFIG_VIDEO_DE2 is not set +# CONFIG_I2C is not set +# CONFIG_DM_I2C is not set +# CONFIG_SYSRESET is not set +# CONFIG_WDT is not set +# CONFIG_DM_KEYBOARD is not set +# CONFIG_PHY_SUN4I_USB is not set +# CONFIG_SUNXI_NO_PMIC is not set + +# Network: the EMAC, RMII to an external PHY +CONFIG_NET=y +CONFIG_NETDEVICES=y +CONFIG_SUN8I_EMAC=y +CONFIG_PHYLIB=y +CONFIG_DM_MDIO=y +CONFIG_CMD_NET=y +CONFIG_CMD_TFTPBOOT=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_PING=y +CONFIG_CMD_MII=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_LIB_RAND=y diff --git a/openipc/v831_config b/openipc/v831_config new file mode 100644 index 000000000000..24761f95ce46 --- /dev/null +++ b/openipc/v831_config @@ -0,0 +1,16 @@ +# V831: 64 MiB of DDR2 in the package, the vendor's parameters (the +# v831 reference boards' sys_config.fex, the same on every V831 flash seen) +CONFIG_PRODUCT_SOC="v831" +CONFIG_DRAM_CLK=528 +CONFIG_DRAM_ZQ=3065 +CONFIG_DRAM_SUN8I_V831_TYPE=2 +CONFIG_DRAM_SUN8I_V831_PARA1=0xd2 +CONFIG_DRAM_SUN8I_V831_MR0=0xe73 +CONFIG_DRAM_SUN8I_V831_MR1=0x2 +CONFIG_DRAM_SUN8I_V831_MR2=0x0 +CONFIG_DRAM_SUN8I_V831_TPR0=0x00471992 +CONFIG_DRAM_SUN8I_V831_TPR1=0x0131a10c +CONFIG_DRAM_SUN8I_V831_TPR2=0x00057041 +CONFIG_DRAM_SUN8I_V831_TPR11=0x00120000 +CONFIG_DRAM_SUN8I_V831_TPR12=0x23 +CONFIG_DRAM_SUN8I_V831_TPR13=0x34000903 diff --git a/openipc/v833_config b/openipc/v833_config new file mode 100644 index 000000000000..68a35aee6f78 --- /dev/null +++ b/openipc/v833_config @@ -0,0 +1,17 @@ +# V833: 128 MiB of DDR3 in the package, the vendor's parameters (the v833 +# lindenis board's sys_config.fex); tpr13 bit 0 clear: the rank, width +# and size are scanned +CONFIG_PRODUCT_SOC="v833" +CONFIG_DRAM_CLK=792 +CONFIG_DRAM_ZQ=3067 +CONFIG_DRAM_SUN8I_V831_TYPE=3 +CONFIG_DRAM_SUN8I_V831_PARA1=0x10f2 +CONFIG_DRAM_SUN8I_V831_MR0=0x1c70 +CONFIG_DRAM_SUN8I_V831_MR1=0x2 +CONFIG_DRAM_SUN8I_V831_MR2=0x18 +CONFIG_DRAM_SUN8I_V831_TPR0=0x004a2195 +CONFIG_DRAM_SUN8I_V831_TPR1=0x02423190 +CONFIG_DRAM_SUN8I_V831_TPR2=0x0008b061 +CONFIG_DRAM_SUN8I_V831_TPR11=0x00540000 +CONFIG_DRAM_SUN8I_V831_TPR12=0x45 +CONFIG_DRAM_SUN8I_V831_TPR13=0x24020900 From fb1ea770b45defb44c8cd08ca3a14785f22ba2b5 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov <7954832+dimerr@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:20:12 +0000 Subject: [PATCH 6/8] ci, docs: call build-allwinner.yml, add CLAUDE.md --- .github/workflows/ci.yml | 25 +++++ CLAUDE.md | 204 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 CLAUDE.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000000..aeb9b5ba4a82 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: CI + +# The build lives in build-allwinner.yml on the openipc branch (the default +# branch, so it can be dispatched). GitHub runs a pull request's +# workflows from the PR's own tree, so this branch needs this caller for +# its pushes and pull requests to be built and smoke-tested at all. +# It never releases: that workflow only uploads on workflow_dispatch. +on: + push: + branches: [openipc-allwinner] + pull_request: + branches: [openipc-allwinner] + +jobs: + build: + uses: OpenIPC/u-boot/.github/workflows/build-allwinner.yml@openipc + # github.sha: on pull_request the merge of the PR into the current + # base, so the PR is built as it would land; on push, that commit. + with: + ref: ${{ github.sha }} + # A called workflow is validated against what its caller grants, + # skipped jobs included; release/publish ask for these. + permissions: + contents: write + id-token: write diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000000..bd49a828438a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,204 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this branch is + +`openipc-allwinner` is OpenIPC's U-Boot for the Allwinner V831 and V833 +(sun8iw19p1): one Cortex-A7 with the H6's peripherals, clocks and memory +map, and the DDR in the package (V831: 64 MiB DDR2, V833: 128 MiB DDR3). +The tree is **mainline U-Boot v2022.10**, the same release as the other +`openipc-*` branches, plus: + +- the OpenIPC boot layer, carried over from `openipc-novatek` + (`common/openipc.c`, `include/openipc.h`, `include/configs/openipc-common.h`); +- the V831/V833 support, as a new mainline `ARCH_SUNXI` SoC, + `MACH_SUN8I_V831`, with its own SPL: no vendor boot0, OP-TEE or + boot package is needed or carried. + +The DRAM initialisation is the vendor boot0's DRAM library (`libdram`, +shipped only as a binary object), translated to C function for function +(`arch/arm/mach-sunxi/dram_sun8i_v831.c`). It is the D1/T113 controller of +mainline's `drivers/ram/sunxi/dram_sun20i_d1.c`, in an older revision of +the library, at other addresses. + +CI and user docs live on the `openipc` branch: + +- `.github/workflows/ci.yml` only calls `build-allwinner.yml@openipc`. +- `git show origin/openipc:CLAUDE.md` covers the workflow invariants. +- `git show origin/openipc-hisilicon:CLAUDE.md` covers the OpenIPC layer + (default environment, `common/openipc.c`, env compatibility). It applies + here unchanged. + +Adding or dropping a SoC needs both branches: + +- here: `build.sh` (`build_soc`, `ALL_SOCS`) and `openipc/_config`; +- there: `build-allwinner.yml`'s matrices and the README. + +## Relationship to mainline + +```sh +git diff --stat v2022.10 HEAD # the whole delta +git diff --diff-filter=M v2022.10 HEAD # the mainline files we patched +``` + +**Added** + +| Area | Location | +| --- | --- | +| DRAM controller (SPL) | `arch/arm/mach-sunxi/dram_sun8i_v831.c`, `arch/arm/include/asm/arch-sunxi/dram_sun8i_v831.h` | +| PSCI monitor: version, features, SYSTEM_RESET (watchdog), CPU_OFF | `arch/arm/cpu/armv7/sunxi/psci_v831.c` | +| U-Boot's device tree | `arch/arm/dts/sun8i-v831.dtsi`, `sun8i-v831-openipc.dts` | +| Board: PHY clock, the kernel's device tree | `board/sunxi/v83x.c`, `v83x_kernel_dtb.S`, `v83x-kernel.dts` | +| Config header | `include/configs/v83x.h` (sunxi-common.h, then `openipc-common.h`) | +| Kconfig fragments, the build | `openipc/`, `build.sh` | + +**Patched mainline files**, each small: + +- `arch/arm/Kconfig`: `ARCH_SUNXI` selects `BINMAN` except on the V831 + (binman needs pylibfdt, which no longer builds with current setuptools; + `build.sh` puts the image together). +- `arch/arm/mach-sunxi/Kconfig`: `MACH_SUN8I_V831` (selects + `SUN50I_GEN_H6`), `DRAM_SUN8I_V831` and the `DRAM_SUN8I_V831_*` + parameters, SoC defaults (DRAM clock and ZQ, CPU 816 MHz, SPL stack in + DDR, `SYS_CONFIG_NAME` "v83x"); `SUN50I_GEN_H6` selects FIT only on arm64. +- `arch/arm/mach-sunxi/{board.c,clock_sun50i_h6.c,cpu_info.c,spl_spi_sunxi.c,Makefile}`, + `arch/arm/include/asm/arch-sunxi/{clock_sun50i_h6.h,dram.h,gpio.h}`: + UART0 on PH9/PH10, the boot0 clock set-up, the H6's SPI0 on the H3's + pins (PC0-PC3), the boot device, an LZMA U-Boot from SPI. +- `arch/arm/cpu/armv7/sunxi/Makefile`: `psci_v831.o` instead of `psci.o`. +- `arch/arm/dts/Makefile`, `drivers/clk/sunxi/{Kconfig,clk_sunxi.c}` + (the V831 CCU is the H6's for every gate U-Boot uses), + `drivers/pinctrl/sunxi/{Kconfig,pinctrl-sunxi.c}` (the V831's functions). +- `board/sunxi/{board.c,Makefile}`: the V831 hooks; `misc_init_r()` → + `openipc_helper()`. +- `boot/image-fdt.c`: with `MACH_SUN8I_V831`, a plain uImage boots with + the device tree at `${fdtaddr}` (as on `openipc-novatek`). +- `common/spl/Kconfig`: the SPL BSS inside 64 MiB. +- `lib/lzma/LzmaTools.c`: `CONFIG_IS_ENABLED(LZMA)`, so SPL_LZMA links + (fixed the same way in later mainline). +- `common/{Kconfig,Makefile}`: `OPENIPC`, `PRODUCT_SOC`, `PRODUCT_SOCMODEL`. + +## Boot + +The boot ROM reads the eGON.BT0 header at SPI NOR offset 0 and loads the +SPL (`spl/sunxi-spl.bin`, `mkimage -T sunxi_egon`) into SRAM A1 at +`0x20000`. The SPL: + +1. sets the clocks as boot0 does (`clock_init_safe()`: PLL_PERI0 1.2 GHz, + AHB 200 / APB1 100 MHz, MBUS reset released; the MBUS clock is a + quarter of the DRAM clock, it has no mux); +2. brings up the DDR (`sunxi_dram_init()`), then the CPU at 816 MHz (the + vendor U-Boot's `boot_clock`); +3. reads U-Boot from offset `0x8000` with the H6's SPI0 (PIO, 6 MHz): an + LZMA-compressed legacy image (`build.sh`), unpacked to `0x42000000`. + +U-Boot does not fit the 256 KiB boot partition uncompressed (~257 KiB); +packed, the whole image is ~170 KiB. + +The boot ROM's boot-media byte in the eGON header is not relied upon: +these images boot from SPI NOR only, so the SPL takes any valid header as +an SPI boot (`sunxi_get_boot_device()`). QEMU's stand-in for the ROM does +not write that byte. + +| Offset | Contents | +| --- | --- | +| `0x00000` | SPL (eGON.BT0), padded to 32 KiB | +| `0x08000` | U-Boot, LZMA legacy image | +| `0x40000` | the environment (not in the image) | +| `0x50000` | kernel (OpenIPC NOR layout) | + +## DRAM + +`dram_sun8i_v831.c` is the vendor library, not a rewrite: the function +names are the library's, every register write is the library's, in its +order. It was checked against the vendor boot0 under QEMU: after the +vendor's boot0 (stopped at OP-TEE's entry) and after this SPL (stopped +right after `sunxi_dram_init()`), every MC_COM (`0x04002000`), MCTL/PHY +(`0x04003000`), PLL_DDR, MBUS, DRAM clock and PRCM ZQ register is equal, +for the V831 parameters and for the V833's (DDR3, rank/width/size scan, +the DDR3 AC remapping). QEMU's DRAM controller completes every training +step at once, so this proves the sequence, not the timing on silicon. + +- The parameters are the vendor's `dram_para` words + (`CONFIG_DRAM_SUN8I_V831_*`, from `sys_config.fex` `[dram_para]`): + `openipc/v831_config` the V831 reference boards' (every V831 flash seen + has the same), `openipc/v833_config` the V833 lindenis board's. +- Left out: the chip binding check (`bond_id_check()`), the PMIC DRAM + supply (`dram_vol_set()`), the super standby resume path. +- `sunxi_dram_init()` checks the size the library computed with + `get_ram_size()`. Under QEMU, which does not model the address mapping, + the library's scan reports 4160 MiB, as the vendor's boot0 does there. + +## Hand-off to the kernel + +- OpenIPC's V83x kernels (Linux 4.9, `openipc/linux` `allwinner-v83x`) + carry no device tree; the vendor U-Boot gives them its own. + `board/sunxi/v83x-kernel.dts` is that tree: the Tina V83x SDK's + reference board over the kernel's `sun8iw19p1.dtsi`, flattened, with + what the vendor U-Boot changes in it before booting from SPI NOR (traced + under QEMU from a camera's flash: SPI0 enabled; OP-TEE's memreserve + dropped, there is no OP-TEE). It is gzipped into U-Boot, unpacked at + `misc_init_r()` and booted from `${fdtaddr}`; `ft_board_setup()` sets + `/dram/dram_para2` (the size, as the vendor does), bootm `/memory` and + `/chosen`. +- The kernel runs in the normal world, entered in HYP mode + (`ARMV7_NONSEC`), with PSCI 1.0 from U-Boot's monitor + (`psci_v831.c`), as it expects from OP-TEE. The GIC is named by address + (`CONFIG_ARM_GIC_BASE_ADDRESS`): QEMU's Cortex-A7 reads CBAR as 0. +- `mem=` is the whole DDR. The MTD name is `spi0.0`, the console `ttyS0`. +- The YI camera's own kernel (Linux 4.9 of a later Tina revision) wants + `allwinner,sun50i-spi` and has no `cmdlinepart`: vendor kernels other + than OpenIPC's may need their own tree. + +## Peripherals + +- **Console:** UART0 on PH9/PH10 (function 5), 115200, 24 MHz. +- **SPI NOR:** SPI0 (`spi-sunxi`, the H3 variant; 64-byte FIFO), PC0-PC3 + function 4; DM SPI flash, the environment at 0x40000. +- **SD:** SMHC0 on PF0-PF5, 4-bit (`sunxi_mmc`, the H6's new timing mode). +- **Ethernet:** the EMAC (`sun8i_emac`, A64 variant: no internal PHY), + RMII on PD (function 5), the PHY found by scanning the MDIO bus, reset + on PD19, its 25 MHz from the SoC (`EPHY_25M`, CCU `0x970`). The MAC + comes from the SID, as on every sunxi board. +- **Reset:** the watchdog behind the timers (`0x030090A0`), from U-Boot + and from the kernel's PSCI SYSTEM_RESET. + +## Build + +```sh +bash build.sh # v831 v833; continues past failures, exits non-zero if any failed +bash build.sh v831 # one SoC (CROSS_COMPILE defaults to arm-linux-gnueabi-) +``` + +- The output is `output/u-boot--nor.bin`, built out of tree in + `output/build--nor/`. It needs `xz` (for the LZMA image). +- `.config` is `openipc/openipc_config`, then `openipc/_config`, then + `olddefconfig`. There are no `configs/*_defconfig` for these targets. +- `build.sh` fails a build whose image is over the 256 KiB boot partition + or whose SPL is over 32 KiB. + +## Verification + +The emulator is qemu-bench (OpenIPC/qemu-hisilicon), machines `v831` and +`v833` (`docs/allwinner-v83x.md` there; at the time of writing on its +`allwinner-v83x` branch, not yet merged). `-M ,flash-file=` +does the boot ROM's job. + +- **Smoke (what CI will run):** two boots of an 8 MiB image holding only + U-Boot; the second must load the environment the first saved. Both SoCs + pass (V831 64 MiB, V833 128 MiB). +- **Checks run by hand** (all passed under qemu-bench): + - The DRAM register comparison with the vendor boot0 above (V831 and + V833 parameters). + - An 8 MiB image with an OpenIPC `allwinner-v83x` kernel at 0x50000 + (`v83x.generic.config`, OpenIPC toolchain) and a SquashFS root at + 0x250000 reaches a shell: PSCIv1.0, HYP, six `cmdlinepart` + partitions on `spi0.0`, root on `mtdblock3`; its `sunxi-gmac` pings + the host; `reboot -f` comes back through the SPL. + - U-Boot: `ping` and `tftpboot` (`-nic user,...`), `mmc info` and + `fatls` on a card (`-drive if=sd,...`), `saveenv` + `reset`, the boot + limit (two failed boots, then `altbootcmd` with `failsafe`). +- **What QEMU does not check:** DRAM timing and training on silicon, the + PLL rates and so the UART/SPI/MMC dividers, pad drive, the PHY reset and + clock, the SPI controller's real timing. Not yet run on a camera. From 549a116be8208642b28eaf52cafcff97bcfb3f8e Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov <7954832+dimerr@users.noreply.github.com> Date: Sun, 25 Oct 2026 05:37:59 +0000 Subject: [PATCH 7/8] sunxi: V831/V833: boot from an SD card (u-boot--mmc.bin) The boot ROM tries the SD card before the SPI NOR: an eGON.BT0 at sector 16 wins. build.sh now also builds u-boot--mmc.bin, the NOR image's layout with an SPL that loads U-Boot from the card (openipc/mmc_config: SPL_MMC; U-Boot 32 KiB after the SPL, sunxi's default), to be written to the card at 8 KiB. It is the unbricking path: - Booted from the card, `source` is `fatload mmc 0` for that boot (as after srcmmc, not saved), so ub, ur, uk and fwupd take their files from the card's FAT partition and write them to the NOR. - The environment stays in the SPI NOR. sunxi's env_get_location() picks FAT or MMC after an MMC boot, neither of which is built in, and env_init() failed with -ENODEV before the console came up: fall back to the SPI flash. - The SPL's MMC raw loader read legacy images to their load address as they are; an LZMA one is now read whole and unpacked by spl_load_legacy_img(). Under qemu-bench (whose ROM stand-in now tries the card first and writes the boot-media byte): both SoCs boot from a card with an erased NOR, and a second boot loads the environment the first saved. A card with the -mmc image and u-boot-v831-nor.bin, uImage.v831, rootfs.squashfs.v831 unbricks an erased NOR: run ub, run fwupd, then the NOR boots the kernel to its root without the card. --- CLAUDE.md | 62 +++++++++++++++++++++++++++++++++----------- board/sunxi/board.c | 11 ++++++++ board/sunxi/v83x.c | 17 ++++++++++++ build.sh | 22 ++++++++++------ common/spl/spl_mmc.c | 46 ++++++++++++++++++++++++++++++++ openipc/mmc_config | 8 ++++++ 6 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 openipc/mmc_config diff --git a/CLAUDE.md b/CLAUDE.md index bd49a828438a..160089a6c818 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -71,10 +71,12 @@ git diff --diff-filter=M v2022.10 HEAD # the mainline files we patched (the V831 CCU is the H6's for every gate U-Boot uses), `drivers/pinctrl/sunxi/{Kconfig,pinctrl-sunxi.c}` (the V831's functions). - `board/sunxi/{board.c,Makefile}`: the V831 hooks; `misc_init_r()` → - `openipc_helper()`. + `openipc_helper()`; `env_get_location()` falls back to the SPI flash. - `boot/image-fdt.c`: with `MACH_SUN8I_V831`, a plain uImage boots with the device tree at `${fdtaddr}` (as on `openipc-novatek`). - `common/spl/Kconfig`: the SPL BSS inside 64 MiB. +- `common/spl/spl_mmc.c`: an LZMA legacy image from the card is read + whole and unpacked (the raw path read it to the load address as it is). - `lib/lzma/LzmaTools.c`: `CONFIG_IS_ENABLED(LZMA)`, so SPL_LZMA links (fixed the same way in later mainline). - `common/{Kconfig,Makefile}`: `OPENIPC`, `PRODUCT_SOC`, `PRODUCT_SOCMODEL`. @@ -96,10 +98,9 @@ SPL (`spl/sunxi-spl.bin`, `mkimage -T sunxi_egon`) into SRAM A1 at U-Boot does not fit the 256 KiB boot partition uncompressed (~257 KiB); packed, the whole image is ~170 KiB. -The boot ROM's boot-media byte in the eGON header is not relied upon: -these images boot from SPI NOR only, so the SPL takes any valid header as -an SPI boot (`sunxi_get_boot_device()`). QEMU's stand-in for the ROM does -not write that byte. +The NOR images do not rely on the boot ROM's boot-media byte in the +eGON header: their SPL has no MMC loader, so it takes any valid header as +an SPI boot (`sunxi_get_boot_device()`). | Offset | Contents | | --- | --- | @@ -108,6 +109,31 @@ not write that byte. | `0x40000` | the environment (not in the image) | | `0x50000` | kernel (OpenIPC NOR layout) | +### SD card boot (`u-boot--mmc.bin`) + +The boot ROM tries the SD card (SMHC0) before the SPI NOR: an eGON.BT0 at +sector 16 (8 KiB) of the card wins. The `-mmc` images +(`openipc/mmc_config`: `SPL_MMC`) have the NOR images' layout and are +written to the card at 8 KiB (`dd ... bs=1k seek=8`), before the first +partition at 1 MiB; the SPL loads U-Boot 32 KiB after itself (sunxi's +`U_BOOT_SECTOR` 0x40, counted from the SPL). U-Boot is the NOR image's. + +- This is the unbricking path: a camera with a dead or blank NOR boots + from the card. `v83x_boot_source()` then sets `source=fatload mmc 0` + (as `srcmmc` does) for that boot, not saved, so `ub`, `ur`, `uk` and + `fwupd` read `u-boot--nor.bin`, `rootfs.squashfs.` and + `uImage.` from the card's FAT partition and write them to the NOR. + The next boot without the card is from the NOR. +- The environment stays in the SPI NOR: `env_get_location()` falls back + to it when booted from MMC (sunxi's picks FAT/MMC there, neither built + in, and `env_init()` failed with -ENODEV before the console). +- The SPL's MMC loader read legacy images raw; with `SPL_LZMA` it now + reads a compressed one whole and unpacks it (`common/spl/spl_mmc.c`). +- The SPL trusts the boot ROM's boot-media byte here (the NOR images do + not: they take any valid header as an SPI boot). The H6's ROM writes it; + that the V831's does is assumed, not seen. qemu-bench's ROM stand-in + writes it. + ## DRAM `dram_sun8i_v831.c` is the vendor library, not a rewrite: the function @@ -171,23 +197,29 @@ bash build.sh # v831 v833; continues past failures, exits non-zero bash build.sh v831 # one SoC (CROSS_COMPILE defaults to arm-linux-gnueabi-) ``` -- The output is `output/u-boot--nor.bin`, built out of tree in - `output/build--nor/`. It needs `xz` (for the LZMA image). -- `.config` is `openipc/openipc_config`, then `openipc/_config`, then - `olddefconfig`. There are no `configs/*_defconfig` for these targets. +- The output is `output/u-boot--nor.bin` and `-mmc.bin`, built out + of tree in `output/build--{nor,mmc}/`. It needs `xz` (for the LZMA + image). +- `.config` is `openipc/openipc_config`, then `openipc/_config` + (and for `-mmc`, `openipc/mmc_config`), then `olddefconfig`. There are + no `configs/*_defconfig` for these targets. - `build.sh` fails a build whose image is over the 256 KiB boot partition or whose SPL is over 32 KiB. ## Verification -The emulator is qemu-bench (OpenIPC/qemu-hisilicon), machines `v831` and -`v833` (`docs/allwinner-v83x.md` there; at the time of writing on its -`allwinner-v83x` branch, not yet merged). `-M ,flash-file=` -does the boot ROM's job. +The emulator is qemu-bench (OpenIPC/qemu-bench, formerly qemu-hisilicon), +machines `v831` and `v833` (`docs/allwinner-v83x.md` there). +`-M ,flash-file=` does the boot ROM's job; booting from an SD +card (`-drive if=sd`) needs qemu-bench #204. - **Smoke (what CI will run):** two boots of an 8 MiB image holding only - U-Boot; the second must load the environment the first saved. Both SoCs - pass (V831 64 MiB, V833 128 MiB). + U-Boot (`-mmc`: an erased NOR and a card with the image at 8 KiB); the + second must load the environment the first saved. Both SoCs, both + kinds pass (V831 64 MiB, V833 128 MiB). + - SD unbricking: an erased NOR and a card with the `-mmc` image and + the three files on FAT: `run ub`, `run fwupd`, then a boot without + the card mounts the root from the NOR. - **Checks run by hand** (all passed under qemu-bench): - The DRAM register comparison with the vendor boot0 above (V831 and V833 parameters). diff --git a/board/sunxi/board.c b/board/sunxi/board.c index fcddedab1dc5..e6376d5bfced 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -54,6 +54,7 @@ void v83x_board_init(void); void v83x_kernel_fdt(void); void v83x_ft_board_setup(void *blob); +void v83x_boot_source(void); DECLARE_GLOBAL_DATA_PTR; @@ -184,6 +185,12 @@ enum env_location env_get_location(enum env_operation op, int prio) return ENVL_FAT; if (IS_ENABLED(CONFIG_ENV_IS_IN_UBI)) return ENVL_UBI; + /* + * The V831/V833 images keep it in the SPI NOR alone, also + * when the SPL and U-Boot came from an SD card + */ + if (IS_ENABLED(CONFIG_ENV_IS_IN_SPI_FLASH)) + return ENVL_SPI_FLASH; } return ENVL_UNKNOWN; @@ -875,6 +882,10 @@ int misc_init_r(void) openipc_helper(); #endif + /* after the helper, which may save the default environment */ + if (IS_ENABLED(CONFIG_MACH_SUN8I_V831)) + v83x_boot_source(); + return 0; } diff --git a/board/sunxi/v83x.c b/board/sunxi/v83x.c index 1434b836572a..6e4020e6bff3 100644 --- a/board/sunxi/v83x.c +++ b/board/sunxi/v83x.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -53,6 +55,21 @@ void v83x_kernel_fdt(void) env_set_hex("fdtaddr", (ulong)fdt); } +/* + * Booted from an SD card (the -mmc images): that card is where the + * unbricking files are, so ub/uk/ur read them from it, as after srcmmc. + * Set for this boot only, not saved: the next boot from the SPI NOR keeps + * the environment's source. + */ +void v83x_boot_source(void) +{ + if (sunxi_get_boot_device() != BOOT_DEVICE_MMC1) + return; + + printf("V83x: booted from the SD card, update source: fatload mmc 0\n"); + env_set("source", "fatload mmc 0"); +} + /* What the vendor U-Boot sets at boot besides /memory and /chosen */ void v83x_ft_board_setup(void *blob) { diff --git a/build.sh b/build.sh index acb8f232fd71..6a367d583aea 100755 --- a/build.sh +++ b/build.sh @@ -12,7 +12,9 @@ export CROSS_COMPILE=${CROSS_COMPILE:-arm-linux-gnueabi-} BOOT_SRCDIR=$(pwd) -# The boot partition: the environment starts at 0x40000 +# The boot partition: the environment starts at 0x40000. On an SD card +# the image starts at 8 KiB and must end before the first partition at +# 1 MiB; the NOR limit is the tighter. BOOT_PART_SIZE=262144 # U-Boot proper's offset in the image, where the SPL reads it from UBOOT_OFFSET=32768 @@ -28,17 +30,21 @@ mkconfig() { cat "$@" > "${builddir}/.config" } -# Allwinner V831/V833: the boot ROM loads the SPL (eGON.BT0) from SPI NOR -# offset 0 into SRAM; the SPL brings up the DDR and unpacks U-Boot, an -# LZMA-compressed legacy image, from offset 0x8000 to its link address. +# Allwinner V831/V833: the boot ROM loads the SPL (eGON.BT0) into SRAM, +# from SPI NOR offset 0 ("nor") or SD card sector 16 ("mmc"); the SPL +# brings up the DDR and unpacks U-Boot, an LZMA-compressed legacy image, +# from 0x8000 after itself to its link address. build_v83x() { soc=$1 - builddir=${BOOT_SRCDIR}/output/build-${soc}-nor + kind=$2 + builddir=${BOOT_SRCDIR}/output/build-${soc}-${kind} make_opts="-C ${BOOT_SRCDIR} O=${builddir} -j$(nproc)" - out=output/u-boot-${soc}-nor.bin + out=output/u-boot-${soc}-${kind}.bin + fragments="openipc/openipc_config openipc/${soc}_config" + [ "${kind}" = mmc ] && fragments="${fragments} openipc/mmc_config" rm -rf "${builddir}" - mkconfig "${builddir}" openipc/openipc_config openipc/${soc}_config && \ + mkconfig "${builddir}" ${fragments} && \ make ${make_opts} olddefconfig /dev/null && \ make ${make_opts} &2 return 1 ;; diff --git a/common/spl/spl_mmc.c b/common/spl/spl_mmc.c index 23a395e63d4e..db30c8731130 100644 --- a/common/spl/spl_mmc.c +++ b/common/spl/spl_mmc.c @@ -16,6 +16,46 @@ #include #include #include +#include + +static ulong spl_mem_load_read(struct spl_load_info *load, ulong offs, + ulong size, void *buf) +{ + memcpy(buf, (u8 *)load->priv + offs, size); + + return size; +} + +/* + * A compressed legacy image cannot be read to its load address: read it + * whole, then let spl_load_legacy_img() unpack it there. + */ +static int mmc_load_legacy_compressed(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev, + struct mmc *mmc, ulong sector, + struct image_header *header) +{ + struct blk_desc *bd = mmc_get_blk_desc(mmc); + ulong len = sizeof(*header) + image_get_data_size(header); + ulong blocks = DIV_ROUND_UP(len, bd->blksz); + struct spl_load_info load; + void *buf; + + buf = malloc(blocks * bd->blksz); + if (!buf) + return -ENOMEM; + + if (blk_dread(bd, sector, blocks, buf) != blocks) + return -EIO; + + load.dev = NULL; + load.priv = buf; + load.filename = NULL; + load.bl_len = 1; + load.read = spl_mem_load_read; + + return spl_load_legacy_img(spl_image, bootdev, &load, 0); +} static int mmc_load_legacy(struct spl_image_info *spl_image, struct spl_boot_device *bootdev, @@ -28,6 +68,12 @@ static int mmc_load_legacy(struct spl_image_info *spl_image, u32 image_offset; int ret; + if (IS_ENABLED(CONFIG_SPL_LZMA) && + image_get_magic(header) == IH_MAGIC && + image_get_comp(header) == IH_COMP_LZMA) + return mmc_load_legacy_compressed(spl_image, bootdev, mmc, + sector, header); + ret = spl_parse_image_header(spl_image, bootdev, header); if (ret) return ret; diff --git a/openipc/mmc_config b/openipc/mmc_config new file mode 100644 index 000000000000..da65bc549c68 --- /dev/null +++ b/openipc/mmc_config @@ -0,0 +1,8 @@ +# SD card boot: the boot ROM finds the SPL at sector 16 (8 KiB) of the +# card, before it tries the SPI NOR; the SPL loads U-Boot from the card +# too, 32 KiB after itself (sunxi's U_BOOT_SECTOR 0x40 counts from the +# SPL, DATA_PART_OFFSET adds the 16 sectors before it). The image is the +# NOR one's layout: dd it to the card at 8 KiB. U-Boot itself is the NOR +# image's, its environment in the SPI NOR. +CONFIG_SPL_MMC=y +CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR=y From bb5a76a115d749e991b8f3fe5750d9f0263d2e56 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov <7954832+dimerr@users.noreply.github.com> Date: Sun, 25 Oct 2026 05:54:17 +0000 Subject: [PATCH 8/8] sunxi: V831/V833: address review -- idle states, env fallback, tpr3 - The kernel device tree advertised three PSCI CPU idle states, which U-Boot's PSCI does not implement (no CPU_SUSPEND): left out of v83x-kernel.dts, as is the cpu node's reference to them. - The SPI flash fallback in sunxi's env_get_location() applied to every sunxi board with ENV_IS_IN_SPI_FLASH, which would have moved theirs to the NOR after an MMC or FEL boot: V831/V833 only. - DRAM_SUN8I_V831_TPR3 went nowhere: in the vendor library dram_tpr3 carries the DRAM supply voltages for dram_vol_set(), the PMIC step that is not translated. The option is gone; the struct keeps the word, so the parameter block is still the vendor's. Both SoCs' NOR and SD images pass the two-boot smoke test, and the OpenIPC kernel boots on the new device tree as before. --- arch/arm/mach-sunxi/Kconfig | 4 --- arch/arm/mach-sunxi/dram_sun8i_v831.c | 3 +-- board/sunxi/board.c | 3 ++- board/sunxi/v83x-kernel.dts | 36 +-------------------------- 4 files changed, 4 insertions(+), 42 deletions(-) diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index fb930a0ccf89..38f19fc8c7a6 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -575,10 +575,6 @@ config DRAM_SUN8I_V831_TPR2 hex "dram_tpr2" default 0x00057041 -config DRAM_SUN8I_V831_TPR3 - hex "dram_tpr3" - default 0xb4787896 - config DRAM_SUN8I_V831_TPR5 hex "dram_tpr5" default 0x48484848 diff --git a/arch/arm/mach-sunxi/dram_sun8i_v831.c b/arch/arm/mach-sunxi/dram_sun8i_v831.c index 7a4269586ef5..d05691acf2e8 100644 --- a/arch/arm/mach-sunxi/dram_sun8i_v831.c +++ b/arch/arm/mach-sunxi/dram_sun8i_v831.c @@ -55,7 +55,7 @@ struct dram_para { u32 tpr0; u32 tpr1; u32 tpr2; - u32 tpr3; + u32 tpr3; /* the DRAM supply voltages, for dram_vol_set() */ u32 tpr4; u32 tpr5; u32 tpr6; @@ -1043,7 +1043,6 @@ unsigned long sunxi_dram_init(void) .tpr0 = CONFIG_DRAM_SUN8I_V831_TPR0, .tpr1 = CONFIG_DRAM_SUN8I_V831_TPR1, .tpr2 = CONFIG_DRAM_SUN8I_V831_TPR2, - .tpr3 = CONFIG_DRAM_SUN8I_V831_TPR3, .tpr5 = CONFIG_DRAM_SUN8I_V831_TPR5, .tpr6 = CONFIG_DRAM_SUN8I_V831_TPR6, .tpr7 = CONFIG_DRAM_SUN8I_V831_TPR7, diff --git a/board/sunxi/board.c b/board/sunxi/board.c index e6376d5bfced..6b0ed4e97816 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -189,7 +189,8 @@ enum env_location env_get_location(enum env_operation op, int prio) * The V831/V833 images keep it in the SPI NOR alone, also * when the SPL and U-Boot came from an SD card */ - if (IS_ENABLED(CONFIG_ENV_IS_IN_SPI_FLASH)) + if (IS_ENABLED(CONFIG_MACH_SUN8I_V831) && + IS_ENABLED(CONFIG_ENV_IS_IN_SPI_FLASH)) return ENVL_SPI_FLASH; } diff --git a/board/sunxi/v83x-kernel.dts b/board/sunxi/v83x-kernel.dts index 140e91add868..b04145749d48 100644 --- a/board/sunxi/v83x-kernel.dts +++ b/board/sunxi/v83x-kernel.dts @@ -7,6 +7,7 @@ * preprocessed and flattened, with what the vendor U-Boot changes in it * before it boots a kernel from SPI NOR (traced under QEMU from a camera's * flash): SPI0, the flash, enabled; and without the memory OP-TEE took. + * The CPU idle states are left out: U-Boot's PSCI has no CPU_SUSPEND. * /memory, /chosen and /dram/dram_para2 (the DRAM size) are set at boot. */ @@ -2935,46 +2936,11 @@ clock-frequency = <0x3c14dc00>; clock-latency = <0x1e8480>; operating-points-v2 = <0xa2>; - cpu-idle-states = <0xa3 0xa4 0xa5>; cooling-min-level = <0x05>; cooling-max-level = <0x00>; #cooling-cells = <0x02>; dynamic-power-coefficient = <0x96>; }; - - idle-states { - entry-method = "arm,psci"; - - cpu-sleep-0 { - compatible = "arm,idle-state"; - arm,psci-suspend-param = <0x10000>; - entry-latency-us = <0x2d>; - exit-latency-us = <0x5a>; - min-residency-us = <0xbb8>; - local-timer-stop; - phandle = <0xa3>; - }; - - cluster-sleep-0 { - compatible = "arm,idle-state"; - arm,psci-suspend-param = <0x1010000>; - entry-latency-us = <0x37>; - exit-latency-us = <0x23a>; - min-residency-us = <0x4268>; - local-timer-stop; - phandle = <0xa4>; - }; - - sys-sleep-0 { - compatible = "arm,idle-state"; - arm,psci-suspend-param = <0x2010000>; - entry-latency-us = <0xffffffff>; - exit-latency-us = <0xffffffff>; - min-residency-us = <0xffffffff>; - local-timer-stop; - phandle = <0xa5>; - }; - }; }; psci {