mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC
@ 2023-05-10 23:37 Jules Maselbas
  2023-05-10 23:37 ` [RFC PATCH 01/11] scripts: Add Allwinner eGON image support Jules Maselbas
                   ` (10 more replies)
  0 siblings, 11 replies; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Hi all,

I have been trying to port barebox on sun50i/A64 SoC targeting the pineA64
single board computer and the pinephone from Pine64.

Currently only the SD/MMC driver have been ported. It does work with SD card
and have worked on eMMC in the past but I might have fried my board...

This patch series has some rough edges, I am looking forward you're insights.

Cheers,

Jules Maselbas (11):
  scripts: Add Allwinner eGON image support
  sunxi: introduce mach-sunxi
  ARM: dls: Add ENTRY_HEADER macro to add .text section
  sunxi: Add lowlevel switch to aarch64
  arm: sunxi: Add debug_ll
  clk: Add clock driver for sun50i-a64
  pinctrl: Add sun50i-a64 pinctrl driver
  mci: Add sunxi-mmc driver
  arm: sunxi: Add sun50i SDRAM init
  arm: boards: sunxi: Add initial support for the pinephone
  arm: boards: sunxi: Add pine64 board

 arch/arm/Kconfig                             |   12 +
 arch/arm/Makefile                            |    1 +
 arch/arm/boards/Makefile                     |    2 +
 arch/arm/boards/pine64-pine64/Makefile       |    1 +
 arch/arm/boards/pine64-pine64/lowlevel.c     |  148 +++
 arch/arm/boards/pine64-pinephone/Makefile    |    2 +
 arch/arm/boards/pine64-pinephone/board.c     |    0
 arch/arm/boards/pine64-pinephone/lowlevel.c  |  119 +++
 arch/arm/configs/pinephone_defconfig         |   11 +
 arch/arm/dts/Makefile                        |    2 +
 arch/arm/dts/sun50i-a64-pine64-plus.dts      |   18 +
 arch/arm/dts/sun50i-a64-pinephone-1_2.dts    |    3 +
 arch/arm/include/asm/barebox.lds.h           |    2 +
 arch/arm/include/asm/debug_ll.h              |    2 +
 arch/arm/lib/pbl.lds.S                       |    1 +
 arch/arm/mach-sunxi/Kconfig                  |   51 +
 arch/arm/mach-sunxi/Makefile                 |    6 +
 arch/arm/mach-sunxi/clock_sun6i.h            |  540 ++++++++++
 arch/arm/mach-sunxi/cpu_init.c               |   44 +
 arch/arm/mach-sunxi/ddr3_1333.c              |   85 ++
 arch/arm/mach-sunxi/dram_sunxi_dw.h          |  241 +++++
 arch/arm/mach-sunxi/egon_header.c            |   11 +
 arch/arm/mach-sunxi/lpddr3_stock.c           |   81 ++
 arch/arm/mach-sunxi/rmr_switch.S             |   47 +
 arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c   |    9 +
 arch/arm/mach-sunxi/sun50i-a64-lpddr3-init.c |    9 +
 arch/arm/mach-sunxi/sun50i-sdram.c           |  903 ++++++++++++++++
 arch/arm/mach-sunxi/sunxi-sdram.c            | 1007 ++++++++++++++++++
 arch/arm/mach-sunxi/sunxi.c                  |    0
 drivers/clk/Makefile                         |    1 +
 drivers/clk/sunxi/Makefile                   |    2 +
 drivers/clk/sunxi/clk-sun50i-a64.c           |  317 ++++++
 drivers/clk/sunxi/clk-sun50i-a64.h           |   62 ++
 drivers/mci/Kconfig                          |    6 +
 drivers/mci/Makefile                         |    2 +
 drivers/mci/sunxi-mmc-common.c               |  259 +++++
 drivers/mci/sunxi-mmc-pbl.c                  |   81 ++
 drivers/mci/sunxi-mmc.c                      |  173 +++
 drivers/mci/sunxi-mmc.h                      |  225 ++++
 drivers/pinctrl/Kconfig                      |    2 +
 drivers/pinctrl/Makefile                     |    1 +
 drivers/pinctrl/sunxi/Kconfig                |   15 +
 drivers/pinctrl/sunxi/Makefile               |    3 +
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c   |  593 +++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.c        |  371 +++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h        |  224 ++++
 images/Makefile                              |    1 +
 images/Makefile.sunxi                        |   32 +
 include/asm-generic/barebox.lds.h            |    4 +
 include/mach/sunxi/barebox.lds.h             |    6 +
 include/mach/sunxi/debug_ll.h                |   35 +
 include/mach/sunxi/egon.h                    |   63 ++
 include/mach/sunxi/init.h                    |   13 +
 include/mach/sunxi/rmr_switch.h              |   10 +
 include/mach/sunxi/sun50i-regs.h             |   36 +
 include/mach/sunxi/sunxi-pinctrl.h           |   13 +
 include/mach/sunxi/xload.h                   |   12 +
 scripts/Kconfig                              |    7 +
 scripts/Makefile                             |    1 +
 scripts/egon_mkimage.c                       |  115 ++
 60 files changed, 6043 insertions(+)
 create mode 100644 arch/arm/boards/pine64-pine64/Makefile
 create mode 100644 arch/arm/boards/pine64-pine64/lowlevel.c
 create mode 100644 arch/arm/boards/pine64-pinephone/Makefile
 create mode 100644 arch/arm/boards/pine64-pinephone/board.c
 create mode 100644 arch/arm/boards/pine64-pinephone/lowlevel.c
 create mode 100644 arch/arm/configs/pinephone_defconfig
 create mode 100644 arch/arm/dts/sun50i-a64-pine64-plus.dts
 create mode 100644 arch/arm/dts/sun50i-a64-pinephone-1_2.dts
 create mode 100644 arch/arm/mach-sunxi/Kconfig
 create mode 100644 arch/arm/mach-sunxi/Makefile
 create mode 100644 arch/arm/mach-sunxi/clock_sun6i.h
 create mode 100644 arch/arm/mach-sunxi/cpu_init.c
 create mode 100644 arch/arm/mach-sunxi/ddr3_1333.c
 create mode 100644 arch/arm/mach-sunxi/dram_sunxi_dw.h
 create mode 100644 arch/arm/mach-sunxi/egon_header.c
 create mode 100644 arch/arm/mach-sunxi/lpddr3_stock.c
 create mode 100644 arch/arm/mach-sunxi/rmr_switch.S
 create mode 100644 arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c
 create mode 100644 arch/arm/mach-sunxi/sun50i-a64-lpddr3-init.c
 create mode 100644 arch/arm/mach-sunxi/sun50i-sdram.c
 create mode 100644 arch/arm/mach-sunxi/sunxi-sdram.c
 create mode 100644 arch/arm/mach-sunxi/sunxi.c
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.c
 create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.h
 create mode 100644 drivers/mci/sunxi-mmc-common.c
 create mode 100644 drivers/mci/sunxi-mmc-pbl.c
 create mode 100644 drivers/mci/sunxi-mmc.c
 create mode 100644 drivers/mci/sunxi-mmc.h
 create mode 100644 drivers/pinctrl/sunxi/Kconfig
 create mode 100644 drivers/pinctrl/sunxi/Makefile
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h
 create mode 100644 images/Makefile.sunxi
 create mode 100644 include/mach/sunxi/barebox.lds.h
 create mode 100644 include/mach/sunxi/debug_ll.h
 create mode 100644 include/mach/sunxi/egon.h
 create mode 100644 include/mach/sunxi/init.h
 create mode 100644 include/mach/sunxi/rmr_switch.h
 create mode 100644 include/mach/sunxi/sun50i-regs.h
 create mode 100644 include/mach/sunxi/sunxi-pinctrl.h
 create mode 100644 include/mach/sunxi/xload.h
 create mode 100644 scripts/egon_mkimage.c

-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 01/11] scripts: Add Allwinner eGON image support
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-11  7:25   ` Sascha Hauer
  2023-05-18 18:47   ` Ahmad Fatoum
  2023-05-10 23:37 ` [RFC PATCH 02/11] sunxi: introduce mach-sunxi Jules Maselbas
                   ` (9 subsequent siblings)
  10 siblings, 2 replies; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

On power-up Allwinner SoC starts in boot ROM, aka BROM, which will search
for an eGON image: first from the SD card, then from eMMC. If no image is
found then the BROM will enter into FEL mode that can be used for initial
programming and recovery of devices using USB.

The eGON header structure is adapted from u-boot: /include/sunxi_image.h,
the header structure is also documented on https://linux-sunxi.org/EGON

BROM will load, at most, the first 32KB of the image into SRAM, including
the header itself! The jump instruction in the header needs to be patched
accordingly with the image size.
---
 arch/arm/mach-sunxi/egon_header.c |  11 +++
 include/mach/sunxi/egon.h         |  63 ++++++++++++++++
 scripts/Kconfig                   |   7 ++
 scripts/Makefile                  |   1 +
 scripts/egon_mkimage.c            | 115 ++++++++++++++++++++++++++++++
 5 files changed, 197 insertions(+)
 create mode 100644 arch/arm/mach-sunxi/egon_header.c
 create mode 100644 include/mach/sunxi/egon.h
 create mode 100644 scripts/egon_mkimage.c

diff --git a/arch/arm/mach-sunxi/egon_header.c b/arch/arm/mach-sunxi/egon_header.c
new file mode 100644
index 0000000000..9a4822f71d
--- /dev/null
+++ b/arch/arm/mach-sunxi/egon_header.c
@@ -0,0 +1,11 @@
+#include <common.h>
+#include <mach/sunxi/egon.h>
+#if 0
+const struct egon_header sunxi_egon_header
+__attribute__((section(".text_egon_header"))) = {
+	 /* put a valid arm32 jump over the header, in order for images to
+	  * to work when not loaded by boot rom but by the FEL */
+	.branch = EGON_HDR_BRANCH,
+	.magic = "eGON",
+};
+#endif
diff --git a/include/mach/sunxi/egon.h b/include/mach/sunxi/egon.h
new file mode 100644
index 0000000000..f70730ee25
--- /dev/null
+++ b/include/mach/sunxi/egon.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef SUNIX_EGON
+#define SUNIX_EGON
+
+struct egon_header {
+	uint32_t branch; /* branch instruction to jump over the header */
+	uint8_t magic[8]; /* "eGON.BT0" or "eGON.BT1" */
+	uint32_t check_sum;
+	uint32_t length;
+	/*
+	 * We use a simplified header, only filling in what is needed
+	 * by the boot ROM. To be compatible with Allwinner tools we
+	 * would need to implement the proper fields here instead of
+	 * padding.
+	 *
+	 * Actually we want the ability to recognize our "sunxi" variant
+	 * of the SPL. To do so, let's place a special signature into the
+	 * "pub_head_size" field. We can reasonably expect Allwinner's
+	 * boot0 to always have the upper 16 bits of this set to 0 (after
+	 * all the value shouldn't be larger than the limit imposed by
+	 * SRAM size).
+	 * If the signature is present (at 0x14), then we know it's safe
+	 * to use the remaining 8 bytes (at 0x18) for our own purposes.
+	 * (E.g. sunxi-tools "fel" utility can pass information there.)
+	 */
+	union {
+		uint32_t header_size;
+		uint8_t spl_signature[4];
+	};
+	uint32_t fel_script_address;/* since v0.1, set by sunxi-fel */
+	/*
+	 * If the fel_uEnv_length member below is set to a non-zero value,
+	 * it specifies the size (byte count) of data at fel_script_address.
+	 * At the same time this indicates that the data is in uEnv.txt
+	 * compatible format, ready to be imported via "env import -t".
+	 */
+	uint32_t fel_uEnv_length;/* since v0.1, set by sunxi-fel */
+	/*
+	 * Offset of an ASCIIZ string (relative to the SPL header), which
+	 * contains the default device tree name (CONFIG_DEFAULT_DEVICE_TREE).
+	 * This is optional and may be set to NULL. Is intended to be used
+	 * by flash programming tools for providing nice informative messages
+	 * to the users.
+	 */
+	uint32_t dt_name_offset;/* since v0.2, set by mksunxiboot */
+	uint32_t dram_size;/* in MiB, since v0.3, set by SPL */
+	uint32_t boot_media;/* written here by the boot ROM */
+	/* A padding area (may be used for storing text strings) */
+	uint32_t string_pool[13];/* since v0.2, filled by mksunxiboot */
+	/* The header must be a multiple of 32 bytes (for VBAR alignment) */
+	/* And at least 64 byte (https://patchwork.ozlabs.org/patch/622173) */
+};
+#define EGON_HDR_BRANCH (0xea000000 | (sizeof(struct egon_header) / 4 - 2))
+#define sunxi_egon_header()						\
+	{								\
+		static const struct egon_header _hdr			\
+			__attribute__((section(".text_head_egon_header"))) = \
+			{ .branch = EGON_HDR_BRANCH, .magic = "eGON" };	\
+		__keep_symbolref(_hdr);					\
+	}
+
+#endif
diff --git a/scripts/Kconfig b/scripts/Kconfig
index dcd5f32d1d..7517f5b79f 100644
--- a/scripts/Kconfig
+++ b/scripts/Kconfig
@@ -56,6 +56,13 @@ config RK_IMAGE
 	help
 	  This enables building the image creation tool for Rockchip SoCs
 
+config EGON_IMAGE
+       bool "Allwinner eGON image tool" if COMPILE_HOST_TOOLS
+       depends on ARCH_SUNXI || COMPILE_HOST_TOOLS
+       default y if ARCH_SUNXI
+       help
+         This enables building the image creation tool for Allwinner sunxi SoCs
+
 config OMAP_IMAGE
 	bool "TI OMAP image tools" if COMPILE_HOST_TOOLS
 	depends on ARCH_OMAP || COMPILE_HOST_TOOLS
diff --git a/scripts/Makefile b/scripts/Makefile
index 72ad9ad7a6..13e80db7af 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -28,6 +28,7 @@ hostprogs-always-$(CONFIG_LAYERSCAPE_PBLIMAGE)		+= pblimage
 hostprogs-always-$(CONFIG_STM32_IMAGE)			+= stm32image
 hostprogs-always-$(CONFIG_RISCV)			+= prelink-riscv
 hostprogs-always-$(CONFIG_RK_IMAGE)			+= rkimage
+hostprogs-always-$(CONFIG_EGON_IMAGE)			+= egon_mkimage
 HOSTCFLAGS_rkimage = `pkg-config --cflags openssl`
 HOSTLDLIBS_rkimage = `pkg-config --libs openssl`
 KBUILD_HOSTCFLAGS += -I$(srctree)/scripts/include/
diff --git a/scripts/egon_mkimage.c b/scripts/egon_mkimage.c
new file mode 100644
index 0000000000..887caa29f0
--- /dev/null
+++ b/scripts/egon_mkimage.c
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "../include/mach/sunxi/egon.h"
+
+#include "compiler.h"
+#include "common.h"
+#include "common.c"
+
+#define ALIGN(x, a)		__ALIGN_MASK(x, (typeof(x))(a) - 1)
+#define __ALIGN_MASK(x, mask)	(((x) + (mask)) & ~(mask))
+#define STAMP_VALUE 0x5f0a6c39
+
+static void mkimage(char *infile, char *outfile)
+{
+	struct egon_header *hdr;
+	uint32_t *p32;
+	uint32_t sum;
+	int i;
+	size_t hdr_size;
+	size_t bin_size;
+	size_t img_size;
+	char *bin;
+	int fd, ret;
+
+	bin = read_file(infile, &bin_size);
+	if (!bin) {
+		perror("read_file");
+		exit(1);
+	}
+
+	/* the header must be a multiple of 32 bytes */
+	hdr_size = sizeof(*hdr);
+
+	/* test if the binary has reserved space for the header */
+	hdr = (void *)bin;
+	if (hdr->branch == EGON_HDR_BRANCH && memcmp(hdr->magic, "eGON", 4) == 0) {
+		/* strip the existing header */
+		bin += hdr_size;
+		bin_size -= hdr_size;
+	}
+	hdr = calloc(1, hdr_size);
+	if (!hdr) {
+		perror("malloc");
+		exit(1);
+	}
+
+	/* The total image length must be a multiple of 4K bytes */
+	img_size = ALIGN(hdr_size + bin_size, 4096);
+
+	hdr->check_sum = 0;
+	hdr->branch = EGON_HDR_BRANCH;
+	hdr->length = cpu_to_le32(img_size);
+	memcpy(hdr->magic, "eGON.BT0", 8);
+	memcpy(hdr->spl_signature, "SPL", 3);
+	hdr->spl_signature[3] = 0x03; /* version 0.3 */
+
+	/* calculate the checksum */
+	sum = STAMP_VALUE;
+	for (p32 = (void *) hdr, i = 0; i < hdr_size / sizeof(uint32_t); i++)
+		sum += le32_to_cpu(p32[i]);
+	for (p32 = (void *) bin, i = 0; i < bin_size / sizeof(uint32_t); i++)
+		sum += le32_to_cpu(p32[i]);
+	hdr->check_sum = cpu_to_le32(sum);
+
+	fd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT,
+		  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+	if (fd < 0) {
+		fprintf(stderr, "Cannot open %s: %s\n", outfile, strerror(errno));
+		exit(1);
+	}
+	/* write the header */
+	ret = write_full(fd, hdr, hdr_size);
+	if (ret < 0) {
+		perror("write_full");
+		exit(1);
+	}
+	/* write the binary */
+	ret = write_full(fd, bin, bin_size);
+	if (ret < 0) {
+		perror("write_full");
+		exit(1);
+	}
+	/* align the image */
+	ret = ftruncate(fd, img_size);
+	if (ret < 0) {
+		perror("ftruncate");
+		exit(1);
+	}
+	close(fd);
+
+	free(hdr);
+}
+
+static void usage(char *argv0)
+{
+	fprintf(stderr, "usage: %s <infile> <outfile>\n", argv0);
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc != 3) {
+		usage(argv[0]);
+		return 1;
+	}
+
+	mkimage(argv[1], argv[2]);
+
+	return 0;
+}
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 02/11] sunxi: introduce mach-sunxi
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
  2023-05-10 23:37 ` [RFC PATCH 01/11] scripts: Add Allwinner eGON image support Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-11  7:27   ` Sascha Hauer
  2023-05-18 18:46   ` Ahmad Fatoum
  2023-05-10 23:37 ` [RFC PATCH 03/11] ARM: dls: Add ENTRY_HEADER macro to add .text section Jules Maselbas
                   ` (8 subsequent siblings)
  10 siblings, 2 replies; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Add kbuild boilerplate and some early init functions
---
 arch/arm/Kconfig                   | 12 ++++++++++
 arch/arm/Makefile                  |  1 +
 arch/arm/mach-sunxi/Kconfig        | 16 +++++++++++++
 arch/arm/mach-sunxi/Makefile       |  2 ++
 arch/arm/mach-sunxi/cpu_init.c     | 33 +++++++++++++++++++++++++++
 arch/arm/mach-sunxi/sunxi.c        |  0
 images/Makefile                    |  1 +
 images/Makefile.sunxi              | 13 +++++++++++
 include/mach/sunxi/init.h          |  6 +++++
 include/mach/sunxi/sun50i-regs.h   | 36 ++++++++++++++++++++++++++++++
 include/mach/sunxi/sunxi-pinctrl.h | 13 +++++++++++
 11 files changed, 133 insertions(+)
 create mode 100644 arch/arm/mach-sunxi/Kconfig
 create mode 100644 arch/arm/mach-sunxi/Makefile
 create mode 100644 arch/arm/mach-sunxi/cpu_init.c
 create mode 100644 arch/arm/mach-sunxi/sunxi.c
 create mode 100644 images/Makefile.sunxi
 create mode 100644 include/mach/sunxi/init.h
 create mode 100644 include/mach/sunxi/sun50i-regs.h
 create mode 100644 include/mach/sunxi/sunxi-pinctrl.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index abe649de49..8da03e2703 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -144,6 +144,17 @@ config ARCH_SOCFPGA
 	select COMMON_CLK
 	select CLKDEV_LOOKUP
 
+config ARCH_SUNXI
+	bool "Allwinner SoCs"
+	select OFTREE
+	select OFDEVICE
+	select COMMON_CLK
+	select COMMON_CLK_OF_PROVIDER
+	select CLKDEV_LOOKUP
+	select GENERIC_GPIO
+	select GPIOLIB
+	select PINCTRL
+
 config ARCH_VERSATILE
 	bool "ARM Versatile boards (ARM926EJ-S)"
 	select GPIOLIB
@@ -311,6 +322,7 @@ source "arch/arm/mach-omap/Kconfig"
 source "arch/arm/mach-pxa/Kconfig"
 source "arch/arm/mach-rockchip/Kconfig"
 source "arch/arm/mach-socfpga/Kconfig"
+source "arch/arm/mach-sunxi/Kconfig"
 source "arch/arm/mach-stm32mp/Kconfig"
 source "arch/arm/mach-versatile/Kconfig"
 source "arch/arm/mach-vexpress/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index a506f1e3a3..bb61392e4c 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -103,6 +103,7 @@ machine-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip
 machine-$(CONFIG_ARCH_SAMSUNG)		+= samsung
 machine-$(CONFIG_ARCH_SOCFPGA)		+= socfpga
 machine-$(CONFIG_ARCH_STM32MP)		+= stm32mp
+machine-$(CONFIG_ARCH_SUNXI)		+= sunxi
 machine-$(CONFIG_ARCH_VERSATILE)	+= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		+= vexpress
 machine-$(CONFIG_ARCH_TEGRA)		+= tegra
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
new file mode 100644
index 0000000000..0e8d83fedd
--- /dev/null
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -0,0 +1,16 @@
+if ARCH_SUNXI
+
+config ARCH_TEXT_BASE
+	hex
+	default 0x0
+
+menuconfig SUNXI_MULTI_BOARDS
+	bool "Allwinner boards"
+	select HAVE_PBL_MULTI_IMAGES
+	select RELOCATABLE
+
+if SUNXI_MULTI_BOARDS
+
+endif
+
+endif
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
new file mode 100644
index 0000000000..d678973ca2
--- /dev/null
+++ b/arch/arm/mach-sunxi/Makefile
@@ -0,0 +1,2 @@
+obj-y += sunxi.o
+lwl-y += cpu_init.o
diff --git a/arch/arm/mach-sunxi/cpu_init.c b/arch/arm/mach-sunxi/cpu_init.c
new file mode 100644
index 0000000000..f4092d8d5d
--- /dev/null
+++ b/arch/arm/mach-sunxi/cpu_init.c
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <common.h>
+#include <linux/sizes.h>
+#include <asm/barebox-arm-head.h>
+// #include <asm/errata.h> TODO: errata 843419 ?
+#include <io.h>
+#include <debug_ll.h>
+#include <mach/sunxi/init.h>
+#include <mach/sunxi/sun50i-regs.h>
+#include <mach/sunxi/sunxi-pinctrl.h>
+
+static void sunxi_ccu_init(void __iomem *ccu)
+{
+	/* APB2 = 24MHz (UART, I2C) */
+	writel((1 << 24 /* src: 1=osc24 */) |
+	       (0 << 16 /* pre_div (N): 0=/1 1=/2 2=/4 3=/8 */) |
+	       (0 << 0) /* M-1 */,
+	       ccu + CCU_APB2_CFG);
+	set_cntfrq(24 * 1000 * 1000);
+
+	/* PIO clock enable */
+	setbits_le32(ccu + CCU_BUS_CLK_GATE2, 1u << 5);
+	/* UART0 clock enable */
+	setbits_le32(ccu + CCU_BUS_CLK_GATE3, 1u << 16);
+	/* UART0 release reset */
+	setbits_le32(ccu + CCU_BUS_SOFT_RST4, 1u << 16);
+}
+
+static void sunxi_cpu_lowlevel_init(void)
+{
+	sunxi_ccu_init(IOMEM(SUNXI_CCU_BASE_ADDR));
+}
diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/images/Makefile b/images/Makefile
index aa5814710f..3a10fe1abb 100644
--- a/images/Makefile
+++ b/images/Makefile
@@ -151,6 +151,7 @@ include $(srctree)/images/Makefile.omap3
 include $(srctree)/images/Makefile.rockchip
 include $(srctree)/images/Makefile.socfpga
 include $(srctree)/images/Makefile.stm32mp
+include $(srctree)/images/Makefile.sunxi
 include $(srctree)/images/Makefile.tegra
 include $(srctree)/images/Makefile.vexpress
 include $(srctree)/images/Makefile.xburst
diff --git a/images/Makefile.sunxi b/images/Makefile.sunxi
new file mode 100644
index 0000000000..778d6f9bdf
--- /dev/null
+++ b/images/Makefile.sunxi
@@ -0,0 +1,13 @@
+#
+# barebox image generation Makefile for Allwinner sunxi eGON boot images
+#
+
+# %.egonimg - convert into eGON.BT0 image
+# ----------------------------------------------------------------------
+quiet_cmd_egon_image = EGON  $@
+      cmd_egon_image = $(objtree)/scripts/egon_mkimage $< $@
+
+$(obj)/%.egonimg: $(obj)/% FORCE
+	$(call if_changed,egon_image)
+
+# ----------------------------------------------------------------------
diff --git a/include/mach/sunxi/init.h b/include/mach/sunxi/init.h
new file mode 100644
index 0000000000..26cc022fde
--- /dev/null
+++ b/include/mach/sunxi/init.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __MACH_INIT_H
+#define __MACH_INIT_H
+
+#endif
diff --git a/include/mach/sunxi/sun50i-regs.h b/include/mach/sunxi/sun50i-regs.h
new file mode 100644
index 0000000000..68501fa351
--- /dev/null
+++ b/include/mach/sunxi/sun50i-regs.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __MACH_SUN50I_REGS_H
+#define __MACH_SUN50I_REGS_H
+
+#define SUN50I_SRAM_A1_BASE_ADDR 0x00000000
+#define SUN50I_SRAM_A1_SIZE SZ_64K
+#define SUN50I_SRAM_A2_BASE_ADDR 0x00044000
+#define SUN50I_SRAM_A2_SIZE SZ_32K
+
+#define SUN50I_DRAM_BASE_ADDR	0x40000000
+
+#define SUN50I_CCU_BASE_ADDR	0x01c20000
+#define SUN50I_PIO_BASE_ADDR	0x01c20800
+#define SUN50I_MMC0_BASE_ADDR	0x01c0f000
+#define SUN50I_MMC1_BASE_ADDR	0x01c10000
+#define SUN50I_MMC2_BASE_ADDR	0x01c11000
+#define SUN50I_TIMER_BASE_ADDR	0x01c20c00
+
+#define CCU_PLL_CPUX		0x00
+#define CCU_PLL_PERIPH0		0x28
+#define CCU_CPUX_AXI_CFG	0x50
+#define CCU_AHB1_APB1_CFG	0x54
+#define CCU_APB2_CFG		0x58
+#define CCU_AHB2_CFG		0x5c
+#define CCU_BUS_CLK_GATE0	0x60
+#define CCU_BUS_CLK_GATE1	0x64
+#define CCU_BUS_CLK_GATE2	0x68
+#define CCU_BUS_CLK_GATE3	0x6c
+#define CCU_CE_CLK		0x9c
+#define CCU_MBUS_CLK		0x15c
+#define CCU_BUS_SOFT_RST0	0x2c0
+#define CCU_BUS_SOFT_RST4	0x2d8
+#define CCU_PLL_LOCK_CTRL	0x320
+
+#endif
diff --git a/include/mach/sunxi/sunxi-pinctrl.h b/include/mach/sunxi/sunxi-pinctrl.h
new file mode 100644
index 0000000000..adb2a24577
--- /dev/null
+++ b/include/mach/sunxi/sunxi-pinctrl.h
@@ -0,0 +1,13 @@
+/* pio aka "allwinner,sun8i-h3-pinctrl" */
+
+#define PIO_PA_CFG0 0x00
+#define PIO_PB_CFG0 0x24
+#define PIO_PB_CFG1 0x28
+#define PIO_PD_CFG0 0x6c
+#define PIO_PD_CFG1 0x70
+#define PIO_PD_CFG2 0x74
+#define PIO_PF_CFG0 0xb4
+
+#define PIO_PA_PULL1 0x20
+#define PIO_PB_PULL0 0x40
+#define PIO_PD_DATA 0x7c
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 03/11] ARM: dls: Add ENTRY_HEADER macro to add .text section
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
  2023-05-10 23:37 ` [RFC PATCH 01/11] scripts: Add Allwinner eGON image support Jules Maselbas
  2023-05-10 23:37 ` [RFC PATCH 02/11] sunxi: introduce mach-sunxi Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-18 18:49   ` Ahmad Fatoum
  2023-05-10 23:37 ` [RFC PATCH 04/11] sunxi: Add lowlevel switch to aarch64 Jules Maselbas
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

On sunxi platforms the boot rom (BROM) looks for a specific header which
will also be loaded in memory, causing pbl, or barebox, image not loaded
at the expected BASE addresse. This also cause an issue with relocatable
pbl: instruction used for relocation expect the image to be aligned on a
4K page boundary.

The proposed solution here is to allow to specify custom sections to put
in the very begging of the .text section.
---
 arch/arm/lib/pbl.lds.S            | 1 +
 include/asm-generic/barebox.lds.h | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/arch/arm/lib/pbl.lds.S b/arch/arm/lib/pbl.lds.S
index 114ec7bc81..6fc97c6c34 100644
--- a/arch/arm/lib/pbl.lds.S
+++ b/arch/arm/lib/pbl.lds.S
@@ -27,6 +27,7 @@ SECTIONS
 	.text      :
 	{
 		_stext = .;
+		ENTRY_HEADER
 		*(.text_head_prologue*)
 		*(.text_head_entry*)
 		__bare_init_start = .;
diff --git a/include/asm-generic/barebox.lds.h b/include/asm-generic/barebox.lds.h
index d3736ebaed..9915ead8a7 100644
--- a/include/asm-generic/barebox.lds.h
+++ b/include/asm-generic/barebox.lds.h
@@ -12,6 +12,10 @@
 #define PRE_IMAGE
 #endif
 
+#ifndef ENTRY_HEADER
+#define ENTRY_HEADER
+#endif
+
 #define BAREBOX_INITCALLS			\
 	STRUCT_ALIGN();				\
 	__barebox_initcalls_start = .;		\
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 04/11] sunxi: Add lowlevel switch to aarch64
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (2 preceding siblings ...)
  2023-05-10 23:37 ` [RFC PATCH 03/11] ARM: dls: Add ENTRY_HEADER macro to add .text section Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-18 19:01   ` Ahmad Fatoum
  2023-05-10 23:37 ` [RFC PATCH 05/11] arm: sunxi: Add debug_ll Jules Maselbas
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Allwinner A64 SoC (and probably others) boots in 32-bits mode. Switching
to aarch64 is achieved by writing to the Reset Management Register (RMR)
which can be accessed through the memory space thanks to an alias.

On A64 this alias is located at 0x017000a0
---
 arch/arm/include/asm/barebox.lds.h |  2 ++
 arch/arm/mach-sunxi/Kconfig        |  5 ++++
 arch/arm/mach-sunxi/Makefile       |  2 ++
 arch/arm/mach-sunxi/rmr_switch.S   | 47 ++++++++++++++++++++++++++++++
 include/mach/sunxi/barebox.lds.h   |  6 ++++
 include/mach/sunxi/rmr_switch.h    | 10 +++++++
 6 files changed, 72 insertions(+)
 create mode 100644 arch/arm/mach-sunxi/rmr_switch.S
 create mode 100644 include/mach/sunxi/barebox.lds.h
 create mode 100644 include/mach/sunxi/rmr_switch.h

diff --git a/arch/arm/include/asm/barebox.lds.h b/arch/arm/include/asm/barebox.lds.h
index a5c74381d8..f94290128e 100644
--- a/arch/arm/include/asm/barebox.lds.h
+++ b/arch/arm/include/asm/barebox.lds.h
@@ -2,6 +2,8 @@
 
 #if defined CONFIG_ARCH_EP93XX
 #include <mach/ep93xx/barebox.lds.h>
+#elif defined CONFIG_ARCH_SUNXI
+#include <mach/sunxi/barebox.lds.h>
 #endif
 
 #ifdef CONFIG_CPU_32
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 0e8d83fedd..e28f04c354 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -4,6 +4,11 @@ config ARCH_TEXT_BASE
 	hex
 	default 0x0
 
+config SUNXI_RVBAR_IOMAP
+	hex
+	default 0x017000a0  if ARCH_SUN50I_A64
+	# default 0x09010040 if ARCH_SUN50I_H5
+
 menuconfig SUNXI_MULTI_BOARDS
 	bool "Allwinner boards"
 	select HAVE_PBL_MULTI_IMAGES
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index d678973ca2..e7fa23c832 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -1,2 +1,4 @@
 obj-y += sunxi.o
 lwl-y += cpu_init.o
+
+pbl-$(CONFIG_CPU_64) += rmr_switch.o
diff --git a/arch/arm/mach-sunxi/rmr_switch.S b/arch/arm/mach-sunxi/rmr_switch.S
new file mode 100644
index 0000000000..bfe3b75e3a
--- /dev/null
+++ b/arch/arm/mach-sunxi/rmr_switch.S
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ARMv8 RMR reset sequence on Allwinner SoCs.
+ *
+ * All 64-bit capable Allwinner SoCs reset in AArch32 (and continue to
+ * exectute the Boot ROM in this state), so we need to switch to AArch64
+ * at some point.
+ * Section G6.2.133 of the ARMv8 ARM describes the Reset Management Register
+ * (RMR), which triggers a warm-reset of a core and can request to switch
+ * into a different execution state (AArch32 or AArch64).
+ * The address at which execution starts after the reset is held in the
+ * RVBAR system register, which is architecturally read-only.
+ * Allwinner provides a writable alias of this register in MMIO space, so
+ * we can easily set the start address of AArch64 code.
+ * This code below switches to AArch64 and starts execution at the specified
+ * start address.
+ *
+ * This file has been adapted from U-Boot code sources:
+ *  - arch/arm/mach-sunxi/rmr_switch.S
+ *  - arch/arm/include/asm/arch-sunxi/boot0.h.
+ *
+ * The aarch32 assembly has already been assembled and are inserted verbatime
+ * as .word statements (the asm source is commented for each statement).
+ *
+ */
+
+#include <config.h>
+#include <linux/linkage.h>
+
+.section .text_head_rmr_switch, "x"
+ENTRY(sunxi_rmr_switch)		/* arm32		arm64		*/
+	.word 0xeb000000	/* bl	.+8		subs x0, x0, x0	*/
+	b end			/* .word 0x1400000c 	b end		*/
+	.word 0xe59f0020	/* ldr	r0, [pc, #32] ; rvbar		*/
+	.word 0xe580e000	/* str  lr, [r0]			*/
+	.word 0xf57ff04f	/* dsb	sy				*/
+	.word 0xf57ff06f	/* isb	syo				*/
+	.word 0xee1c0f50	/* mrc	15, 0, r0, cr12, cr0, {2}	*/
+	.word 0xe3800003	/* orr	r0, r0, #3			*/
+	.word 0xee0c0f50	/* mcr	15, 0, r0, cr12, cr0, {2}	*/
+	.word 0xf57ff06f	/* isb	sy				*/
+	.word 0xe320f003	/* 1b: wfi				*/
+	.word 0xeafffffd	/* b	1b				*/
+	.word CONFIG_SUNXI_RVBAR_IOMAP
+	.align 3 /* prevent linker script from adding padding for aligment */
+end:	/* fall-through */
+ENDPROC(sunxi_rmr_switch)
diff --git a/include/mach/sunxi/barebox.lds.h b/include/mach/sunxi/barebox.lds.h
new file mode 100644
index 0000000000..e04c713611
--- /dev/null
+++ b/include/mach/sunxi/barebox.lds.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* theses two sections should only be needed for the pbl */
+#define ENTRY_HEADER \
+	*(.text_head_egon_header*)		\
+	*(.text_head_rmr_switch*)
diff --git a/include/mach/sunxi/rmr_switch.h b/include/mach/sunxi/rmr_switch.h
new file mode 100644
index 0000000000..2ecbd81d57
--- /dev/null
+++ b/include/mach/sunxi/rmr_switch.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __MACH_RMR_SWITCH_H
+#define __MACH_RMR_SWITCH_H
+
+/* 64-bits Allwinner SoCs reset in AArch32 and need to switch to AArch64 */
+extern const u32 sunxi_rmr_switch[];
+#define	sunxi_switch_to_aarch64() __keep_symbolref(sunxi_rmr_switch)
+
+#endif
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 05/11] arm: sunxi: Add debug_ll
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (3 preceding siblings ...)
  2023-05-10 23:37 ` [RFC PATCH 04/11] sunxi: Add lowlevel switch to aarch64 Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-18 19:05   ` Ahmad Fatoum
  2023-05-10 23:37 ` [RFC PATCH 06/11] clk: Add clock driver for sun50i-a64 Jules Maselbas
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

---
 arch/arm/include/asm/debug_ll.h |  2 ++
 arch/arm/mach-sunxi/Kconfig     |  4 ++++
 include/mach/sunxi/debug_ll.h   | 35 +++++++++++++++++++++++++++++++++
 3 files changed, 41 insertions(+)
 create mode 100644 include/mach/sunxi/debug_ll.h

diff --git a/arch/arm/include/asm/debug_ll.h b/arch/arm/include/asm/debug_ll.h
index a1d5161ccf..054d021bab 100644
--- a/arch/arm/include/asm/debug_ll.h
+++ b/arch/arm/include/asm/debug_ll.h
@@ -50,6 +50,8 @@
 #include <mach/uemd/debug_ll.h>
 #elif defined CONFIG_ARCH_SOCFPGA
 #include <mach/socfpga/debug_ll.h>
+#elif defined CONFIG_ARCH_SUNXI
+#include <mach/sunxi/debug_ll.h>
 #elif defined CONFIG_ARCH_PXA
 #include <mach/pxa/debug_ll.h>
 #elif defined CONFIG_ARCH_NOMADIK
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index e28f04c354..2580a9e56a 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -9,6 +9,10 @@ config SUNXI_RVBAR_IOMAP
 	default 0x017000a0  if ARCH_SUN50I_A64
 	# default 0x09010040 if ARCH_SUN50I_H5
 
+config SUNXI_DEBUG_LL_UART_BASE
+	hex
+	default 0x01c28000
+
 menuconfig SUNXI_MULTI_BOARDS
 	bool "Allwinner boards"
 	select HAVE_PBL_MULTI_IMAGES
diff --git a/include/mach/sunxi/debug_ll.h b/include/mach/sunxi/debug_ll.h
new file mode 100644
index 0000000000..64c65a5917
--- /dev/null
+++ b/include/mach/sunxi/debug_ll.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __MACH_DEBUG_LL_H__
+#define __MACH_DEBUG_LL_H__
+
+#include <io.h>
+
+#define DEBUG_LL_UART_ADDR CONFIG_SUNXI_DEBUG_LL_UART_BASE
+
+static inline uint8_t debug_ll_read_reg(int reg)
+{
+	return readl(IOMEM(DEBUG_LL_UART_ADDR) + reg * sizeof(uint32_t));
+}
+
+static inline void debug_ll_write_reg(int reg, uint8_t val)
+{
+	writel(val, IOMEM(DEBUG_LL_UART_ADDR) + reg * sizeof(uint32_t));
+}
+
+#include <debug_ll/ns16550.h>
+
+static inline void debug_ll_init(void)
+{
+	uint16_t divisor;
+
+	divisor = debug_ll_ns16550_calc_divisor(24 * 1000 * 1000);
+	debug_ll_ns16550_init(divisor);
+}
+
+static inline void PUTC_LL(int c)
+{
+       debug_ll_ns16550_putc(c);
+}
+
+#endif
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 06/11] clk: Add clock driver for sun50i-a64
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (4 preceding siblings ...)
  2023-05-10 23:37 ` [RFC PATCH 05/11] arm: sunxi: Add debug_ll Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-18 19:06   ` Ahmad Fatoum
  2023-05-10 23:37 ` [RFC PATCH 07/11] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Clock driver is adapted from Linux
---
 drivers/clk/Makefile               |   1 +
 drivers/clk/sunxi/Makefile         |   2 +
 drivers/clk/sunxi/clk-sun50i-a64.c | 317 +++++++++++++++++++++++++++++
 drivers/clk/sunxi/clk-sun50i-a64.h |  62 ++++++
 4 files changed, 382 insertions(+)
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.c
 create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.h

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index baf452de98..25be2bdc08 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_COMMON_CLK_STM32F)		+= clk-stm32f4.o
 obj-$(CONFIG_MACH_RPI_COMMON)	+= clk-rpi.o
 obj-y				+= bcm/
 obj-$(CONFIG_COMMON_CLK_SCMI)	+= clk-scmi.o
+obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
new file mode 100644
index 0000000000..4d1dcbebb0
--- /dev/null
+++ b/drivers/clk/sunxi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ARCH_SUN50I_A64) += clk-sun50i-a64.o
diff --git a/drivers/clk/sunxi/clk-sun50i-a64.c b/drivers/clk/sunxi/clk-sun50i-a64.c
new file mode 100644
index 0000000000..0132641495
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sun50i-a64.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2022 Jules Maselbas
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <linux/clk.h>
+#include <io.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+
+#define MHZ (1000 * 1000UL)
+
+#include "clk-sun50i-a64.h"
+
+#define CCU_PLL_CPUX		0x00
+#define CCU_PLL_PERIPH0		0x28
+#define CCU_CPUX_AXI_CFG	0x50
+#define CCU_AHB1_APB1_CFG	0x54
+#define CCU_APB2_CFG		0x58
+#define CCU_AHB2_CFG		0x5c
+#define CCU_BUS_CLK_GATE0	0x60
+#define CCU_BUS_CLK_GATE1	0x64
+#define CCU_BUS_CLK_GATE2	0x68
+#define CCU_BUS_CLK_GATE3	0x6c
+#define CCU_CE_CLK		0x9c
+#define CCU_MBUS_CLK		0x15c
+#define CCU_BUS_SOFT_RST0	0x2c0
+#define CCU_BUS_SOFT_RST4	0x2d8
+#define CCU_PLL_LOCK_CTRL	0x320
+
+static struct clk *clks[CLK_NUMBER];
+static struct clk_onecell_data clk_data = {
+	.clks = clks,
+	.clk_num = ARRAY_SIZE(clks),
+};
+
+static inline struct clk *
+sunxi_clk_gate(const char *name, const char *parent, void __iomem *reg, u8 shift)
+{
+	return clk_gate(name, parent, reg, shift, 0, 0);
+}
+
+static inline struct clk *
+sunxi_clk_mux(const char *name, const char * const *parents, u8 num_parents,
+	      void __iomem *reg, u8 shift, u8 width)
+{
+	return clk_mux(name, 0, reg, shift, width, parents, num_parents, 0);
+}
+
+static inline struct clk *
+sunxi_clk_div(const char *name, const char *parent, struct clk_div_table *table,
+	      void __iomem *reg, u8 shift, u8 width)
+{
+	return clk_divider_table(name, parent, CLK_SET_RATE_PARENT, reg, shift,
+				 width, table, 0);
+}
+
+static struct clk_div_table div_apb1[] = {
+	{ .val = 0, .div = 2 },
+	{ .val = 1, .div = 2 },
+	{ .val = 2, .div = 4 },
+	{ .val = 3, .div = 8 },
+	{ /* Sentinel */ },
+};
+
+static struct clk_div_table div_N[] = {
+	{ .val = 0, .div = 1 },
+	{ .val = 1, .div = 2 },
+	{ .val = 2, .div = 4 },
+	{ .val = 3, .div = 8 },
+	{ /* Sentinel */ },
+};
+
+static const char *sel_cpux[] = {
+	"osc32k",
+	"osc24M",
+	"pll-cpux",
+};
+
+static const char *sel_ahb1[] = {
+	"osc32k",
+	"osc24M",
+	"axi",
+	"pll-periph0",
+};
+
+static const char *sel_apb2[] = {
+	"osc32k",
+	"osc24M",
+	"pll-periph0-2x",
+	"pll-periph0-2x",
+};
+
+static const char *sel_ahb2[] = {
+	"ahb1",
+	"pll-periph0",
+};
+
+static const char *sel_mmc[] = {
+	"osc24M",
+	"pll-periph0-2x",
+	"pll-periph1-2x",
+};
+
+static void sun50i_a64_resets_init(void __iomem *regs)
+{
+	u32 rst;
+
+	rst = 0 |
+		/* RST_USB_PHY0 */ BIT(0) |
+		/* RST_USB_PHY1 */ BIT(1) |
+		/* RST_USB_HSIC */ BIT(2);
+	writel(rst, regs + 0x0cc);
+
+	rst = 0 |
+		/* RST_BUS_MIPI_DSI */ BIT(1) |
+		/* RST_BUS_CE   */ BIT(5) |
+		/* RST_BUS_DMA  */ BIT(6) |
+		/* RST_BUS_MMC0 */ BIT(8) |
+		/* RST_BUS_MMC1 */ BIT(9) |
+		/* RST_BUS_MMC2 */ BIT(10) |
+		/* RST_BUS_NAND */ BIT(13) |
+		/* RST_BUS_DRAM */ BIT(14) |
+		/* RST_BUS_EMAC */ BIT(17) |
+		/* RST_BUS_TS   */ BIT(18) |
+		/* RST_BUS_HSTIMER */ BIT(19) |
+		/* RST_BUS_SPI0  */ BIT(20) |
+		/* RST_BUS_SPI1  */ BIT(21) |
+		/* RST_BUS_OTG   */ BIT(23) |
+		/* RST_BUS_EHCI0 */ BIT(24) |
+		/* RST_BUS_EHCI1 */ BIT(25) |
+		/* RST_BUS_OHCI0 */ BIT(28) |
+		/* RST_BUS_OHCI1 */ BIT(29);
+	writel(rst, regs + 0x2c0);
+
+	rst = 0 |
+		/* RST_BUS_VE    */ BIT(0) |
+		/* RST_BUS_TCON0 */ BIT(3) |
+		/* RST_BUS_TCON1 */ BIT(4) |
+		/* RST_BUS_DEINTERLACE */ BIT(5) |
+		/* RST_BUS_CSI   */ BIT(8) |
+		/* RST_BUS_HDMI0 */ BIT(10) |
+		/* RST_BUS_HDMI1 */ BIT(11) |
+		/* RST_BUS_DE    */ BIT(12) |
+		/* RST_BUS_GPU   */ BIT(20) |
+		/* RST_BUS_MSGBOX   */ BIT(21) |
+		/* RST_BUS_SPINLOCK */ BIT(22) |
+		/* RST_BUS_DBG */ BIT(31);
+	writel(rst, regs + 0x2c4);
+
+	rst = /* RST_BUS_LVDS */ BIT(0);
+	writel(rst, regs + 0x2c8);
+
+	rst = 0 |
+		/* RST_BUS_CODEC */ BIT(0) |
+		/* RST_BUS_SPDIF */ BIT(1) |
+		/* RST_BUS_THS   */ BIT(8) |
+		/* RST_BUS_I2S0  */ BIT(12) |
+		/* RST_BUS_I2S1  */ BIT(13) |
+		/* RST_BUS_I2S2  */ BIT(14);
+	writel(rst, regs + 0x2d0);
+
+	rst = 0 |
+		/* RST_BUS_I2C0 */ BIT(0) |
+		/* RST_BUS_I2C1 */ BIT(1) |
+		/* RST_BUS_I2C2 */ BIT(2) |
+		/* RST_BUS_SCR  */ BIT(5) |
+		/* RST_BUS_UART0 */ BIT(16) |
+		/* RST_BUS_UART1 */ BIT(17) |
+		/* RST_BUS_UART2 */ BIT(18) |
+		/* RST_BUS_UART3 */ BIT(19) |
+		/* RST_BUS_UART4 */ BIT(20);
+	writel(rst, regs + 0x2d8);
+}
+
+static int
+sunxi_clk_set_pll(void __iomem *reg, u32 src, u32 freq)
+{
+	/* NOTE: using u32, max freq is 4GHz
+	 * out freq: src * N * K
+	 * factor N: [1->32]
+	 * factor K: [1->4]
+	 * from the manual: give priority to the choice of K >= 2
+	 */
+	u32 mul = freq / src; /* target multiplier (N * K) */
+	u32 k, n;
+	u32 cfg = BIT(31); /* ENABLE */
+
+	for (k = 4; k > 1; k--) {
+		if ((mul % k) == 0)
+			break;
+	}
+	n = mul / k;
+
+	cfg |= (k - 1) << 4;
+	cfg |= (n - 1) << 8;
+
+	writel(cfg, reg);
+	return wait_on_timeout(1 * MSECOND, readl(reg) & BIT(28));
+}
+
+static void sun50i_a64_clocks_init(struct device_node *np, void __iomem *regs)
+{
+	sun50i_a64_resets_init(regs);
+
+	/* set pll-cpu to 1.2GHz */
+	if (sunxi_clk_set_pll(regs + CCU_PLL_CPUX, 24 * MHZ, 1200 * MHZ))
+		pr_err("fail to lock pll-cpu @ 1.2GHz\n");
+	clks[CLK_PLL_CPUX] = clk_fixed("pll-cpux", 1200 * MHZ);
+
+	/* switch cpu clock source: cpux_src: 1=24mhz 2=PLL_CPUX */
+	clks[CLK_CPUX] = sunxi_clk_mux("cpux", sel_cpux, ARRAY_SIZE(sel_cpux), regs + CCU_CPUX_AXI_CFG, 16, 2);
+	writel(0x20001, regs + CCU_CPUX_AXI_CFG); /* select pll-cpu */
+	udelay(1); /* wait 8 cycles */
+
+	/* set pll-periph0-2x to 1.2GHz, as recommended */
+	if (sunxi_clk_set_pll(regs + CCU_PLL_PERIPH0, 24 * MHZ, 2 * 600 * MHZ))
+		pr_err("fail to lock pll-periph @ 1.2GHz\n");
+
+	clks[CLK_PLL_PERIPH0]    = clk_fixed("pll-periph0", 600 * MHZ);
+	clks[CLK_PLL_PERIPH0_2X] = clk_fixed_factor("pll-periph0-2x", "pll-periph0", 2, 1, 0);
+
+	clks[CLK_AHB1] = sunxi_clk_mux("ahb1", sel_ahb1, ARRAY_SIZE(sel_ahb1), regs + 0x054, 12, 2);
+	clks[CLK_AHB2] = sunxi_clk_mux("ahb2", sel_ahb2, ARRAY_SIZE(sel_ahb2), regs + 0x05c, 0, 1);
+
+	clks[CLK_APB1] = sunxi_clk_div("apb1", "ahb1", div_apb1, regs + 0x054, 8, 2);
+	clks[CLK_APB2] = sunxi_clk_mux("apb2", sel_apb2, ARRAY_SIZE(sel_apb2), regs + 0x058, 24, 2);
+
+	clks[CLK_BUS_MIPI_DSI] = sunxi_clk_gate("bus-mipi-dsi","ahb1",regs + 0x060, 1);
+	clks[CLK_BUS_CE]    = sunxi_clk_gate("bus-ce",    "ahb1", regs + 0x060, 5);
+	clks[CLK_BUS_DMA]   = sunxi_clk_gate("bus-dma",   "ahb1", regs + 0x060, 6);
+	clks[CLK_BUS_MMC0]  = sunxi_clk_gate("bus-mmc0",  "ahb1", regs + 0x060, 8);
+	clks[CLK_BUS_MMC1]  = sunxi_clk_gate("bus-mmc1",  "ahb1", regs + 0x060, 9);
+	clks[CLK_BUS_MMC2]  = sunxi_clk_gate("bus-mmc2",  "ahb1", regs + 0x060, 10);
+	clks[CLK_BUS_NAND]  = sunxi_clk_gate("bus-nand",  "ahb1", regs + 0x060, 13);
+	clks[CLK_BUS_DRAM]  = sunxi_clk_gate("bus-dram",  "ahb1", regs + 0x060, 14);
+	clks[CLK_BUS_EMAC]  = sunxi_clk_gate("bus-emac",  "ahb2", regs + 0x060, 17);
+	clks[CLK_BUS_TS]    = sunxi_clk_gate("bus-ts",    "ahb1", regs + 0x060, 18);
+	clks[CLK_BUS_HSTIMER] = sunxi_clk_gate("bus-hstimer", "ahb1", regs + 0x060, 19);
+	clks[CLK_BUS_SPI0]  = sunxi_clk_gate("bus-spi0",  "ahb1", regs + 0x060,  20);
+	clks[CLK_BUS_SPI1]  = sunxi_clk_gate("bus-spi1",  "ahb1", regs + 0x060,  21);
+	clks[CLK_BUS_OTG]   = sunxi_clk_gate("bus-otg",   "ahb1", regs + 0x060,  23);
+	clks[CLK_BUS_EHCI0] = sunxi_clk_gate("bus-ehci0", "ahb1", regs + 0x060, 24);
+	clks[CLK_BUS_EHCI1] = sunxi_clk_gate("bus-ehci1", "ahb2", regs + 0x060, 25);
+	clks[CLK_BUS_OHCI0] = sunxi_clk_gate("bus-ohci0", "ahb1", regs + 0x060, 28);
+	clks[CLK_BUS_OHCI1] = sunxi_clk_gate("bus-ohci1", "ahb2", regs + 0x060, 29);
+
+	clks[CLK_BUS_CODEC] = sunxi_clk_gate("bus-codec", "apb1", regs + 0x068, 0);
+	clks[CLK_BUS_SPDIF] = sunxi_clk_gate("bus-spdif", "apb1", regs + 0x068, 1);
+	clks[CLK_BUS_PIO]   = sunxi_clk_gate("bus-pio",   "apb1", regs + 0x068, 5);
+	clks[CLK_BUS_THS]   = sunxi_clk_gate("bus-ths",   "apb1", regs + 0x068, 8);
+	clks[CLK_BUS_I2S0]  = sunxi_clk_gate("bus-i2s0",  "apb1", regs + 0x068, 12);
+	clks[CLK_BUS_I2S1]  = sunxi_clk_gate("bus-i2s1",  "apb1", regs + 0x068, 13);
+	clks[CLK_BUS_I2S2]  = sunxi_clk_gate("bus-i2s2",  "apb1", regs + 0x068, 14);
+
+	clks[CLK_BUS_UART0] = sunxi_clk_gate("bus-uart0", "apb2", regs + 0x06c, 16);
+	clks[CLK_BUS_UART1] = sunxi_clk_gate("bus-uart1", "apb2", regs + 0x06c, 17);
+	clks[CLK_BUS_UART2] = sunxi_clk_gate("bus-uart2", "apb2", regs + 0x06c, 18);
+	clks[CLK_BUS_UART3] = sunxi_clk_gate("bus-uart3", "apb2", regs + 0x06c, 19);
+	clks[CLK_BUS_UART4] = sunxi_clk_gate("bus-uart4", "apb2", regs + 0x06c, 20);
+
+	clks[CLK_MMC0] = clk_register_composite(
+		"mmc0",	sel_mmc, ARRAY_SIZE(sel_mmc),
+		sunxi_clk_mux("mmc0-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x088, 24, 2),
+		sunxi_clk_div("mmc0-div-n", "mmc0-gate", div_N, regs + 0x088, 16, 2),
+		sunxi_clk_gate("mmc0-gate", "mmc0-mux", regs + 0x088, 31),
+		0);
+
+	clks[CLK_MMC1] = clk_register_composite(
+		"mmc1",	sel_mmc, ARRAY_SIZE(sel_mmc),
+		sunxi_clk_mux("mmc1-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x08c, 24, 2),
+		sunxi_clk_div("mmc1-div-n", "mmc1-gate", div_N, regs + 0x08c, 16, 2),
+		sunxi_clk_gate("mmc1-gate", "mmc1-mux", regs + 0x08c, 31),
+		0);
+
+	clks[CLK_MMC2] = clk_register_composite(
+		"mmc2",	sel_mmc, ARRAY_SIZE(sel_mmc),
+		sunxi_clk_mux("mmc2-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x090, 24, 2),
+		sunxi_clk_div("mmc2-div-n", "mmc2-gate", div_N, regs + 0x090, 16, 2),
+		sunxi_clk_gate("mmc2-gate", "mmc2-mux", regs + 0x090, 31),
+		0);
+
+	/* generic set_rate doesn't support switching parent,
+	 * let's do it here for now */
+	clk_set_parent(clks[CLK_MMC0], clks[CLK_PLL_PERIPH0_2X]);
+	clk_set_parent(clks[CLK_MMC2], clks[CLK_PLL_PERIPH0_2X]);
+}
+
+static int sun50i_a64_ccu_probe(struct device *dev)
+{
+	struct resource *iores;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+
+	sun50i_a64_clocks_init(dev->of_node, IOMEM(iores->start));
+	of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
+
+	return 0;
+}
+
+static __maybe_unused struct of_device_id sun50i_a64_ccu_dt_ids[] = {
+	{
+		.compatible = "allwinner,sun50i-a64-ccu",
+	}, {
+		/* sentinel */
+	}
+};
+
+static struct driver sun50i_a64_ccu_driver = {
+	.probe	= sun50i_a64_ccu_probe,
+	.name	= "sun50i-a64-ccu",
+	.of_compatible = DRV_OF_COMPAT(sun50i_a64_ccu_dt_ids),
+};
+postcore_platform_driver(sun50i_a64_ccu_driver);
diff --git a/drivers/clk/sunxi/clk-sun50i-a64.h b/drivers/clk/sunxi/clk-sun50i-a64.h
new file mode 100644
index 0000000000..a4ddc39eb8
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sun50i-a64.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ */
+
+#include <dt-bindings/clock/sun50i-a64-ccu.h>
+
+#ifndef _CLK_SUN50I_A64_H_
+#define _CLK_SUN50I_A64_H_
+
+#include <dt-bindings/clock/sun50i-a64-ccu.h>
+#include <dt-bindings/reset/sun50i-a64-ccu.h>
+
+#define CLK_OSC_12M			0
+#define CLK_PLL_CPUX			1
+#define CLK_PLL_AUDIO_BASE		2
+#define CLK_PLL_AUDIO			3
+#define CLK_PLL_AUDIO_2X		4
+#define CLK_PLL_AUDIO_4X		5
+#define CLK_PLL_AUDIO_8X		6
+
+/* PLL_VIDEO0 exported for HDMI PHY */
+
+#define CLK_PLL_VIDEO0_2X		8
+#define CLK_PLL_VE			9
+#define CLK_PLL_DDR0			10
+
+/* PLL_PERIPH0 exported for PRCM */
+
+#define CLK_PLL_PERIPH0_2X		12
+#define CLK_PLL_PERIPH1			13
+#define CLK_PLL_PERIPH1_2X		14
+#define CLK_PLL_VIDEO1			15
+#define CLK_PLL_GPU			16
+#define CLK_PLL_MIPI			17
+#define CLK_PLL_HSIC			18
+#define CLK_PLL_DE			19
+#define CLK_PLL_DDR1			20
+#define CLK_AXI				22
+#define CLK_APB				23
+#define CLK_AHB1			24
+#define CLK_APB1			25
+#define CLK_APB2			26
+#define CLK_AHB2			27
+
+/* All the bus gates are exported */
+
+/* The first bunch of module clocks are exported */
+
+#define CLK_USB_OHCI0_12M		90
+
+#define CLK_USB_OHCI1_12M		92
+
+/* All the DRAM gates are exported */
+
+/* And the DSI and GPU module clock is exported */
+
+#define CLK_NUMBER			(CLK_GPU + 1)
+
+#endif /* _CLK_SUN50I_A64_H_ */
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 07/11] pinctrl: Add sun50i-a64 pinctrl driver
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (5 preceding siblings ...)
  2023-05-10 23:37 ` [RFC PATCH 06/11] clk: Add clock driver for sun50i-a64 Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-18 19:10   ` Ahmad Fatoum
  2023-05-10 23:37 ` [RFC PATCH 08/11] mci: Add sunxi-mmc driver Jules Maselbas
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

sunxi pinctrl driver, adapted from Linux, is split in two parts:
 - pinctrl-sunxi.c that implement sunxi-generic gpio and function mux
 - pinctrl-sun50i-a64.c that only declare sun50i's pin functions
---
 drivers/pinctrl/Kconfig                    |   2 +
 drivers/pinctrl/Makefile                   |   1 +
 drivers/pinctrl/sunxi/Kconfig              |  15 +
 drivers/pinctrl/sunxi/Makefile             |   3 +
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 593 +++++++++++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.c      | 371 +++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h      | 224 ++++++++
 7 files changed, 1209 insertions(+)
 create mode 100644 drivers/pinctrl/sunxi/Kconfig
 create mode 100644 drivers/pinctrl/sunxi/Makefile
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 2ff99a39c8..0b3d79d1cc 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -105,6 +105,8 @@ config PINCTRL_STM32
 	default y if ARCH_STM32
 	help
 	  Pinmux and GPIO controller found on STM32 family
+
+source "drivers/pinctrl/sunxi/Kconfig"
 endif
 
 endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f1a5fa5715..3bc718d355 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o
 obj-$(CONFIG_PINCTRL_STM32) += pinctrl-stm32.o
 
 obj-$(CONFIG_ARCH_MVEBU) += mvebu/
+obj-$(CONFIG_ARCH_SUNXI) += sunxi/
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
new file mode 100644
index 0000000000..36168dc2bb
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+if ARCH_SUNXI
+
+config PINCTRL_SUNXI
+	bool
+	select PINMUX
+	select GENERIC_PINCONF
+	select GPIOLIB
+
+config PINCTRL_SUN50I_A64
+	bool "Support for the Allwinner A64 PIO"
+	default ARM64 && ARCH_SUNXI
+	select PINCTRL_SUNXI
+
+endif
diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
new file mode 100644
index 0000000000..db0ff5b50b
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ARCH_SUNXI)		+= pinctrl-sunxi.o
+obj-$(CONFIG_PINCTRL_SUN50I_A64)	+= pinctrl-sun50i-a64.o
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
new file mode 100644
index 0000000000..4a087802c8
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
@@ -0,0 +1,593 @@
+/*
+ * Allwinner A64 SoCs pinctrl driver.
+ *
+ * Copyright (C) 2016 - ARM Ltd.
+ * Author: Andre Przywara <andre.przywara@arm.com>
+ *
+ * Based on pinctrl-sun7i-a20.c, which is:
+ * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <common.h>
+#include <init.h>
+
+#include "pinctrl-sunxi.h"
+
+static const struct sunxi_desc_pin a64_pins[] = {
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* TX */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* MS0 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* RX */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* CK0 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* VCCEN */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),		/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* RTS */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* DO0 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPEN */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),		/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* CTS */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* MCLK */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* DI0 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPPP */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),		/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* SYNC */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* SYNC */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* CLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),		/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* BCLK */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* BCLK */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* DATA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),		/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* DOUT */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DOUT */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* RST */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),		/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* DIN */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DIN */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* DET */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),		/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x4, "uart0"),		/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),		/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x4, "uart0"),		/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),		/* EINT9 */
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NWE */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* MOSI */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NALE */
+		  SUNXI_FUNCTION(0x3, "mmc2"),		/* DS */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* MISO */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCLE */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* SCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCE1 */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* CS */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0")),	/* NCE0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRE# */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CLK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRB0 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CMD */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0")),	/* NRB1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ0 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ1 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ2 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ3 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ4 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ5 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ6 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ7 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQS */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* RST */
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D2 */
+		  SUNXI_FUNCTION(0x3, "uart3"),		/* TX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* CS */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* CLK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D3 */
+		  SUNXI_FUNCTION(0x3, "uart3"),		/* RX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* CLK */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* DE */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D4 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* TX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* MOSI */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* HSYNC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D5 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* RX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* MISO */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* VSYNC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D6 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* RTS */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D7 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* CTS */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D10 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D11 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D12 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD3 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D13 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD2 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D14 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D15 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D18 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP0 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D19 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN0 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCTL */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D20 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP1 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ENULL */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D21 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN1 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD3 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D22 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP2 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD2 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D23 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN2 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* CLK */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VPC */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* DE */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VNC */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* HSYNC */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP3 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCTL */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* VSYNC */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN3 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ECLKIN */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "pwm"),		/* PWM0 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDIO */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* PCK */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* CLK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* CK */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* ERR */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* HSYNC */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* SYNC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* VSYNC */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* DVLD */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D0 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D1 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D2 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D3 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D4 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D5 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D6 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi"),		/* D7 */
+		  SUNXI_FUNCTION(0x4, "ts")),		/* D7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi")),		/* SCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi")),		/* SDA */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "pll"),		/* LOCK_DBG */
+		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SDA */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D1 */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* MSI */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D0 */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* DI1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CLK */
+		  SUNXI_FUNCTION(0x3, "uart0")),	/* TX */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CMD */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* DO1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D3 */
+		  SUNXI_FUNCTION(0x3, "uart0")),	/* RX */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D2 */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* CK1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CMD */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)),	/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D0 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)),	/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D1 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)),	/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D2 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)),	/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D3 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)),	/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)),	/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)),	/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* RTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)),	/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* CTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)),	/* EINT9 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* SYNC */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* SYNC */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)),	/* EINT10 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* BCLK */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* BCLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)),	/* EINT11 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* DOUT */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DOUT */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)),	/* EINT12 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* DIN */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DIN */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)),	/* EINT13 */
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SCK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SDA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),	/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SCK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)),	/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SDA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)),	/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)),	/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)),	/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* RTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)),	/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* CTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)),	/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "spdif"),		/* OUT */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)),	/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)),	/* EINT9 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mic"),		/* CLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)),	/* EINT10 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mic"),		/* DATA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)),	/* EINT11 */
+};
+
+static const struct sunxi_pinctrl_desc a64_pinctrl_data = {
+	.pins = a64_pins,
+	.npins = ARRAY_SIZE(a64_pins),
+};
+
+static const struct of_device_id a64_pinctrl_dt_match[] = {
+	{
+		.compatible = "allwinner,sun50i-a64-pinctrl",
+		.data = &a64_pinctrl_data
+	}, {
+		/* sentinel */
+	}
+};
+
+static struct driver a64_pinctrl_driver = {
+	.name		= "sun50i-a64-pinctrl",
+	.probe		= sunxi_pinctrl_probe,
+	.of_compatible	= DRV_OF_COMPAT(a64_pinctrl_dt_match),
+};
+core_platform_driver(a64_pinctrl_driver);
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
new file mode 100644
index 0000000000..e2bde96c70
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "pinctrl-sunxi: " fmt
+
+#include <common.h>
+#include <io.h>
+#include <of.h>
+#include <of_address.h>
+#include <malloc.h>
+#include <linux/clk.h>
+
+#include "pinctrl-sunxi.h"
+
+/* This driver assumes the gpio function mux value will not change */
+#define FUNC_GPIO_IN	0
+#define FUNC_GPIO_OUT	1
+
+static struct sunxi_pinctrl *to_sunxi_pinctrl(struct pinctrl_device *pdev)
+{
+	return container_of(pdev, struct sunxi_pinctrl, pdev);
+}
+
+static void sunxi_pinctrl_set_pull(struct sunxi_pinctrl *pinctrl,
+				   u16 pin, u32 pull)
+{
+	u32 reg = sunxi_pull_reg(pin);
+	u32 off = sunxi_pull_offset(pin);
+	u32 msk = MUX_PINS_MASK << off;
+	u32 val = readl(pinctrl->base + reg);
+
+	val &= ~msk;
+	val |= (pull << off) & msk;
+	writel(val, pinctrl->base + reg);
+}
+
+static void sunxi_pinctrl_set_dlevel(struct sunxi_pinctrl *pinctrl,
+				   u16 pin, u32 lvl)
+{
+	u32 reg = sunxi_dlevel_reg(pin);
+	u32 off = sunxi_dlevel_offset(pin);
+	u32 msk = MUX_PINS_MASK << off;
+	u32 val = readl(pinctrl->base + reg);
+
+	val &= ~msk;
+	val |= (lvl << off) & msk;
+	writel(val, pinctrl->base + reg);
+}
+
+static void sunxi_pinctrl_set_mux(struct sunxi_pinctrl *pinctrl,
+				  u16 pin, u8 mux)
+{
+	u32 reg = sunxi_mux_reg(pin);
+	u32 off = sunxi_mux_offset(pin);
+	u32 msk = MUX_PINS_MASK << off;
+	u32 val = readl(pinctrl->base + reg);
+
+	val &= ~msk;
+	val |= (mux << off) & msk;
+	writel(val, pinctrl->base + reg);
+}
+
+static u8 sunxi_pinctrl_get_mux(struct sunxi_pinctrl *pinctrl, u16 pin)
+{
+	u32 reg = sunxi_mux_reg(pin);
+	u32 off = sunxi_mux_offset(pin);
+	u32 val = readl(pinctrl->base + reg);
+
+	return (val >> off) & MUX_PINS_MASK;
+}
+
+static void sunxi_pinctrl_set_conf(struct sunxi_pinctrl *pinctrl,
+				  u16 pin, struct device_node *node)
+{
+	u32 val;
+
+	if (of_find_property(node, "bias-pull-up", NULL))
+		sunxi_pinctrl_set_pull(pinctrl, pin, 1);
+	if (of_find_property(node, "bias-pull-down", NULL))
+		sunxi_pinctrl_set_pull(pinctrl, pin, 2);
+	if (of_find_property(node, "bias-disable", NULL))
+		sunxi_pinctrl_set_pull(pinctrl, pin, 0);
+
+	if (!of_property_read_u32(node, "drive-strength", &val)) {
+		val = rounddown(val, 10) / 10 - 1;
+		sunxi_pinctrl_set_dlevel(pinctrl, pin, val);
+	}
+}
+
+static const char *sunxi_pinctrl_parse_function_prop(struct device_node *node)
+{
+	const char *function;
+	int ret;
+
+	/* Try the generic binding */
+	ret = of_property_read_string(node, "function", &function);
+	if (!ret)
+		return function;
+
+	/* And fall back to our legacy one */
+	ret = of_property_read_string(node, "allwinner,function", &function);
+	if (!ret)
+		return function;
+
+	return NULL;
+}
+
+static struct property *sunxi_pinctrl_find_pins_prop(struct device_node *node)
+{
+	struct property *prop;
+
+	/* Try the generic binding */
+	prop = of_find_property(node, "pins", NULL);
+	if (prop)
+		return prop;
+
+	/* And fall back to our legacy one */
+	prop = of_find_property(node, "allwinner,pins", NULL);
+	if (prop)
+		return prop;
+
+	return NULL;
+}
+
+#define sunxi_pinctrl_of_pins_for_each_string(np, prop, s)	\
+	for (prop = sunxi_pinctrl_find_pins_prop(np),		\
+		s = of_prop_next_string(prop, NULL);		\
+		s;						\
+		s = of_prop_next_string(prop, s))
+
+
+static const struct sunxi_desc_pin *
+sunxi_pinctrl_find_pin(struct sunxi_pinctrl *pinctrl, const char *pin_name)
+{
+	const struct sunxi_desc_pin *pin;
+	int i;
+
+	for (i = 0; i < pinctrl->desc->npins; i++) {
+		pin = &pinctrl->desc->pins[i];
+		if (!strcmp(pin->pin.name, pin_name))
+			return pin;
+	}
+
+	return NULL;
+}
+
+static const struct sunxi_desc_function *
+sunxi_pinctrl_find_func(struct sunxi_pinctrl *pinctrl,
+			const char *pin_name, const char *func_name)
+{
+	const struct sunxi_desc_pin *pin;
+	const struct sunxi_desc_function *func;
+
+	pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
+	if (!pin)
+		return NULL;
+
+	for (func = pin->functions; func->name; func++)
+		if (!strcmp(func->name, func_name))
+			return func;
+
+	return NULL;
+}
+
+static int sunxi_pinctrl_set_func(struct sunxi_pinctrl *pinctrl,
+				  struct device_node *np,
+				  const char *pin_name, const char *func_name)
+{
+	struct device *dev = pinctrl->pdev.dev;
+	const struct sunxi_desc_pin *pin;
+	const struct sunxi_desc_function *func;
+
+	dev_dbg(dev, "setfunc %s @ %s\n", func_name, pin_name);
+
+	pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
+	if (!pin) {
+		dev_err(dev, "pin %s not found\n", pin_name);
+		return -EINVAL;
+	}
+
+	func = sunxi_pinctrl_find_func(pinctrl, pin_name, func_name);
+	if (!func) {
+		dev_err(dev, "func %s not found\n", func_name);
+		return -EINVAL;
+	}
+
+	sunxi_pinctrl_set_mux(pinctrl, pin->pin.number, func->muxval);
+	sunxi_pinctrl_set_conf(pinctrl, pin->pin.number, np);
+
+	return 0;
+}
+
+static int sunxi_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node *np)
+{
+	struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
+	struct device *dev = pinctrl->pdev.dev;
+	struct property *prop;
+	const char *func_name;
+	const char *pin_name;
+
+	func_name = sunxi_pinctrl_parse_function_prop(np);
+	if (!func_name) {
+		dev_err(dev, "%s: missing 'function' property\n", np->full_name);
+		return -EINVAL;
+	}
+
+	sunxi_pinctrl_of_pins_for_each_string(np, prop, pin_name) {
+		sunxi_pinctrl_set_func(pinctrl, np, pin_name, func_name);
+	}
+
+	return 0;
+}
+
+static int sunxi_pinctrl_set_direction(struct pinctrl_device *pdev, unsigned int gpio, bool in)
+{
+	struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
+	u32 func = in ? FUNC_GPIO_IN : FUNC_GPIO_OUT;
+
+	sunxi_pinctrl_set_mux(pinctrl, gpio, func);
+
+	return 0;
+}
+
+static int sunxi_gpio_get(struct gpio_chip *chip, unsigned gpio)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+	u32 reg = sunxi_data_reg(gpio);
+	u32 bit = sunxi_data_offset(gpio);
+	u32 val = readl(pinctrl->base + reg);
+
+	return val & BIT(bit);
+}
+
+static void sunxi_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+	u32 reg = sunxi_data_reg(gpio);
+	u32 bit = sunxi_data_offset(gpio);
+	u32 val = readl(pinctrl->base + reg);
+
+	if (value)
+		val |= BIT(bit);
+	else
+		val &= ~BIT(bit);
+	writel(val, pinctrl->base + reg);
+}
+
+static int sunxi_gpio_direction_output(struct gpio_chip *chip,
+				       unsigned gpio, int value)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+
+	sunxi_gpio_set(chip, gpio, value);
+	sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_OUT);
+
+	return 0;
+}
+
+static int sunxi_gpio_direction_input(struct gpio_chip *chip,
+					unsigned gpio)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+
+	sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_IN);
+
+	return 0;
+}
+
+static int sunxi_gpio_get_direction(struct gpio_chip *chip, unsigned gpio)
+{
+	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
+	u32 func = sunxi_pinctrl_get_mux(pinctrl, gpio);
+
+	if (func == FUNC_GPIO_IN)
+		return GPIOF_DIR_IN;
+	if (func == FUNC_GPIO_OUT)
+		return GPIOF_DIR_OUT;
+	return -EINVAL;
+}
+
+static int sunxi_gpio_of_xlate(struct gpio_chip *chip,
+			       const struct of_phandle_args *gpiospec,
+			       u32 *flags)
+{
+	int pin, base;
+
+	if (gpiospec->args_count != 3)
+		return -EINVAL;
+
+	base = PINS_PER_BANK * gpiospec->args[0];
+	pin = base + gpiospec->args[1];
+
+	if (pin > chip->ngpio)
+		return -EINVAL;
+
+	if (flags)
+		*flags = gpiospec->args[2];
+
+	return pin;
+}
+
+static struct pinctrl_ops sunxi_pinctrl_ops = {
+	.set_state = sunxi_pinctrl_set_state,
+	.set_direction = sunxi_pinctrl_set_direction,
+};
+
+static struct gpio_ops sunxi_gpio_ops = {
+	.request = sunxi_gpio_direction_input, /* switch to input function */
+	.direction_input = sunxi_gpio_direction_input,
+	.direction_output = sunxi_gpio_direction_output,
+	.get_direction = sunxi_gpio_get_direction,
+	.get = sunxi_gpio_get,
+	.set = sunxi_gpio_set,
+	.of_xlate = sunxi_gpio_of_xlate,
+};
+
+int sunxi_pinctrl_probe(struct device *dev)
+{
+	const struct sunxi_pinctrl_desc *desc;
+	struct sunxi_pinctrl *pinctrl;
+	struct resource *iores;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_PINCTRL))
+		return 0;
+
+	desc = device_get_match_data(dev);
+	if (!desc)
+                return -EINVAL;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+
+	pinctrl = xzalloc(sizeof(*pinctrl));
+	dev->priv = pinctrl;
+	pinctrl->base = IOMEM(iores->start);
+
+	pinctrl->desc = desc;
+	pinctrl->pdev.dev = dev;
+	pinctrl->pdev.ops = &sunxi_pinctrl_ops;
+
+	ret = pinctrl_register(&pinctrl->pdev);
+	if (ret) {
+		dev_err(dev, "couldn't register %s driver\n", "pinctrl");
+		goto err;
+	}
+	dev_dbg(dev, "sunxi %s registered\n", "pinctrl");
+
+	pinctrl->chip.dev = dev;
+	pinctrl->chip.ops = &sunxi_gpio_ops;
+	/* only the first 8 bank are supported */
+	pinctrl->chip.base = 0;
+	pinctrl->chip.ngpio = 8 * PINS_PER_BANK;
+
+	if (of_property_read_bool(dev->of_node, "gpio-controller")) {
+		ret = gpiochip_add(&pinctrl->chip);
+		if (ret) {
+			dev_err(dev, "couldn't register %s driver\n", "gpio-chip");
+			goto pinctrl_unregister;
+		}
+		dev_dbg(dev, "sunxi %s registered\n", "gpio-chip");
+	}
+	return 0;
+
+pinctrl_unregister:
+	pinctrl_unregister(&pinctrl->pdev);
+err:
+	release_region(iores);
+	free(pinctrl);
+	return ret;
+}
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
new file mode 100644
index 0000000000..630f1ef98e
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Allwinner A1X SoCs pinctrl driver.
+ *
+ * Copyright (C) 2012 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PINCTRL_SUNXI_H
+#define __PINCTRL_SUNXI_H
+
+#include <pinctrl.h>
+#include <gpio.h>
+
+#define PA_BASE	0
+#define PB_BASE	32
+#define PC_BASE	64
+#define PD_BASE	96
+#define PE_BASE	128
+#define PF_BASE	160
+#define PG_BASE	192
+#define PH_BASE	224
+#define PI_BASE	256
+#define PL_BASE	352
+#define PM_BASE	384
+#define PN_BASE	416
+
+#define SUNXI_PIN_NAME_MAX_LEN	5
+
+#define BANK_MEM_SIZE		0x24
+#define MUX_REGS_OFFSET		0x0
+#define DATA_REGS_OFFSET	0x10
+#define DLEVEL_REGS_OFFSET	0x14
+#define PULL_REGS_OFFSET	0x1c
+
+#define PINS_PER_BANK		32
+#define MUX_PINS_PER_REG	8
+#define MUX_PINS_BITS		4
+#define MUX_PINS_MASK		0x0f
+#define DATA_PINS_PER_REG	32
+#define DATA_PINS_BITS		1
+#define DATA_PINS_MASK		0x01
+#define DLEVEL_PINS_PER_REG	16
+#define DLEVEL_PINS_BITS	2
+#define DLEVEL_PINS_MASK	0x03
+#define PULL_PINS_PER_REG	16
+#define PULL_PINS_BITS		2
+#define PULL_PINS_MASK		0x03
+
+#define GRP_CFG_REG		0x300
+
+#define IO_BIAS_MASK		GENMASK(3, 0)
+
+#define SUN4I_FUNC_INPUT	0
+#define SUN4I_FUNC_IRQ		6
+
+#define PINCTRL_SUN5I_A10S	BIT(1)
+#define PINCTRL_SUN5I_A13	BIT(2)
+#define PINCTRL_SUN5I_GR8	BIT(3)
+#define PINCTRL_SUN6I_A31	BIT(4)
+#define PINCTRL_SUN6I_A31S	BIT(5)
+#define PINCTRL_SUN4I_A10	BIT(6)
+#define PINCTRL_SUN7I_A20	BIT(7)
+#define PINCTRL_SUN8I_R40	BIT(8)
+#define PINCTRL_SUN8I_V3	BIT(9)
+#define PINCTRL_SUN8I_V3S	BIT(10)
+
+#define PIO_POW_MOD_SEL_REG	0x340
+
+enum sunxi_desc_bias_voltage {
+	BIAS_VOLTAGE_NONE,
+	/*
+	 * Bias voltage configuration is done through
+	 * Pn_GRP_CONFIG registers, as seen on A80 SoC.
+	 */
+	BIAS_VOLTAGE_GRP_CONFIG,
+	/*
+	 * Bias voltage is set through PIO_POW_MOD_SEL_REG
+	 * register, as seen on H6 SoC, for example.
+	 */
+	BIAS_VOLTAGE_PIO_POW_MODE_SEL,
+};
+
+struct sunxi_desc_function {
+	const char	*name;
+	u8		muxval;
+};
+
+struct sunxi_desc_pin {
+	struct {
+		const char		name[6];
+		u16			number;
+	} pin;
+	const struct sunxi_desc_function	*functions;
+};
+
+struct sunxi_pinctrl_desc {
+	const struct sunxi_desc_pin	*pins;
+	size_t				npins;
+	unsigned			pin_base;
+	bool				disable_strict_mode;
+	enum sunxi_desc_bias_voltage	io_bias_cfg_variant;
+};
+
+struct sunxi_pinctrl {
+	void __iomem			*base;
+	struct gpio_chip		chip;
+	struct pinctrl_device		pdev;
+	const struct sunxi_pinctrl_desc	*desc;
+};
+
+#define SUNXI_PIN(_pin, ...)					\
+	{							\
+		.pin = _pin,					\
+		.functions = (struct sunxi_desc_function[]){	\
+			__VA_ARGS__, { } },			\
+	}
+
+#define SUNXI_PINCTRL_PIN(bank, pin)				\
+	{							\
+		.name = "P" #bank #pin,				\
+		.number = P ## bank ## _BASE + (pin)		\
+	}
+
+#define SUNXI_FUNCTION(_val, _name)				\
+	{							\
+		.name = _name,					\
+		.muxval = _val,					\
+	}
+
+#define SUNXI_FUNCTION_IRQ_BANK(...)  {}
+
+/*
+ * The sunXi PIO registers are organized as is:
+ * 0x00 - 0x0c	Muxing values.
+ *		8 pins per register, each pin having a 4bits value
+ * 0x10		Pin values
+ *		32 bits per register, each pin corresponding to one bit
+ * 0x14 - 0x18	Drive level
+ *		16 pins per register, each pin having a 2bits value
+ * 0x1c - 0x20	Pull-Up values
+ *		16 pins per register, each pin having a 2bits value
+ *
+ * This is for the first bank. Each bank will have the same layout,
+ * with an offset being a multiple of 0x24.
+ *
+ * The following functions calculate from the pin number the register
+ * and the bit offset that we should access.
+ */
+static inline u32 sunxi_mux_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += MUX_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_mux_offset(u16 pin)
+{
+	u32 pin_num = pin % MUX_PINS_PER_REG;
+	return pin_num * MUX_PINS_BITS;
+}
+
+static inline u32 sunxi_data_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += DATA_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / DATA_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_data_offset(u16 pin)
+{
+	u32 pin_num = pin % DATA_PINS_PER_REG;
+	return pin_num * DATA_PINS_BITS;
+}
+
+static inline u32 sunxi_dlevel_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += DLEVEL_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_dlevel_offset(u16 pin)
+{
+	u32 pin_num = pin % DLEVEL_PINS_PER_REG;
+	return pin_num * DLEVEL_PINS_BITS;
+}
+
+static inline u32 sunxi_pull_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += PULL_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_pull_offset(u16 pin)
+{
+	u32 pin_num = pin % PULL_PINS_PER_REG;
+	return pin_num * PULL_PINS_BITS;
+}
+
+static inline u32 sunxi_grp_config_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+
+	return GRP_CFG_REG + bank * 0x4;
+}
+
+int sunxi_pinctrl_probe(struct device *dev);
+
+#endif /* __PINCTRL_SUNXI_H */
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 08/11] mci: Add sunxi-mmc driver
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (6 preceding siblings ...)
  2023-05-10 23:37 ` [RFC PATCH 07/11] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-18 19:26   ` Ahmad Fatoum
  2023-05-10 23:37 ` [RFC PATCH 09/11] arm: sunxi: Add sun50i SDRAM init Jules Maselbas
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

This driver is adapted from different sources: Linux, u-boot and p-boot.
The latter, p-boot (forked from u-boot), is a bootloader for pinephones.

It currently only support PIO xfer but could be further improved to also
support DMA xfer. This driver is split in three source file so it can be
used by PBL and barebox proper.
---
 drivers/mci/Kconfig            |   6 +
 drivers/mci/Makefile           |   2 +
 drivers/mci/sunxi-mmc-common.c | 259 +++++++++++++++++++++++++++++++++
 drivers/mci/sunxi-mmc-pbl.c    |  81 +++++++++++
 drivers/mci/sunxi-mmc.c        | 173 ++++++++++++++++++++++
 drivers/mci/sunxi-mmc.h        | 225 ++++++++++++++++++++++++++++
 include/mach/sunxi/xload.h     |  12 ++
 7 files changed, 758 insertions(+)
 create mode 100644 drivers/mci/sunxi-mmc-common.c
 create mode 100644 drivers/mci/sunxi-mmc-pbl.c
 create mode 100644 drivers/mci/sunxi-mmc.c
 create mode 100644 drivers/mci/sunxi-mmc.h
 create mode 100644 include/mach/sunxi/xload.h

diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index bbdca67e9d..c1903b6c90 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -72,6 +72,12 @@ config MCI_DW_PIO
 	help
 	  Use PIO mode (instead of IDMAC) in DW MMC driver.
 
+config MCI_SUNXI_SMHC
+	bool "Allwinner SD-MMC Memory Card Host Controller"
+	help
+	  Enable support for the Allwinner SD-MMC Memory Card Host Controller,
+	  his provides host support for SD and MMC interfaces, in PIO mode.
+
 config MCI_MXS
 	bool "i.MX23/i.MX28"
 	depends on ARCH_MXS
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index e3dc5ad8ae..c17cd41db1 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -20,4 +20,6 @@ obj-$(CONFIG_MCI_SPI)		+= mci_spi.o
 obj-$(CONFIG_MCI_DW)		+= dw_mmc.o
 obj-$(CONFIG_MCI_MMCI)		+= mmci.o
 obj-$(CONFIG_MCI_STM32_SDMMC2)	+= stm32_sdmmc2.o
+obj-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc.o
+pbl-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc-pbl.o
 obj-pbl-$(CONFIG_MCI_SDHCI)	+= sdhci.o
diff --git a/drivers/mci/sunxi-mmc-common.c b/drivers/mci/sunxi-mmc-common.c
new file mode 100644
index 0000000000..845078805b
--- /dev/null
+++ b/drivers/mci/sunxi-mmc-common.c
@@ -0,0 +1,259 @@
+#include "sunxi-mmc.h"
+
+static int sdxc_read_data_pio(struct sunxi_mmc_host *host, struct mci_data *data);
+static int sdxc_write_data_pio(struct sunxi_mmc_host *host, struct mci_data *data);
+static int sunxi_mmc_send_cmd(struct sunxi_mmc_host *host, struct mci_cmd *cmd, struct mci_data *data, const char **why);
+static int sunxi_mmc_set_ios(struct sunxi_mmc_host *host, struct mci_ios *ios);
+static void sunxi_mmc_init(struct sunxi_mmc_host *host);
+
+static int sdxc_read_data_pio(struct sunxi_mmc_host *host, struct mci_data *data)
+{
+	size_t i, len = data->blocks * data->blocksize;
+	u8 *dst = data->dest;
+	u32 reg;
+
+	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_ACCESS_BY_AHB);
+
+	for (i = 0; i < len; i += 4) {
+		if (wait_on_timeout(2000 * MSECOND, !sdxc_is_fifo_empty(host)))
+			return -ETIMEDOUT;
+		reg = sdxc_readl(host, SDXC_REG_FIFO);
+		memcpy(dst + i, &reg, sizeof(reg));
+	}
+
+	return i;
+}
+
+static int sdxc_write_data_pio(struct sunxi_mmc_host *host, struct mci_data *data)
+{
+	size_t i, len = data->blocks * data->blocksize;
+	u32 *pdata = (u32 *)data->src;
+
+	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_ACCESS_BY_AHB);
+
+	for (i = 0; i < len; i += 4, pdata++) {
+		if (wait_on_timeout(2000 * MSECOND, !sdxc_is_fifo_full(host)))
+			return -ETIMEDOUT;
+		sdxc_writel(host, SDXC_REG_FIFO, *pdata);
+	}
+#if 0
+	sdxc_writel(host, SDXC_RINTR, SDXC_INTMSK_TXDR);
+
+	if (wait_on_timeout(2000 * MSECOND, sdxc_is_fifo_empty(host))) {
+		return -EIO;
+	}
+#endif
+	return i;
+}
+
+static int sunxi_mmc_send_cmd(struct sunxi_mmc_host *host, struct mci_cmd *cmd,
+			      struct mci_data *data, const char **why)
+{
+	const char *err_why = "";
+	u32 cmdval = SDXC_CMD_START;
+	int ret;
+
+	if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
+		return -EINVAL;
+
+	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+		return 0; /* using ACMD12 */
+	if (cmd->cmdidx == MMC_CMD_GO_IDLE_STATE)
+		cmdval |= SDXC_CMD_SEND_INIT_SEQ;
+
+	if (cmd->resp_type & MMC_RSP_PRESENT)
+		cmdval |= SDXC_CMD_RESP_EXPIRE;
+	if (cmd->resp_type & MMC_RSP_136)
+		cmdval |= SDXC_CMD_LONG_RESPONSE;
+	if (cmd->resp_type & MMC_RSP_CRC)
+		cmdval |= SDXC_CMD_CHK_RESPONSE_CRC;
+
+	/* clear interrupts */
+	sdxc_writel(host, SDXC_REG_RINTR, 0xffffffff);
+
+	if (data) {
+		u32 blksiz = data->blocksize;
+		u32 bytcnt = data->blocks * data->blocksize;
+
+		cmdval |= SDXC_CMD_DATA_EXPIRE;
+		cmdval |= SDXC_CMD_WAIT_PRE_OVER;
+		if (data->flags & MMC_DATA_WRITE)
+			cmdval |= SDXC_CMD_WRITE;
+		if (data->blocks > 1)
+			cmdval |= SDXC_CMD_AUTO_STOP;
+
+		sdxc_writel(host, SDXC_REG_TMOUT, 0xFFFFFF40);
+		sdxc_writel(host, SDXC_REG_BLKSZ, blksiz);
+		sdxc_writel(host, SDXC_REG_BCNTR, bytcnt);
+	}
+
+	sdxc_writel(host, SDXC_REG_CARG, cmd->cmdarg);
+	sdxc_writel(host, SDXC_REG_CMDR, cmdval | cmd->cmdidx);
+	if (data) {
+		if (data->flags & MMC_DATA_WRITE)
+			ret = sdxc_write_data_pio(host, data);
+		else
+			ret = sdxc_read_data_pio(host, data);
+		if (ret < 0) {
+			err_why = "pio error";
+			goto err;
+		}
+	}
+
+	ret = sdxc_xfer_complete(host, 1000 * MSECOND, SDXC_INT_COMMAND_DONE);
+	if (ret) {
+		err_why = "cmd timeout";
+		goto err;
+	}
+
+	if (data) {
+		ret = sdxc_xfer_complete(host, 1000 * MSECOND, data->blocks > 1 ?
+					 SDXC_INT_AUTO_COMMAND_DONE :
+					 SDXC_INT_DATA_OVER);
+		if (ret) {
+			err_why = "data timeout";
+			goto err;
+		}
+	}
+
+	if (cmd->resp_type & MMC_RSP_BUSY) {
+		u32 status;
+		u64 start;
+		start = get_time_ns();
+		do {
+			status = sdxc_readl(host, SDXC_REG_STAS);
+			if (is_timeout(start, 2000 * MSECOND)) {
+				err_why = "resp timeout";
+				ret = -ETIMEDOUT;
+				goto err;
+			}
+		} while (status & SDXC_STATUS_BUSY);
+	}
+
+	if (wait_on_timeout(1000 * MSECOND, !sdxc_is_card_busy(host))) {
+		err_why = "card busy timeout";
+		ret = -ETIMEDOUT;
+		goto err;
+	}
+
+	if (cmd->resp_type & MMC_RSP_136) {
+		cmd->response[0] = sdxc_readl(host, SDXC_REG_RESP3);
+		cmd->response[1] = sdxc_readl(host, SDXC_REG_RESP2);
+		cmd->response[2] = sdxc_readl(host, SDXC_REG_RESP1);
+		cmd->response[3] = sdxc_readl(host, SDXC_REG_RESP0);
+	} else if (cmd->resp_type & MMC_RSP_PRESENT) {
+		cmd->response[0] = sdxc_readl(host, SDXC_REG_RESP0);
+	}
+
+err:
+	if (why)
+		*why = err_why;
+	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_FIFO_RESET);
+	return ret;
+}
+
+static int sunxi_mmc_update_clk(struct sunxi_mmc_host *host)
+{
+	u32 cmdval;
+
+	cmdval = SDXC_CMD_START |
+	         SDXC_CMD_UPCLK_ONLY |
+	         SDXC_CMD_WAIT_PRE_OVER;
+
+	sdxc_writel(host, SDXC_REG_CARG, 0);
+	sdxc_writel(host, SDXC_REG_CMDR, cmdval);
+
+	if (wait_on_timeout(2000 * MSECOND, !(sdxc_readl(host, SDXC_REG_CMDR) & SDXC_CMD_START)))
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int sunxi_mmc_setup_clk(struct sunxi_mmc_host *host, u32 freq)
+{
+	u32 val, div, sclk;
+	int ret;
+
+	sclk = host->clkrate;
+	if (sclk == 0)
+		return -EINVAL;
+
+	sclk /= 2; // WHY ????
+
+	/* disable card clock */
+	val = sdxc_readl(host, SDXC_REG_CLKCR);
+	val &= ~(SDXC_CLK_ENABLE | SDXC_CLK_LOW_POWER_ON);
+	val |= SDXC_CLK_MASK_DATA0;
+	sdxc_writel(host, SDXC_REG_CLKCR, val);
+
+	ret = sunxi_mmc_update_clk(host);
+	if (ret)
+		return ret;
+
+	/*
+	 * Configure the controller to use the new timing mode if needed.
+	 * On controllers that only support the new timing mode, such as
+	 * the eMMC controller on the A64, this register does not exist,
+	 * and any writes to it are ignored.
+	 */
+	if (host->cfg->needs_new_timings) {
+		/* Don't touch the delay bits */
+		val = sdxc_readl(host, SDXC_REG_NTSR);
+		val |= SDXC_NTSR_2X_TIMING_MODE;
+		sdxc_writel(host, SDXC_REG_NTSR, val);
+	}
+
+	/* setup clock rate */
+	div = DIV_ROUND_UP(sclk, freq);
+	if (div > 510)
+		div = 510;
+
+	/* set internal divider */
+	val = sdxc_readl(host, SDXC_REG_CLKCR);
+	val &= ~SDXC_CLK_DIVIDER_MASK;
+	val |= div / 2; /* divisor is multiply by 2 */
+	sdxc_writel(host, SDXC_REG_CLKCR, val);
+
+	/* enable card clock */
+	val = sdxc_readl(host, SDXC_REG_CLKCR);
+	val |= SDXC_CLK_ENABLE;
+	val &= ~SDXC_CLK_MASK_DATA0;
+	sdxc_writel(host, SDXC_REG_CLKCR, val);
+
+	return sunxi_mmc_update_clk(host);
+}
+
+static int sunxi_mmc_set_ios(struct sunxi_mmc_host *host, struct mci_ios *ios)
+{
+	int ret = 0;
+	u32 width;
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_8:
+		width = SDXC_WIDTH_8BIT;
+		break;
+	case MMC_BUS_WIDTH_4:
+		width = SDXC_WIDTH_4BIT;
+		break;
+	default:
+		width = SDXC_WIDTH_1BIT;
+		break;
+	}
+	sdxc_writel(host, SDXC_REG_WIDTH, width);
+
+	if (ios->clock)
+		ret = sunxi_mmc_setup_clk(host, ios->clock);
+	return ret;
+}
+
+static void sunxi_mmc_init(struct sunxi_mmc_host *host)
+{
+	/* Reset controller */
+	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_RESET);
+	udelay(1000);
+
+	sdxc_writel(host, SDXC_REG_RINTR, 0xffffffff);
+	sdxc_writel(host, SDXC_REG_IMASK, 0);
+
+	sdxc_writel(host, SDXC_REG_TMOUT, 0xffffff40);
+}
diff --git a/drivers/mci/sunxi-mmc-pbl.c b/drivers/mci/sunxi-mmc-pbl.c
new file mode 100644
index 0000000000..f9141a599e
--- /dev/null
+++ b/drivers/mci/sunxi-mmc-pbl.c
@@ -0,0 +1,81 @@
+#include <common.h>
+
+#include <mach/sunxi/xload.h>
+#include "sunxi-mmc.h"
+#include "sunxi-mmc-common.c"
+
+#define SECTOR_SIZE 512
+
+static int sunxi_mmc_read_block(struct sunxi_mmc_host *host,
+				void *dst, unsigned int blocknum,
+				unsigned int blocks)
+{
+	struct mci_data data;
+	struct mci_cmd cmd = {
+		.cmdidx = (blocks > 1) ? MMC_CMD_READ_MULTIPLE_BLOCK : MMC_CMD_READ_SINGLE_BLOCK,
+		 /* mci->high_capacity ? blocknum : blocknum * mci->read_bl_len, */
+		 /* TODO: figured out how to detect if card is high-capacity */
+//		.cmdarg = blocknum * SECTOR_SIZE,
+		.cmdarg = blocknum,
+		.resp_type = MMC_RSP_R1,
+	};
+	int ret;
+
+	data.dest = dst;
+	data.blocks = blocks;
+	data.blocksize = SECTOR_SIZE; /* compat with MMC/SD */
+	data.flags = MMC_DATA_READ;
+
+	ret = sunxi_mmc_send_cmd(host, &cmd, &data, NULL);
+
+	if (ret || blocks > 1) {
+		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+		cmd.cmdarg = 0;
+		cmd.resp_type = MMC_RSP_R1b;
+		sunxi_mmc_send_cmd(host, &cmd, NULL, NULL);
+	}
+
+	return ret;
+}
+
+static int sunxi_mmc_bio_read(struct pbl_bio *bio, off_t start,
+				void *buf, unsigned int nblocks)
+{
+	struct sunxi_mmc_host *host = bio->priv;
+	unsigned int count = 0;
+	unsigned int block_len = SECTOR_SIZE;
+	int ret;
+
+	while (count < nblocks) {
+		unsigned int n = min_t(unsigned int, nblocks - count, 16 /* SUPPORT_MAX_BLOCKS */);
+
+		ret = sunxi_mmc_read_block(host, buf, start, n);
+		if (ret < 0)
+			return ret;
+
+		count += n;
+		start += n;
+		buf += n * block_len;
+	}
+
+	return count;
+}
+
+static struct sunxi_mmc_host pouet;
+
+int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
+		       unsigned int clock, unsigned int slot)
+{
+	struct sunxi_mmc_host *host = &pouet;
+	struct mci_ios ios = { .bus_width = MMC_BUS_WIDTH_4, .clock = 400000 };
+
+	host->base = base;
+	host->clkrate = clock;
+	bio->priv = host;
+	bio->read = sunxi_mmc_bio_read;
+
+	sunxi_mmc_init(host);
+	sunxi_mmc_set_ios(host, &ios);
+
+	return 0;
+}
diff --git a/drivers/mci/sunxi-mmc.c b/drivers/mci/sunxi-mmc.c
new file mode 100644
index 0000000000..a537ea1a55
--- /dev/null
+++ b/drivers/mci/sunxi-mmc.c
@@ -0,0 +1,173 @@
+//#define DEBUG
+// SPDX-License-Identifier: GPL-2.0-or-later
+// derived from: linux/drivers/mmc/host/sunxi-mmc.c
+
+#define pr_fmt(fmt) "sunxi-mmc: " fmt
+
+#include <common.h>
+#include <driver.h>
+#include <malloc.h>
+#include <init.h>
+#include <mci.h>
+
+#include <gpio.h>
+#include <of_gpio.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <errno.h>
+#include <dma.h>
+
+#include "sunxi-mmc.h"
+#include "sunxi-mmc-common.c"
+
+static int sdxc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
+{
+	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
+	struct device *dev = mci->hw_dev;
+	const char *why;
+	int ret;
+
+	ret = sunxi_mmc_send_cmd(host, cmd, data, &why);
+	if (ret)
+		dev_err(dev, "error %s CMD%d (%d)\n", why, cmd->cmdidx, ret);
+
+	return ret;
+}
+
+static void sdxc_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
+	struct device *dev = mci->hw_dev;
+
+	dev_dbg(dev, "buswidth = %d, clock: %d\n", ios->bus_width, ios->clock);
+	sunxi_mmc_set_ios(host, ios);
+}
+
+static int sdxc_card_present(struct mci_host *mci)
+{
+	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
+	struct device *dev = mci->hw_dev;
+	int ret;
+
+	/* No gpio, assume card is present */
+	if (!gpio_is_valid(host->gpio_cd)) {
+		dev_err(dev, "%s gpio not valid\n", __func__);
+		return 1;
+	}
+
+	ret = gpio_get_value(host->gpio_cd);
+	dev_dbg(dev, "%s gpio: %d\n", __func__, ret);
+
+	return ret == 0 ? 1 : 0;
+}
+
+static int sdxc_init(struct mci_host *mci, struct device *dev)
+{
+	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
+
+	sunxi_mmc_init(host);
+
+	return 0;
+}
+
+static int sunxi_mmc_probe(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct resource *iores;
+	struct sunxi_mmc_host *host;
+	unsigned int f_min, f_max;
+	int ret;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+	host = xzalloc(sizeof(*host));
+	host->base = IOMEM(iores->start);
+	dma_set_mask(dev, DMA_BIT_MASK(32));
+	host->cfg = device_get_match_data(dev);
+
+	host->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
+
+	host->clk_ahb = clk_get(dev, "ahb");
+	if (IS_ERR(host->clk_ahb)) {
+		ret = PTR_ERR(host->clk_ahb);
+		goto err;
+	}
+
+	host->clk_mmc = clk_get(dev, "mmc");
+	if (IS_ERR(host->clk_mmc)) {
+		ret = PTR_ERR(host->clk_mmc);
+		goto err;
+	}
+
+	clk_enable(host->clk_ahb);
+	clk_enable(host->clk_mmc);
+
+	host->mci.hw_dev = dev;
+	host->mci.send_cmd = sdxc_send_cmd;
+	host->mci.set_ios = sdxc_set_ios;
+	host->mci.init = sdxc_init;
+	host->mci.card_present = sdxc_card_present;
+	host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	host->mci.host_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA
+		| MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED
+		| MMC_CAP_MMC_HIGHSPEED_52MHZ;
+
+	host->clkrate = clk_get_rate(host->clk_mmc);
+	f_min = host->clkrate / 510;
+	f_max = host->clkrate;
+	/* clock must at least support freq as low as 400K, and reach 52M */
+	if (400000 < f_min || f_max < 52000000) {
+		/* if not, try to get a better clock */
+		clk_set_rate(host->clk_mmc, clk_round_rate(host->clk_mmc, 52000000));
+		host->clkrate = clk_get_rate(host->clk_mmc);
+		f_min = host->clkrate / 510;
+		f_max = host->clkrate;
+	}
+	dev_dbg(dev, "freq: min %d max %d\n", f_min, f_max);
+	mci_of_parse(&host->mci);
+
+	f_min = min_t(unsigned int, 400000, f_min);
+	f_max = min_t(unsigned int, 52000000, f_max);
+	host->mci.f_min = max_t(unsigned int, host->mci.f_min, f_min);
+	host->mci.f_max = min_t(unsigned int, host->mci.f_max, f_max);
+
+	return mci_register(&host->mci);
+err:
+	if (host->clk_mmc)
+		clk_put(host->clk_mmc);
+	if (host->clk_ahb)
+		clk_put(host->clk_ahb);
+	free(host);
+	release_region(iores);
+	return ret;
+}
+
+static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
+	.idma_des_size_bits = 16,
+	.clk_delays = NULL,
+	.can_calibrate = true,
+	.mask_data0 = true,
+	.needs_new_timings = true,
+};
+
+static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
+	.idma_des_size_bits = 13,
+	.clk_delays = NULL,
+	.can_calibrate = true,
+	.needs_new_timings = true,
+};
+
+static __maybe_unused struct of_device_id sunxi_mmc_compatible[] = {
+	{ .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
+	{ .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
+	{ /* sentinel */ }
+};
+
+static struct driver sunxi_mmc_driver = {
+	.name  = "sunxi-mmc",
+	.probe = sunxi_mmc_probe,
+	.of_compatible = DRV_OF_COMPAT(sunxi_mmc_compatible),
+};
+device_platform_driver(sunxi_mmc_driver);
diff --git a/drivers/mci/sunxi-mmc.h b/drivers/mci/sunxi-mmc.h
new file mode 100644
index 0000000000..c8113f606a
--- /dev/null
+++ b/drivers/mci/sunxi-mmc.h
@@ -0,0 +1,225 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2023 Jules Maselbas  */
+/* derived from: linux/drivers/mmc/host/sunxi-mmc.c */
+
+#ifndef SUNXI_MMC_H
+#define	SUNXI_MMC_H
+
+#include <io.h>
+#include <linux/bitops.h>
+#include <clock.h>
+#include <mci.h>
+
+#define SDXC_REG_GCTRL	(0x00) /* Global Control */
+#define SDXC_REG_CLKCR	(0x04) /* Clock Control */
+#define SDXC_REG_TMOUT	(0x08) /* Time Out */
+#define SDXC_REG_WIDTH	(0x0C) /* Bus Width */
+#define SDXC_REG_BLKSZ	(0x10) /* Block Size */
+#define SDXC_REG_BCNTR	(0x14) /* Byte Count */
+#define SDXC_REG_CMDR	(0x18) /* Command */
+#define SDXC_REG_CARG	(0x1C) /* Argument */
+#define SDXC_REG_RESP0	(0x20) /* Response 0 */
+#define SDXC_REG_RESP1	(0x24) /* Response 1 */
+#define SDXC_REG_RESP2	(0x28) /* Response 2 */
+#define SDXC_REG_RESP3	(0x2C) /* Response 3 */
+#define SDXC_REG_IMASK	(0x30) /* Interrupt Mask */
+#define SDXC_REG_MISTA	(0x34) /* Masked Interrupt Status */
+#define SDXC_REG_RINTR	(0x38) /* Raw Interrupt Status */
+#define SDXC_REG_STAS	(0x3C) /* Status */
+#define SDXC_REG_FTRGL	(0x40) /* FIFO Threshold Watermark */
+#define SDXC_REG_FUNS	(0x44) /* Function Select */
+#define SDXC_REG_CBCR	(0x48) /* CIU Byte Count */
+#define SDXC_REG_BBCR	(0x4C) /* BIU Byte Count */
+#define SDXC_REG_DBGC	(0x50) /* Debug Enable */
+#define SDXC_REG_A12A	(0x58) /* Auto Command 12 Argument */
+#define SDXC_REG_NTSR	(0x5C) /* SMC New Timing Set Register */
+#define SDXC_REG_HWRST	(0x78) /* Card Hardware Reset */
+#define SDXC_REG_DMAC	(0x80) /* IDMAC Control */
+#define SDXC_REG_DLBA	(0x84) /* IDMAC Descriptor List Base Addresse */
+#define SDXC_REG_IDST	(0x88) /* IDMAC Status */
+#define SDXC_REG_IDIE	(0x8C) /* IDMAC Interrupt Enable */
+#define SDXC_REG_CHDA	(0x90)
+#define SDXC_REG_CBDA	(0x94)
+
+#define SDXC_REG_DRV_DL		0x140 /* Drive Delay Control Register */
+#define SDXC_REG_SAMP_DL_REG	0x144 /* SMC sample delay control */
+#define SDXC_REG_DS_DL_REG	0x148 /* SMC data strobe delay control */
+
+#define SDXC_REG_FIFO	(0x200) /* FIFO */
+
+#define SDXC_GCTRL_SOFT_RESET		BIT(0)
+#define SDXC_GCTRL_FIFO_RESET		BIT(1)
+#define SDXC_GCTRL_DMA_RESET		BIT(2)
+#define SDXC_GCTRL_RESET \
+	(SDXC_GCTRL_SOFT_RESET | SDXC_GCTRL_FIFO_RESET | SDXC_GCTRL_DMA_RESET)
+#define SDXC_GCTRL_DMA_ENABLE		BIT(5)
+#define SDXC_GCTRL_ACCESS_BY_AHB	BIT(31)
+
+#define SDXC_CMD_RESP_EXPIRE		BIT(6)
+#define SDXC_CMD_LONG_RESPONSE		BIT(7)
+#define SDXC_CMD_CHK_RESPONSE_CRC	BIT(8)
+#define SDXC_CMD_DATA_EXPIRE		BIT(9)
+#define SDXC_CMD_WRITE			BIT(10)
+#define SDXC_CMD_AUTO_STOP		BIT(12)
+#define SDXC_CMD_WAIT_PRE_OVER		BIT(13)
+#define SDXC_CMD_ABORT_STOP		BIT(14)
+#define SDXC_CMD_SEND_INIT_SEQ		BIT(15)
+#define SDXC_CMD_UPCLK_ONLY		BIT(21)
+#define SDXC_CMD_START			BIT(31)
+
+#define SDXC_NTSR_2X_TIMING_MODE	BIT(31)
+
+/* clock control bits */
+#define SDXC_CLK_MASK_DATA0	BIT(31)
+#define SDXC_CLK_LOW_POWER_ON	BIT(17)
+#define SDXC_CLK_ENABLE		BIT(16)
+#define SDXC_CLK_DIVIDER_MASK	(0xff)
+
+/* bus width */
+#define SDXC_WIDTH_1BIT	0
+#define SDXC_WIDTH_4BIT	BIT(0)
+#define SDXC_WIDTH_8BIT	BIT(1)
+
+/* interrupt bits */
+#define SDXC_INT_RESP_ERROR		BIT(1)
+#define SDXC_INT_COMMAND_DONE		BIT(2)
+#define SDXC_INT_DATA_OVER		BIT(3)
+#define SDXC_INT_TX_DATA_REQUEST	BIT(4)
+#define SDXC_INT_RX_DATA_REQUEST	BIT(5)
+#define SDXC_INT_RESP_CRC_ERROR		BIT(6)
+#define SDXC_INT_DATA_CRC_ERROR		BIT(7)
+#define SDXC_INT_RESP_TIMEOUT		BIT(8)
+#define SDXC_INT_DATA_TIMEOUT		BIT(9)
+#define SDXC_INT_VOLTAGE_CHANGE_DONE	BIT(10)
+#define SDXC_INT_FIFO_RUN_ERROR		BIT(11)
+#define SDXC_INT_HARD_WARE_LOCKED	BIT(12)
+#define SDXC_INT_START_BIT_ERROR	BIT(13)
+#define SDXC_INT_AUTO_COMMAND_DONE	BIT(14)
+#define SDXC_INT_END_BIT_ERROR		BIT(15)
+#define SDXC_INT_SDIO_INTERRUPT		BIT(16)
+#define SDXC_INT_CARD_INSERT		BIT(30)
+#define SDXC_INT_CARD_REMOVE		BIT(31)
+//	 SDXC_INT_FIFO_RUN_ERROR  |
+#define SDXC_INTERRUPT_ERROR_BIT	\
+	(SDXC_INT_RESP_ERROR |		\
+	 SDXC_INT_RESP_CRC_ERROR |	\
+	 SDXC_INT_DATA_CRC_ERROR |	\
+	 SDXC_INT_RESP_TIMEOUT |	\
+	 SDXC_INT_DATA_TIMEOUT |	\
+	 SDXC_INT_HARD_WARE_LOCKED |	\
+	 SDXC_INT_START_BIT_ERROR |	\
+	 SDXC_INT_END_BIT_ERROR)
+
+#define SDXC_INTERRUPT_DONE_BIT		\
+	(SDXC_INT_AUTO_COMMAND_DONE |	\
+	 SDXC_INT_DATA_OVER |		\
+	 SDXC_INT_COMMAND_DONE |	\
+	 SDXC_INT_VOLTAGE_CHANGE_DONE)
+
+/* status */
+#define SDXC_STATUS_FIFO_EMPTY		BIT(2)
+#define SDXC_STATUS_FIFO_FULL		BIT(3)
+#define SDXC_STATUS_CARD_PRESENT	BIT(8)
+#define SDXC_STATUS_BUSY		BIT(9)
+
+struct sunxi_mmc_clk_delay {
+	u32 output;
+	u32 sample;
+};
+
+struct sunxi_mmc_cfg {
+	u32 idma_des_size_bits;
+	u32 idma_des_shift;
+	const struct sunxi_mmc_clk_delay *clk_delays;
+
+	/* does the IP block support autocalibration? */
+	bool can_calibrate;
+
+	/* Does DATA0 needs to be masked while the clock is updated */
+	bool mask_data0;
+
+	/*
+	 * hardware only supports new timing mode, either due to lack of
+	 * a mode switch in the clock controller, or the mmc controller
+	 * is permanently configured in the new timing mode, without the
+	 * NTSR mode switch.
+	 */
+	bool needs_new_timings;
+
+	/* clock hardware can switch between old and new timing modes */
+	bool ccu_has_timings_switch;
+};
+
+struct sunxi_mmc_host {
+	struct mci_host mci;
+	struct device *dev;
+	struct clk *clk_ahb, *clk_mmc;
+	void __iomem *base;
+	int gpio_cd;
+
+	const struct sunxi_mmc_cfg *cfg;
+	u32 clkrate;
+};
+
+static inline struct sunxi_mmc_host *to_sunxi_mmc_host(struct mci_host *mci)
+{
+	return container_of(mci, struct sunxi_mmc_host, mci);
+}
+
+static inline u32 sdxc_readl(struct sunxi_mmc_host *host, u32 reg)
+{
+	return readl(host->base + reg);
+}
+
+static inline void sdxc_writel(struct sunxi_mmc_host *host, u32 reg, u32 val)
+{
+	writel(val, host->base + reg);
+}
+
+static inline int sdxc_is_fifo_empty(struct sunxi_mmc_host *host)
+{
+	return sdxc_readl(host, SDXC_REG_STAS) & SDXC_STATUS_FIFO_EMPTY;
+}
+
+static inline int sdxc_is_fifo_full(struct sunxi_mmc_host *host)
+{
+	return sdxc_readl(host, SDXC_REG_STAS) & SDXC_STATUS_FIFO_FULL;
+}
+
+static inline int sdxc_is_card_busy(struct sunxi_mmc_host *host)
+{
+	return sdxc_readl(host, SDXC_REG_STAS) & SDXC_STATUS_BUSY;
+}
+
+#ifdef __PBL__
+/*
+ * Stubs to make timeout logic below work in PBL
+ */
+#define get_time_ns()		0
+/*
+ * Use time in us as a busy counter timeout value
+ */
+#define is_timeout(s, t)	((s)++ > ((t) / 1000))
+
+#endif
+
+static inline int sdxc_xfer_complete(struct sunxi_mmc_host *host, u64 timeout, u32 flags)
+{
+	u64 start;
+	u32 rint;
+
+	start = get_time_ns();
+	do {
+		rint = sdxc_readl(host, SDXC_REG_RINTR);
+		if (rint & SDXC_INTERRUPT_ERROR_BIT) {
+			break;
+		}
+		if (rint & flags) {
+			return 0;
+		}
+	} while (!is_timeout(start, timeout));
+
+	return -ETIMEDOUT;
+}
+
+#endif
diff --git a/include/mach/sunxi/xload.h b/include/mach/sunxi/xload.h
new file mode 100644
index 0000000000..f978db4951
--- /dev/null
+++ b/include/mach/sunxi/xload.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __MACH_XLOAD_H
+#define __MACH_XLOAD_H
+
+#include <linux/compiler.h>
+#include <pbl/bio.h>
+
+int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
+		       unsigned int clock, unsigned int slot);
+
+#endif
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 09/11] arm: sunxi: Add sun50i SDRAM init
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (7 preceding siblings ...)
  2023-05-10 23:37 ` [RFC PATCH 08/11] mci: Add sunxi-mmc driver Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-11  7:39   ` Sascha Hauer
  2023-05-10 23:37 ` [RFC PATCH 10/11] arm: boards: sunxi: Add initial support for the pinephone Jules Maselbas
  2023-05-10 23:37 ` [RFC PATCH 11/11] arm: boards: sunxi: Add pine64 board Jules Maselbas
  10 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Adapted from u-boot.
---
 arch/arm/mach-sunxi/Makefile                 |    2 +
 arch/arm/mach-sunxi/clock_sun6i.h            |  540 ++++++++++
 arch/arm/mach-sunxi/ddr3_1333.c              |   85 ++
 arch/arm/mach-sunxi/dram_sunxi_dw.h          |  241 +++++
 arch/arm/mach-sunxi/lpddr3_stock.c           |   81 ++
 arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c   |    9 +
 arch/arm/mach-sunxi/sun50i-a64-lpddr3-init.c |    9 +
 arch/arm/mach-sunxi/sun50i-sdram.c           |  903 ++++++++++++++++
 arch/arm/mach-sunxi/sunxi-sdram.c            | 1007 ++++++++++++++++++
 include/mach/sunxi/init.h                    |    3 +
 10 files changed, 2880 insertions(+)
 create mode 100644 arch/arm/mach-sunxi/clock_sun6i.h
 create mode 100644 arch/arm/mach-sunxi/ddr3_1333.c
 create mode 100644 arch/arm/mach-sunxi/dram_sunxi_dw.h
 create mode 100644 arch/arm/mach-sunxi/lpddr3_stock.c
 create mode 100644 arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c
 create mode 100644 arch/arm/mach-sunxi/sun50i-a64-lpddr3-init.c
 create mode 100644 arch/arm/mach-sunxi/sun50i-sdram.c
 create mode 100644 arch/arm/mach-sunxi/sunxi-sdram.c

diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index e7fa23c832..c7d3d99707 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -1,4 +1,6 @@
 obj-y += sunxi.o
 lwl-y += cpu_init.o
+lwl-y += sun50i-a64-ddr3-init.o
+lwl-y += sun50i-a64-lpddr3-init.o
 
 pbl-$(CONFIG_CPU_64) += rmr_switch.o
diff --git a/arch/arm/mach-sunxi/clock_sun6i.h b/arch/arm/mach-sunxi/clock_sun6i.h
new file mode 100644
index 0000000000..b9893f5a77
--- /dev/null
+++ b/arch/arm/mach-sunxi/clock_sun6i.h
@@ -0,0 +1,540 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * sun6i clock register definitions
+ *
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tangliang@allwinnertech.com>
+ */
+
+#ifndef _SUNXI_CLOCK_SUN6I_H
+#define _SUNXI_CLOCK_SUN6I_H
+
+struct sunxi_ccm_reg {
+	u32 pll1_cfg;		/* 0x00 pll1 control */
+	u32 reserved0;
+	u32 pll2_cfg;		/* 0x08 pll2 control */
+	u32 reserved1;
+	u32 pll3_cfg;		/* 0x10 pll3 control */
+	u32 reserved2;
+	u32 pll4_cfg;		/* 0x18 pll4 control */
+	u32 reserved3;
+	u32 pll5_cfg;		/* 0x20 pll5 control */
+	u32 reserved4;
+	u32 pll6_cfg;		/* 0x28 pll6 control */
+	u32 reserved5;
+	u32 pll7_cfg;		/* 0x30 pll7 control */
+	u32 sata_pll_cfg;	/* 0x34 SATA pll control (R40 only) */
+	u32 pll8_cfg;		/* 0x38 pll8 control */
+	u32 reserved7;
+	u32 mipi_pll_cfg;	/* 0x40 MIPI pll control */
+	u32 pll9_cfg;		/* 0x44 pll9 control */
+	u32 pll10_cfg;		/* 0x48 pll10 control */
+	u32 pll11_cfg;		/* 0x4c pll11 (ddr1) control (A33 only) */
+	u32 cpu_axi_cfg;	/* 0x50 CPU/AXI divide ratio */
+	u32 ahb1_apb1_div;	/* 0x54 AHB1/APB1 divide ratio */
+	u32 apb2_div;		/* 0x58 APB2 divide ratio */
+	u32 axi_gate;		/* 0x5c axi module clock gating */
+	u32 ahb_gate0;		/* 0x60 ahb module clock gating 0 */
+	u32 ahb_gate1;		/* 0x64 ahb module clock gating 1 */
+	u32 apb1_gate;		/* 0x68 apb1 module clock gating */
+	u32 apb2_gate;		/* 0x6c apb2 module clock gating */
+	u32 bus_gate4;          /* 0x70 gate 4 module clock gating */
+	u8 res3[0xc];
+	u32 nand0_clk_cfg;	/* 0x80 nand0 clock control */
+	u32 nand1_clk_cfg;	/* 0x84 nand1 clock control */
+	u32 sd0_clk_cfg;	/* 0x88 sd0 clock control */
+	u32 sd1_clk_cfg;	/* 0x8c sd1 clock control */
+	u32 sd2_clk_cfg;	/* 0x90 sd2 clock control */
+	u32 sd3_clk_cfg;	/* 0x94 sd3 clock control */
+	u32 ts_clk_cfg;		/* 0x98 transport stream clock control */
+	u32 ss_clk_cfg;		/* 0x9c security system clock control */
+	u32 spi0_clk_cfg;	/* 0xa0 spi0 clock control */
+	u32 spi1_clk_cfg;	/* 0xa4 spi1 clock control */
+	u32 spi2_clk_cfg;	/* 0xa8 spi2 clock control */
+	u32 spi3_clk_cfg;	/* 0xac spi3 clock control */
+	u32 i2s0_clk_cfg;	/* 0xb0 I2S0 clock control*/
+	u32 i2s1_clk_cfg;	/* 0xb4 I2S1 clock control */
+	u32 reserved10[2];
+	u32 spdif_clk_cfg;	/* 0xc0 SPDIF clock control */
+	u32 reserved11;
+	u32 sata_clk_cfg;	/* 0xc8 SATA clock control (R40 only) */
+	u32 usb_clk_cfg;	/* 0xcc USB clock control */
+#ifdef CONFIG_MACH_SUN8I_R40
+	u32 cir0_clk_cfg;	/* 0xd0 CIR0 clock control (R40 only) */
+#else
+	u32 gmac_clk_cfg;	/* 0xd0 GMAC clock control (not for R40) */
+#endif
+	u32 reserved12[7];
+	u32 mdfs_clk_cfg;	/* 0xf0 MDFS clock control */
+	u32 dram_clk_cfg;	/* 0xf4 DRAM configuration clock control */
+	u32 dram_pll_cfg;	/* 0xf8 PLL_DDR cfg register, A33 only */
+	u32 mbus_reset;		/* 0xfc MBUS reset control, A33 only */
+	u32 dram_clk_gate;	/* 0x100 DRAM module gating */
+#ifdef CONFIG_SUNXI_DE2
+	u32 de_clk_cfg;		/* 0x104 DE module clock */
+#else
+	u32 be0_clk_cfg;	/* 0x104 BE0 module clock */
+#endif
+	u32 be1_clk_cfg;	/* 0x108 BE1 module clock */
+	u32 fe0_clk_cfg;	/* 0x10c FE0 module clock */
+	u32 fe1_clk_cfg;	/* 0x110 FE1 module clock */
+	u32 mp_clk_cfg;		/* 0x114 MP module clock */
+#ifdef CONFIG_SUNXI_DE2
+	u32 lcd0_clk_cfg;	/* 0x118 LCD0 module clock */
+	u32 lcd1_clk_cfg;	/* 0x11c LCD1 module clock */
+#else
+	u32 lcd0_ch0_clk_cfg;	/* 0x118 LCD0 CH0 module clock */
+	u32 lcd1_ch0_clk_cfg;	/* 0x11c LCD1 CH0 module clock */
+#endif
+	u32 tve_clk_cfg;	/* 0x120 H3/H5 TVE module clock */
+	u32 reserved14[2];
+	u32 lcd0_ch1_clk_cfg;	/* 0x12c LCD0 CH1 module clock */
+	u32 lcd1_ch1_clk_cfg;	/* 0x130 LCD1 CH1 module clock */
+	u32 csi0_clk_cfg;	/* 0x134 CSI0 module clock */
+	u32 csi1_clk_cfg;	/* 0x138 CSI1 module clock */
+	u32 ve_clk_cfg;		/* 0x13c VE module clock */
+	u32 adda_clk_cfg;	/* 0x140 ADDA module clock */
+	u32 avs_clk_cfg;	/* 0x144 AVS module clock */
+	u32 dmic_clk_cfg;	/* 0x148 Digital Mic module clock*/
+	u32 reserved15;
+	u32 hdmi_clk_cfg;	/* 0x150 HDMI module clock */
+#ifdef CONFIG_SUNXI_DE2
+	u32 hdmi_slow_clk_cfg;	/* 0x154 HDMI slow module clock */
+#else
+	u32 ps_clk_cfg;		/* 0x154 PS module clock */
+#endif
+	u32 mtc_clk_cfg;	/* 0x158 MTC module clock */
+	u32 mbus0_clk_cfg;	/* 0x15c MBUS0 module clock */
+	u32 mbus1_clk_cfg;	/* 0x160 MBUS1 module clock */
+#ifdef CONFIG_MACH_SUN8I_R40
+	u32 gmac_clk_cfg;	/* 0x164 GMAC clock control (R40 only) */
+#else
+	u32 reserved16;
+#endif
+	u32 mipi_dsi_clk_cfg;	/* 0x168 MIPI DSI clock control */
+	u32 mipi_csi_clk_cfg;	/* 0x16c MIPI CSI clock control */
+	u32 reserved17[4];
+	u32 iep_drc0_clk_cfg;	/* 0x180 IEP DRC0 module clock */
+	u32 iep_drc1_clk_cfg;	/* 0x184 IEP DRC1 module clock */
+	u32 iep_deu0_clk_cfg;	/* 0x188 IEP DEU0 module clock */
+	u32 iep_deu1_clk_cfg;	/* 0x18c IEP DEU1 module clock */
+	u32 reserved18[4];
+	u32 gpu_core_clk_cfg;	/* 0x1a0 GPU core clock config */
+	u32 gpu_mem_clk_cfg;	/* 0x1a4 GPU memory clock config */
+	u32 gpu_hyd_clk_cfg;	/* 0x1a0 GPU HYD clock config */
+	u32 reserved19[21];
+	u32 pll_lock;		/* 0x200 PLL Lock Time */
+	u32 pll1_lock;		/* 0x204 PLL1 Lock Time */
+	u32 reserved20[6];
+	u32 pll1_bias_cfg;	/* 0x220 PLL1 Bias config */
+	u32 pll2_bias_cfg;	/* 0x224 PLL2 Bias config */
+	u32 pll3_bias_cfg;	/* 0x228 PLL3 Bias config */
+	u32 pll4_bias_cfg;	/* 0x22c PLL4 Bias config */
+	u32 pll5_bias_cfg;	/* 0x230 PLL5 Bias config */
+	u32 pll6_bias_cfg;	/* 0x234 PLL6 Bias config */
+	u32 pll7_bias_cfg;	/* 0x238 PLL7 Bias config */
+	u32 pll8_bias_cfg;	/* 0x23c PLL8 Bias config */
+	u32 mipi_bias_cfg;	/* 0x240 MIPI Bias config */
+	u32 pll9_bias_cfg;	/* 0x244 PLL9 Bias config */
+	u32 pll10_bias_cfg;	/* 0x248 PLL10 Bias config */
+	u32 reserved21[5];
+	u32 pll5_tuning_cfg;	/* 0x260 PLL5 Tuning config */
+	u32 reserved21_5[7];
+	u32 pll1_pattern_cfg;	/* 0x280 PLL1 Pattern config */
+	u32 pll2_pattern_cfg;	/* 0x284 PLL2 Pattern config */
+	u32 pll3_pattern_cfg;	/* 0x288 PLL3 Pattern config */
+	u32 pll4_pattern_cfg;	/* 0x28c PLL4 Pattern config */
+	u32 pll5_pattern_cfg;	/* 0x290 PLL5 Pattern config */
+	u32 pll6_pattern_cfg;	/* 0x294 PLL6 Pattern config */
+	u32 pll7_pattern_cfg;	/* 0x298 PLL7 Pattern config */
+	u32 pll8_pattern_cfg;	/* 0x29c PLL8 Pattern config */
+	u32 mipi_pattern_cfg;	/* 0x2a0 MIPI Pattern config */
+	u32 pll9_pattern_cfg;	/* 0x2a4 PLL9 Pattern config */
+	u32 pll10_pattern_cfg;	/* 0x2a8 PLL10 Pattern config */
+	u32 pll11_pattern_cfg0; /* 0x2ac PLL11 Pattern config0, A33 only */
+	u32 pll11_pattern_cfg1; /* 0x2b0 PLL11 Pattern config0, A33 only */
+	u32 reserved22[3];
+	u32 ahb_reset0_cfg;	/* 0x2c0 AHB1 Reset 0 config */
+	u32 ahb_reset1_cfg;	/* 0x2c4 AHB1 Reset 1 config */
+	u32 ahb_reset2_cfg;	/* 0x2c8 AHB1 Reset 2 config */
+	u32 reserved23;
+	u32 apb1_reset_cfg;	/* 0x2d0 APB1 Reset config */
+	u32 reserved24;
+	u32 apb2_reset_cfg;	/* 0x2d8 APB2 Reset config */
+	u32 reserved25[5];
+	u32 ccu_sec_switch;	/* 0x2f0 CCU Security Switch, H3 only */
+	u32 reserved26[11];
+	u32 pll_lock_ctrl;	/* 0x320 PLL lock control, R40 only */
+};
+
+/* apb2 bit field */
+#define APB2_CLK_SRC_LOSC		(0x0 << 24)
+#define APB2_CLK_SRC_OSC24M		(0x1 << 24)
+#define APB2_CLK_SRC_PLL6		(0x2 << 24)
+#define APB2_CLK_SRC_MASK		(0x3 << 24)
+#define APB2_CLK_RATE_N_1		(0x0 << 16)
+#define APB2_CLK_RATE_N_2		(0x1 << 16)
+#define APB2_CLK_RATE_N_4		(0x2 << 16)
+#define APB2_CLK_RATE_N_8		(0x3 << 16)
+#define APB2_CLK_RATE_N_MASK		(3 << 16)
+#define APB2_CLK_RATE_M(m)		(((m)-1) << 0)
+#define APB2_CLK_RATE_M_MASK            (0x1f << 0)
+
+/* apb2 gate field */
+#define APB2_GATE_UART_SHIFT	(16)
+#define APB2_GATE_UART_MASK		(0xff << APB2_GATE_UART_SHIFT)
+#define APB2_GATE_TWI_SHIFT	(0)
+#define APB2_GATE_TWI_MASK		(0xf << APB2_GATE_TWI_SHIFT)
+
+/* cpu_axi_cfg bits */
+#define AXI_DIV_SHIFT			0
+#define ATB_DIV_SHIFT			8
+#define CPU_CLK_SRC_SHIFT		16
+
+#define AXI_DIV_1			0
+#define AXI_DIV_2			1
+#define AXI_DIV_3			2
+#define AXI_DIV_4			3
+#define ATB_DIV_1			0
+#define ATB_DIV_2			1
+#define ATB_DIV_4			2
+#define AHB_DIV_1			0
+#define CPU_CLK_SRC_OSC24M		1
+#define CPU_CLK_SRC_PLL1		2
+
+#define CCM_PLL1_CTRL_M(n)		((((n) - 1) & 0x3) << 0)
+#define CCM_PLL1_CTRL_K(n)		((((n) - 1) & 0x3) << 4)
+#define CCM_PLL1_CTRL_N(n)		((((n) - 1) & 0x1f) << 8)
+#define CCM_PLL1_CTRL_P(n)		(((n) & 0x3) << 16)
+#define CCM_PLL1_CTRL_EN		(0x1 << 31)
+#define CCM_PLL1_CTRL_LOCK		(0x1 << 28)
+
+#define CCM_PLL3_CTRL_M_SHIFT		0
+#define CCM_PLL3_CTRL_M_MASK		(0xf << CCM_PLL3_CTRL_M_SHIFT)
+#define CCM_PLL3_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+#define CCM_PLL3_CTRL_N_SHIFT		8
+#define CCM_PLL3_CTRL_N_MASK		(0x7f << CCM_PLL3_CTRL_N_SHIFT)
+#define CCM_PLL3_CTRL_N(n)		((((n) - 1) & 0x7f) << 8)
+#define CCM_PLL3_CTRL_INTEGER_MODE	(0x1 << 24)
+#define CCM_PLL3_CTRL_LOCK		(0x1 << 28)
+#define CCM_PLL3_CTRL_EN		(0x1 << 31)
+
+#define CCM_PLL5_CTRL_M(n)		((((n) - 1) & 0x3) << 0)
+#define CCM_PLL5_CTRL_K(n)		((((n) - 1) & 0x3) << 4)
+#define CCM_PLL5_CTRL_N(n)		((((n) - 1) & 0x1f) << 8)
+#define CCM_PLL5_CTRL_UPD		(0x1 << 20)
+#define CCM_PLL5_CTRL_SIGMA_DELTA_EN	(0x1 << 24)
+#define CCM_PLL5_CTRL_EN		(0x1 << 31)
+
+#define PLL6_CFG_DEFAULT		0x90041811 /* 600 MHz */
+
+#define CCM_PLL6_CTRL_N_SHIFT		8
+#define CCM_PLL6_CTRL_N_MASK		(0x1f << CCM_PLL6_CTRL_N_SHIFT)
+#define CCM_PLL6_CTRL_K_SHIFT		4
+#define CCM_PLL6_CTRL_K_MASK		(0x3 << CCM_PLL6_CTRL_K_SHIFT)
+#define CCM_PLL6_CTRL_LOCK		(1 << 28)
+
+#define CCM_SATA_PLL_DEFAULT		0x90005811 /* 100 MHz */
+
+#define CCM_MIPI_PLL_CTRL_M_SHIFT	0
+#define CCM_MIPI_PLL_CTRL_M_MASK	(0xf << CCM_MIPI_PLL_CTRL_M_SHIFT)
+#define CCM_MIPI_PLL_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+#define CCM_MIPI_PLL_CTRL_K_SHIFT	4
+#define CCM_MIPI_PLL_CTRL_K_MASK	(0x3 << CCM_MIPI_PLL_CTRL_K_SHIFT)
+#define CCM_MIPI_PLL_CTRL_K(n)		((((n) - 1) & 0x3) << 4)
+#define CCM_MIPI_PLL_CTRL_N_SHIFT	8
+#define CCM_MIPI_PLL_CTRL_N_MASK	(0xf << CCM_MIPI_PLL_CTRL_N_SHIFT)
+#define CCM_MIPI_PLL_CTRL_N(n)		((((n) - 1) & 0xf) << 8)
+#define CCM_MIPI_PLL_CTRL_LDO_EN	(0x3 << 22)
+#define CCM_MIPI_PLL_CTRL_EN		(0x1 << 31)
+
+#define CCM_PLL10_CTRL_M_SHIFT		0
+#define CCM_PLL10_CTRL_M_MASK		(0xf << CCM_PLL10_CTRL_M_SHIFT)
+#define CCM_PLL10_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+#define CCM_PLL10_CTRL_N_SHIFT		8
+#define CCM_PLL10_CTRL_N_MASK		(0x7f << CCM_PLL10_CTRL_N_SHIFT)
+#define CCM_PLL10_CTRL_N(n)		((((n) - 1) & 0x7f) << 8)
+#define CCM_PLL10_CTRL_INTEGER_MODE	(0x1 << 24)
+#define CCM_PLL10_CTRL_LOCK		(0x1 << 28)
+#define CCM_PLL10_CTRL_EN		(0x1 << 31)
+
+#define CCM_PLL11_CTRL_N(n)		((((n) - 1) & 0x3f) << 8)
+#define CCM_PLL11_CTRL_SIGMA_DELTA_EN	(0x1 << 24)
+#define CCM_PLL11_CTRL_UPD		(0x1 << 30)
+#define CCM_PLL11_CTRL_EN		(0x1 << 31)
+
+#define CCM_PLL5_TUN_LOCK_TIME(x)	(((x) & 0x7) << 24)
+#define CCM_PLL5_TUN_LOCK_TIME_MASK	CCM_PLL5_TUN_LOCK_TIME(0x7)
+#define CCM_PLL5_TUN_INIT_FREQ(x)	(((x) & 0x7f) << 16)
+#define CCM_PLL5_TUN_INIT_FREQ_MASK	CCM_PLL5_TUN_INIT_FREQ(0x7f)
+
+#if defined(CONFIG_MACH_SUN50I)
+/* AHB1=100MHz failsafe setup from the FEL mode, usable with PMIC defaults */
+#define AHB1_ABP1_DIV_DEFAULT		0x00003190 /* AHB1=PLL6/6,APB1=AHB1/2 */
+#else
+#define AHB1_ABP1_DIV_DEFAULT		0x00003180 /* AHB1=PLL6/3,APB1=AHB1/2 */
+#endif
+
+#define AXI_GATE_OFFSET_DRAM		0
+
+/* ahb_gate0 offsets */
+#ifdef CONFIG_MACH_SUNXI_H3_H5
+/*
+ * These are EHCI1 - EHCI3 in the datasheet (EHCI0 is for the OTG) we call
+ * them 0 - 2 like they were called on older SoCs.
+ */
+#define AHB_GATE_OFFSET_USB_OHCI3	31
+#define AHB_GATE_OFFSET_USB_OHCI2	30
+#define AHB_GATE_OFFSET_USB_OHCI1	29
+#define AHB_GATE_OFFSET_USB_OHCI0	28
+#define AHB_GATE_OFFSET_USB_EHCI3	27
+#define AHB_GATE_OFFSET_USB_EHCI2	26
+#define AHB_GATE_OFFSET_USB_EHCI1	25
+#define AHB_GATE_OFFSET_USB_EHCI0	24
+#elif defined(CONFIG_MACH_SUN50I)
+#define AHB_GATE_OFFSET_USB_OHCI0	28
+#define AHB_GATE_OFFSET_USB_OHCI1	29
+#define AHB_GATE_OFFSET_USB_EHCI0	24
+#define AHB_GATE_OFFSET_USB_EHCI1	25
+#else
+#define AHB_GATE_OFFSET_USB_OHCI1	30
+#define AHB_GATE_OFFSET_USB_OHCI0	29
+#define AHB_GATE_OFFSET_USB_EHCI1	27
+#define AHB_GATE_OFFSET_USB_EHCI0	26
+#endif
+#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUNXI_H3_H5)
+#define AHB_GATE_OFFSET_USB0		23
+#elif !defined(CONFIG_MACH_SUN8I_R40)
+#define AHB_GATE_OFFSET_USB0		24
+#else
+#define AHB_GATE_OFFSET_USB0		25
+#define AHB_GATE_OFFSET_SATA		24
+#endif
+#define AHB_GATE_OFFSET_MCTL		14
+#define AHB_GATE_OFFSET_GMAC		17
+#define AHB_GATE_OFFSET_NAND0		13
+#define AHB_GATE_OFFSET_NAND1		12
+#define AHB_GATE_OFFSET_MMC3		11
+#define AHB_GATE_OFFSET_MMC2		10
+#define AHB_GATE_OFFSET_MMC1		9
+#define AHB_GATE_OFFSET_MMC0		8
+#define AHB_GATE_OFFSET_MMC(n)		(AHB_GATE_OFFSET_MMC0 + (n))
+#define AHB_GATE_OFFSET_DMA		6
+#define AHB_GATE_OFFSET_SS		5
+
+/* ahb_gate1 offsets */
+#define AHB_GATE_OFFSET_DRC0		25
+#define AHB_GATE_OFFSET_DE_FE0		14
+#define AHB_GATE_OFFSET_DE_BE0		12
+#define AHB_GATE_OFFSET_DE		12
+#define AHB_GATE_OFFSET_HDMI		11
+#define AHB_GATE_OFFSET_TVE		9
+#ifndef CONFIG_SUNXI_DE2
+#define AHB_GATE_OFFSET_LCD1		5
+#define AHB_GATE_OFFSET_LCD0		4
+#else
+#define AHB_GATE_OFFSET_LCD1		4
+#define AHB_GATE_OFFSET_LCD0		3
+#endif
+
+#define CCM_NAND_CTRL_M(x)		((x) - 1)
+#define CCM_NAND_CTRL_N(x)		((x) << 16)
+#define CCM_NAND_CTRL_PLL6		(0x1 << 24)
+#define CCM_NAND_CTRL_ENABLE		(0x1 << 31)
+
+#define CCM_MMC_CTRL_M(x)		((x) - 1)
+#define CCM_MMC_CTRL_OCLK_DLY(x)	((x) << 8)
+#define CCM_MMC_CTRL_N(x)		((x) << 16)
+#define CCM_MMC_CTRL_SCLK_DLY(x)	((x) << 20)
+#define CCM_MMC_CTRL_OSCM24		(0x0 << 24)
+#define CCM_MMC_CTRL_PLL6		(0x1 << 24)
+#define CCM_MMC_CTRL_PLL6X2		(0x1 << 24) // A64 has PLL_PERIPH0(2X)
+#define CCM_MMC_CTRL_ENABLE		(0x1 << 31)
+
+#define CCM_SATA_CTRL_ENABLE		(0x1 << 31)
+#define CCM_SATA_CTRL_USE_EXTCLK	(0x1 << 24)
+
+#define CCM_USB_CTRL_PHY0_RST (0x1 << 0)
+#define CCM_USB_CTRL_PHY1_RST (0x1 << 1)
+#define CCM_USB_CTRL_PHY2_RST (0x1 << 2)
+#define CCM_USB_CTRL_PHY3_RST (0x1 << 3)
+/* There is no global phy clk gate on sun6i, define as 0 */
+#define CCM_USB_CTRL_PHYGATE 0
+#define CCM_USB_CTRL_PHY0_CLK (0x1 << 8)
+#define CCM_USB_CTRL_PHY1_CLK (0x1 << 9)
+#define CCM_USB_CTRL_PHY2_CLK (0x1 << 10)
+#define CCM_USB_CTRL_PHY3_CLK (0x1 << 11)
+#ifdef CONFIG_MACH_SUNXI_H3_H5
+#define CCM_USB_CTRL_OHCI0_CLK (0x1 << 16)
+#define CCM_USB_CTRL_OHCI1_CLK (0x1 << 17)
+#define CCM_USB_CTRL_OHCI2_CLK (0x1 << 18)
+#define CCM_USB_CTRL_OHCI3_CLK (0x1 << 19)
+#else
+#define CCM_USB_CTRL_OHCI0_CLK (0x1 << 16)
+#define CCM_USB_CTRL_OHCI1_CLK (0x1 << 17)
+#endif
+
+#define CCM_GMAC_CTRL_TX_CLK_SRC_MII	0x0
+#define CCM_GMAC_CTRL_TX_CLK_SRC_EXT_RGMII 0x1
+#define CCM_GMAC_CTRL_TX_CLK_SRC_INT_RGMII 0x2
+#define CCM_GMAC_CTRL_GPIT_MII		(0x0 << 2)
+#define CCM_GMAC_CTRL_GPIT_RGMII	(0x1 << 2)
+#define CCM_GMAC_CTRL_RX_CLK_DELAY(x)	((x) << 5)
+#define CCM_GMAC_CTRL_TX_CLK_DELAY(x)	((x) << 10)
+
+#define MDFS_CLK_DEFAULT		0x81000002 /* PLL6 / 3 */
+
+#define CCM_DRAMCLK_CFG_DIV(x)		((x - 1) << 0)
+#define CCM_DRAMCLK_CFG_DIV_MASK	(0xf << 0)
+#define CCM_DRAMCLK_CFG_DIV0(x)		((x - 1) << 8)
+#define CCM_DRAMCLK_CFG_DIV0_MASK	(0xf << 8)
+#define CCM_DRAMCLK_CFG_SRC_PLL5	(0x0 << 20)
+#define CCM_DRAMCLK_CFG_SRC_PLL6x2	(0x1 << 20)
+#define CCM_DRAMCLK_CFG_SRC_PLL11	(0x1 << 20) /* A64 only */
+#define CCM_DRAMCLK_CFG_SRC_MASK	(0x3 << 20)
+#define CCM_DRAMCLK_CFG_UPD		(0x1 << 16)
+#define CCM_DRAMCLK_CFG_RST		(0x1 << 31)
+
+#define CCM_DRAMPLL_CFG_SRC_PLL5	(0x0 << 16) /* Select PLL5 (DDR0) */
+#define CCM_DRAMPLL_CFG_SRC_PLL11	(0x1 << 16) /* Select PLL11 (DDR1) */
+#define CCM_DRAMPLL_CFG_SRC_MASK	(0x1 << 16)
+
+#define CCM_MBUS_RESET_RESET		(0x1 << 31)
+
+#define CCM_DRAM_GATE_OFFSET_DE_FE0	24
+#define CCM_DRAM_GATE_OFFSET_DE_FE1	25
+#define CCM_DRAM_GATE_OFFSET_DE_BE0	26
+#define CCM_DRAM_GATE_OFFSET_DE_BE1	27
+
+#ifdef CONFIG_MACH_SUN50I
+#define CCM_LCD_CH0_CTRL_PLL3_2X	(2 << 24)
+#define CCM_LCD_CH0_CTRL_MIPI_PLL	(0 << 24)
+#else
+#define CCM_LCD_CH0_CTRL_PLL3		(0 << 24)
+#define CCM_LCD_CH0_CTRL_PLL7		(1 << 24)
+#define CCM_LCD_CH0_CTRL_PLL3_2X	(2 << 24)
+#define CCM_LCD_CH0_CTRL_PLL7_2X	(3 << 24)
+#define CCM_LCD_CH0_CTRL_MIPI_PLL	(4 << 24)
+#endif
+/* No reset bit in ch0_clk_cfg (reset is controlled through ahb_reset1) */
+#define CCM_LCD_CH0_CTRL_RST		0
+#define CCM_LCD_CH0_CTRL_GATE		(0x1 << 31)
+
+#define CCM_LCD_CH1_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+#define CCM_LCD_CH1_CTRL_HALF_SCLK1	0 /* no seperate sclk1 & 2 on sun6i */
+#define CCM_LCD_CH1_CTRL_PLL3		(0 << 24)
+#define CCM_LCD_CH1_CTRL_PLL7		(1 << 24)
+#define CCM_LCD_CH1_CTRL_PLL3_2X	(2 << 24)
+#define CCM_LCD_CH1_CTRL_PLL7_2X	(3 << 24)
+#define CCM_LCD_CH1_CTRL_GATE		(0x1 << 31)
+
+#define CCM_LCD0_CTRL_GATE		(0x1 << 31)
+#define CCM_LCD0_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+
+#define CCM_LCD1_CTRL_GATE		(0x1 << 31)
+#define CCM_LCD1_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+
+#define CCM_HDMI_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+#define CCM_HDMI_CTRL_PLL_MASK		(3 << 24)
+#define CCM_HDMI_CTRL_PLL3		(0 << 24)
+#define CCM_HDMI_CTRL_PLL7		(1 << 24)
+#define CCM_HDMI_CTRL_PLL3_2X		(2 << 24)
+#define CCM_HDMI_CTRL_PLL7_2X		(3 << 24)
+#define CCM_HDMI_CTRL_DDC_GATE		(0x1 << 30)
+#define CCM_HDMI_CTRL_GATE		(0x1 << 31)
+
+#define CCM_HDMI_SLOW_CTRL_DDC_GATE	(1 << 31)
+
+#define CCM_TVE_CTRL_GATE		(0x1 << 31)
+#define CCM_TVE_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+
+#if defined(CONFIG_MACH_SUN50I)
+#define MBUS_CLK_DEFAULT		0x81000002 /* PLL6x2 / 3 */
+#elif defined(CONFIG_MACH_SUN8I)
+#define MBUS_CLK_DEFAULT		0x81000003 /* PLL6 / 4 */
+#else
+#define MBUS_CLK_DEFAULT		0x81000001 /* PLL6 / 2 */
+#endif
+#define MBUS_CLK_GATE			(0x1 << 31)
+
+#define CCM_PLL5_PATTERN		0xd1303333
+#define CCM_PLL11_PATTERN		0xf5860000
+
+/* ahb_reset0 offsets */
+#ifdef CONFIG_MACH_SUN8I_R40
+#define AHB_RESET_OFFSET_SATA		24
+#endif
+#define AHB_RESET_OFFSET_GMAC		17
+#define AHB_RESET_OFFSET_MCTL		14
+#define AHB_RESET_OFFSET_MMC3		11
+#define AHB_RESET_OFFSET_MMC2		10
+#define AHB_RESET_OFFSET_MMC1		9
+#define AHB_RESET_OFFSET_MMC0		8
+#define AHB_RESET_OFFSET_MMC(n)		(AHB_RESET_OFFSET_MMC0 + (n))
+#define AHB_RESET_OFFSET_SS		5
+
+/* ahb_reset1 offsets */
+#define AHB_RESET_OFFSET_SAT		26
+#define AHB_RESET_OFFSET_DRC0		25
+#define AHB_RESET_OFFSET_DE_FE0		14
+#define AHB_RESET_OFFSET_DE_BE0		12
+#define AHB_RESET_OFFSET_DE		12
+#define AHB_RESET_OFFSET_HDMI		11
+#define AHB_RESET_OFFSET_HDMI2		10
+#define AHB_RESET_OFFSET_TVE		9
+#ifndef CONFIG_SUNXI_DE2
+#define AHB_RESET_OFFSET_LCD1		5
+#define AHB_RESET_OFFSET_LCD0		4
+#else
+#define AHB_RESET_OFFSET_LCD1		4
+#define AHB_RESET_OFFSET_LCD0		3
+#endif
+
+/* ahb_reset2 offsets */
+#define AHB_RESET_OFFSET_EPHY		2
+#define AHB_RESET_OFFSET_LVDS		0
+
+/* apb2 reset */
+#define APB2_RESET_UART_SHIFT		(16)
+#define APB2_RESET_UART_MASK		(0xff << APB2_RESET_UART_SHIFT)
+#define APB2_RESET_TWI_SHIFT		(0)
+#define APB2_RESET_TWI_MASK		(0xf << APB2_RESET_TWI_SHIFT)
+
+/* CCM bits common to all Display Engine (and IEP) clock ctrl regs */
+#define CCM_DE_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+#define CCM_DE_CTRL_PLL_MASK		(0xf << 24)
+#define CCM_DE_CTRL_PLL3		(0 << 24)
+#define CCM_DE_CTRL_PLL7		(1 << 24)
+#define CCM_DE_CTRL_PLL6_2X		(2 << 24)
+#define CCM_DE_CTRL_PLL8		(3 << 24)
+#define CCM_DE_CTRL_PLL9		(4 << 24)
+#define CCM_DE_CTRL_PLL10		(5 << 24)
+#define CCM_DE_CTRL_GATE		(1 << 31)
+
+/* CCM bits common to all Display Engine 2.0 clock ctrl regs */
+#define CCM_DE2_CTRL_M(n)		((((n) - 1) & 0xf) << 0)
+#define CCM_DE2_CTRL_PLL_MASK		(3 << 24)
+#define CCM_DE2_CTRL_PLL6_2X		(0 << 24)
+#define CCM_DE2_CTRL_PLL10		(1 << 24)
+#define CCM_DE2_CTRL_GATE		(0x1 << 31)
+
+/* CCU security switch, H3 only */
+#define CCM_SEC_SWITCH_MBUS_NONSEC	(1 << 2)
+#define CCM_SEC_SWITCH_BUS_NONSEC	(1 << 1)
+#define CCM_SEC_SWITCH_PLL_NONSEC	(1 << 0)
+
+#if 0
+void clock_set_pll1(unsigned int hz);
+void clock_set_pll3(unsigned int hz);
+void clock_set_pll3_factors(int m, int n);
+void clock_set_pll5(unsigned int clk, bool sigma_delta_enable);
+void clock_set_pll_de(unsigned int hz);
+void clock_set_pll11(unsigned int clk, bool sigma_delta_enable);
+void clock_set_mipi_pll(unsigned int hz);
+unsigned int clock_get_pll3(void);
+unsigned int clock_get_pll6(void);
+unsigned int clock_get_mipi_pll(void);
+#endif
+
+#endif /* _SUNXI_CLOCK_SUN6I_H */
diff --git a/arch/arm/mach-sunxi/ddr3_1333.c b/arch/arm/mach-sunxi/ddr3_1333.c
new file mode 100644
index 0000000000..2693b263e2
--- /dev/null
+++ b/arch/arm/mach-sunxi/ddr3_1333.c
@@ -0,0 +1,85 @@
+#include <common.h>
+
+static void mctl_set_timing_params(uint16_t socid, struct dram_para *para)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	u8 tccd		= 2;
+	u8 tfaw		= ns_to_t(50);
+	u8 trrd		= max(ns_to_t(10), 4);
+	u8 trcd		= ns_to_t(15);
+	u8 trc		= ns_to_t(53);
+	u8 txp		= max(ns_to_t(8), 3);
+	u8 twtr		= max(ns_to_t(8), 4);
+	u8 trtp		= max(ns_to_t(8), 4);
+	u8 twr		= max(ns_to_t(15), 3);
+	u8 trp		= ns_to_t(15);
+	u8 tras		= ns_to_t(38);
+	u16 trefi	= ns_to_t(7800) / 32;
+	u16 trfc	= ns_to_t(350);
+
+	u8 tmrw		= 0;
+	u8 tmrd		= 4;
+	u8 tmod		= 12;
+	u8 tcke		= 3;
+	u8 tcksrx	= 5;
+	u8 tcksre	= 5;
+	u8 tckesr	= 4;
+	u8 trasmax	= 24;
+
+	u8 tcl		= 6; /* CL 12 */
+	u8 tcwl		= 4; /* CWL 8 */
+	u8 t_rdata_en	= 4;
+	u8 wr_latency	= 2;
+
+	u32 tdinit0	= (500 * CONFIG_DRAM_CLK) + 1;		/* 500us */
+	u32 tdinit1	= (360 * CONFIG_DRAM_CLK) / 1000 + 1;	/* 360ns */
+	u32 tdinit2	= (200 * CONFIG_DRAM_CLK) + 1;		/* 200us */
+	u32 tdinit3	= (1 * CONFIG_DRAM_CLK) + 1;		/* 1us */
+
+	u8 twtp		= tcwl + 2 + twr;	/* WL + BL / 2 + tWR */
+	u8 twr2rd	= tcwl + 2 + twtr;	/* WL + BL / 2 + tWTR */
+	u8 trd2wr	= tcl + 2 + 1 - tcwl;	/* RL + BL / 2 + 2 - WL */
+
+	/* set mode register */
+	writel(0x1c70, &mctl_ctl->mr[0]);	/* CL=11, WR=12 */
+	writel(0x40, &mctl_ctl->mr[1]);
+	writel(0x18, &mctl_ctl->mr[2]);		/* CWL=8 */
+	writel(0x0, &mctl_ctl->mr[3]);
+
+	if (socid == SOCID_R40)
+		writel(0x3, &mctl_ctl->lp3mr11);	/* odt_en[7:4] */
+
+	/* set DRAM timing */
+	writel(DRAMTMG0_TWTP(twtp) | DRAMTMG0_TFAW(tfaw) |
+	       DRAMTMG0_TRAS_MAX(trasmax) | DRAMTMG0_TRAS(tras),
+	       &mctl_ctl->dramtmg[0]);
+	writel(DRAMTMG1_TXP(txp) | DRAMTMG1_TRTP(trtp) | DRAMTMG1_TRC(trc),
+	       &mctl_ctl->dramtmg[1]);
+	writel(DRAMTMG2_TCWL(tcwl) | DRAMTMG2_TCL(tcl) |
+	       DRAMTMG2_TRD2WR(trd2wr) | DRAMTMG2_TWR2RD(twr2rd),
+	       &mctl_ctl->dramtmg[2]);
+	writel(DRAMTMG3_TMRW(tmrw) | DRAMTMG3_TMRD(tmrd) | DRAMTMG3_TMOD(tmod),
+	       &mctl_ctl->dramtmg[3]);
+	writel(DRAMTMG4_TRCD(trcd) | DRAMTMG4_TCCD(tccd) | DRAMTMG4_TRRD(trrd) |
+	       DRAMTMG4_TRP(trp), &mctl_ctl->dramtmg[4]);
+	writel(DRAMTMG5_TCKSRX(tcksrx) | DRAMTMG5_TCKSRE(tcksre) |
+	       DRAMTMG5_TCKESR(tckesr) | DRAMTMG5_TCKE(tcke),
+	       &mctl_ctl->dramtmg[5]);
+
+	/* set two rank timing */
+	clrsetbits_le32(&mctl_ctl->dramtmg[8], (0xff << 8) | (0xff << 0),
+			((socid == SOCID_H5 ? 0x33 : 0x66) << 8) | (0x10 << 0));
+
+	/* set PHY interface timing, write latency and read latency configure */
+	writel((0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) |
+	       (wr_latency << 0), &mctl_ctl->pitmg[0]);
+
+	/* set PHY timing, PTR0-2 use default */
+	writel(PTR3_TDINIT0(tdinit0) | PTR3_TDINIT1(tdinit1), &mctl_ctl->ptr[3]);
+	writel(PTR4_TDINIT2(tdinit2) | PTR4_TDINIT3(tdinit3), &mctl_ctl->ptr[4]);
+
+	/* set refresh timing */
+	writel(RFSHTMG_TREFI(trefi) | RFSHTMG_TRFC(trfc), &mctl_ctl->rfshtmg);
+}
diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.h b/arch/arm/mach-sunxi/dram_sunxi_dw.h
new file mode 100644
index 0000000000..146938460e
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_sunxi_dw.h
@@ -0,0 +1,241 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * sun8i H3 platform dram controller register and constant defines
+ *
+ * (C) Copyright 2007-2015 Allwinner Technology Co.
+ *                         Jerry Wang <wangflord@allwinnertech.com>
+ * (C) Copyright 2015      Vishnu Patekar <vishnupatekar0510@gmail.com>
+ * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
+ * (C) Copyright 2015      Jens Kuske <jenskuske@gmail.com>
+ */
+
+#ifndef _SUNXI_DRAM_SUN8I_H3_H
+#define _SUNXI_DRAM_SUN8I_H3_H
+
+#include <linux/bitops.h>
+
+struct sunxi_mctl_com_reg {
+	u32 cr;			/* 0x00 control register */
+	u32 cr_r1;		/* 0x04 rank 1 control register (R40 only) */
+	u8 res0[0x4];		/* 0x08 */
+	u32 tmr;		/* 0x0c (unused on H3) */
+	u32 mcr[16][2];		/* 0x10 */
+	u32 bwcr;		/* 0x90 bandwidth control register */
+	u32 maer;		/* 0x94 master enable register */
+	u32 mapr;		/* 0x98 master priority register */
+	u32 mcgcr;		/* 0x9c */
+	u32 cpu_bwcr;		/* 0xa0 */
+	u32 gpu_bwcr;		/* 0xa4 */
+	u32 ve_bwcr;		/* 0xa8 */
+	u32 disp_bwcr;		/* 0xac */
+	u32 other_bwcr;		/* 0xb0 */
+	u32 total_bwcr;		/* 0xb4 */
+	u8 res1[0x8];		/* 0xb8 */
+	u32 swonr;		/* 0xc0 */
+	u32 swoffr;		/* 0xc4 */
+	u8 res2[0x8];		/* 0xc8 */
+	u32 cccr;		/* 0xd0 */
+	u8 res3[0x54];		/* 0xd4 */
+	u32 mdfs_bwlr[3];	/* 0x128 (unused on H3) */
+	u8 res4[0x6cc];		/* 0x134 */
+	u32 protect;		/* 0x800 */
+};
+
+#define MCTL_CR_BL8		(0x4 << 20)
+
+#define MCTL_CR_1T		(0x1 << 19)
+#define MCTL_CR_2T		(0x0 << 19)
+
+#define MCTL_CR_LPDDR3		(0x7 << 16)
+#define MCTL_CR_LPDDR2		(0x6 << 16)
+#define MCTL_CR_DDR3		(0x3 << 16)
+#define MCTL_CR_DDR2		(0x2 << 16)
+
+#define MCTL_CR_SEQUENTIAL	(0x1 << 15)
+#define MCTL_CR_INTERLEAVED	(0x0 << 15)
+
+#define MCTL_CR_FULL_WIDTH	(0x1 << 12)
+#define MCTL_CR_HALF_WIDTH	(0x0 << 12)
+#define MCTL_CR_BUS_FULL_WIDTH(x)	((x) << 12)
+
+#define MCTL_CR_PAGE_SIZE(x)	((fls(x) - 4) << 8)
+#define MCTL_CR_ROW_BITS(x)	(((x) - 1) << 4)
+#define MCTL_CR_EIGHT_BANKS	(0x1 << 2)
+#define MCTL_CR_FOUR_BANKS	(0x0 << 2)
+#define MCTL_CR_DUAL_RANK	(0x1 << 0)
+#define MCTL_CR_SINGLE_RANK	(0x0 << 0)
+
+/*
+ * CR_R1 is a register found in the R40's DRAM controller. It sets various
+ * parameters for rank 1. Bits [11:0] have the same meaning as the bits in
+ * MCTL_CR, but they apply to rank 1 only. This implies we can have
+ * different chips for rank 1 than rank 0.
+ *
+ * As address line A15 and CS1 chip select for rank 1 are muxed on the same
+ * pin, if single rank is used, A15 must be muxed in.
+ */
+#define MCTL_CR_R1_MUX_A15	(0x1 << 21)
+
+#define PROTECT_MAGIC		(0x94be6fa3)
+
+struct sunxi_mctl_ctl_reg {
+	u32 pir;		/* 0x00 PHY initialization register */
+	u32 pwrctl;		/* 0x04 */
+	u32 mrctrl;		/* 0x08 */
+	u32 clken;		/* 0x0c */
+	u32 pgsr[2];		/* 0x10 PHY general status registers */
+	u32 statr;		/* 0x18 */
+	u8 res1[0x10];		/* 0x1c */
+	u32 lp3mr11;		/* 0x2c */
+	u32 mr[4];		/* 0x30 mode registers */
+	u32 pllgcr;		/* 0x40 */
+	u32 ptr[5];		/* 0x44 PHY timing registers */
+	u32 dramtmg[9];		/* 0x58 DRAM timing registers */
+	u32 odtcfg;		/* 0x7c */
+	u32 pitmg[2];		/* 0x80 PHY interface timing registers */
+	u8 res2[0x4];		/* 0x88 */
+	u32 rfshctl0;		/* 0x8c */
+	u32 rfshtmg;		/* 0x90 refresh timing */
+	u32 rfshctl1;		/* 0x94 */
+	u32 pwrtmg;		/* 0x98 */
+	u8 res3[0x1c];		/* 0x9c */
+	u32 vtfcr;		/* 0xb8 (unused on H3) */
+	u32 dqsgmr;		/* 0xbc */
+	u32 dtcr;		/* 0xc0 */
+	u32 dtar[4];		/* 0xc4 */
+	u32 dtdr[2];		/* 0xd4 */
+	u32 dtmr[2];		/* 0xdc */
+	u32 dtbmr;		/* 0xe4 */
+	u32 catr[2];		/* 0xe8 */
+	u32 dtedr[2];		/* 0xf0 */
+	u8 res4[0x8];		/* 0xf8 */
+	u32 pgcr[4];		/* 0x100 PHY general configuration registers */
+	u32 iovcr[2];		/* 0x110 */
+	u32 dqsdr;		/* 0x118 */
+	u32 dxccr;		/* 0x11c */
+	u32 odtmap;		/* 0x120 */
+	u32 zqctl[2];		/* 0x124 */
+	u8 res6[0x14];		/* 0x12c */
+	u32 zqcr;		/* 0x140 ZQ control register */
+	u32 zqsr;		/* 0x144 ZQ status register */
+	u32 zqdr[3];		/* 0x148 ZQ data registers */
+	u8 res7[0x6c];		/* 0x154 */
+	u32 sched;		/* 0x1c0 */
+	u32 perfhpr[2];		/* 0x1c4 */
+	u32 perflpr[2];		/* 0x1cc */
+	u32 perfwr[2];		/* 0x1d4 */
+	u8 res8[0x24];		/* 0x1dc */
+	u32 acmdlr;		/* 0x200 AC master delay line register */
+	u32 aclcdlr;		/* 0x204 AC local calibrated delay line register */
+	u32 aciocr;		/* 0x208 AC I/O configuration register */
+	u8 res9[0x4];		/* 0x20c */
+	u32 acbdlr[31];		/* 0x210 AC bit delay line registers */
+	u8 res10[0x74];		/* 0x28c */
+	struct {		/* 0x300 DATX8 modules*/
+		u32 mdlr;		/* 0x00 master delay line register */
+		u32 lcdlr[3];		/* 0x04 local calibrated delay line registers */
+		u32 bdlr[11];		/* 0x10 bit delay line registers */
+		u32 sdlr;		/* 0x3c output enable bit delay registers */
+		u32 gtr;		/* 0x40 general timing register */
+		u32 gcr;		/* 0x44 general configuration register */
+		u32 gsr[3];		/* 0x48 general status registers */
+		u8 res0[0x2c];		/* 0x54 */
+	} dx[4];
+	u8 res11[0x388];	/* 0x500 */
+	u32 upd2;		/* 0x888 */
+};
+
+#define PTR3_TDINIT1(x)		((x) << 20)
+#define PTR3_TDINIT0(x)		((x) <<  0)
+
+#define PTR4_TDINIT3(x)		((x) << 20)
+#define PTR4_TDINIT2(x)		((x) <<  0)
+
+#define DRAMTMG0_TWTP(x)	((x) << 24)
+#define DRAMTMG0_TFAW(x)	((x) << 16)
+#define DRAMTMG0_TRAS_MAX(x)	((x) <<  8)
+#define DRAMTMG0_TRAS(x)	((x) <<  0)
+
+#define DRAMTMG1_TXP(x)		((x) << 16)
+#define DRAMTMG1_TRTP(x)	((x) <<  8)
+#define DRAMTMG1_TRC(x)		((x) <<  0)
+
+#define DRAMTMG2_TCWL(x)	((x) << 24)
+#define DRAMTMG2_TCL(x)		((x) << 16)
+#define DRAMTMG2_TRD2WR(x)	((x) <<  8)
+#define DRAMTMG2_TWR2RD(x)	((x) <<  0)
+
+#define DRAMTMG3_TMRW(x)	((x) << 16)
+#define DRAMTMG3_TMRD(x)	((x) << 12)
+#define DRAMTMG3_TMOD(x)	((x) <<  0)
+
+#define DRAMTMG4_TRCD(x)	((x) << 24)
+#define DRAMTMG4_TCCD(x)	((x) << 16)
+#define DRAMTMG4_TRRD(x)	((x) <<  8)
+#define DRAMTMG4_TRP(x)		((x) <<  0)
+
+#define DRAMTMG5_TCKSRX(x)	((x) << 24)
+#define DRAMTMG5_TCKSRE(x)	((x) << 16)
+#define DRAMTMG5_TCKESR(x)	((x) <<  8)
+#define DRAMTMG5_TCKE(x)	((x) <<  0)
+
+#define RFSHTMG_TREFI(x)	((x) << 16)
+#define RFSHTMG_TRFC(x)		((x) <<  0)
+
+#define PIR_CLRSR	(0x1 << 27)	/* clear status registers */
+#define PIR_QSGATE	(0x1 << 10)	/* Read DQS gate training */
+#define PIR_DRAMINIT	(0x1 << 8)	/* DRAM initialization */
+#define PIR_DRAMRST	(0x1 << 7)	/* DRAM reset */
+#define PIR_PHYRST	(0x1 << 6)	/* PHY reset */
+#define PIR_DCAL	(0x1 << 5)	/* DDL calibration */
+#define PIR_PLLINIT	(0x1 << 4)	/* PLL initialization */
+#define PIR_ZCAL	(0x1 << 1)	/* ZQ calibration */
+#define PIR_INIT	(0x1 << 0)	/* PHY initialization trigger */
+
+#define PGSR_INIT_DONE	(0x1 << 0)	/* PHY init done */
+
+#define ZQCR_PWRDOWN	(1U << 31)	/* ZQ power down */
+
+#define ACBDLR_WRITE_DELAY(x)	((x) << 8)
+
+#define DXBDLR_DQ(x)	(x)		/* DQ0-7 BDLR index */
+#define DXBDLR_DM	8		/* DM BDLR index */
+#define DXBDLR_DQS	9		/* DQS BDLR index */
+#define DXBDLR_DQSN	10		/* DQSN BDLR index */
+
+#define DXBDLR_WRITE_DELAY(x)	((x) << 8)
+#define DXBDLR_READ_DELAY(x)	((x) << 0)
+
+/*
+ * The delay parameters below allow to allegedly specify delay times of some
+ * unknown unit for each individual bit trace in each of the four data bytes
+ * the 32-bit wide access consists of. Also three control signals can be
+ * adjusted individually.
+ */
+#define NR_OF_BYTE_LANES	(32 / BITS_PER_BYTE)
+/* The eight data lines (DQn) plus DM, DQS and DQSN */
+#define LINES_PER_BYTE_LANE	(BITS_PER_BYTE + 3)
+
+struct rank_para {
+	u16 page_size;
+	u8 row_bits;
+	u8 bank_bits;
+};
+
+struct dram_para {
+	u8 dual_rank;
+	u8 bus_full_width;
+	struct rank_para ranks[2];
+	const u8 dx_read_delays[NR_OF_BYTE_LANES][LINES_PER_BYTE_LANE];
+	const u8 dx_write_delays[NR_OF_BYTE_LANES][LINES_PER_BYTE_LANE];
+	const u8 ac_delays[31];
+};
+
+static inline int ns_to_t(int nanoseconds)
+{
+	const unsigned int ctrl_freq = CONFIG_DRAM_CLK / 2;
+
+	return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000);
+}
+
+#endif /* _SUNXI_DRAM_SUN8I_H3_H */
diff --git a/arch/arm/mach-sunxi/lpddr3_stock.c b/arch/arm/mach-sunxi/lpddr3_stock.c
new file mode 100644
index 0000000000..4767eb516d
--- /dev/null
+++ b/arch/arm/mach-sunxi/lpddr3_stock.c
@@ -0,0 +1,81 @@
+#include <common.h>
+
+static void mctl_set_timing_params(uint16_t socid, struct dram_para *para)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	u8 tccd		= 2;
+	u8 tfaw		= max(ns_to_t(50), 4);
+	u8 trrd		= max(ns_to_t(10), 2);
+	u8 trcd		= max(ns_to_t(24), 2);
+	u8 trc		= ns_to_t(70);
+	u8 txp		= max(ns_to_t(8), 2);
+	u8 twtr		= max(ns_to_t(8), 2);
+	u8 trtp		= max(ns_to_t(8), 2);
+	u8 twr		= max(ns_to_t(15), 3);
+	u8 trp		= max(ns_to_t(27), 2);
+	u8 tras		= ns_to_t(42);
+	u16 trefi	= ns_to_t(3900) / 32;
+	u16 trfc	= ns_to_t(210);
+
+	u8 tmrw		= 5;
+	u8 tmrd		= 5;
+	u8 tmod		= 12;
+	u8 tcke		= 3;
+	u8 tcksrx	= 5;
+	u8 tcksre	= 5;
+	u8 tckesr	= 5;
+	u8 trasmax	= 24;
+
+	u8 tcl		= 6; /* CL 12 */
+	u8 tcwl		= 3; /* CWL 6 */
+	u8 t_rdata_en	= 5;
+	u8 wr_latency	= 2;
+
+	u32 tdinit0	= (200 * CONFIG_DRAM_CLK) + 1;		/* 200us */
+	u32 tdinit1	= (100 * CONFIG_DRAM_CLK) / 1000 + 1;	/* 100ns */
+	u32 tdinit2	= (11 * CONFIG_DRAM_CLK) + 1;		/* 11us */
+	u32 tdinit3	= (1 * CONFIG_DRAM_CLK) + 1;		/* 1us */
+
+	u8 twtp		= tcwl + 4 + twr + 1;
+	u8 twr2rd	= tcwl + 4 + 1 + twtr;
+	u8 trd2wr	= tcl + 4 + 5 - tcwl + 1;
+
+	/* set mode register */
+	writel(0xc3, &mctl_ctl->mr[1]);		/* nWR=8, BL8 */
+	writel(0xa, &mctl_ctl->mr[2]);		/* RL=12, WL=6 */
+	writel(0x2, &mctl_ctl->mr[3]);		/* 40 0hms PD/PU */
+
+	/* set DRAM timing */
+	writel(DRAMTMG0_TWTP(twtp) | DRAMTMG0_TFAW(tfaw) |
+	       DRAMTMG0_TRAS_MAX(trasmax) | DRAMTMG0_TRAS(tras),
+	       &mctl_ctl->dramtmg[0]);
+	writel(DRAMTMG1_TXP(txp) | DRAMTMG1_TRTP(trtp) | DRAMTMG1_TRC(trc),
+	       &mctl_ctl->dramtmg[1]);
+	writel(DRAMTMG2_TCWL(tcwl) | DRAMTMG2_TCL(tcl) |
+	       DRAMTMG2_TRD2WR(trd2wr) | DRAMTMG2_TWR2RD(twr2rd),
+	       &mctl_ctl->dramtmg[2]);
+	writel(DRAMTMG3_TMRW(tmrw) | DRAMTMG3_TMRD(tmrd) | DRAMTMG3_TMOD(tmod),
+	       &mctl_ctl->dramtmg[3]);
+	writel(DRAMTMG4_TRCD(trcd) | DRAMTMG4_TCCD(tccd) | DRAMTMG4_TRRD(trrd) |
+	       DRAMTMG4_TRP(trp), &mctl_ctl->dramtmg[4]);
+	writel(DRAMTMG5_TCKSRX(tcksrx) | DRAMTMG5_TCKSRE(tcksre) |
+	       DRAMTMG5_TCKESR(tckesr) | DRAMTMG5_TCKE(tcke),
+	       &mctl_ctl->dramtmg[5]);
+
+	/* set two rank timing */
+	clrsetbits_le32(&mctl_ctl->dramtmg[8], (0xff << 8) | (0xff << 0),
+			(0x66 << 8) | (0x10 << 0));
+
+	/* set PHY interface timing, write latency and read latency configure */
+	writel((0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) |
+	       (wr_latency << 0), &mctl_ctl->pitmg[0]);
+
+	/* set PHY timing, PTR0-2 use default */
+	writel(PTR3_TDINIT0(tdinit0) | PTR3_TDINIT1(tdinit1), &mctl_ctl->ptr[3]);
+	writel(PTR4_TDINIT2(tdinit2) | PTR4_TDINIT3(tdinit3), &mctl_ctl->ptr[4]);
+
+	/* set refresh timing */
+	writel(RFSHTMG_TREFI(trefi) | RFSHTMG_TRFC(trfc), &mctl_ctl->rfshtmg);
+}
diff --git a/arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c b/arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c
new file mode 100644
index 0000000000..4c54588556
--- /dev/null
+++ b/arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c
@@ -0,0 +1,9 @@
+#define CONFIG_MACH_SUN50I y
+#define CONFIG_SUNXI_DRAM_DW y
+#define CONFIG_SUNXI_DRAM_DW_32BIT y
+#define CONFIG_SUNXI_DRAM_DDR3 y
+#define CONFIG_SYS_SDRAM_BASE 0x40000000
+
+#define sunxi_dram_init sun50i_a64_ddr3_dram_init
+
+#include "sun50i-sdram.c"
diff --git a/arch/arm/mach-sunxi/sun50i-a64-lpddr3-init.c b/arch/arm/mach-sunxi/sun50i-a64-lpddr3-init.c
new file mode 100644
index 0000000000..e5ba206661
--- /dev/null
+++ b/arch/arm/mach-sunxi/sun50i-a64-lpddr3-init.c
@@ -0,0 +1,9 @@
+#define CONFIG_MACH_SUN50I y
+#define CONFIG_SUNXI_DRAM_DW y
+#define CONFIG_SUNXI_DRAM_DW_32BIT y
+#define CONFIG_SUNXI_DRAM_LPDDR3 y
+#define CONFIG_SYS_SDRAM_BASE 0x40000000
+
+#define sunxi_dram_init sun50i_a64_lpddr3_dram_init
+
+#include "sun50i-sdram.c"
diff --git a/arch/arm/mach-sunxi/sun50i-sdram.c b/arch/arm/mach-sunxi/sun50i-sdram.c
new file mode 100644
index 0000000000..d9dc1cf5fd
--- /dev/null
+++ b/arch/arm/mach-sunxi/sun50i-sdram.c
@@ -0,0 +1,903 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * sun8i H3 platform dram controller init
+ *
+ * (C) Copyright 2007-2015 Allwinner Technology Co.
+ *                         Jerry Wang <wangflord@allwinnertech.com>
+ * (C) Copyright 2015      Vishnu Patekar <vishnupatekar0510@gmail.com>
+ * (C) Copyright 2015      Hans de Goede <hdegoede@redhat.com>
+ * (C) Copyright 2015      Jens Kuske <jenskuske@gmail.com>
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <asm/system.h> /* dsb */
+#include <debug_ll.h>
+
+#include <mach/sunxi/init.h>
+
+#define REPEAT_BYTE(x)((~0ul / 0xff) * (x))
+
+#define CONFIG_DRAM_CLK 552
+#define CONFIG_DRAM_ZQ 3881949
+
+#define SUNXI_CCM_BASE                    0x01c20000
+#define SUNXI_DRAM_COM_BASE               0x01c62000
+#define SUNXI_DRAM_CTL0_BASE              0x01c63000
+#define SUNXI_DRAM_CTL1_BASE              0x01c64000
+#define SUNXI_DRAM_PHY0_BASE              0x01c65000
+#define SUNXI_DRAM_PHY1_BASE              0x01c66000
+#define SUNXI_SRAMC_BASE          0x01c00000
+
+#define SOCID_A64	0x1689
+#define SOCID_H3	0x1680
+#define SOCID_H5	0x1718
+#define SOCID_R40	0x1701
+
+/* dram regs definition */
+#include "dram_sunxi_dw.h"
+
+/* clock regs definition */
+#include "clock_sun6i.h"
+
+#if defined CONFIG_SUNXI_DRAM_DDR3
+#include "ddr3_1333.c"
+#elif defined CONFIG_SUNXI_DRAM_LPDDR3
+#include "lpddr3_stock.c"
+#else
+#error Unsupported DRAM type!
+#endif
+
+static void clock_set_pll11(unsigned int clk, bool sigma_delta_enable)
+{
+	struct sunxi_ccm_reg * const ccm = (void *)SUNXI_CCM_BASE;
+
+	if (sigma_delta_enable)
+		writel(CCM_PLL11_PATTERN, &ccm->pll11_pattern_cfg0);
+
+	writel(CCM_PLL11_CTRL_EN | CCM_PLL11_CTRL_UPD |
+	       (sigma_delta_enable ? CCM_PLL11_CTRL_SIGMA_DELTA_EN : 0) |
+	       CCM_PLL11_CTRL_N(clk / 24000000), &ccm->pll11_cfg);
+
+	while (readl(&ccm->pll11_cfg) & CCM_PLL11_CTRL_UPD)
+		;
+}
+
+static void clock_set_pll5(unsigned int clk, bool sigma_delta_enable)
+{
+	struct sunxi_ccm_reg * const ccm = (void *)SUNXI_CCM_BASE;
+
+	if (sigma_delta_enable)
+		writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg);
+
+	writel(CCM_PLL5_CTRL_EN | CCM_PLL5_CTRL_UPD |
+	       (sigma_delta_enable ? CCM_PLL5_CTRL_SIGMA_DELTA_EN : 0) |
+	       CCM_PLL5_CTRL_N(clk / 24000000), &ccm->pll5_cfg);
+
+	while (readl(&ccm->pll5_cfg) & CCM_PLL5_CTRL_UPD)
+		;
+}
+
+/*
+ * Wait up to 1s for value to be set in given part of reg.
+ */
+static void mctl_await_completion(u32 *reg, u32 mask, u32 val)
+{
+	int ms = 1000;
+
+	while ((readl(reg) & mask) != val) {
+		mdelay(1);
+		if (ms-- <= 0) {
+			putc_ll('!');
+			panic("Timeout initialising DRAM\n");
+		}
+	}
+}
+
+static void mctl_phy_init(u32 val)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	writel(val | PIR_INIT, &mctl_ctl->pir);
+	mctl_await_completion(&mctl_ctl->pgsr[0], PGSR_INIT_DONE, 0x1);
+}
+
+static void mctl_set_bit_delays(struct dram_para *para)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+	int i, j;
+
+	clrbits_le32(&mctl_ctl->pgcr[0], 1 << 26);
+
+	for (i = 0; i < NR_OF_BYTE_LANES; i++)
+		for (j = 0; j < LINES_PER_BYTE_LANE; j++)
+			writel(DXBDLR_WRITE_DELAY(para->dx_write_delays[i][j]) |
+			       DXBDLR_READ_DELAY(para->dx_read_delays[i][j]),
+			       &mctl_ctl->dx[i].bdlr[j]);
+
+	for (i = 0; i < 31; i++)
+		writel(ACBDLR_WRITE_DELAY(para->ac_delays[i]),
+		       &mctl_ctl->acbdlr[i]);
+
+#ifdef CONFIG_MACH_SUN8I_R40
+	/* DQSn, DMn, DQn output enable bit delay */
+	for (i = 0; i < 4; i++)
+		writel(0x6 << 24, &mctl_ctl->dx[i].sdlr);
+#endif
+
+	setbits_le32(&mctl_ctl->pgcr[0], 1 << 26);
+}
+
+enum {
+	MBUS_PORT_CPU           = 0,
+	MBUS_PORT_GPU           = 1,
+	MBUS_PORT_UNUSED	= 2,
+	MBUS_PORT_DMA           = 3,
+	MBUS_PORT_VE            = 4,
+	MBUS_PORT_CSI           = 5,
+	MBUS_PORT_NAND          = 6,
+	MBUS_PORT_SS            = 7,
+	MBUS_PORT_TS            = 8,
+	MBUS_PORT_DI            = 9,
+	MBUS_PORT_DE            = 10,
+	MBUS_PORT_DE_CFD        = 11,
+	MBUS_PORT_UNKNOWN1	= 12,
+	MBUS_PORT_UNKNOWN2	= 13,
+	MBUS_PORT_UNKNOWN3	= 14,
+};
+
+enum {
+	MBUS_QOS_LOWEST = 0,
+	MBUS_QOS_LOW,
+	MBUS_QOS_HIGH,
+	MBUS_QOS_HIGHEST
+};
+
+static inline void mbus_configure_port(u8 port,
+				       bool bwlimit,
+				       bool priority,
+				       u8 qos,         /* MBUS_QOS_LOWEST .. MBUS_QOS_HIGEST */
+				       u8 waittime,    /* 0 .. 0xf */
+				       u8 acs,         /* 0 .. 0xff */
+				       u16 bwl0,       /* 0 .. 0xffff, bandwidth limit in MB/s */
+				       u16 bwl1,
+				       u16 bwl2)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	const u32 cfg0 = ( (bwlimit ? (1 << 0) : 0)
+			   | (priority ? (1 << 1) : 0)
+			   | ((qos & 0x3) << 2)
+			   | ((waittime & 0xf) << 4)
+			   | ((acs & 0xff) << 8)
+			   | (bwl0 << 16) );
+	const u32 cfg1 = ((u32)bwl2 << 16) | (bwl1 & 0xffff);
+
+	//debug("MBUS port %d cfg0 %08x cfg1 %08x\n", port, cfg0, cfg1);
+	writel(cfg0, &mctl_com->mcr[port][0]);
+	writel(cfg1, &mctl_com->mcr[port][1]);
+}
+
+#define MBUS_CONF(port, bwlimit, qos, acs, bwl0, bwl1, bwl2)	\
+	mbus_configure_port(MBUS_PORT_ ## port, bwlimit, false, \
+			    MBUS_QOS_ ## qos, 0, acs, bwl0, bwl1, bwl2)
+
+static void mctl_set_master_priority_h3(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	/* enable bandwidth limit windows and set windows size 1us */
+	writel((1 << 16) | (400 << 0), &mctl_com->bwcr);
+
+	/* set cpu high priority */
+	writel(0x00000001, &mctl_com->mapr);
+
+	MBUS_CONF(   CPU,  true, HIGHEST, 0,  512,  256,  128);
+	MBUS_CONF(   GPU,  true,    HIGH, 0, 1536, 1024,  256);
+	MBUS_CONF(UNUSED,  true, HIGHEST, 0,  512,  256,   96);
+	MBUS_CONF(   DMA,  true, HIGHEST, 0,  256,  128,   32);
+	MBUS_CONF(    VE,  true,    HIGH, 0, 1792, 1600,  256);
+	MBUS_CONF(   CSI,  true, HIGHEST, 0,  256,  128,   32);
+	MBUS_CONF(  NAND,  true,    HIGH, 0,  256,  128,   64);
+	MBUS_CONF(    SS,  true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    TS,  true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    DI,  true,    HIGH, 0, 1024,  256,   64);
+	MBUS_CONF(    DE,  true, HIGHEST, 3, 8192, 6120, 1024);
+	MBUS_CONF(DE_CFD,  true,    HIGH, 0, 1024,  288,   64);
+}
+
+static void mctl_set_master_priority_a64(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	/* enable bandwidth limit windows and set windows size 1us */
+	writel(399, &mctl_com->tmr);
+	writel((1 << 16), &mctl_com->bwcr);
+
+	/* Port 2 is reserved per Allwinner's linux-3.10 source, yet they
+	 * initialise it */
+	MBUS_CONF(   CPU,  true, HIGHEST, 0,  160,  100,   80);
+	MBUS_CONF(   GPU, false,    HIGH, 0, 1536, 1400,  256);
+	MBUS_CONF(UNUSED,  true, HIGHEST, 0,  512,  256,   96);
+	MBUS_CONF(   DMA,  true,    HIGH, 0,  256,   80,  100);
+	MBUS_CONF(    VE,  true,    HIGH, 0, 1792, 1600,  256);
+	MBUS_CONF(   CSI,  true,    HIGH, 0,  256,  128,    0);
+	MBUS_CONF(  NAND,  true,    HIGH, 0,  256,  128,   64);
+	MBUS_CONF(    SS,  true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    TS,  true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    DI,  true,    HIGH, 0, 1024,  256,   64);
+	MBUS_CONF(    DE,  true,    HIGH, 2, 8192, 6144, 2048);
+	MBUS_CONF(DE_CFD,  true,    HIGH, 0, 1280,  144,   64);
+
+	writel(0x81000004, &mctl_com->mdfs_bwlr[2]);
+}
+
+static void mctl_set_master_priority_h5(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	/* enable bandwidth limit windows and set windows size 1us */
+	writel(399, &mctl_com->tmr);
+	writel((1 << 16), &mctl_com->bwcr);
+
+	/* set cpu high priority */
+	writel(0x00000001, &mctl_com->mapr);
+
+	/* Port 2 is reserved per Allwinner's linux-3.10 source, yet
+	 * they initialise it */
+	MBUS_CONF(   CPU, true, HIGHEST, 0,  300,  260,  150);
+	MBUS_CONF(   GPU, true, HIGHEST, 0,  600,  400,  200);
+	MBUS_CONF(UNUSED, true, HIGHEST, 0,  512,  256,   96);
+	MBUS_CONF(   DMA, true, HIGHEST, 0,  256,  128,   32);
+	MBUS_CONF(    VE, true, HIGHEST, 0, 1900, 1500, 1000);
+	MBUS_CONF(   CSI, true, HIGHEST, 0,  150,  120,  100);
+	MBUS_CONF(  NAND, true,    HIGH, 0,  256,  128,   64);
+	MBUS_CONF(    SS, true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    TS, true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    DI, true,    HIGH, 0, 1024,  256,   64);
+	MBUS_CONF(    DE, true, HIGHEST, 3, 3400, 2400, 1024);
+	MBUS_CONF(DE_CFD, true, HIGHEST, 0,  600,  400,  200);
+}
+
+static void mctl_set_master_priority_r40(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	/* enable bandwidth limit windows and set windows size 1us */
+	writel(399, &mctl_com->tmr);
+	writel((1 << 16), &mctl_com->bwcr);
+
+	/* set cpu high priority */
+	writel(0x00000001, &mctl_com->mapr);
+
+	/* Port 2 is reserved per Allwinner's linux-3.10 source, yet
+	 * they initialise it */
+	MBUS_CONF(     CPU, true, HIGHEST, 0,  300,  260,  150);
+	MBUS_CONF(     GPU, true, HIGHEST, 0,  600,  400,  200);
+	MBUS_CONF(  UNUSED, true, HIGHEST, 0,  512,  256,   96);
+	MBUS_CONF(     DMA, true, HIGHEST, 0,  256,  128,   32);
+	MBUS_CONF(      VE, true, HIGHEST, 0, 1900, 1500, 1000);
+	MBUS_CONF(     CSI, true, HIGHEST, 0,  150,  120,  100);
+	MBUS_CONF(    NAND, true,    HIGH, 0,  256,  128,   64);
+	MBUS_CONF(      SS, true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(      TS, true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(      DI, true,    HIGH, 0, 1024,  256,   64);
+
+	/*
+	 * The port names are probably wrong, but no correct sources
+	 * are available.
+	 */
+	MBUS_CONF(      DE, true,    HIGH, 0,  128,   48,    0);
+	MBUS_CONF(  DE_CFD, true,    HIGH, 0,  384,  256,    0);
+	MBUS_CONF(UNKNOWN1, true, HIGHEST, 0,  512,  384,  256);
+	MBUS_CONF(UNKNOWN2, true, HIGHEST, 2, 8192, 6144, 1024);
+	MBUS_CONF(UNKNOWN3, true,    HIGH, 0, 1280,  144,   64);
+}
+
+static void mctl_set_master_priority(uint16_t socid)
+{
+	switch (socid) {
+	case SOCID_H3:
+		mctl_set_master_priority_h3();
+		return;
+	case SOCID_A64:
+		mctl_set_master_priority_a64();
+		return;
+	case SOCID_H5:
+		mctl_set_master_priority_h5();
+		return;
+	case SOCID_R40:
+		mctl_set_master_priority_r40();
+		return;
+	}
+}
+
+static u32 bin_to_mgray(int val)
+{
+	static const u8 lookup_table[32] = {
+		0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05,
+		0x0c, 0x0d, 0x0e, 0x0f, 0x0a, 0x0b, 0x08, 0x09,
+		0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x1f, 0x1c, 0x1d,
+		0x14, 0x15, 0x16, 0x17, 0x12, 0x13, 0x10, 0x11,
+	};
+
+	return lookup_table[clamp(val, 0, 31)];
+}
+
+static int mgray_to_bin(u32 val)
+{
+	static const u8 lookup_table[32] = {
+		0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05,
+		0x0e, 0x0f, 0x0c, 0x0d, 0x08, 0x09, 0x0a, 0x0b,
+		0x1e, 0x1f, 0x1c, 0x1d, 0x18, 0x19, 0x1a, 0x1b,
+		0x10, 0x11, 0x12, 0x13, 0x16, 0x17, 0x14, 0x15,
+	};
+
+	return lookup_table[val & 0x1f];
+}
+
+static void mctl_h3_zq_calibration_quirk(struct dram_para *para)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+	int zq_count;
+
+#if defined CONFIG_SUNXI_DRAM_DW_16BIT
+	zq_count = 4;
+#else
+	zq_count = 6;
+#endif
+
+	if ((readl(SUNXI_SRAMC_BASE + 0x24) & 0xff) == 0 &&
+	    (readl(SUNXI_SRAMC_BASE + 0xf0) & 0x1) == 0) {
+		u32 reg_val;
+
+		clrsetbits_le32(&mctl_ctl->zqcr, 0xffff,
+				CONFIG_DRAM_ZQ & 0xffff);
+
+		writel(PIR_CLRSR, &mctl_ctl->pir);
+		mctl_phy_init(PIR_ZCAL);
+
+		reg_val = readl(&mctl_ctl->zqdr[0]);
+		reg_val &= (0x1f << 16) | (0x1f << 0);
+		reg_val |= reg_val << 8;
+		writel(reg_val, &mctl_ctl->zqdr[0]);
+
+		reg_val = readl(&mctl_ctl->zqdr[1]);
+		reg_val &= (0x1f << 16) | (0x1f << 0);
+		reg_val |= reg_val << 8;
+		writel(reg_val, &mctl_ctl->zqdr[1]);
+		writel(reg_val, &mctl_ctl->zqdr[2]);
+	} else {
+		int i;
+		u16 zq_val[6];
+		u8 val;
+
+		writel(0x0a0a0a0a, &mctl_ctl->zqdr[2]);
+
+		for (i = 0; i < zq_count; i++) {
+			u8 zq = (CONFIG_DRAM_ZQ >> (i * 4)) & 0xf;
+
+			writel((zq << 20) | (zq << 16) | (zq << 12) |
+					(zq << 8) | (zq << 4) | (zq << 0),
+					&mctl_ctl->zqcr);
+
+			writel(PIR_CLRSR, &mctl_ctl->pir);
+			mctl_phy_init(PIR_ZCAL);
+
+			zq_val[i] = readl(&mctl_ctl->zqdr[0]) & 0xff;
+			writel(REPEAT_BYTE(zq_val[i]), &mctl_ctl->zqdr[2]);
+
+			writel(PIR_CLRSR, &mctl_ctl->pir);
+			mctl_phy_init(PIR_ZCAL);
+
+			val = readl(&mctl_ctl->zqdr[0]) >> 24;
+			zq_val[i] |= bin_to_mgray(mgray_to_bin(val) - 1) << 8;
+		}
+
+		writel((zq_val[1] << 16) | zq_val[0], &mctl_ctl->zqdr[0]);
+		writel((zq_val[3] << 16) | zq_val[2], &mctl_ctl->zqdr[1]);
+		if (zq_count > 4)
+			writel((zq_val[5] << 16) | zq_val[4],
+			       &mctl_ctl->zqdr[2]);
+	}
+}
+
+static void mctl_set_cr(uint16_t socid, struct dram_para *para)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	writel(MCTL_CR_BL8 | MCTL_CR_INTERLEAVED |
+#if defined CONFIG_SUNXI_DRAM_DDR3
+	       MCTL_CR_DDR3 | MCTL_CR_2T |
+#elif defined CONFIG_SUNXI_DRAM_DDR2
+	       MCTL_CR_DDR2 | MCTL_CR_2T |
+#elif defined CONFIG_SUNXI_DRAM_LPDDR3
+	       MCTL_CR_LPDDR3 | MCTL_CR_1T |
+#else
+#error Unsupported DRAM type!
+#endif
+	       (para->ranks[0].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
+	       MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) |
+	       (para->dual_rank ? MCTL_CR_DUAL_RANK : MCTL_CR_SINGLE_RANK) |
+	       MCTL_CR_PAGE_SIZE(para->ranks[0].page_size) |
+	       MCTL_CR_ROW_BITS(para->ranks[0].row_bits), &mctl_com->cr);
+
+	if (socid == SOCID_A64 || socid == SOCID_R40) {
+		writel((para->ranks[1].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
+		       MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) |
+		       (para->dual_rank ? MCTL_CR_DUAL_RANK : MCTL_CR_SINGLE_RANK) |
+		       MCTL_CR_PAGE_SIZE(para->ranks[1].page_size) |
+		       MCTL_CR_ROW_BITS(para->ranks[1].row_bits), &mctl_com->cr_r1);
+	}
+
+	if (socid == SOCID_R40) {
+		/* Mux pin to A15 address line for single rank memory. */
+		if (!para->dual_rank)
+			setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15);
+	}
+}
+
+static void mctl_sys_init(uint16_t socid, struct dram_para *para)
+{
+	struct sunxi_ccm_reg * const ccm =
+			(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	clrbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE);
+	clrbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
+	clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
+	clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
+	clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
+
+	if (socid == SOCID_A64 || socid == SOCID_R40)
+		clrbits_le32(&ccm->pll11_cfg, CCM_PLL11_CTRL_EN);
+	udelay(10);
+
+	clrbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
+	udelay(1000);
+
+	if (socid == SOCID_A64 || socid == SOCID_R40) {
+		clock_set_pll11(CONFIG_DRAM_CLK * 2 * 1000000, false);
+		clrsetbits_le32(&ccm->dram_clk_cfg,
+				CCM_DRAMCLK_CFG_DIV_MASK |
+				CCM_DRAMCLK_CFG_SRC_MASK,
+				CCM_DRAMCLK_CFG_DIV(1) |
+				CCM_DRAMCLK_CFG_SRC_PLL11 |
+				CCM_DRAMCLK_CFG_UPD);
+	} else if (socid == SOCID_H3 || socid == SOCID_H5) {
+		clock_set_pll5(CONFIG_DRAM_CLK * 2 * 1000000, false);
+		clrsetbits_le32(&ccm->dram_clk_cfg,
+				CCM_DRAMCLK_CFG_DIV_MASK |
+				CCM_DRAMCLK_CFG_SRC_MASK,
+				CCM_DRAMCLK_CFG_DIV(1) |
+				CCM_DRAMCLK_CFG_SRC_PLL5 |
+				CCM_DRAMCLK_CFG_UPD);
+	}
+
+	/* SDRCLK Configuration Update.
+	 * Note: Set this bit will validate Configuration.
+	 * It will be auto cleared after the Configuration is valid.
+	 */
+	mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
+
+	setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
+	setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
+	setbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
+	setbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE);
+
+	setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
+	udelay(10);
+
+	writel(socid == SOCID_H5 ? 0x8000 : 0xc00e, &mctl_ctl->clken);
+	udelay(500);
+}
+
+/* These are more guessed based on some Allwinner code. */
+#define DX_GCR_ODT_DYNAMIC	(0x0 << 4)
+#define DX_GCR_ODT_ALWAYS_ON	(0x1 << 4)
+#define DX_GCR_ODT_OFF		(0x2 << 4)
+
+static int mctl_channel_init(uint16_t socid, struct dram_para *para)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	unsigned int i;
+
+	mctl_set_cr(socid, para);
+	mctl_set_timing_params(socid, para);
+	mctl_set_master_priority(socid);
+
+	/* setting VTC, default disable all VT */
+	clrbits_le32(&mctl_ctl->pgcr[0], (1 << 30) | 0x3f);
+	if (socid == SOCID_H5)
+		setbits_le32(&mctl_ctl->pgcr[1], (1 << 24) | (1 << 26));
+	else
+		clrsetbits_le32(&mctl_ctl->pgcr[1], 1 << 24, 1 << 26);
+
+	/* increase DFI_PHY_UPD clock */
+	writel(PROTECT_MAGIC, &mctl_com->protect);
+	udelay(100);
+	clrsetbits_le32(&mctl_ctl->upd2, 0xfff << 16, 0x50 << 16);
+	writel(0x0, &mctl_com->protect);
+	udelay(100);
+
+	/* set dramc odt */
+	for (i = 0; i < 4; i++) {
+		u32 clearmask = (0x3 << 4) | (0x1 << 1) | (0x3 << 2) |
+				(0x3 << 12) | (0x3 << 14);
+		u32 setmask = IS_ENABLED(CONFIG_DRAM_ODT_EN) ?
+				DX_GCR_ODT_DYNAMIC : DX_GCR_ODT_OFF;
+
+		if (socid == SOCID_H5) {
+			clearmask |= 0x2 << 8;
+			setmask |= 0x4 << 8;
+		}
+		clrsetbits_le32(&mctl_ctl->dx[i].gcr, clearmask, setmask);
+	}
+
+	/* AC PDR should always ON */
+	clrsetbits_le32(&mctl_ctl->aciocr, socid == SOCID_H5 ? (0x1 << 11) : 0,
+			0x1 << 1);
+
+	/* set DQS auto gating PD mode */
+	setbits_le32(&mctl_ctl->pgcr[2], 0x3 << 6);
+
+	if (socid == SOCID_H3) {
+		/* dx ddr_clk & hdr_clk dynamic mode */
+		clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12));
+
+		/* dphy & aphy phase select 270 degree */
+		clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
+				(0x1 << 10) | (0x2 << 8));
+	} else if (socid == SOCID_A64 || socid == SOCID_H5) {
+		/* dphy & aphy phase select ? */
+		clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
+				(0x0 << 10) | (0x3 << 8));
+	} else if (socid == SOCID_R40) {
+		/* dx ddr_clk & hdr_clk dynamic mode (tpr13[9] == 0) */
+		clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12));
+
+		/* dphy & aphy phase select ? */
+		clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
+				(0x0 << 10) | (0x3 << 8));
+	}
+
+	/* set half DQ */
+	if (!para->bus_full_width) {
+#if defined CONFIG_SUNXI_DRAM_DW_32BIT
+		writel(0x0, &mctl_ctl->dx[2].gcr);
+		writel(0x0, &mctl_ctl->dx[3].gcr);
+#elif defined CONFIG_SUNXI_DRAM_DW_16BIT
+		writel(0x0, &mctl_ctl->dx[1].gcr);
+#else
+#error Unsupported DRAM bus width!
+#endif
+	}
+
+	/* data training configuration */
+	clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24,
+			(para->dual_rank ? 0x3 : 0x1) << 24);
+
+	mctl_set_bit_delays(para);
+	udelay(50);
+
+	if (socid == SOCID_H3) {
+		mctl_h3_zq_calibration_quirk(para);
+
+		mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
+			      PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE);
+	} else if (socid == SOCID_A64 || socid == SOCID_H5) {
+		clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, CONFIG_DRAM_ZQ);
+
+		mctl_phy_init(PIR_ZCAL | PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
+			      PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE);
+		/* no PIR_QSGATE for H5 ???? */
+	} else if (socid == SOCID_R40) {
+		clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, CONFIG_DRAM_ZQ);
+
+		mctl_phy_init(PIR_ZCAL | PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
+			      PIR_DRAMRST | PIR_DRAMINIT);
+	}
+
+	/* detect ranks and bus width */
+	if (readl(&mctl_ctl->pgsr[0]) & (0xfe << 20)) {
+		/* only one rank */
+		if (((readl(&mctl_ctl->dx[0].gsr[0]) >> 24) & 0x2)
+#if defined CONFIG_SUNXI_DRAM_DW_32BIT
+		    || ((readl(&mctl_ctl->dx[1].gsr[0]) >> 24) & 0x2)
+#endif
+		    ) {
+			clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24, 0x1 << 24);
+			para->dual_rank = 0;
+		}
+
+		/* only half DQ width */
+#if defined CONFIG_SUNXI_DRAM_DW_32BIT
+		if (((readl(&mctl_ctl->dx[2].gsr[0]) >> 24) & 0x1) ||
+		    ((readl(&mctl_ctl->dx[3].gsr[0]) >> 24) & 0x1)) {
+			writel(0x0, &mctl_ctl->dx[2].gcr);
+			writel(0x0, &mctl_ctl->dx[3].gcr);
+			para->bus_full_width = 0;
+		}
+#elif defined CONFIG_SUNXI_DRAM_DW_16BIT
+		if ((readl(&mctl_ctl->dx[1].gsr[0]) >> 24) & 0x1) {
+			writel(0x0, &mctl_ctl->dx[1].gcr);
+			para->bus_full_width = 0;
+		}
+#endif
+
+		mctl_set_cr(socid, para);
+		udelay(20);
+
+		/* re-train */
+		mctl_phy_init(PIR_QSGATE);
+		if (readl(&mctl_ctl->pgsr[0]) & (0xfe << 20))
+			return 1;
+	}
+
+	/* check the dramc status */
+	mctl_await_completion(&mctl_ctl->statr, 0x1, 0x1);
+
+	/* liuke added for refresh debug */
+	setbits_le32(&mctl_ctl->rfshctl0, 0x1 << 31);
+	udelay(10);
+	clrbits_le32(&mctl_ctl->rfshctl0, 0x1 << 31);
+	udelay(10);
+
+	/* set PGCR3, CKE polarity */
+	if (socid == SOCID_H3)
+		writel(0x00aa0060, &mctl_ctl->pgcr[3]);
+	else if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40)
+		writel(0xc0aa0060, &mctl_ctl->pgcr[3]);
+
+	/* power down zq calibration module for power save */
+	setbits_le32(&mctl_ctl->zqcr, ZQCR_PWRDOWN);
+
+	/* enable master access */
+	writel(0xffffffff, &mctl_com->maer);
+
+	return 0;
+}
+
+/*
+ * Test if memory at offset offset matches memory at a certain base
+ */
+static bool mctl_mem_matches_base(u32 offset, ulong base)
+{
+	/* Try to write different values to RAM at two addresses */
+	writel(0, base);
+	writel(0xaa55aa55, base + offset);
+	dsb();
+	/* Check if the same value is actually observed when reading back */
+	return readl(base) ==
+	       readl(base + offset);
+}
+
+static void mctl_auto_detect_dram_size_rank(uint16_t socid, struct dram_para *para, ulong base, struct rank_para *rank)
+{
+	/* detect row address bits */
+	rank->page_size = 512;
+	rank->row_bits = 16;
+	rank->bank_bits = 2;
+	mctl_set_cr(socid, para);
+
+	for (rank->row_bits = 11; rank->row_bits < 16; rank->row_bits++)
+		if (mctl_mem_matches_base((1 << (rank->row_bits + rank->bank_bits)) * rank->page_size, base))
+			break;
+
+	/* detect bank address bits */
+	rank->bank_bits = 3;
+	mctl_set_cr(socid, para);
+
+	for (rank->bank_bits = 2; rank->bank_bits < 3; rank->bank_bits++)
+		if (mctl_mem_matches_base((1 << rank->bank_bits) * rank->page_size, base))
+			break;
+
+	/* detect page size */
+	rank->page_size = 8192;
+	mctl_set_cr(socid, para);
+
+	for (rank->page_size = 512; rank->page_size < 8192; rank->page_size *= 2)
+		if (mctl_mem_matches_base(rank->page_size, base))
+			break;
+}
+
+static unsigned long mctl_calc_rank_size(struct rank_para *rank)
+{
+	return (1UL << (rank->row_bits + rank->bank_bits)) * rank->page_size;
+}
+
+static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para)
+{
+	mctl_auto_detect_dram_size_rank(socid, para, (ulong)CONFIG_SYS_SDRAM_BASE, &para->ranks[0]);
+
+	if ((socid == SOCID_A64 || socid == SOCID_R40) && para->dual_rank) {
+		mctl_auto_detect_dram_size_rank(socid, para, (ulong)CONFIG_SYS_SDRAM_BASE + mctl_calc_rank_size(&para->ranks[0]), &para->ranks[1]);
+	}
+}
+
+/*
+ * The actual values used here are taken from Allwinner provided boot0
+ * binaries, though they are probably board specific, so would likely benefit
+ * from invidual tuning for each board. Apparently a lot of boards copy from
+ * some Allwinner reference design, so we go with those generic values for now
+ * in the hope that they are reasonable for most (all?) boards.
+ */
+#define SUN8I_H3_DX_READ_DELAYS					\
+	{{ 18, 18, 18, 18, 18, 18, 18, 18, 18,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 18, 18, 18, 18, 18, 18, 18, 18, 18,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 }}
+#define SUN8I_H3_DX_WRITE_DELAYS				\
+	{{  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  6 }}
+#define SUN8I_H3_AC_DELAYS					\
+	{  0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0      }
+
+#define SUN8I_R40_DX_READ_DELAYS				\
+	{{ 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 } }
+#define SUN8I_R40_DX_WRITE_DELAYS				\
+	{{  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 } }
+#define SUN8I_R40_AC_DELAYS					\
+	{  0,  0,  3,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0      }
+
+#define SUN50I_A64_DX_READ_DELAYS				\
+	{{ 16, 16, 16, 16, 17, 16, 16, 17, 16,  1,  0 },	\
+	 { 17, 17, 17, 17, 17, 17, 17, 17, 17,  1,  0 },	\
+	 { 16, 17, 17, 16, 16, 16, 16, 16, 16,  0,  0 },	\
+	 { 17, 17, 17, 17, 17, 17, 17, 17, 17,  1,  0 }}
+#define SUN50I_A64_DX_WRITE_DELAYS				\
+	{{  0,  0,  0,  0,  0,  0,  0,  0,  0, 15, 15 },	\
+	 {  0,  0,  0,  0,  1,  1,  1,  1,  0, 10, 10 },	\
+	 {  1,  0,  1,  1,  1,  1,  1,  1,  0, 11, 11 },	\
+	 {  1,  0,  0,  1,  1,  1,  1,  1,  0, 12, 12 }}
+#define SUN50I_A64_AC_DELAYS					\
+	{  5,  5, 13, 10,  2,  5,  3,  3,			\
+	   0,  3,  3,  3,  1,  0,  0,  0,			\
+	   3,  4,  0,  3,  4,  1,  4,  0,			\
+	   1,  1,  0,  1, 13,  5,  4      }
+
+#define SUN8I_H5_DX_READ_DELAYS					\
+	{{ 14, 15, 17, 17, 17, 17, 17, 18, 17,  3,  3 },	\
+	 { 21, 21, 12, 22, 21, 21, 21, 21, 21,  3,  3 },	\
+	 { 16, 19, 19, 17, 22, 22, 21, 22, 19,  3,  3 },	\
+	 { 21, 21, 22, 22, 20, 21, 19, 19, 19,  3,  3 } }
+#define SUN8I_H5_DX_WRITE_DELAYS				\
+	{{  1,  2,  3,  4,  3,  4,  4,  4,  6,  6,  6 },	\
+	 {  6,  6,  6,  5,  5,  5,  5,  5,  6,  6,  6 },	\
+	 {  0,  2,  4,  2,  6,  5,  5,  5,  6,  6,  6 },	\
+	 {  3,  3,  3,  2,  2,  1,  1,  1,  4,  4,  4 } }
+#define SUN8I_H5_AC_DELAYS					\
+	{  0,  0,  5,  5,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  3,  3,  3,  3,			\
+	   3,  3,  3,  3,  3,  3,  3,  3,			\
+	   3,  3,  3,  3,  2,  0,  0      }
+
+unsigned long sunxi_dram_init(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	unsigned long size;
+
+	struct dram_para para = {
+		.dual_rank = 1,
+		.bus_full_width = 1,
+		.ranks = {
+			{
+				.row_bits = 15,
+				.bank_bits = 3,
+				.page_size = 4096,
+			},
+			{
+				.row_bits = 15,
+				.bank_bits = 3,
+				.page_size = 4096,
+			}
+		},
+
+#if defined(CONFIG_MACH_SUN8I_H3)
+		.dx_read_delays  = SUN8I_H3_DX_READ_DELAYS,
+		.dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN8I_H3_AC_DELAYS,
+#elif defined(CONFIG_MACH_SUN8I_R40)
+		.dx_read_delays  = SUN8I_R40_DX_READ_DELAYS,
+		.dx_write_delays = SUN8I_R40_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN8I_R40_AC_DELAYS,
+#elif defined(CONFIG_MACH_SUN50I)
+		.dx_read_delays  = SUN50I_A64_DX_READ_DELAYS,
+		.dx_write_delays = SUN50I_A64_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN50I_A64_AC_DELAYS,
+#elif defined(CONFIG_MACH_SUN50I_H5)
+		.dx_read_delays  = SUN8I_H5_DX_READ_DELAYS,
+		.dx_write_delays = SUN8I_H5_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN8I_H5_AC_DELAYS,
+#endif
+	};
+/*
+ * Let the compiler optimize alternatives away by passing this value into
+ * the static functions. This saves us #ifdefs, but still keeps the binary
+ * small.
+ */
+#if defined(CONFIG_MACH_SUN8I_H3)
+	uint16_t socid = SOCID_H3;
+#elif defined(CONFIG_MACH_SUN8I_R40)
+	uint16_t socid = SOCID_R40;
+	/* Currently we cannot support R40 with dual rank memory */
+	para.dual_rank = 0;
+#elif defined(CONFIG_MACH_SUN8I_V3S)
+	/* TODO: set delays and mbus priority for V3s */
+	uint16_t socid = SOCID_H3;
+#elif defined(CONFIG_MACH_SUN50I)
+	uint16_t socid = SOCID_A64;
+#elif defined(CONFIG_MACH_SUN50I_H5)
+	uint16_t socid = SOCID_H5;
+#endif
+
+	mctl_sys_init(socid, &para);
+	if (mctl_channel_init(socid, &para))
+		return 0;
+
+	if (para.dual_rank)
+		writel(0x00000303, &mctl_ctl->odtmap);
+	else
+		writel(0x00000201, &mctl_ctl->odtmap);
+	udelay(1);
+
+	/* odt delay */
+	if (socid == SOCID_H3)
+		writel(0x0c000400, &mctl_ctl->odtcfg);
+
+	if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40) {
+		/* VTF enable (tpr13[8] == 1) */
+		setbits_le32(&mctl_ctl->vtfcr,
+			     (socid != SOCID_A64 ? 3 : 2) << 8);
+		/* DQ hold disable (tpr13[26] == 1) */
+		clrbits_le32(&mctl_ctl->pgcr[2], (1 << 13));
+	}
+
+	/* clear credit value */
+	setbits_le32(&mctl_com->cccr, 1 << 31);
+	udelay(10);
+
+	mctl_auto_detect_dram_size(socid, &para);
+	mctl_set_cr(socid, &para);
+
+	size = mctl_calc_rank_size(&para.ranks[0]);
+	if (socid == SOCID_A64 || socid == SOCID_R40) {
+		if (para.dual_rank)
+			size += mctl_calc_rank_size(&para.ranks[1]);
+	} else if (para.dual_rank) {
+		size *= 2;
+	}
+
+	return size;
+}
diff --git a/arch/arm/mach-sunxi/sunxi-sdram.c b/arch/arm/mach-sunxi/sunxi-sdram.c
new file mode 100644
index 0000000000..49382e40cd
--- /dev/null
+++ b/arch/arm/mach-sunxi/sunxi-sdram.c
@@ -0,0 +1,1007 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * sun8i H3 platform dram controller init
+ *
+ * (C) Copyright 2007-2015 Allwinner Technology Co.
+ *                         Jerry Wang <wangflord@allwinnertech.com>
+ * (C) Copyright 2015      Vishnu Patekar <vishnupatekar0510@gmail.com>
+ * (C) Copyright 2015      Hans de Goede <hdegoede@redhat.com>
+ * (C) Copyright 2015      Jens Kuske <jenskuske@gmail.com>
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <clock.h>
+
+#include <mach/sunxi/init.h>
+
+#if defined(CONFIG_MACH_SUN50I_A64)
+#define CONFIG_MACH_SUN50I CONFIG_MACH_SUN50I_A64
+#endif
+
+static void mctl_phy_init(u32 val)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	writel(val | PIR_INIT, &mctl_ctl->pir);
+	mctl_await_completion(&mctl_ctl->pgsr[0], PGSR_INIT_DONE, 0x1);
+}
+
+static void mctl_set_bit_delays(struct dram_para *para)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+	int i, j;
+
+	clrbits_le32(&mctl_ctl->pgcr[0], 1 << 26);
+
+	for (i = 0; i < NR_OF_BYTE_LANES; i++)
+		for (j = 0; j < LINES_PER_BYTE_LANE; j++)
+			writel(DXBDLR_WRITE_DELAY(para->dx_write_delays[i][j]) |
+			       DXBDLR_READ_DELAY(para->dx_read_delays[i][j]),
+			       &mctl_ctl->dx[i].bdlr[j]);
+
+	for (i = 0; i < 31; i++)
+		writel(ACBDLR_WRITE_DELAY(para->ac_delays[i]),
+		       &mctl_ctl->acbdlr[i]);
+
+#ifdef CONFIG_MACH_SUN8I_R40
+	/* DQSn, DMn, DQn output enable bit delay */
+	for (i = 0; i < 4; i++)
+		writel(0x6 << 24, &mctl_ctl->dx[i].sdlr);
+#endif
+
+	setbits_le32(&mctl_ctl->pgcr[0], 1 << 26);
+}
+
+enum {
+	MBUS_PORT_CPU           = 0,
+	MBUS_PORT_GPU           = 1,
+	MBUS_PORT_UNUSED	= 2,
+	MBUS_PORT_DMA           = 3,
+	MBUS_PORT_VE            = 4,
+	MBUS_PORT_CSI           = 5,
+	MBUS_PORT_NAND          = 6,
+	MBUS_PORT_SS            = 7,
+	MBUS_PORT_TS            = 8,
+	MBUS_PORT_DI            = 9,
+	MBUS_PORT_DE            = 10,
+	MBUS_PORT_DE_CFD        = 11,
+	MBUS_PORT_UNKNOWN1	= 12,
+	MBUS_PORT_UNKNOWN2	= 13,
+	MBUS_PORT_UNKNOWN3	= 14,
+};
+
+enum {
+	MBUS_QOS_LOWEST = 0,
+	MBUS_QOS_LOW,
+	MBUS_QOS_HIGH,
+	MBUS_QOS_HIGHEST
+};
+
+static inline void mbus_configure_port(u8 port,
+				       bool bwlimit,
+				       bool priority,
+				       u8 qos,         /* MBUS_QOS_LOWEST .. MBUS_QOS_HIGEST */
+				       u8 waittime,    /* 0 .. 0xf */
+				       u8 acs,         /* 0 .. 0xff */
+				       u16 bwl0,       /* 0 .. 0xffff, bandwidth limit in MB/s */
+				       u16 bwl1,
+				       u16 bwl2)
+{
+	struct sunxi_mctl_com_reg *const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	const u32 cfg0 = ( (bwlimit ? (1 << 0) : 0)
+			   | (priority ? (1 << 1) : 0)
+			   | ((qos & 0x3) << 2)
+			   | ((waittime & 0xf) << 4)
+			   | ((acs & 0xff) << 8)
+			   | (bwl0 << 16) );
+	const u32 cfg1 = ((u32)bwl2 << 16) | (bwl1 & 0xffff);
+
+	writel(cfg0, &mctl_com->mcr[port][0]);
+	writel(cfg1, &mctl_com->mcr[port][1]);
+}
+
+#define MBUS_CONF(port, bwlimit, qos, acs, bwl0, bwl1, bwl2)	\
+	mbus_configure_port(MBUS_PORT_ ## port, bwlimit, false, \
+			    MBUS_QOS_ ## qos, 0, acs, bwl0, bwl1, bwl2)
+
+static void mctl_set_master_priority_h3(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	/* enable bandwidth limit windows and set windows size 1us */
+	writel((1 << 16) | (400 << 0), &mctl_com->bwcr);
+
+	/* set cpu high priority */
+	writel(0x00000001, &mctl_com->mapr);
+
+	MBUS_CONF(   CPU,  true, HIGHEST, 0,  512,  256,  128);
+	MBUS_CONF(   GPU,  true,    HIGH, 0, 1536, 1024,  256);
+	MBUS_CONF(UNUSED,  true, HIGHEST, 0,  512,  256,   96);
+	MBUS_CONF(   DMA,  true, HIGHEST, 0,  256,  128,   32);
+	MBUS_CONF(    VE,  true,    HIGH, 0, 1792, 1600,  256);
+	MBUS_CONF(   CSI,  true, HIGHEST, 0,  256,  128,   32);
+	MBUS_CONF(  NAND,  true,    HIGH, 0,  256,  128,   64);
+	MBUS_CONF(    SS,  true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    TS,  true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    DI,  true,    HIGH, 0, 1024,  256,   64);
+	MBUS_CONF(    DE,  true, HIGHEST, 3, 8192, 6120, 1024);
+	MBUS_CONF(DE_CFD,  true,    HIGH, 0, 1024,  288,   64);
+}
+
+static void mctl_set_master_priority_a64(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	/* enable bandwidth limit windows and set windows size 1us */
+	writel(399, &mctl_com->tmr);
+	writel((1 << 16), &mctl_com->bwcr);
+
+	/* Port 2 is reserved per Allwinner's linux-3.10 source, yet they
+	 * initialise it */
+	MBUS_CONF(   CPU,  true, HIGHEST, 0,  160,  100,   80);
+	MBUS_CONF(   GPU, false,    HIGH, 0, 1536, 1400,  256);
+	MBUS_CONF(UNUSED,  true, HIGHEST, 0,  512,  256,   96);
+	MBUS_CONF(   DMA,  true,    HIGH, 0,  256,   80,  100);
+	MBUS_CONF(    VE,  true,    HIGH, 0, 1792, 1600,  256);
+	MBUS_CONF(   CSI,  true,    HIGH, 0,  256,  128,    0);
+	MBUS_CONF(  NAND,  true,    HIGH, 0,  256,  128,   64);
+	MBUS_CONF(    SS,  true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    TS,  true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    DI,  true,    HIGH, 0, 1024,  256,   64);
+	MBUS_CONF(    DE,  true,    HIGH, 2, 8192, 6144, 2048);
+	MBUS_CONF(DE_CFD,  true,    HIGH, 0, 1280,  144,   64);
+
+	writel(0x81000004, &mctl_com->mdfs_bwlr[2]);
+}
+
+static void mctl_set_master_priority_h5(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	/* enable bandwidth limit windows and set windows size 1us */
+	writel(399, &mctl_com->tmr);
+	writel((1 << 16), &mctl_com->bwcr);
+
+	/* set cpu high priority */
+	writel(0x00000001, &mctl_com->mapr);
+
+	/* Port 2 is reserved per Allwinner's linux-3.10 source, yet
+	 * they initialise it */
+	MBUS_CONF(   CPU, true, HIGHEST, 0,  300,  260,  150);
+	MBUS_CONF(   GPU, true, HIGHEST, 0,  600,  400,  200);
+	MBUS_CONF(UNUSED, true, HIGHEST, 0,  512,  256,   96);
+	MBUS_CONF(   DMA, true, HIGHEST, 0,  256,  128,   32);
+	MBUS_CONF(    VE, true, HIGHEST, 0, 1900, 1500, 1000);
+	MBUS_CONF(   CSI, true, HIGHEST, 0,  150,  120,  100);
+	MBUS_CONF(  NAND, true,    HIGH, 0,  256,  128,   64);
+	MBUS_CONF(    SS, true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    TS, true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(    DI, true,    HIGH, 0, 1024,  256,   64);
+	MBUS_CONF(    DE, true, HIGHEST, 3, 3400, 2400, 1024);
+	MBUS_CONF(DE_CFD, true, HIGHEST, 0,  600,  400,  200);
+}
+
+static void mctl_set_master_priority_r40(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	/* enable bandwidth limit windows and set windows size 1us */
+	writel(399, &mctl_com->tmr);
+	writel((1 << 16), &mctl_com->bwcr);
+
+	/* set cpu high priority */
+	writel(0x00000001, &mctl_com->mapr);
+
+	/* Port 2 is reserved per Allwinner's linux-3.10 source, yet
+	 * they initialise it */
+	MBUS_CONF(     CPU, true, HIGHEST, 0,  300,  260,  150);
+	MBUS_CONF(     GPU, true, HIGHEST, 0,  600,  400,  200);
+	MBUS_CONF(  UNUSED, true, HIGHEST, 0,  512,  256,   96);
+	MBUS_CONF(     DMA, true, HIGHEST, 0,  256,  128,   32);
+	MBUS_CONF(      VE, true, HIGHEST, 0, 1900, 1500, 1000);
+	MBUS_CONF(     CSI, true, HIGHEST, 0,  150,  120,  100);
+	MBUS_CONF(    NAND, true,    HIGH, 0,  256,  128,   64);
+	MBUS_CONF(      SS, true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(      TS, true, HIGHEST, 0,  256,  128,   64);
+	MBUS_CONF(      DI, true,    HIGH, 0, 1024,  256,   64);
+
+	/*
+	 * The port names are probably wrong, but no correct sources
+	 * are available.
+	 */
+	MBUS_CONF(      DE, true,    HIGH, 0,  128,   48,    0);
+	MBUS_CONF(  DE_CFD, true,    HIGH, 0,  384,  256,    0);
+	MBUS_CONF(UNKNOWN1, true, HIGHEST, 0,  512,  384,  256);
+	MBUS_CONF(UNKNOWN2, true, HIGHEST, 2, 8192, 6144, 1024);
+	MBUS_CONF(UNKNOWN3, true,    HIGH, 0, 1280,  144,   64);
+}
+
+static void mctl_set_master_priority(uint16_t socid)
+{
+	switch (socid) {
+	case SOCID_H3:
+		mctl_set_master_priority_h3();
+		return;
+	case SOCID_A64:
+		mctl_set_master_priority_a64();
+		return;
+	case SOCID_H5:
+		mctl_set_master_priority_h5();
+		return;
+	case SOCID_R40:
+		mctl_set_master_priority_r40();
+		return;
+	}
+}
+
+static u32 bin_to_mgray(int val)
+{
+	static const u8 lookup_table[32] = {
+		0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05,
+		0x0c, 0x0d, 0x0e, 0x0f, 0x0a, 0x0b, 0x08, 0x09,
+		0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x1f, 0x1c, 0x1d,
+		0x14, 0x15, 0x16, 0x17, 0x12, 0x13, 0x10, 0x11,
+	};
+
+	return lookup_table[clamp(val, 0, 31)];
+}
+
+static int mgray_to_bin(u32 val)
+{
+	static const u8 lookup_table[32] = {
+		0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05,
+		0x0e, 0x0f, 0x0c, 0x0d, 0x08, 0x09, 0x0a, 0x0b,
+		0x1e, 0x1f, 0x1c, 0x1d, 0x18, 0x19, 0x1a, 0x1b,
+		0x10, 0x11, 0x12, 0x13, 0x16, 0x17, 0x14, 0x15,
+	};
+
+	return lookup_table[val & 0x1f];
+}
+
+static void mctl_h3_zq_calibration_quirk(struct dram_para *para)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+	int zq_count;
+
+#if defined CONFIG_SUNXI_DRAM_DW_16BIT
+	zq_count = 4;
+#else
+	zq_count = 6;
+#endif
+
+	if ((readl(SUNXI_SRAMC_BASE + 0x24) & 0xff) == 0 &&
+	    (readl(SUNXI_SRAMC_BASE + 0xf0) & 0x1) == 0) {
+		u32 reg_val;
+
+		clrsetbits_le32(&mctl_ctl->zqcr, 0xffff,
+				CONFIG_DRAM_ZQ & 0xffff);
+
+		writel(PIR_CLRSR, &mctl_ctl->pir);
+		mctl_phy_init(PIR_ZCAL);
+
+		reg_val = readl(&mctl_ctl->zqdr[0]);
+		reg_val &= (0x1f << 16) | (0x1f << 0);
+		reg_val |= reg_val << 8;
+		writel(reg_val, &mctl_ctl->zqdr[0]);
+
+		reg_val = readl(&mctl_ctl->zqdr[1]);
+		reg_val &= (0x1f << 16) | (0x1f << 0);
+		reg_val |= reg_val << 8;
+		writel(reg_val, &mctl_ctl->zqdr[1]);
+		writel(reg_val, &mctl_ctl->zqdr[2]);
+	} else {
+		int i;
+		u16 zq_val[6];
+		u8 val;
+
+		writel(0x0a0a0a0a, &mctl_ctl->zqdr[2]);
+
+		for (i = 0; i < zq_count; i++) {
+			u8 zq = (CONFIG_DRAM_ZQ >> (i * 4)) & 0xf;
+
+			writel((zq << 20) | (zq << 16) | (zq << 12) |
+					(zq << 8) | (zq << 4) | (zq << 0),
+					&mctl_ctl->zqcr);
+
+			writel(PIR_CLRSR, &mctl_ctl->pir);
+			mctl_phy_init(PIR_ZCAL);
+
+			zq_val[i] = readl(&mctl_ctl->zqdr[0]) & 0xff;
+			writel(REPEAT_BYTE(zq_val[i]), &mctl_ctl->zqdr[2]);
+
+			writel(PIR_CLRSR, &mctl_ctl->pir);
+			mctl_phy_init(PIR_ZCAL);
+
+			val = readl(&mctl_ctl->zqdr[0]) >> 24;
+			zq_val[i] |= bin_to_mgray(mgray_to_bin(val) - 1) << 8;
+		}
+
+		writel((zq_val[1] << 16) | zq_val[0], &mctl_ctl->zqdr[0]);
+		writel((zq_val[3] << 16) | zq_val[2], &mctl_ctl->zqdr[1]);
+		if (zq_count > 4)
+			writel((zq_val[5] << 16) | zq_val[4],
+			       &mctl_ctl->zqdr[2]);
+	}
+}
+
+static void mctl_set_cr(uint16_t socid, struct dram_para *para)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	writel(MCTL_CR_BL8 | MCTL_CR_INTERLEAVED |
+#if defined CONFIG_SUNXI_DRAM_DDR3
+	       MCTL_CR_DDR3 | MCTL_CR_2T |
+#elif defined CONFIG_SUNXI_DRAM_DDR2
+	       MCTL_CR_DDR2 | MCTL_CR_2T |
+#elif defined CONFIG_SUNXI_DRAM_LPDDR3
+	       MCTL_CR_LPDDR3 | MCTL_CR_1T |
+#else
+#error Unsupported DRAM type!
+#endif
+	       (para->ranks[0].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
+	       MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) |
+	       (para->dual_rank ? MCTL_CR_DUAL_RANK : MCTL_CR_SINGLE_RANK) |
+	       MCTL_CR_PAGE_SIZE(para->ranks[0].page_size) |
+	       MCTL_CR_ROW_BITS(para->ranks[0].row_bits), &mctl_com->cr);
+
+	if (socid == SOCID_A64 || socid == SOCID_R40) {
+		writel((para->ranks[1].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
+		       MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) |
+		       (para->dual_rank ? MCTL_CR_DUAL_RANK : MCTL_CR_SINGLE_RANK) |
+		       MCTL_CR_PAGE_SIZE(para->ranks[1].page_size) |
+		       MCTL_CR_ROW_BITS(para->ranks[1].row_bits), &mctl_com->cr_r1);
+	}
+
+	if (socid == SOCID_R40) {
+		/* Mux pin to A15 address line for single rank memory. */
+		if (!para->dual_rank)
+			setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15);
+	}
+}
+
+static void mctl_sys_init(uint16_t socid)
+{
+	struct sunxi_ccm_reg *const ccm =
+			(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	struct sunxi_mctl_ctl_reg *const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	clrbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE);
+	clrbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
+	clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
+	clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
+	clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
+	if (socid == SOCID_A64 || socid == SOCID_R40)
+		clrbits_le32(&ccm->pll11_cfg, CCM_PLL11_CTRL_EN);
+	udelay(10);
+
+	clrbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
+	udelay(1000);
+
+	if (socid == SOCID_A64 || socid == SOCID_R40) {
+		clock_set_pll11(CONFIG_DRAM_CLK * 2 * 1000000, false);
+		clrsetbits_le32(&ccm->dram_clk_cfg,
+				CCM_DRAMCLK_CFG_DIV_MASK |
+				CCM_DRAMCLK_CFG_SRC_MASK,
+				CCM_DRAMCLK_CFG_DIV(1) |
+				CCM_DRAMCLK_CFG_SRC_PLL11 |
+				CCM_DRAMCLK_CFG_UPD);
+	} else if (socid == SOCID_H3 || socid == SOCID_H5) {
+		clock_set_pll5(CONFIG_DRAM_CLK * 2 * 1000000, false);
+		clrsetbits_le32(&ccm->dram_clk_cfg,
+				CCM_DRAMCLK_CFG_DIV_MASK |
+				CCM_DRAMCLK_CFG_SRC_MASK,
+				CCM_DRAMCLK_CFG_DIV(1) |
+				CCM_DRAMCLK_CFG_SRC_PLL5 |
+				CCM_DRAMCLK_CFG_UPD);
+	}
+	mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
+
+	setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
+	setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
+	setbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
+	setbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE);
+
+	setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
+	udelay(10);
+
+	writel(socid == SOCID_H5 ? 0x8000 : 0xc00e, &mctl_ctl->clken);
+	udelay(500);
+}
+
+/* These are more guessed based on some Allwinner code. */
+#define DX_GCR_ODT_DYNAMIC	(0x0 << 4)
+#define DX_GCR_ODT_ALWAYS_ON	(0x1 << 4)
+#define DX_GCR_ODT_OFF		(0x2 << 4)
+
+static int mctl_channel_init(uint16_t socid, struct dram_para *para)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	unsigned int i;
+
+	mctl_set_cr(socid, para);
+	mctl_set_timing_params(socid, para);
+	mctl_set_master_priority(socid);
+
+	/* setting VTC, default disable all VT */
+	clrbits_le32(&mctl_ctl->pgcr[0], (1 << 30) | 0x3f);
+	if (socid == SOCID_H5)
+		setbits_le32(&mctl_ctl->pgcr[1], (1 << 24) | (1 << 26));
+	else
+		clrsetbits_le32(&mctl_ctl->pgcr[1], 1 << 24, 1 << 26);
+
+	/* increase DFI_PHY_UPD clock */
+	writel(PROTECT_MAGIC, &mctl_com->protect);
+	udelay(100);
+	clrsetbits_le32(&mctl_ctl->upd2, 0xfff << 16, 0x50 << 16);
+	writel(0x0, &mctl_com->protect);
+	udelay(100);
+
+	/* set dramc odt */
+	for (i = 0; i < 4; i++) {
+		u32 clearmask = (0x3 << 4) | (0x1 << 1) | (0x3 << 2) |
+				(0x3 << 12) | (0x3 << 14);
+		u32 setmask = IS_ENABLED(CONFIG_DRAM_ODT_EN) ?
+				DX_GCR_ODT_DYNAMIC : DX_GCR_ODT_OFF;
+
+		if (socid == SOCID_H5) {
+			clearmask |= 0x2 << 8;
+			setmask |= 0x4 << 8;
+		}
+		clrsetbits_le32(&mctl_ctl->dx[i].gcr, clearmask, setmask);
+	}
+
+	/* AC PDR should always ON */
+	clrsetbits_le32(&mctl_ctl->aciocr, socid == SOCID_H5 ? (0x1 << 11) : 0,
+			0x1 << 1);
+
+	/* set DQS auto gating PD mode */
+	setbits_le32(&mctl_ctl->pgcr[2], 0x3 << 6);
+
+	if (socid == SOCID_H3) {
+		/* dx ddr_clk & hdr_clk dynamic mode */
+		clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12));
+
+		/* dphy & aphy phase select 270 degree */
+		clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
+				(0x1 << 10) | (0x2 << 8));
+	} else if (socid == SOCID_A64 || socid == SOCID_H5) {
+		/* dphy & aphy phase select ? */
+		clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
+				(0x0 << 10) | (0x3 << 8));
+	} else if (socid == SOCID_R40) {
+		/* dx ddr_clk & hdr_clk dynamic mode (tpr13[9] == 0) */
+		clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12));
+
+		/* dphy & aphy phase select ? */
+		clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
+				(0x0 << 10) | (0x3 << 8));
+	}
+
+	/* set half DQ */
+	if (!para->bus_full_width) {
+#if defined CONFIG_SUNXI_DRAM_DW_32BIT
+		writel(0x0, &mctl_ctl->dx[2].gcr);
+		writel(0x0, &mctl_ctl->dx[3].gcr);
+#elif defined CONFIG_SUNXI_DRAM_DW_16BIT
+		writel(0x0, &mctl_ctl->dx[1].gcr);
+#else
+#error Unsupported DRAM bus width!
+#endif
+	}
+
+	/* data training configuration */
+	clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24,
+			(para->dual_rank ? 0x3 : 0x1) << 24);
+
+	mctl_set_bit_delays(para);
+	udelay(50);
+
+	if (socid == SOCID_H3) {
+		mctl_h3_zq_calibration_quirk(para);
+
+		mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
+			      PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE);
+	} else if (socid == SOCID_A64 || socid == SOCID_H5) {
+		clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, CONFIG_DRAM_ZQ);
+
+		mctl_phy_init(PIR_ZCAL | PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
+			      PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE);
+		/* no PIR_QSGATE for H5 ???? */
+	} else if (socid == SOCID_R40) {
+		clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, CONFIG_DRAM_ZQ);
+
+		mctl_phy_init(PIR_ZCAL | PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
+			      PIR_DRAMRST | PIR_DRAMINIT);
+	}
+
+	/* detect ranks and bus width */
+	if (readl(&mctl_ctl->pgsr[0]) & (0xfe << 20)) {
+		/* only one rank */
+		if (((readl(&mctl_ctl->dx[0].gsr[0]) >> 24) & 0x2)
+#if defined CONFIG_SUNXI_DRAM_DW_32BIT
+		    || ((readl(&mctl_ctl->dx[1].gsr[0]) >> 24) & 0x2)
+#endif
+		    ) {
+			clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24, 0x1 << 24);
+			para->dual_rank = 0;
+		}
+
+		/* only half DQ width */
+#if defined CONFIG_SUNXI_DRAM_DW_32BIT
+		if (((readl(&mctl_ctl->dx[2].gsr[0]) >> 24) & 0x1) ||
+		    ((readl(&mctl_ctl->dx[3].gsr[0]) >> 24) & 0x1)) {
+			writel(0x0, &mctl_ctl->dx[2].gcr);
+			writel(0x0, &mctl_ctl->dx[3].gcr);
+			para->bus_full_width = 0;
+		}
+#elif defined CONFIG_SUNXI_DRAM_DW_16BIT
+		if ((readl(&mctl_ctl->dx[1].gsr[0]) >> 24) & 0x1) {
+			writel(0x0, &mctl_ctl->dx[1].gcr);
+			para->bus_full_width = 0;
+		}
+#endif
+
+		mctl_set_cr(socid, para);
+		udelay(20);
+
+		/* re-train */
+		mctl_phy_init(PIR_QSGATE);
+		if (readl(&mctl_ctl->pgsr[0]) & (0xfe << 20))
+			return 1;
+	}
+
+	/* check the dramc status */
+	mctl_await_completion(&mctl_ctl->statr, 0x1, 0x1);
+
+	/* liuke added for refresh debug */
+	setbits_le32(&mctl_ctl->rfshctl0, 0x1 << 31);
+	udelay(10);
+	clrbits_le32(&mctl_ctl->rfshctl0, 0x1 << 31);
+	udelay(10);
+
+	/* set PGCR3, CKE polarity */
+	if (socid == SOCID_H3)
+		writel(0x00aa0060, &mctl_ctl->pgcr[3]);
+	else if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40)
+		writel(0xc0aa0060, &mctl_ctl->pgcr[3]);
+
+	/* power down zq calibration module for power save */
+	setbits_le32(&mctl_ctl->zqcr, ZQCR_PWRDOWN);
+
+	/* enable master access */
+	writel(0xffffffff, &mctl_com->maer);
+
+	return 0;
+}
+
+/*
+ * The actual values used here are taken from Allwinner provided boot0
+ * binaries, though they are probably board specific, so would likely benefit
+ * from invidual tuning for each board. Apparently a lot of boards copy from
+ * some Allwinner reference design, so we go with those generic values for now
+ * in the hope that they are reasonable for most (all?) boards.
+ */
+#define SUN8I_H3_DX_READ_DELAYS					\
+	{{ 18, 18, 18, 18, 18, 18, 18, 18, 18,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 18, 18, 18, 18, 18, 18, 18, 18, 18,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 }}
+#define SUN8I_H3_DX_WRITE_DELAYS				\
+	{{  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  6 }}
+#define SUN8I_H3_AC_DELAYS					\
+	{  0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0      }
+
+#define SUN8I_R40_DX_READ_DELAYS				\
+	{{ 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 } }
+#define SUN8I_R40_DX_WRITE_DELAYS				\
+	{{  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 } }
+#define SUN8I_R40_AC_DELAYS					\
+	{  0,  0,  3,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0      }
+
+#define SUN50I_A64_DX_READ_DELAYS				\
+	{{ 16, 16, 16, 16, 17, 16, 16, 17, 16,  1,  0 },	\
+	 { 17, 17, 17, 17, 17, 17, 17, 17, 17,  1,  0 },	\
+	 { 16, 17, 17, 16, 16, 16, 16, 16, 16,  0,  0 },	\
+	 { 17, 17, 17, 17, 17, 17, 17, 17, 17,  1,  0 }}
+#define SUN50I_A64_DX_WRITE_DELAYS				\
+	{{  0,  0,  0,  0,  0,  0,  0,  0,  0, 15, 15 },	\
+	 {  0,  0,  0,  0,  1,  1,  1,  1,  0, 10, 10 },	\
+	 {  1,  0,  1,  1,  1,  1,  1,  1,  0, 11, 11 },	\
+	 {  1,  0,  0,  1,  1,  1,  1,  1,  0, 12, 12 }}
+#define SUN50I_A64_AC_DELAYS					\
+	{  5,  5, 13, 10,  2,  5,  3,  3,			\
+	   0,  3,  3,  3,  1,  0,  0,  0,			\
+	   3,  4,  0,  3,  4,  1,  4,  0,			\
+	   1,  1,  0,  1, 13,  5,  4      }
+
+#define SUN8I_H5_DX_READ_DELAYS					\
+	{{ 14, 15, 17, 17, 17, 17, 17, 18, 17,  3,  3 },	\
+	 { 21, 21, 12, 22, 21, 21, 21, 21, 21,  3,  3 },	\
+	 { 16, 19, 19, 17, 22, 22, 21, 22, 19,  3,  3 },	\
+	 { 21, 21, 22, 22, 20, 21, 19, 19, 19,  3,  3 } }
+#define SUN8I_H5_DX_WRITE_DELAYS				\
+	{{  1,  2,  3,  4,  3,  4,  4,  4,  6,  6,  6 },	\
+	 {  6,  6,  6,  5,  5,  5,  5,  5,  6,  6,  6 },	\
+	 {  0,  2,  4,  2,  6,  5,  5,  5,  6,  6,  6 },	\
+	 {  3,  3,  3,  2,  2,  1,  1,  1,  4,  4,  4 } }
+#define SUN8I_H5_AC_DELAYS					\
+	{  0,  0,  5,  5,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  3,  3,  3,  3,			\
+	   3,  3,  3,  3,  3,  3,  3,  3,			\
+	   3,  3,  3,  3,  2,  0,  0      }
+
+unsigned long sunxi_dram_init(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+	unsigned long size;
+	struct dram_para para = {
+		.dual_rank = 1,
+		.bus_full_width = 1,
+		.ranks = {
+			{
+				.row_bits = 15,
+				.bank_bits = 3,
+				.page_size = 4096,
+			},
+			{
+				.row_bits = 15,
+				.bank_bits = 3,
+				.page_size = 4096,
+			}
+		},
+#if defined(CONFIG_MACH_SUN8I_H3)
+		.dx_read_delays  = SUN8I_H3_DX_READ_DELAYS,
+		.dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN8I_H3_AC_DELAYS,
+#elif defined(CONFIG_MACH_SUN8I_R40)
+		.dx_read_delays  = SUN8I_R40_DX_READ_DELAYS,
+		.dx_write_delays = SUN8I_R40_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN8I_R40_AC_DELAYS,
+#elif defined(CONFIG_MACH_SUN50I)
+		.dx_read_delays  = SUN50I_A64_DX_READ_DELAYS,
+		.dx_write_delays = SUN50I_A64_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN50I_A64_AC_DELAYS,
+#elif defined(CONFIG_MACH_SUN50I_H5)
+		.dx_read_delays  = SUN8I_H5_DX_READ_DELAYS,
+		.dx_write_delays = SUN8I_H5_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN8I_H5_AC_DELAYS,
+#endif
+	};
+/*
+ * Let the compiler optimize alternatives away by passing this value into
+ * the static functions. This saves us #ifdefs, but still keeps the binary
+ * small.
+ */
+#if defined(CONFIG_MACH_SUN8I_H3)
+	uint16_t socid = SOCID_H3;
+#elif defined(CONFIG_MACH_SUN8I_R40)
+	uint16_t socid = SOCID_R40;
+	/* Currently we cannot support R40 with dual rank memory */
+	para.dual_rank = 0;
+#elif defined(CONFIG_MACH_SUN8I_V3S)
+	/* TODO: set delays and mbus priority for V3s */
+	uint16_t socid = SOCID_H3;
+#elif defined(CONFIG_MACH_SUN50I)
+	uint16_t socid = SOCID_A64;
+#elif defined(CONFIG_MACH_SUN50I_H5)
+	uint16_t socid = SOCID_H5;
+#endif
+
+	mctl_sys_init(socid, &para);
+	if (mctl_channel_init(socid, &para))
+		return 0;
+
+	if (para.dual_rank)
+		writel(0x00000303, &mctl_ctl->odtmap);
+	else
+		writel(0x00000201, &mctl_ctl->odtmap);
+	udelay(1);
+
+	/* odt delay */
+	if (socid == SOCID_H3)
+		writel(0x0c000400, &mctl_ctl->odtcfg);
+
+	if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40) {
+		/* VTF enable (tpr13[8] == 1) */
+		setbits_le32(&mctl_ctl->vtfcr,
+			     (socid != SOCID_A64 ? 3 : 2) << 8);
+		/* DQ hold disable (tpr13[26] == 1) */
+		clrbits_le32(&mctl_ctl->pgcr[2], (1 << 13));
+	}
+
+	/* clear credit value */
+	setbits_le32(&mctl_com->cccr, 1 << 31);
+	udelay(10);
+
+	mctl_set_cr(socid, &para);
+
+	return 0;
+}
+
+
+static void mctl_sys_init(uint16_t socid)
+{
+	struct sunxi_ccm_reg *const ccm =
+			(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	struct sunxi_mctl_ctl_reg *const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	clrbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE);
+	clrbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
+	clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
+	clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
+	clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
+	if (socid == SOCID_A64 || socid == SOCID_R40)
+		clrbits_le32(&ccm->pll11_cfg, CCM_PLL11_CTRL_EN);
+	udelay(10);
+
+	clrbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
+	udelay(1000);
+
+	if (socid == SOCID_A64 || socid == SOCID_R40) {
+		clock_set_pll11(CONFIG_DRAM_CLK * 2 * 1000000, false);
+		clrsetbits_le32(&ccm->dram_clk_cfg,
+				CCM_DRAMCLK_CFG_DIV_MASK |
+				CCM_DRAMCLK_CFG_SRC_MASK,
+				CCM_DRAMCLK_CFG_DIV(1) |
+				CCM_DRAMCLK_CFG_SRC_PLL11 |
+				CCM_DRAMCLK_CFG_UPD);
+	} else if (socid == SOCID_H3 || socid == SOCID_H5) {
+		clock_set_pll5(CONFIG_DRAM_CLK * 2 * 1000000, false);
+		clrsetbits_le32(&ccm->dram_clk_cfg,
+				CCM_DRAMCLK_CFG_DIV_MASK |
+				CCM_DRAMCLK_CFG_SRC_MASK,
+				CCM_DRAMCLK_CFG_DIV(1) |
+				CCM_DRAMCLK_CFG_SRC_PLL5 |
+				CCM_DRAMCLK_CFG_UPD);
+	}
+	mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
+
+	setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
+	setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
+	setbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
+	setbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE);
+
+	setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
+	udelay(10);
+
+	writel(socid == SOCID_H5 ? 0x8000 : 0xc00e, &mctl_ctl->clken);
+	udelay(500);
+}
+
+void sunxi_dram_cfg(void)
+{
+	mctl_sys_init(socid);
+	if (mctl_channel_init(socid, &para))
+		return 0;
+
+}
+
+/* ------------------------------------------------------------ */
+
+static inline unsigned long sunxi_dram_init(uint16_t socid, struct dram_para *para)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	unsigned long size;
+
+	mctl_sys_init(socid, para);
+	if (mctl_channel_init(socid, para))
+		return 0;
+
+	if (para->dual_rank)
+		writel(0x00000303, &mctl_ctl->odtmap);
+	else
+		writel(0x00000201, &mctl_ctl->odtmap);
+	udelay(1);
+
+	/* odt delay */
+	if (socid == SOCID_H3)
+		writel(0x0c000400, &mctl_ctl->odtcfg);
+
+	if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40) {
+		/* VTF enable (tpr13[8] == 1) */
+		setbits_le32(&mctl_ctl->vtfcr,
+			     (socid != SOCID_A64 ? 3 : 2) << 8);
+		/* DQ hold disable (tpr13[26] == 1) */
+		clrbits_le32(&mctl_ctl->pgcr[2], (1 << 13));
+	}
+
+	/* clear credit value */
+	setbits_le32(&mctl_com->cccr, 1 << 31);
+	udelay(10);
+
+	mctl_auto_detect_dram_size(socid, para);
+	mctl_set_cr(socid, para);
+
+	size = mctl_calc_rank_size(&para->ranks[0]);
+	if (socid == SOCID_A64 || socid == SOCID_R40) {
+		if (para->dual_rank)
+			size += mctl_calc_rank_size(&para->ranks[1]);
+	} else if (para->dual_rank) {
+		size *= 2;
+	}
+
+	return size;
+}
+
+/*
+ * The actual values used here are taken from Allwinner provided boot0
+ * binaries, though they are probably board specific, so would likely benefit
+ * from invidual tuning for each board. Apparently a lot of boards copy from
+ * some Allwinner reference design, so we go with those generic values for now
+ * in the hope that they are reasonable for most (all?) boards.
+ */
+#define SUN8I_H3_DX_READ_DELAYS					\
+	{{ 18, 18, 18, 18, 18, 18, 18, 18, 18,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 18, 18, 18, 18, 18, 18, 18, 18, 18,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 }}
+#define SUN8I_H3_DX_WRITE_DELAYS				\
+	{{  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  6 }}
+#define SUN8I_H3_AC_DELAYS					\
+	{  0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0      }
+
+#define SUN8I_R40_DX_READ_DELAYS				\
+	{{ 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 },	\
+	 { 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  0 } }
+#define SUN8I_R40_DX_WRITE_DELAYS				\
+	{{  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 },	\
+	 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0 } }
+#define SUN8I_R40_AC_DELAYS					\
+	{  0,  0,  3,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0      }
+
+#define SUN50I_A64_DX_READ_DELAYS				\
+	{{ 16, 16, 16, 16, 17, 16, 16, 17, 16,  1,  0 },	\
+	 { 17, 17, 17, 17, 17, 17, 17, 17, 17,  1,  0 },	\
+	 { 16, 17, 17, 16, 16, 16, 16, 16, 16,  0,  0 },	\
+	 { 17, 17, 17, 17, 17, 17, 17, 17, 17,  1,  0 }}
+#define SUN50I_A64_DX_WRITE_DELAYS				\
+	{{  0,  0,  0,  0,  0,  0,  0,  0,  0, 15, 15 },	\
+	 {  0,  0,  0,  0,  1,  1,  1,  1,  0, 10, 10 },	\
+	 {  1,  0,  1,  1,  1,  1,  1,  1,  0, 11, 11 },	\
+	 {  1,  0,  0,  1,  1,  1,  1,  1,  0, 12, 12 }}
+#define SUN50I_A64_AC_DELAYS					\
+	{  5,  5, 13, 10,  2,  5,  3,  3,			\
+	   0,  3,  3,  3,  1,  0,  0,  0,			\
+	   3,  4,  0,  3,  4,  1,  4,  0,			\
+	   1,  1,  0,  1, 13,  5,  4      }
+
+#define SUN8I_H5_DX_READ_DELAYS					\
+	{{ 14, 15, 17, 17, 17, 17, 17, 18, 17,  3,  3 },	\
+	 { 21, 21, 12, 22, 21, 21, 21, 21, 21,  3,  3 },	\
+	 { 16, 19, 19, 17, 22, 22, 21, 22, 19,  3,  3 },	\
+	 { 21, 21, 22, 22, 20, 21, 19, 19, 19,  3,  3 } }
+#define SUN8I_H5_DX_WRITE_DELAYS				\
+	{{  1,  2,  3,  4,  3,  4,  4,  4,  6,  6,  6 },	\
+	 {  6,  6,  6,  5,  5,  5,  5,  5,  6,  6,  6 },	\
+	 {  0,  2,  4,  2,  6,  5,  5,  5,  6,  6,  6 },	\
+	 {  3,  3,  3,  2,  2,  1,  1,  1,  4,  4,  4 } }
+#define SUN8I_H5_AC_DELAYS					\
+	{  0,  0,  5,  5,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  3,  3,  3,  3,			\
+	   3,  3,  3,  3,  3,  3,  3,  3,			\
+	   3,  3,  3,  3,  2,  0,  0      }
+
+static struct dram_para sun8i_h3_para = {
+	.dual_rank = 1,
+	.bus_full_width = 1,
+	.ranks = {
+		{ .row_bits = 15, .bank_bits = 3, .page_size = 4096, },
+		{ .row_bits = 15, .bank_bits = 3, .page_size = 4096, },
+	},
+	.dx_read_delays  = SUN8I_H3_DX_READ_DELAYS,
+	.dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS,
+	.ac_delays	 = SUN8I_H3_AC_DELAYS,
+};
+
+static struct dram_para sun8i_r40_para = {
+	.dual_rank = 0, /* Currently we cannot support R40 with dual rank memory */
+	.bus_full_width = 1,
+	.ranks = {
+		{ .row_bits = 15, .bank_bits = 3, .page_size = 4096, },
+		{ .row_bits = 15, .bank_bits = 3, .page_size = 4096, },
+	},
+		.dx_read_delays  = SUN8I_R40_DX_READ_DELAYS,
+		.dx_write_delays = SUN8I_R40_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN8I_R40_AC_DELAYS,
+};
+
+static struct dram_para sun50i_a64_para = {
+	.dual_rank = 1,
+	.bus_full_width = 1,
+	.ranks = {
+		{ .row_bits = 15, .bank_bits = 3, .page_size = 4096, },
+		{ .row_bits = 15, .bank_bits = 3, .page_size = 4096, },
+	},
+	.dx_read_delays  = SUN50I_A64_DX_READ_DELAYS,
+	.dx_write_delays = SUN50I_A64_DX_WRITE_DELAYS,
+	.ac_delays	 = SUN50I_A64_AC_DELAYS,
+};
+
+static struct dram_para sun50i_h5_para = {
+	.dual_rank = 1,
+	.bus_full_width = 1,
+	.ranks = {
+		{ .row_bits = 15, .bank_bits = 3, .page_size = 4096, },
+		{ .row_bits = 15, .bank_bits = 3, .page_size = 4096, },
+	},
+	.dx_read_delays  = SUN8I_H5_DX_READ_DELAYS,
+	.dx_write_delays = SUN8I_H5_DX_WRITE_DELAYS,
+	.ac_delays	 = SUN8I_H5_AC_DELAYS,
+};
+
+unsigned long sun8i_h3_dram_init(void)
+{
+	return sunxi_dram_init(SOCID_H3, &sun8i_h3_para);
+}
+
+unsigned long sun8i_r40_dram_init(void)
+{
+	return sunxi_dram_init(SOCID_R40, &sun8i_r40_para);
+}
+
+unsigned long sun8i_v3s_dram_init(void)
+{
+	/* TODO: set delays and mbus priority for V3s */
+	return sunxi_dram_init(SOCID_H3, &sun8i_h3_para);
+}
+
+unsigned long sun50i_dram_init(void)
+{
+	return sunxi_dram_init(SOCID_A64, &sun50i_a64_para);
+}
+
+unsigned long sun50i_h5_dram_init(void)
+{
+	return sunxi_dram_init(SOCID_H5, &sun50i_h5_para);
+}
diff --git a/include/mach/sunxi/init.h b/include/mach/sunxi/init.h
index 26cc022fde..40c072a028 100644
--- a/include/mach/sunxi/init.h
+++ b/include/mach/sunxi/init.h
@@ -3,4 +3,7 @@
 #ifndef __MACH_INIT_H
 #define __MACH_INIT_H
 
+unsigned long sun50i_a64_ddr3_dram_init(void);
+unsigned long sun50i_a64_lpddr3_dram_init(void);
+
 #endif
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 10/11] arm: boards: sunxi: Add initial support for the pinephone
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (8 preceding siblings ...)
  2023-05-10 23:37 ` [RFC PATCH 09/11] arm: sunxi: Add sun50i SDRAM init Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-18 19:39   ` Ahmad Fatoum
  2023-05-10 23:37 ` [RFC PATCH 11/11] arm: boards: sunxi: Add pine64 board Jules Maselbas
  10 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

---
 arch/arm/boards/Makefile                    |   1 +
 arch/arm/boards/pine64-pinephone/Makefile   |   2 +
 arch/arm/boards/pine64-pinephone/board.c    |   0
 arch/arm/boards/pine64-pinephone/lowlevel.c | 119 ++++++++++++++++++++
 arch/arm/configs/pinephone_defconfig        |  11 ++
 arch/arm/dts/Makefile                       |   1 +
 arch/arm/dts/sun50i-a64-pinephone-1_2.dts   |   3 +
 arch/arm/mach-sunxi/Kconfig                 |  21 ++++
 arch/arm/mach-sunxi/cpu_init.c              |  15 ++-
 images/Makefile.sunxi                       |   9 ++
 include/mach/sunxi/init.h                   |   4 +
 11 files changed, 184 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/boards/pine64-pinephone/Makefile
 create mode 100644 arch/arm/boards/pine64-pinephone/board.c
 create mode 100644 arch/arm/boards/pine64-pinephone/lowlevel.c
 create mode 100644 arch/arm/configs/pinephone_defconfig
 create mode 100644 arch/arm/dts/sun50i-a64-pinephone-1_2.dts

diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile
index 37b1650e63..083ec2dce6 100644
--- a/arch/arm/boards/Makefile
+++ b/arch/arm/boards/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_MACH_PHYTEC_SOM_IMX6)		+= phytec-som-imx6/
 obj-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7)		+= phytec-phycore-imx7/
 obj-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1)	+= phytec-phycore-stm32mp1/
 obj-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ)		+= phytec-som-imx8mq/
+obj-$(CONFIG_MACH_PINE64_PINEPHONE)		+= pine64-pinephone/
 obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3)	+= plathome-openblocks-ax3/
 obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6)	+= plathome-openblocks-a6/
 obj-$(CONFIG_MACH_PM9261)			+= pm9261/
diff --git a/arch/arm/boards/pine64-pinephone/Makefile b/arch/arm/boards/pine64-pinephone/Makefile
new file mode 100644
index 0000000000..092c31d6b2
--- /dev/null
+++ b/arch/arm/boards/pine64-pinephone/Makefile
@@ -0,0 +1,2 @@
+lwl-y += lowlevel.o
+obj-y += board.o
diff --git a/arch/arm/boards/pine64-pinephone/board.c b/arch/arm/boards/pine64-pinephone/board.c
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/arch/arm/boards/pine64-pinephone/lowlevel.c b/arch/arm/boards/pine64-pinephone/lowlevel.c
new file mode 100644
index 0000000000..8e7ba1c0a9
--- /dev/null
+++ b/arch/arm/boards/pine64-pinephone/lowlevel.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <common.h>
+#include <debug_ll.h>
+#include <linux/sizes.h>
+#include <linux/bitops.h>
+#include <asm/barebox-arm.h>
+#include <asm/cache.h>
+#include <mach/sunxi/init.h>
+#include <mach/sunxi/xload.h>
+#include <mach/sunxi/egon.h>
+#include <mach/sunxi/rmr_switch.h>
+#include <mach/sunxi/sun50i-regs.h>
+#include <mach/sunxi/sunxi-pinctrl.h>
+
+#define STACK_TOP   CONFIG_SUNXI_PBL_STACK_TOP
+
+#ifdef DEBUG
+static void debug_led_rgb(int rgb)
+{
+	void __iomem *piobase = IOMEM(SUN50I_PIO_BASE_ADDR);
+	uint32_t clr, set = 0;
+	int r = rgb & 0xff0000;
+	int g = rgb & 0x00ff00;
+	int b = rgb & 0x0000ff;
+
+	clr = (1 << 19) | (1 << 18) | (1 << 20);
+	set |= r ? 1 << 19 : 0;
+	set |= g ? 1 << 18 : 0;
+	set |= b ? 1 << 20 : 0;
+
+	clrsetbits_le32(piobase + PIO_PD_DATA, clr, set);
+}
+
+static void debug_led_init(void)
+{
+	void __iomem *ccubase = IOMEM(SUN50I_CCU_BASE_ADDR);
+	void __iomem *piobase = IOMEM(SUN50I_PIO_BASE_ADDR);
+
+	/* PIO clock enable */
+	setbits_le32(ccubase + CCU_BUS_CLK_GATE2, 1u << 5);
+	/* LED set output */
+	clrsetbits_le32(piobase + PIO_PD_CFG2, 0x000fff00, 0x00011100);
+}
+#else
+static void debug_led_rgb(int rgb) {}
+static void debug_led_init(void) {}
+#endif
+
+static void setup_uart(void)
+{
+	void __iomem *base = IOMEM(SUN50I_PIO_BASE_ADDR);
+
+	/* UART0 pinmux (PB8 + PB9) */
+	clrsetbits_le32(base + PIO_PB_CFG1, 0x000000ff, 0x00000044);
+
+	debug_ll_init();
+	putc_ll('>');
+}
+
+ENTRY_FUNCTION_WITHSTACK(start_pine64_pinephone_xload, STACK_TOP, r0, r1, r2)
+{
+
+	sunxi_egon_header();
+	sunxi_switch_to_aarch64();
+
+	debug_led_init();
+	debug_led_rgb(0xff0000);
+
+	sun50i_cpu_lowlevel_init();
+	setup_uart();
+	debug_led_rgb(0xffff00);
+
+	relocate_to_current_adr();
+	setup_c();
+
+	sun50i_a64_lpddr3_dram_init();
+
+	debug_led_rgb(0xff0000);
+
+	sun50i_cpu_lowlevel_reset();
+}
+
+ENTRY_FUNCTION_WITHSTACK(start_pine64_pinephone, STACK_TOP, r0, r1, r2)
+{
+	extern char __dtb_z_sun50i_a64_pinephone_1_2_start[];
+	void *fdt;
+	u32 size;
+
+	sunxi_switch_to_aarch64();
+
+	debug_led_init();
+	debug_led_rgb(0xffff00);
+
+	sun50i_cpu_lowlevel_init();
+	setup_uart();
+
+	relocate_to_current_adr();
+	setup_c();
+
+	/* Skip SDRAM initialization if we run from it */
+	if (get_pc() < SUN50I_DRAM_BASE_ADDR) {
+		size = sun50i_a64_lpddr3_dram_init();
+		if (size == 0) {
+			puts_ll("FAIL: dram init\r\n");
+			goto reset;
+		}
+		puthex_ll(size);
+		putc_ll('\r');	putc_ll('\n');
+	}
+
+	puts_ll("now booting\r\n");
+	fdt = __dtb_z_sun50i_a64_pinephone_1_2_start + get_runtime_offset();
+	barebox_arm_entry(SUN50I_DRAM_BASE_ADDR, SZ_1G, fdt);
+
+reset:
+	debug_led_rgb(0xff0000);
+	sun50i_cpu_lowlevel_reset();
+}
diff --git a/arch/arm/configs/pinephone_defconfig b/arch/arm/configs/pinephone_defconfig
new file mode 100644
index 0000000000..b63f8c6ec7
--- /dev/null
+++ b/arch/arm/configs/pinephone_defconfig
@@ -0,0 +1,11 @@
+CONFIG_ARCH_SUNXI=y
+CONFIG_SUNXI_MULTI_BOARDS=y
+CONFIG_MACH_PINE64_PINEPHONE=y
+CONFIG_MACH_PINE64_PINE64=y
+CONFIG_DEBUG_LL=y
+CONFIG_DRIVER_SERIAL_NS16550=y
+CONFIG_MCI=y
+CONFIG_MCI_SUNXI_SMHC=y
+CONFIG_CMD_DMESG=y
+CONFIG_MCI_STARTUP=y
+CONFIG_FS_FAT=y
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 0a7cceb461..564a60a0d8 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -81,6 +81,7 @@ lwl-$(CONFIG_MACH_PHYTEC_SOM_IMX6) += imx6q-phytec-phycard.dtb.o \
 lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7) += imx7d-phyboard-zeta.dtb.o
 lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1) += stm32mp157c-phycore-stm32mp1-3.dtb.o
 lwl-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ) += imx8mq-phytec-phycore-som.dtb.o
+lwl-$(CONFIG_MACH_PINE64_PINEPHONE) += sun50i-a64-pinephone-1_2.dtb.o
 lwl-$(CONFIG_MACH_PINE64_QUARTZ64) += rk3566-quartz64-a.dtb.o
 lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3) += armada-xp-openblocks-ax3-4-bb.dtb.o
 lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6) += kirkwood-openblocks_a6-bb.dtb.o
diff --git a/arch/arm/dts/sun50i-a64-pinephone-1_2.dts b/arch/arm/dts/sun50i-a64-pinephone-1_2.dts
new file mode 100644
index 0000000000..ac6fba1b91
--- /dev/null
+++ b/arch/arm/dts/sun50i-a64-pinephone-1_2.dts
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <arm64/allwinner/sun50i-a64-pinephone-1.2.dts>
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 2580a9e56a..e2d236f020 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -9,6 +9,22 @@ config SUNXI_RVBAR_IOMAP
 	default 0x017000a0  if ARCH_SUN50I_A64
 	# default 0x09010040 if ARCH_SUN50I_H5
 
+config SUNXI_PBL_STACK_TOP
+       hex
+       default 0x00018000 if ARCH_SUN50I_A64
+
+config ARCH_SUN50I_A64
+	bool
+	select CPU_V8
+	select CPU_SUPPORTS_64BIT_KERNEL
+	select CLOCKSOURCE_ARM_ARCHITECTED_TIMER
+	select PINCTRL_SUN50I_A64
+	select HAS_DEBUG_LL
+	select PBL_RELOCATABLE
+	select PBL_FULLY_PIC
+	help
+	  Allwiner A64 (sun50iw1) SoC
+
 config SUNXI_DEBUG_LL_UART_BASE
 	hex
 	default 0x01c28000
@@ -20,6 +36,11 @@ menuconfig SUNXI_MULTI_BOARDS
 
 if SUNXI_MULTI_BOARDS
 
+config MACH_PINE64_PINEPHONE
+	bool "Allwinner A64 based Pine64 PinePhone"
+	select ARCH_SUN50I_A64
+	select ARM_USE_COMPRESSED_DTB
+
 endif
 
 endif
diff --git a/arch/arm/mach-sunxi/cpu_init.c b/arch/arm/mach-sunxi/cpu_init.c
index f4092d8d5d..52b7a396b3 100644
--- a/arch/arm/mach-sunxi/cpu_init.c
+++ b/arch/arm/mach-sunxi/cpu_init.c
@@ -27,7 +27,18 @@ static void sunxi_ccu_init(void __iomem *ccu)
 	setbits_le32(ccu + CCU_BUS_SOFT_RST4, 1u << 16);
 }
 
-static void sunxi_cpu_lowlevel_init(void)
+void sun50i_cpu_lowlevel_init(void)
 {
-	sunxi_ccu_init(IOMEM(SUNXI_CCU_BASE_ADDR));
+	arm_cpu_lowlevel_init();
+	sunxi_ccu_init(IOMEM(SUN50I_CCU_BASE_ADDR));
+}
+
+void sun50i_cpu_lowlevel_reset(void)
+{
+	void __iomem *reg = IOMEM(SUN50I_TIMER_BASE_ADDR);
+	/* Set the watchdog for its shortest interval (.5s) and wait */
+	writel(1, reg + 0xB4); /* reset whole system */
+	writel(1, reg + 0xB8); /* enable */
+	writel((0xa57 << 1) | 1, reg + 0xB0); /* restart */
+	while (1);
 }
diff --git a/images/Makefile.sunxi b/images/Makefile.sunxi
index 778d6f9bdf..070b1bf00d 100644
--- a/images/Makefile.sunxi
+++ b/images/Makefile.sunxi
@@ -11,3 +11,12 @@ $(obj)/%.egonimg: $(obj)/% FORCE
 	$(call if_changed,egon_image)
 
 # ----------------------------------------------------------------------
+
+pblb-$(CONFIG_MACH_PINE64_PINEPHONE) += start_pine64_pinephone_xload
+MAX_PBL_IMAGE_SIZE_start_pine64_pinephone_xload = 0x8000
+FILE_barebox-pine64-pinephone_xload.img = start_pine64_pinephone_xload.pblb.egonimg
+image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone_xload.img
+
+pblb-$(CONFIG_MACH_PINE64_PINEPHONE) += start_pine64_pinephone
+FILE_barebox-pine64-pinephone.img = start_pine64_pinephone.pblb
+image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone.img
diff --git a/include/mach/sunxi/init.h b/include/mach/sunxi/init.h
index 40c072a028..441425be63 100644
--- a/include/mach/sunxi/init.h
+++ b/include/mach/sunxi/init.h
@@ -6,4 +6,8 @@
 unsigned long sun50i_a64_ddr3_dram_init(void);
 unsigned long sun50i_a64_lpddr3_dram_init(void);
 
+void sun50i_cpu_lowlevel_init(void);
+
+void sun50i_cpu_lowlevel_reset(void);
+
 #endif
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* [RFC PATCH 11/11] arm: boards: sunxi: Add pine64 board
  2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (9 preceding siblings ...)
  2023-05-10 23:37 ` [RFC PATCH 10/11] arm: boards: sunxi: Add initial support for the pinephone Jules Maselbas
@ 2023-05-10 23:37 ` Jules Maselbas
  2023-05-18 19:44   ` Ahmad Fatoum
  10 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-10 23:37 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

---
 arch/arm/boards/Makefile                 |   1 +
 arch/arm/boards/pine64-pine64/Makefile   |   1 +
 arch/arm/boards/pine64-pine64/lowlevel.c | 148 +++++++++++++++++++++++
 arch/arm/dts/Makefile                    |   1 +
 arch/arm/dts/sun50i-a64-pine64-plus.dts  |  18 +++
 arch/arm/mach-sunxi/Kconfig              |   5 +
 images/Makefile.sunxi                    |  10 ++
 7 files changed, 184 insertions(+)
 create mode 100644 arch/arm/boards/pine64-pine64/Makefile
 create mode 100644 arch/arm/boards/pine64-pine64/lowlevel.c
 create mode 100644 arch/arm/dts/sun50i-a64-pine64-plus.dts

diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile
index 083ec2dce6..1fa1f962b3 100644
--- a/arch/arm/boards/Makefile
+++ b/arch/arm/boards/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7)		+= phytec-phycore-imx7/
 obj-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1)	+= phytec-phycore-stm32mp1/
 obj-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ)		+= phytec-som-imx8mq/
 obj-$(CONFIG_MACH_PINE64_PINEPHONE)		+= pine64-pinephone/
+obj-$(CONFIG_MACH_PINE64_PINE64)		+= pine64-pine64/
 obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3)	+= plathome-openblocks-ax3/
 obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6)	+= plathome-openblocks-a6/
 obj-$(CONFIG_MACH_PM9261)			+= pm9261/
diff --git a/arch/arm/boards/pine64-pine64/Makefile b/arch/arm/boards/pine64-pine64/Makefile
new file mode 100644
index 0000000000..b08c4a93ca
--- /dev/null
+++ b/arch/arm/boards/pine64-pine64/Makefile
@@ -0,0 +1 @@
+lwl-y += lowlevel.o
diff --git a/arch/arm/boards/pine64-pine64/lowlevel.c b/arch/arm/boards/pine64-pine64/lowlevel.c
new file mode 100644
index 0000000000..744f37a8e0
--- /dev/null
+++ b/arch/arm/boards/pine64-pine64/lowlevel.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <common.h>
+#include <debug_ll.h>
+#include <linux/sizes.h>
+#include <linux/bitops.h>
+#include <asm/barebox-arm.h>
+#include <asm/cache.h>
+#include <mach/sunxi/init.h>
+#include <mach/sunxi/xload.h>
+#include <mach/sunxi/egon.h>
+#include <mach/sunxi/rmr_switch.h>
+#include <mach/sunxi/sun50i-regs.h>
+#include <mach/sunxi/sunxi-pinctrl.h>
+
+#define STACK_TOP   CONFIG_SUNXI_PBL_STACK_TOP
+
+#if 0
+static void sun50i_pinmux_set(void __iomem *pio, u32 port, u32 pins, u8 func)
+{
+	u32 i, msk = 0, cfg = 0;
+	for (i = 0; i < 8; i++) {
+		if (pins & (1 << i)) {
+			cfg |= func << (i * 4);
+			msk |= 0xf << (i * 4);
+		}
+	}
+	clrsetbits_le32(pio + port, msk, cfg);
+}
+#endif
+
+static void setup_uart(void)
+{
+	void __iomem *pio = IOMEM(SUN50I_PIO_BASE_ADDR);
+
+	/// ... uuuuhh
+//	sun50i_pinmux_set(pio, PIO_PB_CFG1, BIT(0) | BIT(1), 4);
+	/* UART0 pinmux (PB8 + PB9) */
+	clrsetbits_le32(pio + PIO_PB_CFG1, 0x000000ff, 0x00000044);
+
+	debug_ll_init();
+	putc_ll('>');
+}
+
+ENTRY_FUNCTION_WITHSTACK(start_pine64_pine64, STACK_TOP, r0, r1, r2)
+{
+	extern char __dtb_z_sun50i_a64_pine64_plus_start[];
+	void *fdt;
+	u32 size;
+
+	sunxi_switch_to_aarch64();
+
+	sun50i_cpu_lowlevel_init();
+	setup_uart();
+
+	relocate_to_current_adr();
+	setup_c();
+
+	/* Skip SDRAM initialization if we run from it */
+	if (get_pc() < SUN50I_DRAM_BASE_ADDR) {
+		size = sun50i_a64_ddr3_dram_init();
+		if (size == 0) {
+			puts_ll("FAIL: dram init\r\n");
+			goto reset;
+		}
+		puthex_ll(size);
+		putc_ll('\r');	putc_ll('\n');
+	}
+
+	puts_ll("now booting\r\n");
+	fdt = __dtb_z_sun50i_a64_pine64_plus_start + get_runtime_offset();
+	barebox_arm_entry(SUN50I_DRAM_BASE_ADDR, SZ_1G, fdt);
+
+reset:
+	sun50i_cpu_lowlevel_reset();
+}
+
+static void sun50i_mmc0_init(void)
+{
+	void __iomem *ccu = IOMEM(SUN50I_CCU_BASE_ADDR);
+	void __iomem *pio = IOMEM(SUN50I_PIO_BASE_ADDR);
+
+	/* - configure clock gate pinctrl controller */
+	/* PIO clock enable */
+	setbits_le32(ccu + CCU_BUS_CLK_GATE2, BIT(5));
+
+	/* - configure pinctrl for mmc controller */
+	/* set alt-function 2 for pins PF0 -> PF5 */
+	clrsetbits_le32(pio + PIO_PF_CFG0, 0x00ffffff, 0x00222222);
+//	sun50i_pinmux_set(pio, PIO_PF_CFG0, 0x3f, 2); TO BE REWORK'D
+
+	/* - configure clock gate mmc controller */
+	/* MMC0 and MMC2 clock un-gate and reset */
+	setbits_le32(ccu + 0x2c0, /* RST_BUS_MMC0 */ BIT(8) | /* RST_BUS_MMC2 */ BIT(10));
+	setbits_le32(ccu + 0x060, /* CLK_BUS_MMC0 */ BIT(8) | /* CLK_BUS_MMC2 */ BIT(10));
+
+	writel(BIT(31), ccu + 0x88); /* MMC0 clock gate */
+	writel(BIT(31), ccu + 0x90); /* MMC2 clock gate */
+}
+
+static void sunxi_fat_start_image(struct pbl_bio *bio, void *buf, size_t size)
+{
+	void (*start)(void) = buf;
+	int ret;
+
+	ret = pbl_fat_load(bio, "barebox.bin", buf, size);
+	if (ret < 0)
+		return;
+	sync_caches_for_execution();
+	start();
+}
+
+ENTRY_FUNCTION_WITHSTACK(start_pine64_pine64_xload, STACK_TOP, r0, r1, r2)
+{
+	struct pbl_bio bio;
+	u32 size;
+	int ret;
+
+	sunxi_egon_header();
+	sunxi_switch_to_aarch64();
+
+	sun50i_cpu_lowlevel_init();
+	setup_uart();
+
+	relocate_to_current_adr();
+	setup_c();
+
+	size = sun50i_a64_ddr3_dram_init();
+	if (size == 0) {
+		puts_ll("FAIL: dram init\r\n");
+		goto reset;
+	}
+
+	/* now let's boot from sd/mmc */
+	sun50i_mmc0_init();
+
+	/* - setup pbl mci driver */
+	ret = sunxi_mmc_bio_init(&bio, IOMEM(SUN50I_MMC0_BASE_ADDR), 24000000 /*osc24M */, 0);
+	if (ret)
+		goto reset;
+	puts_ll("bio init\r\n");
+
+	sunxi_fat_start_image(&bio, IOMEM(SUN50I_DRAM_BASE_ADDR), SZ_16M);
+	puts_ll("fat fail\r\n");
+
+reset:
+	sun50i_cpu_lowlevel_reset();
+}
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 564a60a0d8..b3de5ff54a 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -82,6 +82,7 @@ lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7) += imx7d-phyboard-zeta.dtb.o
 lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1) += stm32mp157c-phycore-stm32mp1-3.dtb.o
 lwl-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ) += imx8mq-phytec-phycore-som.dtb.o
 lwl-$(CONFIG_MACH_PINE64_PINEPHONE) += sun50i-a64-pinephone-1_2.dtb.o
+lwl-$(CONFIG_MACH_PINE64_PINE64) += sun50i-a64-pine64-plus.dtb.o
 lwl-$(CONFIG_MACH_PINE64_QUARTZ64) += rk3566-quartz64-a.dtb.o
 lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3) += armada-xp-openblocks-ax3-4-bb.dtb.o
 lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6) += kirkwood-openblocks_a6-bb.dtb.o
diff --git a/arch/arm/dts/sun50i-a64-pine64-plus.dts b/arch/arm/dts/sun50i-a64-pine64-plus.dts
new file mode 100644
index 0000000000..b7856bcddf
--- /dev/null
+++ b/arch/arm/dts/sun50i-a64-pine64-plus.dts
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <arm64/allwinner/sun50i-a64-pine64-plus.dts>
+/{
+	memory@40000000 {
+		device_type = "memory";
+		reg = <0x40000000 0x40000000>; /* 1 GB */
+	};
+};
+&mmc0 { /* SD */
+};
+&mmc1 { /* PIO */
+	max-frequency = <400000>;
+};
+&mmc2 { /* eMMC */
+	max-frequency = <400000>;
+	status = "ok";
+};
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index e2d236f020..f101ae45ed 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -41,6 +41,11 @@ config MACH_PINE64_PINEPHONE
 	select ARCH_SUN50I_A64
 	select ARM_USE_COMPRESSED_DTB
 
+config MACH_PINE64_PINE64
+	bool "Allwinner A64 based Pine64 PINE64"
+	select ARCH_SUN50I_A64
+	select ARM_USE_COMPRESSED_DTB
+
 endif
 
 endif
diff --git a/images/Makefile.sunxi b/images/Makefile.sunxi
index 070b1bf00d..75adba7f7f 100644
--- a/images/Makefile.sunxi
+++ b/images/Makefile.sunxi
@@ -20,3 +20,13 @@ image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone_xload.img
 pblb-$(CONFIG_MACH_PINE64_PINEPHONE) += start_pine64_pinephone
 FILE_barebox-pine64-pinephone.img = start_pine64_pinephone.pblb
 image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone.img
+
+pblb-$(CONFIG_MACH_PINE64_PINE64) += start_pine64_pine64
+FILE_barebox-pine64-pine64.img = start_pine64_pine64.pblb
+image-$(CONFIG_MACH_PINE64_PINE64) += barebox-pine64-pine64.img
+
+pblb-$(CONFIG_MACH_PINE64_PINE64) += start_pine64_pine64_xload
+MAX_PBL_IMAGE_SIZE_start_pine64_pine64_xload = 0x8000
+FILE_barebox-pine64-pine64_xload.img = start_pine64_pine64_xload.pblb.egonimg
+image-$(CONFIG_MACH_PINE64_PINE64) += barebox-pine64-pine64_xload.img
+
-- 
2.40.0




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 01/11] scripts: Add Allwinner eGON image support
  2023-05-10 23:37 ` [RFC PATCH 01/11] scripts: Add Allwinner eGON image support Jules Maselbas
@ 2023-05-11  7:25   ` Sascha Hauer
  2023-05-11 20:14     ` Jules Maselbas
  2023-05-18 18:47   ` Ahmad Fatoum
  1 sibling, 1 reply; 34+ messages in thread
From: Sascha Hauer @ 2023-05-11  7:25 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On Thu, May 11, 2023 at 01:37:01AM +0200, Jules Maselbas wrote:
> On power-up Allwinner SoC starts in boot ROM, aka BROM, which will search
> for an eGON image: first from the SD card, then from eMMC. If no image is
> found then the BROM will enter into FEL mode that can be used for initial
> programming and recovery of devices using USB.
> 
> The eGON header structure is adapted from u-boot: /include/sunxi_image.h,
> the header structure is also documented on https://linux-sunxi.org/EGON
> 
> BROM will load, at most, the first 32KB of the image into SRAM, including
> the header itself! The jump instruction in the header needs to be patched
> accordingly with the image size.

Do I understand it correctly that all code needed to load the full
barebox image needs to fit into these 32KiB?

Where must this image be placed? Directly at the origin of the SD card?
That would mean the partition table is somewhere inside the binary,
right?

> +
> +#define ALIGN(x, a)		__ALIGN_MASK(x, (typeof(x))(a) - 1)
> +#define __ALIGN_MASK(x, mask)	(((x) + (mask)) & ~(mask))

You can reuse the definition of these in scripts/include/linux/kernel.h

> +#define STAMP_VALUE 0x5f0a6c39
> +
> +static void mkimage(char *infile, char *outfile)
> +{
> +	struct egon_header *hdr;
> +	uint32_t *p32;
> +	uint32_t sum;
> +	int i;
> +	size_t hdr_size;
> +	size_t bin_size;
> +	size_t img_size;
> +	char *bin;
> +	int fd, ret;
> +
> +	bin = read_file(infile, &bin_size);
> +	if (!bin) {
> +		perror("read_file");
> +		exit(1);
> +	}
> +
> +	/* the header must be a multiple of 32 bytes */
> +	hdr_size = sizeof(*hdr);
> +
> +	/* test if the binary has reserved space for the header */
> +	hdr = (void *)bin;

Declare bin as void *, this makes the explicit cast unnecessary.

> +	if (hdr->branch == EGON_HDR_BRANCH && memcmp(hdr->magic, "eGON", 4) == 0) {
> +		/* strip the existing header */
> +		bin += hdr_size;
> +		bin_size -= hdr_size;
> +	}
> +	hdr = calloc(1, hdr_size);
> +	if (!hdr) {
> +		perror("malloc");
> +		exit(1);
> +	}
> +
> +	/* The total image length must be a multiple of 4K bytes */
> +	img_size = ALIGN(hdr_size + bin_size, 4096);
> +
> +	hdr->check_sum = 0;

hdr is already zeroed due to the use of calloc.

> +	hdr->branch = EGON_HDR_BRANCH;
> +	hdr->length = cpu_to_le32(img_size);
> +	memcpy(hdr->magic, "eGON.BT0", 8);
> +	memcpy(hdr->spl_signature, "SPL", 3);
> +	hdr->spl_signature[3] = 0x03; /* version 0.3 */
> +
> +	/* calculate the checksum */
> +	sum = STAMP_VALUE;
> +	for (p32 = (void *) hdr, i = 0; i < hdr_size / sizeof(uint32_t); i++)
> +		sum += le32_to_cpu(p32[i]);
> +	for (p32 = (void *) bin, i = 0; i < bin_size / sizeof(uint32_t); i++)
> +		sum += le32_to_cpu(p32[i]);

Does this work when bin_size is not a multiple of sizeof(uint32_t)?
It likely is, but who knows...

You are calculating the checksum up to bin_size, not including the final
alignment. That works because the alignment is filled with 0x0 which
doesn't add to this checksum. That is ok, but maybe it's worth noting
that in the code.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 02/11] sunxi: introduce mach-sunxi
  2023-05-10 23:37 ` [RFC PATCH 02/11] sunxi: introduce mach-sunxi Jules Maselbas
@ 2023-05-11  7:27   ` Sascha Hauer
  2023-05-18 18:46   ` Ahmad Fatoum
  1 sibling, 0 replies; 34+ messages in thread
From: Sascha Hauer @ 2023-05-11  7:27 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On Thu, May 11, 2023 at 01:37:02AM +0200, Jules Maselbas wrote:
> Add kbuild boilerplate and some early init functions
> ---
>  arch/arm/Kconfig                   | 12 ++++++++++
>  arch/arm/Makefile                  |  1 +
>  arch/arm/mach-sunxi/Kconfig        | 16 +++++++++++++
>  arch/arm/mach-sunxi/Makefile       |  2 ++
>  arch/arm/mach-sunxi/cpu_init.c     | 33 +++++++++++++++++++++++++++
>  arch/arm/mach-sunxi/sunxi.c        |  0
>  images/Makefile                    |  1 +
>  images/Makefile.sunxi              | 13 +++++++++++
>  include/mach/sunxi/init.h          |  6 +++++
>  include/mach/sunxi/sun50i-regs.h   | 36 ++++++++++++++++++++++++++++++
>  include/mach/sunxi/sunxi-pinctrl.h | 13 +++++++++++
>  11 files changed, 133 insertions(+)
>  create mode 100644 arch/arm/mach-sunxi/Kconfig
>  create mode 100644 arch/arm/mach-sunxi/Makefile
>  create mode 100644 arch/arm/mach-sunxi/cpu_init.c
>  create mode 100644 arch/arm/mach-sunxi/sunxi.c
>  create mode 100644 images/Makefile.sunxi
>  create mode 100644 include/mach/sunxi/init.h
>  create mode 100644 include/mach/sunxi/sun50i-regs.h
>  create mode 100644 include/mach/sunxi/sunxi-pinctrl.h
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index abe649de49..8da03e2703 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -144,6 +144,17 @@ config ARCH_SOCFPGA
>  	select COMMON_CLK
>  	select CLKDEV_LOOKUP
>  
> +config ARCH_SUNXI
> +	bool "Allwinner SoCs"
> +	select OFTREE
> +	select OFDEVICE
> +	select COMMON_CLK
> +	select COMMON_CLK_OF_PROVIDER
> +	select CLKDEV_LOOKUP
> +	select GENERIC_GPIO
> +	select GPIOLIB
> +	select PINCTRL
> +
>  config ARCH_VERSATILE
>  	bool "ARM Versatile boards (ARM926EJ-S)"
>  	select GPIOLIB
> @@ -311,6 +322,7 @@ source "arch/arm/mach-omap/Kconfig"
>  source "arch/arm/mach-pxa/Kconfig"
>  source "arch/arm/mach-rockchip/Kconfig"
>  source "arch/arm/mach-socfpga/Kconfig"
> +source "arch/arm/mach-sunxi/Kconfig"
>  source "arch/arm/mach-stm32mp/Kconfig"
>  source "arch/arm/mach-versatile/Kconfig"
>  source "arch/arm/mach-vexpress/Kconfig"
> diff --git a/arch/arm/Makefile b/arch/arm/Makefile
> index a506f1e3a3..bb61392e4c 100644
> --- a/arch/arm/Makefile
> +++ b/arch/arm/Makefile
> @@ -103,6 +103,7 @@ machine-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip
>  machine-$(CONFIG_ARCH_SAMSUNG)		+= samsung
>  machine-$(CONFIG_ARCH_SOCFPGA)		+= socfpga
>  machine-$(CONFIG_ARCH_STM32MP)		+= stm32mp
> +machine-$(CONFIG_ARCH_SUNXI)		+= sunxi
>  machine-$(CONFIG_ARCH_VERSATILE)	+= versatile
>  machine-$(CONFIG_ARCH_VEXPRESS)		+= vexpress
>  machine-$(CONFIG_ARCH_TEGRA)		+= tegra
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> new file mode 100644
> index 0000000000..0e8d83fedd
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -0,0 +1,16 @@
> +if ARCH_SUNXI
> +
> +config ARCH_TEXT_BASE
> +	hex
> +	default 0x0

This shouldn't be necessary anymore.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 09/11] arm: sunxi: Add sun50i SDRAM init
  2023-05-10 23:37 ` [RFC PATCH 09/11] arm: sunxi: Add sun50i SDRAM init Jules Maselbas
@ 2023-05-11  7:39   ` Sascha Hauer
  0 siblings, 0 replies; 34+ messages in thread
From: Sascha Hauer @ 2023-05-11  7:39 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On Thu, May 11, 2023 at 01:37:09AM +0200, Jules Maselbas wrote:
> diff --git a/arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c b/arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c
> new file mode 100644
> index 0000000000..4c54588556
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/sun50i-a64-ddr3-init.c
> @@ -0,0 +1,9 @@
> +#define CONFIG_MACH_SUN50I y
> +#define CONFIG_SUNXI_DRAM_DW y
> +#define CONFIG_SUNXI_DRAM_DW_32BIT y
> +#define CONFIG_SUNXI_DRAM_DDR3 y
> +#define CONFIG_SYS_SDRAM_BASE 0x40000000
> +
> +#define sunxi_dram_init sun50i_a64_ddr3_dram_init
> +
> +#include "sun50i-sdram.c"

I wanted to mourn about the use of defines and ifdefs for configuring
the SoC type and configuration of the SDRAM which makes it impossible to
compile in multiple configurations, but you found a nice way to bypass
this problem ;)

How much variation in the parameters is there in practice? Adding
boards with CONFIG_SUNXI_DRAM_DW_16BIT would mean we have to duplicate
these two files into four. This won't scale anymore quite fast.
We could make these files board specific.

I think we should find another prefix for the CONFIG_ defines and leave
the CONFIG_ namespace for Kconfig only.


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 01/11] scripts: Add Allwinner eGON image support
  2023-05-11  7:25   ` Sascha Hauer
@ 2023-05-11 20:14     ` Jules Maselbas
  0 siblings, 0 replies; 34+ messages in thread
From: Jules Maselbas @ 2023-05-11 20:14 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

On Thu, May 11, 2023 at 09:25:12AM +0200, Sascha Hauer wrote:
> On Thu, May 11, 2023 at 01:37:01AM +0200, Jules Maselbas wrote:
> > On power-up Allwinner SoC starts in boot ROM, aka BROM, which will search
> > for an eGON image: first from the SD card, then from eMMC. If no image is
> > found then the BROM will enter into FEL mode that can be used for initial
> > programming and recovery of devices using USB.
> > 
> > The eGON header structure is adapted from u-boot: /include/sunxi_image.h,
> > the header structure is also documented on https://linux-sunxi.org/EGON
> > 
> > BROM will load, at most, the first 32KB of the image into SRAM, including
> > the header itself! The jump instruction in the header needs to be patched
> > accordingly with the image size.
> 
> Do I understand it correctly that all code needed to load the full
> barebox image needs to fit into these 32KiB?
Yes, this is done by the "xload" PBL-only image.
But for developpement I load code through USB to a different SRAM of 108KB!

> Where must this image be placed? Directly at the origin of the SD card?
> That would mean the partition table is somewhere inside the binary,
> right?
The eGON image must start exactly at 8KB offset from the origin of the SD
card. This leave room for the actual partition table.

> 
> > +
> > +#define ALIGN(x, a)		__ALIGN_MASK(x, (typeof(x))(a) - 1)
> > +#define __ALIGN_MASK(x, mask)	(((x) + (mask)) & ~(mask))
> 
> You can reuse the definition of these in scripts/include/linux/kernel.h
Ack

> > +#define STAMP_VALUE 0x5f0a6c39
> > +
> > +static void mkimage(char *infile, char *outfile)
> > +{
> > +	struct egon_header *hdr;
> > +	uint32_t *p32;
> > +	uint32_t sum;
> > +	int i;
> > +	size_t hdr_size;
> > +	size_t bin_size;
> > +	size_t img_size;
> > +	char *bin;
> > +	int fd, ret;
> > +
> > +	bin = read_file(infile, &bin_size);
> > +	if (!bin) {
> > +		perror("read_file");
> > +		exit(1);
> > +	}
> > +
> > +	/* the header must be a multiple of 32 bytes */
> > +	hdr_size = sizeof(*hdr);
> > +
> > +	/* test if the binary has reserved space for the header */
> > +	hdr = (void *)bin;
> 
> Declare bin as void *, this makes the explicit cast unnecessary.
> 
> > +	if (hdr->branch == EGON_HDR_BRANCH && memcmp(hdr->magic, "eGON", 4) == 0) {
> > +		/* strip the existing header */
> > +		bin += hdr_size;
> > +		bin_size -= hdr_size;
> > +	}
> > +	hdr = calloc(1, hdr_size);
> > +	if (!hdr) {
> > +		perror("malloc");
> > +		exit(1);
> > +	}
> > +
> > +	/* The total image length must be a multiple of 4K bytes */
> > +	img_size = ALIGN(hdr_size + bin_size, 4096);
> > +
> > +	hdr->check_sum = 0;
> 
> hdr is already zeroed due to the use of calloc.
Yes, i don't recall why I did this... I don't exactly recall how the
checksum is verified by the boot rom.

> > +	hdr->branch = EGON_HDR_BRANCH;
> > +	hdr->length = cpu_to_le32(img_size);
> > +	memcpy(hdr->magic, "eGON.BT0", 8);
> > +	memcpy(hdr->spl_signature, "SPL", 3);
> > +	hdr->spl_signature[3] = 0x03; /* version 0.3 */
> > +
> > +	/* calculate the checksum */
> > +	sum = STAMP_VALUE;
> > +	for (p32 = (void *) hdr, i = 0; i < hdr_size / sizeof(uint32_t); i++)
> > +		sum += le32_to_cpu(p32[i]);
> > +	for (p32 = (void *) bin, i = 0; i < bin_size / sizeof(uint32_t); i++)
> > +		sum += le32_to_cpu(p32[i]);
> 
> Does this work when bin_size is not a multiple of sizeof(uint32_t)?
> It likely is, but who knows...
bin_size should be a multiple of sizeof(uint32_t), if that's not the case
I don't think that the result will be correct.
But I could add something to handle this exact case.

> You are calculating the checksum up to bin_size, not including the final
> alignment. That works because the alignment is filled with 0x0 which
> doesn't add to this checksum. That is ok, but maybe it's worth noting
> that in the code.
Yes you're right, I'll add a comment, and also on ftruncate that is used
to pad the image with zero.

> 
> Sascha
> 
> -- 
> Pengutronix e.K.                           |                             |
> Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
> 31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> 



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 02/11] sunxi: introduce mach-sunxi
  2023-05-10 23:37 ` [RFC PATCH 02/11] sunxi: introduce mach-sunxi Jules Maselbas
  2023-05-11  7:27   ` Sascha Hauer
@ 2023-05-18 18:46   ` Ahmad Fatoum
  2023-05-19 10:09     ` Jules Maselbas
  1 sibling, 1 reply; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 18:46 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> Add kbuild boilerplate and some early init functions
> ---
>  arch/arm/Kconfig                   | 12 ++++++++++
>  arch/arm/Makefile                  |  1 +
>  arch/arm/mach-sunxi/Kconfig        | 16 +++++++++++++
>  arch/arm/mach-sunxi/Makefile       |  2 ++
>  arch/arm/mach-sunxi/cpu_init.c     | 33 +++++++++++++++++++++++++++
>  arch/arm/mach-sunxi/sunxi.c        |  0
>  images/Makefile                    |  1 +
>  images/Makefile.sunxi              | 13 +++++++++++
>  include/mach/sunxi/init.h          |  6 +++++
>  include/mach/sunxi/sun50i-regs.h   | 36 ++++++++++++++++++++++++++++++
>  include/mach/sunxi/sunxi-pinctrl.h | 13 +++++++++++
>  11 files changed, 133 insertions(+)
>  create mode 100644 arch/arm/mach-sunxi/Kconfig
>  create mode 100644 arch/arm/mach-sunxi/Makefile
>  create mode 100644 arch/arm/mach-sunxi/cpu_init.c
>  create mode 100644 arch/arm/mach-sunxi/sunxi.c
>  create mode 100644 images/Makefile.sunxi
>  create mode 100644 include/mach/sunxi/init.h
>  create mode 100644 include/mach/sunxi/sun50i-regs.h
>  create mode 100644 include/mach/sunxi/sunxi-pinctrl.h
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index abe649de49..8da03e2703 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -144,6 +144,17 @@ config ARCH_SOCFPGA
>  	select COMMON_CLK
>  	select CLKDEV_LOOKUP
>  
> +config ARCH_SUNXI
> +	bool "Allwinner SoCs"

depends on ARCH_MULTIARCH? :-)
That's the new hot stuff where you can build multiple SoC families
in one go. Also, no HAS_DEBUG_LL?

> +	select OFTREE
> +	select OFDEVICE
> +	select COMMON_CLK
> +	select COMMON_CLK_OF_PROVIDER
> +	select CLKDEV_LOOKUP
> +	select GENERIC_GPIO

This is implied by select GPIOLIB below.

> +	select GPIOLIB
> +	select PINCTRL
> +
>  config ARCH_VERSATILE
>  	bool "ARM Versatile boards (ARM926EJ-S)"
>  	select GPIOLIB
> @@ -311,6 +322,7 @@ source "arch/arm/mach-omap/Kconfig"
>  source "arch/arm/mach-pxa/Kconfig"
>  source "arch/arm/mach-rockchip/Kconfig"
>  source "arch/arm/mach-socfpga/Kconfig"
> +source "arch/arm/mach-sunxi/Kconfig"
>  source "arch/arm/mach-stm32mp/Kconfig"
>  source "arch/arm/mach-versatile/Kconfig"
>  source "arch/arm/mach-vexpress/Kconfig"
> diff --git a/arch/arm/Makefile b/arch/arm/Makefile
> index a506f1e3a3..bb61392e4c 100644
> --- a/arch/arm/Makefile
> +++ b/arch/arm/Makefile
> @@ -103,6 +103,7 @@ machine-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip
>  machine-$(CONFIG_ARCH_SAMSUNG)		+= samsung
>  machine-$(CONFIG_ARCH_SOCFPGA)		+= socfpga
>  machine-$(CONFIG_ARCH_STM32MP)		+= stm32mp
> +machine-$(CONFIG_ARCH_SUNXI)		+= sunxi
>  machine-$(CONFIG_ARCH_VERSATILE)	+= versatile
>  machine-$(CONFIG_ARCH_VEXPRESS)		+= vexpress
>  machine-$(CONFIG_ARCH_TEGRA)		+= tegra
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> new file mode 100644
> index 0000000000..0e8d83fedd
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -0,0 +1,16 @@
> +if ARCH_SUNXI
> +
> +config ARCH_TEXT_BASE
> +	hex
> +	default 0x0
> +
> +menuconfig SUNXI_MULTI_BOARDS
> +	bool "Allwinner boards"
> +	select HAVE_PBL_MULTI_IMAGES
> +	select RELOCATABLE

Just make this the default?

> +
> +if SUNXI_MULTI_BOARDS
> +
> +endif
> +
> +endif
> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> new file mode 100644
> index 0000000000..d678973ca2
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/Makefile
> @@ -0,0 +1,2 @@
> +obj-y += sunxi.o
> +lwl-y += cpu_init.o
> diff --git a/arch/arm/mach-sunxi/cpu_init.c b/arch/arm/mach-sunxi/cpu_init.c
> new file mode 100644
> index 0000000000..f4092d8d5d
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/cpu_init.c
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#include <common.h>
> +#include <linux/sizes.h>
> +#include <asm/barebox-arm-head.h>
> +// #include <asm/errata.h> TODO: errata 843419 ?
> +#include <io.h>
> +#include <debug_ll.h>
> +#include <mach/sunxi/init.h>
> +#include <mach/sunxi/sun50i-regs.h>
> +#include <mach/sunxi/sunxi-pinctrl.h>
> +
> +static void sunxi_ccu_init(void __iomem *ccu)
> +{
> +	/* APB2 = 24MHz (UART, I2C) */
> +	writel((1 << 24 /* src: 1=osc24 */) |
> +	       (0 << 16 /* pre_div (N): 0=/1 1=/2 2=/4 3=/8 */) |
> +	       (0 << 0) /* M-1 */,
> +	       ccu + CCU_APB2_CFG);
> +	set_cntfrq(24 * 1000 * 1000);
> +
> +	/* PIO clock enable */
> +	setbits_le32(ccu + CCU_BUS_CLK_GATE2, 1u << 5);

Nit: There's a BIT() macro that could be used here.

> +	/* UART0 clock enable */
> +	setbits_le32(ccu + CCU_BUS_CLK_GATE3, 1u << 16);
> +	/* UART0 release reset */
> +	setbits_le32(ccu + CCU_BUS_SOFT_RST4, 1u << 16);

Can the pads UART0 uses be muxed otherwise? Is it ok to unconditionally
enable UART0 here? I'd expect this to be a separate sunxi_uart_setup(port)
that can be used by board code after doing lowlevel_init.

> +}
> +
> +static void sunxi_cpu_lowlevel_init(void)
> +{
> +	sunxi_ccu_init(IOMEM(SUNXI_CCU_BASE_ADDR));
> +}
> diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/images/Makefile b/images/Makefile
> index aa5814710f..3a10fe1abb 100644
> --- a/images/Makefile
> +++ b/images/Makefile
> @@ -151,6 +151,7 @@ include $(srctree)/images/Makefile.omap3
>  include $(srctree)/images/Makefile.rockchip
>  include $(srctree)/images/Makefile.socfpga
>  include $(srctree)/images/Makefile.stm32mp
> +include $(srctree)/images/Makefile.sunxi
>  include $(srctree)/images/Makefile.tegra
>  include $(srctree)/images/Makefile.vexpress
>  include $(srctree)/images/Makefile.xburst
> diff --git a/images/Makefile.sunxi b/images/Makefile.sunxi
> new file mode 100644
> index 0000000000..778d6f9bdf
> --- /dev/null
> +++ b/images/Makefile.sunxi
> @@ -0,0 +1,13 @@
> +#
> +# barebox image generation Makefile for Allwinner sunxi eGON boot images
> +#
> +
> +# %.egonimg - convert into eGON.BT0 image
> +# ----------------------------------------------------------------------
> +quiet_cmd_egon_image = EGON  $@
> +      cmd_egon_image = $(objtree)/scripts/egon_mkimage $< $@
> +
> +$(obj)/%.egonimg: $(obj)/% FORCE
> +	$(call if_changed,egon_image)
> +
> +# ----------------------------------------------------------------------
> diff --git a/include/mach/sunxi/init.h b/include/mach/sunxi/init.h
> new file mode 100644
> index 0000000000..26cc022fde
> --- /dev/null
> +++ b/include/mach/sunxi/init.h
> @@ -0,0 +1,6 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#ifndef __MACH_INIT_H
> +#define __MACH_INIT_H
> +
> +#endif
> diff --git a/include/mach/sunxi/sun50i-regs.h b/include/mach/sunxi/sun50i-regs.h
> new file mode 100644
> index 0000000000..68501fa351
> --- /dev/null
> +++ b/include/mach/sunxi/sun50i-regs.h
> @@ -0,0 +1,36 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#ifndef __MACH_SUN50I_REGS_H
> +#define __MACH_SUN50I_REGS_H

#include <linux/sizes.h>

> +
> +#define SUN50I_SRAM_A1_BASE_ADDR 0x00000000
> +#define SUN50I_SRAM_A1_SIZE SZ_64K
> +#define SUN50I_SRAM_A2_BASE_ADDR 0x00044000
> +#define SUN50I_SRAM_A2_SIZE SZ_32K
> +
> +#define SUN50I_DRAM_BASE_ADDR	0x40000000
> +
> +#define SUN50I_CCU_BASE_ADDR	0x01c20000
> +#define SUN50I_PIO_BASE_ADDR	0x01c20800
> +#define SUN50I_MMC0_BASE_ADDR	0x01c0f000
> +#define SUN50I_MMC1_BASE_ADDR	0x01c10000
> +#define SUN50I_MMC2_BASE_ADDR	0x01c11000
> +#define SUN50I_TIMER_BASE_ADDR	0x01c20c00

I know much existing code doesn't, but you could already use IOMEM
here to avoid casts in consumer code.

> +
> +#define CCU_PLL_CPUX		0x00
> +#define CCU_PLL_PERIPH0		0x28
> +#define CCU_CPUX_AXI_CFG	0x50
> +#define CCU_AHB1_APB1_CFG	0x54
> +#define CCU_APB2_CFG		0x58
> +#define CCU_AHB2_CFG		0x5c
> +#define CCU_BUS_CLK_GATE0	0x60
> +#define CCU_BUS_CLK_GATE1	0x64
> +#define CCU_BUS_CLK_GATE2	0x68
> +#define CCU_BUS_CLK_GATE3	0x6c
> +#define CCU_CE_CLK		0x9c
> +#define CCU_MBUS_CLK		0x15c
> +#define CCU_BUS_SOFT_RST0	0x2c0
> +#define CCU_BUS_SOFT_RST4	0x2d8
> +#define CCU_PLL_LOCK_CTRL	0x320
> +
> +#endif
> diff --git a/include/mach/sunxi/sunxi-pinctrl.h b/include/mach/sunxi/sunxi-pinctrl.h
> new file mode 100644
> index 0000000000..adb2a24577
> --- /dev/null
> +++ b/include/mach/sunxi/sunxi-pinctrl.h
> @@ -0,0 +1,13 @@
> +/* pio aka "allwinner,sun8i-h3-pinctrl" */

No include guard?

> +
> +#define PIO_PA_CFG0 0x00
> +#define PIO_PB_CFG0 0x24
> +#define PIO_PB_CFG1 0x28
> +#define PIO_PD_CFG0 0x6c
> +#define PIO_PD_CFG1 0x70
> +#define PIO_PD_CFG2 0x74
> +#define PIO_PF_CFG0 0xb4
> +
> +#define PIO_PA_PULL1 0x20
> +#define PIO_PB_PULL0 0x40
> +#define PIO_PD_DATA 0x7c

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 01/11] scripts: Add Allwinner eGON image support
  2023-05-10 23:37 ` [RFC PATCH 01/11] scripts: Add Allwinner eGON image support Jules Maselbas
  2023-05-11  7:25   ` Sascha Hauer
@ 2023-05-18 18:47   ` Ahmad Fatoum
  2023-05-19  9:40     ` Jules Maselbas
  1 sibling, 1 reply; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 18:47 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> On power-up Allwinner SoC starts in boot ROM, aka BROM, which will search
> for an eGON image: first from the SD card, then from eMMC. If no image is
> found then the BROM will enter into FEL mode that can be used for initial
> programming and recovery of devices using USB.
> 
> The eGON header structure is adapted from u-boot: /include/sunxi_image.h,
> the header structure is also documented on https://linux-sunxi.org/EGON
> 
> BROM will load, at most, the first 32KB of the image into SRAM, including
> the header itself! The jump instruction in the header needs to be patched
> accordingly with the image size.

S-o-b missing.

> ---
>  arch/arm/mach-sunxi/egon_header.c |  11 +++
>  include/mach/sunxi/egon.h         |  63 ++++++++++++++++
>  scripts/Kconfig                   |   7 ++
>  scripts/Makefile                  |   1 +
>  scripts/egon_mkimage.c            | 115 ++++++++++++++++++++++++++++++
>  5 files changed, 197 insertions(+)
>  create mode 100644 arch/arm/mach-sunxi/egon_header.c
>  create mode 100644 include/mach/sunxi/egon.h
>  create mode 100644 scripts/egon_mkimage.c
> 
> diff --git a/arch/arm/mach-sunxi/egon_header.c b/arch/arm/mach-sunxi/egon_header.c
> new file mode 100644
> index 0000000000..9a4822f71d
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/egon_header.c
> @@ -0,0 +1,11 @@
> +#include <common.h>
> +#include <mach/sunxi/egon.h>
> +#if 0

Dead code

> +const struct egon_header sunxi_egon_header
> +__attribute__((section(".text_egon_header"))) = {
> +	 /* put a valid arm32 jump over the header, in order for images to
> +	  * to work when not loaded by boot rom but by the FEL */
> +	.branch = EGON_HDR_BRANCH,
> +	.magic = "eGON",
> +};
> +#endif
> diff --git a/include/mach/sunxi/egon.h b/include/mach/sunxi/egon.h
> new file mode 100644
> index 0000000000..f70730ee25
> --- /dev/null
> +++ b/include/mach/sunxi/egon.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#ifndef SUNIX_EGON
> +#define SUNIX_EGON
> +
> +struct egon_header {
> +	uint32_t branch; /* branch instruction to jump over the header */
> +	uint8_t magic[8]; /* "eGON.BT0" or "eGON.BT1" */
> +	uint32_t check_sum;
> +	uint32_t length;
> +	/*
> +	 * We use a simplified header, only filling in what is needed
> +	 * by the boot ROM. To be compatible with Allwinner tools we
> +	 * would need to implement the proper fields here instead of
> +	 * padding.
> +	 *
> +	 * Actually we want the ability to recognize our "sunxi" variant
> +	 * of the SPL. To do so, let's place a special signature into the
> +	 * "pub_head_size" field. We can reasonably expect Allwinner's
> +	 * boot0 to always have the upper 16 bits of this set to 0 (after
> +	 * all the value shouldn't be larger than the limit imposed by
> +	 * SRAM size).
> +	 * If the signature is present (at 0x14), then we know it's safe
> +	 * to use the remaining 8 bytes (at 0x18) for our own purposes.
> +	 * (E.g. sunxi-tools "fel" utility can pass information there.)
> +	 */
> +	union {
> +		uint32_t header_size;
> +		uint8_t spl_signature[4];
> +	};
> +	uint32_t fel_script_address;/* since v0.1, set by sunxi-fel */
> +	/*
> +	 * If the fel_uEnv_length member below is set to a non-zero value,
> +	 * it specifies the size (byte count) of data at fel_script_address.
> +	 * At the same time this indicates that the data is in uEnv.txt
> +	 * compatible format, ready to be imported via "env import -t".
> +	 */
> +	uint32_t fel_uEnv_length;/* since v0.1, set by sunxi-fel */
> +	/*
> +	 * Offset of an ASCIIZ string (relative to the SPL header), which
> +	 * contains the default device tree name (CONFIG_DEFAULT_DEVICE_TREE).
> +	 * This is optional and may be set to NULL. Is intended to be used
> +	 * by flash programming tools for providing nice informative messages
> +	 * to the users.
> +	 */
> +	uint32_t dt_name_offset;/* since v0.2, set by mksunxiboot */
> +	uint32_t dram_size;/* in MiB, since v0.3, set by SPL */
> +	uint32_t boot_media;/* written here by the boot ROM */
> +	/* A padding area (may be used for storing text strings) */
> +	uint32_t string_pool[13];/* since v0.2, filled by mksunxiboot */
> +	/* The header must be a multiple of 32 bytes (for VBAR alignment) */
> +	/* And at least 64 byte (https://patchwork.ozlabs.org/patch/622173) */
> +};
> +#define EGON_HDR_BRANCH (0xea000000 | (sizeof(struct egon_header) / 4 - 2))
> +#define sunxi_egon_header()						\
> +	{								\
> +		static const struct egon_header _hdr			\
> +			__attribute__((section(".text_head_egon_header"))) = \
> +			{ .branch = EGON_HDR_BRANCH, .magic = "eGON" };	\
> +		__keep_symbolref(_hdr);					\
> +	}
> +
> +#endif
> diff --git a/scripts/Kconfig b/scripts/Kconfig
> index dcd5f32d1d..7517f5b79f 100644
> --- a/scripts/Kconfig
> +++ b/scripts/Kconfig
> @@ -56,6 +56,13 @@ config RK_IMAGE
>  	help
>  	  This enables building the image creation tool for Rockchip SoCs
>  
> +config EGON_IMAGE
> +       bool "Allwinner eGON image tool" if COMPILE_HOST_TOOLS
> +       depends on ARCH_SUNXI || COMPILE_HOST_TOOLS
> +       default y if ARCH_SUNXI
> +       help
> +         This enables building the image creation tool for Allwinner sunxi SoCs
> +
>  config OMAP_IMAGE
>  	bool "TI OMAP image tools" if COMPILE_HOST_TOOLS
>  	depends on ARCH_OMAP || COMPILE_HOST_TOOLS
> diff --git a/scripts/Makefile b/scripts/Makefile
> index 72ad9ad7a6..13e80db7af 100644
> --- a/scripts/Makefile
> +++ b/scripts/Makefile
> @@ -28,6 +28,7 @@ hostprogs-always-$(CONFIG_LAYERSCAPE_PBLIMAGE)		+= pblimage
>  hostprogs-always-$(CONFIG_STM32_IMAGE)			+= stm32image
>  hostprogs-always-$(CONFIG_RISCV)			+= prelink-riscv
>  hostprogs-always-$(CONFIG_RK_IMAGE)			+= rkimage
> +hostprogs-always-$(CONFIG_EGON_IMAGE)			+= egon_mkimage
>  HOSTCFLAGS_rkimage = `pkg-config --cflags openssl`
>  HOSTLDLIBS_rkimage = `pkg-config --libs openssl`
>  KBUILD_HOSTCFLAGS += -I$(srctree)/scripts/include/
> diff --git a/scripts/egon_mkimage.c b/scripts/egon_mkimage.c
> new file mode 100644
> index 0000000000..887caa29f0
> --- /dev/null
> +++ b/scripts/egon_mkimage.c
> @@ -0,0 +1,115 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#include <stdio.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <string.h>
> +
> +#include "../include/mach/sunxi/egon.h"
> +
> +#include "compiler.h"
> +#include "common.h"
> +#include "common.c"
> +
> +#define ALIGN(x, a)		__ALIGN_MASK(x, (typeof(x))(a) - 1)
> +#define __ALIGN_MASK(x, mask)	(((x) + (mask)) & ~(mask))
> +#define STAMP_VALUE 0x5f0a6c39
> +
> +static void mkimage(char *infile, char *outfile)
> +{
> +	struct egon_header *hdr;
> +	uint32_t *p32;
> +	uint32_t sum;
> +	int i;
> +	size_t hdr_size;
> +	size_t bin_size;
> +	size_t img_size;
> +	char *bin;
> +	int fd, ret;
> +
> +	bin = read_file(infile, &bin_size);
> +	if (!bin) {
> +		perror("read_file");
> +		exit(1);
> +	}
> +
> +	/* the header must be a multiple of 32 bytes */
> +	hdr_size = sizeof(*hdr);
> +
> +	/* test if the binary has reserved space for the header */
> +	hdr = (void *)bin;
> +	if (hdr->branch == EGON_HDR_BRANCH && memcmp(hdr->magic, "eGON", 4) == 0) {
> +		/* strip the existing header */
> +		bin += hdr_size;
> +		bin_size -= hdr_size;
> +	}
> +	hdr = calloc(1, hdr_size);
> +	if (!hdr) {
> +		perror("malloc");
> +		exit(1);
> +	}
> +
> +	/* The total image length must be a multiple of 4K bytes */
> +	img_size = ALIGN(hdr_size + bin_size, 4096);
> +
> +	hdr->check_sum = 0;
> +	hdr->branch = EGON_HDR_BRANCH;
> +	hdr->length = cpu_to_le32(img_size);
> +	memcpy(hdr->magic, "eGON.BT0", 8);
> +	memcpy(hdr->spl_signature, "SPL", 3);
> +	hdr->spl_signature[3] = 0x03; /* version 0.3 */
> +
> +	/* calculate the checksum */
> +	sum = STAMP_VALUE;
> +	for (p32 = (void *) hdr, i = 0; i < hdr_size / sizeof(uint32_t); i++)
> +		sum += le32_to_cpu(p32[i]);
> +	for (p32 = (void *) bin, i = 0; i < bin_size / sizeof(uint32_t); i++)
> +		sum += le32_to_cpu(p32[i]);
> +	hdr->check_sum = cpu_to_le32(sum);
> +
> +	fd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT,
> +		  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
> +	if (fd < 0) {
> +		fprintf(stderr, "Cannot open %s: %s\n", outfile, strerror(errno));
> +		exit(1);
> +	}
> +	/* write the header */
> +	ret = write_full(fd, hdr, hdr_size);
> +	if (ret < 0) {
> +		perror("write_full");
> +		exit(1);
> +	}
> +	/* write the binary */
> +	ret = write_full(fd, bin, bin_size);
> +	if (ret < 0) {
> +		perror("write_full");
> +		exit(1);
> +	}
> +	/* align the image */
> +	ret = ftruncate(fd, img_size);
> +	if (ret < 0) {
> +		perror("ftruncate");
> +		exit(1);
> +	}
> +	close(fd);
> +
> +	free(hdr);
> +}
> +
> +static void usage(char *argv0)
> +{
> +	fprintf(stderr, "usage: %s <infile> <outfile>\n", argv0);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	if (argc != 3) {
> +		usage(argv[0]);
> +		return 1;
> +	}
> +
> +	mkimage(argv[1], argv[2]);
> +
> +	return 0;
> +}

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 03/11] ARM: dls: Add ENTRY_HEADER macro to add .text section
  2023-05-10 23:37 ` [RFC PATCH 03/11] ARM: dls: Add ENTRY_HEADER macro to add .text section Jules Maselbas
@ 2023-05-18 18:49   ` Ahmad Fatoum
  0 siblings, 0 replies; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 18:49 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> On sunxi platforms the boot rom (BROM) looks for a specific header which
> will also be loaded in memory, causing pbl, or barebox, image not loaded
> at the expected BASE addresse. This also cause an issue with relocatable
> pbl: instruction used for relocation expect the image to be aligned on a
> 4K page boundary.
> 
> The proposed solution here is to allow to specify custom sections to put
> in the very begging of the .text section.
> ---
>  arch/arm/lib/pbl.lds.S            | 1 +
>  include/asm-generic/barebox.lds.h | 4 ++++
>  2 files changed, 5 insertions(+)
> 
> diff --git a/arch/arm/lib/pbl.lds.S b/arch/arm/lib/pbl.lds.S
> index 114ec7bc81..6fc97c6c34 100644
> --- a/arch/arm/lib/pbl.lds.S
> +++ b/arch/arm/lib/pbl.lds.S
> @@ -27,6 +27,7 @@ SECTIONS
>  	.text      :
>  	{
>  		_stext = .;
> +		ENTRY_HEADER

Let's just have *(.text_head_soc_header*) here unconditionally. The use of ifdefs
will clash with multiarch.

>  		*(.text_head_prologue*)
>  		*(.text_head_entry*)
>  		__bare_init_start = .;
> diff --git a/include/asm-generic/barebox.lds.h b/include/asm-generic/barebox.lds.h
> index d3736ebaed..9915ead8a7 100644
> --- a/include/asm-generic/barebox.lds.h
> +++ b/include/asm-generic/barebox.lds.h
> @@ -12,6 +12,10 @@
>  #define PRE_IMAGE
>  #endif
>  
> +#ifndef ENTRY_HEADER
> +#define ENTRY_HEADER
> +#endif
> +
>  #define BAREBOX_INITCALLS			\
>  	STRUCT_ALIGN();				\
>  	__barebox_initcalls_start = .;		\

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 04/11] sunxi: Add lowlevel switch to aarch64
  2023-05-10 23:37 ` [RFC PATCH 04/11] sunxi: Add lowlevel switch to aarch64 Jules Maselbas
@ 2023-05-18 19:01   ` Ahmad Fatoum
  0 siblings, 0 replies; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 19:01 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> Allwinner A64 SoC (and probably others) boots in 32-bits mode. Switching
> to aarch64 is achieved by writing to the Reset Management Register (RMR)
> which can be accessed through the memory space thanks to an alias.
> 
> On A64 this alias is located at 0x017000a0
> ---
>  arch/arm/include/asm/barebox.lds.h |  2 ++
>  arch/arm/mach-sunxi/Kconfig        |  5 ++++
>  arch/arm/mach-sunxi/Makefile       |  2 ++
>  arch/arm/mach-sunxi/rmr_switch.S   | 47 ++++++++++++++++++++++++++++++
>  include/mach/sunxi/barebox.lds.h   |  6 ++++
>  include/mach/sunxi/rmr_switch.h    | 10 +++++++
>  6 files changed, 72 insertions(+)
>  create mode 100644 arch/arm/mach-sunxi/rmr_switch.S
>  create mode 100644 include/mach/sunxi/barebox.lds.h
>  create mode 100644 include/mach/sunxi/rmr_switch.h
> 
> diff --git a/arch/arm/include/asm/barebox.lds.h b/arch/arm/include/asm/barebox.lds.h
> index a5c74381d8..f94290128e 100644
> --- a/arch/arm/include/asm/barebox.lds.h
> +++ b/arch/arm/include/asm/barebox.lds.h
> @@ -2,6 +2,8 @@
>  
>  #if defined CONFIG_ARCH_EP93XX
>  #include <mach/ep93xx/barebox.lds.h>
> +#elif defined CONFIG_ARCH_SUNXI
> +#include <mach/sunxi/barebox.lds.h>
>  #endif

As mentioned, we should leave it to the linker to elide the unneeded headers
instead of the preprocessor, so we enable multi-image across SoCs/SoC families.

>  
>  #ifdef CONFIG_CPU_32
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index 0e8d83fedd..e28f04c354 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -4,6 +4,11 @@ config ARCH_TEXT_BASE
>  	hex
>  	default 0x0
>  
> +config SUNXI_RVBAR_IOMAP
> +	hex
> +	default 0x017000a0  if ARCH_SUN50I_A64
> +	# default 0x09010040 if ARCH_SUN50I_H5

Both of these are ARMv8, so there should be no reason to prevent
a build that targets both SoCs. Could you move this into a header
that defines both SUN50I_H5_RVBAR_IOMAP and
ARCH_SUN50I_A64_RVBAR_IOMAP

> +
>  menuconfig SUNXI_MULTI_BOARDS
>  	bool "Allwinner boards"
>  	select HAVE_PBL_MULTI_IMAGES
> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> index d678973ca2..e7fa23c832 100644
> --- a/arch/arm/mach-sunxi/Makefile
> +++ b/arch/arm/mach-sunxi/Makefile
> @@ -1,2 +1,4 @@
>  obj-y += sunxi.o
>  lwl-y += cpu_init.o
> +
> +pbl-$(CONFIG_CPU_64) += rmr_switch.o
> diff --git a/arch/arm/mach-sunxi/rmr_switch.S b/arch/arm/mach-sunxi/rmr_switch.S
> new file mode 100644
> index 0000000000..bfe3b75e3a
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/rmr_switch.S
> @@ -0,0 +1,47 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * ARMv8 RMR reset sequence on Allwinner SoCs.
> + *
> + * All 64-bit capable Allwinner SoCs reset in AArch32 (and continue to
> + * exectute the Boot ROM in this state), so we need to switch to AArch64

execute

> + * at some point.
> + * Section G6.2.133 of the ARMv8 ARM describes the Reset Management Register
> + * (RMR), which triggers a warm-reset of a core and can request to switch
> + * into a different execution state (AArch32 or AArch64).
> + * The address at which execution starts after the reset is held in the
> + * RVBAR system register, which is architecturally read-only.
> + * Allwinner provides a writable alias of this register in MMIO space, so
> + * we can easily set the start address of AArch64 code.
> + * This code below switches to AArch64 and starts execution at the specified
> + * start address.
> + *
> + * This file has been adapted from U-Boot code sources:
> + *  - arch/arm/mach-sunxi/rmr_switch.S
> + *  - arch/arm/include/asm/arch-sunxi/boot0.h.
> + *
> + * The aarch32 assembly has already been assembled and are inserted verbatime
> + * as .word statements (the asm source is commented for each statement).
> + *
> + */
> +
> +#include <config.h>
> +#include <linux/linkage.h>
> +
> +.section .text_head_rmr_switch, "x"
> +ENTRY(sunxi_rmr_switch)		/* arm32		arm64		*/
> +	.word 0xeb000000	/* bl	.+8		subs x0, x0, x0	*/
> +	b end			/* .word 0x1400000c 	b end		*/
> +	.word 0xe59f0020	/* ldr	r0, [pc, #32] ; rvbar		*/
> +	.word 0xe580e000	/* str  lr, [r0]			*/
> +	.word 0xf57ff04f	/* dsb	sy				*/
> +	.word 0xf57ff06f	/* isb	syo				*/
> +	.word 0xee1c0f50	/* mrc	15, 0, r0, cr12, cr0, {2}	*/
> +	.word 0xe3800003	/* orr	r0, r0, #3			*/
> +	.word 0xee0c0f50	/* mcr	15, 0, r0, cr12, cr0, {2}	*/
> +	.word 0xf57ff06f	/* isb	sy				*/
> +	.word 0xe320f003	/* 1b: wfi				*/
> +	.word 0xeafffffd	/* b	1b				*/
> +	.word CONFIG_SUNXI_RVBAR_IOMAP
> +	.align 3 /* prevent linker script from adding padding for aligment */
> +end:	/* fall-through */
> +ENDPROC(sunxi_rmr_switch)
> diff --git a/include/mach/sunxi/barebox.lds.h b/include/mach/sunxi/barebox.lds.h
> new file mode 100644
> index 0000000000..e04c713611
> --- /dev/null
> +++ b/include/mach/sunxi/barebox.lds.h
> @@ -0,0 +1,6 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +/* theses two sections should only be needed for the pbl */
> +#define ENTRY_HEADER \
> +	*(.text_head_egon_header*)		\
> +	*(.text_head_rmr_switch*)


I'd rather suggest a SUNXI_ENTRY_FUNCTION(name, rvbar, x0, x1, x2) macro and then
a more specific SUN50I_A64_ENTRY_FUNCTION(name, x0, x1, x2)

> diff --git a/include/mach/sunxi/rmr_switch.h b/include/mach/sunxi/rmr_switch.h
> new file mode 100644
> index 0000000000..2ecbd81d57
> --- /dev/null
> +++ b/include/mach/sunxi/rmr_switch.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#ifndef __MACH_RMR_SWITCH_H
> +#define __MACH_RMR_SWITCH_H
> +
> +/* 64-bits Allwinner SoCs reset in AArch32 and need to switch to AArch64 */
> +extern const u32 sunxi_rmr_switch[];
> +#define	sunxi_switch_to_aarch64() __keep_symbolref(sunxi_rmr_switch)
> +
> +#endif

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 05/11] arm: sunxi: Add debug_ll
  2023-05-10 23:37 ` [RFC PATCH 05/11] arm: sunxi: Add debug_ll Jules Maselbas
@ 2023-05-18 19:05   ` Ahmad Fatoum
  2023-05-19 10:36     ` Jules Maselbas
  0 siblings, 1 reply; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 19:05 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> ---
>  arch/arm/include/asm/debug_ll.h |  2 ++
>  arch/arm/mach-sunxi/Kconfig     |  4 ++++
>  include/mach/sunxi/debug_ll.h   | 35 +++++++++++++++++++++++++++++++++
>  3 files changed, 41 insertions(+)
>  create mode 100644 include/mach/sunxi/debug_ll.h
> 
> diff --git a/arch/arm/include/asm/debug_ll.h b/arch/arm/include/asm/debug_ll.h
> index a1d5161ccf..054d021bab 100644
> --- a/arch/arm/include/asm/debug_ll.h
> +++ b/arch/arm/include/asm/debug_ll.h
> @@ -50,6 +50,8 @@
>  #include <mach/uemd/debug_ll.h>
>  #elif defined CONFIG_ARCH_SOCFPGA
>  #include <mach/socfpga/debug_ll.h>
> +#elif defined CONFIG_ARCH_SUNXI
> +#include <mach/sunxi/debug_ll.h>
>  #elif defined CONFIG_ARCH_PXA
>  #include <mach/pxa/debug_ll.h>
>  #elif defined CONFIG_ARCH_NOMADIK
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index e28f04c354..2580a9e56a 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -9,6 +9,10 @@ config SUNXI_RVBAR_IOMAP
>  	default 0x017000a0  if ARCH_SUN50I_A64
>  	# default 0x09010040 if ARCH_SUN50I_H5
>  
> +config SUNXI_DEBUG_LL_UART_BASE
> +	hex
> +	default 0x01c28000
> +
>  menuconfig SUNXI_MULTI_BOARDS
>  	bool "Allwinner boards"
>  	select HAVE_PBL_MULTI_IMAGES
> diff --git a/include/mach/sunxi/debug_ll.h b/include/mach/sunxi/debug_ll.h
> new file mode 100644
> index 0000000000..64c65a5917
> --- /dev/null
> +++ b/include/mach/sunxi/debug_ll.h
> @@ -0,0 +1,35 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#ifndef __MACH_DEBUG_LL_H__
> +#define __MACH_DEBUG_LL_H__
> +
> +#include <io.h>
> +
> +#define DEBUG_LL_UART_ADDR CONFIG_SUNXI_DEBUG_LL_UART_BASE
> +
> +static inline uint8_t debug_ll_read_reg(int reg)
> +{
> +	return readl(IOMEM(DEBUG_LL_UART_ADDR) + reg * sizeof(uint32_t));
> +}
> +
> +static inline void debug_ll_write_reg(int reg, uint8_t val)
> +{
> +	writel(val, IOMEM(DEBUG_LL_UART_ADDR) + reg * sizeof(uint32_t));
> +}
> +
> +#include <debug_ll/ns16550.h>
> +
> +static inline void debug_ll_init(void)
> +{
> +	uint16_t divisor;

Call sunxi_uart_setup here?

> +
> +	divisor = debug_ll_ns16550_calc_divisor(24 * 1000 * 1000);
> +	debug_ll_ns16550_init(divisor);
> +}
> +
> +static inline void PUTC_LL(int c)
> +{
> +       debug_ll_ns16550_putc(c);
> +}
> +
> +#endif

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 06/11] clk: Add clock driver for sun50i-a64
  2023-05-10 23:37 ` [RFC PATCH 06/11] clk: Add clock driver for sun50i-a64 Jules Maselbas
@ 2023-05-18 19:06   ` Ahmad Fatoum
  0 siblings, 0 replies; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 19:06 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> Clock driver is adapted from Linux
> ---
>  drivers/clk/Makefile               |   1 +
>  drivers/clk/sunxi/Makefile         |   2 +
>  drivers/clk/sunxi/clk-sun50i-a64.c | 317 +++++++++++++++++++++++++++++
>  drivers/clk/sunxi/clk-sun50i-a64.h |  62 ++++++
>  4 files changed, 382 insertions(+)
>  create mode 100644 drivers/clk/sunxi/Makefile
>  create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.c
>  create mode 100644 drivers/clk/sunxi/clk-sun50i-a64.h
> 
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index baf452de98..25be2bdc08 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -28,3 +28,4 @@ obj-$(CONFIG_COMMON_CLK_STM32F)		+= clk-stm32f4.o
>  obj-$(CONFIG_MACH_RPI_COMMON)	+= clk-rpi.o
>  obj-y				+= bcm/
>  obj-$(CONFIG_COMMON_CLK_SCMI)	+= clk-scmi.o
> +obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> new file mode 100644
> index 0000000000..4d1dcbebb0
> --- /dev/null
> +++ b/drivers/clk/sunxi/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_ARCH_SUN50I_A64) += clk-sun50i-a64.o
> diff --git a/drivers/clk/sunxi/clk-sun50i-a64.c b/drivers/clk/sunxi/clk-sun50i-a64.c
> new file mode 100644
> index 0000000000..0132641495
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-sun50i-a64.c
> @@ -0,0 +1,317 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +// SPDX-FileCopyrightText: 2022 Jules Maselbas
> +
> +#include <common.h>
> +#include <init.h>
> +#include <driver.h>
> +#include <linux/clk.h>
> +#include <io.h>
> +#include <linux/clkdev.h>
> +#include <linux/err.h>
> +
> +#define MHZ (1000 * 1000UL)
> +
> +#include "clk-sun50i-a64.h"
> +
> +#define CCU_PLL_CPUX		0x00
> +#define CCU_PLL_PERIPH0		0x28
> +#define CCU_CPUX_AXI_CFG	0x50
> +#define CCU_AHB1_APB1_CFG	0x54
> +#define CCU_APB2_CFG		0x58
> +#define CCU_AHB2_CFG		0x5c
> +#define CCU_BUS_CLK_GATE0	0x60
> +#define CCU_BUS_CLK_GATE1	0x64
> +#define CCU_BUS_CLK_GATE2	0x68
> +#define CCU_BUS_CLK_GATE3	0x6c
> +#define CCU_CE_CLK		0x9c
> +#define CCU_MBUS_CLK		0x15c
> +#define CCU_BUS_SOFT_RST0	0x2c0
> +#define CCU_BUS_SOFT_RST4	0x2d8
> +#define CCU_PLL_LOCK_CTRL	0x320
> +
> +static struct clk *clks[CLK_NUMBER];
> +static struct clk_onecell_data clk_data = {
> +	.clks = clks,
> +	.clk_num = ARRAY_SIZE(clks),
> +};
> +
> +static inline struct clk *
> +sunxi_clk_gate(const char *name, const char *parent, void __iomem *reg, u8 shift)
> +{
> +	return clk_gate(name, parent, reg, shift, 0, 0);
> +}
> +
> +static inline struct clk *
> +sunxi_clk_mux(const char *name, const char * const *parents, u8 num_parents,
> +	      void __iomem *reg, u8 shift, u8 width)
> +{
> +	return clk_mux(name, 0, reg, shift, width, parents, num_parents, 0);
> +}
> +
> +static inline struct clk *
> +sunxi_clk_div(const char *name, const char *parent, struct clk_div_table *table,
> +	      void __iomem *reg, u8 shift, u8 width)
> +{
> +	return clk_divider_table(name, parent, CLK_SET_RATE_PARENT, reg, shift,
> +				 width, table, 0);
> +}
> +
> +static struct clk_div_table div_apb1[] = {
> +	{ .val = 0, .div = 2 },
> +	{ .val = 1, .div = 2 },
> +	{ .val = 2, .div = 4 },
> +	{ .val = 3, .div = 8 },
> +	{ /* Sentinel */ },
> +};
> +
> +static struct clk_div_table div_N[] = {
> +	{ .val = 0, .div = 1 },
> +	{ .val = 1, .div = 2 },
> +	{ .val = 2, .div = 4 },
> +	{ .val = 3, .div = 8 },
> +	{ /* Sentinel */ },
> +};
> +
> +static const char *sel_cpux[] = {
> +	"osc32k",
> +	"osc24M",
> +	"pll-cpux",
> +};
> +
> +static const char *sel_ahb1[] = {
> +	"osc32k",
> +	"osc24M",
> +	"axi",
> +	"pll-periph0",
> +};
> +
> +static const char *sel_apb2[] = {
> +	"osc32k",
> +	"osc24M",
> +	"pll-periph0-2x",
> +	"pll-periph0-2x",
> +};
> +
> +static const char *sel_ahb2[] = {
> +	"ahb1",
> +	"pll-periph0",
> +};
> +
> +static const char *sel_mmc[] = {
> +	"osc24M",
> +	"pll-periph0-2x",
> +	"pll-periph1-2x",
> +};
> +
> +static void sun50i_a64_resets_init(void __iomem *regs)
> +{
> +	u32 rst;
> +
> +	rst = 0 |
> +		/* RST_USB_PHY0 */ BIT(0) |
> +		/* RST_USB_PHY1 */ BIT(1) |
> +		/* RST_USB_HSIC */ BIT(2);
> +	writel(rst, regs + 0x0cc);
> +
> +	rst = 0 |
> +		/* RST_BUS_MIPI_DSI */ BIT(1) |
> +		/* RST_BUS_CE   */ BIT(5) |
> +		/* RST_BUS_DMA  */ BIT(6) |
> +		/* RST_BUS_MMC0 */ BIT(8) |
> +		/* RST_BUS_MMC1 */ BIT(9) |
> +		/* RST_BUS_MMC2 */ BIT(10) |
> +		/* RST_BUS_NAND */ BIT(13) |
> +		/* RST_BUS_DRAM */ BIT(14) |
> +		/* RST_BUS_EMAC */ BIT(17) |
> +		/* RST_BUS_TS   */ BIT(18) |
> +		/* RST_BUS_HSTIMER */ BIT(19) |
> +		/* RST_BUS_SPI0  */ BIT(20) |
> +		/* RST_BUS_SPI1  */ BIT(21) |
> +		/* RST_BUS_OTG   */ BIT(23) |
> +		/* RST_BUS_EHCI0 */ BIT(24) |
> +		/* RST_BUS_EHCI1 */ BIT(25) |
> +		/* RST_BUS_OHCI0 */ BIT(28) |
> +		/* RST_BUS_OHCI1 */ BIT(29);
> +	writel(rst, regs + 0x2c0);
> +
> +	rst = 0 |
> +		/* RST_BUS_VE    */ BIT(0) |
> +		/* RST_BUS_TCON0 */ BIT(3) |
> +		/* RST_BUS_TCON1 */ BIT(4) |
> +		/* RST_BUS_DEINTERLACE */ BIT(5) |
> +		/* RST_BUS_CSI   */ BIT(8) |
> +		/* RST_BUS_HDMI0 */ BIT(10) |
> +		/* RST_BUS_HDMI1 */ BIT(11) |
> +		/* RST_BUS_DE    */ BIT(12) |
> +		/* RST_BUS_GPU   */ BIT(20) |
> +		/* RST_BUS_MSGBOX   */ BIT(21) |
> +		/* RST_BUS_SPINLOCK */ BIT(22) |
> +		/* RST_BUS_DBG */ BIT(31);
> +	writel(rst, regs + 0x2c4);
> +
> +	rst = /* RST_BUS_LVDS */ BIT(0);
> +	writel(rst, regs + 0x2c8);
> +
> +	rst = 0 |
> +		/* RST_BUS_CODEC */ BIT(0) |
> +		/* RST_BUS_SPDIF */ BIT(1) |
> +		/* RST_BUS_THS   */ BIT(8) |
> +		/* RST_BUS_I2S0  */ BIT(12) |
> +		/* RST_BUS_I2S1  */ BIT(13) |
> +		/* RST_BUS_I2S2  */ BIT(14);
> +	writel(rst, regs + 0x2d0);
> +
> +	rst = 0 |
> +		/* RST_BUS_I2C0 */ BIT(0) |
> +		/* RST_BUS_I2C1 */ BIT(1) |
> +		/* RST_BUS_I2C2 */ BIT(2) |
> +		/* RST_BUS_SCR  */ BIT(5) |
> +		/* RST_BUS_UART0 */ BIT(16) |
> +		/* RST_BUS_UART1 */ BIT(17) |
> +		/* RST_BUS_UART2 */ BIT(18) |
> +		/* RST_BUS_UART3 */ BIT(19) |
> +		/* RST_BUS_UART4 */ BIT(20);
> +	writel(rst, regs + 0x2d8);
> +}
> +
> +static int
> +sunxi_clk_set_pll(void __iomem *reg, u32 src, u32 freq)
> +{
> +	/* NOTE: using u32, max freq is 4GHz
> +	 * out freq: src * N * K
> +	 * factor N: [1->32]
> +	 * factor K: [1->4]
> +	 * from the manual: give priority to the choice of K >= 2
> +	 */
> +	u32 mul = freq / src; /* target multiplier (N * K) */
> +	u32 k, n;
> +	u32 cfg = BIT(31); /* ENABLE */
> +
> +	for (k = 4; k > 1; k--) {
> +		if ((mul % k) == 0)
> +			break;
> +	}
> +	n = mul / k;
> +
> +	cfg |= (k - 1) << 4;
> +	cfg |= (n - 1) << 8;
> +
> +	writel(cfg, reg);
> +	return wait_on_timeout(1 * MSECOND, readl(reg) & BIT(28));
> +}
> +
> +static void sun50i_a64_clocks_init(struct device_node *np, void __iomem *regs)
> +{
> +	sun50i_a64_resets_init(regs);
> +
> +	/* set pll-cpu to 1.2GHz */
> +	if (sunxi_clk_set_pll(regs + CCU_PLL_CPUX, 24 * MHZ, 1200 * MHZ))
> +		pr_err("fail to lock pll-cpu @ 1.2GHz\n");
> +	clks[CLK_PLL_CPUX] = clk_fixed("pll-cpux", 1200 * MHZ);
> +
> +	/* switch cpu clock source: cpux_src: 1=24mhz 2=PLL_CPUX */
> +	clks[CLK_CPUX] = sunxi_clk_mux("cpux", sel_cpux, ARRAY_SIZE(sel_cpux), regs + CCU_CPUX_AXI_CFG, 16, 2);
> +	writel(0x20001, regs + CCU_CPUX_AXI_CFG); /* select pll-cpu */
> +	udelay(1); /* wait 8 cycles */
> +
> +	/* set pll-periph0-2x to 1.2GHz, as recommended */
> +	if (sunxi_clk_set_pll(regs + CCU_PLL_PERIPH0, 24 * MHZ, 2 * 600 * MHZ))
> +		pr_err("fail to lock pll-periph @ 1.2GHz\n");
> +
> +	clks[CLK_PLL_PERIPH0]    = clk_fixed("pll-periph0", 600 * MHZ);
> +	clks[CLK_PLL_PERIPH0_2X] = clk_fixed_factor("pll-periph0-2x", "pll-periph0", 2, 1, 0);
> +
> +	clks[CLK_AHB1] = sunxi_clk_mux("ahb1", sel_ahb1, ARRAY_SIZE(sel_ahb1), regs + 0x054, 12, 2);
> +	clks[CLK_AHB2] = sunxi_clk_mux("ahb2", sel_ahb2, ARRAY_SIZE(sel_ahb2), regs + 0x05c, 0, 1);
> +
> +	clks[CLK_APB1] = sunxi_clk_div("apb1", "ahb1", div_apb1, regs + 0x054, 8, 2);
> +	clks[CLK_APB2] = sunxi_clk_mux("apb2", sel_apb2, ARRAY_SIZE(sel_apb2), regs + 0x058, 24, 2);
> +
> +	clks[CLK_BUS_MIPI_DSI] = sunxi_clk_gate("bus-mipi-dsi","ahb1",regs + 0x060, 1);
> +	clks[CLK_BUS_CE]    = sunxi_clk_gate("bus-ce",    "ahb1", regs + 0x060, 5);
> +	clks[CLK_BUS_DMA]   = sunxi_clk_gate("bus-dma",   "ahb1", regs + 0x060, 6);
> +	clks[CLK_BUS_MMC0]  = sunxi_clk_gate("bus-mmc0",  "ahb1", regs + 0x060, 8);
> +	clks[CLK_BUS_MMC1]  = sunxi_clk_gate("bus-mmc1",  "ahb1", regs + 0x060, 9);
> +	clks[CLK_BUS_MMC2]  = sunxi_clk_gate("bus-mmc2",  "ahb1", regs + 0x060, 10);
> +	clks[CLK_BUS_NAND]  = sunxi_clk_gate("bus-nand",  "ahb1", regs + 0x060, 13);
> +	clks[CLK_BUS_DRAM]  = sunxi_clk_gate("bus-dram",  "ahb1", regs + 0x060, 14);
> +	clks[CLK_BUS_EMAC]  = sunxi_clk_gate("bus-emac",  "ahb2", regs + 0x060, 17);
> +	clks[CLK_BUS_TS]    = sunxi_clk_gate("bus-ts",    "ahb1", regs + 0x060, 18);
> +	clks[CLK_BUS_HSTIMER] = sunxi_clk_gate("bus-hstimer", "ahb1", regs + 0x060, 19);
> +	clks[CLK_BUS_SPI0]  = sunxi_clk_gate("bus-spi0",  "ahb1", regs + 0x060,  20);
> +	clks[CLK_BUS_SPI1]  = sunxi_clk_gate("bus-spi1",  "ahb1", regs + 0x060,  21);
> +	clks[CLK_BUS_OTG]   = sunxi_clk_gate("bus-otg",   "ahb1", regs + 0x060,  23);
> +	clks[CLK_BUS_EHCI0] = sunxi_clk_gate("bus-ehci0", "ahb1", regs + 0x060, 24);
> +	clks[CLK_BUS_EHCI1] = sunxi_clk_gate("bus-ehci1", "ahb2", regs + 0x060, 25);
> +	clks[CLK_BUS_OHCI0] = sunxi_clk_gate("bus-ohci0", "ahb1", regs + 0x060, 28);
> +	clks[CLK_BUS_OHCI1] = sunxi_clk_gate("bus-ohci1", "ahb2", regs + 0x060, 29);
> +
> +	clks[CLK_BUS_CODEC] = sunxi_clk_gate("bus-codec", "apb1", regs + 0x068, 0);
> +	clks[CLK_BUS_SPDIF] = sunxi_clk_gate("bus-spdif", "apb1", regs + 0x068, 1);
> +	clks[CLK_BUS_PIO]   = sunxi_clk_gate("bus-pio",   "apb1", regs + 0x068, 5);
> +	clks[CLK_BUS_THS]   = sunxi_clk_gate("bus-ths",   "apb1", regs + 0x068, 8);
> +	clks[CLK_BUS_I2S0]  = sunxi_clk_gate("bus-i2s0",  "apb1", regs + 0x068, 12);
> +	clks[CLK_BUS_I2S1]  = sunxi_clk_gate("bus-i2s1",  "apb1", regs + 0x068, 13);
> +	clks[CLK_BUS_I2S2]  = sunxi_clk_gate("bus-i2s2",  "apb1", regs + 0x068, 14);
> +
> +	clks[CLK_BUS_UART0] = sunxi_clk_gate("bus-uart0", "apb2", regs + 0x06c, 16);
> +	clks[CLK_BUS_UART1] = sunxi_clk_gate("bus-uart1", "apb2", regs + 0x06c, 17);
> +	clks[CLK_BUS_UART2] = sunxi_clk_gate("bus-uart2", "apb2", regs + 0x06c, 18);
> +	clks[CLK_BUS_UART3] = sunxi_clk_gate("bus-uart3", "apb2", regs + 0x06c, 19);
> +	clks[CLK_BUS_UART4] = sunxi_clk_gate("bus-uart4", "apb2", regs + 0x06c, 20);
> +
> +	clks[CLK_MMC0] = clk_register_composite(
> +		"mmc0",	sel_mmc, ARRAY_SIZE(sel_mmc),
> +		sunxi_clk_mux("mmc0-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x088, 24, 2),
> +		sunxi_clk_div("mmc0-div-n", "mmc0-gate", div_N, regs + 0x088, 16, 2),
> +		sunxi_clk_gate("mmc0-gate", "mmc0-mux", regs + 0x088, 31),
> +		0);
> +
> +	clks[CLK_MMC1] = clk_register_composite(
> +		"mmc1",	sel_mmc, ARRAY_SIZE(sel_mmc),
> +		sunxi_clk_mux("mmc1-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x08c, 24, 2),
> +		sunxi_clk_div("mmc1-div-n", "mmc1-gate", div_N, regs + 0x08c, 16, 2),
> +		sunxi_clk_gate("mmc1-gate", "mmc1-mux", regs + 0x08c, 31),
> +		0);
> +
> +	clks[CLK_MMC2] = clk_register_composite(
> +		"mmc2",	sel_mmc, ARRAY_SIZE(sel_mmc),
> +		sunxi_clk_mux("mmc2-mux", sel_mmc, ARRAY_SIZE(sel_mmc), regs + 0x090, 24, 2),
> +		sunxi_clk_div("mmc2-div-n", "mmc2-gate", div_N, regs + 0x090, 16, 2),
> +		sunxi_clk_gate("mmc2-gate", "mmc2-mux", regs + 0x090, 31),
> +		0);
> +
> +	/* generic set_rate doesn't support switching parent,
> +	 * let's do it here for now */
> +	clk_set_parent(clks[CLK_MMC0], clks[CLK_PLL_PERIPH0_2X]);
> +	clk_set_parent(clks[CLK_MMC2], clks[CLK_PLL_PERIPH0_2X]);
> +}
> +
> +static int sun50i_a64_ccu_probe(struct device *dev)
> +{
> +	struct resource *iores;
> +
> +	iores = dev_request_mem_resource(dev, 0);
> +	if (IS_ERR(iores))
> +		return PTR_ERR(iores);
> +
> +	sun50i_a64_clocks_init(dev->of_node, IOMEM(iores->start));
> +	of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);

You can return this instead of unconditional 0.

> +
> +	return 0;
> +}
> +
> +static __maybe_unused struct of_device_id sun50i_a64_ccu_dt_ids[] = {
> +	{
> +		.compatible = "allwinner,sun50i-a64-ccu",
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +
> +static struct driver sun50i_a64_ccu_driver = {
> +	.probe	= sun50i_a64_ccu_probe,
> +	.name	= "sun50i-a64-ccu",
> +	.of_compatible = DRV_OF_COMPAT(sun50i_a64_ccu_dt_ids),
> +};
> +postcore_platform_driver(sun50i_a64_ccu_driver);
> diff --git a/drivers/clk/sunxi/clk-sun50i-a64.h b/drivers/clk/sunxi/clk-sun50i-a64.h
> new file mode 100644
> index 0000000000..a4ddc39eb8
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-sun50i-a64.h
> @@ -0,0 +1,62 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright 2016 Maxime Ripard
> + *
> + * Maxime Ripard <maxime.ripard@free-electrons.com>
> + */
> +
> +#include <dt-bindings/clock/sun50i-a64-ccu.h>
> +
> +#ifndef _CLK_SUN50I_A64_H_
> +#define _CLK_SUN50I_A64_H_
> +
> +#include <dt-bindings/clock/sun50i-a64-ccu.h>
> +#include <dt-bindings/reset/sun50i-a64-ccu.h>
> +
> +#define CLK_OSC_12M			0
> +#define CLK_PLL_CPUX			1
> +#define CLK_PLL_AUDIO_BASE		2
> +#define CLK_PLL_AUDIO			3
> +#define CLK_PLL_AUDIO_2X		4
> +#define CLK_PLL_AUDIO_4X		5
> +#define CLK_PLL_AUDIO_8X		6
> +
> +/* PLL_VIDEO0 exported for HDMI PHY */
> +
> +#define CLK_PLL_VIDEO0_2X		8
> +#define CLK_PLL_VE			9
> +#define CLK_PLL_DDR0			10
> +
> +/* PLL_PERIPH0 exported for PRCM */
> +
> +#define CLK_PLL_PERIPH0_2X		12
> +#define CLK_PLL_PERIPH1			13
> +#define CLK_PLL_PERIPH1_2X		14
> +#define CLK_PLL_VIDEO1			15
> +#define CLK_PLL_GPU			16
> +#define CLK_PLL_MIPI			17
> +#define CLK_PLL_HSIC			18
> +#define CLK_PLL_DE			19
> +#define CLK_PLL_DDR1			20
> +#define CLK_AXI				22
> +#define CLK_APB				23
> +#define CLK_AHB1			24
> +#define CLK_APB1			25
> +#define CLK_APB2			26
> +#define CLK_AHB2			27
> +
> +/* All the bus gates are exported */
> +
> +/* The first bunch of module clocks are exported */
> +
> +#define CLK_USB_OHCI0_12M		90
> +
> +#define CLK_USB_OHCI1_12M		92
> +
> +/* All the DRAM gates are exported */
> +
> +/* And the DSI and GPU module clock is exported */
> +
> +#define CLK_NUMBER			(CLK_GPU + 1)
> +
> +#endif /* _CLK_SUN50I_A64_H_ */

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 07/11] pinctrl: Add sun50i-a64 pinctrl driver
  2023-05-10 23:37 ` [RFC PATCH 07/11] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
@ 2023-05-18 19:10   ` Ahmad Fatoum
  2023-05-19 10:52     ` Jules Maselbas
  0 siblings, 1 reply; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 19:10 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> sunxi pinctrl driver, adapted from Linux, is split in two parts:
>  - pinctrl-sunxi.c that implement sunxi-generic gpio and function mux
>  - pinctrl-sun50i-a64.c that only declare sun50i's pin functions

Could you describe why the pin functions is needed? Is this just sanity
checking?

> ---
>  drivers/pinctrl/Kconfig                    |   2 +
>  drivers/pinctrl/Makefile                   |   1 +
>  drivers/pinctrl/sunxi/Kconfig              |  15 +
>  drivers/pinctrl/sunxi/Makefile             |   3 +
>  drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 593 +++++++++++++++++++++
>  drivers/pinctrl/sunxi/pinctrl-sunxi.c      | 371 +++++++++++++
>  drivers/pinctrl/sunxi/pinctrl-sunxi.h      | 224 ++++++++
>  7 files changed, 1209 insertions(+)
>  create mode 100644 drivers/pinctrl/sunxi/Kconfig
>  create mode 100644 drivers/pinctrl/sunxi/Makefile
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h
> 
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 2ff99a39c8..0b3d79d1cc 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -105,6 +105,8 @@ config PINCTRL_STM32
>  	default y if ARCH_STM32
>  	help
>  	  Pinmux and GPIO controller found on STM32 family
> +
> +source "drivers/pinctrl/sunxi/Kconfig"
>  endif
>  
>  endmenu
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index f1a5fa5715..3bc718d355 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o
>  obj-$(CONFIG_PINCTRL_STM32) += pinctrl-stm32.o
>  
>  obj-$(CONFIG_ARCH_MVEBU) += mvebu/
> +obj-$(CONFIG_ARCH_SUNXI) += sunxi/
> diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
> new file mode 100644
> index 0000000000..36168dc2bb
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +if ARCH_SUNXI
> +
> +config PINCTRL_SUNXI
> +	bool
> +	select PINMUX

Undefined

> +	select GENERIC_PINCONF

Undefined

> +	select GPIOLIB
> +
> +config PINCTRL_SUN50I_A64
> +	bool "Support for the Allwinner A64 PIO"
> +	default ARM64 && ARCH_SUNXI

ARM64 undefined

> +	select PINCTRL_SUNXI
> +
> +endif
> diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
> new file mode 100644
> index 0000000000..db0ff5b50b
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_ARCH_SUNXI)		+= pinctrl-sunxi.o
> +obj-$(CONFIG_PINCTRL_SUN50I_A64)	+= pinctrl-sun50i-a64.o
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> new file mode 100644
> index 0000000000..4a087802c8
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> @@ -0,0 +1,593 @@
> +/*
> + * Allwinner A64 SoCs pinctrl driver.
> + *
> + * Copyright (C) 2016 - ARM Ltd.
> + * Author: Andre Przywara <andre.przywara@arm.com>
> + *
> + * Based on pinctrl-sun7i-a20.c, which is:
> + * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2.  This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.

SPDX?

> + */
> +
> +#include <common.h>
> +#include <init.h>
> +
> +#include "pinctrl-sunxi.h"
> +
> +static const struct sunxi_desc_pin a64_pins[] = {
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart2"),		/* TX */
> +		  SUNXI_FUNCTION(0x4, "jtag"),		/* MS0 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),	/* EINT0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart2"),		/* RX */
> +		  SUNXI_FUNCTION(0x4, "jtag"),		/* CK0 */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* VCCEN */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),		/* EINT1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart2"),		/* RTS */
> +		  SUNXI_FUNCTION(0x4, "jtag"),		/* DO0 */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPEN */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),		/* EINT2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart2"),		/* CTS */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* MCLK */
> +		  SUNXI_FUNCTION(0x4, "jtag"),		/* DI0 */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPPP */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),		/* EINT3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif2"),		/* SYNC */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* SYNC */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* CLK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),		/* EINT4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif2"),		/* BCLK */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* BCLK */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* DATA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),		/* EINT5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif2"),		/* DOUT */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DOUT */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* RST */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),		/* EINT6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif2"),		/* DIN */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DIN */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* DET */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),		/* EINT7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x4, "uart0"),		/* TX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),		/* EINT8 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x4, "uart0"),		/* RX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),		/* EINT9 */
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NWE */
> +		  SUNXI_FUNCTION(0x4, "spi0")),		/* MOSI */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NALE */
> +		  SUNXI_FUNCTION(0x3, "mmc2"),		/* DS */
> +		  SUNXI_FUNCTION(0x4, "spi0")),		/* MISO */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCLE */
> +		  SUNXI_FUNCTION(0x4, "spi0")),		/* SCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCE1 */
> +		  SUNXI_FUNCTION(0x4, "spi0")),		/* CS */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0")),	/* NCE0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRE# */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CLK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRB0 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CMD */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0")),	/* NRB1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ0 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ1 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ2 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ3 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ4 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ5 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ6 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ7 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQS */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* RST */
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D2 */
> +		  SUNXI_FUNCTION(0x3, "uart3"),		/* TX */
> +		  SUNXI_FUNCTION(0x4, "spi1"),		/* CS */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* CLK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D3 */
> +		  SUNXI_FUNCTION(0x3, "uart3"),		/* RX */
> +		  SUNXI_FUNCTION(0x4, "spi1"),		/* CLK */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* DE */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D4 */
> +		  SUNXI_FUNCTION(0x3, "uart4"),		/* TX */
> +		  SUNXI_FUNCTION(0x4, "spi1"),		/* MOSI */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* HSYNC */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D5 */
> +		  SUNXI_FUNCTION(0x3, "uart4"),		/* RX */
> +		  SUNXI_FUNCTION(0x4, "spi1"),		/* MISO */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* VSYNC */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D6 */
> +		  SUNXI_FUNCTION(0x3, "uart4"),		/* RTS */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D7 */
> +		  SUNXI_FUNCTION(0x3, "uart4"),		/* CTS */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D10 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D11 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D12 */
> +		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD3 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D13 */
> +		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD2 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D14 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D15 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D18 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP0 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D19 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN0 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCTL */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D20 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP1 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ENULL */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D21 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN1 */
> +		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD3 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D22 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP2 */
> +		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD2 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D23 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN2 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* CLK */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VPC */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* DE */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VNC */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* HSYNC */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP3 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCTL */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* VSYNC */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN3 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ECLKIN */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "pwm"),		/* PWM0 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDC */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDIO */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* PCK */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* CLK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* CK */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* ERR */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* HSYNC */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* SYNC */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* VSYNC */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* DVLD */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* D0 */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* D0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* D1 */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* D1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* D2 */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* D2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* D3 */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* D3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* D4 */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* D4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* D5 */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* D5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* D6 */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* D6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi"),		/* D7 */
> +		  SUNXI_FUNCTION(0x4, "ts")),		/* D7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi")),		/* SCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi")),		/* SDA */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "pll"),		/* LOCK_DBG */
> +		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SDA */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D1 */
> +		  SUNXI_FUNCTION(0x3, "jtag")),		/* MSI */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D0 */
> +		  SUNXI_FUNCTION(0x3, "jtag")),		/* DI1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CLK */
> +		  SUNXI_FUNCTION(0x3, "uart0")),	/* TX */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CMD */
> +		  SUNXI_FUNCTION(0x3, "jtag")),		/* DO1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D3 */
> +		  SUNXI_FUNCTION(0x3, "uart0")),	/* RX */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D2 */
> +		  SUNXI_FUNCTION(0x3, "jtag")),		/* CK1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CLK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)),	/* EINT0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CMD */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)),	/* EINT1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D0 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)),	/* EINT2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D1 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)),	/* EINT3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D2 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)),	/* EINT4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D3 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)),	/* EINT5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart1"),		/* TX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)),	/* EINT6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart1"),		/* RX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)),	/* EINT7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart1"),		/* RTS */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)),	/* EINT8 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart1"),		/* CTS */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)),	/* EINT9 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif3"),		/* SYNC */
> +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* SYNC */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)),	/* EINT10 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif3"),		/* BCLK */
> +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* BCLK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)),	/* EINT11 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif3"),		/* DOUT */
> +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DOUT */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)),	/* EINT12 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif3"),		/* DIN */
> +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DIN */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)),	/* EINT13 */
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SCK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)),	/* EINT0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SDA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),	/* EINT1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SCK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)),	/* EINT2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SDA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)),	/* EINT3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart3"),		/* TX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)),	/* EINT4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart3"),		/* RX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)),	/* EINT5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart3"),		/* RTS */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)),	/* EINT6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart3"),		/* CTS */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)),	/* EINT7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "spdif"),		/* OUT */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)),	/* EINT8 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)),	/* EINT9 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mic"),		/* CLK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)),	/* EINT10 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mic"),		/* DATA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)),	/* EINT11 */
> +};
> +
> +static const struct sunxi_pinctrl_desc a64_pinctrl_data = {
> +	.pins = a64_pins,
> +	.npins = ARRAY_SIZE(a64_pins),
> +};
> +
> +static const struct of_device_id a64_pinctrl_dt_match[] = {
> +	{
> +		.compatible = "allwinner,sun50i-a64-pinctrl",
> +		.data = &a64_pinctrl_data
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +
> +static struct driver a64_pinctrl_driver = {
> +	.name		= "sun50i-a64-pinctrl",
> +	.probe		= sunxi_pinctrl_probe,
> +	.of_compatible	= DRV_OF_COMPAT(a64_pinctrl_dt_match),
> +};
> +core_platform_driver(a64_pinctrl_driver);
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> new file mode 100644
> index 0000000000..e2bde96c70
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> @@ -0,0 +1,371 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#define pr_fmt(fmt) "pinctrl-sunxi: " fmt
> +
> +#include <common.h>
> +#include <io.h>
> +#include <of.h>
> +#include <of_address.h>
> +#include <malloc.h>
> +#include <linux/clk.h>
> +
> +#include "pinctrl-sunxi.h"
> +
> +/* This driver assumes the gpio function mux value will not change */
> +#define FUNC_GPIO_IN	0
> +#define FUNC_GPIO_OUT	1
> +
> +static struct sunxi_pinctrl *to_sunxi_pinctrl(struct pinctrl_device *pdev)
> +{
> +	return container_of(pdev, struct sunxi_pinctrl, pdev);
> +}
> +
> +static void sunxi_pinctrl_set_pull(struct sunxi_pinctrl *pinctrl,
> +				   u16 pin, u32 pull)
> +{
> +	u32 reg = sunxi_pull_reg(pin);
> +	u32 off = sunxi_pull_offset(pin);
> +	u32 msk = MUX_PINS_MASK << off;
> +	u32 val = readl(pinctrl->base + reg);
> +
> +	val &= ~msk;
> +	val |= (pull << off) & msk;
> +	writel(val, pinctrl->base + reg);
> +}
> +
> +static void sunxi_pinctrl_set_dlevel(struct sunxi_pinctrl *pinctrl,
> +				   u16 pin, u32 lvl)
> +{
> +	u32 reg = sunxi_dlevel_reg(pin);
> +	u32 off = sunxi_dlevel_offset(pin);
> +	u32 msk = MUX_PINS_MASK << off;
> +	u32 val = readl(pinctrl->base + reg);
> +
> +	val &= ~msk;
> +	val |= (lvl << off) & msk;
> +	writel(val, pinctrl->base + reg);
> +}
> +
> +static void sunxi_pinctrl_set_mux(struct sunxi_pinctrl *pinctrl,
> +				  u16 pin, u8 mux)
> +{
> +	u32 reg = sunxi_mux_reg(pin);
> +	u32 off = sunxi_mux_offset(pin);
> +	u32 msk = MUX_PINS_MASK << off;
> +	u32 val = readl(pinctrl->base + reg);
> +
> +	val &= ~msk;
> +	val |= (mux << off) & msk;
> +	writel(val, pinctrl->base + reg);
> +}
> +
> +static u8 sunxi_pinctrl_get_mux(struct sunxi_pinctrl *pinctrl, u16 pin)
> +{
> +	u32 reg = sunxi_mux_reg(pin);
> +	u32 off = sunxi_mux_offset(pin);
> +	u32 val = readl(pinctrl->base + reg);
> +
> +	return (val >> off) & MUX_PINS_MASK;
> +}
> +
> +static void sunxi_pinctrl_set_conf(struct sunxi_pinctrl *pinctrl,
> +				  u16 pin, struct device_node *node)
> +{
> +	u32 val;
> +
> +	if (of_find_property(node, "bias-pull-up", NULL))
> +		sunxi_pinctrl_set_pull(pinctrl, pin, 1);
> +	if (of_find_property(node, "bias-pull-down", NULL))
> +		sunxi_pinctrl_set_pull(pinctrl, pin, 2);
> +	if (of_find_property(node, "bias-disable", NULL))
> +		sunxi_pinctrl_set_pull(pinctrl, pin, 0);
> +
> +	if (!of_property_read_u32(node, "drive-strength", &val)) {
> +		val = rounddown(val, 10) / 10 - 1;
> +		sunxi_pinctrl_set_dlevel(pinctrl, pin, val);
> +	}
> +}
> +
> +static const char *sunxi_pinctrl_parse_function_prop(struct device_node *node)
> +{
> +	const char *function;
> +	int ret;
> +
> +	/* Try the generic binding */
> +	ret = of_property_read_string(node, "function", &function);
> +	if (!ret)
> +		return function;
> +
> +	/* And fall back to our legacy one */
> +	ret = of_property_read_string(node, "allwinner,function", &function);
> +	if (!ret)
> +		return function;
> +
> +	return NULL;
> +}
> +
> +static struct property *sunxi_pinctrl_find_pins_prop(struct device_node *node)
> +{
> +	struct property *prop;
> +
> +	/* Try the generic binding */
> +	prop = of_find_property(node, "pins", NULL);
> +	if (prop)
> +		return prop;
> +
> +	/* And fall back to our legacy one */
> +	prop = of_find_property(node, "allwinner,pins", NULL);
> +	if (prop)
> +		return prop;
> +
> +	return NULL;
> +}
> +
> +#define sunxi_pinctrl_of_pins_for_each_string(np, prop, s)	\
> +	for (prop = sunxi_pinctrl_find_pins_prop(np),		\
> +		s = of_prop_next_string(prop, NULL);		\
> +		s;						\
> +		s = of_prop_next_string(prop, s))
> +
> +
> +static const struct sunxi_desc_pin *
> +sunxi_pinctrl_find_pin(struct sunxi_pinctrl *pinctrl, const char *pin_name)
> +{
> +	const struct sunxi_desc_pin *pin;
> +	int i;
> +
> +	for (i = 0; i < pinctrl->desc->npins; i++) {
> +		pin = &pinctrl->desc->pins[i];
> +		if (!strcmp(pin->pin.name, pin_name))
> +			return pin;
> +	}
> +
> +	return NULL;
> +}
> +
> +static const struct sunxi_desc_function *
> +sunxi_pinctrl_find_func(struct sunxi_pinctrl *pinctrl,
> +			const char *pin_name, const char *func_name)
> +{
> +	const struct sunxi_desc_pin *pin;
> +	const struct sunxi_desc_function *func;
> +
> +	pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
> +	if (!pin)
> +		return NULL;
> +
> +	for (func = pin->functions; func->name; func++)
> +		if (!strcmp(func->name, func_name))
> +			return func;
> +
> +	return NULL;
> +}
> +
> +static int sunxi_pinctrl_set_func(struct sunxi_pinctrl *pinctrl,
> +				  struct device_node *np,
> +				  const char *pin_name, const char *func_name)
> +{
> +	struct device *dev = pinctrl->pdev.dev;
> +	const struct sunxi_desc_pin *pin;
> +	const struct sunxi_desc_function *func;
> +
> +	dev_dbg(dev, "setfunc %s @ %s\n", func_name, pin_name);
> +
> +	pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
> +	if (!pin) {
> +		dev_err(dev, "pin %s not found\n", pin_name);
> +		return -EINVAL;
> +	}
> +
> +	func = sunxi_pinctrl_find_func(pinctrl, pin_name, func_name);
> +	if (!func) {
> +		dev_err(dev, "func %s not found\n", func_name);
> +		return -EINVAL;
> +	}
> +
> +	sunxi_pinctrl_set_mux(pinctrl, pin->pin.number, func->muxval);
> +	sunxi_pinctrl_set_conf(pinctrl, pin->pin.number, np);
> +
> +	return 0;
> +}
> +
> +static int sunxi_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node *np)
> +{
> +	struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
> +	struct device *dev = pinctrl->pdev.dev;
> +	struct property *prop;
> +	const char *func_name;
> +	const char *pin_name;
> +
> +	func_name = sunxi_pinctrl_parse_function_prop(np);
> +	if (!func_name) {
> +		dev_err(dev, "%s: missing 'function' property\n", np->full_name);
> +		return -EINVAL;
> +	}
> +
> +	sunxi_pinctrl_of_pins_for_each_string(np, prop, pin_name) {
> +		sunxi_pinctrl_set_func(pinctrl, np, pin_name, func_name);
> +	}
> +
> +	return 0;
> +}
> +
> +static int sunxi_pinctrl_set_direction(struct pinctrl_device *pdev, unsigned int gpio, bool in)
> +{
> +	struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
> +	u32 func = in ? FUNC_GPIO_IN : FUNC_GPIO_OUT;
> +
> +	sunxi_pinctrl_set_mux(pinctrl, gpio, func);
> +
> +	return 0;
> +}
> +
> +static int sunxi_gpio_get(struct gpio_chip *chip, unsigned gpio)
> +{
> +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> +	u32 reg = sunxi_data_reg(gpio);
> +	u32 bit = sunxi_data_offset(gpio);
> +	u32 val = readl(pinctrl->base + reg);
> +
> +	return val & BIT(bit);
> +}
> +
> +static void sunxi_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
> +{
> +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> +	u32 reg = sunxi_data_reg(gpio);
> +	u32 bit = sunxi_data_offset(gpio);
> +	u32 val = readl(pinctrl->base + reg);
> +
> +	if (value)
> +		val |= BIT(bit);
> +	else
> +		val &= ~BIT(bit);
> +	writel(val, pinctrl->base + reg);
> +}
> +
> +static int sunxi_gpio_direction_output(struct gpio_chip *chip,
> +				       unsigned gpio, int value)
> +{
> +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> +
> +	sunxi_gpio_set(chip, gpio, value);
> +	sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_OUT);
> +
> +	return 0;
> +}
> +
> +static int sunxi_gpio_direction_input(struct gpio_chip *chip,
> +					unsigned gpio)
> +{
> +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> +
> +	sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_IN);
> +
> +	return 0;
> +}
> +
> +static int sunxi_gpio_get_direction(struct gpio_chip *chip, unsigned gpio)
> +{
> +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> +	u32 func = sunxi_pinctrl_get_mux(pinctrl, gpio);
> +
> +	if (func == FUNC_GPIO_IN)
> +		return GPIOF_DIR_IN;
> +	if (func == FUNC_GPIO_OUT)
> +		return GPIOF_DIR_OUT;
> +	return -EINVAL;
> +}
> +
> +static int sunxi_gpio_of_xlate(struct gpio_chip *chip,
> +			       const struct of_phandle_args *gpiospec,
> +			       u32 *flags)
> +{
> +	int pin, base;
> +
> +	if (gpiospec->args_count != 3)
> +		return -EINVAL;
> +
> +	base = PINS_PER_BANK * gpiospec->args[0];
> +	pin = base + gpiospec->args[1];
> +
> +	if (pin > chip->ngpio)
> +		return -EINVAL;
> +
> +	if (flags)
> +		*flags = gpiospec->args[2];
> +
> +	return pin;
> +}
> +
> +static struct pinctrl_ops sunxi_pinctrl_ops = {
> +	.set_state = sunxi_pinctrl_set_state,
> +	.set_direction = sunxi_pinctrl_set_direction,
> +};
> +
> +static struct gpio_ops sunxi_gpio_ops = {
> +	.request = sunxi_gpio_direction_input, /* switch to input function */
> +	.direction_input = sunxi_gpio_direction_input,
> +	.direction_output = sunxi_gpio_direction_output,
> +	.get_direction = sunxi_gpio_get_direction,
> +	.get = sunxi_gpio_get,
> +	.set = sunxi_gpio_set,
> +	.of_xlate = sunxi_gpio_of_xlate,
> +};
> +
> +int sunxi_pinctrl_probe(struct device *dev)
> +{
> +	const struct sunxi_pinctrl_desc *desc;
> +	struct sunxi_pinctrl *pinctrl;
> +	struct resource *iores;
> +	int ret;
> +
> +	if (!IS_ENABLED(CONFIG_PINCTRL))
> +		return 0;
> +
> +	desc = device_get_match_data(dev);
> +	if (!desc)
> +                return -EINVAL;
> +
> +	iores = dev_request_mem_resource(dev, 0);
> +	if (IS_ERR(iores))
> +		return PTR_ERR(iores);
> +
> +	pinctrl = xzalloc(sizeof(*pinctrl));
> +	dev->priv = pinctrl;
> +	pinctrl->base = IOMEM(iores->start);
> +
> +	pinctrl->desc = desc;
> +	pinctrl->pdev.dev = dev;
> +	pinctrl->pdev.ops = &sunxi_pinctrl_ops;
> +
> +	ret = pinctrl_register(&pinctrl->pdev);
> +	if (ret) {
> +		dev_err(dev, "couldn't register %s driver\n", "pinctrl");
> +		goto err;
> +	}
> +	dev_dbg(dev, "sunxi %s registered\n", "pinctrl");
> +
> +	pinctrl->chip.dev = dev;
> +	pinctrl->chip.ops = &sunxi_gpio_ops;
> +	/* only the first 8 bank are supported */
> +	pinctrl->chip.base = 0;
> +	pinctrl->chip.ngpio = 8 * PINS_PER_BANK;
> +
> +	if (of_property_read_bool(dev->of_node, "gpio-controller")) {
> +		ret = gpiochip_add(&pinctrl->chip);
> +		if (ret) {
> +			dev_err(dev, "couldn't register %s driver\n", "gpio-chip");
> +			goto pinctrl_unregister;
> +		}
> +		dev_dbg(dev, "sunxi %s registered\n", "gpio-chip");
> +	}
> +	return 0;
> +
> +pinctrl_unregister:
> +	pinctrl_unregister(&pinctrl->pdev);
> +err:
> +	release_region(iores);
> +	free(pinctrl);
> +	return ret;
> +}
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
> new file mode 100644
> index 0000000000..630f1ef98e
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
> @@ -0,0 +1,224 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Allwinner A1X SoCs pinctrl driver.
> + *
> + * Copyright (C) 2012 Maxime Ripard
> + *
> + * Maxime Ripard <maxime.ripard@free-electrons.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2.  This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#ifndef __PINCTRL_SUNXI_H
> +#define __PINCTRL_SUNXI_H
> +
> +#include <pinctrl.h>
> +#include <gpio.h>
> +
> +#define PA_BASE	0
> +#define PB_BASE	32
> +#define PC_BASE	64
> +#define PD_BASE	96
> +#define PE_BASE	128
> +#define PF_BASE	160
> +#define PG_BASE	192
> +#define PH_BASE	224
> +#define PI_BASE	256
> +#define PL_BASE	352
> +#define PM_BASE	384
> +#define PN_BASE	416
> +
> +#define SUNXI_PIN_NAME_MAX_LEN	5
> +
> +#define BANK_MEM_SIZE		0x24
> +#define MUX_REGS_OFFSET		0x0
> +#define DATA_REGS_OFFSET	0x10
> +#define DLEVEL_REGS_OFFSET	0x14
> +#define PULL_REGS_OFFSET	0x1c
> +
> +#define PINS_PER_BANK		32
> +#define MUX_PINS_PER_REG	8
> +#define MUX_PINS_BITS		4
> +#define MUX_PINS_MASK		0x0f
> +#define DATA_PINS_PER_REG	32
> +#define DATA_PINS_BITS		1
> +#define DATA_PINS_MASK		0x01
> +#define DLEVEL_PINS_PER_REG	16
> +#define DLEVEL_PINS_BITS	2
> +#define DLEVEL_PINS_MASK	0x03
> +#define PULL_PINS_PER_REG	16
> +#define PULL_PINS_BITS		2
> +#define PULL_PINS_MASK		0x03
> +
> +#define GRP_CFG_REG		0x300
> +
> +#define IO_BIAS_MASK		GENMASK(3, 0)
> +
> +#define SUN4I_FUNC_INPUT	0
> +#define SUN4I_FUNC_IRQ		6
> +
> +#define PINCTRL_SUN5I_A10S	BIT(1)
> +#define PINCTRL_SUN5I_A13	BIT(2)
> +#define PINCTRL_SUN5I_GR8	BIT(3)
> +#define PINCTRL_SUN6I_A31	BIT(4)
> +#define PINCTRL_SUN6I_A31S	BIT(5)
> +#define PINCTRL_SUN4I_A10	BIT(6)
> +#define PINCTRL_SUN7I_A20	BIT(7)
> +#define PINCTRL_SUN8I_R40	BIT(8)
> +#define PINCTRL_SUN8I_V3	BIT(9)
> +#define PINCTRL_SUN8I_V3S	BIT(10)
> +
> +#define PIO_POW_MOD_SEL_REG	0x340
> +
> +enum sunxi_desc_bias_voltage {
> +	BIAS_VOLTAGE_NONE,
> +	/*
> +	 * Bias voltage configuration is done through
> +	 * Pn_GRP_CONFIG registers, as seen on A80 SoC.
> +	 */
> +	BIAS_VOLTAGE_GRP_CONFIG,
> +	/*
> +	 * Bias voltage is set through PIO_POW_MOD_SEL_REG
> +	 * register, as seen on H6 SoC, for example.
> +	 */
> +	BIAS_VOLTAGE_PIO_POW_MODE_SEL,
> +};
> +
> +struct sunxi_desc_function {
> +	const char	*name;
> +	u8		muxval;
> +};
> +
> +struct sunxi_desc_pin {
> +	struct {
> +		const char		name[6];
> +		u16			number;
> +	} pin;
> +	const struct sunxi_desc_function	*functions;
> +};
> +
> +struct sunxi_pinctrl_desc {
> +	const struct sunxi_desc_pin	*pins;
> +	size_t				npins;
> +	unsigned			pin_base;
> +	bool				disable_strict_mode;
> +	enum sunxi_desc_bias_voltage	io_bias_cfg_variant;
> +};
> +
> +struct sunxi_pinctrl {
> +	void __iomem			*base;
> +	struct gpio_chip		chip;
> +	struct pinctrl_device		pdev;
> +	const struct sunxi_pinctrl_desc	*desc;
> +};
> +
> +#define SUNXI_PIN(_pin, ...)					\
> +	{							\
> +		.pin = _pin,					\
> +		.functions = (struct sunxi_desc_function[]){	\
> +			__VA_ARGS__, { } },			\
> +	}
> +
> +#define SUNXI_PINCTRL_PIN(bank, pin)				\
> +	{							\
> +		.name = "P" #bank #pin,				\
> +		.number = P ## bank ## _BASE + (pin)		\
> +	}
> +
> +#define SUNXI_FUNCTION(_val, _name)				\
> +	{							\
> +		.name = _name,					\
> +		.muxval = _val,					\
> +	}
> +
> +#define SUNXI_FUNCTION_IRQ_BANK(...)  {}
> +
> +/*
> + * The sunXi PIO registers are organized as is:
> + * 0x00 - 0x0c	Muxing values.
> + *		8 pins per register, each pin having a 4bits value
> + * 0x10		Pin values
> + *		32 bits per register, each pin corresponding to one bit
> + * 0x14 - 0x18	Drive level
> + *		16 pins per register, each pin having a 2bits value
> + * 0x1c - 0x20	Pull-Up values
> + *		16 pins per register, each pin having a 2bits value
> + *
> + * This is for the first bank. Each bank will have the same layout,
> + * with an offset being a multiple of 0x24.
> + *
> + * The following functions calculate from the pin number the register
> + * and the bit offset that we should access.
> + */
> +static inline u32 sunxi_mux_reg(u16 pin)
> +{
> +	u8 bank = pin / PINS_PER_BANK;
> +	u32 offset = bank * BANK_MEM_SIZE;
> +	offset += MUX_REGS_OFFSET;
> +	offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04;
> +	return round_down(offset, 4);
> +}
> +
> +static inline u32 sunxi_mux_offset(u16 pin)
> +{
> +	u32 pin_num = pin % MUX_PINS_PER_REG;
> +	return pin_num * MUX_PINS_BITS;
> +}
> +
> +static inline u32 sunxi_data_reg(u16 pin)
> +{
> +	u8 bank = pin / PINS_PER_BANK;
> +	u32 offset = bank * BANK_MEM_SIZE;
> +	offset += DATA_REGS_OFFSET;
> +	offset += pin % PINS_PER_BANK / DATA_PINS_PER_REG * 0x04;
> +	return round_down(offset, 4);
> +}
> +
> +static inline u32 sunxi_data_offset(u16 pin)
> +{
> +	u32 pin_num = pin % DATA_PINS_PER_REG;
> +	return pin_num * DATA_PINS_BITS;
> +}
> +
> +static inline u32 sunxi_dlevel_reg(u16 pin)
> +{
> +	u8 bank = pin / PINS_PER_BANK;
> +	u32 offset = bank * BANK_MEM_SIZE;
> +	offset += DLEVEL_REGS_OFFSET;
> +	offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04;
> +	return round_down(offset, 4);
> +}
> +
> +static inline u32 sunxi_dlevel_offset(u16 pin)
> +{
> +	u32 pin_num = pin % DLEVEL_PINS_PER_REG;
> +	return pin_num * DLEVEL_PINS_BITS;
> +}
> +
> +static inline u32 sunxi_pull_reg(u16 pin)
> +{
> +	u8 bank = pin / PINS_PER_BANK;
> +	u32 offset = bank * BANK_MEM_SIZE;
> +	offset += PULL_REGS_OFFSET;
> +	offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04;
> +	return round_down(offset, 4);
> +}
> +
> +static inline u32 sunxi_pull_offset(u16 pin)
> +{
> +	u32 pin_num = pin % PULL_PINS_PER_REG;
> +	return pin_num * PULL_PINS_BITS;
> +}
> +
> +static inline u32 sunxi_grp_config_reg(u16 pin)
> +{
> +	u8 bank = pin / PINS_PER_BANK;
> +
> +	return GRP_CFG_REG + bank * 0x4;
> +}
> +
> +int sunxi_pinctrl_probe(struct device *dev);
> +
> +#endif /* __PINCTRL_SUNXI_H */

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 08/11] mci: Add sunxi-mmc driver
  2023-05-10 23:37 ` [RFC PATCH 08/11] mci: Add sunxi-mmc driver Jules Maselbas
@ 2023-05-18 19:26   ` Ahmad Fatoum
  2023-05-19  5:51     ` Sascha Hauer
  0 siblings, 1 reply; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 19:26 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> This driver is adapted from different sources: Linux, u-boot and p-boot.
> The latter, p-boot (forked from u-boot), is a bootloader for pinephones.
> 
> It currently only support PIO xfer but could be further improved to also
> support DMA xfer. This driver is split in three source file so it can be
> used by PBL and barebox proper.
> ---
>  drivers/mci/Kconfig            |   6 +
>  drivers/mci/Makefile           |   2 +
>  drivers/mci/sunxi-mmc-common.c | 259 +++++++++++++++++++++++++++++++++
>  drivers/mci/sunxi-mmc-pbl.c    |  81 +++++++++++
>  drivers/mci/sunxi-mmc.c        | 173 ++++++++++++++++++++++
>  drivers/mci/sunxi-mmc.h        | 225 ++++++++++++++++++++++++++++
>  include/mach/sunxi/xload.h     |  12 ++
>  7 files changed, 758 insertions(+)
>  create mode 100644 drivers/mci/sunxi-mmc-common.c
>  create mode 100644 drivers/mci/sunxi-mmc-pbl.c
>  create mode 100644 drivers/mci/sunxi-mmc.c
>  create mode 100644 drivers/mci/sunxi-mmc.h
>  create mode 100644 include/mach/sunxi/xload.h
> 
> diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
> index bbdca67e9d..c1903b6c90 100644
> --- a/drivers/mci/Kconfig
> +++ b/drivers/mci/Kconfig
> @@ -72,6 +72,12 @@ config MCI_DW_PIO
>  	help
>  	  Use PIO mode (instead of IDMAC) in DW MMC driver.
>  
> +config MCI_SUNXI_SMHC
> +	bool "Allwinner SD-MMC Memory Card Host Controller"
> +	help
> +	  Enable support for the Allwinner SD-MMC Memory Card Host Controller,
> +	  his provides host support for SD and MMC interfaces, in PIO mode.

This

> +
>  config MCI_MXS
>  	bool "i.MX23/i.MX28"
>  	depends on ARCH_MXS
> diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
> index e3dc5ad8ae..c17cd41db1 100644
> --- a/drivers/mci/Makefile
> +++ b/drivers/mci/Makefile
> @@ -20,4 +20,6 @@ obj-$(CONFIG_MCI_SPI)		+= mci_spi.o
>  obj-$(CONFIG_MCI_DW)		+= dw_mmc.o
>  obj-$(CONFIG_MCI_MMCI)		+= mmci.o
>  obj-$(CONFIG_MCI_STM32_SDMMC2)	+= stm32_sdmmc2.o
> +obj-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc.o
> +pbl-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc-pbl.o
>  obj-pbl-$(CONFIG_MCI_SDHCI)	+= sdhci.o
> diff --git a/drivers/mci/sunxi-mmc-common.c b/drivers/mci/sunxi-mmc-common.c
> new file mode 100644
> index 0000000000..845078805b
> --- /dev/null
> +++ b/drivers/mci/sunxi-mmc-common.c
> @@ -0,0 +1,259 @@
> +#include "sunxi-mmc.h"
> +
> +static int sdxc_read_data_pio(struct sunxi_mmc_host *host, struct mci_data *data);
> +static int sdxc_write_data_pio(struct sunxi_mmc_host *host, struct mci_data *data);
> +static int sunxi_mmc_send_cmd(struct sunxi_mmc_host *host, struct mci_cmd *cmd, struct mci_data *data, const char **why);
> +static int sunxi_mmc_set_ios(struct sunxi_mmc_host *host, struct mci_ios *ios);
> +static void sunxi_mmc_init(struct sunxi_mmc_host *host);
> +
> +static int sdxc_read_data_pio(struct sunxi_mmc_host *host, struct mci_data *data)
> +{
> +	size_t i, len = data->blocks * data->blocksize;
> +	u8 *dst = data->dest;
> +	u32 reg;
> +
> +	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_ACCESS_BY_AHB);
> +
> +	for (i = 0; i < len; i += 4) {
> +		if (wait_on_timeout(2000 * MSECOND, !sdxc_is_fifo_empty(host)))

This can add up to 8 seconds. Just use is_timeout directly?

> +			return -ETIMEDOUT;
> +		reg = sdxc_readl(host, SDXC_REG_FIFO);
> +		memcpy(dst + i, &reg, sizeof(reg));
> +	}
> +
> +	return i;
> +}
> +
> +static int sdxc_write_data_pio(struct sunxi_mmc_host *host, struct mci_data *data)
> +{
> +	size_t i, len = data->blocks * data->blocksize;
> +	u32 *pdata = (u32 *)data->src;
> +
> +	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_ACCESS_BY_AHB);
> +
> +	for (i = 0; i < len; i += 4, pdata++) {
> +		if (wait_on_timeout(2000 * MSECOND, !sdxc_is_fifo_full(host)))
> +			return -ETIMEDOUT;
> +		sdxc_writel(host, SDXC_REG_FIFO, *pdata);
> +	}
> +#if 0
> +	sdxc_writel(host, SDXC_RINTR, SDXC_INTMSK_TXDR);
> +
> +	if (wait_on_timeout(2000 * MSECOND, sdxc_is_fifo_empty(host))) {
> +		return -EIO;
> +	}
> +#endif

Left-over?

> +	return i;
> +}
> +
> +static int sunxi_mmc_send_cmd(struct sunxi_mmc_host *host, struct mci_cmd *cmd,
> +			      struct mci_data *data, const char **why)
> +{
> +	const char *err_why = "";
> +	u32 cmdval = SDXC_CMD_START;
> +	int ret;
> +
> +	if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
> +		return -EINVAL;
> +
> +	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
> +		return 0; /* using ACMD12 */
> +	if (cmd->cmdidx == MMC_CMD_GO_IDLE_STATE)
> +		cmdval |= SDXC_CMD_SEND_INIT_SEQ;
> +
> +	if (cmd->resp_type & MMC_RSP_PRESENT)
> +		cmdval |= SDXC_CMD_RESP_EXPIRE;
> +	if (cmd->resp_type & MMC_RSP_136)
> +		cmdval |= SDXC_CMD_LONG_RESPONSE;
> +	if (cmd->resp_type & MMC_RSP_CRC)
> +		cmdval |= SDXC_CMD_CHK_RESPONSE_CRC;
> +
> +	/* clear interrupts */
> +	sdxc_writel(host, SDXC_REG_RINTR, 0xffffffff);
> +
> +	if (data) {
> +		u32 blksiz = data->blocksize;
> +		u32 bytcnt = data->blocks * data->blocksize;
> +
> +		cmdval |= SDXC_CMD_DATA_EXPIRE;
> +		cmdval |= SDXC_CMD_WAIT_PRE_OVER;
> +		if (data->flags & MMC_DATA_WRITE)
> +			cmdval |= SDXC_CMD_WRITE;
> +		if (data->blocks > 1)
> +			cmdval |= SDXC_CMD_AUTO_STOP;
> +
> +		sdxc_writel(host, SDXC_REG_TMOUT, 0xFFFFFF40);
> +		sdxc_writel(host, SDXC_REG_BLKSZ, blksiz);
> +		sdxc_writel(host, SDXC_REG_BCNTR, bytcnt);
> +	}
> +
> +	sdxc_writel(host, SDXC_REG_CARG, cmd->cmdarg);
> +	sdxc_writel(host, SDXC_REG_CMDR, cmdval | cmd->cmdidx);
> +	if (data) {
> +		if (data->flags & MMC_DATA_WRITE)
> +			ret = sdxc_write_data_pio(host, data);
> +		else
> +			ret = sdxc_read_data_pio(host, data);
> +		if (ret < 0) {
> +			err_why = "pio error";
> +			goto err;
> +		}
> +	}
> +
> +	ret = sdxc_xfer_complete(host, 1000 * MSECOND, SDXC_INT_COMMAND_DONE);
> +	if (ret) {
> +		err_why = "cmd timeout";
> +		goto err;
> +	}
> +
> +	if (data) {
> +		ret = sdxc_xfer_complete(host, 1000 * MSECOND, data->blocks > 1 ?
> +					 SDXC_INT_AUTO_COMMAND_DONE :
> +					 SDXC_INT_DATA_OVER);
> +		if (ret) {
> +			err_why = "data timeout";
> +			goto err;
> +		}
> +	}
> +
> +	if (cmd->resp_type & MMC_RSP_BUSY) {
> +		u32 status;
> +		u64 start;
> +		start = get_time_ns();
> +		do {
> +			status = sdxc_readl(host, SDXC_REG_STAS);
> +			if (is_timeout(start, 2000 * MSECOND)) {
> +				err_why = "resp timeout";
> +				ret = -ETIMEDOUT;
> +				goto err;
> +			}
> +		} while (status & SDXC_STATUS_BUSY);
> +	}
> +
> +	if (wait_on_timeout(1000 * MSECOND, !sdxc_is_card_busy(host))) {
> +		err_why = "card busy timeout";
> +		ret = -ETIMEDOUT;
> +		goto err;
> +	}
> +
> +	if (cmd->resp_type & MMC_RSP_136) {
> +		cmd->response[0] = sdxc_readl(host, SDXC_REG_RESP3);
> +		cmd->response[1] = sdxc_readl(host, SDXC_REG_RESP2);
> +		cmd->response[2] = sdxc_readl(host, SDXC_REG_RESP1);
> +		cmd->response[3] = sdxc_readl(host, SDXC_REG_RESP0);
> +	} else if (cmd->resp_type & MMC_RSP_PRESENT) {
> +		cmd->response[0] = sdxc_readl(host, SDXC_REG_RESP0);
> +	}
> +
> +err:
> +	if (why)
> +		*why = err_why;
> +	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_FIFO_RESET);
> +	return ret;
> +}
> +
> +static int sunxi_mmc_update_clk(struct sunxi_mmc_host *host)
> +{
> +	u32 cmdval;
> +
> +	cmdval = SDXC_CMD_START |
> +	         SDXC_CMD_UPCLK_ONLY |
> +	         SDXC_CMD_WAIT_PRE_OVER;
> +
> +	sdxc_writel(host, SDXC_REG_CARG, 0);
> +	sdxc_writel(host, SDXC_REG_CMDR, cmdval);
> +
> +	if (wait_on_timeout(2000 * MSECOND, !(sdxc_readl(host, SDXC_REG_CMDR) & SDXC_CMD_START)))
> +		return -ETIMEDOUT;
> +
> +	return 0;
> +}
> +
> +static int sunxi_mmc_setup_clk(struct sunxi_mmc_host *host, u32 freq)
> +{
> +	u32 val, div, sclk;
> +	int ret;
> +
> +	sclk = host->clkrate;
> +	if (sclk == 0)
> +		return -EINVAL;
> +
> +	sclk /= 2; // WHY ????
> +
> +	/* disable card clock */
> +	val = sdxc_readl(host, SDXC_REG_CLKCR);
> +	val &= ~(SDXC_CLK_ENABLE | SDXC_CLK_LOW_POWER_ON);
> +	val |= SDXC_CLK_MASK_DATA0;
> +	sdxc_writel(host, SDXC_REG_CLKCR, val);
> +
> +	ret = sunxi_mmc_update_clk(host);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Configure the controller to use the new timing mode if needed.
> +	 * On controllers that only support the new timing mode, such as
> +	 * the eMMC controller on the A64, this register does not exist,
> +	 * and any writes to it are ignored.
> +	 */
> +	if (host->cfg->needs_new_timings) {
> +		/* Don't touch the delay bits */
> +		val = sdxc_readl(host, SDXC_REG_NTSR);
> +		val |= SDXC_NTSR_2X_TIMING_MODE;
> +		sdxc_writel(host, SDXC_REG_NTSR, val);
> +	}
> +
> +	/* setup clock rate */
> +	div = DIV_ROUND_UP(sclk, freq);
> +	if (div > 510)
> +		div = 510;
> +
> +	/* set internal divider */
> +	val = sdxc_readl(host, SDXC_REG_CLKCR);
> +	val &= ~SDXC_CLK_DIVIDER_MASK;
> +	val |= div / 2; /* divisor is multiply by 2 */
> +	sdxc_writel(host, SDXC_REG_CLKCR, val);
> +
> +	/* enable card clock */
> +	val = sdxc_readl(host, SDXC_REG_CLKCR);
> +	val |= SDXC_CLK_ENABLE;
> +	val &= ~SDXC_CLK_MASK_DATA0;
> +	sdxc_writel(host, SDXC_REG_CLKCR, val);
> +
> +	return sunxi_mmc_update_clk(host);
> +}
> +
> +static int sunxi_mmc_set_ios(struct sunxi_mmc_host *host, struct mci_ios *ios)
> +{
> +	int ret = 0;
> +	u32 width;
> +
> +	switch (ios->bus_width) {
> +	case MMC_BUS_WIDTH_8:
> +		width = SDXC_WIDTH_8BIT;
> +		break;
> +	case MMC_BUS_WIDTH_4:
> +		width = SDXC_WIDTH_4BIT;
> +		break;
> +	default:
> +		width = SDXC_WIDTH_1BIT;
> +		break;
> +	}
> +	sdxc_writel(host, SDXC_REG_WIDTH, width);
> +
> +	if (ios->clock)
> +		ret = sunxi_mmc_setup_clk(host, ios->clock);
> +	return ret;
> +}
> +
> +static void sunxi_mmc_init(struct sunxi_mmc_host *host)
> +{
> +	/* Reset controller */
> +	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_RESET);
> +	udelay(1000);
> +
> +	sdxc_writel(host, SDXC_REG_RINTR, 0xffffffff);
> +	sdxc_writel(host, SDXC_REG_IMASK, 0);
> +
> +	sdxc_writel(host, SDXC_REG_TMOUT, 0xffffff40);
> +}
> diff --git a/drivers/mci/sunxi-mmc-pbl.c b/drivers/mci/sunxi-mmc-pbl.c
> new file mode 100644
> index 0000000000..f9141a599e
> --- /dev/null
> +++ b/drivers/mci/sunxi-mmc-pbl.c
> @@ -0,0 +1,81 @@
> +#include <common.h>
> +
> +#include <mach/sunxi/xload.h>
> +#include "sunxi-mmc.h"
> +#include "sunxi-mmc-common.c"
> +
> +#define SECTOR_SIZE 512
> +
> +static int sunxi_mmc_read_block(struct sunxi_mmc_host *host,
> +				void *dst, unsigned int blocknum,
> +				unsigned int blocks)
> +{
> +	struct mci_data data;
> +	struct mci_cmd cmd = {
> +		.cmdidx = (blocks > 1) ? MMC_CMD_READ_MULTIPLE_BLOCK : MMC_CMD_READ_SINGLE_BLOCK,
> +		 /* mci->high_capacity ? blocknum : blocknum * mci->read_bl_len, */
> +		 /* TODO: figured out how to detect if card is high-capacity */

Quoting from a conversation between me and Yann in IRC:

"I looked into Part1_Physical_Layer_Simplified_Specification_Ver9.00.pdf again
 and for the first time I see "Figure 4-13 : SD Memory Card State Diagram
 (data transfer mode)".

 Apparently,  you don't necessarily need CMD0 (Go idle) to be able to get to
 a state where you can execute CMD9 (SEND CSD), but one could also execute CMD7
 (SELECT CARD) to deselect card and then run CMD9 and get back to transmission
 mode. One would need to issue a CMD3 (SEND_RELATIVE_ADDR) to get the RCA to
 communicate with the correct SD (it's argument to CMD9), but this might work."

I intend to look into it, but it might be some weeks/months before I get to it.

> +//		.cmdarg = blocknum * SECTOR_SIZE,

left-over?

> +		.cmdarg = blocknum,
> +		.resp_type = MMC_RSP_R1,
> +	};
> +	int ret;
> +
> +	data.dest = dst;
> +	data.blocks = blocks;
> +	data.blocksize = SECTOR_SIZE; /* compat with MMC/SD */
> +	data.flags = MMC_DATA_READ;
> +
> +	ret = sunxi_mmc_send_cmd(host, &cmd, &data, NULL);
> +
> +	if (ret || blocks > 1) {
> +		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
> +		cmd.cmdarg = 0;
> +		cmd.resp_type = MMC_RSP_R1b;
> +		sunxi_mmc_send_cmd(host, &cmd, NULL, NULL);
> +	}
> +
> +	return ret;
> +}
> +
> +static int sunxi_mmc_bio_read(struct pbl_bio *bio, off_t start,
> +				void *buf, unsigned int nblocks)
> +{
> +	struct sunxi_mmc_host *host = bio->priv;
> +	unsigned int count = 0;
> +	unsigned int block_len = SECTOR_SIZE;
> +	int ret;
> +
> +	while (count < nblocks) {
> +		unsigned int n = min_t(unsigned int, nblocks - count, 16 /* SUPPORT_MAX_BLOCKS */);
> +
> +		ret = sunxi_mmc_read_block(host, buf, start, n);
> +		if (ret < 0)
> +			return ret;
> +
> +		count += n;
> +		start += n;
> +		buf += n * block_len;
> +	}
> +
> +	return count;
> +}
> +
> +static struct sunxi_mmc_host pouet;
> +
> +int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
> +		       unsigned int clock, unsigned int slot)
> +{
> +	struct sunxi_mmc_host *host = &pouet;
> +	struct mci_ios ios = { .bus_width = MMC_BUS_WIDTH_4, .clock = 400000 };
> +
> +	host->base = base;
> +	host->clkrate = clock;
> +	bio->priv = host;
> +	bio->read = sunxi_mmc_bio_read;
> +
> +	sunxi_mmc_init(host);
> +	sunxi_mmc_set_ios(host, &ios);
> +
> +	return 0;
> +}
> diff --git a/drivers/mci/sunxi-mmc.c b/drivers/mci/sunxi-mmc.c
> new file mode 100644
> index 0000000000..a537ea1a55
> --- /dev/null
> +++ b/drivers/mci/sunxi-mmc.c
> @@ -0,0 +1,173 @@
> +//#define DEBUG
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +// derived from: linux/drivers/mmc/host/sunxi-mmc.c
> +
> +#define pr_fmt(fmt) "sunxi-mmc: " fmt
> +
> +#include <common.h>
> +#include <driver.h>
> +#include <malloc.h>
> +#include <init.h>
> +#include <mci.h>
> +
> +#include <gpio.h>
> +#include <of_gpio.h>
> +#include <linux/reset.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <errno.h>
> +#include <dma.h>
> +
> +#include "sunxi-mmc.h"
> +#include "sunxi-mmc-common.c"
> +
> +static int sdxc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
> +{
> +	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
> +	struct device *dev = mci->hw_dev;
> +	const char *why;
> +	int ret;
> +
> +	ret = sunxi_mmc_send_cmd(host, cmd, data, &why);
> +	if (ret)
> +		dev_err(dev, "error %s CMD%d (%d)\n", why, cmd->cmdidx, ret);
> +
> +	return ret;
> +}
> +
> +static void sdxc_set_ios(struct mci_host *mci, struct mci_ios *ios)
> +{
> +	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
> +	struct device *dev = mci->hw_dev;
> +
> +	dev_dbg(dev, "buswidth = %d, clock: %d\n", ios->bus_width, ios->clock);
> +	sunxi_mmc_set_ios(host, ios);
> +}
> +
> +static int sdxc_card_present(struct mci_host *mci)
> +{
> +	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
> +	struct device *dev = mci->hw_dev;
> +	int ret;
> +
> +	/* No gpio, assume card is present */
> +	if (!gpio_is_valid(host->gpio_cd)) {
> +		dev_err(dev, "%s gpio not valid\n", __func__);
> +		return 1;
> +	}
> +
> +	ret = gpio_get_value(host->gpio_cd);
> +	dev_dbg(dev, "%s gpio: %d\n", __func__, ret);
> +
> +	return ret == 0 ? 1 : 0;
> +}
> +
> +static int sdxc_init(struct mci_host *mci, struct device *dev)
> +{
> +	struct sunxi_mmc_host *host = to_sunxi_mmc_host(mci);
> +
> +	sunxi_mmc_init(host);
> +
> +	return 0;
> +}
> +
> +static int sunxi_mmc_probe(struct device *dev)
> +{
> +	struct device_node *np = dev->of_node;
> +	struct resource *iores;
> +	struct sunxi_mmc_host *host;
> +	unsigned int f_min, f_max;
> +	int ret;
> +
> +	iores = dev_request_mem_resource(dev, 0);
> +	if (IS_ERR(iores))
> +		return PTR_ERR(iores);
> +	host = xzalloc(sizeof(*host));
> +	host->base = IOMEM(iores->start);
> +	dma_set_mask(dev, DMA_BIT_MASK(32));
> +	host->cfg = device_get_match_data(dev);
> +
> +	host->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
> +
> +	host->clk_ahb = clk_get(dev, "ahb");
> +	if (IS_ERR(host->clk_ahb)) {
> +		ret = PTR_ERR(host->clk_ahb);
> +		goto err;
> +	}
> +
> +	host->clk_mmc = clk_get(dev, "mmc");
> +	if (IS_ERR(host->clk_mmc)) {
> +		ret = PTR_ERR(host->clk_mmc);
> +		goto err;
> +	}
> +
> +	clk_enable(host->clk_ahb);
> +	clk_enable(host->clk_mmc);
> +
> +	host->mci.hw_dev = dev;
> +	host->mci.send_cmd = sdxc_send_cmd;
> +	host->mci.set_ios = sdxc_set_ios;
> +	host->mci.init = sdxc_init;
> +	host->mci.card_present = sdxc_card_present;
> +	host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
> +	host->mci.host_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA
> +		| MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED
> +		| MMC_CAP_MMC_HIGHSPEED_52MHZ;
> +
> +	host->clkrate = clk_get_rate(host->clk_mmc);
> +	f_min = host->clkrate / 510;
> +	f_max = host->clkrate;
> +	/* clock must at least support freq as low as 400K, and reach 52M */
> +	if (400000 < f_min || f_max < 52000000) {
> +		/* if not, try to get a better clock */
> +		clk_set_rate(host->clk_mmc, clk_round_rate(host->clk_mmc, 52000000));
> +		host->clkrate = clk_get_rate(host->clk_mmc);
> +		f_min = host->clkrate / 510;
> +		f_max = host->clkrate;
> +	}
> +	dev_dbg(dev, "freq: min %d max %d\n", f_min, f_max);
> +	mci_of_parse(&host->mci);
> +
> +	f_min = min_t(unsigned int, 400000, f_min);

Nit: Alternatively, min(400000u, f_min)

> +	f_max = min_t(unsigned int, 52000000, f_max);
> +	host->mci.f_min = max_t(unsigned int, host->mci.f_min, f_min);
> +	host->mci.f_max = min_t(unsigned int, host->mci.f_max, f_max);
> +
> +	return mci_register(&host->mci);
> +err:
> +	if (host->clk_mmc)
> +		clk_put(host->clk_mmc);
> +	if (host->clk_ahb)
> +		clk_put(host->clk_ahb);
> +	free(host);
> +	release_region(iores);
> +	return ret;
> +}
> +
> +static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
> +	.idma_des_size_bits = 16,
> +	.clk_delays = NULL,
> +	.can_calibrate = true,
> +	.mask_data0 = true,
> +	.needs_new_timings = true,
> +};
> +
> +static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
> +	.idma_des_size_bits = 13,
> +	.clk_delays = NULL,
> +	.can_calibrate = true,
> +	.needs_new_timings = true,
> +};
> +
> +static __maybe_unused struct of_device_id sunxi_mmc_compatible[] = {
> +	{ .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
> +	{ .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
> +	{ /* sentinel */ }
> +};
> +
> +static struct driver sunxi_mmc_driver = {
> +	.name  = "sunxi-mmc",
> +	.probe = sunxi_mmc_probe,
> +	.of_compatible = DRV_OF_COMPAT(sunxi_mmc_compatible),
> +};
> +device_platform_driver(sunxi_mmc_driver);
> diff --git a/drivers/mci/sunxi-mmc.h b/drivers/mci/sunxi-mmc.h
> new file mode 100644
> index 0000000000..c8113f606a
> --- /dev/null
> +++ b/drivers/mci/sunxi-mmc.h
> @@ -0,0 +1,225 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/* SPDX-FileCopyrightText: 2023 Jules Maselbas  */
> +/* derived from: linux/drivers/mmc/host/sunxi-mmc.c */
> +
> +#ifndef SUNXI_MMC_H
> +#define	SUNXI_MMC_H
> +
> +#include <io.h>
> +#include <linux/bitops.h>
> +#include <clock.h>
> +#include <mci.h>
> +
> +#define SDXC_REG_GCTRL	(0x00) /* Global Control */
> +#define SDXC_REG_CLKCR	(0x04) /* Clock Control */
> +#define SDXC_REG_TMOUT	(0x08) /* Time Out */
> +#define SDXC_REG_WIDTH	(0x0C) /* Bus Width */
> +#define SDXC_REG_BLKSZ	(0x10) /* Block Size */
> +#define SDXC_REG_BCNTR	(0x14) /* Byte Count */
> +#define SDXC_REG_CMDR	(0x18) /* Command */
> +#define SDXC_REG_CARG	(0x1C) /* Argument */
> +#define SDXC_REG_RESP0	(0x20) /* Response 0 */
> +#define SDXC_REG_RESP1	(0x24) /* Response 1 */
> +#define SDXC_REG_RESP2	(0x28) /* Response 2 */
> +#define SDXC_REG_RESP3	(0x2C) /* Response 3 */
> +#define SDXC_REG_IMASK	(0x30) /* Interrupt Mask */
> +#define SDXC_REG_MISTA	(0x34) /* Masked Interrupt Status */
> +#define SDXC_REG_RINTR	(0x38) /* Raw Interrupt Status */
> +#define SDXC_REG_STAS	(0x3C) /* Status */
> +#define SDXC_REG_FTRGL	(0x40) /* FIFO Threshold Watermark */
> +#define SDXC_REG_FUNS	(0x44) /* Function Select */
> +#define SDXC_REG_CBCR	(0x48) /* CIU Byte Count */
> +#define SDXC_REG_BBCR	(0x4C) /* BIU Byte Count */
> +#define SDXC_REG_DBGC	(0x50) /* Debug Enable */
> +#define SDXC_REG_A12A	(0x58) /* Auto Command 12 Argument */
> +#define SDXC_REG_NTSR	(0x5C) /* SMC New Timing Set Register */
> +#define SDXC_REG_HWRST	(0x78) /* Card Hardware Reset */
> +#define SDXC_REG_DMAC	(0x80) /* IDMAC Control */
> +#define SDXC_REG_DLBA	(0x84) /* IDMAC Descriptor List Base Addresse */
> +#define SDXC_REG_IDST	(0x88) /* IDMAC Status */
> +#define SDXC_REG_IDIE	(0x8C) /* IDMAC Interrupt Enable */
> +#define SDXC_REG_CHDA	(0x90)
> +#define SDXC_REG_CBDA	(0x94)
> +
> +#define SDXC_REG_DRV_DL		0x140 /* Drive Delay Control Register */
> +#define SDXC_REG_SAMP_DL_REG	0x144 /* SMC sample delay control */
> +#define SDXC_REG_DS_DL_REG	0x148 /* SMC data strobe delay control */
> +
> +#define SDXC_REG_FIFO	(0x200) /* FIFO */
> +
> +#define SDXC_GCTRL_SOFT_RESET		BIT(0)
> +#define SDXC_GCTRL_FIFO_RESET		BIT(1)
> +#define SDXC_GCTRL_DMA_RESET		BIT(2)
> +#define SDXC_GCTRL_RESET \
> +	(SDXC_GCTRL_SOFT_RESET | SDXC_GCTRL_FIFO_RESET | SDXC_GCTRL_DMA_RESET)
> +#define SDXC_GCTRL_DMA_ENABLE		BIT(5)
> +#define SDXC_GCTRL_ACCESS_BY_AHB	BIT(31)
> +
> +#define SDXC_CMD_RESP_EXPIRE		BIT(6)
> +#define SDXC_CMD_LONG_RESPONSE		BIT(7)
> +#define SDXC_CMD_CHK_RESPONSE_CRC	BIT(8)
> +#define SDXC_CMD_DATA_EXPIRE		BIT(9)
> +#define SDXC_CMD_WRITE			BIT(10)
> +#define SDXC_CMD_AUTO_STOP		BIT(12)
> +#define SDXC_CMD_WAIT_PRE_OVER		BIT(13)
> +#define SDXC_CMD_ABORT_STOP		BIT(14)
> +#define SDXC_CMD_SEND_INIT_SEQ		BIT(15)
> +#define SDXC_CMD_UPCLK_ONLY		BIT(21)
> +#define SDXC_CMD_START			BIT(31)
> +
> +#define SDXC_NTSR_2X_TIMING_MODE	BIT(31)
> +
> +/* clock control bits */
> +#define SDXC_CLK_MASK_DATA0	BIT(31)
> +#define SDXC_CLK_LOW_POWER_ON	BIT(17)
> +#define SDXC_CLK_ENABLE		BIT(16)
> +#define SDXC_CLK_DIVIDER_MASK	(0xff)
> +
> +/* bus width */
> +#define SDXC_WIDTH_1BIT	0
> +#define SDXC_WIDTH_4BIT	BIT(0)
> +#define SDXC_WIDTH_8BIT	BIT(1)
> +
> +/* interrupt bits */
> +#define SDXC_INT_RESP_ERROR		BIT(1)
> +#define SDXC_INT_COMMAND_DONE		BIT(2)
> +#define SDXC_INT_DATA_OVER		BIT(3)
> +#define SDXC_INT_TX_DATA_REQUEST	BIT(4)
> +#define SDXC_INT_RX_DATA_REQUEST	BIT(5)
> +#define SDXC_INT_RESP_CRC_ERROR		BIT(6)
> +#define SDXC_INT_DATA_CRC_ERROR		BIT(7)
> +#define SDXC_INT_RESP_TIMEOUT		BIT(8)
> +#define SDXC_INT_DATA_TIMEOUT		BIT(9)
> +#define SDXC_INT_VOLTAGE_CHANGE_DONE	BIT(10)
> +#define SDXC_INT_FIFO_RUN_ERROR		BIT(11)
> +#define SDXC_INT_HARD_WARE_LOCKED	BIT(12)
> +#define SDXC_INT_START_BIT_ERROR	BIT(13)
> +#define SDXC_INT_AUTO_COMMAND_DONE	BIT(14)
> +#define SDXC_INT_END_BIT_ERROR		BIT(15)
> +#define SDXC_INT_SDIO_INTERRUPT		BIT(16)
> +#define SDXC_INT_CARD_INSERT		BIT(30)
> +#define SDXC_INT_CARD_REMOVE		BIT(31)
> +//	 SDXC_INT_FIFO_RUN_ERROR  |
> +#define SDXC_INTERRUPT_ERROR_BIT	\
> +	(SDXC_INT_RESP_ERROR |		\
> +	 SDXC_INT_RESP_CRC_ERROR |	\
> +	 SDXC_INT_DATA_CRC_ERROR |	\
> +	 SDXC_INT_RESP_TIMEOUT |	\
> +	 SDXC_INT_DATA_TIMEOUT |	\
> +	 SDXC_INT_HARD_WARE_LOCKED |	\
> +	 SDXC_INT_START_BIT_ERROR |	\
> +	 SDXC_INT_END_BIT_ERROR)
> +
> +#define SDXC_INTERRUPT_DONE_BIT		\
> +	(SDXC_INT_AUTO_COMMAND_DONE |	\
> +	 SDXC_INT_DATA_OVER |		\
> +	 SDXC_INT_COMMAND_DONE |	\
> +	 SDXC_INT_VOLTAGE_CHANGE_DONE)
> +
> +/* status */
> +#define SDXC_STATUS_FIFO_EMPTY		BIT(2)
> +#define SDXC_STATUS_FIFO_FULL		BIT(3)
> +#define SDXC_STATUS_CARD_PRESENT	BIT(8)
> +#define SDXC_STATUS_BUSY		BIT(9)
> +
> +struct sunxi_mmc_clk_delay {
> +	u32 output;
> +	u32 sample;
> +};
> +
> +struct sunxi_mmc_cfg {
> +	u32 idma_des_size_bits;
> +	u32 idma_des_shift;
> +	const struct sunxi_mmc_clk_delay *clk_delays;
> +
> +	/* does the IP block support autocalibration? */
> +	bool can_calibrate;
> +
> +	/* Does DATA0 needs to be masked while the clock is updated */
> +	bool mask_data0;
> +
> +	/*
> +	 * hardware only supports new timing mode, either due to lack of
> +	 * a mode switch in the clock controller, or the mmc controller
> +	 * is permanently configured in the new timing mode, without the
> +	 * NTSR mode switch.
> +	 */
> +	bool needs_new_timings;
> +
> +	/* clock hardware can switch between old and new timing modes */
> +	bool ccu_has_timings_switch;
> +};
> +
> +struct sunxi_mmc_host {
> +	struct mci_host mci;
> +	struct device *dev;
> +	struct clk *clk_ahb, *clk_mmc;

Doesn't look like you need these beyond the probe?

> +	void __iomem *base;
> +	int gpio_cd;
> +
> +	const struct sunxi_mmc_cfg *cfg;
> +	u32 clkrate;
> +};
> +
> +static inline struct sunxi_mmc_host *to_sunxi_mmc_host(struct mci_host *mci)
> +{
> +	return container_of(mci, struct sunxi_mmc_host, mci);
> +}
> +
> +static inline u32 sdxc_readl(struct sunxi_mmc_host *host, u32 reg)
> +{
> +	return readl(host->base + reg);
> +}
> +
> +static inline void sdxc_writel(struct sunxi_mmc_host *host, u32 reg, u32 val)
> +{
> +	writel(val, host->base + reg);
> +}
> +
> +static inline int sdxc_is_fifo_empty(struct sunxi_mmc_host *host)
> +{
> +	return sdxc_readl(host, SDXC_REG_STAS) & SDXC_STATUS_FIFO_EMPTY;
> +}
> +
> +static inline int sdxc_is_fifo_full(struct sunxi_mmc_host *host)
> +{
> +	return sdxc_readl(host, SDXC_REG_STAS) & SDXC_STATUS_FIFO_FULL;
> +}
> +
> +static inline int sdxc_is_card_busy(struct sunxi_mmc_host *host)
> +{
> +	return sdxc_readl(host, SDXC_REG_STAS) & SDXC_STATUS_BUSY;
> +}
> +
> +#ifdef __PBL__
> +/*
> + * Stubs to make timeout logic below work in PBL
> + */
> +#define get_time_ns()		0
> +/*
> + * Use time in us as a busy counter timeout value
> + */
> +#define is_timeout(s, t)	((s)++ > ((t) / 1000))

These could be implemented properly for ARM64 using the architected
timer. See arch/arm/lib64/pbl.c

> +
> +#endif
> +
> +static inline int sdxc_xfer_complete(struct sunxi_mmc_host *host, u64 timeout, u32 flags)
> +{
> +	u64 start;
> +	u32 rint;
> +
> +	start = get_time_ns();
> +	do {
> +		rint = sdxc_readl(host, SDXC_REG_RINTR);
> +		if (rint & SDXC_INTERRUPT_ERROR_BIT) {
> +			break;
> +		}

Single statement clauses should have their { braces } dropped.

> +		if (rint & flags) {
> +			return 0;
> +		}
> +	} while (!is_timeout(start, timeout));
> +
> +	return -ETIMEDOUT;
> +}
> +
> +#endif
> diff --git a/include/mach/sunxi/xload.h b/include/mach/sunxi/xload.h
> new file mode 100644
> index 0000000000..f978db4951
> --- /dev/null
> +++ b/include/mach/sunxi/xload.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#ifndef __MACH_XLOAD_H
> +#define __MACH_XLOAD_H
> +
> +#include <linux/compiler.h>
> +#include <pbl/bio.h>
> +
> +int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
> +		       unsigned int clock, unsigned int slot);
> +
> +#endif

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 10/11] arm: boards: sunxi: Add initial support for the pinephone
  2023-05-10 23:37 ` [RFC PATCH 10/11] arm: boards: sunxi: Add initial support for the pinephone Jules Maselbas
@ 2023-05-18 19:39   ` Ahmad Fatoum
  0 siblings, 0 replies; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 19:39 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> ---
>  arch/arm/boards/Makefile                    |   1 +
>  arch/arm/boards/pine64-pinephone/Makefile   |   2 +
>  arch/arm/boards/pine64-pinephone/board.c    |   0
>  arch/arm/boards/pine64-pinephone/lowlevel.c | 119 ++++++++++++++++++++
>  arch/arm/configs/pinephone_defconfig        |  11 ++
>  arch/arm/dts/Makefile                       |   1 +
>  arch/arm/dts/sun50i-a64-pinephone-1_2.dts   |   3 +
>  arch/arm/mach-sunxi/Kconfig                 |  21 ++++
>  arch/arm/mach-sunxi/cpu_init.c              |  15 ++-
>  images/Makefile.sunxi                       |   9 ++
>  include/mach/sunxi/init.h                   |   4 +
>  11 files changed, 184 insertions(+), 2 deletions(-)
>  create mode 100644 arch/arm/boards/pine64-pinephone/Makefile
>  create mode 100644 arch/arm/boards/pine64-pinephone/board.c
>  create mode 100644 arch/arm/boards/pine64-pinephone/lowlevel.c
>  create mode 100644 arch/arm/configs/pinephone_defconfig
>  create mode 100644 arch/arm/dts/sun50i-a64-pinephone-1_2.dts
> 
> diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile
> index 37b1650e63..083ec2dce6 100644
> --- a/arch/arm/boards/Makefile
> +++ b/arch/arm/boards/Makefile
> @@ -96,6 +96,7 @@ obj-$(CONFIG_MACH_PHYTEC_SOM_IMX6)		+= phytec-som-imx6/
>  obj-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7)		+= phytec-phycore-imx7/
>  obj-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1)	+= phytec-phycore-stm32mp1/
>  obj-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ)		+= phytec-som-imx8mq/
> +obj-$(CONFIG_MACH_PINE64_PINEPHONE)		+= pine64-pinephone/
>  obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3)	+= plathome-openblocks-ax3/
>  obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6)	+= plathome-openblocks-a6/
>  obj-$(CONFIG_MACH_PM9261)			+= pm9261/
> diff --git a/arch/arm/boards/pine64-pinephone/Makefile b/arch/arm/boards/pine64-pinephone/Makefile
> new file mode 100644
> index 0000000000..092c31d6b2
> --- /dev/null
> +++ b/arch/arm/boards/pine64-pinephone/Makefile
> @@ -0,0 +1,2 @@
> +lwl-y += lowlevel.o
> +obj-y += board.o
> diff --git a/arch/arm/boards/pine64-pinephone/board.c b/arch/arm/boards/pine64-pinephone/board.c
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/arch/arm/boards/pine64-pinephone/lowlevel.c b/arch/arm/boards/pine64-pinephone/lowlevel.c
> new file mode 100644
> index 0000000000..8e7ba1c0a9
> --- /dev/null
> +++ b/arch/arm/boards/pine64-pinephone/lowlevel.c
> @@ -0,0 +1,119 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +#include <common.h>
> +#include <debug_ll.h>
> +#include <linux/sizes.h>
> +#include <linux/bitops.h>
> +#include <asm/barebox-arm.h>
> +#include <asm/cache.h>
> +#include <mach/sunxi/init.h>
> +#include <mach/sunxi/xload.h>
> +#include <mach/sunxi/egon.h>
> +#include <mach/sunxi/rmr_switch.h>
> +#include <mach/sunxi/sun50i-regs.h>
> +#include <mach/sunxi/sunxi-pinctrl.h>
> +
> +#define STACK_TOP   CONFIG_SUNXI_PBL_STACK_TOP
> +
> +#ifdef DEBUG
> +static void debug_led_rgb(int rgb)
> +{
> +	void __iomem *piobase = IOMEM(SUN50I_PIO_BASE_ADDR);
> +	uint32_t clr, set = 0;
> +	int r = rgb & 0xff0000;
> +	int g = rgb & 0x00ff00;
> +	int b = rgb & 0x0000ff;
> +
> +	clr = (1 << 19) | (1 << 18) | (1 << 20);
> +	set |= r ? 1 << 19 : 0;
> +	set |= g ? 1 << 18 : 0;
> +	set |= b ? 1 << 20 : 0;
> +
> +	clrsetbits_le32(piobase + PIO_PD_DATA, clr, set);
> +}
> +
> +static void debug_led_init(void)
> +{
> +	void __iomem *ccubase = IOMEM(SUN50I_CCU_BASE_ADDR);
> +	void __iomem *piobase = IOMEM(SUN50I_PIO_BASE_ADDR);
> +
> +	/* PIO clock enable */
> +	setbits_le32(ccubase + CCU_BUS_CLK_GATE2, 1u << 5);
> +	/* LED set output */
> +	clrsetbits_le32(piobase + PIO_PD_CFG2, 0x000fff00, 0x00011100);
> +}
> +#else
> +static void debug_led_rgb(int rgb) {}
> +static void debug_led_init(void) {}
> +#endif
> +
> +static void setup_uart(void)
> +{
> +	void __iomem *base = IOMEM(SUN50I_PIO_BASE_ADDR);
> +
> +	/* UART0 pinmux (PB8 + PB9) */
> +	clrsetbits_le32(base + PIO_PB_CFG1, 0x000000ff, 0x00000044);
> +
> +	debug_ll_init();
> +	putc_ll('>');
> +}
> +
> +ENTRY_FUNCTION_WITHSTACK(start_pine64_pinephone_xload, STACK_TOP, r0, r1, r2)

What's this image for? 

> +{
> +
> +	sunxi_egon_header();
> +	sunxi_switch_to_aarch64();
> +
> +	debug_led_init();
> +	debug_led_rgb(0xff0000);
> +
> +	sun50i_cpu_lowlevel_init();
> +	setup_uart();
> +	debug_led_rgb(0xffff00);
> +
> +	relocate_to_current_adr();
> +	setup_c();
> +
> +	sun50i_a64_lpddr3_dram_init();
> +
> +	debug_led_rgb(0xff0000);
> +
> +	sun50i_cpu_lowlevel_reset();
> +}
> +
> +ENTRY_FUNCTION_WITHSTACK(start_pine64_pinephone, STACK_TOP, r0, r1, r2)
> +{
> +	extern char __dtb_z_sun50i_a64_pinephone_1_2_start[];
> +	void *fdt;
> +	u32 size;
> +
> +	sunxi_switch_to_aarch64();
> +
> +	debug_led_init();
> +	debug_led_rgb(0xffff00);
> +
> +	sun50i_cpu_lowlevel_init();
> +	setup_uart();
> +
> +	relocate_to_current_adr();
> +	setup_c();

You could call pbl_set_putc here. It would be better if you move
everything below into a separate continue_pine64_pinephone function.

> +
> +	/* Skip SDRAM initialization if we run from it */
> +	if (get_pc() < SUN50I_DRAM_BASE_ADDR) {
> +		size = sun50i_a64_lpddr3_dram_init();
> +		if (size == 0) {
> +			puts_ll("FAIL: dram init\r\n");

and then use pr_ family of functions here instead if it would fit
into your OCRAM.

> +			goto reset;
> +		}
> +		puthex_ll(size);
> +		putc_ll('\r');	putc_ll('\n');
> +	}
> +
> +	puts_ll("now booting\r\n");
> +	fdt = __dtb_z_sun50i_a64_pinephone_1_2_start + get_runtime_offset();
> +	barebox_arm_entry(SUN50I_DRAM_BASE_ADDR, SZ_1G, fdt);
> +
> +reset:
> +	debug_led_rgb(0xff0000);
> +	sun50i_cpu_lowlevel_reset();
> +}
> diff --git a/arch/arm/configs/pinephone_defconfig b/arch/arm/configs/pinephone_defconfig
> new file mode 100644
> index 0000000000..b63f8c6ec7
> --- /dev/null
> +++ b/arch/arm/configs/pinephone_defconfig
> @@ -0,0 +1,11 @@
> +CONFIG_ARCH_SUNXI=y
> +CONFIG_SUNXI_MULTI_BOARDS=y
> +CONFIG_MACH_PINE64_PINEPHONE=y
> +CONFIG_MACH_PINE64_PINE64=y
> +CONFIG_DEBUG_LL=y
> +CONFIG_DRIVER_SERIAL_NS16550=y
> +CONFIG_MCI=y
> +CONFIG_MCI_SUNXI_SMHC=y
> +CONFIG_CMD_DMESG=y
> +CONFIG_MCI_STARTUP=y
> +CONFIG_FS_FAT=y
> diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
> index 0a7cceb461..564a60a0d8 100644
> --- a/arch/arm/dts/Makefile
> +++ b/arch/arm/dts/Makefile
> @@ -81,6 +81,7 @@ lwl-$(CONFIG_MACH_PHYTEC_SOM_IMX6) += imx6q-phytec-phycard.dtb.o \
>  lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7) += imx7d-phyboard-zeta.dtb.o
>  lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1) += stm32mp157c-phycore-stm32mp1-3.dtb.o
>  lwl-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ) += imx8mq-phytec-phycore-som.dtb.o
> +lwl-$(CONFIG_MACH_PINE64_PINEPHONE) += sun50i-a64-pinephone-1_2.dtb.o
>  lwl-$(CONFIG_MACH_PINE64_QUARTZ64) += rk3566-quartz64-a.dtb.o
>  lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3) += armada-xp-openblocks-ax3-4-bb.dtb.o
>  lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6) += kirkwood-openblocks_a6-bb.dtb.o
> diff --git a/arch/arm/dts/sun50i-a64-pinephone-1_2.dts b/arch/arm/dts/sun50i-a64-pinephone-1_2.dts
> new file mode 100644
> index 0000000000..ac6fba1b91
> --- /dev/null
> +++ b/arch/arm/dts/sun50i-a64-pinephone-1_2.dts
> @@ -0,0 +1,3 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <arm64/allwinner/sun50i-a64-pinephone-1.2.dts>
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index 2580a9e56a..e2d236f020 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -9,6 +9,22 @@ config SUNXI_RVBAR_IOMAP
>  	default 0x017000a0  if ARCH_SUN50I_A64
>  	# default 0x09010040 if ARCH_SUN50I_H5
>  
> +config SUNXI_PBL_STACK_TOP
> +       hex
> +       default 0x00018000 if ARCH_SUN50I_A64

Move to header with SUN50I in macro name.

> +
> +config ARCH_SUN50I_A64
> +	bool
> +	select CPU_V8
> +	select CPU_SUPPORTS_64BIT_KERNEL
> +	select CLOCKSOURCE_ARM_ARCHITECTED_TIMER
> +	select PINCTRL_SUN50I_A64
> +	select HAS_DEBUG_LL
> +	select PBL_RELOCATABLE
> +	select PBL_FULLY_PIC

That's not upstream yet. Do you require it?

> +	help
> +	  Allwiner A64 (sun50iw1) SoC
> +
>  config SUNXI_DEBUG_LL_UART_BASE
>  	hex
>  	default 0x01c28000
> @@ -20,6 +36,11 @@ menuconfig SUNXI_MULTI_BOARDS
>  
>  if SUNXI_MULTI_BOARDS
>  
> +config MACH_PINE64_PINEPHONE
> +	bool "Allwinner A64 based Pine64 PinePhone"
> +	select ARCH_SUN50I_A64
> +	select ARM_USE_COMPRESSED_DTB
> +
>  endif
>  
>  endif
> diff --git a/arch/arm/mach-sunxi/cpu_init.c b/arch/arm/mach-sunxi/cpu_init.c
> index f4092d8d5d..52b7a396b3 100644
> --- a/arch/arm/mach-sunxi/cpu_init.c
> +++ b/arch/arm/mach-sunxi/cpu_init.c
> @@ -27,7 +27,18 @@ static void sunxi_ccu_init(void __iomem *ccu)
>  	setbits_le32(ccu + CCU_BUS_SOFT_RST4, 1u << 16);
>  }
>  
> -static void sunxi_cpu_lowlevel_init(void)
> +void sun50i_cpu_lowlevel_init(void)
>  {
> -	sunxi_ccu_init(IOMEM(SUNXI_CCU_BASE_ADDR));
> +	arm_cpu_lowlevel_init();
> +	sunxi_ccu_init(IOMEM(SUN50I_CCU_BASE_ADDR));

Fold into commit adding the function?

> +}
> +
> +void sun50i_cpu_lowlevel_reset(void)
> +{
> +	void __iomem *reg = IOMEM(SUN50I_TIMER_BASE_ADDR);
> +	/* Set the watchdog for its shortest interval (.5s) and wait */
> +	writel(1, reg + 0xB4); /* reset whole system */
> +	writel(1, reg + 0xB8); /* enable */
> +	writel((0xa57 << 1) | 1, reg + 0xB0); /* restart */
> +	while (1);

Nit: __hang() 

>  }
> diff --git a/images/Makefile.sunxi b/images/Makefile.sunxi
> index 778d6f9bdf..070b1bf00d 100644
> --- a/images/Makefile.sunxi
> +++ b/images/Makefile.sunxi
> @@ -11,3 +11,12 @@ $(obj)/%.egonimg: $(obj)/% FORCE
>  	$(call if_changed,egon_image)
>  
>  # ----------------------------------------------------------------------
> +
> +pblb-$(CONFIG_MACH_PINE64_PINEPHONE) += start_pine64_pinephone_xload
> +MAX_PBL_IMAGE_SIZE_start_pine64_pinephone_xload = 0x8000
> +FILE_barebox-pine64-pinephone_xload.img = start_pine64_pinephone_xload.pblb.egonimg
> +image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone_xload.img
> +
> +pblb-$(CONFIG_MACH_PINE64_PINEPHONE) += start_pine64_pinephone
> +FILE_barebox-pine64-pinephone.img = start_pine64_pinephone.pblb
> +image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone.img

Don't you want to restrict DRAM init code to 32K too? You should be able
to do this by marking code running in SRAM as __bare_init/__BARE_INIT for
C and assembly respectively and then use MAX_BARE_INIT_SIZE. 

> diff --git a/include/mach/sunxi/init.h b/include/mach/sunxi/init.h
> index 40c072a028..441425be63 100644
> --- a/include/mach/sunxi/init.h
> +++ b/include/mach/sunxi/init.h
> @@ -6,4 +6,8 @@
>  unsigned long sun50i_a64_ddr3_dram_init(void);
>  unsigned long sun50i_a64_lpddr3_dram_init(void);
>  
> +void sun50i_cpu_lowlevel_init(void);
> +
> +void sun50i_cpu_lowlevel_reset(void);
> +
>  #endif

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 11/11] arm: boards: sunxi: Add pine64 board
  2023-05-10 23:37 ` [RFC PATCH 11/11] arm: boards: sunxi: Add pine64 board Jules Maselbas
@ 2023-05-18 19:44   ` Ahmad Fatoum
  2023-05-19 16:30     ` Jules Maselbas
  0 siblings, 1 reply; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-18 19:44 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 11.05.23 01:37, Jules Maselbas wrote:
> ---
>  arch/arm/boards/Makefile                 |   1 +
>  arch/arm/boards/pine64-pine64/Makefile   |   1 +
>  arch/arm/boards/pine64-pine64/lowlevel.c | 148 +++++++++++++++++++++++
>  arch/arm/dts/Makefile                    |   1 +
>  arch/arm/dts/sun50i-a64-pine64-plus.dts  |  18 +++
>  arch/arm/mach-sunxi/Kconfig              |   5 +
>  images/Makefile.sunxi                    |  10 ++
>  7 files changed, 184 insertions(+)
>  create mode 100644 arch/arm/boards/pine64-pine64/Makefile
>  create mode 100644 arch/arm/boards/pine64-pine64/lowlevel.c
>  create mode 100644 arch/arm/dts/sun50i-a64-pine64-plus.dts
> 
> diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile
> index 083ec2dce6..1fa1f962b3 100644
> --- a/arch/arm/boards/Makefile
> +++ b/arch/arm/boards/Makefile
> @@ -97,6 +97,7 @@ obj-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7)		+= phytec-phycore-imx7/
>  obj-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1)	+= phytec-phycore-stm32mp1/
>  obj-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ)		+= phytec-som-imx8mq/
>  obj-$(CONFIG_MACH_PINE64_PINEPHONE)		+= pine64-pinephone/
> +obj-$(CONFIG_MACH_PINE64_PINE64)		+= pine64-pine64/

That's an odd name.

>  obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3)	+= plathome-openblocks-ax3/
>  obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6)	+= plathome-openblocks-a6/
>  obj-$(CONFIG_MACH_PM9261)			+= pm9261/
> diff --git a/arch/arm/boards/pine64-pine64/Makefile b/arch/arm/boards/pine64-pine64/Makefile
> new file mode 100644
> index 0000000000..b08c4a93ca
> --- /dev/null
> +++ b/arch/arm/boards/pine64-pine64/Makefile
> @@ -0,0 +1 @@
> +lwl-y += lowlevel.o

No board.c for environment and barebox_update handler? =)

> diff --git a/arch/arm/boards/pine64-pine64/lowlevel.c b/arch/arm/boards/pine64-pine64/lowlevel.c
> new file mode 100644
> index 0000000000..744f37a8e0
> --- /dev/null
> +++ b/arch/arm/boards/pine64-pine64/lowlevel.c
> @@ -0,0 +1,148 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +#include <common.h>
> +#include <debug_ll.h>
> +#include <linux/sizes.h>
> +#include <linux/bitops.h>
> +#include <asm/barebox-arm.h>
> +#include <asm/cache.h>
> +#include <mach/sunxi/init.h>
> +#include <mach/sunxi/xload.h>
> +#include <mach/sunxi/egon.h>
> +#include <mach/sunxi/rmr_switch.h>
> +#include <mach/sunxi/sun50i-regs.h>
> +#include <mach/sunxi/sunxi-pinctrl.h>
> +
> +#define STACK_TOP   CONFIG_SUNXI_PBL_STACK_TOP
> +
> +#if 0
> +static void sun50i_pinmux_set(void __iomem *pio, u32 port, u32 pins, u8 func)
> +{
> +	u32 i, msk = 0, cfg = 0;
> +	for (i = 0; i < 8; i++) {
> +		if (pins & (1 << i)) {
> +			cfg |= func << (i * 4);
> +			msk |= 0xf << (i * 4);
> +		}
> +	}
> +	clrsetbits_le32(pio + port, msk, cfg);
> +}
> +#endif

left over?

> +
> +static void setup_uart(void)
> +{
> +	void __iomem *pio = IOMEM(SUN50I_PIO_BASE_ADDR);
> +
> +	/// ... uuuuhh
> +//	sun50i_pinmux_set(pio, PIO_PB_CFG1, BIT(0) | BIT(1), 4);

left over?

> +	/* UART0 pinmux (PB8 + PB9) */
> +	clrsetbits_le32(pio + PIO_PB_CFG1, 0x000000ff, 0x00000044);
> +
> +	debug_ll_init();
> +	putc_ll('>');
> +}
> +
> +ENTRY_FUNCTION_WITHSTACK(start_pine64_pine64, STACK_TOP, r0, r1, r2)
> +{
> +	extern char __dtb_z_sun50i_a64_pine64_plus_start[];
> +	void *fdt;
> +	u32 size;
> +
> +	sunxi_switch_to_aarch64();
> +
> +	sun50i_cpu_lowlevel_init();
> +	setup_uart();
> +
> +	relocate_to_current_adr();
> +	setup_c();
> +
> +	/* Skip SDRAM initialization if we run from it */
> +	if (get_pc() < SUN50I_DRAM_BASE_ADDR) {
> +		size = sun50i_a64_ddr3_dram_init();
> +		if (size == 0) {
> +			puts_ll("FAIL: dram init\r\n");
> +			goto reset;
> +		}
> +		puthex_ll(size);
> +		putc_ll('\r');	putc_ll('\n');
> +	}
> +
> +	puts_ll("now booting\r\n");
> +	fdt = __dtb_z_sun50i_a64_pine64_plus_start + get_runtime_offset();
> +	barebox_arm_entry(SUN50I_DRAM_BASE_ADDR, SZ_1G, fdt);
> +
> +reset:
> +	sun50i_cpu_lowlevel_reset();
> +}
> +
> +static void sun50i_mmc0_init(void)
> +{
> +	void __iomem *ccu = IOMEM(SUN50I_CCU_BASE_ADDR);
> +	void __iomem *pio = IOMEM(SUN50I_PIO_BASE_ADDR);
> +
> +	/* - configure clock gate pinctrl controller */
> +	/* PIO clock enable */
> +	setbits_le32(ccu + CCU_BUS_CLK_GATE2, BIT(5));
> +
> +	/* - configure pinctrl for mmc controller */
> +	/* set alt-function 2 for pins PF0 -> PF5 */
> +	clrsetbits_le32(pio + PIO_PF_CFG0, 0x00ffffff, 0x00222222);
> +//	sun50i_pinmux_set(pio, PIO_PF_CFG0, 0x3f, 2); TO BE REWORK'D
> +
> +	/* - configure clock gate mmc controller */
> +	/* MMC0 and MMC2 clock un-gate and reset */
> +	setbits_le32(ccu + 0x2c0, /* RST_BUS_MMC0 */ BIT(8) | /* RST_BUS_MMC2 */ BIT(10));
> +	setbits_le32(ccu + 0x060, /* CLK_BUS_MMC0 */ BIT(8) | /* CLK_BUS_MMC2 */ BIT(10));
> +
> +	writel(BIT(31), ccu + 0x88); /* MMC0 clock gate */
> +	writel(BIT(31), ccu + 0x90); /* MMC2 clock gate */

Would this be a candidate for moving into mach-sunxi, so other boards
can just reuse it? See for example arch/arm/mach-at91/xload-mmc.c.

> +}
> +
> +static void sunxi_fat_start_image(struct pbl_bio *bio, void *buf, size_t size)
> +{
> +	void (*start)(void) = buf;
> +	int ret;
> +
> +	ret = pbl_fat_load(bio, "barebox.bin", buf, size);

hmm, why chainload from FAT here, but not for Pinephone?

> +	if (ret < 0)
> +		return;
> +	sync_caches_for_execution();
> +	start();
> +}
> +
> +ENTRY_FUNCTION_WITHSTACK(start_pine64_pine64_xload, STACK_TOP, r0, r1, r2)
> +{
> +	struct pbl_bio bio;
> +	u32 size;
> +	int ret;
> +
> +	sunxi_egon_header();
> +	sunxi_switch_to_aarch64();
> +
> +	sun50i_cpu_lowlevel_init();
> +	setup_uart();
> +
> +	relocate_to_current_adr();
> +	setup_c();
> +
> +	size = sun50i_a64_ddr3_dram_init();
> +	if (size == 0) {
> +		puts_ll("FAIL: dram init\r\n");
> +		goto reset;
> +	}
> +
> +	/* now let's boot from sd/mmc */
> +	sun50i_mmc0_init();
> +
> +	/* - setup pbl mci driver */
> +	ret = sunxi_mmc_bio_init(&bio, IOMEM(SUN50I_MMC0_BASE_ADDR), 24000000 /*osc24M */, 0);
> +	if (ret)
> +		goto reset;
> +	puts_ll("bio init\r\n");
> +
> +	sunxi_fat_start_image(&bio, IOMEM(SUN50I_DRAM_BASE_ADDR), SZ_16M);
> +	puts_ll("fat fail\r\n");
> +
> +reset:
> +	sun50i_cpu_lowlevel_reset();
> +}
> diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
> index 564a60a0d8..b3de5ff54a 100644
> --- a/arch/arm/dts/Makefile
> +++ b/arch/arm/dts/Makefile
> @@ -82,6 +82,7 @@ lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7) += imx7d-phyboard-zeta.dtb.o
>  lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1) += stm32mp157c-phycore-stm32mp1-3.dtb.o
>  lwl-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ) += imx8mq-phytec-phycore-som.dtb.o
>  lwl-$(CONFIG_MACH_PINE64_PINEPHONE) += sun50i-a64-pinephone-1_2.dtb.o
> +lwl-$(CONFIG_MACH_PINE64_PINE64) += sun50i-a64-pine64-plus.dtb.o
>  lwl-$(CONFIG_MACH_PINE64_QUARTZ64) += rk3566-quartz64-a.dtb.o
>  lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3) += armada-xp-openblocks-ax3-4-bb.dtb.o
>  lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6) += kirkwood-openblocks_a6-bb.dtb.o
> diff --git a/arch/arm/dts/sun50i-a64-pine64-plus.dts b/arch/arm/dts/sun50i-a64-pine64-plus.dts
> new file mode 100644
> index 0000000000..b7856bcddf
> --- /dev/null
> +++ b/arch/arm/dts/sun50i-a64-pine64-plus.dts
> @@ -0,0 +1,18 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <arm64/allwinner/sun50i-a64-pine64-plus.dts>
> +/{
> +	memory@40000000 {
> +		device_type = "memory";
> +		reg = <0x40000000 0x40000000>; /* 1 GB */
> +	};
> +};
> +&mmc0 { /* SD */
> +};
> +&mmc1 { /* PIO */
> +	max-frequency = <400000>;
> +};
> +&mmc2 { /* eMMC */
> +	max-frequency = <400000>;

What happens when you don't constraint this?

> +	status = "ok";
> +};
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index e2d236f020..f101ae45ed 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -41,6 +41,11 @@ config MACH_PINE64_PINEPHONE
>  	select ARCH_SUN50I_A64
>  	select ARM_USE_COMPRESSED_DTB
>  
> +config MACH_PINE64_PINE64
> +	bool "Allwinner A64 based Pine64 PINE64"
> +	select ARCH_SUN50I_A64
> +	select ARM_USE_COMPRESSED_DTB
> +
>  endif
>  
>  endif
> diff --git a/images/Makefile.sunxi b/images/Makefile.sunxi
> index 070b1bf00d..75adba7f7f 100644
> --- a/images/Makefile.sunxi
> +++ b/images/Makefile.sunxi
> @@ -20,3 +20,13 @@ image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone_xload.img
>  pblb-$(CONFIG_MACH_PINE64_PINEPHONE) += start_pine64_pinephone
>  FILE_barebox-pine64-pinephone.img = start_pine64_pinephone.pblb
>  image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone.img
> +
> +pblb-$(CONFIG_MACH_PINE64_PINE64) += start_pine64_pine64
> +FILE_barebox-pine64-pine64.img = start_pine64_pine64.pblb
> +image-$(CONFIG_MACH_PINE64_PINE64) += barebox-pine64-pine64.img
> +
> +pblb-$(CONFIG_MACH_PINE64_PINE64) += start_pine64_pine64_xload
> +MAX_PBL_IMAGE_SIZE_start_pine64_pine64_xload = 0x8000
> +FILE_barebox-pine64-pine64_xload.img = start_pine64_pine64_xload.pblb.egonimg
> +image-$(CONFIG_MACH_PINE64_PINE64) += barebox-pine64-pine64_xload.img
> +

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 08/11] mci: Add sunxi-mmc driver
  2023-05-18 19:26   ` Ahmad Fatoum
@ 2023-05-19  5:51     ` Sascha Hauer
  2023-05-19  6:51       ` Ahmad Fatoum
  0 siblings, 1 reply; 34+ messages in thread
From: Sascha Hauer @ 2023-05-19  5:51 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: Jules Maselbas, barebox

On Thu, May 18, 2023 at 09:26:56PM +0200, Ahmad Fatoum wrote:
> On 11.05.23 01:37, Jules Maselbas wrote:
> > This driver is adapted from different sources: Linux, u-boot and p-boot.
> > The latter, p-boot (forked from u-boot), is a bootloader for pinephones.
> > 
> > It currently only support PIO xfer but could be further improved to also
> > support DMA xfer. This driver is split in three source file so it can be
> > used by PBL and barebox proper.
> > ---
> >  drivers/mci/Kconfig            |   6 +
> >  drivers/mci/Makefile           |   2 +
> >  drivers/mci/sunxi-mmc-common.c | 259 +++++++++++++++++++++++++++++++++
> >  drivers/mci/sunxi-mmc-pbl.c    |  81 +++++++++++
> >  drivers/mci/sunxi-mmc.c        | 173 ++++++++++++++++++++++
> >  drivers/mci/sunxi-mmc.h        | 225 ++++++++++++++++++++++++++++
> >  include/mach/sunxi/xload.h     |  12 ++
> >  7 files changed, 758 insertions(+)
> >  create mode 100644 drivers/mci/sunxi-mmc-common.c
> >  create mode 100644 drivers/mci/sunxi-mmc-pbl.c
> >  create mode 100644 drivers/mci/sunxi-mmc.c
> >  create mode 100644 drivers/mci/sunxi-mmc.h
> >  create mode 100644 include/mach/sunxi/xload.h
> > 
> > diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
> > index bbdca67e9d..c1903b6c90 100644
> > --- a/drivers/mci/Kconfig
> > +++ b/drivers/mci/Kconfig
> > @@ -72,6 +72,12 @@ config MCI_DW_PIO
> >  	help
> >  	  Use PIO mode (instead of IDMAC) in DW MMC driver.
> >  
> > +config MCI_SUNXI_SMHC
> > +	bool "Allwinner SD-MMC Memory Card Host Controller"
> > +	help
> > +	  Enable support for the Allwinner SD-MMC Memory Card Host Controller,
> > +	  his provides host support for SD and MMC interfaces, in PIO mode.
> 
> This
> 
> > +
> >  config MCI_MXS
> >  	bool "i.MX23/i.MX28"
> >  	depends on ARCH_MXS
> > diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
> > index e3dc5ad8ae..c17cd41db1 100644
> > --- a/drivers/mci/Makefile
> > +++ b/drivers/mci/Makefile
> > @@ -20,4 +20,6 @@ obj-$(CONFIG_MCI_SPI)		+= mci_spi.o
> >  obj-$(CONFIG_MCI_DW)		+= dw_mmc.o
> >  obj-$(CONFIG_MCI_MMCI)		+= mmci.o
> >  obj-$(CONFIG_MCI_STM32_SDMMC2)	+= stm32_sdmmc2.o
> > +obj-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc.o
> > +pbl-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc-pbl.o
> >  obj-pbl-$(CONFIG_MCI_SDHCI)	+= sdhci.o
> > diff --git a/drivers/mci/sunxi-mmc-common.c b/drivers/mci/sunxi-mmc-common.c
> > new file mode 100644
> > index 0000000000..845078805b
> > --- /dev/null
> > +++ b/drivers/mci/sunxi-mmc-common.c
> > @@ -0,0 +1,259 @@
> > +#include "sunxi-mmc.h"
> > +
> > +static int sdxc_read_data_pio(struct sunxi_mmc_host *host, struct mci_data *data);
> > +static int sdxc_write_data_pio(struct sunxi_mmc_host *host, struct mci_data *data);
> > +static int sunxi_mmc_send_cmd(struct sunxi_mmc_host *host, struct mci_cmd *cmd, struct mci_data *data, const char **why);
> > +static int sunxi_mmc_set_ios(struct sunxi_mmc_host *host, struct mci_ios *ios);
> > +static void sunxi_mmc_init(struct sunxi_mmc_host *host);
> > +
> > +static int sdxc_read_data_pio(struct sunxi_mmc_host *host, struct mci_data *data)
> > +{
> > +	size_t i, len = data->blocks * data->blocksize;
> > +	u8 *dst = data->dest;
> > +	u32 reg;
> > +
> > +	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_ACCESS_BY_AHB);
> > +
> > +	for (i = 0; i < len; i += 4) {
> > +		if (wait_on_timeout(2000 * MSECOND, !sdxc_is_fifo_empty(host)))
> 
> This can add up to 8 seconds. Just use is_timeout directly?
> 
> > +			return -ETIMEDOUT;

It will return -ETIMEDOUT after two seconds at maximum.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 08/11] mci: Add sunxi-mmc driver
  2023-05-19  5:51     ` Sascha Hauer
@ 2023-05-19  6:51       ` Ahmad Fatoum
  0 siblings, 0 replies; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-19  6:51 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: Jules Maselbas, barebox

On 19.05.23 07:51, Sascha Hauer wrote:
> On Thu, May 18, 2023 at 09:26:56PM +0200, Ahmad Fatoum wrote:
>> On 11.05.23 01:37, Jules Maselbas wrote:
>>> This driver is adapted from different sources: Linux, u-boot and p-boot.
>>> The latter, p-boot (forked from u-boot), is a bootloader for pinephones.
>>>
>>> It currently only support PIO xfer but could be further improved to also
>>> support DMA xfer. This driver is split in three source file so it can be
>>> used by PBL and barebox proper.
>>> ---
>>>  drivers/mci/Kconfig            |   6 +
>>>  drivers/mci/Makefile           |   2 +
>>>  drivers/mci/sunxi-mmc-common.c | 259 +++++++++++++++++++++++++++++++++
>>>  drivers/mci/sunxi-mmc-pbl.c    |  81 +++++++++++
>>>  drivers/mci/sunxi-mmc.c        | 173 ++++++++++++++++++++++
>>>  drivers/mci/sunxi-mmc.h        | 225 ++++++++++++++++++++++++++++
>>>  include/mach/sunxi/xload.h     |  12 ++
>>>  7 files changed, 758 insertions(+)
>>>  create mode 100644 drivers/mci/sunxi-mmc-common.c
>>>  create mode 100644 drivers/mci/sunxi-mmc-pbl.c
>>>  create mode 100644 drivers/mci/sunxi-mmc.c
>>>  create mode 100644 drivers/mci/sunxi-mmc.h
>>>  create mode 100644 include/mach/sunxi/xload.h
>>>
>>> diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
>>> index bbdca67e9d..c1903b6c90 100644
>>> --- a/drivers/mci/Kconfig
>>> +++ b/drivers/mci/Kconfig
>>> @@ -72,6 +72,12 @@ config MCI_DW_PIO
>>>  	help
>>>  	  Use PIO mode (instead of IDMAC) in DW MMC driver.
>>>  
>>> +config MCI_SUNXI_SMHC
>>> +	bool "Allwinner SD-MMC Memory Card Host Controller"
>>> +	help
>>> +	  Enable support for the Allwinner SD-MMC Memory Card Host Controller,
>>> +	  his provides host support for SD and MMC interfaces, in PIO mode.
>>
>> This
>>
>>> +
>>>  config MCI_MXS
>>>  	bool "i.MX23/i.MX28"
>>>  	depends on ARCH_MXS
>>> diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
>>> index e3dc5ad8ae..c17cd41db1 100644
>>> --- a/drivers/mci/Makefile
>>> +++ b/drivers/mci/Makefile
>>> @@ -20,4 +20,6 @@ obj-$(CONFIG_MCI_SPI)		+= mci_spi.o
>>>  obj-$(CONFIG_MCI_DW)		+= dw_mmc.o
>>>  obj-$(CONFIG_MCI_MMCI)		+= mmci.o
>>>  obj-$(CONFIG_MCI_STM32_SDMMC2)	+= stm32_sdmmc2.o
>>> +obj-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc.o
>>> +pbl-$(CONFIG_MCI_SUNXI_SMHC)	+= sunxi-mmc-pbl.o
>>>  obj-pbl-$(CONFIG_MCI_SDHCI)	+= sdhci.o
>>> diff --git a/drivers/mci/sunxi-mmc-common.c b/drivers/mci/sunxi-mmc-common.c
>>> new file mode 100644
>>> index 0000000000..845078805b
>>> --- /dev/null
>>> +++ b/drivers/mci/sunxi-mmc-common.c
>>> @@ -0,0 +1,259 @@
>>> +#include "sunxi-mmc.h"
>>> +
>>> +static int sdxc_read_data_pio(struct sunxi_mmc_host *host, struct mci_data *data);
>>> +static int sdxc_write_data_pio(struct sunxi_mmc_host *host, struct mci_data *data);
>>> +static int sunxi_mmc_send_cmd(struct sunxi_mmc_host *host, struct mci_cmd *cmd, struct mci_data *data, const char **why);
>>> +static int sunxi_mmc_set_ios(struct sunxi_mmc_host *host, struct mci_ios *ios);
>>> +static void sunxi_mmc_init(struct sunxi_mmc_host *host);
>>> +
>>> +static int sdxc_read_data_pio(struct sunxi_mmc_host *host, struct mci_data *data)
>>> +{
>>> +	size_t i, len = data->blocks * data->blocksize;
>>> +	u8 *dst = data->dest;
>>> +	u32 reg;
>>> +
>>> +	sdxc_writel(host, SDXC_REG_GCTRL, SDXC_GCTRL_ACCESS_BY_AHB);
>>> +
>>> +	for (i = 0; i < len; i += 4) {
>>> +		if (wait_on_timeout(2000 * MSECOND, !sdxc_is_fifo_empty(host)))
>>
>> This can add up to 8 seconds. Just use is_timeout directly?
>>
>>> +			return -ETIMEDOUT;
> 
> It will return -ETIMEDOUT after two seconds at maximum.

Thanks. I misread and thought the loop was from 0 to 4 and wondered if
we shouldn't use a timeout for the whole loop runtime instead of iteration.

Thanks,
Ahmad

> 
> Sascha
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 01/11] scripts: Add Allwinner eGON image support
  2023-05-18 18:47   ` Ahmad Fatoum
@ 2023-05-19  9:40     ` Jules Maselbas
  0 siblings, 0 replies; 34+ messages in thread
From: Jules Maselbas @ 2023-05-19  9:40 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Thu, May 18, 2023 at 08:47:27PM +0200, Ahmad Fatoum wrote:
> On 11.05.23 01:37, Jules Maselbas wrote:
> > On power-up Allwinner SoC starts in boot ROM, aka BROM, which will search
> > for an eGON image: first from the SD card, then from eMMC. If no image is
> > found then the BROM will enter into FEL mode that can be used for initial
> > programming and recovery of devices using USB.
> > 
> > The eGON header structure is adapted from u-boot: /include/sunxi_image.h,
> > the header structure is also documented on https://linux-sunxi.org/EGON
> > 
> > BROM will load, at most, the first 32KB of the image into SRAM, including
> > the header itself! The jump instruction in the header needs to be patched
> > accordingly with the image size.
> 
> S-o-b missing.
I haven't properly setup git on this computer...



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 02/11] sunxi: introduce mach-sunxi
  2023-05-18 18:46   ` Ahmad Fatoum
@ 2023-05-19 10:09     ` Jules Maselbas
  2023-05-22 10:32       ` Ahmad Fatoum
  0 siblings, 1 reply; 34+ messages in thread
From: Jules Maselbas @ 2023-05-19 10:09 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Thu, May 18, 2023 at 08:46:56PM +0200, Ahmad Fatoum wrote:
> On 11.05.23 01:37, Jules Maselbas wrote:
> > Add kbuild boilerplate and some early init functions
> > ---
> >  arch/arm/Kconfig                   | 12 ++++++++++
> >  arch/arm/Makefile                  |  1 +
> >  arch/arm/mach-sunxi/Kconfig        | 16 +++++++++++++
> >  arch/arm/mach-sunxi/Makefile       |  2 ++
> >  arch/arm/mach-sunxi/cpu_init.c     | 33 +++++++++++++++++++++++++++
> >  arch/arm/mach-sunxi/sunxi.c        |  0
> >  images/Makefile                    |  1 +
> >  images/Makefile.sunxi              | 13 +++++++++++
> >  include/mach/sunxi/init.h          |  6 +++++
> >  include/mach/sunxi/sun50i-regs.h   | 36 ++++++++++++++++++++++++++++++
> >  include/mach/sunxi/sunxi-pinctrl.h | 13 +++++++++++
> >  11 files changed, 133 insertions(+)
> >  create mode 100644 arch/arm/mach-sunxi/Kconfig
> >  create mode 100644 arch/arm/mach-sunxi/Makefile
> >  create mode 100644 arch/arm/mach-sunxi/cpu_init.c
> >  create mode 100644 arch/arm/mach-sunxi/sunxi.c
> >  create mode 100644 images/Makefile.sunxi
> >  create mode 100644 include/mach/sunxi/init.h
> >  create mode 100644 include/mach/sunxi/sun50i-regs.h
> >  create mode 100644 include/mach/sunxi/sunxi-pinctrl.h
> > 
> > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> > index abe649de49..8da03e2703 100644
> > --- a/arch/arm/Kconfig
> > +++ b/arch/arm/Kconfig
> > @@ -144,6 +144,17 @@ config ARCH_SOCFPGA
> >  	select COMMON_CLK
> >  	select CLKDEV_LOOKUP
> >  
> > +config ARCH_SUNXI
> > +	bool "Allwinner SoCs"
> 
> depends on ARCH_MULTIARCH? :-)
I don't recall testing a multiarch build, yet

> That's the new hot stuff where you can build multiple SoC families
> in one go. Also, no HAS_DEBUG_LL?
low-level debug is added in a later patch. my initial though was that
HAS_DEBUG_LL is more board specific as it depends on the acctual UART
used for debugging, that's why it is added in ARCH_SUN50I_A64 Kconfig.
I can change this, add HAS_DEBUG_LL to ARCH_SUNXI instead.

> > +	select OFTREE
> > +	select OFDEVICE
> > +	select COMMON_CLK
> > +	select COMMON_CLK_OF_PROVIDER
> > +	select CLKDEV_LOOKUP
> > +	select GENERIC_GPIO
> 
> This is implied by select GPIOLIB below.
ack

> > +	select GPIOLIB
> > +	select PINCTRL
> > +
> >  config ARCH_VERSATILE
> >  	bool "ARM Versatile boards (ARM926EJ-S)"
> >  	select GPIOLIB
> > @@ -311,6 +322,7 @@ source "arch/arm/mach-omap/Kconfig"
> >  source "arch/arm/mach-pxa/Kconfig"
> >  source "arch/arm/mach-rockchip/Kconfig"
> >  source "arch/arm/mach-socfpga/Kconfig"
> > +source "arch/arm/mach-sunxi/Kconfig"
> >  source "arch/arm/mach-stm32mp/Kconfig"
> >  source "arch/arm/mach-versatile/Kconfig"
> >  source "arch/arm/mach-vexpress/Kconfig"
> > diff --git a/arch/arm/Makefile b/arch/arm/Makefile
> > index a506f1e3a3..bb61392e4c 100644
> > --- a/arch/arm/Makefile
> > +++ b/arch/arm/Makefile
> > @@ -103,6 +103,7 @@ machine-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip
> >  machine-$(CONFIG_ARCH_SAMSUNG)		+= samsung
> >  machine-$(CONFIG_ARCH_SOCFPGA)		+= socfpga
> >  machine-$(CONFIG_ARCH_STM32MP)		+= stm32mp
> > +machine-$(CONFIG_ARCH_SUNXI)		+= sunxi
> >  machine-$(CONFIG_ARCH_VERSATILE)	+= versatile
> >  machine-$(CONFIG_ARCH_VEXPRESS)		+= vexpress
> >  machine-$(CONFIG_ARCH_TEGRA)		+= tegra
> > diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> > new file mode 100644
> > index 0000000000..0e8d83fedd
> > --- /dev/null
> > +++ b/arch/arm/mach-sunxi/Kconfig
> > @@ -0,0 +1,16 @@
> > +if ARCH_SUNXI
> > +
> > +config ARCH_TEXT_BASE
> > +	hex
> > +	default 0x0
> > +
> > +menuconfig SUNXI_MULTI_BOARDS
> > +	bool "Allwinner boards"
> > +	select HAVE_PBL_MULTI_IMAGES
> > +	select RELOCATABLE
> 
> Just make this the default?
by putting this into ARCH_SUNXI ?


> > +
> > +if SUNXI_MULTI_BOARDS
> > +
> > +endif
> > +
> > +endif
> > diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> > new file mode 100644
> > index 0000000000..d678973ca2
> > --- /dev/null
> > +++ b/arch/arm/mach-sunxi/Makefile
> > @@ -0,0 +1,2 @@
> > +obj-y += sunxi.o
> > +lwl-y += cpu_init.o
> > diff --git a/arch/arm/mach-sunxi/cpu_init.c b/arch/arm/mach-sunxi/cpu_init.c
> > new file mode 100644
> > index 0000000000..f4092d8d5d
> > --- /dev/null
> > +++ b/arch/arm/mach-sunxi/cpu_init.c
> > @@ -0,0 +1,33 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +
> > +#include <common.h>
> > +#include <linux/sizes.h>
> > +#include <asm/barebox-arm-head.h>
> > +// #include <asm/errata.h> TODO: errata 843419 ?
> > +#include <io.h>
> > +#include <debug_ll.h>
> > +#include <mach/sunxi/init.h>
> > +#include <mach/sunxi/sun50i-regs.h>
> > +#include <mach/sunxi/sunxi-pinctrl.h>
> > +
> > +static void sunxi_ccu_init(void __iomem *ccu)
> > +{
> > +	/* APB2 = 24MHz (UART, I2C) */
> > +	writel((1 << 24 /* src: 1=osc24 */) |
> > +	       (0 << 16 /* pre_div (N): 0=/1 1=/2 2=/4 3=/8 */) |
> > +	       (0 << 0) /* M-1 */,
> > +	       ccu + CCU_APB2_CFG);
> > +	set_cntfrq(24 * 1000 * 1000);
> > +
> > +	/* PIO clock enable */
> > +	setbits_le32(ccu + CCU_BUS_CLK_GATE2, 1u << 5);
> 
> Nit: There's a BIT() macro that could be used here.
ack
 
> > +	/* UART0 clock enable */
> > +	setbits_le32(ccu + CCU_BUS_CLK_GATE3, 1u << 16);
> > +	/* UART0 release reset */
> > +	setbits_le32(ccu + CCU_BUS_SOFT_RST4, 1u << 16);
> 
> Can the pads UART0 uses be muxed otherwise? Is it ok to unconditionally
To be clear: nothing touches the pin-muxes here, only clock-gate and reset
for gpio and uart. UART0 isn't muxed by default, it can be either muxed to
PB8/PB9 or PF2/PF4. To enable UART0 on PB8/PB9 you will to write 0x-----33-
into PIO PB_CFG1(0x28) register. Which I didn't do here... I think that's
because the boot ROM has done it... or i am missing something.

> enable UART0 here? I'd expect this to be a separate sunxi_uart_setup(port)
> that can be used by board code after doing lowlevel_init.
okay

> > +}
> > +
> > +static void sunxi_cpu_lowlevel_init(void)
> > +{
> > +	sunxi_ccu_init(IOMEM(SUNXI_CCU_BASE_ADDR));
> > +}
> > diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
> > new file mode 100644
> > index 0000000000..e69de29bb2
> > diff --git a/images/Makefile b/images/Makefile
> > index aa5814710f..3a10fe1abb 100644
> > --- a/images/Makefile
> > +++ b/images/Makefile
> > @@ -151,6 +151,7 @@ include $(srctree)/images/Makefile.omap3
> >  include $(srctree)/images/Makefile.rockchip
> >  include $(srctree)/images/Makefile.socfpga
> >  include $(srctree)/images/Makefile.stm32mp
> > +include $(srctree)/images/Makefile.sunxi
> >  include $(srctree)/images/Makefile.tegra
> >  include $(srctree)/images/Makefile.vexpress
> >  include $(srctree)/images/Makefile.xburst
> > diff --git a/images/Makefile.sunxi b/images/Makefile.sunxi
> > new file mode 100644
> > index 0000000000..778d6f9bdf
> > --- /dev/null
> > +++ b/images/Makefile.sunxi
> > @@ -0,0 +1,13 @@
> > +#
> > +# barebox image generation Makefile for Allwinner sunxi eGON boot images
> > +#
> > +
> > +# %.egonimg - convert into eGON.BT0 image
> > +# ----------------------------------------------------------------------
> > +quiet_cmd_egon_image = EGON  $@
> > +      cmd_egon_image = $(objtree)/scripts/egon_mkimage $< $@
> > +
> > +$(obj)/%.egonimg: $(obj)/% FORCE
> > +	$(call if_changed,egon_image)
> > +
> > +# ----------------------------------------------------------------------
> > diff --git a/include/mach/sunxi/init.h b/include/mach/sunxi/init.h
> > new file mode 100644
> > index 0000000000..26cc022fde
> > --- /dev/null
> > +++ b/include/mach/sunxi/init.h
> > @@ -0,0 +1,6 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +
> > +#ifndef __MACH_INIT_H
> > +#define __MACH_INIT_H
> > +
> > +#endif
> > diff --git a/include/mach/sunxi/sun50i-regs.h b/include/mach/sunxi/sun50i-regs.h
> > new file mode 100644
> > index 0000000000..68501fa351
> > --- /dev/null
> > +++ b/include/mach/sunxi/sun50i-regs.h
> > @@ -0,0 +1,36 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +
> > +#ifndef __MACH_SUN50I_REGS_H
> > +#define __MACH_SUN50I_REGS_H
> 
> #include <linux/sizes.h>
> 
> > +
> > +#define SUN50I_SRAM_A1_BASE_ADDR 0x00000000
> > +#define SUN50I_SRAM_A1_SIZE SZ_64K
> > +#define SUN50I_SRAM_A2_BASE_ADDR 0x00044000
> > +#define SUN50I_SRAM_A2_SIZE SZ_32K
> > +
> > +#define SUN50I_DRAM_BASE_ADDR	0x40000000
> > +
> > +#define SUN50I_CCU_BASE_ADDR	0x01c20000
> > +#define SUN50I_PIO_BASE_ADDR	0x01c20800
> > +#define SUN50I_MMC0_BASE_ADDR	0x01c0f000
> > +#define SUN50I_MMC1_BASE_ADDR	0x01c10000
> > +#define SUN50I_MMC2_BASE_ADDR	0x01c11000
> > +#define SUN50I_TIMER_BASE_ADDR	0x01c20c00
> 
> I know much existing code doesn't, but you could already use IOMEM
> here to avoid casts in consumer code.
ack

> > +
> > +#define CCU_PLL_CPUX		0x00
> > +#define CCU_PLL_PERIPH0		0x28
> > +#define CCU_CPUX_AXI_CFG	0x50
> > +#define CCU_AHB1_APB1_CFG	0x54
> > +#define CCU_APB2_CFG		0x58
> > +#define CCU_AHB2_CFG		0x5c
> > +#define CCU_BUS_CLK_GATE0	0x60
> > +#define CCU_BUS_CLK_GATE1	0x64
> > +#define CCU_BUS_CLK_GATE2	0x68
> > +#define CCU_BUS_CLK_GATE3	0x6c
> > +#define CCU_CE_CLK		0x9c
> > +#define CCU_MBUS_CLK		0x15c
> > +#define CCU_BUS_SOFT_RST0	0x2c0
> > +#define CCU_BUS_SOFT_RST4	0x2d8
> > +#define CCU_PLL_LOCK_CTRL	0x320
> > +
> > +#endif
> > diff --git a/include/mach/sunxi/sunxi-pinctrl.h b/include/mach/sunxi/sunxi-pinctrl.h
> > new file mode 100644
> > index 0000000000..adb2a24577
> > --- /dev/null
> > +++ b/include/mach/sunxi/sunxi-pinctrl.h
> > @@ -0,0 +1,13 @@
> > +/* pio aka "allwinner,sun8i-h3-pinctrl" */
> 
> No include guard?
... this file could be merged in "sun50i-regs.h", what do you think ?
 
> > +
> > +#define PIO_PA_CFG0 0x00
> > +#define PIO_PB_CFG0 0x24
> > +#define PIO_PB_CFG1 0x28
> > +#define PIO_PD_CFG0 0x6c
> > +#define PIO_PD_CFG1 0x70
> > +#define PIO_PD_CFG2 0x74
> > +#define PIO_PF_CFG0 0xb4
> > +
> > +#define PIO_PA_PULL1 0x20
> > +#define PIO_PB_PULL0 0x40
> > +#define PIO_PD_DATA 0x7c
> 
> -- 
> Pengutronix e.K.                           |                             |
> Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
> 31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> 
> 



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 05/11] arm: sunxi: Add debug_ll
  2023-05-18 19:05   ` Ahmad Fatoum
@ 2023-05-19 10:36     ` Jules Maselbas
  0 siblings, 0 replies; 34+ messages in thread
From: Jules Maselbas @ 2023-05-19 10:36 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Thu, May 18, 2023 at 09:05:21PM +0200, Ahmad Fatoum wrote:
> On 11.05.23 01:37, Jules Maselbas wrote:
snip
> > diff --git a/include/mach/sunxi/debug_ll.h b/include/mach/sunxi/debug_ll.h
> > new file mode 100644
> > index 0000000000..64c65a5917
> > --- /dev/null
> > +++ b/include/mach/sunxi/debug_ll.h
> > @@ -0,0 +1,35 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +
> > +#ifndef __MACH_DEBUG_LL_H__
> > +#define __MACH_DEBUG_LL_H__
> > +
> > +#include <io.h>
> > +
> > +#define DEBUG_LL_UART_ADDR CONFIG_SUNXI_DEBUG_LL_UART_BASE
> > +
> > +static inline uint8_t debug_ll_read_reg(int reg)
> > +{
> > +	return readl(IOMEM(DEBUG_LL_UART_ADDR) + reg * sizeof(uint32_t));
> > +}
> > +
> > +static inline void debug_ll_write_reg(int reg, uint8_t val)
> > +{
> > +	writel(val, IOMEM(DEBUG_LL_UART_ADDR) + reg * sizeof(uint32_t));
> > +}
> > +
> > +#include <debug_ll/ns16550.h>
> > +
> > +static inline void debug_ll_init(void)
> > +{
> > +	uint16_t divisor;
> 
> Call sunxi_uart_setup here?
While taking look at this, the initial code (in sun50i_cpu_lowlevel_init)
and now the lowlevel board code. There is bits done every where, and the
acctual pin mux is done in lowlevel board code, in a function setup_uart.
I think I will "fuse" setup_uart and the bits originally done by
sun50i_cpu_lowlevel_init: lowlevel code will call sunxi_uart_setup (for
clock gating, un-reset and pinmux).
 
> > +
> > +	divisor = debug_ll_ns16550_calc_divisor(24 * 1000 * 1000);
> > +	debug_ll_ns16550_init(divisor);
> > +}
> > +
> > +static inline void PUTC_LL(int c)
> > +{
> > +       debug_ll_ns16550_putc(c);
> > +}
> > +
> > +#endif
> 
> -- 
> Pengutronix e.K.                           |                             |
> Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
> 31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> 
> 



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 07/11] pinctrl: Add sun50i-a64 pinctrl driver
  2023-05-18 19:10   ` Ahmad Fatoum
@ 2023-05-19 10:52     ` Jules Maselbas
  0 siblings, 0 replies; 34+ messages in thread
From: Jules Maselbas @ 2023-05-19 10:52 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Thu, May 18, 2023 at 09:10:27PM +0200, Ahmad Fatoum wrote:
> On 11.05.23 01:37, Jules Maselbas wrote:
> > sunxi pinctrl driver, adapted from Linux, is split in two parts:
> >  - pinctrl-sunxi.c that implement sunxi-generic gpio and function mux
> >  - pinctrl-sun50i-a64.c that only declare sun50i's pin functions
> 
> Could you describe why the pin functions is needed? Is this just sanity
> checking?
The pin functions are needed: for uart and SD/MMC.
The "only declare" part might be misleading: I meant that pinctrl-sun50i-a64.c
has no code, all logic is in the pinctrl-sunxi.c but actual pin functions
are not described in the device-tree but by structures in pinctrl-sun50i-a64.c
(which is almost a direct copy from Linux)

 
> > ---
> >  drivers/pinctrl/Kconfig                    |   2 +
> >  drivers/pinctrl/Makefile                   |   1 +
> >  drivers/pinctrl/sunxi/Kconfig              |  15 +
> >  drivers/pinctrl/sunxi/Makefile             |   3 +
> >  drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 593 +++++++++++++++++++++
> >  drivers/pinctrl/sunxi/pinctrl-sunxi.c      | 371 +++++++++++++
> >  drivers/pinctrl/sunxi/pinctrl-sunxi.h      | 224 ++++++++
> >  7 files changed, 1209 insertions(+)
> >  create mode 100644 drivers/pinctrl/sunxi/Kconfig
> >  create mode 100644 drivers/pinctrl/sunxi/Makefile
> >  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> >  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
> >  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h
> > 
> > diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> > index 2ff99a39c8..0b3d79d1cc 100644
> > --- a/drivers/pinctrl/Kconfig
> > +++ b/drivers/pinctrl/Kconfig
> > @@ -105,6 +105,8 @@ config PINCTRL_STM32
> >  	default y if ARCH_STM32
> >  	help
> >  	  Pinmux and GPIO controller found on STM32 family
> > +
> > +source "drivers/pinctrl/sunxi/Kconfig"
> >  endif
> >  
> >  endmenu
> > diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> > index f1a5fa5715..3bc718d355 100644
> > --- a/drivers/pinctrl/Makefile
> > +++ b/drivers/pinctrl/Makefile
> > @@ -16,3 +16,4 @@ obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o
> >  obj-$(CONFIG_PINCTRL_STM32) += pinctrl-stm32.o
> >  
> >  obj-$(CONFIG_ARCH_MVEBU) += mvebu/
> > +obj-$(CONFIG_ARCH_SUNXI) += sunxi/
> > diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
> > new file mode 100644
> > index 0000000000..36168dc2bb
> > --- /dev/null
> > +++ b/drivers/pinctrl/sunxi/Kconfig
> > @@ -0,0 +1,15 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +if ARCH_SUNXI
> > +
> > +config PINCTRL_SUNXI
> > +	bool
> > +	select PINMUX
> 
> Undefined
> 
> > +	select GENERIC_PINCONF
> 
> Undefined
> 
> > +	select GPIOLIB
> > +
> > +config PINCTRL_SUN50I_A64
> > +	bool "Support for the Allwinner A64 PIO"
> > +	default ARM64 && ARCH_SUNXI
> 
> ARM64 undefined
> 
> > +	select PINCTRL_SUNXI
> > +
> > +endif
> > diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
> > new file mode 100644
> > index 0000000000..db0ff5b50b
> > --- /dev/null
> > +++ b/drivers/pinctrl/sunxi/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +obj-$(CONFIG_ARCH_SUNXI)		+= pinctrl-sunxi.o
> > +obj-$(CONFIG_PINCTRL_SUN50I_A64)	+= pinctrl-sun50i-a64.o
> > diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> > new file mode 100644
> > index 0000000000..4a087802c8
> > --- /dev/null
> > +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> > @@ -0,0 +1,593 @@
> > +/*
> > + * Allwinner A64 SoCs pinctrl driver.
> > + *
> > + * Copyright (C) 2016 - ARM Ltd.
> > + * Author: Andre Przywara <andre.przywara@arm.com>
> > + *
> > + * Based on pinctrl-sun7i-a20.c, which is:
> > + * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com>
> > + *
> > + * This file is licensed under the terms of the GNU General Public
> > + * License version 2.  This program is licensed "as is" without any
> > + * warranty of any kind, whether express or implied.
> 
> SPDX?
> 
> > + */
> > +
> > +#include <common.h>
> > +#include <init.h>
> > +
> > +#include "pinctrl-sunxi.h"
> > +
> > +static const struct sunxi_desc_pin a64_pins[] = {
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart2"),		/* TX */
> > +		  SUNXI_FUNCTION(0x4, "jtag"),		/* MS0 */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),	/* EINT0 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart2"),		/* RX */
> > +		  SUNXI_FUNCTION(0x4, "jtag"),		/* CK0 */
> > +		  SUNXI_FUNCTION(0x5, "sim"),		/* VCCEN */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),		/* EINT1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart2"),		/* RTS */
> > +		  SUNXI_FUNCTION(0x4, "jtag"),		/* DO0 */
> > +		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPEN */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),		/* EINT2 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart2"),		/* CTS */
> > +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* MCLK */
> > +		  SUNXI_FUNCTION(0x4, "jtag"),		/* DI0 */
> > +		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPPP */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),		/* EINT3 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "aif2"),		/* SYNC */
> > +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* SYNC */
> > +		  SUNXI_FUNCTION(0x5, "sim"),		/* CLK */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),		/* EINT4 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "aif2"),		/* BCLK */
> > +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* BCLK */
> > +		  SUNXI_FUNCTION(0x5, "sim"),		/* DATA */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),		/* EINT5 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "aif2"),		/* DOUT */
> > +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DOUT */
> > +		  SUNXI_FUNCTION(0x5, "sim"),		/* RST */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),		/* EINT6 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "aif2"),		/* DIN */
> > +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DIN */
> > +		  SUNXI_FUNCTION(0x5, "sim"),		/* DET */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),		/* EINT7 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x4, "uart0"),		/* TX */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),		/* EINT8 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x4, "uart0"),		/* RX */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),		/* EINT9 */
> > +	/* Hole */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NWE */
> > +		  SUNXI_FUNCTION(0x4, "spi0")),		/* MOSI */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NALE */
> > +		  SUNXI_FUNCTION(0x3, "mmc2"),		/* DS */
> > +		  SUNXI_FUNCTION(0x4, "spi0")),		/* MISO */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCLE */
> > +		  SUNXI_FUNCTION(0x4, "spi0")),		/* SCK */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCE1 */
> > +		  SUNXI_FUNCTION(0x4, "spi0")),		/* CS */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0")),	/* NCE0 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRE# */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CLK */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRB0 */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CMD */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0")),	/* NRB1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ0 */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D0 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ1 */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ2 */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D2 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ3 */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D3 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ4 */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D4 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ5 */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D5 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ6 */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D6 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ7 */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D7 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQS */
> > +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* RST */
> > +	/* Hole */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D2 */
> > +		  SUNXI_FUNCTION(0x3, "uart3"),		/* TX */
> > +		  SUNXI_FUNCTION(0x4, "spi1"),		/* CS */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* CLK */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D3 */
> > +		  SUNXI_FUNCTION(0x3, "uart3"),		/* RX */
> > +		  SUNXI_FUNCTION(0x4, "spi1"),		/* CLK */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* DE */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D4 */
> > +		  SUNXI_FUNCTION(0x3, "uart4"),		/* TX */
> > +		  SUNXI_FUNCTION(0x4, "spi1"),		/* MOSI */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* HSYNC */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D5 */
> > +		  SUNXI_FUNCTION(0x3, "uart4"),		/* RX */
> > +		  SUNXI_FUNCTION(0x4, "spi1"),		/* MISO */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* VSYNC */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D6 */
> > +		  SUNXI_FUNCTION(0x3, "uart4"),		/* RTS */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D0 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D7 */
> > +		  SUNXI_FUNCTION(0x3, "uart4"),		/* CTS */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D10 */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D2 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D11 */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D3 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D12 */
> > +		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD3 */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D4 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D13 */
> > +		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD2 */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D5 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D14 */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D15 */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD0 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D18 */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP0 */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCK */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D19 */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN0 */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCTL */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D20 */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP1 */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ENULL */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D21 */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN1 */
> > +		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD3 */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D6 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D22 */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP2 */
> > +		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD2 */
> > +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D7 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D23 */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN2 */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* CLK */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VPC */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD0 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* DE */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VNC */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCK */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* HSYNC */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP3 */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCTL */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* VSYNC */
> > +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN3 */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* ECLKIN */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "pwm"),		/* PWM0 */
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDC */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDIO */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> > +	/* Hole */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* PCK */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* CLK */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* CK */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* ERR */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* HSYNC */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* SYNC */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* VSYNC */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* DVLD */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* D0 */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* D0 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* D1 */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* D1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* D2 */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* D2 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* D3 */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* D3 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* D4 */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* D4 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* D5 */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* D5 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* D6 */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* D6 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi"),		/* D7 */
> > +		  SUNXI_FUNCTION(0x4, "ts")),		/* D7 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi")),		/* SCK */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "csi")),		/* SDA */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "pll"),		/* LOCK_DBG */
> > +		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SCK */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SDA */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> > +	/* Hole */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D1 */
> > +		  SUNXI_FUNCTION(0x3, "jtag")),		/* MSI */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D0 */
> > +		  SUNXI_FUNCTION(0x3, "jtag")),		/* DI1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CLK */
> > +		  SUNXI_FUNCTION(0x3, "uart0")),	/* TX */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CMD */
> > +		  SUNXI_FUNCTION(0x3, "jtag")),		/* DO1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D3 */
> > +		  SUNXI_FUNCTION(0x3, "uart0")),	/* RX */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D2 */
> > +		  SUNXI_FUNCTION(0x3, "jtag")),		/* CK1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> > +	/* Hole */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CLK */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)),	/* EINT0 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CMD */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)),	/* EINT1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D0 */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)),	/* EINT2 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D1 */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)),	/* EINT3 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D2 */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)),	/* EINT4 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D3 */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)),	/* EINT5 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart1"),		/* TX */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)),	/* EINT6 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart1"),		/* RX */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)),	/* EINT7 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart1"),		/* RTS */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)),	/* EINT8 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart1"),		/* CTS */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)),	/* EINT9 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "aif3"),		/* SYNC */
> > +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* SYNC */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)),	/* EINT10 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "aif3"),		/* BCLK */
> > +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* BCLK */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)),	/* EINT11 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "aif3"),		/* DOUT */
> > +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DOUT */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)),	/* EINT12 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "aif3"),		/* DIN */
> > +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DIN */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)),	/* EINT13 */
> > +	/* Hole */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SCK */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)),	/* EINT0 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SDA */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),	/* EINT1 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SCK */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)),	/* EINT2 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SDA */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)),	/* EINT3 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart3"),		/* TX */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)),	/* EINT4 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart3"),		/* RX */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)),	/* EINT5 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart3"),		/* RTS */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)),	/* EINT6 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "uart3"),		/* CTS */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)),	/* EINT7 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "spdif"),		/* OUT */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)),	/* EINT8 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)),	/* EINT9 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mic"),		/* CLK */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)),	/* EINT10 */
> > +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11),
> > +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> > +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> > +		  SUNXI_FUNCTION(0x2, "mic"),		/* DATA */
> > +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)),	/* EINT11 */
> > +};
> > +
> > +static const struct sunxi_pinctrl_desc a64_pinctrl_data = {
> > +	.pins = a64_pins,
> > +	.npins = ARRAY_SIZE(a64_pins),
> > +};
> > +
> > +static const struct of_device_id a64_pinctrl_dt_match[] = {
> > +	{
> > +		.compatible = "allwinner,sun50i-a64-pinctrl",
> > +		.data = &a64_pinctrl_data
> > +	}, {
> > +		/* sentinel */
> > +	}
> > +};
> > +
> > +static struct driver a64_pinctrl_driver = {
> > +	.name		= "sun50i-a64-pinctrl",
> > +	.probe		= sunxi_pinctrl_probe,
> > +	.of_compatible	= DRV_OF_COMPAT(a64_pinctrl_dt_match),
> > +};
> > +core_platform_driver(a64_pinctrl_driver);
> > diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> > new file mode 100644
> > index 0000000000..e2bde96c70
> > --- /dev/null
> > +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> > @@ -0,0 +1,371 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +#define pr_fmt(fmt) "pinctrl-sunxi: " fmt
> > +
> > +#include <common.h>
> > +#include <io.h>
> > +#include <of.h>
> > +#include <of_address.h>
> > +#include <malloc.h>
> > +#include <linux/clk.h>
> > +
> > +#include "pinctrl-sunxi.h"
> > +
> > +/* This driver assumes the gpio function mux value will not change */
> > +#define FUNC_GPIO_IN	0
> > +#define FUNC_GPIO_OUT	1
> > +
> > +static struct sunxi_pinctrl *to_sunxi_pinctrl(struct pinctrl_device *pdev)
> > +{
> > +	return container_of(pdev, struct sunxi_pinctrl, pdev);
> > +}
> > +
> > +static void sunxi_pinctrl_set_pull(struct sunxi_pinctrl *pinctrl,
> > +				   u16 pin, u32 pull)
> > +{
> > +	u32 reg = sunxi_pull_reg(pin);
> > +	u32 off = sunxi_pull_offset(pin);
> > +	u32 msk = MUX_PINS_MASK << off;
> > +	u32 val = readl(pinctrl->base + reg);
> > +
> > +	val &= ~msk;
> > +	val |= (pull << off) & msk;
> > +	writel(val, pinctrl->base + reg);
> > +}
> > +
> > +static void sunxi_pinctrl_set_dlevel(struct sunxi_pinctrl *pinctrl,
> > +				   u16 pin, u32 lvl)
> > +{
> > +	u32 reg = sunxi_dlevel_reg(pin);
> > +	u32 off = sunxi_dlevel_offset(pin);
> > +	u32 msk = MUX_PINS_MASK << off;
> > +	u32 val = readl(pinctrl->base + reg);
> > +
> > +	val &= ~msk;
> > +	val |= (lvl << off) & msk;
> > +	writel(val, pinctrl->base + reg);
> > +}
> > +
> > +static void sunxi_pinctrl_set_mux(struct sunxi_pinctrl *pinctrl,
> > +				  u16 pin, u8 mux)
> > +{
> > +	u32 reg = sunxi_mux_reg(pin);
> > +	u32 off = sunxi_mux_offset(pin);
> > +	u32 msk = MUX_PINS_MASK << off;
> > +	u32 val = readl(pinctrl->base + reg);
> > +
> > +	val &= ~msk;
> > +	val |= (mux << off) & msk;
> > +	writel(val, pinctrl->base + reg);
> > +}
> > +
> > +static u8 sunxi_pinctrl_get_mux(struct sunxi_pinctrl *pinctrl, u16 pin)
> > +{
> > +	u32 reg = sunxi_mux_reg(pin);
> > +	u32 off = sunxi_mux_offset(pin);
> > +	u32 val = readl(pinctrl->base + reg);
> > +
> > +	return (val >> off) & MUX_PINS_MASK;
> > +}
> > +
> > +static void sunxi_pinctrl_set_conf(struct sunxi_pinctrl *pinctrl,
> > +				  u16 pin, struct device_node *node)
> > +{
> > +	u32 val;
> > +
> > +	if (of_find_property(node, "bias-pull-up", NULL))
> > +		sunxi_pinctrl_set_pull(pinctrl, pin, 1);
> > +	if (of_find_property(node, "bias-pull-down", NULL))
> > +		sunxi_pinctrl_set_pull(pinctrl, pin, 2);
> > +	if (of_find_property(node, "bias-disable", NULL))
> > +		sunxi_pinctrl_set_pull(pinctrl, pin, 0);
> > +
> > +	if (!of_property_read_u32(node, "drive-strength", &val)) {
> > +		val = rounddown(val, 10) / 10 - 1;
> > +		sunxi_pinctrl_set_dlevel(pinctrl, pin, val);
> > +	}
> > +}
> > +
> > +static const char *sunxi_pinctrl_parse_function_prop(struct device_node *node)
> > +{
> > +	const char *function;
> > +	int ret;
> > +
> > +	/* Try the generic binding */
> > +	ret = of_property_read_string(node, "function", &function);
> > +	if (!ret)
> > +		return function;
> > +
> > +	/* And fall back to our legacy one */
> > +	ret = of_property_read_string(node, "allwinner,function", &function);
> > +	if (!ret)
> > +		return function;
> > +
> > +	return NULL;
> > +}
> > +
> > +static struct property *sunxi_pinctrl_find_pins_prop(struct device_node *node)
> > +{
> > +	struct property *prop;
> > +
> > +	/* Try the generic binding */
> > +	prop = of_find_property(node, "pins", NULL);
> > +	if (prop)
> > +		return prop;
> > +
> > +	/* And fall back to our legacy one */
> > +	prop = of_find_property(node, "allwinner,pins", NULL);
> > +	if (prop)
> > +		return prop;
> > +
> > +	return NULL;
> > +}
> > +
> > +#define sunxi_pinctrl_of_pins_for_each_string(np, prop, s)	\
> > +	for (prop = sunxi_pinctrl_find_pins_prop(np),		\
> > +		s = of_prop_next_string(prop, NULL);		\
> > +		s;						\
> > +		s = of_prop_next_string(prop, s))
> > +
> > +
> > +static const struct sunxi_desc_pin *
> > +sunxi_pinctrl_find_pin(struct sunxi_pinctrl *pinctrl, const char *pin_name)
> > +{
> > +	const struct sunxi_desc_pin *pin;
> > +	int i;
> > +
> > +	for (i = 0; i < pinctrl->desc->npins; i++) {
> > +		pin = &pinctrl->desc->pins[i];
> > +		if (!strcmp(pin->pin.name, pin_name))
> > +			return pin;
> > +	}
> > +
> > +	return NULL;
> > +}
> > +
> > +static const struct sunxi_desc_function *
> > +sunxi_pinctrl_find_func(struct sunxi_pinctrl *pinctrl,
> > +			const char *pin_name, const char *func_name)
> > +{
> > +	const struct sunxi_desc_pin *pin;
> > +	const struct sunxi_desc_function *func;
> > +
> > +	pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
> > +	if (!pin)
> > +		return NULL;
> > +
> > +	for (func = pin->functions; func->name; func++)
> > +		if (!strcmp(func->name, func_name))
> > +			return func;
> > +
> > +	return NULL;
> > +}
> > +
> > +static int sunxi_pinctrl_set_func(struct sunxi_pinctrl *pinctrl,
> > +				  struct device_node *np,
> > +				  const char *pin_name, const char *func_name)
> > +{
> > +	struct device *dev = pinctrl->pdev.dev;
> > +	const struct sunxi_desc_pin *pin;
> > +	const struct sunxi_desc_function *func;
> > +
> > +	dev_dbg(dev, "setfunc %s @ %s\n", func_name, pin_name);
> > +
> > +	pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
> > +	if (!pin) {
> > +		dev_err(dev, "pin %s not found\n", pin_name);
> > +		return -EINVAL;
> > +	}
> > +
> > +	func = sunxi_pinctrl_find_func(pinctrl, pin_name, func_name);
> > +	if (!func) {
> > +		dev_err(dev, "func %s not found\n", func_name);
> > +		return -EINVAL;
> > +	}
> > +
> > +	sunxi_pinctrl_set_mux(pinctrl, pin->pin.number, func->muxval);
> > +	sunxi_pinctrl_set_conf(pinctrl, pin->pin.number, np);
> > +
> > +	return 0;
> > +}
> > +
> > +static int sunxi_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node *np)
> > +{
> > +	struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
> > +	struct device *dev = pinctrl->pdev.dev;
> > +	struct property *prop;
> > +	const char *func_name;
> > +	const char *pin_name;
> > +
> > +	func_name = sunxi_pinctrl_parse_function_prop(np);
> > +	if (!func_name) {
> > +		dev_err(dev, "%s: missing 'function' property\n", np->full_name);
> > +		return -EINVAL;
> > +	}
> > +
> > +	sunxi_pinctrl_of_pins_for_each_string(np, prop, pin_name) {
> > +		sunxi_pinctrl_set_func(pinctrl, np, pin_name, func_name);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int sunxi_pinctrl_set_direction(struct pinctrl_device *pdev, unsigned int gpio, bool in)
> > +{
> > +	struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
> > +	u32 func = in ? FUNC_GPIO_IN : FUNC_GPIO_OUT;
> > +
> > +	sunxi_pinctrl_set_mux(pinctrl, gpio, func);
> > +
> > +	return 0;
> > +}
> > +
> > +static int sunxi_gpio_get(struct gpio_chip *chip, unsigned gpio)
> > +{
> > +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> > +	u32 reg = sunxi_data_reg(gpio);
> > +	u32 bit = sunxi_data_offset(gpio);
> > +	u32 val = readl(pinctrl->base + reg);
> > +
> > +	return val & BIT(bit);
> > +}
> > +
> > +static void sunxi_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
> > +{
> > +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> > +	u32 reg = sunxi_data_reg(gpio);
> > +	u32 bit = sunxi_data_offset(gpio);
> > +	u32 val = readl(pinctrl->base + reg);
> > +
> > +	if (value)
> > +		val |= BIT(bit);
> > +	else
> > +		val &= ~BIT(bit);
> > +	writel(val, pinctrl->base + reg);
> > +}
> > +
> > +static int sunxi_gpio_direction_output(struct gpio_chip *chip,
> > +				       unsigned gpio, int value)
> > +{
> > +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> > +
> > +	sunxi_gpio_set(chip, gpio, value);
> > +	sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_OUT);
> > +
> > +	return 0;
> > +}
> > +
> > +static int sunxi_gpio_direction_input(struct gpio_chip *chip,
> > +					unsigned gpio)
> > +{
> > +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> > +
> > +	sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_IN);
> > +
> > +	return 0;
> > +}
> > +
> > +static int sunxi_gpio_get_direction(struct gpio_chip *chip, unsigned gpio)
> > +{
> > +	struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> > +	u32 func = sunxi_pinctrl_get_mux(pinctrl, gpio);
> > +
> > +	if (func == FUNC_GPIO_IN)
> > +		return GPIOF_DIR_IN;
> > +	if (func == FUNC_GPIO_OUT)
> > +		return GPIOF_DIR_OUT;
> > +	return -EINVAL;
> > +}
> > +
> > +static int sunxi_gpio_of_xlate(struct gpio_chip *chip,
> > +			       const struct of_phandle_args *gpiospec,
> > +			       u32 *flags)
> > +{
> > +	int pin, base;
> > +
> > +	if (gpiospec->args_count != 3)
> > +		return -EINVAL;
> > +
> > +	base = PINS_PER_BANK * gpiospec->args[0];
> > +	pin = base + gpiospec->args[1];
> > +
> > +	if (pin > chip->ngpio)
> > +		return -EINVAL;
> > +
> > +	if (flags)
> > +		*flags = gpiospec->args[2];
> > +
> > +	return pin;
> > +}
> > +
> > +static struct pinctrl_ops sunxi_pinctrl_ops = {
> > +	.set_state = sunxi_pinctrl_set_state,
> > +	.set_direction = sunxi_pinctrl_set_direction,
> > +};
> > +
> > +static struct gpio_ops sunxi_gpio_ops = {
> > +	.request = sunxi_gpio_direction_input, /* switch to input function */
> > +	.direction_input = sunxi_gpio_direction_input,
> > +	.direction_output = sunxi_gpio_direction_output,
> > +	.get_direction = sunxi_gpio_get_direction,
> > +	.get = sunxi_gpio_get,
> > +	.set = sunxi_gpio_set,
> > +	.of_xlate = sunxi_gpio_of_xlate,
> > +};
> > +
> > +int sunxi_pinctrl_probe(struct device *dev)
> > +{
> > +	const struct sunxi_pinctrl_desc *desc;
> > +	struct sunxi_pinctrl *pinctrl;
> > +	struct resource *iores;
> > +	int ret;
> > +
> > +	if (!IS_ENABLED(CONFIG_PINCTRL))
> > +		return 0;
> > +
> > +	desc = device_get_match_data(dev);
> > +	if (!desc)
> > +                return -EINVAL;
> > +
> > +	iores = dev_request_mem_resource(dev, 0);
> > +	if (IS_ERR(iores))
> > +		return PTR_ERR(iores);
> > +
> > +	pinctrl = xzalloc(sizeof(*pinctrl));
> > +	dev->priv = pinctrl;
> > +	pinctrl->base = IOMEM(iores->start);
> > +
> > +	pinctrl->desc = desc;
> > +	pinctrl->pdev.dev = dev;
> > +	pinctrl->pdev.ops = &sunxi_pinctrl_ops;
> > +
> > +	ret = pinctrl_register(&pinctrl->pdev);
> > +	if (ret) {
> > +		dev_err(dev, "couldn't register %s driver\n", "pinctrl");
> > +		goto err;
> > +	}
> > +	dev_dbg(dev, "sunxi %s registered\n", "pinctrl");
> > +
> > +	pinctrl->chip.dev = dev;
> > +	pinctrl->chip.ops = &sunxi_gpio_ops;
> > +	/* only the first 8 bank are supported */
> > +	pinctrl->chip.base = 0;
> > +	pinctrl->chip.ngpio = 8 * PINS_PER_BANK;
> > +
> > +	if (of_property_read_bool(dev->of_node, "gpio-controller")) {
> > +		ret = gpiochip_add(&pinctrl->chip);
> > +		if (ret) {
> > +			dev_err(dev, "couldn't register %s driver\n", "gpio-chip");
> > +			goto pinctrl_unregister;
> > +		}
> > +		dev_dbg(dev, "sunxi %s registered\n", "gpio-chip");
> > +	}
> > +	return 0;
> > +
> > +pinctrl_unregister:
> > +	pinctrl_unregister(&pinctrl->pdev);
> > +err:
> > +	release_region(iores);
> > +	free(pinctrl);
> > +	return ret;
> > +}
> > diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
> > new file mode 100644
> > index 0000000000..630f1ef98e
> > --- /dev/null
> > +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
> > @@ -0,0 +1,224 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Allwinner A1X SoCs pinctrl driver.
> > + *
> > + * Copyright (C) 2012 Maxime Ripard
> > + *
> > + * Maxime Ripard <maxime.ripard@free-electrons.com>
> > + *
> > + * This file is licensed under the terms of the GNU General Public
> > + * License version 2.  This program is licensed "as is" without any
> > + * warranty of any kind, whether express or implied.
> > + */
> > +
> > +#ifndef __PINCTRL_SUNXI_H
> > +#define __PINCTRL_SUNXI_H
> > +
> > +#include <pinctrl.h>
> > +#include <gpio.h>
> > +
> > +#define PA_BASE	0
> > +#define PB_BASE	32
> > +#define PC_BASE	64
> > +#define PD_BASE	96
> > +#define PE_BASE	128
> > +#define PF_BASE	160
> > +#define PG_BASE	192
> > +#define PH_BASE	224
> > +#define PI_BASE	256
> > +#define PL_BASE	352
> > +#define PM_BASE	384
> > +#define PN_BASE	416
> > +
> > +#define SUNXI_PIN_NAME_MAX_LEN	5
> > +
> > +#define BANK_MEM_SIZE		0x24
> > +#define MUX_REGS_OFFSET		0x0
> > +#define DATA_REGS_OFFSET	0x10
> > +#define DLEVEL_REGS_OFFSET	0x14
> > +#define PULL_REGS_OFFSET	0x1c
> > +
> > +#define PINS_PER_BANK		32
> > +#define MUX_PINS_PER_REG	8
> > +#define MUX_PINS_BITS		4
> > +#define MUX_PINS_MASK		0x0f
> > +#define DATA_PINS_PER_REG	32
> > +#define DATA_PINS_BITS		1
> > +#define DATA_PINS_MASK		0x01
> > +#define DLEVEL_PINS_PER_REG	16
> > +#define DLEVEL_PINS_BITS	2
> > +#define DLEVEL_PINS_MASK	0x03
> > +#define PULL_PINS_PER_REG	16
> > +#define PULL_PINS_BITS		2
> > +#define PULL_PINS_MASK		0x03
> > +
> > +#define GRP_CFG_REG		0x300
> > +
> > +#define IO_BIAS_MASK		GENMASK(3, 0)
> > +
> > +#define SUN4I_FUNC_INPUT	0
> > +#define SUN4I_FUNC_IRQ		6
> > +
> > +#define PINCTRL_SUN5I_A10S	BIT(1)
> > +#define PINCTRL_SUN5I_A13	BIT(2)
> > +#define PINCTRL_SUN5I_GR8	BIT(3)
> > +#define PINCTRL_SUN6I_A31	BIT(4)
> > +#define PINCTRL_SUN6I_A31S	BIT(5)
> > +#define PINCTRL_SUN4I_A10	BIT(6)
> > +#define PINCTRL_SUN7I_A20	BIT(7)
> > +#define PINCTRL_SUN8I_R40	BIT(8)
> > +#define PINCTRL_SUN8I_V3	BIT(9)
> > +#define PINCTRL_SUN8I_V3S	BIT(10)
> > +
> > +#define PIO_POW_MOD_SEL_REG	0x340
> > +
> > +enum sunxi_desc_bias_voltage {
> > +	BIAS_VOLTAGE_NONE,
> > +	/*
> > +	 * Bias voltage configuration is done through
> > +	 * Pn_GRP_CONFIG registers, as seen on A80 SoC.
> > +	 */
> > +	BIAS_VOLTAGE_GRP_CONFIG,
> > +	/*
> > +	 * Bias voltage is set through PIO_POW_MOD_SEL_REG
> > +	 * register, as seen on H6 SoC, for example.
> > +	 */
> > +	BIAS_VOLTAGE_PIO_POW_MODE_SEL,
> > +};
> > +
> > +struct sunxi_desc_function {
> > +	const char	*name;
> > +	u8		muxval;
> > +};
> > +
> > +struct sunxi_desc_pin {
> > +	struct {
> > +		const char		name[6];
> > +		u16			number;
> > +	} pin;
> > +	const struct sunxi_desc_function	*functions;
> > +};
> > +
> > +struct sunxi_pinctrl_desc {
> > +	const struct sunxi_desc_pin	*pins;
> > +	size_t				npins;
> > +	unsigned			pin_base;
> > +	bool				disable_strict_mode;
> > +	enum sunxi_desc_bias_voltage	io_bias_cfg_variant;
> > +};
> > +
> > +struct sunxi_pinctrl {
> > +	void __iomem			*base;
> > +	struct gpio_chip		chip;
> > +	struct pinctrl_device		pdev;
> > +	const struct sunxi_pinctrl_desc	*desc;
> > +};
> > +
> > +#define SUNXI_PIN(_pin, ...)					\
> > +	{							\
> > +		.pin = _pin,					\
> > +		.functions = (struct sunxi_desc_function[]){	\
> > +			__VA_ARGS__, { } },			\
> > +	}
> > +
> > +#define SUNXI_PINCTRL_PIN(bank, pin)				\
> > +	{							\
> > +		.name = "P" #bank #pin,				\
> > +		.number = P ## bank ## _BASE + (pin)		\
> > +	}
> > +
> > +#define SUNXI_FUNCTION(_val, _name)				\
> > +	{							\
> > +		.name = _name,					\
> > +		.muxval = _val,					\
> > +	}
> > +
> > +#define SUNXI_FUNCTION_IRQ_BANK(...)  {}
> > +
> > +/*
> > + * The sunXi PIO registers are organized as is:
> > + * 0x00 - 0x0c	Muxing values.
> > + *		8 pins per register, each pin having a 4bits value
> > + * 0x10		Pin values
> > + *		32 bits per register, each pin corresponding to one bit
> > + * 0x14 - 0x18	Drive level
> > + *		16 pins per register, each pin having a 2bits value
> > + * 0x1c - 0x20	Pull-Up values
> > + *		16 pins per register, each pin having a 2bits value
> > + *
> > + * This is for the first bank. Each bank will have the same layout,
> > + * with an offset being a multiple of 0x24.
> > + *
> > + * The following functions calculate from the pin number the register
> > + * and the bit offset that we should access.
> > + */
> > +static inline u32 sunxi_mux_reg(u16 pin)
> > +{
> > +	u8 bank = pin / PINS_PER_BANK;
> > +	u32 offset = bank * BANK_MEM_SIZE;
> > +	offset += MUX_REGS_OFFSET;
> > +	offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04;
> > +	return round_down(offset, 4);
> > +}
> > +
> > +static inline u32 sunxi_mux_offset(u16 pin)
> > +{
> > +	u32 pin_num = pin % MUX_PINS_PER_REG;
> > +	return pin_num * MUX_PINS_BITS;
> > +}
> > +
> > +static inline u32 sunxi_data_reg(u16 pin)
> > +{
> > +	u8 bank = pin / PINS_PER_BANK;
> > +	u32 offset = bank * BANK_MEM_SIZE;
> > +	offset += DATA_REGS_OFFSET;
> > +	offset += pin % PINS_PER_BANK / DATA_PINS_PER_REG * 0x04;
> > +	return round_down(offset, 4);
> > +}
> > +
> > +static inline u32 sunxi_data_offset(u16 pin)
> > +{
> > +	u32 pin_num = pin % DATA_PINS_PER_REG;
> > +	return pin_num * DATA_PINS_BITS;
> > +}
> > +
> > +static inline u32 sunxi_dlevel_reg(u16 pin)
> > +{
> > +	u8 bank = pin / PINS_PER_BANK;
> > +	u32 offset = bank * BANK_MEM_SIZE;
> > +	offset += DLEVEL_REGS_OFFSET;
> > +	offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04;
> > +	return round_down(offset, 4);
> > +}
> > +
> > +static inline u32 sunxi_dlevel_offset(u16 pin)
> > +{
> > +	u32 pin_num = pin % DLEVEL_PINS_PER_REG;
> > +	return pin_num * DLEVEL_PINS_BITS;
> > +}
> > +
> > +static inline u32 sunxi_pull_reg(u16 pin)
> > +{
> > +	u8 bank = pin / PINS_PER_BANK;
> > +	u32 offset = bank * BANK_MEM_SIZE;
> > +	offset += PULL_REGS_OFFSET;
> > +	offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04;
> > +	return round_down(offset, 4);
> > +}
> > +
> > +static inline u32 sunxi_pull_offset(u16 pin)
> > +{
> > +	u32 pin_num = pin % PULL_PINS_PER_REG;
> > +	return pin_num * PULL_PINS_BITS;
> > +}
> > +
> > +static inline u32 sunxi_grp_config_reg(u16 pin)
> > +{
> > +	u8 bank = pin / PINS_PER_BANK;
> > +
> > +	return GRP_CFG_REG + bank * 0x4;
> > +}
> > +
> > +int sunxi_pinctrl_probe(struct device *dev);
> > +
> > +#endif /* __PINCTRL_SUNXI_H */
> 
> -- 
> Pengutronix e.K.                           |                             |
> Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
> 31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> 
> 



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 11/11] arm: boards: sunxi: Add pine64 board
  2023-05-18 19:44   ` Ahmad Fatoum
@ 2023-05-19 16:30     ` Jules Maselbas
  0 siblings, 0 replies; 34+ messages in thread
From: Jules Maselbas @ 2023-05-19 16:30 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Thu, May 18, 2023 at 09:44:28PM +0200, Ahmad Fatoum wrote:
> On 11.05.23 01:37, Jules Maselbas wrote:
> > ---
> >  arch/arm/boards/Makefile                 |   1 +
> >  arch/arm/boards/pine64-pine64/Makefile   |   1 +
> >  arch/arm/boards/pine64-pine64/lowlevel.c | 148 +++++++++++++++++++++++
> >  arch/arm/dts/Makefile                    |   1 +
> >  arch/arm/dts/sun50i-a64-pine64-plus.dts  |  18 +++
> >  arch/arm/mach-sunxi/Kconfig              |   5 +
> >  images/Makefile.sunxi                    |  10 ++
> >  7 files changed, 184 insertions(+)
> >  create mode 100644 arch/arm/boards/pine64-pine64/Makefile
> >  create mode 100644 arch/arm/boards/pine64-pine64/lowlevel.c
> >  create mode 100644 arch/arm/dts/sun50i-a64-pine64-plus.dts
> > 
> > diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile
> > index 083ec2dce6..1fa1f962b3 100644
> > --- a/arch/arm/boards/Makefile
> > +++ b/arch/arm/boards/Makefile
> > @@ -97,6 +97,7 @@ obj-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7)		+= phytec-phycore-imx7/
> >  obj-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1)	+= phytec-phycore-stm32mp1/
> >  obj-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ)		+= phytec-som-imx8mq/
> >  obj-$(CONFIG_MACH_PINE64_PINEPHONE)		+= pine64-pinephone/
> > +obj-$(CONFIG_MACH_PINE64_PINE64)		+= pine64-pine64/
> 
> That's an odd name.
yeah... that's the name used in Linux device-trees but the actual board
name is "Pine A64" like the SoC used...

> >  obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3)	+= plathome-openblocks-ax3/
> >  obj-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6)	+= plathome-openblocks-a6/
> >  obj-$(CONFIG_MACH_PM9261)			+= pm9261/
> > diff --git a/arch/arm/boards/pine64-pine64/Makefile b/arch/arm/boards/pine64-pine64/Makefile
> > new file mode 100644
> > index 0000000000..b08c4a93ca
> > --- /dev/null
> > +++ b/arch/arm/boards/pine64-pine64/Makefile
> > @@ -0,0 +1 @@
> > +lwl-y += lowlevel.o
> 
> No board.c for environment and barebox_update handler? =)
not yet i guess, I haven't looked much further than getting the SD card working

> > diff --git a/arch/arm/boards/pine64-pine64/lowlevel.c b/arch/arm/boards/pine64-pine64/lowlevel.c
> > new file mode 100644
> > index 0000000000..744f37a8e0
> > --- /dev/null
> > +++ b/arch/arm/boards/pine64-pine64/lowlevel.c
> > @@ -0,0 +1,148 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +
> > +#include <common.h>
> > +#include <debug_ll.h>
> > +#include <linux/sizes.h>
> > +#include <linux/bitops.h>
> > +#include <asm/barebox-arm.h>
> > +#include <asm/cache.h>
> > +#include <mach/sunxi/init.h>
> > +#include <mach/sunxi/xload.h>
> > +#include <mach/sunxi/egon.h>
> > +#include <mach/sunxi/rmr_switch.h>
> > +#include <mach/sunxi/sun50i-regs.h>
> > +#include <mach/sunxi/sunxi-pinctrl.h>
> > +
> > +#define STACK_TOP   CONFIG_SUNXI_PBL_STACK_TOP
> > +
> > +#if 0
> > +static void sun50i_pinmux_set(void __iomem *pio, u32 port, u32 pins, u8 func)
> > +{
> > +	u32 i, msk = 0, cfg = 0;
> > +	for (i = 0; i < 8; i++) {
> > +		if (pins & (1 << i)) {
> > +			cfg |= func << (i * 4);
> > +			msk |= 0xf << (i * 4);
> > +		}
> > +	}
> > +	clrsetbits_le32(pio + port, msk, cfg);
> > +}
> > +#endif
> 
> left over?
Yes, of an attempt to create lowlevel pinmux functions

> > +
> > +static void setup_uart(void)
> > +{
> > +	void __iomem *pio = IOMEM(SUN50I_PIO_BASE_ADDR);
> > +
> > +	/// ... uuuuhh
> > +//	sun50i_pinmux_set(pio, PIO_PB_CFG1, BIT(0) | BIT(1), 4);
> 
> left over?
yup

> > +	/* UART0 pinmux (PB8 + PB9) */
> > +	clrsetbits_le32(pio + PIO_PB_CFG1, 0x000000ff, 0x00000044);
> > +
> > +	debug_ll_init();
> > +	putc_ll('>');
> > +}
> > +
> > +ENTRY_FUNCTION_WITHSTACK(start_pine64_pine64, STACK_TOP, r0, r1, r2)
> > +{
> > +	extern char __dtb_z_sun50i_a64_pine64_plus_start[];
> > +	void *fdt;
> > +	u32 size;
> > +
> > +	sunxi_switch_to_aarch64();
> > +
> > +	sun50i_cpu_lowlevel_init();
> > +	setup_uart();
> > +
> > +	relocate_to_current_adr();
> > +	setup_c();
> > +
> > +	/* Skip SDRAM initialization if we run from it */
> > +	if (get_pc() < SUN50I_DRAM_BASE_ADDR) {
> > +		size = sun50i_a64_ddr3_dram_init();
> > +		if (size == 0) {
> > +			puts_ll("FAIL: dram init\r\n");
> > +			goto reset;
> > +		}
> > +		puthex_ll(size);
> > +		putc_ll('\r');	putc_ll('\n');
> > +	}
> > +
> > +	puts_ll("now booting\r\n");
> > +	fdt = __dtb_z_sun50i_a64_pine64_plus_start + get_runtime_offset();
> > +	barebox_arm_entry(SUN50I_DRAM_BASE_ADDR, SZ_1G, fdt);
> > +
> > +reset:
> > +	sun50i_cpu_lowlevel_reset();
> > +}
> > +
> > +static void sun50i_mmc0_init(void)
> > +{
> > +	void __iomem *ccu = IOMEM(SUN50I_CCU_BASE_ADDR);
> > +	void __iomem *pio = IOMEM(SUN50I_PIO_BASE_ADDR);
> > +
> > +	/* - configure clock gate pinctrl controller */
> > +	/* PIO clock enable */
> > +	setbits_le32(ccu + CCU_BUS_CLK_GATE2, BIT(5));
> > +
> > +	/* - configure pinctrl for mmc controller */
> > +	/* set alt-function 2 for pins PF0 -> PF5 */
> > +	clrsetbits_le32(pio + PIO_PF_CFG0, 0x00ffffff, 0x00222222);
> > +//	sun50i_pinmux_set(pio, PIO_PF_CFG0, 0x3f, 2); TO BE REWORK'D
> > +
> > +	/* - configure clock gate mmc controller */
> > +	/* MMC0 and MMC2 clock un-gate and reset */
> > +	setbits_le32(ccu + 0x2c0, /* RST_BUS_MMC0 */ BIT(8) | /* RST_BUS_MMC2 */ BIT(10));
> > +	setbits_le32(ccu + 0x060, /* CLK_BUS_MMC0 */ BIT(8) | /* CLK_BUS_MMC2 */ BIT(10));
> > +
> > +	writel(BIT(31), ccu + 0x88); /* MMC0 clock gate */
> > +	writel(BIT(31), ccu + 0x90); /* MMC2 clock gate */
> 
> Would this be a candidate for moving into mach-sunxi, so other boards
> can just reuse it? See for example arch/arm/mach-at91/xload-mmc.c.
ack

> > +}
> > +
> > +static void sunxi_fat_start_image(struct pbl_bio *bio, void *buf, size_t size)
> > +{
> > +	void (*start)(void) = buf;
> > +	int ret;
> > +
> > +	ret = pbl_fat_load(bio, "barebox.bin", buf, size);
> 
> hmm, why chainload from FAT here, but not for Pinephone?
pinephone didn't get the same attention, it should also be done on pinephone.

> > +	if (ret < 0)
> > +		return;
> > +	sync_caches_for_execution();
> > +	start();
> > +}
> > +
> > +ENTRY_FUNCTION_WITHSTACK(start_pine64_pine64_xload, STACK_TOP, r0, r1, r2)
> > +{
> > +	struct pbl_bio bio;
> > +	u32 size;
> > +	int ret;
> > +
> > +	sunxi_egon_header();
> > +	sunxi_switch_to_aarch64();
> > +
> > +	sun50i_cpu_lowlevel_init();
> > +	setup_uart();
> > +
> > +	relocate_to_current_adr();
> > +	setup_c();
> > +
> > +	size = sun50i_a64_ddr3_dram_init();
> > +	if (size == 0) {
> > +		puts_ll("FAIL: dram init\r\n");
> > +		goto reset;
> > +	}
> > +
> > +	/* now let's boot from sd/mmc */
> > +	sun50i_mmc0_init();
> > +
> > +	/* - setup pbl mci driver */
> > +	ret = sunxi_mmc_bio_init(&bio, IOMEM(SUN50I_MMC0_BASE_ADDR), 24000000 /*osc24M */, 0);
> > +	if (ret)
> > +		goto reset;
> > +	puts_ll("bio init\r\n");
> > +
> > +	sunxi_fat_start_image(&bio, IOMEM(SUN50I_DRAM_BASE_ADDR), SZ_16M);
> > +	puts_ll("fat fail\r\n");
> > +
> > +reset:
> > +	sun50i_cpu_lowlevel_reset();
> > +}
> > diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
> > index 564a60a0d8..b3de5ff54a 100644
> > --- a/arch/arm/dts/Makefile
> > +++ b/arch/arm/dts/Makefile
> > @@ -82,6 +82,7 @@ lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_IMX7) += imx7d-phyboard-zeta.dtb.o
> >  lwl-$(CONFIG_MACH_PHYTEC_PHYCORE_STM32MP1) += stm32mp157c-phycore-stm32mp1-3.dtb.o
> >  lwl-$(CONFIG_MACH_PHYTEC_SOM_IMX8MQ) += imx8mq-phytec-phycore-som.dtb.o
> >  lwl-$(CONFIG_MACH_PINE64_PINEPHONE) += sun50i-a64-pinephone-1_2.dtb.o
> > +lwl-$(CONFIG_MACH_PINE64_PINE64) += sun50i-a64-pine64-plus.dtb.o
> >  lwl-$(CONFIG_MACH_PINE64_QUARTZ64) += rk3566-quartz64-a.dtb.o
> >  lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_AX3) += armada-xp-openblocks-ax3-4-bb.dtb.o
> >  lwl-$(CONFIG_MACH_PLATHOME_OPENBLOCKS_A6) += kirkwood-openblocks_a6-bb.dtb.o
> > diff --git a/arch/arm/dts/sun50i-a64-pine64-plus.dts b/arch/arm/dts/sun50i-a64-pine64-plus.dts
> > new file mode 100644
> > index 0000000000..b7856bcddf
> > --- /dev/null
> > +++ b/arch/arm/dts/sun50i-a64-pine64-plus.dts
> > @@ -0,0 +1,18 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <arm64/allwinner/sun50i-a64-pine64-plus.dts>
> > +/{
> > +	memory@40000000 {
> > +		device_type = "memory";
> > +		reg = <0x40000000 0x40000000>; /* 1 GB */
> > +	};
> > +};
> > +&mmc0 { /* SD */
> > +};
> > +&mmc1 { /* PIO */
> > +	max-frequency = <400000>;
> > +};
> > +&mmc2 { /* eMMC */
> > +	max-frequency = <400000>;
> 
> What happens when you don't constraint this?
that's some more leftovers... sadly I cannot get the eMMC working again :'(

> > +	status = "ok";
> > +};
> > diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> > index e2d236f020..f101ae45ed 100644
> > --- a/arch/arm/mach-sunxi/Kconfig
> > +++ b/arch/arm/mach-sunxi/Kconfig
> > @@ -41,6 +41,11 @@ config MACH_PINE64_PINEPHONE
> >  	select ARCH_SUN50I_A64
> >  	select ARM_USE_COMPRESSED_DTB
> >  
> > +config MACH_PINE64_PINE64
> > +	bool "Allwinner A64 based Pine64 PINE64"
> > +	select ARCH_SUN50I_A64
> > +	select ARM_USE_COMPRESSED_DTB
> > +
> >  endif
> >  
> >  endif
> > diff --git a/images/Makefile.sunxi b/images/Makefile.sunxi
> > index 070b1bf00d..75adba7f7f 100644
> > --- a/images/Makefile.sunxi
> > +++ b/images/Makefile.sunxi
> > @@ -20,3 +20,13 @@ image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone_xload.img
> >  pblb-$(CONFIG_MACH_PINE64_PINEPHONE) += start_pine64_pinephone
> >  FILE_barebox-pine64-pinephone.img = start_pine64_pinephone.pblb
> >  image-$(CONFIG_MACH_PINE64_PINEPHONE) += barebox-pine64-pinephone.img
> > +
> > +pblb-$(CONFIG_MACH_PINE64_PINE64) += start_pine64_pine64
> > +FILE_barebox-pine64-pine64.img = start_pine64_pine64.pblb
> > +image-$(CONFIG_MACH_PINE64_PINE64) += barebox-pine64-pine64.img
> > +
> > +pblb-$(CONFIG_MACH_PINE64_PINE64) += start_pine64_pine64_xload
> > +MAX_PBL_IMAGE_SIZE_start_pine64_pine64_xload = 0x8000
> > +FILE_barebox-pine64-pine64_xload.img = start_pine64_pine64_xload.pblb.egonimg
> > +image-$(CONFIG_MACH_PINE64_PINE64) += barebox-pine64-pine64_xload.img
> > +
> 
> -- 
> Pengutronix e.K.                           |                             |
> Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
> 31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> 
> 



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC PATCH 02/11] sunxi: introduce mach-sunxi
  2023-05-19 10:09     ` Jules Maselbas
@ 2023-05-22 10:32       ` Ahmad Fatoum
  0 siblings, 0 replies; 34+ messages in thread
From: Ahmad Fatoum @ 2023-05-22 10:32 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On 19.05.23 12:09, Jules Maselbas wrote:
> On Thu, May 18, 2023 at 08:46:56PM +0200, Ahmad Fatoum wrote:
>> On 11.05.23 01:37, Jules Maselbas wrote:
>>> +menuconfig SUNXI_MULTI_BOARDS
>>> +	bool "Allwinner boards"
>>> +	select HAVE_PBL_MULTI_IMAGES
>>> +	select RELOCATABLE
>>
>> Just make this the default?
> by putting this into ARCH_SUNXI ?

Just drop the SUNXI_MULTI_BOARDS and select HAVE_PBL_MULTI_IMAGES
and RELOCATABLE directly from ARCH_SUNXI.

>>> +	/* UART0 clock enable */
>>> +	setbits_le32(ccu + CCU_BUS_CLK_GATE3, 1u << 16);
>>> +	/* UART0 release reset */
>>> +	setbits_le32(ccu + CCU_BUS_SOFT_RST4, 1u << 16);
>>
>> Can the pads UART0 uses be muxed otherwise? Is it ok to unconditionally
> To be clear: nothing touches the pin-muxes here, only clock-gate and reset
> for gpio and uart. UART0 isn't muxed by default, it can be either muxed to
> PB8/PB9 or PF2/PF4. To enable UART0 on PB8/PB9 you will to write 0x-----33-
> into PIO PB_CFG1(0x28) register. Which I didn't do here... I think that's
> because the boot ROM has done it... or i am missing something.

I see. Still it's odd that UART0 specific stuff is done in lowlevel init.

>>> diff --git a/include/mach/sunxi/sunxi-pinctrl.h b/include/mach/sunxi/sunxi-pinctrl.h
>>> new file mode 100644
>>> index 0000000000..adb2a24577
>>> --- /dev/null
>>> +++ b/include/mach/sunxi/sunxi-pinctrl.h
>>> @@ -0,0 +1,13 @@
>>> +/* pio aka "allwinner,sun8i-h3-pinctrl" */
>>
>> No include guard?
> ... this file could be merged in "sun50i-regs.h", what do you think ?

Either is fine by me.

Cheers,
Ahmad 



-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2023-05-22 10:34 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-10 23:37 [RFC PATCH 00/11] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
2023-05-10 23:37 ` [RFC PATCH 01/11] scripts: Add Allwinner eGON image support Jules Maselbas
2023-05-11  7:25   ` Sascha Hauer
2023-05-11 20:14     ` Jules Maselbas
2023-05-18 18:47   ` Ahmad Fatoum
2023-05-19  9:40     ` Jules Maselbas
2023-05-10 23:37 ` [RFC PATCH 02/11] sunxi: introduce mach-sunxi Jules Maselbas
2023-05-11  7:27   ` Sascha Hauer
2023-05-18 18:46   ` Ahmad Fatoum
2023-05-19 10:09     ` Jules Maselbas
2023-05-22 10:32       ` Ahmad Fatoum
2023-05-10 23:37 ` [RFC PATCH 03/11] ARM: dls: Add ENTRY_HEADER macro to add .text section Jules Maselbas
2023-05-18 18:49   ` Ahmad Fatoum
2023-05-10 23:37 ` [RFC PATCH 04/11] sunxi: Add lowlevel switch to aarch64 Jules Maselbas
2023-05-18 19:01   ` Ahmad Fatoum
2023-05-10 23:37 ` [RFC PATCH 05/11] arm: sunxi: Add debug_ll Jules Maselbas
2023-05-18 19:05   ` Ahmad Fatoum
2023-05-19 10:36     ` Jules Maselbas
2023-05-10 23:37 ` [RFC PATCH 06/11] clk: Add clock driver for sun50i-a64 Jules Maselbas
2023-05-18 19:06   ` Ahmad Fatoum
2023-05-10 23:37 ` [RFC PATCH 07/11] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
2023-05-18 19:10   ` Ahmad Fatoum
2023-05-19 10:52     ` Jules Maselbas
2023-05-10 23:37 ` [RFC PATCH 08/11] mci: Add sunxi-mmc driver Jules Maselbas
2023-05-18 19:26   ` Ahmad Fatoum
2023-05-19  5:51     ` Sascha Hauer
2023-05-19  6:51       ` Ahmad Fatoum
2023-05-10 23:37 ` [RFC PATCH 09/11] arm: sunxi: Add sun50i SDRAM init Jules Maselbas
2023-05-11  7:39   ` Sascha Hauer
2023-05-10 23:37 ` [RFC PATCH 10/11] arm: boards: sunxi: Add initial support for the pinephone Jules Maselbas
2023-05-18 19:39   ` Ahmad Fatoum
2023-05-10 23:37 ` [RFC PATCH 11/11] arm: boards: sunxi: Add pine64 board Jules Maselbas
2023-05-18 19:44   ` Ahmad Fatoum
2023-05-19 16:30     ` Jules Maselbas

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox