Difference between revisions of "User:Oogwaymaki/PinePhone Multiboot"

From PINE64
Jump to navigation Jump to search
(How To Create MultiBoot Configuration In Pinephone)
 
Line 1,814: Line 1,814:
+
+
</pre>
</pre>
= How To Flash A Custom KERNEL from MEGI: Using Debian =

Revision as of 21:36, 29 June 2020

Tips on creating a Multi Boot Configuration for an SDCARD and EMMC

Manual MultiBoot Configuration

First, and foremost these steps do the following for you:

  • HOLD VOLUME UP AND POWER => BOOT JUMPDRIVE
  • HOLD VOLUME DOWN AND POWER => BOOT YOUR SD CARD OS
  • HOLD NOTHING REGULAR BOOT => BOOT EMMC DRIVE


Below is series of steps, which will enable MultiBoot on an external SD Card for the PinePhone. You will not be required to edit the EMMC in any way at all, leaving the the EMMC intact with original software or with your own Primary OS, that is only for stable use of the phone, without worrying about breaking your permanent software.

You could potentially use this for 3 OS's, but ideally only 2 with this current instruction set because you want to keep JumpDrive to make it easy to mount the cards with an option on booting the device.


After reading and following these steps, you should able to do the following with a bit more research and a few tweaks:

  • Change the PostMarketOS to Mobian or another Distro of your choice pretty easily.
  • Use these instructions to create a More Advanced boot structure, using Volume Up and Volume Down with a menu system (This endeavour may take you a while this is a programming exercise within Uboot.scr)

Essentially you are experimenting without the risk of complications with your EMMC.

For example of these instructions , with slight differences for Mobian see below:

https://www.youtube.com/user/enterusernamo/featured

Summary

After following these steps:


  • You'll have a Custom UBOOT (Crust ENABLED) that allows MultiBOOT Using 3 different OS's depending on key combo of volume up / volume down or none pressing on boot
  • You'll be able to experiment on SD Card without Touching your EMMC
  • You'll learn how to take a part a PINE PHONE IMAGE (most, and put each OS On one partition

All the information here, has been discussed in the open source community on web sites. I have simply developed a process on how to do this on the pine phone.

We use utilities such as Cross Compilation, Uboot, Patches, which will be credited.

First step, make sure you have a cross compiler for your Computer, for particularly the arm device, you will need to download a few packages, I assume we're using Debian Linux (if your using another OS you can find similar commands by searching on the web or to make it simpler download a docker Debian image, and install the pre-requisites which I will list here for Debian)

You an also use vagrant/virtual box VMware or other virtualization platforms.

the tools mentioned are the following (in my words possible not maintainers):

Below are all more or less free:


Vagrant: A multi/functional wrapper tool to make using Virtual Box, VMWARE , and other virtualization components easier. There are default images(Preconfigured operating systems you can download) or you can make your own.

VirtualBox: This is a free virtualization tool, I don't think you can use prebundled images as without vagrant, but it allows you to freshly install operating systems from disk. It's like using a computer within a computer to install an OS.

Docker: A linux container system, that allows you to have many different snapshots running in the same linux environment. Similar to jails, quicker and faster than Vagrant and VirtualBox, and not an actual computer emulation....

Commercial Tools available, non free:

VMWARE...numerous ones.


pick your poison! j/k

Installing Dependencies

First please install these Dependencies:

apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu build-essential autoconf libtool cmake pkg-config git python-dev swig3.0 libpcre3-dev nodejs-dev crossbuild-essential-arm64 git losetup rsync

The following main sources are needed:

This will download quite a few sources needed for Uboot and existing OS's available to PinePhone

https://github.com/crust-firmware/meta.git

We will also apply this patch:

https://megous.com/git/u-boot/patch/?id=0ab6225154c3d8b74f06fb3b181b52a9a64b4602

(I will attach the one I used I had to modify the above patch to work with the code.)

The crust-firmware also says you need to download the or1k compiler:

https://musl.cc/or1k-linux-musl-cross.tgz

I suggest untarring this in the /opt folder.

Preparing Crust

  • You will see we are patching crust, from the original code, the reason why we are doing this is to ENABLE the Volume UP and VOLUME DOWN Environment variables
  • so the script knows when a user presses up and down on the volume button on pine phone
  • Without the patch this functionality isn't there.
  • Feel free to look through the patch to see how its done, I did not make this patch its found on a gmail thread through another site. I just cleaned it up so it won't have rejects.

Once you have these dependencies:

# cd /usr/src
# git clone https://github.com/crust-firmware/meta.git
# cd /usr/src/meta

Edit the following file Make file

# Cross compiler
CROSS_aarch64	 = aarch64-linux-musl-
CROSS_or1k	 = or1k-linux-musl-

# General options
DEBUG		?= 0
REPRODUCIBLE	?= 0

# Board selection
BOARD		?= pinebook

Example:

# Cross compiler
CROSS_aarch64	 =  /usr/bin/aarch64-linux-gnu-
CROSS_or1k	 = /opt/or1k-linux-musl-cross/bin/or1k-linux-musl-

# General options
DEBUG		?= 0
REPRODUCIBLE	?= 0

# Board selection
BOARD		?= pinephone
# make

(this should take a 10-30 mins depending on speed..I hope less than 30 mins!!)k

# cd u-boot
# make clean
# cd ..
 FIND THIS PATCH AT END OF DOCUMENT CUT AND PASTE INTO FILE
# patch -p1 < volumecontrol.patch
# make

Partitioning

now, go to your sdcard (use a CLEAN scard NO Partitions)

fdisk /dev/sd#

create 4 primary partitions, the 1rst one should be the "boot" partition containing Uboot stuff, I'd suggest a gigabyte the other 3 could be your choice

After creating partitions, flash the image:

# cd build/pinephone

Do not use wrong drive, and copy exactly as this erases and puts the new Uboot on the sdcard the first 512 sectors are partition you do NOT want to write that or you lose your partition

# dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8 conv=sync

Make sure your disk is OK.

# fdisk -l
Disk model: e microSD       
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x50fdd1d4

Device     Boot     Start       End   Sectors  Size Id Type
/dev/sdb1            2048   2099199   2097152    1G 83 Linux
/dev/sdb2         2099200  65013759  62914560   30G 83 Linux
/dev/sdb3        65013760 127928319  62914560   30G 83 Linux
/dev/sdb4       127928320 251131903 123203584 58.8G 83 Linux
root@wifirouter:/usr/src# 

If it looks like above its ok.

Flashing JumpDrive

Lets start with the first OS were going to use:

JumpDrive (allows booting the EMMC and fixing it if we break it!!!)

Download it here:

https://github.com/dreemurrs-embedded/Jumpdrive/releases/download/0.4/pine64-pinephone.img.xz

then use losetup to mount the image to copy the files needed

root@wifirouter:/home/dave# losetup -P -f pine64-pinephone.img 

it'll mount a loop back

root@wifirouter:/home/dave# ls /dev/loop0*
/dev/loop0  /dev/loop0p1

Mount there first image:

root@wifirouter:/home/dave# mkdir /media/jumpdrive
root@wifirouter:/home/dave# mount /dev/loop0p1 /media/jumpdrive/

mount the sd card:

root@wifirouter:/media# mkdir /media/boot
root@wifirouter:/media# fdisk -l

Your sd card should look like below:


Disk /dev/sdb: 119.8 GiB, 128579534848 bytes, 251131904 sectors
Disk model: e microSD       
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x50fdd1d4

Device     Boot     Start       End   Sectors  Size Id Type
/dev/sdb1            2048   2099199   2097152    1G 83 Linux
/dev/sdb2         2099200  65013759  62914560   30G 83 Linux
/dev/sdb3        65013760 127928319  62914560   30G 83 Linux
/dev/sdb4       127928320 251131903 123203584 58.8G 83 Linux
root@wifirouter:/media# mount /dev/sdb1 /media/boot/
root@wifirouter:/media# cd /media/boot
root@wifirouter:/media/boot# mkdir boot
root@wifirouter:/media/boot# mkdir -p multiboot/jumpdrive
root@wifirouter:/media/boot# cp * multiboot/jumpdrive 

End result should be like this ( this is complete file)

root@wifirouter:/media/boot/multiboot/jumpdrive# ls -la
total 4988
drwxr-xr-x 2 root  root     4096 Jun 25 05:51 .
drwxr-xr-x 3 root  root     4096 Jun 25 05:46 ..
-rwxr-xr-x 1 32011 32011 3991895 Jun 25 05:45 Image.gz
-rwxr-xr-x 1 32011 32011 1068435 Jun 25 05:45 initramfs.gz
-rwxr-xr-x 1 32011 32011   33457 Jun 25 05:45 sun50i-a64-pinephone.dtb
root@wifirouter:/media/boot/multiboot/jumpdrive# pwd

Now we need to create the boot.txt for uboot to load:

Your text file should look like this:

setenv kernel_addr_z 0x44080000

setenv bootargs loglevel=0 silent console=tty0 vt.global_cursor_default=0

if test "${volume_key}" = "up" ; then 
echo "yes im here"
setenv devtype mmc
setenv devnum 0
setenv distro_bootpart 1
if load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_z} /multiboot/jumpdrive/Image.gz; then
  unzip ${kernel_addr_z} ${kernel_addr_r}
  if load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} /multiboot/jumpdrive/sun50i-a64-pinephone.dtb; then
    if load ${devtype} ${devnum}:${distro_bootpart} ${ramdisk_addr_r} /multiboot/jumpdrive/initramfs.gz; then
      booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r};
    else
      booti ${kernel_addr_r} - ${fdt_addr_r};
    fi;
  fi;
fi
exit
fi
if test "${volume_key}" = "down" ; then 
echo "down"
ext4load mmc 0:2 ${kernel_addr_r} /boot/Image
ext4load mmc 0:2 ${fdt_addr_r} /boot/dtb/sun50i-a64-pinephone-1.2.dtb
setenv bootargs root=/dev/mmcblk0p2 rw console=ttyS0,115200 console=tty1 initrd=/boot/initrd.img quiet
booti ${kernel_addr_r} - ${fdt_addr_r} 
else
setenv devtype mmc
setenv devnum 1
setenv partnum 4
load ${devtype} ${devnum}:2 ${scriptaddr} /boot.scr
source ${scriptaddr}
fi

Compile the boot script:

mkimage -C none -A arm -T script -d boot.text boot.scr
cp boot.scr /media/boot/boot

This should now be working with JumpDrive..

Flashing PostMarketOS

Now lets download PostMarketOS

# wget http://images.postmarketos.org/pinephone/pine-pinephone-20200624-phosh-crust.img.xz
root@wifirouter:/home/dave# unxz pine-pinephone-20200624-phosh-crust.img.xz
root@wifirouter:/home/dave# losetup -P -f pine-pinephone-20200624-phosh-crust.img
root@wifirouter:/home/dave# ls /dev/loop1*
/dev/loop1  /dev/loop1p1  /dev/loop1p2
root@wifirouter:/home/dave# mount /dev/loop1p1 /media/postmarketboot/
root@wifirouter:/home/dave# mount /dev/loop1p2 /media/postmarket/
root@wifirouter:/home/dave# fdisk -l /dev/loop1
Disk /dev/loop1: 1.4 GiB, 1504706560 bytes, 2938880 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0eef1887

Device       Boot  Start     End Sectors  Size Id Type
/dev/loop1p1 *      2048  249855  247808  121M 83 Linux
/dev/loop1p2      249856 2938879 2689024  1.3G 83 Linux
root@wifirouter:/media# ls -la /media/postmarketboot/
total 40202
drwxr-xr-x  3 root root     1024 Jun 24 08:40 .
drwxr-xr-x 16 root root     4096 Jun 25 21:24 ..
-rw-r--r--  1 root root      949 Jun 17 05:19 boot.scr
-rw-r--r--  1 root root  1431266 Jun 24 08:40 initramfs-postmarketos-allwinner
-rw-r--r--  1 root root  6875596 Jun 24 08:40 initramfs-postmarketos-allwinner-extra
drwx------  2 root root    12288 Jun 24 08:42 lost+found
-rw-r--r--  1 root root    33835 Jun 24 08:40 sun50i-a64-pinephone-1.1.dtb
-rw-r--r--  1 root root 15601736 Jun 24 08:40 uImage-postmarketos-allwinner
-rw-r--r--  1 root root  1431330 Jun 24 08:40 uInitrd-postmarketos-allwinner
-rw-r--r--  1 root root 15601672 Jun 21 16:57 vmlinuz-postmarketos-allwinner

Take a look at how the boot.scr works with postmarketos:

root@wifirouter:/media# cat /media/postmarketboot/boot.scr 
'Vi?_^??;u??npostmarketosmgpio set 98
gpio set 114

if test ${mmc_bootdev} -eq 0 ; then
	echo "Booting from SD";
	setenv bootdev 0;
else
	echo "Booting from eMMC";
	setenv bootdev 2;
fi;

setenv bootargs init=/init.sh rw console=tty0 console=ttyS0,115200 no_console_suspend earlycon=uart,mmio32,0x01c28000 panic=10 consoleblank=0 loglevel=1 cma=256M PMOS_NO_OUTPUT_REDIRECT pmos_boot=/dev/mmcblk${bootdev}p1 pmos_root=/dev/mmcblk${bootdev}p2

printenv

echo Loading DTB
load mmc ${mmc_bootdev}:1 ${fdt_addr_r} sun50i-a64-pinephone-1.1.dtb

echo Loading Initramfs
load mmc ${mmc_bootdev}:1 ${ramdisk_addr_r} uInitrd-postmarketos-allwinner

echo Loading Kernel
load mmc ${mmc_bootdev}:1 ${kernel_addr_r} vmlinuz-postmarketos-allwinner

gpio set 115

echo Resizing FDT
fdt addr ${fdt_addr_r}
fdt resize

echo Booting kernel
gpio set 116
gpio clear 98
booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
# mkdir -p /media/boot/multiboot/postmarketos/
# cd /media/postmarketboot
# cp -r * /media/boot/multiboot/postmarketos/
# mkfs.ext4 /dev/sdb#
# mkdir -p /media/newpostmarketbin
# mount /dev/sdb# /media/newpostmarketbin
# cd /media/postmarketos
# rsync -avh * /media/newpostmarketbin
# cp /media/postmarketboot /media/newpostmarketbin/boot
# cp /media/postmarketboot /media/newpostmarketbin/

This is the script UBOOT with load and ch use different OS's

now edit the boot.text to reflect the changes of the images

setenv kernel_addr_z 0x44080000

setenv bootargs loglevel=0 silent console=tty0 vt.global_cursor_default=0

# This if statement boots up jumpdrive, if you notice there is an environment variable
# which is what the uboot patch does, it allows you to have an environment variable
# if someone holds the volume UP key it is set

if test "${volume_key}" = "up" ; then 
echo "yes im here"
setenv devtype mmc
setenv devnum 0
setenv distro_bootpart 1
if load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_z} /multiboot/jumpdrive/Image.gz; then
  unzip ${kernel_addr_z} ${kernel_addr_r}
  if load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} /multiboot/jumpdrive/sun50i-a64-pinephone.dtb; then
    if load ${devtype} ${devnum}:${distro_bootpart} ${ramdisk_addr_r} /multiboot/jumpdrive/initramfs.gz; then
      booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r};
    else
      booti ${kernel_addr_r} - ${fdt_addr_r};
    fi;
  fi;
fi
exit
fi
# BOOT UP SECONDARY OS, this is customized for Postmarket OS make your changes here to change the OS LOADED
# and specific boot OPTIONS For your Second boot environment
# Again if the volume_key is pressed down, do this which is set by environment variable

if test "${volume_key}" = "down" ; then 
echo "down"

setenv bootdev 0
setenv bootargs init=/init.sh rw console=tty0 console=ttyS0,115200 no_console_suspend earlycon=uart,mmio32,0x01c28000 panic=10 consoleblank=0 loglevel=1 cma=256M PMOS_NO_OUTPUT_REDIRECT pmos_boot=/dev/mmcblk${bootdev}p3 pmos_root=/dev/mmcblk${bootdev}p3

printenv

echo Loading DTB
load mmc ${mmc_bootdev}:1 ${fdt_addr_r} /multiboot/postmarketos/sun50i-a64-pinephone-1.1.dtb

echo Loading Initramfs
load mmc ${mmc_bootdev}:1 ${ramdisk_addr_r} /multiboot/postmarketos/uInitrd-postmarketos-allwinner

echo Loading Kernel
load mmc ${mmc_bootdev}:1 ${kernel_addr_r} /multiboot/postmarketos/vmlinuz-postmarketos-allwinner

gpio set 115

echo Resizing FDT
fdt addr ${fdt_addr_r}
fdt resize

echo Booting kernel
gpio set 116
gpio clear 98
booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}

# This ELSE statement assumes you have UBUNTU PORTS on your EMMC by default it says the following
# If nothing is being pressed than load the boot script on the EMMC drive, 1,4 where the boot script resides
# on UBUNTU PORTS EMMC device


else

setenv devtype mmc
setenv devnum 1
setenv partnum 4
load ${devtype} ${devnum}:2 ${scriptaddr} /boot.scr
source ${scriptaddr}
fi

compile it:

# mkimage -C none -A arm -T script -d boot.text boot.scr

Once booted, you will need to run depmod -a after logging into it.


Sites Referenced/Inspiration


Patch To Cut And Paste into file for uboot

PATCH BELOW CUT AND PASTE INTO FILE!

volumecontrol.patch

diff -urN u-boot2/board/sunxi/Makefile u-boot/board/sunxi/Makefile
--- u-boot2/board/sunxi/Makefile	2020-06-27 01:09:17.687936000 +0000
+++ u-boot/board/sunxi/Makefile	2020-06-27 01:11:32.102472000 +0000
@@ -11,3 +11,4 @@
 obj-$(CONFIG_MACH_SUN4I)	+= dram_sun4i_auto.o
 obj-$(CONFIG_MACH_SUN5I)	+= dram_sun5i_auto.o
 obj-$(CONFIG_MACH_SUN7I)	+= dram_sun5i_auto.o
+obj-$(CONFIG_MACH_SUN50I)	+= lradc.o
diff -urN u-boot2/board/sunxi/board.c u-boot/board/sunxi/board.c
--- u-boot2/board/sunxi/board.c	2020-06-27 01:09:17.687936000 +0000
+++ u-boot/board/sunxi/board.c	2020-06-27 01:19:15.609314000 +0000
@@ -44,6 +44,7 @@
 #include <spl.h>
 #include <sy8106a.h>
 #include <asm/setup.h>
+#include "lradc.h"
 
 #if defined CONFIG_VIDEO_LCD_PANEL_I2C && !(defined CONFIG_SPL_BUILD)
 /* So that we can use pin names in Kconfig and sunxi_name_to_gpio() */
@@ -639,6 +640,11 @@
 void sunxi_board_init(void)
 {
 	int power_failed = 0;
+#ifdef CONFIG_MACH_SUN50I
+	// we init the lradc in SPL to get the ADC started early to have
+	// a valid sample when U-Boot main binary gets executed.
+	lradc_enable();
+#endif
 
 #ifdef CONFIG_SY8106A_POWER
 	power_failed = sy8106a_set_vout1(CONFIG_SY8106A_VOUT1_VOLT);
@@ -899,6 +905,17 @@
 		env_set("fdtfile", str);
 	}
 
+#ifdef CONFIG_MACH_SUN50I
+	int key = lradc_get_pressed_key();
+	if (key == KEY_VOLUMEDOWN)
+		env_set("volume_key", "down");
+	else if (key == KEY_VOLUMEUP)
+		env_set("volume_key", "up");
+	
+	// no longer needed
+	lradc_disable();
+#endif
+
 	setup_environment(gd->fdt_blob);
 
 #ifdef CONFIG_USB_ETHER
diff -urN u-boot2/board/sunxi/board.c.orig u-boot/board/sunxi/board.c.orig
--- u-boot2/board/sunxi/board.c.orig	1970-01-01 00:00:00.000000000 +0000
+++ u-boot/board/sunxi/board.c.orig	2020-06-27 01:03:03.795626000 +0000
@@ -0,0 +1,982 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2012-2013 Henrik Nordstrom <henrik@henriknordstrom.net>
+ * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+ *
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tangliang@allwinnertech.com>
+ *
+ * Some board init for the Allwinner A10-evb board.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <env.h>
+#include <hang.h>
+#include <image.h>
+#include <init.h>
+#include <log.h>
+#include <mmc.h>
+#include <axp_pmic.h>
+#include <generic-phy.h>
+#include <phy-sun4i-usb.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/display.h>
+#include <asm/arch/dram.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/mmc.h>
+#include <asm/arch/prcm.h>
+#include <asm/arch/spl.h>
+#include <linux/delay.h>
+#include <u-boot/crc.h>
+#ifndef CONFIG_ARM64
+#include <asm/armv7.h>
+#endif
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <u-boot/crc.h>
+#include <env_internal.h>
+#include <linux/libfdt.h>
+#include <nand.h>
+#include <net.h>
+#include <spl.h>
+#include <sy8106a.h>
+#include <asm/setup.h>
+
+#if defined CONFIG_VIDEO_LCD_PANEL_I2C && !(defined CONFIG_SPL_BUILD)
+/* So that we can use pin names in Kconfig and sunxi_name_to_gpio() */
+int soft_i2c_gpio_sda;
+int soft_i2c_gpio_scl;
+
+static int soft_i2c_board_init(void)
+{
+	int ret;
+
+	soft_i2c_gpio_sda = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_PANEL_I2C_SDA);
+	if (soft_i2c_gpio_sda < 0) {
+		printf("Error invalid soft i2c sda pin: '%s', err %d\n",
+		       CONFIG_VIDEO_LCD_PANEL_I2C_SDA, soft_i2c_gpio_sda);
+		return soft_i2c_gpio_sda;
+	}
+	ret = gpio_request(soft_i2c_gpio_sda, "soft-i2c-sda");
+	if (ret) {
+		printf("Error requesting soft i2c sda pin: '%s', err %d\n",
+		       CONFIG_VIDEO_LCD_PANEL_I2C_SDA, ret);
+		return ret;
+	}
+
+	soft_i2c_gpio_scl = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_PANEL_I2C_SCL);
+	if (soft_i2c_gpio_scl < 0) {
+		printf("Error invalid soft i2c scl pin: '%s', err %d\n",
+		       CONFIG_VIDEO_LCD_PANEL_I2C_SCL, soft_i2c_gpio_scl);
+		return soft_i2c_gpio_scl;
+	}
+	ret = gpio_request(soft_i2c_gpio_scl, "soft-i2c-scl");
+	if (ret) {
+		printf("Error requesting soft i2c scl pin: '%s', err %d\n",
+		       CONFIG_VIDEO_LCD_PANEL_I2C_SCL, ret);
+		return ret;
+	}
+
+	return 0;
+}
+#else
+static int soft_i2c_board_init(void) { return 0; }
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void i2c_init_board(void)
+{
+#ifdef CONFIG_I2C0_ENABLE
+#if defined(CONFIG_MACH_SUN4I) || \
+    defined(CONFIG_MACH_SUN5I) || \
+    defined(CONFIG_MACH_SUN7I) || \
+    defined(CONFIG_MACH_SUN8I_R40)
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUN4I_GPB_TWI0);
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN4I_GPB_TWI0);
+	clock_twi_onoff(0, 1);
+#elif defined(CONFIG_MACH_SUN6I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(14), SUN6I_GPH_TWI0);
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(15), SUN6I_GPH_TWI0);
+	clock_twi_onoff(0, 1);
+#elif defined(CONFIG_MACH_SUN8I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(2), SUN8I_GPH_TWI0);
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(3), SUN8I_GPH_TWI0);
+	clock_twi_onoff(0, 1);
+#elif defined(CONFIG_MACH_SUN50I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_GPH_TWI0);
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_GPH_TWI0);
+	clock_twi_onoff(0, 1);
+#endif
+#endif
+
+#ifdef CONFIG_I2C1_ENABLE
+#if defined(CONFIG_MACH_SUN4I) || \
+    defined(CONFIG_MACH_SUN7I) || \
+    defined(CONFIG_MACH_SUN8I_R40)
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(18), SUN4I_GPB_TWI1);
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(19), SUN4I_GPB_TWI1);
+	clock_twi_onoff(1, 1);
+#elif defined(CONFIG_MACH_SUN5I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(15), SUN5I_GPB_TWI1);
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(16), SUN5I_GPB_TWI1);
+	clock_twi_onoff(1, 1);
+#elif defined(CONFIG_MACH_SUN6I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(16), SUN6I_GPH_TWI1);
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(17), SUN6I_GPH_TWI1);
+	clock_twi_onoff(1, 1);
+#elif defined(CONFIG_MACH_SUN8I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(4), SUN8I_GPH_TWI1);
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(5), SUN8I_GPH_TWI1);
+	clock_twi_onoff(1, 1);
+#elif defined(CONFIG_MACH_SUN50I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(2), SUN50I_GPH_TWI1);
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(3), SUN50I_GPH_TWI1);
+	clock_twi_onoff(1, 1);
+#endif
+#endif
+
+#ifdef CONFIG_I2C2_ENABLE
+#if defined(CONFIG_MACH_SUN4I) || \
+    defined(CONFIG_MACH_SUN7I) || \
+    defined(CONFIG_MACH_SUN8I_R40)
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN4I_GPB_TWI2);
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(21), SUN4I_GPB_TWI2);
+	clock_twi_onoff(2, 1);
+#elif defined(CONFIG_MACH_SUN5I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(17), SUN5I_GPB_TWI2);
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(18), SUN5I_GPB_TWI2);
+	clock_twi_onoff(2, 1);
+#elif defined(CONFIG_MACH_SUN6I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(18), SUN6I_GPH_TWI2);
+	sunxi_gpio_set_cfgpin(SUNXI_GPH(19), SUN6I_GPH_TWI2);
+	clock_twi_onoff(2, 1);
+#elif defined(CONFIG_MACH_SUN8I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPE(12), SUN8I_GPE_TWI2);
+	sunxi_gpio_set_cfgpin(SUNXI_GPE(13), SUN8I_GPE_TWI2);
+	clock_twi_onoff(2, 1);
+#elif defined(CONFIG_MACH_SUN50I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPE(14), SUN50I_GPE_TWI2);
+	sunxi_gpio_set_cfgpin(SUNXI_GPE(15), SUN50I_GPE_TWI2);
+	clock_twi_onoff(2, 1);
+#endif
+#endif
+
+#ifdef CONFIG_I2C3_ENABLE
+#if defined(CONFIG_MACH_SUN6I)
+	sunxi_gpio_set_cfgpin(SUNXI_GPG(10), SUN6I_GPG_TWI3);
+	sunxi_gpio_set_cfgpin(SUNXI_GPG(11), SUN6I_GPG_TWI3);
+	clock_twi_onoff(3, 1);
+#elif defined(CONFIG_MACH_SUN7I) || \
+      defined(CONFIG_MACH_SUN8I_R40)
+	sunxi_gpio_set_cfgpin(SUNXI_GPI(0), SUN7I_GPI_TWI3);
+	sunxi_gpio_set_cfgpin(SUNXI_GPI(1), SUN7I_GPI_TWI3);
+	clock_twi_onoff(3, 1);
+#endif
+#endif
+
+#ifdef CONFIG_I2C4_ENABLE
+#if defined(CONFIG_MACH_SUN7I) || \
+    defined(CONFIG_MACH_SUN8I_R40)
+	sunxi_gpio_set_cfgpin(SUNXI_GPI(2), SUN7I_GPI_TWI4);
+	sunxi_gpio_set_cfgpin(SUNXI_GPI(3), SUN7I_GPI_TWI4);
+	clock_twi_onoff(4, 1);
+#endif
+#endif
+
+#ifdef CONFIG_R_I2C_ENABLE
+#ifdef CONFIG_MACH_SUN50I
+	clock_twi_onoff(5, 1);
+	sunxi_gpio_set_cfgpin(SUNXI_GPL(8), SUN50I_GPL_R_TWI);
+	sunxi_gpio_set_cfgpin(SUNXI_GPL(9), SUN50I_GPL_R_TWI);
+#else
+	clock_twi_onoff(5, 1);
+	sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_H3_GPL_R_TWI);
+	sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_H3_GPL_R_TWI);
+#endif
+#endif
+}
+
+#if defined(CONFIG_ENV_IS_IN_MMC) && defined(CONFIG_ENV_IS_IN_FAT)
+enum env_location env_get_location(enum env_operation op, int prio)
+{
+	switch (prio) {
+	case 0:
+		return ENVL_FAT;
+
+	case 1:
+		return ENVL_MMC;
+
+	default:
+		return ENVL_UNKNOWN;
+	}
+}
+#endif
+
+#ifdef CONFIG_DM_MMC
+static void mmc_pinmux_setup(int sdc);
+#endif
+
+/* add board specific code here */
+int board_init(void)
+{
+	__maybe_unused int id_pfr1, ret, satapwr_pin, macpwr_pin;
+
+	gd->bd->bi_boot_params = (PHYS_SDRAM_0 + 0x100);
+
+#ifndef CONFIG_ARM64
+	asm volatile("mrc p15, 0, %0, c0, c1, 1" : "=r"(id_pfr1));
+	debug("id_pfr1: 0x%08x\n", id_pfr1);
+	/* Generic Timer Extension available? */
+	if ((id_pfr1 >> CPUID_ARM_GENTIMER_SHIFT) & 0xf) {
+		uint32_t freq;
+
+		debug("Setting CNTFRQ\n");
+
+		/*
+		 * CNTFRQ is a secure register, so we will crash if we try to
+		 * write this from the non-secure world (read is OK, though).
+		 * In case some bootcode has already set the correct value,
+		 * we avoid the risk of writing to it.
+		 */
+		asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r"(freq));
+		if (freq != COUNTER_FREQUENCY) {
+			debug("arch timer frequency is %d Hz, should be %d, fixing ...\n",
+			      freq, COUNTER_FREQUENCY);
+#ifdef CONFIG_NON_SECURE
+			printf("arch timer frequency is wrong, but cannot adjust it\n");
+#else
+			asm volatile("mcr p15, 0, %0, c14, c0, 0"
+				     : : "r"(COUNTER_FREQUENCY));
+#endif
+		}
+	}
+#endif /* !CONFIG_ARM64 */
+
+	ret = axp_gpio_init();
+	if (ret)
+		return ret;
+
+#ifdef CONFIG_SATAPWR
+	satapwr_pin = sunxi_name_to_gpio(CONFIG_SATAPWR);
+	gpio_request(satapwr_pin, "satapwr");
+	gpio_direction_output(satapwr_pin, 1);
+	/* Give attached sata device time to power-up to avoid link timeouts */
+	mdelay(500);
+#endif
+#ifdef CONFIG_MACPWR
+	macpwr_pin = sunxi_name_to_gpio(CONFIG_MACPWR);
+	gpio_request(macpwr_pin, "macpwr");
+	gpio_direction_output(macpwr_pin, 1);
+#endif
+
+#ifdef CONFIG_DM_I2C
+	/*
+	 * Temporary workaround for enabling I2C clocks until proper sunxi DM
+	 * clk, reset and pinctrl drivers land.
+	 */
+	i2c_init_board();
+#endif
+
+#ifdef CONFIG_DM_MMC
+	/*
+	 * Temporary workaround for enabling MMC clocks until a sunxi DM
+	 * pinctrl driver lands.
+	 */
+	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT);
+#if CONFIG_MMC_SUNXI_SLOT_EXTRA != -1
+	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT_EXTRA);
+#endif
+#endif	/* CONFIG_DM_MMC */
+
+	/* Uses dm gpio code so do this here and not in i2c_init_board() */
+	return soft_i2c_board_init();
+}
+
+/*
+ * On older SoCs the SPL is actually at address zero, so using NULL as
+ * an error value does not work.
+ */
+#define INVALID_SPL_HEADER ((void *)~0UL)
+
+static struct boot_file_head * get_spl_header(uint8_t req_version)
+{
+	struct boot_file_head *spl = (void *)(ulong)SPL_ADDR;
+	uint8_t spl_header_version = spl->spl_signature[3];
+
+	/* Is there really the SPL header (still) there? */
+	if (memcmp(spl->spl_signature, SPL_SIGNATURE, 3) != 0)
+		return INVALID_SPL_HEADER;
+
+	if (spl_header_version < req_version) {
+		printf("sunxi SPL version mismatch: expected %u, got %u\n",
+		       req_version, spl_header_version);
+		return INVALID_SPL_HEADER;
+	}
+
+	return spl;
+}
+
+static const char *get_spl_dt_name(void)
+{
+	struct boot_file_head *spl = get_spl_header(SPL_DT_HEADER_VERSION);
+
+	/* Check if there is a DT name stored in the SPL header. */
+	if (spl != INVALID_SPL_HEADER && spl->dt_name_offset)
+		return (char *)spl + spl->dt_name_offset;
+
+	return NULL;
+}
+
+static void set_spl_dt_name(const char *name)
+{
+	struct boot_file_head *spl = get_spl_header(SPL_ENV_HEADER_VERSION);
+
+	if (spl == INVALID_SPL_HEADER)
+		return;
+
+	/* Promote the header version for U-Boot proper, if needed. */
+	if (spl->spl_signature[3] < SPL_DT_HEADER_VERSION)
+		spl->spl_signature[3] = SPL_DT_HEADER_VERSION;
+
+	strcpy((char *)&spl->string_pool, name);
+	spl->dt_name_offset = offsetof(struct boot_file_head, string_pool);
+}
+
+int dram_init(void)
+{
+	struct boot_file_head *spl = get_spl_header(SPL_DRAM_HEADER_VERSION);
+
+	if (spl == INVALID_SPL_HEADER)
+		gd->ram_size = get_ram_size((long *)PHYS_SDRAM_0,
+					    PHYS_SDRAM_0_SIZE);
+	else
+		gd->ram_size = (phys_addr_t)spl->dram_size << 20;
+
+	if (gd->ram_size > CONFIG_SUNXI_DRAM_MAX_SIZE)
+		gd->ram_size = CONFIG_SUNXI_DRAM_MAX_SIZE;
+
+	return 0;
+}
+
+#if defined(CONFIG_NAND_SUNXI)
+static void nand_pinmux_setup(void)
+{
+	unsigned int pin;
+
+	for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(19); pin++)
+		sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_NAND);
+
+#if defined CONFIG_MACH_SUN4I || defined CONFIG_MACH_SUN7I
+	for (pin = SUNXI_GPC(20); pin <= SUNXI_GPC(22); pin++)
+		sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_NAND);
+#endif
+	/* sun4i / sun7i do have a PC23, but it is not used for nand,
+	 * only sun7i has a PC24 */
+#ifdef CONFIG_MACH_SUN7I
+	sunxi_gpio_set_cfgpin(SUNXI_GPC(24), SUNXI_GPC_NAND);
+#endif
+}
+
+static void nand_clock_setup(void)
+{
+	struct sunxi_ccm_reg *const ccm =
+		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+	setbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
+#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I || \
+    defined CONFIG_MACH_SUN9I || defined CONFIG_MACH_SUN50I
+	setbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_GATE_OFFSET_NAND0));
+#endif
+	setbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
+}
+
+void board_nand_init(void)
+{
+	nand_pinmux_setup();
+	nand_clock_setup();
+#ifndef CONFIG_SPL_BUILD
+	sunxi_nand_init();
+#endif
+}
+#endif
+
+#ifdef CONFIG_MMC
+static void mmc_pinmux_setup(int sdc)
+{
+	unsigned int pin;
+	__maybe_unused int pins;
+
+	switch (sdc) {
+	case 0:
+		/* SDC0: PF0-PF5 */
+		for (pin = SUNXI_GPF(0); pin <= SUNXI_GPF(5); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUNXI_GPF_SDC0);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+		break;
+
+	case 1:
+		pins = sunxi_name_to_gpio_bank(CONFIG_MMC1_PINS);
+
+#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN7I) || \
+    defined(CONFIG_MACH_SUN8I_R40)
+		if (pins == SUNXI_GPIO_H) {
+			/* SDC1: PH22-PH-27 */
+			for (pin = SUNXI_GPH(22); pin <= SUNXI_GPH(27); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUN4I_GPH_SDC1);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+		} else {
+			/* SDC1: PG0-PG5 */
+			for (pin = SUNXI_GPG(0); pin <= SUNXI_GPG(5); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUN4I_GPG_SDC1);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+		}
+#elif defined(CONFIG_MACH_SUN5I)
+		/* SDC1: PG3-PG8 */
+		for (pin = SUNXI_GPG(3); pin <= SUNXI_GPG(8); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUN5I_GPG_SDC1);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+#elif defined(CONFIG_MACH_SUN6I)
+		/* SDC1: PG0-PG5 */
+		for (pin = SUNXI_GPG(0); pin <= SUNXI_GPG(5); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUN6I_GPG_SDC1);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+#elif defined(CONFIG_MACH_SUN8I)
+		if (pins == SUNXI_GPIO_D) {
+			/* SDC1: PD2-PD7 */
+			for (pin = SUNXI_GPD(2); pin <= SUNXI_GPD(7); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUN8I_GPD_SDC1);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+		} else {
+			/* SDC1: PG0-PG5 */
+			for (pin = SUNXI_GPG(0); pin <= SUNXI_GPG(5); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUN8I_GPG_SDC1);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+		}
+#endif
+		break;
+
+	case 2:
+		pins = sunxi_name_to_gpio_bank(CONFIG_MMC2_PINS);
+
+#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN7I)
+		/* SDC2: PC6-PC11 */
+		for (pin = SUNXI_GPC(6); pin <= SUNXI_GPC(11); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SDC2);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+#elif defined(CONFIG_MACH_SUN5I)
+		if (pins == SUNXI_GPIO_E) {
+			/* SDC2: PE4-PE9 */
+			for (pin = SUNXI_GPE(4); pin <= SUNXI_GPD(9); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUN5I_GPE_SDC2);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+		} else {
+			/* SDC2: PC6-PC15 */
+			for (pin = SUNXI_GPC(6); pin <= SUNXI_GPC(15); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SDC2);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+		}
+#elif defined(CONFIG_MACH_SUN6I)
+		if (pins == SUNXI_GPIO_A) {
+			/* SDC2: PA9-PA14 */
+			for (pin = SUNXI_GPA(9); pin <= SUNXI_GPA(14); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_SDC2);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+		} else {
+			/* SDC2: PC6-PC15, PC24 */
+			for (pin = SUNXI_GPC(6); pin <= SUNXI_GPC(15); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SDC2);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+
+			sunxi_gpio_set_cfgpin(SUNXI_GPC(24), SUNXI_GPC_SDC2);
+			sunxi_gpio_set_pull(SUNXI_GPC(24), SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(SUNXI_GPC(24), 2);
+		}
+#elif defined(CONFIG_MACH_SUN8I_R40)
+		/* SDC2: PC6-PC15, PC24 */
+		for (pin = SUNXI_GPC(6); pin <= SUNXI_GPC(15); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SDC2);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+
+		sunxi_gpio_set_cfgpin(SUNXI_GPC(24), SUNXI_GPC_SDC2);
+		sunxi_gpio_set_pull(SUNXI_GPC(24), SUNXI_GPIO_PULL_UP);
+		sunxi_gpio_set_drv(SUNXI_GPC(24), 2);
+#elif defined(CONFIG_MACH_SUN8I) || defined(CONFIG_MACH_SUN50I)
+		/* SDC2: PC5-PC6, PC8-PC16 */
+		for (pin = SUNXI_GPC(5); pin <= SUNXI_GPC(6); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SDC2);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+
+		for (pin = SUNXI_GPC(8); pin <= SUNXI_GPC(16); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SDC2);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+#elif defined(CONFIG_MACH_SUN50I_H6)
+		/* SDC2: PC4-PC14 */
+		for (pin = SUNXI_GPC(4); pin <= SUNXI_GPC(14); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SDC2);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+#elif defined(CONFIG_MACH_SUN9I)
+		/* SDC2: PC6-PC16 */
+		for (pin = SUNXI_GPC(6); pin <= SUNXI_GPC(16); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SDC2);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+#endif
+		break;
+
+	case 3:
+		pins = sunxi_name_to_gpio_bank(CONFIG_MMC3_PINS);
+
+#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN7I) || \
+    defined(CONFIG_MACH_SUN8I_R40)
+		/* SDC3: PI4-PI9 */
+		for (pin = SUNXI_GPI(4); pin <= SUNXI_GPI(9); pin++) {
+			sunxi_gpio_set_cfgpin(pin, SUNXI_GPI_SDC3);
+			sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(pin, 2);
+		}
+#elif defined(CONFIG_MACH_SUN6I)
+		if (pins == SUNXI_GPIO_A) {
+			/* SDC3: PA9-PA14 */
+			for (pin = SUNXI_GPA(9); pin <= SUNXI_GPA(14); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_SDC3);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+		} else {
+			/* SDC3: PC6-PC15, PC24 */
+			for (pin = SUNXI_GPC(6); pin <= SUNXI_GPC(15); pin++) {
+				sunxi_gpio_set_cfgpin(pin, SUN6I_GPC_SDC3);
+				sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
+				sunxi_gpio_set_drv(pin, 2);
+			}
+
+			sunxi_gpio_set_cfgpin(SUNXI_GPC(24), SUN6I_GPC_SDC3);
+			sunxi_gpio_set_pull(SUNXI_GPC(24), SUNXI_GPIO_PULL_UP);
+			sunxi_gpio_set_drv(SUNXI_GPC(24), 2);
+		}
+#endif
+		break;
+
+	default:
+		printf("sunxi: invalid MMC slot %d for pinmux setup\n", sdc);
+		break;
+	}
+}
+
+int board_mmc_init(bd_t *bis)
+{
+	__maybe_unused struct mmc *mmc0, *mmc1;
+
+	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT);
+	mmc0 = sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT);
+	if (!mmc0)
+		return -1;
+
+#if CONFIG_MMC_SUNXI_SLOT_EXTRA != -1
+	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT_EXTRA);
+	mmc1 = sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT_EXTRA);
+	if (!mmc1)
+		return -1;
+#endif
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_SPL_BUILD
+
+static void sunxi_spl_store_dram_size(phys_addr_t dram_size)
+{
+	struct boot_file_head *spl = get_spl_header(SPL_DT_HEADER_VERSION);
+
+	if (spl == INVALID_SPL_HEADER)
+		return;
+
+	/* Promote the header version for U-Boot proper, if needed. */
+	if (spl->spl_signature[3] < SPL_DRAM_HEADER_VERSION)
+		spl->spl_signature[3] = SPL_DRAM_HEADER_VERSION;
+
+	spl->dram_size = dram_size >> 20;
+}
+
+void sunxi_board_init(void)
+{
+	int power_failed = 0;
+
+#ifdef CONFIG_SY8106A_POWER
+	power_failed = sy8106a_set_vout1(CONFIG_SY8106A_VOUT1_VOLT);
+#endif
+
+#if defined CONFIG_AXP152_POWER || defined CONFIG_AXP209_POWER || \
+	defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
+	defined CONFIG_AXP818_POWER
+	power_failed = axp_init();
+
+#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
+	defined CONFIG_AXP818_POWER
+	power_failed |= axp_set_dcdc1(CONFIG_AXP_DCDC1_VOLT);
+#endif
+	power_failed |= axp_set_dcdc2(CONFIG_AXP_DCDC2_VOLT);
+	power_failed |= axp_set_dcdc3(CONFIG_AXP_DCDC3_VOLT);
+#if !defined(CONFIG_AXP209_POWER) && !defined(CONFIG_AXP818_POWER)
+	power_failed |= axp_set_dcdc4(CONFIG_AXP_DCDC4_VOLT);
+#endif
+#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
+	defined CONFIG_AXP818_POWER
+	power_failed |= axp_set_dcdc5(CONFIG_AXP_DCDC5_VOLT);
+#endif
+
+#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
+	defined CONFIG_AXP818_POWER
+	power_failed |= axp_set_aldo1(CONFIG_AXP_ALDO1_VOLT);
+#endif
+	power_failed |= axp_set_aldo2(CONFIG_AXP_ALDO2_VOLT);
+#if !defined(CONFIG_AXP152_POWER)
+	power_failed |= axp_set_aldo3(CONFIG_AXP_ALDO3_VOLT);
+#endif
+#ifdef CONFIG_AXP209_POWER
+	power_failed |= axp_set_aldo4(CONFIG_AXP_ALDO4_VOLT);
+#endif
+
+#if defined(CONFIG_AXP221_POWER) || defined(CONFIG_AXP809_POWER) || \
+	defined(CONFIG_AXP818_POWER)
+	power_failed |= axp_set_dldo(1, CONFIG_AXP_DLDO1_VOLT);
+	power_failed |= axp_set_dldo(2, CONFIG_AXP_DLDO2_VOLT);
+#if !defined CONFIG_AXP809_POWER
+	power_failed |= axp_set_dldo(3, CONFIG_AXP_DLDO3_VOLT);
+	power_failed |= axp_set_dldo(4, CONFIG_AXP_DLDO4_VOLT);
+#endif
+	power_failed |= axp_set_eldo(1, CONFIG_AXP_ELDO1_VOLT);
+	power_failed |= axp_set_eldo(2, CONFIG_AXP_ELDO2_VOLT);
+	power_failed |= axp_set_eldo(3, CONFIG_AXP_ELDO3_VOLT);
+#endif
+
+#ifdef CONFIG_AXP818_POWER
+	power_failed |= axp_set_fldo(1, CONFIG_AXP_FLDO1_VOLT);
+	power_failed |= axp_set_fldo(2, CONFIG_AXP_FLDO2_VOLT);
+	power_failed |= axp_set_fldo(3, CONFIG_AXP_FLDO3_VOLT);
+#endif
+
+#if defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER
+	power_failed |= axp_set_sw(IS_ENABLED(CONFIG_AXP_SW_ON));
+#endif
+#endif
+	printf("DRAM:");
+	gd->ram_size = sunxi_dram_init();
+	printf(" %d MiB\n", (int)(gd->ram_size >> 20));
+	if (!gd->ram_size)
+		hang();
+
+	sunxi_spl_store_dram_size(gd->ram_size);
+
+	/*
+	 * Only clock up the CPU to full speed if we are reasonably
+	 * assured it's being powered with suitable core voltage
+	 */
+	if (!power_failed)
+		clock_set_pll1(CONFIG_SYS_CLK_FREQ);
+	else
+		printf("Failed to set core voltage! Can't set CPU frequency\n");
+}
+#endif
+
+#ifdef CONFIG_USB_GADGET
+int g_dnl_board_usb_cable_connected(void)
+{
+	struct udevice *dev;
+	struct phy phy;
+	int ret;
+
+	ret = uclass_get_device(UCLASS_USB_GADGET_GENERIC, 0, &dev);
+	if (ret) {
+		pr_err("%s: Cannot find USB device\n", __func__);
+		return ret;
+	}
+
+	ret = generic_phy_get_by_name(dev, "usb", &phy);
+	if (ret) {
+		pr_err("failed to get %s USB PHY\n", dev->name);
+		return ret;
+	}
+
+	ret = generic_phy_init(&phy);
+	if (ret) {
+		pr_err("failed to init %s USB PHY\n", dev->name);
+		return ret;
+	}
+
+	ret = sun4i_usb_phy_vbus_detect(&phy);
+	if (ret == 1) {
+		pr_err("A charger is plugged into the OTG\n");
+		return -ENODEV;
+	}
+
+	return ret;
+}
+#endif
+
+#ifdef CONFIG_SERIAL_TAG
+void get_board_serial(struct tag_serialnr *serialnr)
+{
+	char *serial_string;
+	unsigned long long serial;
+
+	serial_string = env_get("serial#");
+
+	if (serial_string) {
+		serial = simple_strtoull(serial_string, NULL, 16);
+
+		serialnr->high = (unsigned int) (serial >> 32);
+		serialnr->low = (unsigned int) (serial & 0xffffffff);
+	} else {
+		serialnr->high = 0;
+		serialnr->low = 0;
+	}
+}
+#endif
+
+/*
+ * Check the SPL header for the "sunxi" variant. If found: parse values
+ * that might have been passed by the loader ("fel" utility), and update
+ * the environment accordingly.
+ */
+static void parse_spl_header(const uint32_t spl_addr)
+{
+	struct boot_file_head *spl = get_spl_header(SPL_ENV_HEADER_VERSION);
+
+	if (spl == INVALID_SPL_HEADER)
+		return;
+
+	if (!spl->fel_script_address)
+		return;
+
+	if (spl->fel_uEnv_length != 0) {
+		/*
+		 * data is expected in uEnv.txt compatible format, so "env
+		 * import -t" the string(s) at fel_script_address right away.
+		 */
+		himport_r(&env_htab, (char *)(uintptr_t)spl->fel_script_address,
+			  spl->fel_uEnv_length, '\n', H_NOCLEAR, 0, 0, NULL);
+		return;
+	}
+	/* otherwise assume .scr format (mkimage-type script) */
+	env_set_hex("fel_scriptaddr", spl->fel_script_address);
+}
+
+/*
+ * Note this function gets called multiple times.
+ * It must not make any changes to env variables which already exist.
+ */
+static void setup_environment(const void *fdt)
+{
+	char serial_string[17] = { 0 };
+	unsigned int sid[4];
+	uint8_t mac_addr[6];
+	char ethaddr[16];
+	int i, ret;
+
+	ret = sunxi_get_sid(sid);
+	if (ret == 0 && sid[0] != 0) {
+		/*
+		 * The single words 1 - 3 of the SID have quite a few bits
+		 * which are the same on many models, so we take a crc32
+		 * of all 3 words, to get a more unique value.
+		 *
+		 * Note we only do this on newer SoCs as we cannot change
+		 * the algorithm on older SoCs since those have been using
+		 * fixed mac-addresses based on only using word 3 for a
+		 * long time and changing a fixed mac-address with an
+		 * u-boot update is not good.
+		 */
+#if !defined(CONFIG_MACH_SUN4I) && !defined(CONFIG_MACH_SUN5I) && \
+    !defined(CONFIG_MACH_SUN6I) && !defined(CONFIG_MACH_SUN7I) && \
+    !defined(CONFIG_MACH_SUN8I_A23) && !defined(CONFIG_MACH_SUN8I_A33)
+		sid[3] = crc32(0, (unsigned char *)&sid[1], 12);
+#endif
+
+		/* Ensure the NIC specific bytes of the mac are not all 0 */
+		if ((sid[3] & 0xffffff) == 0)
+			sid[3] |= 0x800000;
+
+		for (i = 0; i < 4; i++) {
+			sprintf(ethaddr, "ethernet%d", i);
+			if (!fdt_get_alias(fdt, ethaddr))
+				continue;
+
+			if (i == 0)
+				strcpy(ethaddr, "ethaddr");
+			else
+				sprintf(ethaddr, "eth%daddr", i);
+
+			if (env_get(ethaddr))
+				continue;
+
+			/* Non OUI / registered MAC address */
+			mac_addr[0] = (i << 4) | 0x02;
+			mac_addr[1] = (sid[0] >>  0) & 0xff;
+			mac_addr[2] = (sid[3] >> 24) & 0xff;
+			mac_addr[3] = (sid[3] >> 16) & 0xff;
+			mac_addr[4] = (sid[3] >>  8) & 0xff;
+			mac_addr[5] = (sid[3] >>  0) & 0xff;
+
+			eth_env_set_enetaddr(ethaddr, mac_addr);
+		}
+
+		if (!env_get("serial#")) {
+			snprintf(serial_string, sizeof(serial_string),
+				"%08x%08x", sid[0], sid[3]);
+
+			env_set("serial#", serial_string);
+		}
+	}
+}
+
+int misc_init_r(void)
+{
+	const char *spl_dt_name;
+	uint boot;
+
+	env_set("fel_booted", NULL);
+	env_set("fel_scriptaddr", NULL);
+	env_set("mmc_bootdev", NULL);
+
+	boot = sunxi_get_boot_device();
+	/* determine if we are running in FEL mode */
+	if (boot == BOOT_DEVICE_BOARD) {
+		env_set("fel_booted", "1");
+		parse_spl_header(SPL_ADDR);
+	/* or if we booted from MMC, and which one */
+	} else if (boot == BOOT_DEVICE_MMC1) {
+		env_set("mmc_bootdev", "0");
+	} else if (boot == BOOT_DEVICE_MMC2) {
+		env_set("mmc_bootdev", "1");
+	}
+
+	/* Set fdtfile to match the FIT configuration chosen in SPL. */
+	spl_dt_name = get_spl_dt_name();
+	if (spl_dt_name) {
+		char *prefix = IS_ENABLED(CONFIG_ARM64) ? "allwinner/" : "";
+		char str[64];
+
+		snprintf(str, sizeof(str), "%s%s.dtb", prefix, spl_dt_name);
+		env_set("fdtfile", str);
+	}
+
+	setup_environment(gd->fdt_blob);
+
+#ifdef CONFIG_USB_ETHER
+	usb_ether_init();
+#endif
+
+	return 0;
+}
+
+int ft_board_setup(void *blob, bd_t *bd)
+{
+	int __maybe_unused r;
+
+	/*
+	 * Call setup_environment again in case the boot fdt has
+	 * ethernet aliases the u-boot copy does not have.
+	 */
+	setup_environment(blob);
+
+#ifdef CONFIG_VIDEO_DT_SIMPLEFB
+	r = sunxi_simplefb_setup(blob);
+	if (r)
+		return r;
+#endif
+	return 0;
+}
+
+#ifdef CONFIG_SPL_LOAD_FIT
+int board_fit_config_name_match(const char *name)
+{
+	const char *best_dt_name = get_spl_dt_name();
+	int ret;
+
+#ifdef CONFIG_DEFAULT_DEVICE_TREE
+	if (best_dt_name == NULL)
+		best_dt_name = CONFIG_DEFAULT_DEVICE_TREE;
+#endif
+
+	if (best_dt_name == NULL) {
+		/* No DT name was provided, so accept the first config. */
+		return 0;
+	}
+#ifdef CONFIG_PINE64_DT_SELECTION
+	else if (strstr(best_dt_name, "-pine64-plus")) {
+		/* Differentiate the Pine A64 boards by their DRAM size. */
+		if ((gd->ram_size == 512 * 1024 * 1024))
+			best_dt_name = "sun50i-a64-pine64";
+	}
+#endif
+#ifdef CONFIG_PINEPHONE_DT_SELECTION
+	else if (strstr(best_dt_name, "-pinephone")) {
+		/* Differentiate the PinePhone revisions by GPIO inputs. */
+		prcm_apb0_enable(PRCM_APB0_GATE_PIO);
+		sunxi_gpio_set_pull(SUNXI_GPL(6), SUNXI_GPIO_PULL_UP);
+		sunxi_gpio_set_cfgpin(SUNXI_GPL(6), SUNXI_GPIO_INPUT);
+		udelay(100);
+
+		/* PL6 is pulled low by the modem on v1.2. */
+		if (gpio_get_value(SUNXI_GPL(6)) == 0)
+			best_dt_name = "sun50i-a64-pinephone-1.2";
+		else
+			best_dt_name = "sun50i-a64-pinephone-1.1";
+
+		sunxi_gpio_set_cfgpin(SUNXI_GPL(6), SUNXI_GPIO_DISABLE);
+		sunxi_gpio_set_pull(SUNXI_GPL(6), SUNXI_GPIO_PULL_DISABLE);
+		prcm_apb0_disable(PRCM_APB0_GATE_PIO);
+	}
+#endif
+
+	ret = strcmp(name, best_dt_name);
+
+	/*
+	 * If one of the FIT configurations matches the most accurate DT name,
+	 * update the SPL header to provide that DT name to U-Boot proper.
+	 */
+	if (ret == 0)
+		set_spl_dt_name(best_dt_name);
+
+	return ret;
+}
+#endif
diff -urN u-boot2/board/sunxi/board.c.rej u-boot/board/sunxi/board.c.rej
--- u-boot2/board/sunxi/board.c.rej	1970-01-01 00:00:00.000000000 +0000
+++ u-boot/board/sunxi/board.c.rej	2020-06-27 01:11:32.103472000 +0000
@@ -0,0 +1,15 @@
+--- board/sunxi/board.c
++++ board/sunxi/board.c
+@@ -623,6 +624,12 @@ void sunxi_board_init(void)
+ {
+ 	int power_failed = 0;
+ 
++#ifdef CONFIG_MACH_SUN50I
++	// we init the lradc in SPL to get the ADC started early to have
++	// a valid sample when U-Boot main binary gets executed.
++	lradc_enable();
++#endif
++
+ #ifdef CONFIG_PINEPHONE_LEDS
+ 	/* PD18:G PD19:R PD20:B */
+ 	gpio_request(SUNXI_GPD(18), "led:green");
diff -urN u-boot2/board/sunxi/lradc.c u-boot/board/sunxi/lradc.c
--- u-boot2/board/sunxi/lradc.c	1970-01-01 00:00:00.000000000 +0000
+++ u-boot/board/sunxi/lradc.c	2020-06-27 01:11:32.104472000 +0000
@@ -0,0 +1,81 @@
+#include <common.h>
+#include <asm/io.h>
+#include "lradc.h"
+
+#define LRADC_BASE		0x1c21800
+
+#define LRADC_CTRL		(LRADC_BASE + 0x00)
+#define LRADC_INTC		(LRADC_BASE + 0x04)
+#define LRADC_INTS		(LRADC_BASE + 0x08)
+#define LRADC_DATA0		(LRADC_BASE + 0x0c)
+#define LRADC_DATA1		(LRADC_BASE + 0x10)
+
+/* LRADC_CTRL bits */
+#define FIRST_CONVERT_DLY(x)	((x) << 24) /* 8 bits */
+#define CHAN_SELECT(x)		((x) << 22) /* 2 bits */
+#define CONTINUE_TIME_SEL(x)	((x) << 16) /* 4 bits */
+#define KEY_MODE_SEL(x)		((x) << 12) /* 2 bits */
+#define LEVELA_B_CNT(x)		((x) << 8)  /* 4 bits */
+#define HOLD_KEY_EN(x)		((x) << 7)
+#define HOLD_EN(x)		((x) << 6)
+#define LEVELB_VOL(x)		((x) << 4)  /* 2 bits */
+#define SAMPLE_RATE(x)		((x) << 2)  /* 2 bits */
+#define ENABLE(x)		((x) << 0)
+
+/* LRADC_INTC and LRADC_INTS bits */
+#define CHAN1_KEYUP_IRQ		BIT(12)
+#define CHAN1_ALRDY_HOLD_IRQ	BIT(11)
+#define CHAN1_HOLD_IRQ		BIT(10)
+#define	CHAN1_KEYDOWN_IRQ	BIT(9)
+#define CHAN1_DATA_IRQ		BIT(8)
+#define CHAN0_KEYUP_IRQ		BIT(4)
+#define CHAN0_ALRDY_HOLD_IRQ	BIT(3)
+#define CHAN0_HOLD_IRQ		BIT(2)
+#define	CHAN0_KEYDOWN_IRQ	BIT(1)
+#define CHAN0_DATA_IRQ		BIT(0)
+
+// this is for PinePhone only
+
+int lradc_get_pressed_key(void)
+{
+	uint32_t val;
+	uint32_t vref = 3000000 * 2 / 3;
+
+	val = readl(LRADC_DATA0) & 0x3f;
+	val = val * vref / 63;
+
+//	printf("lradc=%u\n", val);
+
+	if (val < 200000) // 158730
+		return KEY_VOLUMEUP;
+	else if (val < 400000) // 349206
+		return KEY_VOLUMEDOWN;
+
+	return 0;
+}
+
+void lradc_enable(void)
+{
+	// aldo3 is always on and defaults to 3V
+
+	writel(0xffffffff, LRADC_INTS);
+	writel(0, LRADC_INTC);
+
+	/*
+	 * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to
+	 * stabilize on press, wait (1 + 1) * 4 ms for key release
+	 */
+	writel(FIRST_CONVERT_DLY(0) | LEVELA_B_CNT(0) | HOLD_EN(0) |
+		SAMPLE_RATE(0) | ENABLE(1), LRADC_CTRL);
+
+}
+
+void lradc_disable(void)
+{
+	writel(0xffffffff, LRADC_INTS);
+	writel(0, LRADC_INTC);
+
+	/* Disable lradc, leave other settings unchanged */
+	writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
+		SAMPLE_RATE(2), LRADC_CTRL);
+}
diff -urN u-boot2/board/sunxi/lradc.h u-boot/board/sunxi/lradc.h
--- u-boot2/board/sunxi/lradc.h	1970-01-01 00:00:00.000000000 +0000
+++ u-boot/board/sunxi/lradc.h	2020-06-27 01:11:32.104472000 +0000
@@ -0,0 +1,11 @@
+#pragma once
+
+enum {
+	KEY_NONE = 0,
+	KEY_VOLUMEDOWN = 1,
+	KEY_VOLUMEUP = 2,
+};
+
+int lradc_get_pressed_key(void);
+void lradc_enable(void);
+void lradc_disable(void);
diff -urN u-boot2/volumepatchfirst.patch u-boot/volumepatchfirst.patch
--- u-boot2/volumepatchfirst.patch	1970-01-01 00:00:00.000000000 +0000
+++ u-boot/volumepatchfirst.patch	2020-06-27 01:11:04.466880000 +0000
@@ -0,0 +1,177 @@
+From 0ab6225154c3d8b74f06fb3b181b52a9a64b4602 Mon Sep 17 00:00:00 2001
+From: Ondrej Jirman
+Date: Tue, 11 Feb 2020 14:10:05 +0100
+Subject: pinephone: Add volume_key environment variable
+
+When the user has a volume key pressed volume_key variable will
+contain either value 'down' or 'up', otherwise it will be empty.
+
+Signed-off-by: Ondrej Jirman <megous@megous.com>
+---
+ board/sunxi/Makefile |  1 +
+ board/sunxi/board.c  | 18 ++++++++++++
+ board/sunxi/lradc.c  | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ board/sunxi/lradc.h  | 11 +++++++
+ 4 files changed, 111 insertions(+)
+ create mode 100644 board/sunxi/lradc.c
+ create mode 100644 board/sunxi/lradc.h
+
+diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile
+index c4e13f8c38..6a8a2f5b42 100644
+--- a/board/sunxi/Makefile
++++ b/board/sunxi/Makefile
+@@ -11,3 +11,4 @@ obj-$(CONFIG_SUN7I_GMAC)	+= gmac.o
+ obj-$(CONFIG_MACH_SUN4I)	+= dram_sun4i_auto.o
+ obj-$(CONFIG_MACH_SUN5I)	+= dram_sun5i_auto.o
+ obj-$(CONFIG_MACH_SUN7I)	+= dram_sun5i_auto.o
++obj-$(CONFIG_MACH_SUN50I)	+= lradc.o
+diff --git a/board/sunxi/board.c b/board/sunxi/board.c
+index 6de4b53b11..2bedf7a16a 100644
+--- a/board/sunxi/board.c
++++ b/board/sunxi/board.c
+@@ -39,6 +39,7 @@
+ #include <spl.h>
+ #include <sy8106a.h>
+ #include <asm/setup.h>
++#include "lradc.h"
+ 
+ #if defined CONFIG_VIDEO_LCD_PANEL_I2C && !(defined CONFIG_SPL_BUILD)
+ /* So that we can use pin names in Kconfig and sunxi_name_to_gpio() */
+@@ -622,6 +623,12 @@ void sunxi_board_init(void)
+ {
+ 	int power_failed = 0;
+ 
++#ifdef CONFIG_MACH_SUN50I
++	// we init the lradc in SPL to get the ADC started early to have
++	// a valid sample when U-Boot main binary gets executed.
++	lradc_enable();
++#endif
++
+ #ifdef CONFIG_PINEPHONE_LEDS
+ 	/* PD18:G PD19:R PD20:B */
+ 	gpio_request(SUNXI_GPD(18), "led:green");
+@@ -905,6 +912,17 @@ int misc_init_r(void)
+ 		env_set("mmc_bootdev", "1");
+ 	}
+ 
++#ifdef CONFIG_MACH_SUN50I
++	int key = lradc_get_pressed_key();
++	if (key == KEY_VOLUMEDOWN)
++		env_set("volume_key", "down");
++	else if (key == KEY_VOLUMEUP)
++		env_set("volume_key", "up");
++	
++	// no longer needed
++	lradc_disable();
++#endif
++
+ 	setup_environment(gd->fdt_blob);
+ 
+ #ifdef CONFIG_USB_ETHER
+diff --git a/board/sunxi/lradc.c b/board/sunxi/lradc.c
+new file mode 100644
+index 0000000000..693b198e25
+--- /dev/null
++++ b/board/sunxi/lradc.c
+@@ -0,0 +1,81 @@
++#include <common.h>
++#include <asm/io.h>
++#include "lradc.h"
++
++#define LRADC_BASE		0x1c21800
++
++#define LRADC_CTRL		(LRADC_BASE + 0x00)
++#define LRADC_INTC		(LRADC_BASE + 0x04)
++#define LRADC_INTS		(LRADC_BASE + 0x08)
++#define LRADC_DATA0		(LRADC_BASE + 0x0c)
++#define LRADC_DATA1		(LRADC_BASE + 0x10)
++
++/* LRADC_CTRL bits */
++#define FIRST_CONVERT_DLY(x)	((x) << 24) /* 8 bits */
++#define CHAN_SELECT(x)		((x) << 22) /* 2 bits */
++#define CONTINUE_TIME_SEL(x)	((x) << 16) /* 4 bits */
++#define KEY_MODE_SEL(x)		((x) << 12) /* 2 bits */
++#define LEVELA_B_CNT(x)		((x) << 8)  /* 4 bits */
++#define HOLD_KEY_EN(x)		((x) << 7)
++#define HOLD_EN(x)		((x) << 6)
++#define LEVELB_VOL(x)		((x) << 4)  /* 2 bits */
++#define SAMPLE_RATE(x)		((x) << 2)  /* 2 bits */
++#define ENABLE(x)		((x) << 0)
++
++/* LRADC_INTC and LRADC_INTS bits */
++#define CHAN1_KEYUP_IRQ		BIT(12)
++#define CHAN1_ALRDY_HOLD_IRQ	BIT(11)
++#define CHAN1_HOLD_IRQ		BIT(10)
++#define	CHAN1_KEYDOWN_IRQ	BIT(9)
++#define CHAN1_DATA_IRQ		BIT(8)
++#define CHAN0_KEYUP_IRQ		BIT(4)
++#define CHAN0_ALRDY_HOLD_IRQ	BIT(3)
++#define CHAN0_HOLD_IRQ		BIT(2)
++#define	CHAN0_KEYDOWN_IRQ	BIT(1)
++#define CHAN0_DATA_IRQ		BIT(0)
++
++// this is for PinePhone only
++
++int lradc_get_pressed_key(void)
++{
++	uint32_t val;
++	uint32_t vref = 3000000 * 2 / 3;
++
++	val = readl(LRADC_DATA0) & 0x3f;
++	val = val * vref / 63;
++
++//	printf("lradc=%u\n", val);
++
++	if (val < 200000) // 158730
++		return KEY_VOLUMEUP;
++	else if (val < 400000) // 349206
++		return KEY_VOLUMEDOWN;
++
++	return 0;
++}
++
++void lradc_enable(void)
++{
++	// aldo3 is always on and defaults to 3V
++
++	writel(0xffffffff, LRADC_INTS);
++	writel(0, LRADC_INTC);
++
++	/*
++	 * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to
++	 * stabilize on press, wait (1 + 1) * 4 ms for key release
++	 */
++	writel(FIRST_CONVERT_DLY(0) | LEVELA_B_CNT(0) | HOLD_EN(0) |
++		SAMPLE_RATE(0) | ENABLE(1), LRADC_CTRL);
++
++}
++
++void lradc_disable(void)
++{
++	writel(0xffffffff, LRADC_INTS);
++	writel(0, LRADC_INTC);
++
++	/* Disable lradc, leave other settings unchanged */
++	writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
++		SAMPLE_RATE(2), LRADC_CTRL);
++}
+diff --git a/board/sunxi/lradc.h b/board/sunxi/lradc.h
+new file mode 100644
+index 0000000000..c908401b5b
+--- /dev/null
++++ b/board/sunxi/lradc.h
+@@ -0,0 +1,11 @@
++#pragma once
++
++enum {
++	KEY_NONE = 0,
++	KEY_VOLUMEDOWN = 1,
++	KEY_VOLUMEUP = 2,
++};
++
++int lradc_get_pressed_key(void);
++void lradc_enable(void);
++void lradc_disable(void);
+-- 
+cgit v1.2.3
+