mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v2 00/13]  Add support for Allwinner (sunxi) A64 SoC
@ 2023-05-24 23:43 Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 01/13] Documentation: sunxi: Add some documentation Jules Maselbas
                   ` (12 more replies)
  0 siblings, 13 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Hi all,

Here is the fresh update of patches for running barebox on sunxi soc,
rebased on 2023.05.0.

Main changes since rfc is the addition of an entry in the documentation,
the correction of some typo in both comments and code.

This series still has some rough edges and few `#if 0` still around. I need
a bit more time to decide if such things are really needed... or not.

I would like to get the eMMC working before this gets merged but I don't
know what happened on my board, so I am looking for more testers !

Cheers,

Jules Maselbas (13):
  Documentation: sunxi: Add some documentation
  scripts: Add Allwinner eGON image support
  ARM: sunxi: introduce mach-sunxi
  ARM: lds: Add SoC specific sections to go before .text_head_prologue
  ARM: 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
  ARM: sunxi: xload: Add helpers for chain-loading from SD-card

 Documentation/boards/sunxi.rst               |   95 ++
 arch/arm/Kconfig                             |   14 +
 arch/arm/Makefile                            |    1 +
 arch/arm/boards/Makefile                     |    2 +
 arch/arm/boards/pine64-pine64/Makefile       |    1 +
 arch/arm/boards/pine64-pine64/lowlevel.c     |   70 ++
 arch/arm/boards/pine64-pinephone/Makefile    |    2 +
 arch/arm/boards/pine64-pinephone/board.c     |    0
 arch/arm/boards/pine64-pinephone/lowlevel.c  |  104 ++
 arch/arm/configs/pinephone_defconfig         |   12 +
 arch/arm/dts/Makefile                        |    2 +
 arch/arm/dts/sun50i-a64-pine64-plus.dts      |   16 +
 arch/arm/dts/sun50i-a64-pinephone-1_2.dts    |    3 +
 arch/arm/include/asm/debug_ll.h              |    2 +
 arch/arm/lib/pbl.lds.S                       |    1 +
 arch/arm/mach-sunxi/Kconfig                  |   29 +
 arch/arm/mach-sunxi/Makefile                 |    5 +
 arch/arm/mach-sunxi/clock_sun6i.h            |  540 ++++++++++
 arch/arm/mach-sunxi/cpu_init.c               |   57 +
 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 ++++++++++++++++++
 arch/arm/mach-sunxi/sunxi.c                  |    0
 arch/arm/mach-sunxi/xload-mmc.c              |   67 ++
 drivers/clk/Makefile                         |    1 +
 drivers/clk/sunxi/Makefile                   |    2 +
 drivers/clk/sunxi/clk-sun50i-a64.c           |  315 ++++++
 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                  |   80 ++
 drivers/mci/sunxi-mmc.c                      |  173 +++
 drivers/mci/sunxi-mmc.h                      |  229 ++++
 drivers/pinctrl/Kconfig                      |    2 +
 drivers/pinctrl/Makefile                     |    1 +
 drivers/pinctrl/sunxi/Kconfig                |   13 +
 drivers/pinctrl/sunxi/Makefile               |    3 +
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c   |  594 +++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.c        |  371 +++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h        |  224 ++++
 images/Makefile                              |    1 +
 images/Makefile.sunxi                        |   32 +
 include/mach/sunxi/debug_ll.h                |   35 +
 include/mach/sunxi/egon.h                    |   59 +
 include/mach/sunxi/init.h                    |   18 +
 include/mach/sunxi/rmr_switch.h              |   50 +
 include/mach/sunxi/sun50i-regs.h             |   43 +
 include/mach/sunxi/sunxi-pinctrl.h           |   76 ++
 include/mach/sunxi/xload.h                   |   14 +
 scripts/Kconfig                              |    7 +
 scripts/Makefile                             |    1 +
 scripts/egon_mkimage.c                       |  122 +++
 57 files changed, 6153 insertions(+)
 create mode 100644 Documentation/boards/sunxi.rst
 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/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
 create mode 100644 arch/arm/mach-sunxi/sunxi.c
 create mode 100644 arch/arm/mach-sunxi/xload-mmc.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/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.1




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

* [PATCH v2 01/13] Documentation: sunxi: Add some documentation
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-29  9:24   ` Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 02/13] scripts: Add Allwinner eGON image support Jules Maselbas
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Add some informations about Allwinner sunxi boardssupport: the general
boot process, how to use sunxi-fel tool, and how to create a bootable
image disk.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 Documentation/boards/sunxi.rst | 95 ++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)
 create mode 100644 Documentation/boards/sunxi.rst

diff --git a/Documentation/boards/sunxi.rst b/Documentation/boards/sunxi.rst
new file mode 100644
index 0000000000..535c9671b2
--- /dev/null
+++ b/Documentation/boards/sunxi.rst
@@ -0,0 +1,95 @@
+Allwinner sunxi
+===============
+
+Because of size constraints Barebox proper cannot boot directly, the uses
+of :doc:`PBL` allows to compress the Barebox image and it's device-tree.
+However this is not enough, and two images are acctually needed. The first
+image is suffixed *_xload* and it only consist of the PBL with a special
+entry point that looks for ``barebox.bin`` the root of a FAT partition.
+Only the SD card is currently searched, but this could also be in eMMC.
+The second image is your standard Barebox plus PBL image (suffixed ``.pblb``).
+
+Boot process
+------------
+
+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.
+
+Some board may have a button to enter FEL mode at startup. If not, another
+way to enter FEL mode is to not have a valid image eGON image, this can be
+achived by erasing existing eGON image headers.
+
+eGON header
+-----------
+
+The eGON header structure is described in the file ``include/mach/sunxi/egon.h``.
+This is also documented on https://linux-sunxi.org/EGON .
+
+The eGON header, followed by the actual image, must be located at a fixed
+offset of 8192 bytes (4K) from the start of the disk, either SD; or eMMC.
+
+.. code-block:: sh
+
+  # copy the "pine64_xload" eGON image into disk sdd
+  dd if=images/start_pine64_pine64_xload.pblb.egonimg of=/dev/sdd bs=1024 seek=8
+
+The above will write the entire "pine64_xload" Barebox PBL plus the eGON
+header into the disk "/dev/sdd".
+
+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.
+
+Note that on on sunxi platforms the boot ROM will load the entire image
+**including** the eGON header. The actual load address will be offset by
+the eGON header (currently 96 bytes), this bad because arm instructions
+used for relocation expect the base address to be aligned on 4K boundary.
+As a workaround, a egon header is included and linked into the Barebox
+pbl image, this dummy header will be filled later by egon_mkimage.
+
+Board images are defined in ``images/Makefile.sunxi``, here is an example::
+
+.. code-block:: none
+
+  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
+
+
+RMR aarch64 switch
+------------------
+
+Aarch64 capable SoC (A64/sun50i) boot by default in 32-bit mode. A special header
+is added to the start of the PBL image in order to switch to aarch64 mode as soon
+as possible. This must be done very early in the boot process since both ISA are
+not compatible. The code to switch mode is already assembled (mostly arm 32bit)
+and is documented in the header file ``include/mach/sunxi/rmr_switch.h``.
+
+FEL
+---
+
+The ``sunxi-fel`` tool is used to interact, through USB, with sunxi devices
+in FEL mode. ``sunxi-fel`` is part of the sunxi-tools_.
+
+.. _sunxi-tools: https://github.com/linux-sunxi/sunxi-tools
+
+More documentation about FEL_ and how to use the sunxi-fel tool can be
+found on https://linux-sunxi.org/FEL/USBBoot .
+
+**Note:** ``sunxi-fel`` has a commands dedicated to boot u-boot images but theses
+commands require a valid eGON header, if not more. This can be easily bypassed.
+
+The ``sunxi-fel`` tool can be used to load any arbitrary image at a given address
+and can also request the processor to jump and start executing at any address.
+This can be achieved by the following two commands::
+
+.. code-block:: sh
+
+  sunxi-fel write-with-progress 0x00018000 images/start_pine64_pinephone.pblb
+  sunxi-fel exe 0x00018000
+
+These two commands allows the use of a different and bigger SRAM than the
+default 32KB used by the boot ROM.
-- 
2.40.1




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

* [PATCH v2 02/13] scripts: Add Allwinner eGON image support
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 01/13] Documentation: sunxi: Add some documentation Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-06-16 22:00   ` Marco Felsch
  2023-05-24 23:43 ` [PATCH v2 03/13] ARM: sunxi: introduce mach-sunxi Jules Maselbas
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 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, followed by the actual image, must be located at a fixed
offset of 8192 bytes (4K) from the start of the disk, either SD; or eMMC.

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.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
rfc->v2:
 - removed arch/arm/mach-sunxi/egon_header.c (unused)
 - reworked egon_mkimage.c to handle images size not multiple of 4bytes
   and added comments

 include/mach/sunxi/egon.h |  59 ++++++++++++++++++
 scripts/Kconfig           |   7 +++
 scripts/Makefile          |   1 +
 scripts/egon_mkimage.c    | 122 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 189 insertions(+)
 create mode 100644 include/mach/sunxi/egon.h
 create mode 100644 scripts/egon_mkimage.c

diff --git a/include/mach/sunxi/egon.h b/include/mach/sunxi/egon.h
new file mode 100644
index 0000000000..e00992eb7d
--- /dev/null
+++ b/include/mach/sunxi/egon.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef MACH_SUNXI_EGON_H
+#define MACH_SUNXI_EGON_H
+
+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(section) {					\
+		__section(section) static const struct egon_header hdr= \
+			{ .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..5983bdb28a
--- /dev/null
+++ b/scripts/egon_mkimage.c
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <linux/kernel.h>
+
+#include "../include/mach/sunxi/egon.h"
+
+#include "compiler.h"
+#include "common.h"
+#include "common.c"
+
+#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 = sizeof(*hdr);
+	size_t bin_size;
+	size_t img_size;
+	void *bin;
+	int fd, ret;
+
+	bin = read_file(infile, &bin_size);
+	if (!bin) {
+		perror("read_file");
+		exit(1);
+	}
+
+	/* test if the binary has reserved space for the header */
+	hdr = bin;
+	if (hdr->branch == EGON_HDR_BRANCH && memcmp(hdr->magic, "eGON", 4) == 0) {
+		/* strip/skip existing header */
+		bin += hdr_size;
+		bin_size -= hdr_size;
+	}
+
+	hdr = calloc(1, hdr_size);
+	if (!hdr) {
+		perror("malloc");
+		exit(1);
+	}
+
+	/* total image length must be a multiple of 4K bytes */
+	img_size = ALIGN(hdr_size + bin_size, 4096);
+
+	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 header checksum: */
+	sum = STAMP_VALUE;
+	/*  - add the header checksum */
+	for (p32 = (void *)hdr, i = 0; i < hdr_size / sizeof(uint32_t); i++)
+		sum += le32_to_cpu(p32[i]);
+	/*  - add the image checksum */
+	for (p32 = bin, i = 0; i < bin_size / sizeof(uint32_t); i++)
+		sum += le32_to_cpu(p32[i]);
+	/*  - handle image size not aligned on 32-bits */
+	if (bin_size % sizeof(uint32_t)) {
+		uint32_t tmp = 0;
+		size_t rem = bin_size % sizeof(uint32_t);
+		memcpy(&tmp, bin + (bin_size - rem), rem);
+		sum += le32_to_cpu(tmp);
+	}
+	/* final image will be padded with zeros: doesn't change the checksum */
+	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 file size on a 4K bytes multiple (img_size),
+	 * if neccessary ftruncate will pad the end of the file with zeros */
+	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.1




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

* [PATCH v2 03/13] ARM: sunxi: introduce mach-sunxi
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 01/13] Documentation: sunxi: Add some documentation Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 02/13] scripts: Add Allwinner eGON image support Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 04/13] ARM: lds: Add SoC specific sections to go before .text_head_prologue Jules Maselbas
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Add kbuild boilerplate and some early init functions and lowlevel
pinctrl functions that will be necessary for early debug uart.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
rfc->v2:
 - removed ARCH_TEXT_BASE
 - add lowlevel pinmux

 arch/arm/Kconfig                   | 14 ++++++
 arch/arm/Makefile                  |  1 +
 arch/arm/mach-sunxi/Kconfig        |  3 ++
 arch/arm/mach-sunxi/Makefile       |  2 +
 arch/arm/mach-sunxi/cpu_init.c     | 57 ++++++++++++++++++++++
 arch/arm/mach-sunxi/sunxi.c        |  0
 images/Makefile                    |  1 +
 images/Makefile.sunxi              | 13 +++++
 include/mach/sunxi/init.h          | 11 +++++
 include/mach/sunxi/sun50i-regs.h   | 43 +++++++++++++++++
 include/mach/sunxi/sunxi-pinctrl.h | 76 ++++++++++++++++++++++++++++++
 11 files changed, 221 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 5aef8fcd3b..3c6a3133a8 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -155,6 +155,19 @@ config ARCH_SOCFPGA
 	select COMMON_CLK
 	select CLKDEV_LOOKUP
 
+config ARCH_SUNXI
+	bool "Allwinner SoCs"
+	depends on 64BIT
+	select CLKDEV_LOOKUP
+	select COMMON_CLK
+	select COMMON_CLK_OF_PROVIDER
+	select GPIOLIB
+	select HAVE_PBL_MULTI_IMAGES
+	select OFDEVICE
+	select OFTREE
+	select PINCTRL
+	select RELOCATABLE
+
 config ARCH_VERSATILE
 	bool "ARM Versatile boards (ARM926EJ-S)"
 	depends on 32BIT
@@ -325,6 +338,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..6094f40e2e
--- /dev/null
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -0,0 +1,3 @@
+if ARCH_SUNXI
+
+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..aa6add243e
--- /dev/null
+++ b/arch/arm/mach-sunxi/cpu_init.c
@@ -0,0 +1,57 @@
+/* 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)
+{
+	/* running from osc24 */
+	set_cntfrq(24 * 1000 * 1000);
+	/* 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);
+}
+
+void sun50i_cpu_lowlevel_init(void)
+{
+	arm_cpu_lowlevel_init();
+	sunxi_ccu_init(SUN50I_CCU_BASE_ADDR);
+}
+
+void sun50i_cpu_lowlevel_reset(void)
+{
+	void __iomem *reg = 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 */
+	__hang();
+}
+
+void sun50i_uart_setup(void)
+{
+	void __iomem *ccu = SUN50I_CCU_BASE_ADDR;
+	void __iomem *pio = SUN50I_PIO_BASE_ADDR;
+
+	/* PIO clock enable */
+	setbits_le32(ccu + CCU_BUS_CLK_GATE2, BIT(5));
+	/* UART0 clock enable */
+	setbits_le32(ccu + CCU_BUS_CLK_GATE3, BIT(16));
+	/* UART0 release reset */
+	setbits_le32(ccu + CCU_BUS_SOFT_RST4, BIT(16));
+
+	/* UART0 pinmux (PB8 + PB9) */
+	sunxi_pinmux_set_func(pio, PIO_PB_CFG0, GENMASK(9, 8), 4);
+
+	debug_ll_init();
+	putc_ll('>');
+}
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..fed9afe6ba
--- /dev/null
+++ b/include/mach/sunxi/init.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef MACH_SUNXI_INIT_H
+#define MACH_SUNXI_INIT_H
+
+void sun50i_cpu_lowlevel_init(void);
+
+void sun50i_cpu_lowlevel_reset(void);
+
+void sun50i_uart_setup(void);
+
+#endif
diff --git a/include/mach/sunxi/sun50i-regs.h b/include/mach/sunxi/sun50i-regs.h
new file mode 100644
index 0000000000..23934601b4
--- /dev/null
+++ b/include/mach/sunxi/sun50i-regs.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef MACH_SUNXI_SUN50I_REGS_H
+#define MACH_SUNXI_SUN50I_REGS_H
+
+#include <linux/sizes.h>
+
+#define SUN50I_A64_RVBAR_IOMAP	0x017000a0
+#define SUN50I_PBL_STACK_TOP	(SUN50I_SRAM_A1_ADDR + SUN50I_SRAM_A1_SIZE)
+
+#define SUN50I_SRAM_A1_ADDR	0x00010000
+#define SUN50I_SRAM_A1_SIZE	SZ_32K
+#define SUN50I_SRAM_A2_ADDR	0x00044000
+#define SUN50I_SRAM_A2_SIZE	SZ_64K
+#define SUN50I_SRAM_C_ADDR	0x00018000
+/* advertised as 160K, but only ~108K can be used */
+#define SUN50I_SRAM_C_SIZE	(108 * SZ_1K)
+
+#define SUN50I_DRAM_ADDR	0x40000000
+
+#define SUN50I_CCU_BASE_ADDR	IOMEM(0x01c20000)
+#define SUN50I_PIO_BASE_ADDR	IOMEM(0x01c20800)
+#define SUN50I_MMC0_BASE_ADDR	IOMEM(0x01c0f000)
+#define SUN50I_MMC1_BASE_ADDR	IOMEM(0x01c10000)
+#define SUN50I_MMC2_BASE_ADDR	IOMEM(0x01c11000)
+#define SUN50I_TIMER_BASE_ADDR	IOMEM(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..5e796197bd
--- /dev/null
+++ b/include/mach/sunxi/sunxi-pinctrl.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __MACH_SUNXI_PINCTRL_H
+#define __MACH_SUNXI_PINCTRL_H
+
+/* low-level gpio "pio" defines to be used in PBL early init,
+ * pio aka "allwinner,sun8i-h3-pinctrl" */
+
+#define PIO_Pn_CFG0(n) ((n)+0x0)
+#define PIO_Pn_CFG1(n) ((n)+0x4)
+#define PIO_Pn_CFG2(n) ((n)+0x8)
+#define PIO_Pn_CFG3(n) ((n)+0xc)
+
+#define PIO_PB_BASE (0x24*1)
+#define PIO_PC_BASE (0x24*2)
+#define PIO_PD_BASE (0x24*3)
+#define PIO_PE_BASE (0x24*4)
+#define PIO_PF_BASE (0x24*5)
+#define PIO_PG_BASE (0x24*6)
+#define PIO_PH_BASE (0x24*7)
+
+/* PORT B */
+#define PIO_PB_CFG0 PIO_Pn_CFG0(PIO_PB_BASE)
+#define PIO_PB_CFG1 PIO_Pn_CFG1(PIO_PB_BASE)
+/* PORT C */
+#define PIO_PC_CFG0 PIO_Pn_CFG0(PIO_PC_BASE)
+#define PIO_PC_CFG1 PIO_Pn_CFG1(PIO_PC_BASE)
+#define PIO_PC_CFG2 PIO_Pn_CFG2(PIO_PC_BASE)
+/* PORT D */
+#define PIO_PD_CFG0 PIO_Pn_CFG0(PIO_PD_BASE)
+#define PIO_PD_CFG1 PIO_Pn_CFG1(PIO_PD_BASE)
+#define PIO_PD_CFG2 PIO_Pn_CFG2(PIO_PD_BASE)
+#define PIO_PD_CFG3 PIO_Pn_CFG3(PIO_PD_BASE)
+/* PORT E */
+#define PIO_PE_CFG0 PIO_Pn_CFG0(PIO_PE_BASE)
+#define PIO_PE_CFG1 PIO_Pn_CFG1(PIO_PE_BASE)
+#define PIO_PE_CFG2 PIO_Pn_CFG2(PIO_PE_BASE)
+/* PORT F */
+#define PIO_PF_CFG0 PIO_Pn_CFG0(PIO_PF_BASE)
+/* PORT G */
+#define PIO_PG_CFG0 PIO_Pn_CFG0(PIO_PG_BASE)
+#define PIO_PG_CFG1 PIO_Pn_CFG1(PIO_PG_BASE)
+/* PORT H */
+#define PIO_PH_CFG0 PIO_Pn_CFG0(PIO_PH_BASE)
+#define PIO_PH_CFG1 PIO_Pn_CFG1(PIO_PH_BASE)
+
+#define PIO_PA_PULL1 0x20
+#define PIO_PB_PULL0 0x40
+#define PIO_PD_DATA 0x7c
+
+/* static inline so this function can be optimized to a single "call" to clrsetbits */
+static inline void sunxi_pinmux_set_func_port(void __iomem *pio, u32 port, u32 pins, u8 func)
+{
+	u32 i, msk = 0, cfg = 0;
+
+	if (!(pins & 0xff))
+		return;
+
+	for (i = 0; i < 8; i++) {
+		if (pins & (1 << i)) {
+			cfg |= func << (i * 4);
+			msk |= 0xf << (i * 4);
+		}
+	}
+	clrsetbits_le32(pio + port, msk, cfg);
+}
+
+static inline void sunxi_pinmux_set_func(void __iomem *pio, u32 port, u32 pins, u8 func)
+{
+	sunxi_pinmux_set_func_port(pio, PIO_Pn_CFG0(port), pins, func);
+	sunxi_pinmux_set_func_port(pio, PIO_Pn_CFG1(port), pins >> 8, func);
+	sunxi_pinmux_set_func_port(pio, PIO_Pn_CFG2(port), pins >> 16, func);
+	sunxi_pinmux_set_func_port(pio, PIO_Pn_CFG3(port), pins >> 24, func);
+}
+
+#endif
-- 
2.40.1




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

* [PATCH v2 04/13] ARM: lds: Add SoC specific sections to go before .text_head_prologue
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (2 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 03/13] ARM: sunxi: introduce mach-sunxi Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-06-01  6:34   ` Ahmad Fatoum
  2023-05-24 23:43 ` [PATCH v2 05/13] ARM: sunxi: Add lowlevel switch to aarch64 Jules Maselbas
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 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 soc specific section to be put
in the very begging of the .text section, before anything else.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
rfc->v2:
 - fix typo in commit title
 - replace the use of macro with SORT_BY_NAME(.text_head_soc_header*)

 arch/arm/lib/pbl.lds.S | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/lib/pbl.lds.S b/arch/arm/lib/pbl.lds.S
index 114ec7bc81..059909806e 100644
--- a/arch/arm/lib/pbl.lds.S
+++ b/arch/arm/lib/pbl.lds.S
@@ -27,6 +27,7 @@ SECTIONS
 	.text      :
 	{
 		_stext = .;
+		*(SORT_BY_NAME(.text_head_soc_header*))
 		*(.text_head_prologue*)
 		*(.text_head_entry*)
 		__bare_init_start = .;
-- 
2.40.1




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

* [PATCH v2 05/13] ARM: sunxi: Add lowlevel switch to aarch64
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (3 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 04/13] ARM: lds: Add SoC specific sections to go before .text_head_prologue Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 06/13] ARM: sunxi: Add debug_ll Jules Maselbas
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 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

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
rfc->v2:
 - code is no longer assembled from .S, it now an array of 16 u32
 - added 3 nops at the end of padding (aligning the header on 16*4 bytes)
 - the code can be "modified" for other rvbar addresses

 include/mach/sunxi/rmr_switch.h | 50 +++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 include/mach/sunxi/rmr_switch.h

diff --git a/include/mach/sunxi/rmr_switch.h b/include/mach/sunxi/rmr_switch.h
new file mode 100644
index 0000000000..f136b11e46
--- /dev/null
+++ b/include/mach/sunxi/rmr_switch.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef MACH_SUNXI_RMR_SWITCH_H
+#define MACH_SUNXI_RMR_SWITCH_H
+
+/*
+ * ARMv8 RMR reset sequence on Allwinner SoCs.
+ *
+ * All 64-bit capable Allwinner SoCs reset in AArch32 (and continue to
+ * execute 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).
+ *
+ */
+#define SUNXI_RMR_SWITCH_CODE(rvbar_addr) {		       \
+	0xeb000000, /* bl   .+8             subs x0, x0, x0 */ \
+	0x1400000c, /* .word 0x1400000c     b end           */ \
+	0xe59f0020, /* ldr  r0, [pc, #32] ; rvbar           */ \
+	0xe580e000, /* str  lr, [r0]                        */ \
+	0xf57ff04f, /* dsb  sy                              */ \
+	0xf57ff06f, /* isb  syo                             */ \
+	0xee1c0f50, /* mrc  15, 0, r0, cr12, cr0, {2}       */ \
+	0xe3800003, /* orr  r0, r0, #3                      */ \
+	0xee0c0f50, /* mcr  15, 0, r0, cr12, cr0, {2}       */ \
+	0xf57ff06f, /* isb  sy                              */ \
+	0xe320f003, /* 1b: wfi                              */ \
+	0xeafffffd, /* b    1b                              */ \
+	rvbar_addr, \
+	0xd503201f, 0xd503201f, 0xd503201f } /* nop * 3 */
+#define sunxi_switch_to_aarch64(section, rvbar_addr) {			\
+		__section(section) static const u32 rmr_switch[] =	\
+			SUNXI_RMR_SWITCH_CODE(rvbar_addr);		\
+		__keep_symbolref(rmr_switch);				\
+	}
+
+#endif
-- 
2.40.1




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

* [PATCH v2 06/13] ARM: sunxi: Add debug_ll
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (4 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 05/13] ARM: sunxi: Add lowlevel switch to aarch64 Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 07/13] clk: Add clock driver for sun50i-a64 Jules Maselbas
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 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 6094f40e2e..61a5ae9ae2 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -1,3 +1,7 @@
 if ARCH_SUNXI
 
+config SUNXI_DEBUG_LL_UART_BASE
+	hex
+	default 0x01c28000
+
 endif
diff --git a/include/mach/sunxi/debug_ll.h b/include/mach/sunxi/debug_ll.h
new file mode 100644
index 0000000000..99f77ab2e4
--- /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 IOMEM(CONFIG_SUNXI_DEBUG_LL_UART_BASE)
+
+static inline uint8_t debug_ll_read_reg(int reg)
+{
+	return readl(DEBUG_LL_UART_ADDR + reg * sizeof(uint32_t));
+}
+
+static inline void debug_ll_write_reg(int reg, uint8_t val)
+{
+	writel(val, 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.1




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

* [PATCH v2 07/13] clk: Add clock driver for sun50i-a64
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (5 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 06/13] ARM: sunxi: Add debug_ll Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 08/13] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Clock driver is adapted from Linux.
During clock init, the pll-cpu is switched to 1.2GHz, the same is done
for the pll-periph0-2x as recommended.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
rfc->v2:
 - return of_clk_add_provider value on probe

 drivers/clk/Makefile               |   1 +
 drivers/clk/sunxi/Makefile         |   2 +
 drivers/clk/sunxi/clk-sun50i-a64.c | 315 +++++++++++++++++++++++++++++
 drivers/clk/sunxi/clk-sun50i-a64.h |  62 ++++++
 4 files changed, 380 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 c865e4c274..2d87e0c244 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_MACH_RPI_COMMON)	+= clk-rpi.o
 obj-y				+= bcm/
 obj-$(CONFIG_COMMON_CLK_SCMI)	+= clk-scmi.o
 obj-$(CONFIG_COMMON_CLK_GPIO)   += clk-gpio.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..c7463948ea
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sun50i-a64.c
@@ -0,0 +1,315 @@
+// 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));
+	return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &clk_data);
+}
+
+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.1




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

* [PATCH v2 08/13] pinctrl: Add sun50i-a64 pinctrl driver
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (6 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 07/13] clk: Add clock driver for sun50i-a64 Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 09/13] mci: Add sunxi-mmc driver Jules Maselbas
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

sunxi pinctrl driver, adapted from Linux, is split in two parts:
 - pinctrl-sunxi.c that implements gpio, pinctrl and pinmux functions
 - pinctrl-sun50i-a64.c that declare sun50i pins and their functions,
   as such information are not described in device-tree. This file
   only require minor adaptations and be easily copied from Linux.

The pin functions are needed for uart, SD/MMC, and likely for other
controllers too.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
rfc->v2:
 - removed undefined symbols in Kconfig
 - add missing SPDX identifier

 drivers/pinctrl/Kconfig                    |   2 +
 drivers/pinctrl/Makefile                   |   1 +
 drivers/pinctrl/sunxi/Kconfig              |  13 +
 drivers/pinctrl/sunxi/Makefile             |   3 +
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 594 +++++++++++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.c      | 371 +++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h      | 224 ++++++++
 7 files changed, 1208 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..38d8894a7e
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+if ARCH_SUNXI
+
+config PINCTRL_SUNXI
+	bool
+	select GPIOLIB
+
+config PINCTRL_SUN50I_A64
+	bool "Support for Allwinner A64 PIO"
+	default 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..db96cb52b9
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 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.1




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

* [PATCH v2 09/13] mci: Add sunxi-mmc driver
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (7 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 08/13] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-30  8:14   ` Sascha Hauer
  2023-05-24 23:43 ` [PATCH v2 10/13] ARM: sunxi: Add sun50i SDRAM init Jules Maselbas
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 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.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
rfc->v1:
 - cleanup

 drivers/mci/Kconfig            |   6 +
 drivers/mci/Makefile           |   2 +
 drivers/mci/sunxi-mmc-common.c | 259 +++++++++++++++++++++++++++++++++
 drivers/mci/sunxi-mmc-pbl.c    |  80 ++++++++++
 drivers/mci/sunxi-mmc.c        | 173 ++++++++++++++++++++++
 drivers/mci/sunxi-mmc.h        | 229 +++++++++++++++++++++++++++++
 include/mach/sunxi/xload.h     |  12 ++
 7 files changed, 761 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..1de2c8edfa 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,
+	  this 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..af60e7e355
--- /dev/null
+++ b/drivers/mci/sunxi-mmc-pbl.c
@@ -0,0 +1,80 @@
+#include <common.h>
+
+#include <mach/sunxi/xload.h>
+#include "sunxi-mmc.h"
+#include "sunxi-mmc-common.c"
+
+#define SECTOR_SIZE			512
+#define SUPPORT_MAX_BLOCKS		16U
+
+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, 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;
+}
+
+int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
+		       unsigned int clock, unsigned int slot)
+{
+	static struct sunxi_mmc_host host;
+	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..edfa5d853c
--- /dev/null
+++ b/drivers/mci/sunxi-mmc.h
@@ -0,0 +1,229 @@
+/* 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__
+#if 0 /* TODO: test this */
+#include <asm/system.h>
+/* TODO: test this ! */
+#define get_time_ns()		get_cntpct()
+#define is_timeout(s, t)	(s + (t * get_cntfrq() / 1000000) < get_cntpct())
+#else
+/*
+ * 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
+#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.1




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

* [PATCH v2 10/13] ARM: sunxi: Add sun50i SDRAM init
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (8 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 09/13] mci: Add sunxi-mmc driver Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone Jules Maselbas
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Adapted from u-boot.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 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 d678973ca2..0808a21605 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
+lwl-y += sun50i-a64-ddr3-init.o
+lwl-y += sun50i-a64-lpddr3-init.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 fed9afe6ba..626b459533 100644
--- a/include/mach/sunxi/init.h
+++ b/include/mach/sunxi/init.h
@@ -8,4 +8,7 @@ void sun50i_cpu_lowlevel_reset(void);
 
 void sun50i_uart_setup(void);
 
+unsigned long sun50i_a64_ddr3_dram_init(void);
+unsigned long sun50i_a64_lpddr3_dram_init(void);
+
 #endif
-- 
2.40.1




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

* [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (9 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 10/13] ARM: sunxi: Add sun50i SDRAM init Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-30  8:42   ` Sascha Hauer
  2023-05-24 23:43 ` [PATCH v2 12/13] ARM: boards: sunxi: Add pine64 board Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 13/13] ARM: sunxi: xload: Add helpers for chain-loading from SD-card Jules Maselbas
  12 siblings, 1 reply; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 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 | 104 ++++++++++++++++++++
 arch/arm/configs/pinephone_defconfig        |  12 +++
 arch/arm/dts/Makefile                       |   1 +
 arch/arm/dts/sun50i-a64-pinephone-1_2.dts   |   3 +
 arch/arm/mach-sunxi/Kconfig                 |  17 ++++
 images/Makefile.sunxi                       |   9 ++
 include/mach/sunxi/init.h                   |   4 +
 10 files changed, 153 insertions(+)
 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 b204c257f6..f4796f5374 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..262d194864
--- /dev/null
+++ b/arch/arm/boards/pine64-pinephone/lowlevel.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <common.h>
+#include <debug_ll.h>
+#include <linux/sizes.h>
+#include <linux/bitops.h>
+#include <mach/sunxi/barebox-arm.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>
+
+#ifdef DEBUG
+static void debug_led_rgb(int rgb)
+{
+	void __iomem *piobase = 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 = SUN50I_CCU_BASE_ADDR;
+	void __iomem *piobase = SUN50I_PIO_BASE_ADDR;
+
+	/* PIO clock enable */
+	setbits_le32(ccubase + CCU_BUS_CLK_GATE2, BIT(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
+
+SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone, r0, r1, r2)
+{
+	extern char __dtb_z_sun50i_a64_pinephone_1_2_start[];
+	void *fdt;
+	u32 size;
+
+	sunxi_switch_to_aarch64(.text_head_soc_header2, SUN50I_A64_RVBAR_IOMAP);
+
+	debug_led_init();
+	debug_led_rgb(0xffff00);
+
+	sun50i_cpu_lowlevel_init();
+	sun50i_uart_setup();
+
+	relocate_to_current_adr();
+	setup_c();
+
+	/* Skip SDRAM initialization if we run from it */
+	if (get_pc() < SUN50I_DRAM_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_ADDR, SZ_1G, fdt);
+
+reset:
+	debug_led_rgb(0xff0000);
+	sun50i_cpu_lowlevel_reset();
+}
+
+SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone_xload, r0, r1, r2)
+{
+
+	sunxi_egon_header(.text_head_soc_header0);
+	sunxi_switch_to_aarch64(.text_head_soc_header1, SUN50I_A64_RVBAR_IOMAP);
+
+	debug_led_init();
+	debug_led_rgb(0xff0000);
+
+	sun50i_cpu_lowlevel_init();
+	sun50i_uart_setup();
+	debug_led_rgb(0xffff00);
+
+	relocate_to_current_adr();
+	setup_c();
+
+	sun50i_a64_lpddr3_dram_init();
+
+	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..a77cd4ba0b
--- /dev/null
+++ b/arch/arm/configs/pinephone_defconfig
@@ -0,0 +1,12 @@
+CONFIG_ARCH_SUNXI=y
+CONFIG_64BIT=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
+CONFIG_IMAGE_COMPRESSION_XZKERN=y
\ No newline at end of file
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 220e1617e3..e989f399c2 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 61a5ae9ae2..9975ceb471 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -1,7 +1,24 @@
 if ARCH_SUNXI
 
+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
 
+config MACH_PINE64_PINEPHONE
+	bool "Allwinner A64 based Pine64 PinePhone"
+	select ARCH_SUN50I_A64
+	select ARM_USE_COMPRESSED_DTB
+
 endif
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 626b459533..e30986d998 100644
--- a/include/mach/sunxi/init.h
+++ b/include/mach/sunxi/init.h
@@ -11,4 +11,8 @@ void sun50i_uart_setup(void);
 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.1




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

* [PATCH v2 12/13] ARM: boards: sunxi: Add pine64 board
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (10 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  2023-05-24 23:43 ` [PATCH v2 13/13] ARM: sunxi: xload: Add helpers for chain-loading from SD-card Jules Maselbas
  12 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas


Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
rfc->v1:
 - removed max-frequency on eMMC and PIO device-tree node

 arch/arm/boards/Makefile                 |  1 +
 arch/arm/boards/pine64-pine64/Makefile   |  1 +
 arch/arm/boards/pine64-pine64/lowlevel.c | 70 ++++++++++++++++++++++++
 arch/arm/dts/Makefile                    |  1 +
 arch/arm/dts/sun50i-a64-pine64-plus.dts  | 16 ++++++
 arch/arm/mach-sunxi/Kconfig              |  5 ++
 images/Makefile.sunxi                    | 10 ++++
 7 files changed, 104 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 f4796f5374..2a9424d2c2 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..d7d3111557
--- /dev/null
+++ b/arch/arm/boards/pine64-pine64/lowlevel.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <common.h>
+#include <debug_ll.h>
+#include <linux/sizes.h>
+#include <linux/bitops.h>
+#include <mach/sunxi/barebox-arm.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>
+
+SUN50I_A64_ENTRY_FUNCTION(start_pine64_pine64, r0, r1, r2)
+{
+	extern char __dtb_z_sun50i_a64_pine64_plus_start[];
+	void *fdt;
+	u32 size;
+
+	sunxi_switch_to_aarch64(.text_head_soc_header2, SUN50I_A64_RVBAR_IOMAP);
+
+	sun50i_cpu_lowlevel_init();
+	sun50i_uart_setup();
+
+	relocate_to_current_adr();
+	setup_c();
+
+	/* Skip SDRAM initialization if we run from it */
+	if (get_pc() < SUN50I_DRAM_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_ADDR, SZ_1G, fdt);
+
+reset:
+	sun50i_cpu_lowlevel_reset();
+}
+
+SUN50I_A64_ENTRY_FUNCTION(start_pine64_pine64_xload, r0, r1, r2)
+{
+	u32 size;
+
+	sunxi_egon_header(.text_head_soc_header0);
+	sunxi_switch_to_aarch64(.text_head_soc_header1, SUN50I_A64_RVBAR_IOMAP);
+
+	sun50i_cpu_lowlevel_init();
+	sun50i_uart_setup();
+
+	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 */
+	sun50i_mmc0_start_image();
+reset:
+	sun50i_cpu_lowlevel_reset();
+}
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index e989f399c2..23589a5dfa 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..c16b683e88
--- /dev/null
+++ b/arch/arm/dts/sun50i-a64-pine64-plus.dts
@@ -0,0 +1,16 @@
+// 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 */
+};
+&mmc2 { /* eMMC */
+	status = "ok";
+};
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 9975ceb471..0af924ba96 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -21,4 +21,9 @@ 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
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.1




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

* [PATCH v2 13/13] ARM: sunxi: xload: Add helpers for chain-loading from SD-card
  2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
                   ` (11 preceding siblings ...)
  2023-05-24 23:43 ` [PATCH v2 12/13] ARM: boards: sunxi: Add pine64 board Jules Maselbas
@ 2023-05-24 23:43 ` Jules Maselbas
  12 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-24 23:43 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Provide sun50i_mmc0_start_image helper that can be called from PBL to
to easily initialize mmc0 controller and chainload a barebox.bin file
from the first FAT partition.

Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
---
 arch/arm/mach-sunxi/Makefile    |  1 +
 arch/arm/mach-sunxi/xload-mmc.c | 67 +++++++++++++++++++++++++++++++++
 include/mach/sunxi/xload.h      |  2 +
 3 files changed, 70 insertions(+)
 create mode 100644 arch/arm/mach-sunxi/xload-mmc.c

diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 0808a21605..fd38462477 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -2,3 +2,4 @@ obj-y += sunxi.o
 lwl-y += cpu_init.o
 lwl-y += sun50i-a64-ddr3-init.o
 lwl-y += sun50i-a64-lpddr3-init.o
+pbl-y += xload-mmc.o
diff --git a/arch/arm/mach-sunxi/xload-mmc.c b/arch/arm/mach-sunxi/xload-mmc.c
new file mode 100644
index 0000000000..9b2d81148e
--- /dev/null
+++ b/arch/arm/mach-sunxi/xload-mmc.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <common.h>
+#include <asm/cache.h>
+#include <linux/bitops.h>
+#include <mach/sunxi/sun50i-regs.h>
+#include <mach/sunxi/sunxi-pinctrl.h>
+#include <mach/sunxi/xload.h>
+
+static void sun50i_mmc0_init(void)
+{
+	void __iomem *ccu = SUN50I_CCU_BASE_ADDR;
+	void __iomem *pio = SUN50I_PIO_BASE_ADDR;
+
+	/* - clock un-gate pinctrl controller */
+	setbits_le32(ccu + CCU_BUS_CLK_GATE2, BIT(5));
+	/* - set mmc alt-function (2) for pins PF5 to PF0 */
+	sunxi_pinmux_set_func(pio, PIO_PF_CFG0, GENMASK(5, 0), 2);
+
+	/* - clock un-gate mmc controller and release reset */
+	setbits_le32(ccu + 0x2c0, /* RST_BUS_MMC0 */ BIT(8));
+	setbits_le32(ccu + 0x060, /* CLK_BUS_MMC0 */ BIT(8));
+	writel(BIT(31), ccu + 0x88); /* MMC0 clock gate */
+}
+
+#if 0 /* currently unused */
+static void sun50i_mmc2_init(void)
+{
+	void __iomem *ccu = SUN50I_CCU_BASE_ADDR;
+	void __iomem *pio = SUN50I_PIO_BASE_ADDR;
+
+	/* - clock un-gate pinctrl controller */
+	setbits_le32(ccu + CCU_BUS_CLK_GATE2, BIT(5));
+	/* - set mmc alt-function (3) for pins PC16 to PC5 */
+	sunxi_pinmux_set_func(pio, PIO_PC_BASE, GENMASK(16, 5), 3);
+
+	/* - clock un-gate mmc controller and release reset */
+	setbits_le32(ccu + 0x2c0, /* RST_BUS_MMC2 */ BIT(10));
+	setbits_le32(ccu + 0x060, /* CLK_BUS_MMC2 */ BIT(10));
+	writel(BIT(31), ccu + 0x90); /* MMC2 clock gate */
+}
+#endif
+
+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();
+}
+
+void sun50i_mmc0_start_image(void)
+{
+	struct pbl_bio bio;
+	int ret;
+
+	sun50i_mmc0_init();
+
+	ret = sunxi_mmc_bio_init(&bio, SUN50I_MMC0_BASE_ADDR, 24000000, 0);
+	if (ret)
+		return;
+	sunxi_fat_start_image(&bio, IOMEM(SUN50I_DRAM_ADDR), SZ_16M);
+}
diff --git a/include/mach/sunxi/xload.h b/include/mach/sunxi/xload.h
index f978db4951..005f78b5c5 100644
--- a/include/mach/sunxi/xload.h
+++ b/include/mach/sunxi/xload.h
@@ -9,4 +9,6 @@
 int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
 		       unsigned int clock, unsigned int slot);
 
+void sun50i_mmc0_start_image(void);
+
 #endif
-- 
2.40.1




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

* Re: [PATCH v2 01/13] Documentation: sunxi: Add some documentation
  2023-05-24 23:43 ` [PATCH v2 01/13] Documentation: sunxi: Add some documentation Jules Maselbas
@ 2023-05-29  9:24   ` Jules Maselbas
  0 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-05-29  9:24 UTC (permalink / raw)
  To: barebox

I will have to fix this

On Thu, May 25, 2023 at 01:43:16AM +0200, Jules Maselbas wrote:
> Add some informations about Allwinner sunxi boardssupport: the general
> boot process, how to use sunxi-fel tool, and how to create a bootable
> image disk.
> 
> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> ---
>  Documentation/boards/sunxi.rst | 95 ++++++++++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
>  create mode 100644 Documentation/boards/sunxi.rst
> 
> diff --git a/Documentation/boards/sunxi.rst b/Documentation/boards/sunxi.rst
> new file mode 100644
> index 0000000000..535c9671b2
> --- /dev/null
> +++ b/Documentation/boards/sunxi.rst
...
> +eGON header
> +-----------
> +
> +The eGON header structure is described in the file ``include/mach/sunxi/egon.h``.
> +This is also documented on https://linux-sunxi.org/EGON .
> +
> +The eGON header, followed by the actual image, must be located at a fixed
> +offset of 8192 bytes (4K) from the start of the disk, either SD; or eMMC.
typo: s/4K/8K/



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

* Re: [PATCH v2 09/13] mci: Add sunxi-mmc driver
  2023-05-24 23:43 ` [PATCH v2 09/13] mci: Add sunxi-mmc driver Jules Maselbas
@ 2023-05-30  8:14   ` Sascha Hauer
  2023-06-01  6:15     ` Jules Maselbas
  0 siblings, 1 reply; 30+ messages in thread
From: Sascha Hauer @ 2023-05-30  8:14 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On Thu, May 25, 2023 at 01:43:24AM +0200, 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.
> 
> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> ---
> rfc->v1:
>  - cleanup
> 
>  drivers/mci/Kconfig            |   6 +
>  drivers/mci/Makefile           |   2 +
>  drivers/mci/sunxi-mmc-common.c | 259 +++++++++++++++++++++++++++++++++
>  drivers/mci/sunxi-mmc-pbl.c    |  80 ++++++++++
>  drivers/mci/sunxi-mmc.c        | 173 ++++++++++++++++++++++
>  drivers/mci/sunxi-mmc.h        | 229 +++++++++++++++++++++++++++++
>  include/mach/sunxi/xload.h     |  12 ++
>  7 files changed, 761 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..1de2c8edfa 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,
> +	  this 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);

Some (all?) of these forward declarations are unnecessary.

> +
> +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));

Why memcpy? You can safely assume that dst is sufficiently aligned for
putting the register value there directly.

> +	}
> +
> +	return i;

The caller is not interested in the actual count, better just return 0.

> +}
> +
> +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

Please drop if that's not needed (or explain why it might be needed)

> +	return i;

Return 0.

> +}
> +
> +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..af60e7e355
> --- /dev/null
> +++ b/drivers/mci/sunxi-mmc-pbl.c
> @@ -0,0 +1,80 @@
> +#include <common.h>
> +
> +#include <mach/sunxi/xload.h>
> +#include "sunxi-mmc.h"
> +#include "sunxi-mmc-common.c"
> +
> +#define SECTOR_SIZE			512
> +#define SUPPORT_MAX_BLOCKS		16U
> +
> +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, 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;
> +}
> +
> +int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
> +		       unsigned int clock, unsigned int slot)
> +{
> +	static struct sunxi_mmc_host host;
> +	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..edfa5d853c
> --- /dev/null
> +++ b/drivers/mci/sunxi-mmc.h
> @@ -0,0 +1,229 @@
> +/* 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__
> +#if 0 /* TODO: test this */
> +#include <asm/system.h>
> +/* TODO: test this ! */
> +#define get_time_ns()		get_cntpct()
> +#define is_timeout(s, t)	(s + (t * get_cntfrq() / 1000000) < get_cntpct()

You could base your timeout loop on udelay which is available for
Aarch64 PBL

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] 30+ messages in thread

* Re: [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone
  2023-05-24 23:43 ` [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone Jules Maselbas
@ 2023-05-30  8:42   ` Sascha Hauer
  2023-06-01  5:50     ` Jules Maselbas
  0 siblings, 1 reply; 30+ messages in thread
From: Sascha Hauer @ 2023-05-30  8:42 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On Thu, May 25, 2023 at 01:43:26AM +0200, Jules Maselbas wrote:
> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> ---
>  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 | 104 ++++++++++++++++++++
>  arch/arm/configs/pinephone_defconfig        |  12 +++
>  arch/arm/dts/Makefile                       |   1 +
>  arch/arm/dts/sun50i-a64-pinephone-1_2.dts   |   3 +
>  arch/arm/mach-sunxi/Kconfig                 |  17 ++++
>  images/Makefile.sunxi                       |   9 ++
>  include/mach/sunxi/init.h                   |   4 +
>  10 files changed, 153 insertions(+)
>  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 b204c257f6..f4796f5374 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..262d194864
> --- /dev/null
> +++ b/arch/arm/boards/pine64-pinephone/lowlevel.c
> @@ -0,0 +1,104 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +#include <common.h>
> +#include <debug_ll.h>
> +#include <linux/sizes.h>
> +#include <linux/bitops.h>
> +#include <mach/sunxi/barebox-arm.h>

This file is missing in this series. Forgot to git add it?

> +#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>
> +
> +#ifdef DEBUG
> +static void debug_led_rgb(int rgb)
> +{
> +	void __iomem *piobase = 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 = SUN50I_CCU_BASE_ADDR;
> +	void __iomem *piobase = SUN50I_PIO_BASE_ADDR;
> +
> +	/* PIO clock enable */
> +	setbits_le32(ccubase + CCU_BUS_CLK_GATE2, BIT(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
> +
> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone, r0, r1, r2)
> +{
> +	extern char __dtb_z_sun50i_a64_pinephone_1_2_start[];
> +	void *fdt;
> +	u32 size;
> +
> +	sunxi_switch_to_aarch64(.text_head_soc_header2, SUN50I_A64_RVBAR_IOMAP);
> +
> +	debug_led_init();
> +	debug_led_rgb(0xffff00);
> +
> +	sun50i_cpu_lowlevel_init();
> +	sun50i_uart_setup();
> +
> +	relocate_to_current_adr();
> +	setup_c();
> +
> +	/* Skip SDRAM initialization if we run from it */
> +	if (get_pc() < SUN50I_DRAM_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');
> +	}

How can we get here with SDRAM uninitialized? Is this for USB or JTAG
boot?

> +
> +	puts_ll("now booting\r\n");
> +	fdt = __dtb_z_sun50i_a64_pinephone_1_2_start + get_runtime_offset();
> +	barebox_arm_entry(SUN50I_DRAM_ADDR, SZ_1G, fdt);
> +
> +reset:
> +	debug_led_rgb(0xff0000);
> +	sun50i_cpu_lowlevel_reset();
> +}
> +
> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone_xload, r0, r1, r2)
> +{
> +
> +	sunxi_egon_header(.text_head_soc_header0);
> +	sunxi_switch_to_aarch64(.text_head_soc_header1, SUN50I_A64_RVBAR_IOMAP);
> +
> +	debug_led_init();
> +	debug_led_rgb(0xff0000);
> +
> +	sun50i_cpu_lowlevel_init();
> +	sun50i_uart_setup();
> +	debug_led_rgb(0xffff00);
> +
> +	relocate_to_current_adr();
> +	setup_c();
> +
> +	sun50i_a64_lpddr3_dram_init();
> +
> +	debug_led_rgb(0xff0000);
> +

You would have to place code here to continue booting, right? In that
case you should add a comment to let the reader know that there's
something missing here.

debug_led_rgb(0xff0000) is the same color you used at the beginning of
the function. Would it make more sense to use a different color?

> +	sun50i_cpu_lowlevel_reset();

Maybe hang() here instead to give the user a chance to see the last
color?

> +}
> diff --git a/arch/arm/configs/pinephone_defconfig b/arch/arm/configs/pinephone_defconfig

In the hope that the config is not only suitable for the pinephone
better name it sunxi_v8_defconfig.


> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -1,7 +1,24 @@
>  if ARCH_SUNXI
>  
> +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

s/Allwiner/Allwinner/

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] 30+ messages in thread

* Re: [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone
  2023-05-30  8:42   ` Sascha Hauer
@ 2023-06-01  5:50     ` Jules Maselbas
  2023-06-01  6:00       ` Ahmad Fatoum
  0 siblings, 1 reply; 30+ messages in thread
From: Jules Maselbas @ 2023-06-01  5:50 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

Hi Sascha,

Thanks for your review

On Tue, May 30, 2023 at 10:42:36AM +0200, Sascha Hauer wrote:
> On Thu, May 25, 2023 at 01:43:26AM +0200, Jules Maselbas wrote:
> > Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> > ---
> >  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 | 104 ++++++++++++++++++++
> >  arch/arm/configs/pinephone_defconfig        |  12 +++
> >  arch/arm/dts/Makefile                       |   1 +
> >  arch/arm/dts/sun50i-a64-pinephone-1_2.dts   |   3 +
> >  arch/arm/mach-sunxi/Kconfig                 |  17 ++++
> >  images/Makefile.sunxi                       |   9 ++
> >  include/mach/sunxi/init.h                   |   4 +
> >  10 files changed, 153 insertions(+)
> >  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 b204c257f6..f4796f5374 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..262d194864
> > --- /dev/null
> > +++ b/arch/arm/boards/pine64-pinephone/lowlevel.c
> > @@ -0,0 +1,104 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +#include <common.h>
> > +#include <debug_ll.h>
> > +#include <linux/sizes.h>
> > +#include <linux/bitops.h>
> > +#include <mach/sunxi/barebox-arm.h>
> 
> This file is missing in this series. Forgot to git add it?
ooops, yes forgot to add it.
There is almost nothing in it:
```
#include <asm/barebox-arm.h>

#define SUN50I_A64_ENTRY_FUNCTION(name, arg0, arg1, arg2) \
	ENTRY_FUNCTION_WITHSTACK(name, SUN50I_PBL_STACK_TOP, arg0, arg1, arg2)
```
that's all

> > +#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>
> > +
> > +#ifdef DEBUG
> > +static void debug_led_rgb(int rgb)
> > +{
> > +	void __iomem *piobase = 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 = SUN50I_CCU_BASE_ADDR;
> > +	void __iomem *piobase = SUN50I_PIO_BASE_ADDR;
> > +
> > +	/* PIO clock enable */
> > +	setbits_le32(ccubase + CCU_BUS_CLK_GATE2, BIT(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
> > +
> > +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone, r0, r1, r2)
> > +{
> > +	extern char __dtb_z_sun50i_a64_pinephone_1_2_start[];
> > +	void *fdt;
> > +	u32 size;
> > +
> > +	sunxi_switch_to_aarch64(.text_head_soc_header2, SUN50I_A64_RVBAR_IOMAP);
> > +
> > +	debug_led_init();
> > +	debug_led_rgb(0xffff00);
> > +
> > +	sun50i_cpu_lowlevel_init();
> > +	sun50i_uart_setup();
> > +
> > +	relocate_to_current_adr();
> > +	setup_c();
> > +
> > +	/* Skip SDRAM initialization if we run from it */
> > +	if (get_pc() < SUN50I_DRAM_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');
> > +	}
> 
> How can we get here with SDRAM uninitialized? Is this for USB or JTAG
> boot?
Yes this is when not chain loaded, when started directly from USB via fel or
via JTAG.

> > +
> > +	puts_ll("now booting\r\n");
> > +	fdt = __dtb_z_sun50i_a64_pinephone_1_2_start + get_runtime_offset();
> > +	barebox_arm_entry(SUN50I_DRAM_ADDR, SZ_1G, fdt);
> > +
> > +reset:
> > +	debug_led_rgb(0xff0000);
> > +	sun50i_cpu_lowlevel_reset();
> > +}
> > +
> > +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone_xload, r0, r1, r2)
> > +{
> > +
> > +	sunxi_egon_header(.text_head_soc_header0);
> > +	sunxi_switch_to_aarch64(.text_head_soc_header1, SUN50I_A64_RVBAR_IOMAP);
> > +
> > +	debug_led_init();
> > +	debug_led_rgb(0xff0000);
> > +
> > +	sun50i_cpu_lowlevel_init();
> > +	sun50i_uart_setup();
> > +	debug_led_rgb(0xffff00);
> > +
> > +	relocate_to_current_adr();
> > +	setup_c();
> > +
> > +	sun50i_a64_lpddr3_dram_init();
> > +
> > +	debug_led_rgb(0xff0000);
> > +
> 
> You would have to place code here to continue booting, right? In that
> case you should add a comment to let the reader know that there's
> something missing here.
I forgot to add the code that search for barebox.bin and continue execution

> 
> debug_led_rgb(0xff0000) is the same color you used at the beginning of
> the function. Would it make more sense to use a different color?
Yes, I'll remove the first red color, I want to reserved it for error conditions.

> > +	sun50i_cpu_lowlevel_reset();
> 
> Maybe hang() here instead to give the user a chance to see the last
> color?
Yes, I put the reset so it will re-enter fel so the board could be reprogrammed
without needing a powercycle, but that's only true if there are no valid boot
entry in eMMC nor SD, IMHO this only makes sense for developpement/debugging.
 
> > +}
> > diff --git a/arch/arm/configs/pinephone_defconfig b/arch/arm/configs/pinephone_defconfig
> 
> In the hope that the config is not only suitable for the pinephone
> better name it sunxi_v8_defconfig.
Yes, currently use this config for the pine64 board, will make this change.

> 
> > +++ b/arch/arm/mach-sunxi/Kconfig
> > @@ -1,7 +1,24 @@
> >  if ARCH_SUNXI
> >  
> > +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
> 
> s/Allwiner/Allwinner/
ack

> 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] 30+ messages in thread

* Re: [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone
  2023-06-01  5:50     ` Jules Maselbas
@ 2023-06-01  6:00       ` Ahmad Fatoum
  2023-06-01  6:19         ` Jules Maselbas
  0 siblings, 1 reply; 30+ messages in thread
From: Ahmad Fatoum @ 2023-06-01  6:00 UTC (permalink / raw)
  To: Jules Maselbas, Sascha Hauer; +Cc: barebox

On 01.06.23 07:50, Jules Maselbas wrote:
> Hi Sascha,
> 
> Thanks for your review
> 
> On Tue, May 30, 2023 at 10:42:36AM +0200, Sascha Hauer wrote:
>> On Thu, May 25, 2023 at 01:43:26AM +0200, Jules Maselbas wrote:
>>> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
>>> ---
>>>  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 | 104 ++++++++++++++++++++
>>>  arch/arm/configs/pinephone_defconfig        |  12 +++
>>>  arch/arm/dts/Makefile                       |   1 +
>>>  arch/arm/dts/sun50i-a64-pinephone-1_2.dts   |   3 +
>>>  arch/arm/mach-sunxi/Kconfig                 |  17 ++++
>>>  images/Makefile.sunxi                       |   9 ++
>>>  include/mach/sunxi/init.h                   |   4 +
>>>  10 files changed, 153 insertions(+)
>>>  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 b204c257f6..f4796f5374 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..262d194864
>>> --- /dev/null
>>> +++ b/arch/arm/boards/pine64-pinephone/lowlevel.c
>>> @@ -0,0 +1,104 @@
>>> +// SPDX-License-Identifier: GPL-2.0+
>>> +#include <common.h>
>>> +#include <debug_ll.h>
>>> +#include <linux/sizes.h>
>>> +#include <linux/bitops.h>
>>> +#include <mach/sunxi/barebox-arm.h>
>>
>> This file is missing in this series. Forgot to git add it?
> ooops, yes forgot to add it.
> There is almost nothing in it:
> ```
> #include <asm/barebox-arm.h>
> 
> #define SUN50I_A64_ENTRY_FUNCTION(name, arg0, arg1, arg2) \
> 	ENTRY_FUNCTION_WITHSTACK(name, SUN50I_PBL_STACK_TOP, arg0, arg1, arg2)
> ```
> that's all

Can this pull in the eGON header as well? That way sun50i entry points
can just look like normal C functions and board porters need not be aware
of the magic.

> 
>>> +#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>
>>> +
>>> +#ifdef DEBUG
>>> +static void debug_led_rgb(int rgb)
>>> +{
>>> +	void __iomem *piobase = 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 = SUN50I_CCU_BASE_ADDR;
>>> +	void __iomem *piobase = SUN50I_PIO_BASE_ADDR;
>>> +
>>> +	/* PIO clock enable */
>>> +	setbits_le32(ccubase + CCU_BUS_CLK_GATE2, BIT(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
>>> +
>>> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone, r0, r1, r2)
>>> +{
>>> +	extern char __dtb_z_sun50i_a64_pinephone_1_2_start[];
>>> +	void *fdt;
>>> +	u32 size;
>>> +
>>> +	sunxi_switch_to_aarch64(.text_head_soc_header2, SUN50I_A64_RVBAR_IOMAP);
>>> +
>>> +	debug_led_init();
>>> +	debug_led_rgb(0xffff00);
>>> +
>>> +	sun50i_cpu_lowlevel_init();
>>> +	sun50i_uart_setup();
>>> +
>>> +	relocate_to_current_adr();
>>> +	setup_c();
>>> +
>>> +	/* Skip SDRAM initialization if we run from it */
>>> +	if (get_pc() < SUN50I_DRAM_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');
>>> +	}
>>
>> How can we get here with SDRAM uninitialized? Is this for USB or JTAG
>> boot?
> Yes this is when not chain loaded, when started directly from USB via fel or
> via JTAG.
> 
>>> +
>>> +	puts_ll("now booting\r\n");
>>> +	fdt = __dtb_z_sun50i_a64_pinephone_1_2_start + get_runtime_offset();
>>> +	barebox_arm_entry(SUN50I_DRAM_ADDR, SZ_1G, fdt);
>>> +
>>> +reset:
>>> +	debug_led_rgb(0xff0000);
>>> +	sun50i_cpu_lowlevel_reset();
>>> +}
>>> +
>>> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone_xload, r0, r1, r2)
>>> +{
>>> +
>>> +	sunxi_egon_header(.text_head_soc_header0);
>>> +	sunxi_switch_to_aarch64(.text_head_soc_header1, SUN50I_A64_RVBAR_IOMAP);
>>> +
>>> +	debug_led_init();
>>> +	debug_led_rgb(0xff0000);
>>> +
>>> +	sun50i_cpu_lowlevel_init();
>>> +	sun50i_uart_setup();
>>> +	debug_led_rgb(0xffff00);
>>> +
>>> +	relocate_to_current_adr();
>>> +	setup_c();
>>> +
>>> +	sun50i_a64_lpddr3_dram_init();
>>> +
>>> +	debug_led_rgb(0xff0000);
>>> +
>>
>> You would have to place code here to continue booting, right? In that
>> case you should add a comment to let the reader know that there's
>> something missing here.
> I forgot to add the code that search for barebox.bin and continue execution
> 
>>
>> debug_led_rgb(0xff0000) is the same color you used at the beginning of
>> the function. Would it make more sense to use a different color?
> Yes, I'll remove the first red color, I want to reserved it for error conditions.
> 
>>> +	sun50i_cpu_lowlevel_reset();
>>
>> Maybe hang() here instead to give the user a chance to see the last
>> color?
> Yes, I put the reset so it will re-enter fel so the board could be reprogrammed
> without needing a powercycle, but that's only true if there are no valid boot
> entry in eMMC nor SD, IMHO this only makes sense for developpement/debugging.
>  
>>> +}
>>> diff --git a/arch/arm/configs/pinephone_defconfig b/arch/arm/configs/pinephone_defconfig
>>
>> In the hope that the config is not only suitable for the pinephone
>> better name it sunxi_v8_defconfig.
> Yes, currently use this config for the pine64 board, will make this change.
> 
>>
>>> +++ b/arch/arm/mach-sunxi/Kconfig
>>> @@ -1,7 +1,24 @@
>>>  if ARCH_SUNXI
>>>  
>>> +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
>>
>> s/Allwiner/Allwinner/
> ack
> 
>> 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 |
>>
> 
> 

-- 
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] 30+ messages in thread

* Re: [PATCH v2 09/13] mci: Add sunxi-mmc driver
  2023-05-30  8:14   ` Sascha Hauer
@ 2023-06-01  6:15     ` Jules Maselbas
  2023-06-01  8:35       ` Sascha Hauer
  0 siblings, 1 reply; 30+ messages in thread
From: Jules Maselbas @ 2023-06-01  6:15 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

On Tue, May 30, 2023 at 10:14:20AM +0200, Sascha Hauer wrote:
> On Thu, May 25, 2023 at 01:43:24AM +0200, 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.
> > 
> > Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> > ---
> > rfc->v1:
> >  - cleanup
> > 
> >  drivers/mci/Kconfig            |   6 +
> >  drivers/mci/Makefile           |   2 +
> >  drivers/mci/sunxi-mmc-common.c | 259 +++++++++++++++++++++++++++++++++
> >  drivers/mci/sunxi-mmc-pbl.c    |  80 ++++++++++
> >  drivers/mci/sunxi-mmc.c        | 173 ++++++++++++++++++++++
> >  drivers/mci/sunxi-mmc.h        | 229 +++++++++++++++++++++++++++++
> >  include/mach/sunxi/xload.h     |  12 ++
> >  7 files changed, 761 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..1de2c8edfa 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,
> > +	  this 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);
> 
> Some (all?) of these forward declarations are unnecessary.
None of them are necessary, they will be removed
 
> > +
> > +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));
> 
> Why memcpy? You can safely assume that dst is sufficiently aligned for
> putting the register value there directly.
I don't recall why, will remove the memcpy

> > +	}
> > +
> > +	return i;
> 
> The caller is not interested in the actual count, better just return 0.
ack

> > +}
> > +
> > +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
> 
> Please drop if that's not needed (or explain why it might be needed)
done

> > +	return i;
> 
> Return 0.
ack

> > +}
> > +
> > +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..af60e7e355
> > --- /dev/null
> > +++ b/drivers/mci/sunxi-mmc-pbl.c
> > @@ -0,0 +1,80 @@
> > +#include <common.h>
> > +
> > +#include <mach/sunxi/xload.h>
> > +#include "sunxi-mmc.h"
> > +#include "sunxi-mmc-common.c"
> > +
> > +#define SECTOR_SIZE			512
> > +#define SUPPORT_MAX_BLOCKS		16U
> > +
> > +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, 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;
> > +}
> > +
> > +int sunxi_mmc_bio_init(struct pbl_bio *bio, void __iomem *base,
> > +		       unsigned int clock, unsigned int slot)
> > +{
> > +	static struct sunxi_mmc_host host;
> > +	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..edfa5d853c
> > --- /dev/null
> > +++ b/drivers/mci/sunxi-mmc.h
> > @@ -0,0 +1,229 @@
> > +/* 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__
> > +#if 0 /* TODO: test this */
> > +#include <asm/system.h>
> > +/* TODO: test this ! */
> > +#define get_time_ns()		get_cntpct()
> > +#define is_timeout(s, t)	(s + (t * get_cntfrq() / 1000000) < get_cntpct()
> 
> You could base your timeout loop on udelay which is available for
> Aarch64 PBL
Can I make a version of is_timeout for aarch64 PBl ? instead of doing a hack in
the mmc driver ?

> 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] 30+ messages in thread

* Re: [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone
  2023-06-01  6:00       ` Ahmad Fatoum
@ 2023-06-01  6:19         ` Jules Maselbas
  2023-06-01  6:36           ` Ahmad Fatoum
  0 siblings, 1 reply; 30+ messages in thread
From: Jules Maselbas @ 2023-06-01  6:19 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: Sascha Hauer, barebox

On Thu, Jun 01, 2023 at 08:00:47AM +0200, Ahmad Fatoum wrote:
> On 01.06.23 07:50, Jules Maselbas wrote:
> > Hi Sascha,
> > 
> > Thanks for your review
> > 
> > On Tue, May 30, 2023 at 10:42:36AM +0200, Sascha Hauer wrote:
> >> On Thu, May 25, 2023 at 01:43:26AM +0200, Jules Maselbas wrote:
> >>> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> >>> ---
> >>>  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 | 104 ++++++++++++++++++++
> >>>  arch/arm/configs/pinephone_defconfig        |  12 +++
> >>>  arch/arm/dts/Makefile                       |   1 +
> >>>  arch/arm/dts/sun50i-a64-pinephone-1_2.dts   |   3 +
> >>>  arch/arm/mach-sunxi/Kconfig                 |  17 ++++
> >>>  images/Makefile.sunxi                       |   9 ++
> >>>  include/mach/sunxi/init.h                   |   4 +
> >>>  10 files changed, 153 insertions(+)
> >>>  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 b204c257f6..f4796f5374 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..262d194864
> >>> --- /dev/null
> >>> +++ b/arch/arm/boards/pine64-pinephone/lowlevel.c
> >>> @@ -0,0 +1,104 @@
> >>> +// SPDX-License-Identifier: GPL-2.0+
> >>> +#include <common.h>
> >>> +#include <debug_ll.h>
> >>> +#include <linux/sizes.h>
> >>> +#include <linux/bitops.h>
> >>> +#include <mach/sunxi/barebox-arm.h>
> >>
> >> This file is missing in this series. Forgot to git add it?
> > ooops, yes forgot to add it.
> > There is almost nothing in it:
> > ```
> > #include <asm/barebox-arm.h>
> > 
> > #define SUN50I_A64_ENTRY_FUNCTION(name, arg0, arg1, arg2) \
> > 	ENTRY_FUNCTION_WITHSTACK(name, SUN50I_PBL_STACK_TOP, arg0, arg1, arg2)
> > ```
> > that's all
> 
> Can this pull in the eGON header as well? That way sun50i entry points
> can just look like normal C functions and board porters need not be aware
> of the magic.
well, I already spent huge amount of time trying to do so and failed so right
now I do not want to try to fit the headers into the entry macro again.
The issue was that I got multiple instances of the each headers because there
where two instance of the entry macro in the same lowlevel.c file...

> > 
> >>> +#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>
> >>> +
> >>> +#ifdef DEBUG
> >>> +static void debug_led_rgb(int rgb)
> >>> +{
> >>> +	void __iomem *piobase = 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 = SUN50I_CCU_BASE_ADDR;
> >>> +	void __iomem *piobase = SUN50I_PIO_BASE_ADDR;
> >>> +
> >>> +	/* PIO clock enable */
> >>> +	setbits_le32(ccubase + CCU_BUS_CLK_GATE2, BIT(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
> >>> +
> >>> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone, r0, r1, r2)
> >>> +{
> >>> +	extern char __dtb_z_sun50i_a64_pinephone_1_2_start[];
> >>> +	void *fdt;
> >>> +	u32 size;
> >>> +
> >>> +	sunxi_switch_to_aarch64(.text_head_soc_header2, SUN50I_A64_RVBAR_IOMAP);
> >>> +
> >>> +	debug_led_init();
> >>> +	debug_led_rgb(0xffff00);
> >>> +
> >>> +	sun50i_cpu_lowlevel_init();
> >>> +	sun50i_uart_setup();
> >>> +
> >>> +	relocate_to_current_adr();
> >>> +	setup_c();
> >>> +
> >>> +	/* Skip SDRAM initialization if we run from it */
> >>> +	if (get_pc() < SUN50I_DRAM_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');
> >>> +	}
> >>
> >> How can we get here with SDRAM uninitialized? Is this for USB or JTAG
> >> boot?
> > Yes this is when not chain loaded, when started directly from USB via fel or
> > via JTAG.
> > 
> >>> +
> >>> +	puts_ll("now booting\r\n");
> >>> +	fdt = __dtb_z_sun50i_a64_pinephone_1_2_start + get_runtime_offset();
> >>> +	barebox_arm_entry(SUN50I_DRAM_ADDR, SZ_1G, fdt);
> >>> +
> >>> +reset:
> >>> +	debug_led_rgb(0xff0000);
> >>> +	sun50i_cpu_lowlevel_reset();
> >>> +}
> >>> +
> >>> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone_xload, r0, r1, r2)
> >>> +{
> >>> +
> >>> +	sunxi_egon_header(.text_head_soc_header0);
> >>> +	sunxi_switch_to_aarch64(.text_head_soc_header1, SUN50I_A64_RVBAR_IOMAP);
> >>> +
> >>> +	debug_led_init();
> >>> +	debug_led_rgb(0xff0000);
> >>> +
> >>> +	sun50i_cpu_lowlevel_init();
> >>> +	sun50i_uart_setup();
> >>> +	debug_led_rgb(0xffff00);
> >>> +
> >>> +	relocate_to_current_adr();
> >>> +	setup_c();
> >>> +
> >>> +	sun50i_a64_lpddr3_dram_init();
> >>> +
> >>> +	debug_led_rgb(0xff0000);
> >>> +
> >>
> >> You would have to place code here to continue booting, right? In that
> >> case you should add a comment to let the reader know that there's
> >> something missing here.
> > I forgot to add the code that search for barebox.bin and continue execution
> > 
> >>
> >> debug_led_rgb(0xff0000) is the same color you used at the beginning of
> >> the function. Would it make more sense to use a different color?
> > Yes, I'll remove the first red color, I want to reserved it for error conditions.
> > 
> >>> +	sun50i_cpu_lowlevel_reset();
> >>
> >> Maybe hang() here instead to give the user a chance to see the last
> >> color?
> > Yes, I put the reset so it will re-enter fel so the board could be reprogrammed
> > without needing a powercycle, but that's only true if there are no valid boot
> > entry in eMMC nor SD, IMHO this only makes sense for developpement/debugging.
> >  
> >>> +}
> >>> diff --git a/arch/arm/configs/pinephone_defconfig b/arch/arm/configs/pinephone_defconfig
> >>
> >> In the hope that the config is not only suitable for the pinephone
> >> better name it sunxi_v8_defconfig.
> > Yes, currently use this config for the pine64 board, will make this change.
> > 
> >>
> >>> +++ b/arch/arm/mach-sunxi/Kconfig
> >>> @@ -1,7 +1,24 @@
> >>>  if ARCH_SUNXI
> >>>  
> >>> +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
> >>
> >> s/Allwiner/Allwinner/
> > ack
> > 
> >> 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 |
> >>
> > 
> > 
> 
> -- 
> 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] 30+ messages in thread

* Re: [PATCH v2 04/13] ARM: lds: Add SoC specific sections to go before .text_head_prologue
  2023-05-24 23:43 ` [PATCH v2 04/13] ARM: lds: Add SoC specific sections to go before .text_head_prologue Jules Maselbas
@ 2023-06-01  6:34   ` Ahmad Fatoum
  2023-06-01 21:20     ` Jules Maselbas
  0 siblings, 1 reply; 30+ messages in thread
From: Ahmad Fatoum @ 2023-06-01  6:34 UTC (permalink / raw)
  To: Jules Maselbas, barebox

On 25.05.23 01:43, 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 soc specific section to be put
> in the very begging of the .text section, before anything else.
> 
> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> ---
> rfc->v2:
>  - fix typo in commit title
>  - replace the use of macro with SORT_BY_NAME(.text_head_soc_header*)
> 
>  arch/arm/lib/pbl.lds.S | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm/lib/pbl.lds.S b/arch/arm/lib/pbl.lds.S
> index 114ec7bc81..059909806e 100644
> --- a/arch/arm/lib/pbl.lds.S
> +++ b/arch/arm/lib/pbl.lds.S
> @@ -27,6 +27,7 @@ SECTIONS
>  	.text      :
>  	{
>  		_stext = .;
> +		*(SORT_BY_NAME(.text_head_soc_header*))

You shouldn't need SORT_BY_NAME here, because they should be just one.

>  		*(.text_head_prologue*)
>  		*(.text_head_entry*)
>  		__bare_init_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] 30+ messages in thread

* Re: [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone
  2023-06-01  6:19         ` Jules Maselbas
@ 2023-06-01  6:36           ` Ahmad Fatoum
  2023-06-01  7:09             ` Ahmad Fatoum
  0 siblings, 1 reply; 30+ messages in thread
From: Ahmad Fatoum @ 2023-06-01  6:36 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: Sascha Hauer, barebox

On 01.06.23 08:19, Jules Maselbas wrote:
> On Thu, Jun 01, 2023 at 08:00:47AM +0200, Ahmad Fatoum wrote:
>> On 01.06.23 07:50, Jules Maselbas wrote:
>>> Hi Sascha,
>>>
>>> Thanks for your review
>>>
>>> On Tue, May 30, 2023 at 10:42:36AM +0200, Sascha Hauer wrote:
>>>> On Thu, May 25, 2023 at 01:43:26AM +0200, Jules Maselbas wrote:
>>>>> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
>>>>> ---
>>>>>  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 | 104 ++++++++++++++++++++
>>>>>  arch/arm/configs/pinephone_defconfig        |  12 +++
>>>>>  arch/arm/dts/Makefile                       |   1 +
>>>>>  arch/arm/dts/sun50i-a64-pinephone-1_2.dts   |   3 +
>>>>>  arch/arm/mach-sunxi/Kconfig                 |  17 ++++
>>>>>  images/Makefile.sunxi                       |   9 ++
>>>>>  include/mach/sunxi/init.h                   |   4 +
>>>>>  10 files changed, 153 insertions(+)
>>>>>  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 b204c257f6..f4796f5374 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..262d194864
>>>>> --- /dev/null
>>>>> +++ b/arch/arm/boards/pine64-pinephone/lowlevel.c
>>>>> @@ -0,0 +1,104 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0+
>>>>> +#include <common.h>
>>>>> +#include <debug_ll.h>
>>>>> +#include <linux/sizes.h>
>>>>> +#include <linux/bitops.h>
>>>>> +#include <mach/sunxi/barebox-arm.h>
>>>>
>>>> This file is missing in this series. Forgot to git add it?
>>> ooops, yes forgot to add it.
>>> There is almost nothing in it:
>>> ```
>>> #include <asm/barebox-arm.h>
>>>
>>> #define SUN50I_A64_ENTRY_FUNCTION(name, arg0, arg1, arg2) \
>>> 	ENTRY_FUNCTION_WITHSTACK(name, SUN50I_PBL_STACK_TOP, arg0, arg1, arg2)
>>> ```
>>> that's all
>>
>> Can this pull in the eGON header as well? That way sun50i entry points
>> can just look like normal C functions and board porters need not be aware
>> of the magic.
> well, I already spent huge amount of time trying to do so and failed so right
> now I do not want to try to fit the headers into the entry macro again.
> The issue was that I got multiple instances of the each headers because there
> where two instance of the entry macro in the same lowlevel.c file...

Do you have your attempt uploaded somewhere? Duplicate instances sounds like
if the section name didn't change. This should be fixable by concatenating the
function name at the end.

> 
>>>
>>>>> +#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>
>>>>> +
>>>>> +#ifdef DEBUG
>>>>> +static void debug_led_rgb(int rgb)
>>>>> +{
>>>>> +	void __iomem *piobase = 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 = SUN50I_CCU_BASE_ADDR;
>>>>> +	void __iomem *piobase = SUN50I_PIO_BASE_ADDR;
>>>>> +
>>>>> +	/* PIO clock enable */
>>>>> +	setbits_le32(ccubase + CCU_BUS_CLK_GATE2, BIT(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
>>>>> +
>>>>> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone, r0, r1, r2)
>>>>> +{
>>>>> +	extern char __dtb_z_sun50i_a64_pinephone_1_2_start[];
>>>>> +	void *fdt;
>>>>> +	u32 size;
>>>>> +
>>>>> +	sunxi_switch_to_aarch64(.text_head_soc_header2, SUN50I_A64_RVBAR_IOMAP);
>>>>> +
>>>>> +	debug_led_init();
>>>>> +	debug_led_rgb(0xffff00);
>>>>> +
>>>>> +	sun50i_cpu_lowlevel_init();
>>>>> +	sun50i_uart_setup();
>>>>> +
>>>>> +	relocate_to_current_adr();
>>>>> +	setup_c();
>>>>> +
>>>>> +	/* Skip SDRAM initialization if we run from it */
>>>>> +	if (get_pc() < SUN50I_DRAM_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');
>>>>> +	}
>>>>
>>>> How can we get here with SDRAM uninitialized? Is this for USB or JTAG
>>>> boot?
>>> Yes this is when not chain loaded, when started directly from USB via fel or
>>> via JTAG.
>>>
>>>>> +
>>>>> +	puts_ll("now booting\r\n");
>>>>> +	fdt = __dtb_z_sun50i_a64_pinephone_1_2_start + get_runtime_offset();
>>>>> +	barebox_arm_entry(SUN50I_DRAM_ADDR, SZ_1G, fdt);
>>>>> +
>>>>> +reset:
>>>>> +	debug_led_rgb(0xff0000);
>>>>> +	sun50i_cpu_lowlevel_reset();
>>>>> +}
>>>>> +
>>>>> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone_xload, r0, r1, r2)
>>>>> +{
>>>>> +
>>>>> +	sunxi_egon_header(.text_head_soc_header0);
>>>>> +	sunxi_switch_to_aarch64(.text_head_soc_header1, SUN50I_A64_RVBAR_IOMAP);
>>>>> +
>>>>> +	debug_led_init();
>>>>> +	debug_led_rgb(0xff0000);
>>>>> +
>>>>> +	sun50i_cpu_lowlevel_init();
>>>>> +	sun50i_uart_setup();
>>>>> +	debug_led_rgb(0xffff00);
>>>>> +
>>>>> +	relocate_to_current_adr();
>>>>> +	setup_c();
>>>>> +
>>>>> +	sun50i_a64_lpddr3_dram_init();
>>>>> +
>>>>> +	debug_led_rgb(0xff0000);
>>>>> +
>>>>
>>>> You would have to place code here to continue booting, right? In that
>>>> case you should add a comment to let the reader know that there's
>>>> something missing here.
>>> I forgot to add the code that search for barebox.bin and continue execution

I originally thought that the BootROM searches for a file in FAT, where it would
make sense to place the follow-up boot stage in FAT as well, but apparently,
it loads from a fixed offset and then first stage bootloader (here PBL) loads
second stage from FAT. This makes me wonder why use FAT at all and not just do
like we do e.g. on i.MX:

  - place full barebox at address where BootROM looks at
  - use minimum PBL size to ensure barebox truncated to SRAM behaves correctly
  - then barebox loads itself from same address into DRAM and reenters PBL
  - either have all of this in unpartitioned space at the start or create a
    partition around it if possible.

Is this possible here as well?

Also, just to be sure: barebox.bin still has a PBL right? The API between PBL
and barebox proper may change, so each barebox proper should have its own
PBL in front.

>>>
>>>>
>>>> debug_led_rgb(0xff0000) is the same color you used at the beginning of
>>>> the function. Would it make more sense to use a different color?
>>> Yes, I'll remove the first red color, I want to reserved it for error conditions.
>>>
>>>>> +	sun50i_cpu_lowlevel_reset();
>>>>
>>>> Maybe hang() here instead to give the user a chance to see the last
>>>> color?
>>> Yes, I put the reset so it will re-enter fel so the board could be reprogrammed
>>> without needing a powercycle, but that's only true if there are no valid boot
>>> entry in eMMC nor SD, IMHO this only makes sense for developpement/debugging.

How about:

if (IS_ENABLED(CONFIG_PANIC_HANG)) {
	hang();
} else {
	/* some delay to see the LED */
	sun50i_cpu_lowlevel_reset();
}
	


-- 
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] 30+ messages in thread

* Re: [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone
  2023-06-01  6:36           ` Ahmad Fatoum
@ 2023-06-01  7:09             ` Ahmad Fatoum
  0 siblings, 0 replies; 30+ messages in thread
From: Ahmad Fatoum @ 2023-06-01  7:09 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: Sascha Hauer, barebox

On 01.06.23 08:36, Ahmad Fatoum wrote:
> On 01.06.23 08:19, Jules Maselbas wrote:
>> On Thu, Jun 01, 2023 at 08:00:47AM +0200, Ahmad Fatoum wrote:
>>> On 01.06.23 07:50, Jules Maselbas wrote:
>>>> Hi Sascha,
>>>>
>>>> Thanks for your review
>>>>
>>>> On Tue, May 30, 2023 at 10:42:36AM +0200, Sascha Hauer wrote:
>>>>> On Thu, May 25, 2023 at 01:43:26AM +0200, Jules Maselbas wrote:
>>>>>> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
>>>>>> ---
>>>>>>  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 | 104 ++++++++++++++++++++
>>>>>>  arch/arm/configs/pinephone_defconfig        |  12 +++
>>>>>>  arch/arm/dts/Makefile                       |   1 +
>>>>>>  arch/arm/dts/sun50i-a64-pinephone-1_2.dts   |   3 +
>>>>>>  arch/arm/mach-sunxi/Kconfig                 |  17 ++++
>>>>>>  images/Makefile.sunxi                       |   9 ++
>>>>>>  include/mach/sunxi/init.h                   |   4 +
>>>>>>  10 files changed, 153 insertions(+)
>>>>>>  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 b204c257f6..f4796f5374 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..262d194864
>>>>>> --- /dev/null
>>>>>> +++ b/arch/arm/boards/pine64-pinephone/lowlevel.c
>>>>>> @@ -0,0 +1,104 @@
>>>>>> +// SPDX-License-Identifier: GPL-2.0+
>>>>>> +#include <common.h>
>>>>>> +#include <debug_ll.h>
>>>>>> +#include <linux/sizes.h>
>>>>>> +#include <linux/bitops.h>
>>>>>> +#include <mach/sunxi/barebox-arm.h>
>>>>>
>>>>> This file is missing in this series. Forgot to git add it?
>>>> ooops, yes forgot to add it.
>>>> There is almost nothing in it:
>>>> ```
>>>> #include <asm/barebox-arm.h>
>>>>
>>>> #define SUN50I_A64_ENTRY_FUNCTION(name, arg0, arg1, arg2) \
>>>> 	ENTRY_FUNCTION_WITHSTACK(name, SUN50I_PBL_STACK_TOP, arg0, arg1, arg2)
>>>> ```
>>>> that's all
>>>
>>> Can this pull in the eGON header as well? That way sun50i entry points
>>> can just look like normal C functions and board porters need not be aware
>>> of the magic.
>> well, I already spent huge amount of time trying to do so and failed so right
>> now I do not want to try to fit the headers into the entry macro again.
>> The issue was that I got multiple instances of the each headers because there
>> where two instance of the entry macro in the same lowlevel.c file...
> 
> Do you have your attempt uploaded somewhere? Duplicate instances sounds like
> if the section name didn't change. This should be fixable by concatenating the
> function name at the end.

Or was the problem that __keep_symbolref wasn't inside a function? In that case,
you may be able have something like:

#define SUN50I_A64_ENTRY_FUNCTION(name, arg0, arg1, arg2) \
	static void __always_inline __egon_##name(ulong, ulong, ulong);
	ENTRY_FUNCTION_WITHSTACK(name, SUN50I_PBL_STACK_TOP, arg0, arg1, arg2) {
		/* do egon stuff here */
		__egon_##name(arg0, arg1, arg2);
	}
	static void __always_inline __egon_##name(ulong arg0, ulong arg1, ulong arg2)

Looking at this, I am wondering why we still have noinline in the ARM64 version.
I need to test if we really need that and drop it if we don't, but that shouldn't
affect your changes.

> 
>>
>>>>
>>>>>> +#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>
>>>>>> +
>>>>>> +#ifdef DEBUG
>>>>>> +static void debug_led_rgb(int rgb)
>>>>>> +{
>>>>>> +	void __iomem *piobase = 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 = SUN50I_CCU_BASE_ADDR;
>>>>>> +	void __iomem *piobase = SUN50I_PIO_BASE_ADDR;
>>>>>> +
>>>>>> +	/* PIO clock enable */
>>>>>> +	setbits_le32(ccubase + CCU_BUS_CLK_GATE2, BIT(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
>>>>>> +
>>>>>> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone, r0, r1, r2)
>>>>>> +{
>>>>>> +	extern char __dtb_z_sun50i_a64_pinephone_1_2_start[];
>>>>>> +	void *fdt;
>>>>>> +	u32 size;
>>>>>> +
>>>>>> +	sunxi_switch_to_aarch64(.text_head_soc_header2, SUN50I_A64_RVBAR_IOMAP);
>>>>>> +
>>>>>> +	debug_led_init();
>>>>>> +	debug_led_rgb(0xffff00);
>>>>>> +
>>>>>> +	sun50i_cpu_lowlevel_init();
>>>>>> +	sun50i_uart_setup();
>>>>>> +
>>>>>> +	relocate_to_current_adr();
>>>>>> +	setup_c();
>>>>>> +
>>>>>> +	/* Skip SDRAM initialization if we run from it */
>>>>>> +	if (get_pc() < SUN50I_DRAM_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');
>>>>>> +	}
>>>>>
>>>>> How can we get here with SDRAM uninitialized? Is this for USB or JTAG
>>>>> boot?
>>>> Yes this is when not chain loaded, when started directly from USB via fel or
>>>> via JTAG.
>>>>
>>>>>> +
>>>>>> +	puts_ll("now booting\r\n");
>>>>>> +	fdt = __dtb_z_sun50i_a64_pinephone_1_2_start + get_runtime_offset();
>>>>>> +	barebox_arm_entry(SUN50I_DRAM_ADDR, SZ_1G, fdt);
>>>>>> +
>>>>>> +reset:
>>>>>> +	debug_led_rgb(0xff0000);
>>>>>> +	sun50i_cpu_lowlevel_reset();
>>>>>> +}
>>>>>> +
>>>>>> +SUN50I_A64_ENTRY_FUNCTION(start_pine64_pinephone_xload, r0, r1, r2)
>>>>>> +{
>>>>>> +
>>>>>> +	sunxi_egon_header(.text_head_soc_header0);
>>>>>> +	sunxi_switch_to_aarch64(.text_head_soc_header1, SUN50I_A64_RVBAR_IOMAP);
>>>>>> +
>>>>>> +	debug_led_init();
>>>>>> +	debug_led_rgb(0xff0000);
>>>>>> +
>>>>>> +	sun50i_cpu_lowlevel_init();
>>>>>> +	sun50i_uart_setup();
>>>>>> +	debug_led_rgb(0xffff00);
>>>>>> +
>>>>>> +	relocate_to_current_adr();
>>>>>> +	setup_c();
>>>>>> +
>>>>>> +	sun50i_a64_lpddr3_dram_init();
>>>>>> +
>>>>>> +	debug_led_rgb(0xff0000);
>>>>>> +
>>>>>
>>>>> You would have to place code here to continue booting, right? In that
>>>>> case you should add a comment to let the reader know that there's
>>>>> something missing here.
>>>> I forgot to add the code that search for barebox.bin and continue execution
> 
> I originally thought that the BootROM searches for a file in FAT, where it would
> make sense to place the follow-up boot stage in FAT as well, but apparently,
> it loads from a fixed offset and then first stage bootloader (here PBL) loads
> second stage from FAT. This makes me wonder why use FAT at all and not just do
> like we do e.g. on i.MX:
> 
>   - place full barebox at address where BootROM looks at
>   - use minimum PBL size to ensure barebox truncated to SRAM behaves correctly
>   - then barebox loads itself from same address into DRAM and reenters PBL
>   - either have all of this in unpartitioned space at the start or create a
>     partition around it if possible.
> 
> Is this possible here as well?
> 
> Also, just to be sure: barebox.bin still has a PBL right? The API between PBL
> and barebox proper may change, so each barebox proper should have its own
> PBL in front.
> 
>>>>
>>>>>
>>>>> debug_led_rgb(0xff0000) is the same color you used at the beginning of
>>>>> the function. Would it make more sense to use a different color?
>>>> Yes, I'll remove the first red color, I want to reserved it for error conditions.
>>>>
>>>>>> +	sun50i_cpu_lowlevel_reset();
>>>>>
>>>>> Maybe hang() here instead to give the user a chance to see the last
>>>>> color?
>>>> Yes, I put the reset so it will re-enter fel so the board could be reprogrammed
>>>> without needing a powercycle, but that's only true if there are no valid boot
>>>> entry in eMMC nor SD, IMHO this only makes sense for developpement/debugging.
> 
> How about:
> 
> if (IS_ENABLED(CONFIG_PANIC_HANG)) {
> 	hang();
> } else {
> 	/* some delay to see the LED */
> 	sun50i_cpu_lowlevel_reset();
> }
> 	
> 
> 

-- 
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] 30+ messages in thread

* Re: [PATCH v2 09/13] mci: Add sunxi-mmc driver
  2023-06-01  6:15     ` Jules Maselbas
@ 2023-06-01  8:35       ` Sascha Hauer
  0 siblings, 0 replies; 30+ messages in thread
From: Sascha Hauer @ 2023-06-01  8:35 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

On Thu, Jun 01, 2023 at 08:15:43AM +0200, Jules Maselbas wrote:
> On Tue, May 30, 2023 at 10:14:20AM +0200, Sascha Hauer wrote:
> > +#ifdef __PBL__
> > > +#if 0 /* TODO: test this */
> > > +#include <asm/system.h>
> > > +/* TODO: test this ! */
> > > +#define get_time_ns()		get_cntpct()
> > > +#define is_timeout(s, t)	(s + (t * get_cntfrq() / 1000000) < get_cntpct()
> > 
> > You could base your timeout loop on udelay which is available for
> > Aarch64 PBL
> Can I make a version of is_timeout for aarch64 PBl ? instead of doing a hack in
> the mmc driver ?

Sure, even better.

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] 30+ messages in thread

* Re: [PATCH v2 04/13] ARM: lds: Add SoC specific sections to go before .text_head_prologue
  2023-06-01  6:34   ` Ahmad Fatoum
@ 2023-06-01 21:20     ` Jules Maselbas
  0 siblings, 0 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-06-01 21:20 UTC (permalink / raw)
  To: Ahmad Fatoum, barebox



On June 1, 2023 8:34:21 AM GMT+02:00, Ahmad Fatoum <a.fatoum@pengutronix.de> wrote:
> On 25.05.23 01:43, 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 soc specific section to be put
> > in the very begging of the .text section, before anything else.
> > 
> > Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> > ---
> > rfc->v2:
> >  - fix typo in commit title
> >  - replace the use of macro with SORT_BY_NAME(.text_head_soc_header*)
> > 
> >  arch/arm/lib/pbl.lds.S | 1 +
> >  1 file changed, 1 insertion(+)
> > 
> > diff --git a/arch/arm/lib/pbl.lds.S b/arch/arm/lib/pbl.lds.S
> > index 114ec7bc81..059909806e 100644
> > --- a/arch/arm/lib/pbl.lds.S
> > +++ b/arch/arm/lib/pbl.lds.S
> > @@ -27,6 +27,7 @@ SECTIONS
> >  	.text      :
> >  	{
> >  		_stext = .;
> > +		*(SORT_BY_NAME(.text_head_soc_header*))
> 
> You shouldn't need SORT_BY_NAME here, because they should be just one.
sadly there isnt one but two:
 - egon header
 - switch to aarch64 header

moreover the egon header is only needed once ... well actually egon header cannot be executed in aarch64 so it should never came after it (hence only once)
This makes two configurations:
 - eGON header followed by switch to aarch64 (xload conf)
 - only te switch to aarch64 (boot from USB/fel)

maybe this wont be an issue if there is only one image (not xload).

> 
> >  		*(.text_head_prologue*)
> >  		*(.text_head_entry*)
> >  		__bare_init_start = .;
> 



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

* Re: [PATCH v2 02/13] scripts: Add Allwinner eGON image support
  2023-05-24 23:43 ` [PATCH v2 02/13] scripts: Add Allwinner eGON image support Jules Maselbas
@ 2023-06-16 22:00   ` Marco Felsch
  2023-06-17  7:25     ` Jules Maselbas
  0 siblings, 1 reply; 30+ messages in thread
From: Marco Felsch @ 2023-06-16 22:00 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

[-- Attachment #1: Type: text/plain, Size: 9666 bytes --]

Hi Jules,

since I work on the D1 support I also had to port the eGON image support
to barebox ^^ Please see my below comments.

On 23-05-25, 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, followed by the actual image, must be located at a fixed
> offset of 8192 bytes (4K) from the start of the disk, either SD; or eMMC.
> 
> 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

Nit: as noted on https://linux-sunxi.org it's not always limited to 32KB.

> the header itself! The jump instruction in the header needs to be patched
> accordingly with the image size.
> 
> Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> ---
> rfc->v2:
>  - removed arch/arm/mach-sunxi/egon_header.c (unused)
>  - reworked egon_mkimage.c to handle images size not multiple of 4bytes
>    and added comments
> 
>  include/mach/sunxi/egon.h |  59 ++++++++++++++++++
>  scripts/Kconfig           |   7 +++
>  scripts/Makefile          |   1 +
>  scripts/egon_mkimage.c    | 122 ++++++++++++++++++++++++++++++++++++++
>  4 files changed, 189 insertions(+)
>  create mode 100644 include/mach/sunxi/egon.h
>  create mode 100644 scripts/egon_mkimage.c
> 
> diff --git a/include/mach/sunxi/egon.h b/include/mach/sunxi/egon.h
> new file mode 100644
> index 0000000000..e00992eb7d
> --- /dev/null
> +++ b/include/mach/sunxi/egon.h
> @@ -0,0 +1,59 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +#ifndef MACH_SUNXI_EGON_H
> +#define MACH_SUNXI_EGON_H
> +
> +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(section) {					\
> +		__section(section) static const struct egon_header hdr= \
> +			{ .branch = EGON_HDR_BRANCH, .magic = "eGON" };	\
> +		__keep_symbolref(hdr);					\
> +	}

Using an additional sections seems a bit odd here. We can just write the
header within the image tool.

> +#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..5983bdb28a
> --- /dev/null
> +++ b/scripts/egon_mkimage.c
> @@ -0,0 +1,122 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#include <stdio.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <linux/kernel.h>
> +
> +#include "../include/mach/sunxi/egon.h"
> +
> +#include "compiler.h"
> +#include "common.h"
> +#include "common.c"
> +
> +#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 = sizeof(*hdr);
> +	size_t bin_size;
> +	size_t img_size;
> +	void *bin;
> +	int fd, ret;
> +
> +	bin = read_file(infile, &bin_size);
> +	if (!bin) {
> +		perror("read_file");
> +		exit(1);
> +	}
> +
> +	/* test if the binary has reserved space for the header */
> +	hdr = bin;
> +	if (hdr->branch == EGON_HDR_BRANCH && memcmp(hdr->magic, "eGON", 4) == 0) {
> +		/* strip/skip existing header */
> +		bin += hdr_size;
> +		bin_size -= hdr_size;
> +	}

Hm.. the 'normal' way is to write the header via the image tool, like it
is done for the i.MX. The infile don't need to have reserved space in
front, instead this tool should prepend the header.

I attached you my two patches adding the eGON image support. Since I
work on the D1 it is RSIC-V related but the eGON image creation should
not differ that much, maybe the offset must be adapted which can be done
via the command line. We could skip this special section handling if my
patches do work for you as well :)

> +
> +	hdr = calloc(1, hdr_size);
> +	if (!hdr) {
> +		perror("malloc");
> +		exit(1);
> +	}
> +
> +	/* total image length must be a multiple of 4K bytes */
> +	img_size = ALIGN(hdr_size + bin_size, 4096);
> +
> +	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 header checksum: */
> +	sum = STAMP_VALUE;
> +	/*  - add the header checksum */
> +	for (p32 = (void *)hdr, i = 0; i < hdr_size / sizeof(uint32_t); i++)
> +		sum += le32_to_cpu(p32[i]);
> +	/*  - add the image checksum */
> +	for (p32 = bin, i = 0; i < bin_size / sizeof(uint32_t); i++)
> +		sum += le32_to_cpu(p32[i]);
> +	/*  - handle image size not aligned on 32-bits */
> +	if (bin_size % sizeof(uint32_t)) {
> +		uint32_t tmp = 0;
> +		size_t rem = bin_size % sizeof(uint32_t);
> +		memcpy(&tmp, bin + (bin_size - rem), rem);
> +		sum += le32_to_cpu(tmp);
> +	}
> +	/* final image will be padded with zeros: doesn't change the checksum */
> +	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 file size on a 4K bytes multiple (img_size),
> +	 * if neccessary ftruncate will pad the end of the file with zeros */
> +	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;
> +}

[-- Attachment #2: 0001-sunxi-add-image-support.patch --]
[-- Type: text/x-diff, Size: 11752 bytes --]

>From 13cc73335b3d35b511087d0dc18085e9f67e03ca Mon Sep 17 00:00:00 2001
From: Marco Felsch <marco.felsch@gmail.com>
Date: Wed, 26 Apr 2023 10:29:59 +0200
Subject: [PATCH 1/2] sunxi: add image support

---
 arch/riscv/Kconfig.socs              |   6 +-
 include/mach/allwinner/sunxi_image.h | 118 +++++++++++++++
 scripts/.gitignore                   |   1 +
 scripts/Kconfig                      |   7 +
 scripts/Makefile                     |   2 +
 scripts/sunxi_egon.c                 | 208 +++++++++++++++++++++++++++
 6 files changed, 339 insertions(+), 3 deletions(-)
 create mode 100644 include/mach/allwinner/sunxi_image.h
 create mode 100644 scripts/sunxi_egon.c

diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
index 2cb0716cd5..9f528df7de 100644
--- a/arch/riscv/Kconfig.socs
+++ b/arch/riscv/Kconfig.socs
@@ -110,13 +110,13 @@ config BOARD_BEAGLEV_BETA
 
 endif
 
-config SOC_ALLWINNER_SUN20I
-	bool "Allwinner Sun20i SoCs"
+config ARCH_SUNXI
+	bool "Allwinner sun20i SoCs"
 	depends on ARCH_RV64I
 	select HAS_DEBUG_LL
 	select HAS_CACHE
 
-if SOC_ALLWINNER_SUN20I
+if ARCH_SUNXI
 
 config BOARD_ALLWINNER_D1
 	bool "Allwinner D1 Nezha"
diff --git a/include/mach/allwinner/sunxi_image.h b/include/mach/allwinner/sunxi_image.h
new file mode 100644
index 0000000000..f90ea600ad
--- /dev/null
+++ b/include/mach/allwinner/sunxi_image.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tangliang@allwinnertech.com>
+ *
+ * Constants and data structures used in Allwinner "eGON" images, as
+ * parsed by the Boot-ROM.
+ *
+ * Shared between mkimage and the SPL.
+ */
+
+#ifndef	SUNXI_IMAGE_H
+#define	SUNXI_IMAGE_H
+
+#include <linux/types.h>
+
+#define BOOT0_MAGIC		"eGON.BT0"
+#define BROM_STAMP_VALUE	0x5f0a6c39
+#define SPL_SIGNATURE		"SPL" /* marks "sunxi" SPL header */
+#define SPL_MAJOR_BITS		3
+#define SPL_MINOR_BITS		5
+#define SPL_VERSION(maj, min)						\
+	((((maj) & ((1U << SPL_MAJOR_BITS) - 1)) << SPL_MINOR_BITS) | \
+	((min) & ((1U << SPL_MINOR_BITS) - 1)))
+
+#define SPL_HEADER_VERSION	SPL_VERSION(0, 2)
+
+#define SPL_ENV_HEADER_VERSION	SPL_VERSION(0, 1)
+#define SPL_DT_HEADER_VERSION	SPL_VERSION(0, 2)
+#define SPL_DRAM_HEADER_VERSION	SPL_VERSION(0, 3)
+
+/* boot head definition from sun4i boot code */
+struct boot_file_head {
+	uint32_t b_instruction;	/* one intruction jumping to real code */
+	uint8_t magic[8];	/* ="eGON.BT0" or "eGON.BT1", not C-style str */
+	uint32_t check_sum;	/* generated by PC */
+	uint32_t length;	/* generated by PC */
+	/*
+	 * We use a simplified header, only filling in what is needed
+	 * by the boot ROM. To be compatible with Allwinner tools we
+	 * would need to implement the proper fields here instead of
+	 * padding.
+	 *
+	 * Actually we want the ability to recognize our "sunxi" variant
+	 * of the SPL. To do so, let's place a special signature into the
+	 * "pub_head_size" field. We can reasonably expect Allwinner's
+	 * boot0 to always have the upper 16 bits of this set to 0 (after
+	 * all the value shouldn't be larger than the limit imposed by
+	 * SRAM size).
+	 * If the signature is present (at 0x14), then we know it's safe
+	 * to use the remaining 8 bytes (at 0x18) for our own purposes.
+	 * (E.g. sunxi-tools "fel" utility can pass information there.)
+	 */
+	union {
+		uint32_t pub_head_size;
+		uint8_t spl_signature[4];
+	};
+	uint32_t fel_script_address;	/* since v0.1, set by sunxi-fel */
+	/*
+	 * If the fel_uEnv_length member below is set to a non-zero value,
+	 * it specifies the size (byte count) of data at fel_script_address.
+	 * At the same time this indicates that the data is in uEnv.txt
+	 * compatible format, ready to be imported via "env import -t".
+	 */
+	uint32_t fel_uEnv_length;	/* since v0.1, set by sunxi-fel */
+	/*
+	 * Offset of an ASCIIZ string (relative to the SPL header), which
+	 * contains the default device tree name (CONFIG_DEFAULT_DEVICE_TREE).
+	 * This is optional and may be set to NULL. Is intended to be used
+	 * by flash programming tools for providing nice informative messages
+	 * to the users.
+	 */
+	uint32_t dt_name_offset;	/* since v0.2, set by mksunxiboot */
+	uint32_t dram_size;		/* in MiB, since v0.3, set by SPL */
+	uint32_t boot_media;		/* written here by the boot ROM */
+	/* A padding area (may be used for storing text strings) */
+	uint32_t string_pool[13];	/* since v0.2, filled by mksunxiboot */
+	/* The header must be a multiple of 32 bytes (for VBAR alignment) */
+};
+
+/* Compile time check to assure proper alignment of structure */
+typedef char boot_file_head_not_multiple_of_32[1 - 2*(sizeof(struct boot_file_head) % 32)];
+
+struct toc0_main_info {
+	uint8_t	name[8];
+	__le32	magic;
+	__le32	checksum;
+	__le32	serial;
+	__le32	status;
+	__le32	num_items;
+	__le32	length;
+	uint8_t	platform[4];
+	uint8_t	reserved[8];
+	uint8_t	end[4];
+} __attribute__((packed));
+
+#define TOC0_MAIN_INFO_NAME		"TOC0.GLH"
+#define TOC0_MAIN_INFO_MAGIC		0x89119800
+#define TOC0_MAIN_INFO_END		"MIE;"
+
+struct toc0_item_info {
+	__le32	name;
+	__le32	offset;
+	__le32	length;
+	__le32	status;
+	__le32	type;
+	__le32	load_addr;
+	uint8_t	reserved[4];
+	uint8_t	end[4];
+} __attribute__((packed));
+
+#define TOC0_ITEM_INFO_NAME_CERT	0x00010101
+#define TOC0_ITEM_INFO_NAME_FIRMWARE	0x00010202
+#define TOC0_ITEM_INFO_NAME_KEY		0x00010303
+#define TOC0_ITEM_INFO_END		"IIE;"
+
+#endif
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 3ca742ac6e..3b827c293c 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -37,3 +37,4 @@ rsatoc
 stm32image
 mvebuimg
 prelink-riscv
+sunxi_egon
diff --git a/scripts/Kconfig b/scripts/Kconfig
index dcd5f32d1d..e9447fac99 100644
--- a/scripts/Kconfig
+++ b/scripts/Kconfig
@@ -49,6 +49,13 @@ config STM32_IMAGE
 	help
 	  This enables building the image creation tool for STM32MP SoCs
 
+config SUNXI_EGON_IMAGE
+	bool "Sunxi 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 Sunxi SoCs
+
 config RK_IMAGE
 	bool "Rockchip image tool" if COMPILE_HOST_TOOLS
 	depends on ARCH_ROCKCHIP || COMPILE_HOST_TOOLS
diff --git a/scripts/Makefile b/scripts/Makefile
index 72ad9ad7a6..c335029c6d 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -41,6 +41,8 @@ hostprogs-always-$(CONFIG_OMAP4_HOSTTOOL_USBBOOT)	+= omap4_usbboot
 HOSTCFLAGS_rk-usb-loader.o = `pkg-config --cflags libusb-1.0`
 HOSTLDLIBS_rk-usb-loader  = `pkg-config --libs libusb-1.0`
 hostprogs-always-$(CONFIG_RK_USB_LOADER)		+= rk-usb-loader
+HOSTCFLAGS_sunxi_egon.o = -I$(srctree) -I$(srctree)/include/mach
+hostprogs-always-$(CONFIG_SUNXI_EGON_IMAGE)             += sunxi_egon
 
 userprogs-always-$(CONFIG_BAREBOXENV_TARGET)		+= bareboxenv-target
 userprogs-always-$(CONFIG_KERNEL_INSTALL_TARGET)	+= kernel-install-target
diff --git a/scripts/sunxi_egon.c b/scripts/sunxi_egon.c
new file mode 100644
index 0000000000..6d74a83ad8
--- /dev/null
+++ b/scripts/sunxi_egon.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018 Arm Ltd.
+ */
+
+#include <unistd.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <sys/stat.h>
+
+#include <allwinner/sunxi_image.h>
+#include "compiler.h"
+
+/*
+ * NAND requires 8K padding. SD/eMMC gets away with 512 bytes,
+ * but let's use the larger padding by default to cover both.
+ */
+#define PAD_SIZE			8192
+
+enum sunxi_arch {
+	SUNXI_ARCH_ARM,
+	SUNXI_ARCH_RISCV,
+	SUNXI_ARCH_UNKOWN
+};
+
+static int write_image(int infd, int outfd, enum sunxi_arch arch,
+		       unsigned long pblsize, unsigned long ofs)
+{
+	struct boot_file_head *header;
+	uint32_t checksum = 0;
+	void *buf, *origbuf;
+	unsigned long size;
+	uint32_t *buf32;
+       	uint32_t value;
+	int ret;
+	int i;
+
+	size = ALIGN(pblsize + sizeof(*header), PAD_SIZE);
+
+	buf = malloc(size);
+	if (!buf)
+		return 1;
+
+	header = buf;
+	buf32 = buf;
+
+	/*
+	 * Different architectures need different first instruction to
+	 * branch to the body.
+	 */
+	switch (arch) {
+	case SUNXI_ARCH_ARM:
+		/* Generate an ARM branch instruction to jump over the header. */
+		value = 0xea000000 | (sizeof(*header) / 4 - 2);
+		header->b_instruction = cpu_to_le32(value);
+		break;
+	case SUNXI_ARCH_RISCV:
+		/*
+		 * Generate a RISC-V JAL instruction with rd=x0
+		 * (pseudo instruction J, jump without side effects).
+		 *
+		 * The following weird bit operation maps imm[20]
+		 * to inst[31], imm[10:1] to inst[30:21],
+		 * imm[11] to inst[20], imm[19:12] to inst[19:12],
+		 * and imm[0] is dropped (because 1-byte RISC-V instruction
+		 * is not allowed).
+		 */
+		value = 0x0000006f |
+			((sizeof(*header) & 0x00100000) << 11) |
+			((sizeof(*header) & 0x000007fe) << 20) |
+			((sizeof(*header) & 0x00000800) << 9) |
+			((sizeof(*header) & 0x000ff000) << 0);
+		header->b_instruction = cpu_to_le32(value);
+		break;
+	default:
+		return 1;
+	}
+
+	memcpy(header->magic, BOOT0_MAGIC, sizeof(header->magic));
+	header->check_sum = cpu_to_le32(BROM_STAMP_VALUE);
+	header->length = cpu_to_le32(size);
+
+	memcpy(header->spl_signature, SPL_SIGNATURE, 3);
+	header->spl_signature[3] = SPL_ENV_HEADER_VERSION;
+
+	/* Calculate the checksum. Yes, it's that simple. */
+	for (i = 0; i < size / 4; i++)
+		checksum += le32_to_cpu(buf32[i]);
+	header->check_sum = cpu_to_le32(checksum);
+
+	origbuf = buf;
+	buf += sizeof(*header);
+	ret = read(infd, buf, pblsize);
+	if (ret > pblsize) {
+		printf("Error: While read: 0x%d > 0x%ld bytes!\n",
+		       ret, pblsize);
+		free(origbuf);
+		return 1;
+	}
+
+	if (ofs)
+		lseek(outfd, ofs, SEEK_SET);
+
+	ret = write(outfd, origbuf, size);
+	if (ret != size) {
+		printf("Error: While write: 0x%d != 0x%ld bytes!\n",
+		       ret, size);
+		free(origbuf);
+		return 1;
+	}
+
+	free(origbuf);
+
+	return 0;
+}
+
+static void usage(const char *prog)
+{
+	printf("\nUsage: %s [options] input_file output_file", prog);
+	printf("\n-a   architecture: riscv, arm");
+	printf("\n-h   this help");
+	printf("\n-o   offset (hex)");
+	printf("\n-p   barebox pbl size in bytes (hex)");
+	printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+	enum sunxi_arch arch = SUNXI_ARCH_UNKOWN;
+	unsigned long pblsize = 0;
+	unsigned long ofs = 0;
+	int infd, outfd;
+	int opt;
+	int ret;
+
+	while ((opt = getopt(argc, argv, "a:o:p:h")) != -1)
+	{
+		switch (opt) {
+		case 'a':
+			if (!strcmp(optarg, "riscv"))
+				arch = SUNXI_ARCH_RISCV;
+			else if (!strcmp(optarg, "arm"))
+				arch = SUNXI_ARCH_ARM;
+			else {
+				printf("Error: unsupported architecture: %s\n",
+				       optarg);
+				return EXIT_FAILURE;
+			}
+			break;
+		case 'o':
+			ofs = strtoul(optarg, NULL, 0);
+			break;
+		case 'p':
+			pblsize = strtoul(optarg, NULL, 0);
+			break;
+		case 'h':
+			usage(argv[0]);
+			return EXIT_SUCCESS;
+		default:
+			usage(argv[0]);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (optind >= argc) {
+		printf("Error: %d\n", argc);
+		usage(argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	infd = open(argv[optind++], O_RDONLY);
+	if (infd < 0) {
+		printf("Error: Open input file\n");
+		return EXIT_FAILURE;
+	}
+
+	if (pblsize == 0) {
+		struct stat st;
+
+		ret = fstat(infd, &st);
+		if (ret) {
+			ret = EXIT_FAILURE;
+			goto out_err_infd;
+		}
+		pblsize = st.st_size;
+	}
+
+	outfd = open(argv[optind], O_WRONLY | O_CREAT, 0666);
+	if (outfd < 0) {
+		printf("Error: Open output file\n");
+		ret = EXIT_FAILURE;
+		goto out_err_infd;
+	}
+
+	ret = write_image(infd, outfd, arch, pblsize, ofs);
+	if (ret)
+		ret = EXIT_FAILURE;
+
+	ret = EXIT_SUCCESS;
+
+	close(outfd);
+out_err_infd:
+	close(infd);
+
+	return ret;
+}
-- 
2.39.2


[-- Attachment #3: 0002-images-add-Sunxi-eGON-image-support.patch --]
[-- Type: text/x-diff, Size: 3856 bytes --]

>From d32de6b6e10378ab430c4c0164dcbb704bb5f70c Mon Sep 17 00:00:00 2001
From: Marco Felsch <marco.felsch@gmail.com>
Date: Wed, 26 Apr 2023 11:57:18 +0200
Subject: [PATCH 2/2] images: add Sunxi eGON image support

Add Makefile.sunxi to make it possible to build sunxi eGON images out of
the box.

Signed-off-by: Marco Felsch <marco.felsch@gmail.com>
---
 images/.gitignore     |  1 +
 images/Makefile       |  3 ++-
 images/Makefile.riscv |  4 ----
 images/Makefile.sunxi | 28 ++++++++++++++++++++++++++++
 scripts/Makefile.lib  |  3 +++
 5 files changed, 34 insertions(+), 5 deletions(-)
 create mode 100644 images/Makefile.sunxi

diff --git a/images/.gitignore b/images/.gitignore
index 1aa9620a42..a3cc17a3bd 100644
--- a/images/.gitignore
+++ b/images/.gitignore
@@ -35,3 +35,4 @@ barebox.sum
 *.stm32
 *.nmon
 *.swapped
+*.egon
diff --git a/images/Makefile b/images/Makefile
index aa5814710f..4f08404e7b 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
@@ -209,5 +210,5 @@ $(flash-list): $(image-y-path)
 clean-files := *.pbl *.pblb *.map start_*.imximg *.img barebox.z start_*.kwbimg \
 	start_*.kwbuartimg *.socfpgaimg *.mlo *.t20img *.t20img.cfg *.t30img \
 	*.t30img.cfg *.t124img *.t124img.cfg *.mlospi *.mlo *.mxsbs *.mxssd \
-	start_*.simximg start_*.usimximg *.zynqimg *.image *.swapped
+	start_*.simximg start_*.usimximg *.zynqimg *.image *.swapped *.egon
 clean-files += pbl.lds
diff --git a/images/Makefile.riscv b/images/Makefile.riscv
index df0e5a9146..0645238c43 100644
--- a/images/Makefile.riscv
+++ b/images/Makefile.riscv
@@ -23,7 +23,3 @@ image-$(CONFIG_BOARD_BEAGLEV) += barebox-beaglev-starlight.img
 pblb-$(CONFIG_BOARD_LITEX_LINUX) += start_litex_linux
 FILE_barebox-litex-linux.img = start_litex_linux.pblb
 image-$(CONFIG_BOARD_LITEX_LINUX) += barebox-litex-linux.img
-
-pblb-$(CONFIG_BOARD_ALLWINNER_D1) += start_allwinner_d1
-FILE_barebox-allwinner-d1.img = start_allwinner_d1.pblb
-image-$(CONFIG_BOARD_ALLWINNER_D1) += barebox-allwinner-d1.img
diff --git a/images/Makefile.sunxi b/images/Makefile.sunxi
new file mode 100644
index 0000000000..77cac3aa86
--- /dev/null
+++ b/images/Makefile.sunxi
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# barebox image generation Makefile for Allwinner Sunxi SoC's
+#
+
+# %.egon - convert into Sunxi egon image
+# --------------------------------------
+
+.SECONDEXPANSION:
+$(obj)/%.egon: $(obj)/$$(FILE_$$(@F)) FORCE
+	$(Q)if [ -z $(FILE_$(@F)) ]; then echo "FILE_$(@F) empty!"; false; fi
+	$(call if_changed,egon_image)
+
+define build_egon_image =
+$(eval
+ifeq ($($(strip $(1))), y)
+	pblb-y += $(strip $(2))
+	FILE_barebox-$(strip $(3)).egon  = $(strip $(2)).pblb
+	OPTS_barebox-$(strip $(3)).egon = -a $(4) -o 0x2000 -p $$($$(patsubst $$(obj)/%.pblb,PBL_MEMORY_SIZE_%,$$<))
+	image-y += barebox-$(strip $(3)).egon
+endif
+)
+endef
+
+# Sunxi RISC-V images
+# -------------------
+
+$(call build_egon_image, CONFIG_BOARD_ALLWINNER_D1, start_allwinner_d1, allwinner-d1, riscv)
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 51beff56ae..0db2a7b716 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -603,6 +603,9 @@ $(obj)/%.bct: $(obj)/%.bct.cfg
 quiet_cmd_stm32_image = STM32-IMG $@
       cmd_stm32_image = $(objtree)/scripts/stm32image $(OPTS_$(@F)) -i $< -o $@
 
+quiet_cmd_egon_image = EGON-IMG $@
+      cmd_egon_image = $(objtree)/scripts/sunxi_egon $(OPTS_$(@F)) $< $@
+
 quiet_cmd_b64dec = B64DEC  $@
       cmd_b64dec = base64 -d $< > $@
 
-- 
2.39.2


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

* Re: [PATCH v2 02/13] scripts: Add Allwinner eGON image support
  2023-06-16 22:00   ` Marco Felsch
@ 2023-06-17  7:25     ` Jules Maselbas
  2023-06-20  4:52       ` Marco Felsch
  2023-06-21  8:26       ` Sascha Hauer
  0 siblings, 2 replies; 30+ messages in thread
From: Jules Maselbas @ 2023-06-17  7:25 UTC (permalink / raw)
  To: Marco Felsch; +Cc: barebox

Hi Marco,

On June 17, 2023 12:00:57 AM GMT+02:00, Marco Felsch <m.felsch@pengutronix.de> wrote:
> Hi Jules,
> 
> since I work on the D1 support I also had to port the eGON image support
> to barebox ^^ Please see my below comments.
> 
> On 23-05-25, 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, followed by the actual image, must be located at a fixed
> > offset of 8192 bytes (4K) from the start of the disk, either SD; or eMMC.
> > 
> > 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
> 
> Nit: as noted on https://linux-sunxi.org it's not always limited to 32KB.
> 
> > the header itself! The jump instruction in the header needs to be patched
> > accordingly with the image size.
> > 
> > Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
> > ---
> > rfc->v2:
> >  - removed arch/arm/mach-sunxi/egon_header.c (unused)
> >  - reworked egon_mkimage.c to handle images size not multiple of 4bytes
> >    and added comments
> > 
> >  include/mach/sunxi/egon.h |  59 ++++++++++++++++++
> >  scripts/Kconfig           |   7 +++
> >  scripts/Makefile          |   1 +
> >  scripts/egon_mkimage.c    | 122 ++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 189 insertions(+)
> >  create mode 100644 include/mach/sunxi/egon.h
> >  create mode 100644 scripts/egon_mkimage.c
> > 
> > diff --git a/include/mach/sunxi/egon.h b/include/mach/sunxi/egon.h
> > new file mode 100644
> > index 0000000000..e00992eb7d
> > --- /dev/null
> > +++ b/include/mach/sunxi/egon.h
> > @@ -0,0 +1,59 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +#ifndef MACH_SUNXI_EGON_H
> > +#define MACH_SUNXI_EGON_H
> > +
> > +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(section) {					\
> > +		__section(section) static const struct egon_header hdr= \
> > +			{ .branch = EGON_HDR_BRANCH, .magic = "eGON" };	\
> > +		__keep_symbolref(hdr);					\
> > +	}
> 
> Using an additional sections seems a bit odd here. We can just write the
> header within the image tool.
That's what I wanted to do in the first place but I struggled a lot to get barebox relocation working.
Having the eGON header embedded in the .text (since the header is loaded by bootrom) is the only solution i found to get the relocation working.
I am all for a better way but I really whish to have a first version applied.

> 
> > +#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..5983bdb28a
> > --- /dev/null
> > +++ b/scripts/egon_mkimage.c
> > @@ -0,0 +1,122 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +
> > +#include <stdio.h>
> > +#include <errno.h>
> > +#include <stdlib.h>
> > +#include <stdint.h>
> > +#include <string.h>
> > +#include <linux/kernel.h>
> > +
> > +#include "../include/mach/sunxi/egon.h"
> > +
> > +#include "compiler.h"
> > +#include "common.h"
> > +#include "common.c"
> > +
> > +#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 = sizeof(*hdr);
> > +	size_t bin_size;
> > +	size_t img_size;
> > +	void *bin;
> > +	int fd, ret;
> > +
> > +	bin = read_file(infile, &bin_size);
> > +	if (!bin) {
> > +		perror("read_file");
> > +		exit(1);
> > +	}
> > +
> > +	/* test if the binary has reserved space for the header */
> > +	hdr = bin;
> > +	if (hdr->branch == EGON_HDR_BRANCH && memcmp(hdr->magic, "eGON", 4) == 0) {
> > +		/* strip/skip existing header */
> > +		bin += hdr_size;
> > +		bin_size -= hdr_size;
> > +	}
> 
> Hm.. the 'normal' way is to write the header via the image tool, like it
> is done for the i.MX. The infile don't need to have reserved space in
> front, instead this tool should prepend the header.
Yes, thisis only to accomodate for having the header in the .text (see my reply above)

> I attached you my two patches adding the eGON image support. Since I
> work on the D1 it is RSIC-V related but the eGON image creation should
> not differ that much, maybe the offset must be adapted which can be done
> via the command line. We could skip this special section handling if my
> patches do work for you as well :)
Sounds nice, I don't when I will have time to test this.

Does the eGON header starts will a risc-v jump instruction ? Or is it still an arm32 insn ?


Cheers



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

* Re: [PATCH v2 02/13] scripts: Add Allwinner eGON image support
  2023-06-17  7:25     ` Jules Maselbas
@ 2023-06-20  4:52       ` Marco Felsch
  2023-06-21  8:26       ` Sascha Hauer
  1 sibling, 0 replies; 30+ messages in thread
From: Marco Felsch @ 2023-06-20  4:52 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

[-- Attachment #1: Type: text/plain, Size: 5116 bytes --]

On 23-06-17, Jules Maselbas wrote:
> Hi Marco,
> 
> On June 17, 2023 12:00:57 AM GMT+02:00, Marco Felsch <m.felsch@pengutronix.de> wrote:
> > Hi Jules,
> > 
> > since I work on the D1 support I also had to port the eGON image support
> > to barebox ^^ Please see my below comments.
> > 
> > On 23-05-25, Jules Maselbas wrote:

...

> > > +#define EGON_HDR_BRANCH (0xea000000 | (sizeof(struct egon_header) / 4 - 2))
> > > +#define sunxi_egon_header(section) {					\
> > > +		__section(section) static const struct egon_header hdr= \
> > > +			{ .branch = EGON_HDR_BRANCH, .magic = "eGON" };	\
> > > +		__keep_symbolref(hdr);					\
> > > +	}
> > 
> > Using an additional sections seems a bit odd here. We can just write the
> > header within the image tool.
>
> That's what I wanted to do in the first place but I struggled a lot to
> get barebox relocation working.
> Having the eGON header embedded in the .text (since the header is
> loaded by bootrom) is the only solution i found to get the relocation
> working.

Hm.. at least on RISC-V I had no problems with
relocate_to_current_adr(). Also checking the ARM relocation code does
not show why the header should be a problem if
relocate_to_current_adr() is used.

> I am all for a better way but I really whish to have a first version
> applied.
> 
> > 
> > > +#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..5983bdb28a
> > > --- /dev/null
> > > +++ b/scripts/egon_mkimage.c
> > > @@ -0,0 +1,122 @@
> > > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > > +
> > > +#include <stdio.h>
> > > +#include <errno.h>
> > > +#include <stdlib.h>
> > > +#include <stdint.h>
> > > +#include <string.h>
> > > +#include <linux/kernel.h>
> > > +
> > > +#include "../include/mach/sunxi/egon.h"
> > > +
> > > +#include "compiler.h"
> > > +#include "common.h"
> > > +#include "common.c"
> > > +
> > > +#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 = sizeof(*hdr);
> > > +	size_t bin_size;
> > > +	size_t img_size;
> > > +	void *bin;
> > > +	int fd, ret;
> > > +
> > > +	bin = read_file(infile, &bin_size);
> > > +	if (!bin) {
> > > +		perror("read_file");
> > > +		exit(1);
> > > +	}
> > > +
> > > +	/* test if the binary has reserved space for the header */
> > > +	hdr = bin;
> > > +	if (hdr->branch == EGON_HDR_BRANCH && memcmp(hdr->magic, "eGON", 4) == 0) {
> > > +		/* strip/skip existing header */
> > > +		bin += hdr_size;
> > > +		bin_size -= hdr_size;
> > > +	}
> > 
> > Hm.. the 'normal' way is to write the header via the image tool, like it
> > is done for the i.MX. The infile don't need to have reserved space in
> > front, instead this tool should prepend the header.
>
> Yes, thisis only to accomodate for having the header in the .text (see
> my reply above)

Thanks for the explanation.

> > I attached you my two patches adding the eGON image support. Since I
> > work on the D1 it is RSIC-V related but the eGON image creation should
> > not differ that much, maybe the offset must be adapted which can be done
> > via the command line. We could skip this special section handling if my
> > patches do work for you as well :)
>
> Sounds nice, I don't when I will have time to test this.

No worries, I will start picking your patches as well for the D1
lowlevel support.

> Does the eGON header starts will a risc-v jump instruction ? Or is it
> still an arm32 insn ?

Yep, there is a special handling for each architecture. That been said,
I noticed that I had a fixup patch ontop of my image generation tool,
which I attached.

[-- Attachment #2: 0001-fixup-sunxi-add-image-support.patch --]
[-- Type: text/x-diff, Size: 3159 bytes --]

>From 1e9e0c9515e644abeda066c5387d1338d927cbda Mon Sep 17 00:00:00 2001
From: Marco Felsch <marco.felsch@gmail.com>
Date: Wed, 26 Apr 2023 21:51:26 +0200
Subject: [PATCH] fixup! sunxi: add image support

---
 scripts/sunxi_egon.c | 40 +++++++++++++++++++++-------------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/scripts/sunxi_egon.c b/scripts/sunxi_egon.c
index 6d74a83ad8..b339955295 100644
--- a/scripts/sunxi_egon.c
+++ b/scripts/sunxi_egon.c
@@ -29,11 +29,12 @@ static int write_image(int infd, int outfd, enum sunxi_arch arch,
 		       unsigned long pblsize, unsigned long ofs)
 {
 	struct boot_file_head *header;
+	unsigned int header_sz;
 	uint32_t checksum = 0;
-	void *buf, *origbuf;
 	unsigned long size;
 	uint32_t *buf32;
        	uint32_t value;
+	void *buf;
 	int ret;
 	int i;
 
@@ -43,8 +44,10 @@ static int write_image(int infd, int outfd, enum sunxi_arch arch,
 	if (!buf)
 		return 1;
 
+	memset(buf, 0, size);
+
 	header = buf;
-	buf32 = buf;
+	header_sz = sizeof(*header);
 
 	/*
 	 * Different architectures need different first instruction to
@@ -53,7 +56,7 @@ static int write_image(int infd, int outfd, enum sunxi_arch arch,
 	switch (arch) {
 	case SUNXI_ARCH_ARM:
 		/* Generate an ARM branch instruction to jump over the header. */
-		value = 0xea000000 | (sizeof(*header) / 4 - 2);
+		value = 0xea000000 | (header_sz / 4 - 2);
 		header->b_instruction = cpu_to_le32(value);
 		break;
 	case SUNXI_ARCH_RISCV:
@@ -68,10 +71,10 @@ static int write_image(int infd, int outfd, enum sunxi_arch arch,
 		 * is not allowed).
 		 */
 		value = 0x0000006f |
-			((sizeof(*header) & 0x00100000) << 11) |
-			((sizeof(*header) & 0x000007fe) << 20) |
-			((sizeof(*header) & 0x00000800) << 9) |
-			((sizeof(*header) & 0x000ff000) << 0);
+			((header_sz & 0x00100000) << 11) |
+			((header_sz & 0x000007fe) << 20) |
+			((header_sz & 0x00000800) << 9) |
+			((header_sz & 0x000ff000) << 0);
 		header->b_instruction = cpu_to_le32(value);
 		break;
 	default:
@@ -85,33 +88,32 @@ static int write_image(int infd, int outfd, enum sunxi_arch arch,
 	memcpy(header->spl_signature, SPL_SIGNATURE, 3);
 	header->spl_signature[3] = SPL_ENV_HEADER_VERSION;
 
-	/* Calculate the checksum. Yes, it's that simple. */
-	for (i = 0; i < size / 4; i++)
-		checksum += le32_to_cpu(buf32[i]);
-	header->check_sum = cpu_to_le32(checksum);
-
-	origbuf = buf;
-	buf += sizeof(*header);
-	ret = read(infd, buf, pblsize);
+	ret = read(infd, buf + header_sz, pblsize);
 	if (ret > pblsize) {
 		printf("Error: While read: 0x%d > 0x%ld bytes!\n",
 		       ret, pblsize);
-		free(origbuf);
+		free(buf);
 		return 1;
 	}
 
+	/* Calculate the checksum. Yes, it's that simple. */
+	buf32 = buf;
+	for (i = 0; i < size / 4; i++)
+		checksum += le32_to_cpu(buf32[i]);
+	header->check_sum = cpu_to_le32(checksum);
+
 	if (ofs)
 		lseek(outfd, ofs, SEEK_SET);
 
-	ret = write(outfd, origbuf, size);
+	ret = write(outfd, buf, size);
 	if (ret != size) {
 		printf("Error: While write: 0x%d != 0x%ld bytes!\n",
 		       ret, size);
-		free(origbuf);
+		free(buf);
 		return 1;
 	}
 
-	free(origbuf);
+	free(buf);
 
 	return 0;
 }
-- 
2.39.2


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

* Re: [PATCH v2 02/13] scripts: Add Allwinner eGON image support
  2023-06-17  7:25     ` Jules Maselbas
  2023-06-20  4:52       ` Marco Felsch
@ 2023-06-21  8:26       ` Sascha Hauer
  1 sibling, 0 replies; 30+ messages in thread
From: Sascha Hauer @ 2023-06-21  8:26 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: Marco Felsch, barebox

On Sat, Jun 17, 2023 at 09:25:41AM +0200, Jules Maselbas wrote:
> Hi Marco,
> 
> On June 17, 2023 12:00:57 AM GMT+02:00, Marco Felsch <m.felsch@pengutronix.de> wrote:
> > > +	 * 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(section) {					\
> > > +		__section(section) static const struct egon_header hdr= \
> > > +			{ .branch = EGON_HDR_BRANCH, .magic = "eGON" };	\
> > > +		__keep_symbolref(hdr);					\
> > > +	}
> > 
> > Using an additional sections seems a bit odd here. We can just write the
> > header within the image tool.
> That's what I wanted to do in the first place but I struggled a lot to get barebox relocation working.
> Having the eGON header embedded in the .text (since the header is
> loaded by bootrom) is the only solution i found to get the relocation
> working.

The barebox image (the .text section) needs a 4k alignment for relocation.
If you are pasting just a few bytes before the image then you are
breaking this alignment. Can you blow up the header to 4k?

You only have 32k space initially and I don't know how tight in space
you are already, so this might or or might not be an option.

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] 30+ messages in thread

end of thread, other threads:[~2023-06-21  8:27 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-24 23:43 [PATCH v2 00/13] Add support for Allwinner (sunxi) A64 SoC Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 01/13] Documentation: sunxi: Add some documentation Jules Maselbas
2023-05-29  9:24   ` Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 02/13] scripts: Add Allwinner eGON image support Jules Maselbas
2023-06-16 22:00   ` Marco Felsch
2023-06-17  7:25     ` Jules Maselbas
2023-06-20  4:52       ` Marco Felsch
2023-06-21  8:26       ` Sascha Hauer
2023-05-24 23:43 ` [PATCH v2 03/13] ARM: sunxi: introduce mach-sunxi Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 04/13] ARM: lds: Add SoC specific sections to go before .text_head_prologue Jules Maselbas
2023-06-01  6:34   ` Ahmad Fatoum
2023-06-01 21:20     ` Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 05/13] ARM: sunxi: Add lowlevel switch to aarch64 Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 06/13] ARM: sunxi: Add debug_ll Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 07/13] clk: Add clock driver for sun50i-a64 Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 08/13] pinctrl: Add sun50i-a64 pinctrl driver Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 09/13] mci: Add sunxi-mmc driver Jules Maselbas
2023-05-30  8:14   ` Sascha Hauer
2023-06-01  6:15     ` Jules Maselbas
2023-06-01  8:35       ` Sascha Hauer
2023-05-24 23:43 ` [PATCH v2 10/13] ARM: sunxi: Add sun50i SDRAM init Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 11/13] ARM: boards: sunxi: Add initial support for the pinephone Jules Maselbas
2023-05-30  8:42   ` Sascha Hauer
2023-06-01  5:50     ` Jules Maselbas
2023-06-01  6:00       ` Ahmad Fatoum
2023-06-01  6:19         ` Jules Maselbas
2023-06-01  6:36           ` Ahmad Fatoum
2023-06-01  7:09             ` Ahmad Fatoum
2023-05-24 23:43 ` [PATCH v2 12/13] ARM: boards: sunxi: Add pine64 board Jules Maselbas
2023-05-24 23:43 ` [PATCH v2 13/13] ARM: sunxi: xload: Add helpers for chain-loading from SD-card Jules Maselbas

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