Difference between revisions of "User:Oogwaymaki/PinePhone Multiboot"
Oogwaymaki (talk | contribs) |
m (Fxc moved page How To Create A MultiBoot Configuration In PinePHone to PinePhone Multiboot: Naming convention) |
Revision as of 07:40, 21 August 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
- https://www.96boards.org/documentation/iot/
- https://github.com/dreemurrs-embedded/Jumpdrive
- https://github.com/dreemurrs-embedded/Pineloader
- https://www.thegeekstuff.com/2014/12/patch-command-examples/
- https://stackoverflow.com/questions/9980186/how-to-create-a-patch-for-a-whole-directory-to-update-it
- https://courses.linuxchix.org/kernel-hacking-2002/11-creating-applying-and-submitting-patches.html
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 +