diff --git a/Documentation/boards/mxs.rst b/Documentation/boards/mxs.rst new file mode 100644 index 0000000..6c8bdb5 --- /dev/null +++ b/Documentation/boards/mxs.rst @@ -0,0 +1,119 @@ +Freescale i.MXs +=============== + +Freescale i.MXs or MXS are a SoC family which consists of the i.MX23 +and the i.MX28. These are quite different from the regular i.MX SoCs +and thus are represented by its own architecture in both the Kernel +and barebox. + +Bootlets +-------- + +Traditionally These SoCs need the Freescale bootlets source and the +elf2sb2 binary to build a bootable image out of the barebox binary. +Since the bootlets are board specific and the source code is only +hardly customisable each vendor usually has his own slightly different +version of the bootlets. Booting with the Freescale bootlets is not +described here, refer to the bootlet sourcecode or your vendors +documentation instead. + +U-Boot and barebox have a port of the bootlets integrated into their +source. The barebox bootlet code is derived from the U-Boot bootlet +code written by Marek Vasut. + +Currently only the Karo TX28 is supported by the barebox bootlets, +but we recommend that this approach should be followed for new boards +and existing boards should be ported over. + +Booting Freescale i.MXs +----------------------- + +The Freescale MXS SoCs have a multi staged boot process which needs +different images composed out of different binaries. The ROM executes +a so called bootstream which contains multiple executables. The first +one is executed in SRAM and the purpose of this binary is to setup +the internal PMIC and the SDRAM. The second image is usually the +bootloader itself. In case of barebox the bootstream is composed +out of the self extracting barebox image (pblx) and the prepare +stage for setting up the SDRAM. + +The bootstream image itself is useful for USB boot, but for booting from +SD cards or NAND a BCB header has to be prepended to the image. In case +of SD boot the image has the .mxssd file extension in barebox. + +Since the bootstream images are encrypted they are not suitable for +2nd stage execution. For this purpose the 2nd stage images are generated. + +Booting from USB +---------------- + +barebox has the mxs-usb-loader tool (derived from the sbloader tool from +the rockbox project). If the board is connected to the PC and started in +USB Boot mode it should show up in lsusb:: + + Bus 001 Device 098: ID 15a2:004f Freescale Semiconductor, Inc. i.MX28 SystemOnChip in RecoveryMode + +The bootstream images can be directly booted with:: + + ./scripts/mxs-usb-loader 0 images/barebox-karo-tx28-bootstream.img + +You might require appropriate udev rules or sudo to gain the rights to +access the USB device. + +Booting from SD cards +--------------------- + +The SD images are suitable for booting from SD cards. SD cards need a special +partitioning which can be created with the following fdisk sequence (using +/dev/sdg as example):: + + fdisk /dev/sdg + + Welcome to fdisk (util-linux 2.25.1). + Changes will remain in memory only, until you decide to write them. + Be careful before using the write command. + + + Command (m for help): o + Created a new DOS disklabel with disk identifier 0xd7e5d328. + + Command (m for help): n + Partition type + p primary (0 primary, 0 extended, 4 free) + e extended (container for logical partitions) + Select (default p): p + Partition number (1-4, default 1): 1 + First sector (2048-7829503, default 2048): + Last sector, +sectors or +size{K,M,G,T,P} (2048-7829503, default 7829503): +1M + + Created a new partition 1 of type 'Linux' and of size 1 MiB. + + Command (m for help): t + Selected partition 1 + Hex code (type L to list all codes): 53 + Changed type of partition 'Linux' to 'OnTrack DM6 Aux3'. + + Command (m for help): + + Command (m for help): w + +After writing the new partition table the image can be written directly to +the partition:: + + cat images/barebox-karo-tx28-sd.img > /dev/sdg1 + +** NOTE ** + +The MXS SoCs require a special partition of type 0x53 (OnTrack DM6 Aux) +which contains the BCB header. For some unknown reason the BCB header is +inside a partition, but contains the sector number of the raw device from +which the rest of the image is read from. With standard settings booting +from SD card only works if the partition containing the bootloader starts +at sector 2048 (the standard for fdisk). See the -p parameter to the +mxsboot tool which changes this sector number in the image. + +Booting second stage +-------------------- + +The second stage images can be started with the barebox bootm command or +just jumped into using the 'go' command. diff --git a/Documentation/boards/mxs/KaRo-TX28.rst b/Documentation/boards/mxs/KaRo-TX28.rst index 0fbd4df..0c5dc71 100644 --- a/Documentation/boards/mxs/KaRo-TX28.rst +++ b/Documentation/boards/mxs/KaRo-TX28.rst @@ -34,20 +34,9 @@ **NOTE:** replace the armv5compiler with your ARM v5 cross compiler. -**NOTE:** to use the result, you also need the following resources from Freescale: +This produces the following images: - * the 'bootlets' archive - * the 'elftosb2' encryption tool - * in the case you want to start barebox from an attached SD card - the 'sdimage' tool from Freescale's 'uuc' archive. + * barebox-karo-tx28-bootstream.img - Use with the bcb command + * barebox-karo-tx28-sd.img - Use for SD cards + * barebox-karo-tx28-2nd.img - Use for 2nd stage booting (with bootm) -Memory layout when barebox is running -------------------------------------- - - * 0x40000000 start of SDRAM - * 0x40000100 start of kernel's boot parameters - - * below malloc area: stack area - * below barebox: malloc area - - * 0x47000000 start of barebox diff --git a/arch/arm/boards/imx233-olinuxino/Makefile b/arch/arm/boards/imx233-olinuxino/Makefile index 644f7e5..987b343 100644 --- a/arch/arm/boards/imx233-olinuxino/Makefile +++ b/arch/arm/boards/imx233-olinuxino/Makefile @@ -1,2 +1,3 @@ obj-y = imx23-olinuxino.o lwl-y += lowlevel.o +bbenv-y += defaultenv-imx233-olinuxino diff --git a/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/boot/sd b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/boot/sd new file mode 100644 index 0000000..3ddfb9d --- /dev/null +++ b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/boot/sd @@ -0,0 +1,6 @@ +#!/bin/sh + +global.bootm.image=/mnt/sd-boot/linuximage +global.bootm.oftree=/mnt/sd-boot/imx23-olinuxino.dtb +#global.bootm.initrd= +global.linux.bootargs.dyn.root="root=/dev/mmcblk0p3 rootfstype=ext4 rw rootwait" diff --git a/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/init/automount-sd b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/init/automount-sd new file mode 100644 index 0000000..eb34c9b --- /dev/null +++ b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/init/automount-sd @@ -0,0 +1,4 @@ +#!/bin/sh + +mkdir -p /mnt/sd-boot +automount -d /mnt/sd-boot 'detect mci0 && [ -e /dev/disk0.1 ] && mount /dev/disk0.1 /mnt/sd-boot' diff --git a/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/network/eth0-discover b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/network/eth0-discover new file mode 100644 index 0000000..76494fe --- /dev/null +++ b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/network/eth0-discover @@ -0,0 +1,6 @@ +#!/bin/sh + +# In OLinuXino board i.MX233 SoC uses LAN9512 (attached to USB) to connect +# to an Ethernet LAN. + +usb diff --git a/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/nv/boot.default b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/nv/boot.default new file mode 100644 index 0000000..e1476cf --- /dev/null +++ b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/nv/boot.default @@ -0,0 +1 @@ +sd diff --git a/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/nv/hostname b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/nv/hostname new file mode 100644 index 0000000..e5646ed --- /dev/null +++ b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/nv/hostname @@ -0,0 +1 @@ +olinuxino diff --git a/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/nv/linux.bootargs.base b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/nv/linux.bootargs.base new file mode 100644 index 0000000..5b56caf --- /dev/null +++ b/arch/arm/boards/imx233-olinuxino/defaultenv-imx233-olinuxino/nv/linux.bootargs.base @@ -0,0 +1 @@ +console=ttyAMA0,115200 diff --git a/arch/arm/boards/imx233-olinuxino/env/bin/boot b/arch/arm/boards/imx233-olinuxino/env/bin/boot deleted file mode 100644 index 60dd93f..0000000 --- a/arch/arm/boards/imx233-olinuxino/env/bin/boot +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -. /env/config - -if [ x$1 = xdisk ]; then - rootfs_loc=disk - kernel_loc=disk -elif [ x$1 = xnet ]; then - rootfs_loc=net - kernel_loc=net -fi - -if [ x$ip = xdhcp ]; then - bootargs="$bootargs ip=dhcp" -elif [ x$ip = xnone ]; then - bootargs="$bootargs ip=none" -else - bootargs="$bootargs ip=$eth0.ipaddr::$eth0.gateway:$eth0.netmask:::" -fi - -if [ x$rootfs_loc = xdisk ]; then - bootargs="$bootargs noinitrd rootfstype=$rootfs_type root=/dev/$rootfs_part" -elif [ x$rootfs_loc = xnet ]; then - bootargs="$bootargs root=/dev/nfs nfsroot=$nfsroot,v3,tcp noinitrd" -elif [ x$rootfs_loc = xinitrd ]; then - bootargs="$bootargs root=/dev/ram0 rdinit=/sbin/init" -fi - - -bootm -o $oftree_loc/imx23-olinuxino.dtb /dev/$kernel_part - -echo "Booting failed. Correct setup of 'kernel_part'?" diff --git a/arch/arm/boards/imx233-olinuxino/env/bin/init b/arch/arm/boards/imx233-olinuxino/env/bin/init deleted file mode 100644 index 3ed68f7..0000000 --- a/arch/arm/boards/imx233-olinuxino/env/bin/init +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -PATH=/env/bin -export PATH - -. /env/config - -echo -echo -n "Hit any key to stop autoboot: " -timeout -a $autoboot_timeout -if [ $? != 0 ]; then - exit -fi - -boot diff --git a/arch/arm/boards/imx233-olinuxino/env/config b/arch/arm/boards/imx233-olinuxino/env/config deleted file mode 100644 index c84014f..0000000 --- a/arch/arm/boards/imx233-olinuxino/env/config +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -# use 'dhcp' to do dhcp in barebox and in kernel -# use 'none' if you want to skip kernel ip autoconfiguration -ip=none - -# or set your networking parameters here (if a USB network adapter is attached) -#eth0.ipaddr=a.b.c.d -#eth0.netmask=a.b.c.d -#eth0.gateway=a.b.c.d -#eth0.serverip=a.b.c.d - -# can be either 'net' or 'disk' -kernel_loc=disk - -# can be either 'net', or 'disk' or 'initrd' -rootfs_loc=disk - -# can be any regular filesystem like ext2, ext3, reiserfs in case of 'rootfs_loc=disk' -rootfs_type=ext3 - -# Where is the rootfs in case of 'rootfs_loc=disk' -rootfs_part=mmcblk0p4 - -# Where is the rootfs in case of 'rootfs_loc=net' -nfsroot=FIXME - -# Where to get the kernel image in case of 'kernel_loc=disk' -kernel_part=disk0.2 - -# Where to get the device tree image in case of 'kernel_loc=disk' -oftree_loc=env/oftree - -# base kernel parameter -bootargs="console=ttyAMA0,115200 rw rootwait" - -autoboot_timeout=2 - -# set a fancy prompt (if support is compiled in) -PS1="\e[1;32mbarebox@\e[1;31m\h:\w\e[0m " diff --git a/arch/arm/boards/imx233-olinuxino/env/network/eth0-discover b/arch/arm/boards/imx233-olinuxino/env/network/eth0-discover deleted file mode 100644 index 76494fe..0000000 --- a/arch/arm/boards/imx233-olinuxino/env/network/eth0-discover +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -# In OLinuXino board i.MX233 SoC uses LAN9512 (attached to USB) to connect -# to an Ethernet LAN. - -usb diff --git a/arch/arm/boards/imx233-olinuxino/imx23-olinuxino.c b/arch/arm/boards/imx233-olinuxino/imx23-olinuxino.c index fa95d72..b87a676 100644 --- a/arch/arm/boards/imx233-olinuxino/imx23-olinuxino.c +++ b/arch/arm/boards/imx233-olinuxino/imx23-olinuxino.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -39,60 +40,6 @@ .f_min = 400000, }; -static const uint32_t pad_setup[] = { - /* debug port */ - PWM1_DUART_TX | STRENGTH(S4MA), /* PWM0/DUART_TXD - U_DEBUG PIN 2 */ - PWM0_DUART_RX | STRENGTH(S4MA), /* PWM0/DUART_RXD - U_DEBUG PIN 1 */ - - /* auart */ - I2C_SDA_AUART1_RX | STRENGTH(S4MA), - I2C_CLK_AUART1_TX | STRENGTH(S4MA), - - /* lcd */ - LCD_D17 | STRENGTH(S12MA), /*PIN18/LCD_D17 - GPIO PIN 3 */ - LCD_D16 | STRENGTH(S12MA), - LCD_D15 | STRENGTH(S12MA), - LCD_D14 | STRENGTH(S12MA), - LCD_D13 | STRENGTH(S12MA), - LCD_D12 | STRENGTH(S12MA), - LCD_D11 | STRENGTH(S12MA), - LCD_D10 | STRENGTH(S12MA), - LCD_D9 | STRENGTH(S12MA), - LCD_D8 | STRENGTH(S12MA), - LCD_D7 | STRENGTH(S12MA), - LCD_D6 | STRENGTH(S12MA), - LCD_D5 | STRENGTH(S12MA), - LCD_D4 | STRENGTH(S12MA), - LCD_D3 | STRENGTH(S12MA), - LCD_D2 | STRENGTH(S12MA), /* PIN3/LCD_D02 - GPIO PIN 31*/ - LCD_D1 | STRENGTH(S12MA), /* PIN2/LCD_D01 - GPIO PIN 33*/ - LCD_D0 | STRENGTH(S12MA), /* PIN1/LCD_D00 - GPIO PIN 35*/ - LCD_CS, /* PIN26/LCD_CS - GPIO PIN 20*/ - LCD_RS, /* PIN25/LCD_RS - GPIO PIN 18*/ - LCD_WR, /* PIN24/LCD_WR - GPIO PIN 16*/ - LCD_RESET, /* PIN23/LCD_DISP - GPIO PIN 14*/ - LCD_ENABE | STRENGTH(S12MA), /* PIN22/LCD_EN/I2C_SCL - GPIO PIN 12*/ - LCD_VSYNC | STRENGTH(S12MA), /* PIN21/LCD_HSYNC/I2C_SDA- GPIO PIN 10*/ - LCD_HSYNC | STRENGTH(S12MA), /* PIN20/LCD_VSYNC - GPIO PIN 8*/ - LCD_DOTCLOCK | STRENGTH(S12MA), /* PIN19/LCD_DOTCLK - GPIO PIN 6*/ - - - /* SD card interface */ - SSP1_DATA0 | PULLUP(1), - SSP1_DATA1 | PULLUP(1), - SSP1_DATA2 | PULLUP(1), - SSP1_DATA3 | PULLUP(1), - SSP1_SCK, - SSP1_CMD | PULLUP(1), - SSP1_DETECT | PULLUP(1), - - /* led */ - SSP1_DETECT_GPIO | GPIO_OUT | GPIO_VALUE(1), - - /* gpio - USB hub LAN9512-JZX*/ - GPMI_ALE_GPIO | GPIO_OUT | GPIO_VALUE(1), -}; - static int imx23_olinuxino_mem_init(void) { arm_add_mem_device("ram0", IMX_MEMORY_BASE, 64 * 1024 * 1024); @@ -103,21 +50,25 @@ static void olinuxino_init_usb(void) { - imx23_usb_phy_enable(); add_generic_usb_ehci_device(DEVICE_ID_DYNAMIC, IMX_USB_BASE, NULL); } +static struct gpio_led led1 = { + .gpio = 65, + .led = { + .name = "led1", + } +}; + static int imx23_olinuxino_devices_init(void) { - int i; - - /* initizalize gpios */ - for (i = 0; i < ARRAY_SIZE(pad_setup); i++) - imx_gpio_mode(pad_setup[i]); - armlinux_set_architecture(MACH_TYPE_IMX233_OLINUXINO); + defaultenv_append_directory(defaultenv_imx233_olinuxino); + + led_gpio_register(&led1); + led_set_trigger(LED_TRIGGER_HEARTBEAT, &led1.led); add_generic_device("mxs_mci", DEVICE_ID_DYNAMIC, NULL, IMX_SSP1_BASE, 0x8000, IORESOURCE_MEM, &mci_pdata); diff --git a/arch/arm/boards/imx233-olinuxino/lowlevel.c b/arch/arm/boards/imx233-olinuxino/lowlevel.c index bfc76cc..6e4b830 100644 --- a/arch/arm/boards/imx233-olinuxino/lowlevel.c +++ b/arch/arm/boards/imx233-olinuxino/lowlevel.c @@ -3,9 +3,175 @@ #include #include #include +#include +#include +#include +#include -void __naked barebox_arm_reset_vector(void) +ENTRY_FUNCTION(start_barebox_olinuxino_imx23, r0, r1, r2) { - arm_cpu_lowlevel_init(); barebox_arm_entry(IMX_MEMORY_BASE, SZ_64M, NULL); } + +static const uint32_t pad_setup[] = { + /* debug port */ + PWM1_DUART_TX | STRENGTH(S4MA), /* PWM0/DUART_TXD - U_DEBUG PIN 2 */ + PWM0_DUART_RX | STRENGTH(S4MA), /* PWM0/DUART_RXD - U_DEBUG PIN 1 */ + + /* SDRAM */ + EMI_D0 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D1 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D2 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D3 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D4 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D5 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D6 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D7 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D8 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D9 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D10 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D11 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D12 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D13 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D14 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_D15 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_DQM0 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_DQM1 | VE_2_5V | STRENGTH(S12MA) | PULLUP(1), + EMI_DQS0 | VE_2_5V | STRENGTH(S12MA), + EMI_DQS1 | VE_2_5V | STRENGTH(S12MA), + + EMI_CLK | VE_2_5V | STRENGTH(S12MA), + EMI_CLKN | VE_2_5V | STRENGTH(S12MA), + EMI_A0 | VE_2_5V | STRENGTH(S12MA), + EMI_A1 | VE_2_5V | STRENGTH(S12MA), + EMI_A2 | VE_2_5V | STRENGTH(S12MA), + EMI_A3 | VE_2_5V | STRENGTH(S12MA), + EMI_A4 | VE_2_5V | STRENGTH(S12MA), + EMI_A5 | VE_2_5V | STRENGTH(S12MA), + EMI_A6 | VE_2_5V | STRENGTH(S12MA), + EMI_A7 | VE_2_5V | STRENGTH(S12MA), + EMI_A8 | VE_2_5V | STRENGTH(S12MA), + EMI_A9 | VE_2_5V | STRENGTH(S12MA), + EMI_A10 | VE_2_5V | STRENGTH(S12MA), + EMI_A11 | VE_2_5V | STRENGTH(S12MA), + EMI_A12 | VE_2_5V | STRENGTH(S12MA), + EMI_BA0 | VE_2_5V | STRENGTH(S12MA), + EMI_BA1 | VE_2_5V | STRENGTH(S12MA), + + EMI_CASN | VE_2_5V | STRENGTH(S12MA), + EMI_CE0N | VE_2_5V | STRENGTH(S12MA), + EMI_CE1N | VE_2_5V | STRENGTH(S12MA), + EMI_CKE | VE_2_5V | STRENGTH(S12MA), + EMI_RASN | VE_2_5V | STRENGTH(S12MA), + EMI_WEN | VE_2_5V | STRENGTH(S12MA), + + /* auart */ + I2C_SDA_AUART1_RX | STRENGTH(S4MA), + I2C_CLK_AUART1_TX | STRENGTH(S4MA), + + /* LCD */ + LCD_D17 | STRENGTH(S12MA), /*PIN18/LCD_D17 - GPIO PIN 3 */ + LCD_D16 | STRENGTH(S12MA), + LCD_D15 | STRENGTH(S12MA), + LCD_D14 | STRENGTH(S12MA), + LCD_D13 | STRENGTH(S12MA), + LCD_D12 | STRENGTH(S12MA), + LCD_D11 | STRENGTH(S12MA), + LCD_D10 | STRENGTH(S12MA), + LCD_D9 | STRENGTH(S12MA), + LCD_D8 | STRENGTH(S12MA), + LCD_D7 | STRENGTH(S12MA), + LCD_D6 | STRENGTH(S12MA), + LCD_D5 | STRENGTH(S12MA), + LCD_D4 | STRENGTH(S12MA), + LCD_D3 | STRENGTH(S12MA), + LCD_D2 | STRENGTH(S12MA), /* PIN3/LCD_D02 - GPIO PIN 31*/ + LCD_D1 | STRENGTH(S12MA), /* PIN2/LCD_D01 - GPIO PIN 33*/ + LCD_D0 | STRENGTH(S12MA), /* PIN1/LCD_D00 - GPIO PIN 35*/ + LCD_CS, /* PIN26/LCD_CS - GPIO PIN 20*/ + LCD_RS, /* PIN25/LCD_RS - GPIO PIN 18*/ + LCD_WR, /* PIN24/LCD_WR - GPIO PIN 16*/ + LCD_RESET, /* PIN23/LCD_DISP - GPIO PIN 14*/ + LCD_ENABE | STRENGTH(S12MA), /* PIN22/LCD_EN/I2C_SCL - GPIO PIN 12*/ + LCD_VSYNC | STRENGTH(S12MA), /* PIN21/LCD_HSYNC/I2C_SDA- GPIO PIN 10*/ + LCD_HSYNC | STRENGTH(S12MA), /* PIN20/LCD_VSYNC - GPIO PIN 8*/ + LCD_DOTCLOCK | STRENGTH(S12MA), /* PIN19/LCD_DOTCLK - GPIO PIN 6*/ + + /* SD card interface */ + SSP1_DATA0 | PULLUP(1), + SSP1_DATA1 | PULLUP(1), + SSP1_DATA2 | PULLUP(1), + SSP1_DATA3 | PULLUP(1), + SSP1_SCK, + SSP1_CMD | PULLUP(1), + SSP1_DETECT | PULLUP(1), + + /* LED */ + SSP1_DETECT_GPIO | GPIO_OUT | GPIO_VALUE(1), + + /* GPIO - USB hub LAN9512-JZX*/ + GPMI_ALE_GPIO | GPIO_OUT | GPIO_VALUE(1), +}; + + +/* Fine-tune the DRAM configuration. */ +void imx23_olinuxino_adjust_memory_params(uint32_t *dram_vals) +{ + /* Enable Auto Precharge. */ + dram_vals[3] |= 1 << 8; + /* Enable Fast Writes. */ + dram_vals[5] |= 1 << 8; + /* tEMRS = 3*tCK */ + dram_vals[10] &= ~(0x3 << 8); + dram_vals[10] |= (0x3 << 8); + /* CASLAT = 3*tCK */ + dram_vals[11] &= ~(0x3 << 0); + dram_vals[11] |= (0x3 << 0); + /* tCKE = 1*tCK */ + dram_vals[12] &= ~(0x7 << 0); + dram_vals[12] |= (0x1 << 0); + /* CASLAT_LIN_GATE = 3*tCK , CASLAT_LIN = 3*tCK, tWTR=2*tCK */ + dram_vals[13] &= ~((0xf << 16) | (0xf << 24) | (0xf << 0)); + dram_vals[13] |= (0x6 << 16) | (0x6 << 24) | (0x2 << 0); + /* tDAL = 6*tCK */ + dram_vals[15] &= ~(0xf << 16); + dram_vals[15] |= (0x6 << 16); + /* tREF = 1040*tCK */ + dram_vals[26] &= ~0xffff; + dram_vals[26] |= 0x0410; + /* tRAS_MAX = 9334*tCK */ + dram_vals[32] &= ~0xffff; + dram_vals[32] |= 0x2475; +} + +static noinline void imx23_olinuxino_init(void) +{ + int i; + + /* initizalize gpios */ + for (i = 0; i < ARRAY_SIZE(pad_setup); i++) + imx_gpio_mode(pad_setup[i]); + + pr_debug("initializing power...\n"); + + mx23_power_init(); + + pr_debug("initializing SDRAM...\n"); + + imx23_olinuxino_adjust_memory_params(mx23_dram_vals); + mx23_mem_init(); + + pr_debug("DONE\n"); +} + +ENTRY_FUNCTION(prep_start_barebox_olinuxino_imx23, r0, r1, r2) +{ + void (*back)(unsigned long) = (void *)get_lr(); + + relocate_to_current_adr(); + setup_c(); + + imx23_olinuxino_init(); + + back(0); +} diff --git a/arch/arm/boards/karo-tx28/env/config b/arch/arm/boards/karo-tx28/env/config deleted file mode 100644 index a6b1025..0000000 --- a/arch/arm/boards/karo-tx28/env/config +++ /dev/null @@ -1,41 +0,0 @@ -# -# - -baseboard=tx28stk5 - -# use 'dhcp' to do dhcp in barebox and in kernel -# use 'none' if you want to skip kernel ip autoconfiguration -ip=dhcp - -# or set your networking parameters here -#eth0.ipaddr=a.b.c.d -#eth0.ethaddr=de:ad:be:ef:00:00 -#eth0.netmask=a.b.c.d -#eth0.serverip=a.b.c.d -#eth0.gateway=a.b.c.d - -# can be either 'nfs' or 'tftp' -kernel_loc=tftp -# can be either 'net' or 'initrd' -rootfs_loc=net - -# can be either 'jffs2' or 'ubifs' -rootfs_type=ubifs -rootfsimage=root-${global.hostname}.$rootfs_type - -kernelimage=zImage-${global.hostname} -#kernelimage=uImage-${global.hostname} -#kernelimage=Image-${global.hostname} -#kernelimage=Image-${global.hostname}.lzo - -if [ -n $user ]; then - kernelimage="$user"-"$kernelimage" - nfsroot="$eth0.serverip:/home/$user/nfsroot/${global.hostname}" - rootfsimage="$user"-"$rootfsimage" -else - nfsroot="$eth0.serverip:/path/to/nfs/root" -fi - -autoboot_timeout=3 - -bootargs="console=ttyAM0,115200 tx28_base=$baseboard" diff --git a/arch/arm/boards/karo-tx28/lowlevel.c b/arch/arm/boards/karo-tx28/lowlevel.c index 3c7248e..c5fdda1 100644 --- a/arch/arm/boards/karo-tx28/lowlevel.c +++ b/arch/arm/boards/karo-tx28/lowlevel.c @@ -1,11 +1,65 @@ +#define pr_fmt(fmt) "KARO TX28: " fmt +#define DEBUG + #include #include #include #include #include +#include +#include +#include +#include +#include -void __naked barebox_arm_reset_vector(void) +ENTRY_FUNCTION(start_barebox_karo_tx28, r0, r1, r2) { - arm_cpu_lowlevel_init(); barebox_arm_entry(IMX_MEMORY_BASE, SZ_128M, NULL); } + +static const uint32_t iomux_pads[] = { + /* EMI */ + EMI_DATA0, EMI_DATA1, EMI_DATA2, EMI_DATA3, EMI_DATA4, EMI_DATA5, + EMI_DATA6, EMI_DATA7, EMI_DATA8, EMI_DATA9, EMI_DATA10, EMI_DATA11, + EMI_DATA12, EMI_DATA13, EMI_DATA14, EMI_DATA15, EMI_ODT0, EMI_DQM0, + EMI_ODT1, EMI_DQM1, EMI_DDR_OPEN_FB, EMI_CLK, EMI_DSQ0, EMI_DSQ1, + EMI_DDR_OPEN, EMI_A0, EMI_A1, EMI_A2, EMI_A3, EMI_A4, EMI_A5, + EMI_A6, EMI_A7, EMI_A8, EMI_A9, EMI_A10, EMI_A11, EMI_A12, EMI_A13, + EMI_A14, EMI_BA0, EMI_BA1, EMI_BA2, EMI_CASN, EMI_RASN, EMI_WEN, + EMI_CE0N, EMI_CE1N, EMI_CKE, + + /* Debug UART */ + AUART0_RTS_DUART_TX | VE_3_3V | STRENGTH(S8MA), + AUART0_CTS_DUART_RX | VE_3_3V | STRENGTH(S8MA), +}; + +static noinline void karo_tx28_init(void) +{ + int i; + + /* initialize muxing */ + for (i = 0; i < ARRAY_SIZE(iomux_pads); i++) + imx_gpio_mode(iomux_pads[i]); + + pr_debug("initializing power...\n"); + + mx28_power_init_battery_input(); + + pr_debug("initializing SDRAM...\n"); + + mx28_mem_init(); + + pr_debug("DONE\n"); +} + +ENTRY_FUNCTION(prep_start_barebox_karo_tx28, r0, r1, r2) +{ + void (*back)(unsigned long) = (void *)get_lr(); + + relocate_to_current_adr(); + setup_c(); + + karo_tx28_init(); + + back(0); +} diff --git a/arch/arm/configs/imx233-olinuxino_defconfig b/arch/arm/configs/imx233-olinuxino_defconfig index 75b5911..5aed6a9 100644 --- a/arch/arm/configs/imx233-olinuxino_defconfig +++ b/arch/arm/configs/imx233-olinuxino_defconfig @@ -1,52 +1,89 @@ CONFIG_ARCH_MXS=y CONFIG_MACH_IMX233_OLINUXINO=y +CONFIG_MXS_OCOTP=y CONFIG_AEABI=y CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y -CONFIG_BROKEN=y -CONFIG_EXPERIMENTAL=y -CONFIG_MODULES=y -CONFIG_LONGHELP=y +CONFIG_ARM_UNWIND=y +CONFIG_MMU=y +CONFIG_TEXT_BASE=0x0 +CONFIG_MALLOC_SIZE=0x1000000 +CONFIG_MALLOC_TLSF=y +CONFIG_KALLSYMS=y +CONFIG_RELOCATABLE=y +CONFIG_HUSH_FANCY_PROMPT=y CONFIG_CMDLINE_EDITING=y CONFIG_AUTO_COMPLETE=y CONFIG_MENU=y +CONFIG_BLSPEC=y +CONFIG_DEFAULT_COMPRESSION_LZO=y CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/imx233-olinuxino/env" -CONFIG_BAREBOXENV_TARGET=y +CONFIG_RESET_SOURCE=y CONFIG_DEBUG_INFO=y -CONFIG_ENABLE_FLASH_NOISE=y -CONFIG_ENABLE_PARTITION_NOISE=y -CONFIG_ENABLE_DEVICE_NOISE=y -CONFIG_CMD_EDIT=y -CONFIG_CMD_SLEEP=y -CONFIG_CMD_SAVEENV=y -CONFIG_CMD_EXPORT=y -CONFIG_CMD_PRINTENV=y -CONFIG_CMD_READLINE=y -CONFIG_CMD_ECHO_E=y -CONFIG_CMD_MTEST=y -CONFIG_CMD_MTEST_ALTERNATIVE=y +CONFIG_CMD_DMESG=y +CONFIG_LONGHELP=y +CONFIG_CMD_IOMEM=y +CONFIG_CMD_IMD=y +CONFIG_CMD_MEMINFO=y CONFIG_CMD_BOOTM_SHOW_TYPE=y CONFIG_CMD_BOOTM_VERBOSE=y CONFIG_CMD_BOOTM_INITRD=y CONFIG_CMD_BOOTM_OFTREE=y CONFIG_CMD_BOOTM_OFTREE_UIMAGE=y -CONFIG_CMD_UIMAGE=y -CONFIG_CMD_BOOTZ=y +# CONFIG_CMD_BOOTU is not set +CONFIG_CMD_GO=y CONFIG_CMD_RESET=y -CONFIG_CMD_OFTREE=y -CONFIG_CMD_TIMEOUT=y CONFIG_CMD_PARTITION=y -CONFIG_NET=y +CONFIG_CMD_EXPORT=y +CONFIG_CMD_DEFAULTENV=y +CONFIG_CMD_LOADENV=y +CONFIG_CMD_PRINTENV=y +CONFIG_CMD_MAGICVAR=y +CONFIG_CMD_MAGICVAR_HELP=y +CONFIG_CMD_SAVEENV=y +CONFIG_CMD_CMP=y +CONFIG_CMD_FILETYPE=y +CONFIG_CMD_LN=y +CONFIG_CMD_MD5SUM=y +CONFIG_CMD_SHA1SUM=y +CONFIG_CMD_SHA224SUM=y +CONFIG_CMD_SHA256SUM=y +CONFIG_CMD_UNCOMPRESS=y +CONFIG_CMD_LET=y +CONFIG_CMD_MSLEEP=y +CONFIG_CMD_READF=y +CONFIG_CMD_SLEEP=y CONFIG_CMD_DHCP=y -CONFIG_NET_NFS=y +CONFIG_CMD_HOST=y CONFIG_CMD_PING=y -CONFIG_NET_TFTP=y -CONFIG_NET_TFTP_PUSH=y +CONFIG_CMD_TFTP=y +CONFIG_CMD_ECHO_E=y +CONFIG_CMD_EDIT=y +CONFIG_CMD_MENU=y +CONFIG_CMD_MENU_MANAGEMENT=y +CONFIG_CMD_MENUTREE=y +CONFIG_CMD_SPLASH=y +CONFIG_CMD_READLINE=y +CONFIG_CMD_TIMEOUT=y +CONFIG_CMD_CRC=y +CONFIG_CMD_CRC_CMP=y +CONFIG_CMD_MEMTEST=y +CONFIG_CMD_CLK=y +CONFIG_CMD_DETECT=y +CONFIG_CMD_FLASH=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_2048=y +CONFIG_CMD_OF_NODE=y +CONFIG_CMD_OF_PROPERTY=y +CONFIG_CMD_OF_DISPLAY_TIMINGS=y +CONFIG_CMD_OFTREE=y +CONFIG_CMD_TIME=y +CONFIG_NET=y +CONFIG_NET_NFS=y CONFIG_NET_NETCONSOLE=y -CONFIG_NET_RESOLV=y +CONFIG_DRIVER_SERIAL_AUART=y CONFIG_NET_USB=y CONFIG_NET_USB_SMSC95XX=y -CONFIG_DISK_INTF_PLATFORM_IDE=y +# CONFIG_SPI is not set CONFIG_USB_HOST=y CONFIG_USB_EHCI=y CONFIG_USB_STORAGE=y @@ -56,8 +93,15 @@ CONFIG_MCI_STARTUP=y CONFIG_MCI_MXS=y CONFIG_LED=y +CONFIG_LED_GPIO=y +CONFIG_LED_TRIGGERS=y CONFIG_KEYBOARD_GPIO=y CONFIG_MXS_APBH_DMA=y +CONFIG_FS_EXT4=y CONFIG_FS_TFTP=y CONFIG_FS_NFS=y CONFIG_FS_FAT=y +CONFIG_FS_FAT_WRITE=y +CONFIG_FS_FAT_LFN=y +CONFIG_ZLIB=y +CONFIG_LZO_DECOMPRESS=y diff --git a/arch/arm/configs/tx28stk5_defconfig b/arch/arm/configs/tx28stk5_defconfig index fe80d98..d5714f2 100644 --- a/arch/arm/configs/tx28stk5_defconfig +++ b/arch/arm/configs/tx28stk5_defconfig @@ -4,58 +4,92 @@ CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y CONFIG_ARM_UNWIND=y CONFIG_MMU=y -CONFIG_MALLOC_SIZE=0x01000000 -CONFIG_BROKEN=y +CONFIG_TEXT_BASE=0x0 +CONFIG_MALLOC_SIZE=0x0 CONFIG_MALLOC_TLSF=y CONFIG_KALLSYMS=y -CONFIG_LONGHELP=y -CONFIG_GLOB=y +CONFIG_RELOCATABLE=y CONFIG_HUSH_FANCY_PROMPT=y CONFIG_CMDLINE_EDITING=y CONFIG_AUTO_COMPLETE=y CONFIG_MENU=y -CONFIG_DEFAULT_ENVIRONMENT_GENERIC=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/karo-tx28/env" +CONFIG_BLSPEC=y +CONFIG_IMD=y +CONFIG_DEFAULT_COMPRESSION_LZO=y +CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y +CONFIG_RESET_SOURCE=y CONFIG_DEBUG_INFO=y -CONFIG_CMD_EDIT=y -CONFIG_CMD_SLEEP=y -CONFIG_CMD_SAVEENV=y -CONFIG_CMD_LOADENV=y -CONFIG_CMD_EXPORT=y -CONFIG_CMD_PRINTENV=y -CONFIG_CMD_READLINE=y -CONFIG_CMD_TIME=y -CONFIG_CMD_ECHO_E=y +CONFIG_DEBUG_LL=y +CONFIG_PBL_CONSOLE=y +CONFIG_CMD_DMESG=y +CONFIG_LONGHELP=y CONFIG_CMD_IOMEM=y -CONFIG_CMD_MTEST=y -CONFIG_CMD_MTEST_ALTERNATIVE=y +CONFIG_CMD_MEMINFO=y CONFIG_CMD_BOOTM_SHOW_TYPE=y CONFIG_CMD_BOOTM_VERBOSE=y CONFIG_CMD_BOOTM_INITRD=y CONFIG_CMD_BOOTM_OFTREE=y CONFIG_CMD_BOOTM_OFTREE_UIMAGE=y -CONFIG_CMD_UIMAGE=y -CONFIG_CMD_RESET=y CONFIG_CMD_GO=y -CONFIG_CMD_TIMEOUT=y +CONFIG_CMD_RESET=y +CONFIG_CMD_UIMAGE=y CONFIG_CMD_PARTITION=y +CONFIG_CMD_EXPORT=y +CONFIG_CMD_DEFAULTENV=y +CONFIG_CMD_LOADENV=y +CONFIG_CMD_PRINTENV=y CONFIG_CMD_MAGICVAR=y CONFIG_CMD_MAGICVAR_HELP=y -CONFIG_CMD_SPLASH=y -CONFIG_CMD_GPIO=y +CONFIG_CMD_SAVEENV=y +CONFIG_CMD_CMP=y +CONFIG_CMD_FILETYPE=y +CONFIG_CMD_LN=y +CONFIG_CMD_MD5SUM=y +CONFIG_CMD_SHA1SUM=y +CONFIG_CMD_SHA224SUM=y +CONFIG_CMD_SHA256SUM=y CONFIG_CMD_UNCOMPRESS=y -CONFIG_NET=y +CONFIG_CMD_LET=y +CONFIG_CMD_MSLEEP=y +CONFIG_CMD_READF=y +CONFIG_CMD_SLEEP=y CONFIG_CMD_DHCP=y +CONFIG_CMD_HOST=y +CONFIG_CMD_MIITOOL=y +CONFIG_CMD_PING=y CONFIG_CMD_TFTP=y -CONFIG_FS_TFTP=y -CONFIG_NET_RESOLV=y +CONFIG_CMD_ECHO_E=y +CONFIG_CMD_EDIT=y +CONFIG_CMD_MENUTREE=y +CONFIG_CMD_SPLASH=y +CONFIG_CMD_READLINE=y +CONFIG_CMD_TIMEOUT=y +CONFIG_CMD_CRC=y +CONFIG_CMD_CRC_CMP=y +CONFIG_CMD_CLK=y +CONFIG_CMD_DETECT=y +CONFIG_CMD_FLASH=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_2048=y +CONFIG_CMD_OF_NODE=y +CONFIG_CMD_OF_PROPERTY=y +CONFIG_CMD_OF_DISPLAY_TIMINGS=y +CONFIG_CMD_OFTREE=y +CONFIG_CMD_TIME=y +CONFIG_NET=y +CONFIG_DRIVER_SERIAL_AUART=y CONFIG_DRIVER_NET_FEC_IMX=y # CONFIG_SPI is not set +CONFIG_MTD=y CONFIG_VIDEO=y CONFIG_DRIVER_VIDEO_STM=y CONFIG_MCI=y CONFIG_MCI_STARTUP=y CONFIG_MCI_MXS=y +CONFIG_MXS_APBH_DMA=y +CONFIG_FS_EXT4=y +CONFIG_FS_TFTP=y +CONFIG_FS_NFS=y CONFIG_FS_FAT=y CONFIG_FS_FAT_WRITE=y CONFIG_FS_FAT_LFN=y diff --git a/arch/arm/cpu/start.c b/arch/arm/cpu/start.c index f6061f4..418870f 100644 --- a/arch/arm/cpu/start.c +++ b/arch/arm/cpu/start.c @@ -16,6 +16,7 @@ * GNU General Public License for more details. * */ +#define pr_fmt(fmt) "start.c: " fmt #include #include @@ -62,6 +63,8 @@ setup_c(); + pr_debug("memory at 0x%08lx, size 0x%08lx\n", membase, memsize); + barebox_boarddata = boarddata; arm_stack_top = endmem; endmem -= STACK_SIZE; /* Stack */ @@ -74,6 +77,7 @@ if (IS_ENABLED(CONFIG_PBL_IMAGE)) { arm_set_cache_functions(); } else { + pr_debug("enabling MMU, ttb @ 0x%08lx\n", endmem); arm_early_mmu_cache_invalidate(); mmu_early_enable(membase, memsize, endmem); } @@ -87,6 +91,8 @@ uint32_t totalsize = get_unaligned_be32(boarddata + 4); endmem -= ALIGN(totalsize, 64); barebox_boot_dtb = (void *)endmem; + pr_debug("found DTB in boarddata, copying to 0x%p\n", + barebox_boot_dtb); memcpy(barebox_boot_dtb, boarddata, totalsize); } @@ -115,8 +121,13 @@ malloc_start = malloc_end - SZ_1G; } + pr_debug("initializing malloc pool at 0x%08lx (size 0x%08lx)\n", + malloc_start, malloc_end - malloc_start); + mem_malloc_init((void *)malloc_start, (void *)malloc_end - 1); + pr_debug("starting barebox...\n"); + start_barebox(); } diff --git a/arch/arm/cpu/uncompress.c b/arch/arm/cpu/uncompress.c index ab194e1..b0b7c6d 100644 --- a/arch/arm/cpu/uncompress.c +++ b/arch/arm/cpu/uncompress.c @@ -17,6 +17,7 @@ * GNU General Public License for more details. * */ +#define pr_fmt(fmt) "uncompress.c: " fmt #include #include @@ -76,9 +77,12 @@ setup_c(); + pr_debug("memory at 0x%08lx, size 0x%08lx\n", membase, memsize); + if (IS_ENABLED(CONFIG_MMU_EARLY)) { endmem &= ~0x3fff; endmem -= SZ_16K; /* ttb */ + pr_debug("enabling MMU, ttb @ 0x%08x\n", endmem); mmu_early_enable(membase, memsize, endmem); } @@ -93,6 +97,9 @@ pg_start = image_end + 1; pg_len = *(image_end); + pr_debug("uncompressing barebox binary at 0x%p (size 0x%08x) to 0x%08lx\n", + pg_start, pg_len, barebox_base); + pbl_barebox_uncompress((void*)barebox_base, pg_start, pg_len); arm_early_mmu_cache_flush(); @@ -103,6 +110,8 @@ else barebox = (void *)barebox_base; + pr_debug("jumping to uncompressed image at 0x%p\n", barebox); + barebox(membase, memsize, boarddata); } diff --git a/arch/arm/include/asm/common.h b/arch/arm/include/asm/common.h index 133bb8e..9ff3b19 100644 --- a/arch/arm/include/asm/common.h +++ b/arch/arm/include/asm/common.h @@ -16,6 +16,32 @@ return pc; } +static inline unsigned long get_lr(void) +{ + unsigned long lr; + + __asm__ __volatile__( + "mov %0, lr\n" + : "=r" (lr) + : + : "memory"); + + return lr; +} + +static inline unsigned long get_sp(void) +{ + unsigned long sp; + + __asm__ __volatile__( + "mov %0, sp\n" + : "=r" (sp) + : + : "memory"); + + return sp; +} + static inline void arm_setup_stack(unsigned long top) { __asm__ __volatile__("mov sp, %0" : : "r"(top)); diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index ccf1f59..850a99c 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -12,4 +12,61 @@ extern void memcpy_toio(volatile void __iomem *, const void *, size_t); extern void memset_io(volatile void __iomem *, int, size_t); +/* + * Clear and set bits in one shot. These macros can be used to clear and + * set multiple bits in a register using a single call. These macros can + * also be used to set a multiple-bit bit pattern using a mask, by + * specifying the mask in the 'clear' parameter and the new bit pattern + * in the 'set' parameter. + */ + +#define out_arch(type,endian,a,v) __raw_write##type(cpu_to_##endian(v),a) +#define in_arch(type,endian,a) endian##_to_cpu(__raw_read##type(a)) + +#define out_le64(a,v) out_arch(q,le64,a,v) +#define out_le32(a,v) out_arch(l,le32,a,v) +#define out_le16(a,v) out_arch(w,le16,a,v) + +#define in_le64(a) in_arch(q,le64,a) +#define in_le32(a) in_arch(l,le32,a) +#define in_le16(a) in_arch(w,le16,a) + +#define out_be32(a,v) out_arch(l,be32,a,v) +#define out_be16(a,v) out_arch(w,be16,a,v) + +#define in_be32(a) in_arch(l,be32,a) +#define in_be16(a) in_arch(w,be16,a) + +#define out_8(a,v) __raw_writeb(v,a) +#define in_8(a) __raw_readb(a) + +#define clrbits(type, addr, clear) \ + out_##type((addr), in_##type(addr) & ~(clear)) + +#define setbits(type, addr, set) \ + out_##type((addr), in_##type(addr) | (set)) + +#define clrsetbits(type, addr, clear, set) \ + out_##type((addr), (in_##type(addr) & ~(clear)) | (set)) + +#define clrbits_be32(addr, clear) clrbits(be32, addr, clear) +#define setbits_be32(addr, set) setbits(be32, addr, set) +#define clrsetbits_be32(addr, clear, set) clrsetbits(be32, addr, clear, set) + +#define clrbits_le32(addr, clear) clrbits(le32, addr, clear) +#define setbits_le32(addr, set) setbits(le32, addr, set) +#define clrsetbits_le32(addr, clear, set) clrsetbits(le32, addr, clear, set) + +#define clrbits_be16(addr, clear) clrbits(be16, addr, clear) +#define setbits_be16(addr, set) setbits(be16, addr, set) +#define clrsetbits_be16(addr, clear, set) clrsetbits(be16, addr, clear, set) + +#define clrbits_le16(addr, clear) clrbits(le16, addr, clear) +#define setbits_le16(addr, set) setbits(le16, addr, set) +#define clrsetbits_le16(addr, clear, set) clrsetbits(le16, addr, clear, set) + +#define clrbits_8(addr, clear) clrbits(8, addr, clear) +#define setbits_8(addr, set) setbits(8, addr, set) +#define clrsetbits_8(addr, clear, set) clrsetbits(8, addr, clear, set) + #endif /* __ASM_ARM_IO_H */ diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig index 214f940..4022710 100644 --- a/arch/arm/mach-mxs/Kconfig +++ b/arch/arm/mach-mxs/Kconfig @@ -45,6 +45,8 @@ config MACH_IMX233_OLINUXINO bool "Olimex.ltd imx223-olinuxino" + select HAVE_DEFAULT_ENVIRONMENT_NEW + select HAVE_PBL_MULTI_IMAGES help Say Y here if you are using the imx233-olinuxino @@ -59,6 +61,8 @@ config MACH_TX28 bool "KARO tx28" + select HAVE_DEFAULT_ENVIRONMENT_NEW + select HAVE_PBL_MULTI_IMAGES help Say Y here if you are using the KARO TX28 CPU module. @@ -134,4 +138,11 @@ endmenu +config ARCH_MXS_USBLOADER + bool "compile mxs-usb-loader" + help + mxs-usb-loader is a tool to upload and start mxs bootstream images to an + i.MX SoC in ROM boot mode. It requires libusb, so make sure you have the libusb + devel package installed on your machine. + endif diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile index bd6892e..e384336 100644 --- a/arch/arm/mach-mxs/Makefile +++ b/arch/arm/mach-mxs/Makefile @@ -1,5 +1,7 @@ obj-y += imx.o iomux-imx.o power.o +pbl-y += iomux-imx.o obj-$(CONFIG_ARCH_IMX23) += clocksource-imx23.o usb-imx23.o soc-imx23.o obj-$(CONFIG_ARCH_IMX28) += clocksource-imx28.o usb-imx28.o soc-imx28.o obj-$(CONFIG_MXS_OCOTP) += ocotp.o obj-$(CONFIG_MXS_CMD_BCB) += bcb.o +pbl-y += power-init.o mem-init.o lradc-init.o diff --git a/arch/arm/mach-mxs/clocksource-imx23.c b/arch/arm/mach-mxs/clocksource-imx23.c index d9b7c1a..8279ee2 100644 --- a/arch/arm/mach-mxs/clocksource-imx23.c +++ b/arch/arm/mach-mxs/clocksource-imx23.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-mxs/clocksource-imx28.c b/arch/arm/mach-mxs/clocksource-imx28.c index d26aa93..4f38af6 100644 --- a/arch/arm/mach-mxs/clocksource-imx28.c +++ b/arch/arm/mach-mxs/clocksource-imx28.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-mxs/include/mach/imx23-regs.h b/arch/arm/mach-mxs/include/mach/imx23-regs.h index 7fb664b..3fd2f3d 100644 --- a/arch/arm/mach-mxs/include/mach/imx23-regs.h +++ b/arch/arm/mach-mxs/include/mach/imx23-regs.h @@ -24,9 +24,11 @@ #define IMX_DBGUART_BASE 0x80070000 #define IMX_TIM1_BASE 0x80068000 #define IMX_IOMUXC_BASE 0x80018000 +#define IMX_EMI_BASE 0x80020000 #define IMX_OCOTP_BASE 0x8002c000 #define IMX_WDT_BASE 0x8005c000 #define IMX_CCM_BASE 0x80040000 +#define IMX_LRADC_BASE 0x80050000 #define IMX_I2C1_BASE 0x80058000 #define IMX_SSP1_BASE 0x80010000 #define IMX_FB_BASE 0x80030000 @@ -35,5 +37,6 @@ #define IMX_USBPHY_BASE 0x8007c000 #define IMX_DIGCTL_BASE 0x8001c000 #define IMX_USB_BASE 0x80080000 +#define IMX_SDRAMC_BASE 0x800e0000 #endif /* __ASM_ARCH_MX23_REGS_H */ diff --git a/arch/arm/mach-mxs/include/mach/imx28-regs.h b/arch/arm/mach-mxs/include/mach/imx28-regs.h index 0882829..de0d882 100644 --- a/arch/arm/mach-mxs/include/mach/imx28-regs.h +++ b/arch/arm/mach-mxs/include/mach/imx28-regs.h @@ -25,10 +25,12 @@ #define IMX_SSP3_BASE 0x80016000 #define IMX_IOMUXC_BASE 0x80018000 #define IMX_DIGCTL_BASE 0x8001c000 +#define IMX_EMI_BASE 0x80020000 #define IMX_OCOTP_BASE 0x8002c000 #define IMX_FB_BASE 0x80030000 #define IMX_CCM_BASE 0x80040000 #define IMX_POWER_BASE 0x80044000 +#define IMX_LRADC_BASE 0x80050000 #define IMX_WDT_BASE 0x80056000 #define IMX_I2C0_BASE 0x80058000 #define IMX_I2C1_BASE 0x8005a000 @@ -43,6 +45,7 @@ #define IMX_USBPHY1_BASE 0x8007e000 #define IMX_USB0_BASE 0x80080000 #define IMX_USB1_BASE 0x80090000 +#define IMX_SDRAMC_BASE 0x800e0000 #define IMX_FEC0_BASE 0x800F0000 #define IMX_FEC1_BASE 0x800F4000 diff --git a/arch/arm/mach-mxs/include/mach/init.h b/arch/arm/mach-mxs/include/mach/init.h new file mode 100644 index 0000000..1f9d8d4 --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/init.h @@ -0,0 +1,33 @@ +/* + * Freescale i.MX28 SPL functions + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __M28_INIT_H__ +#define __M28_INIT_H__ + +void mxs_early_delay(int delay); + +void mx23_power_init(void); +void mx23_power_init_battery_input(void); +void mx28_power_init(void); +void mx28_power_init_battery_input(void); +void mxs_power_wait_pswitch(void); + +extern uint32_t mx28_dram_vals[]; +extern uint32_t mx23_dram_vals[]; + +void mx23_mem_init(void); +void mx28_mem_init(void); +void mxs_mem_setup_cpu_and_hbus(void); +void mxs_mem_setup_vdda(void); +void mxs_mem_init_clock(unsigned char divider); + +void mxs_lradc_init(void); +void mxs_lradc_enable_batt_measurement(void); + +#endif /* __M28_INIT_H__ */ diff --git a/arch/arm/mach-mxs/include/mach/iomux.h b/arch/arm/mach-mxs/include/mach/iomux.h index 4361be5..0091dba 100644 --- a/arch/arm/mach-mxs/include/mach/iomux.h +++ b/arch/arm/mach-mxs/include/mach/iomux.h @@ -97,7 +97,7 @@ * .. PORTF(1, 15). So the PORTF macro is more ugly than necessary. */ # define PORTF(bank,bit) (BANK((bank) / 2) | BANKPIN((((bank) & 1) << 4) | (bit)) | ERROR((bit) & ~15) | ERROR((bank) & ~7)) -# define VE_2_5V VOLTAGE(1) +# define VE_2_5V VOLTAGE(0) # include #endif diff --git a/arch/arm/mach-mxs/include/mach/regs-clkctrl-mx23.h b/arch/arm/mach-mxs/include/mach/regs-clkctrl-mx23.h new file mode 100644 index 0000000..289b159 --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/regs-clkctrl-mx23.h @@ -0,0 +1,208 @@ +/* + * Freescale i.MX23 CLKCTRL Register Definitions + * + * Copyright (C) 2012 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MX23_REGS_CLKCTRL_H__ +#define __MX23_REGS_CLKCTRL_H__ + +#include + +#ifndef __ASSEMBLY__ +struct mxs_clkctrl_regs { + mxs_reg_32(hw_clkctrl_pll0ctrl0) /* 0x00 */ + uint32_t hw_clkctrl_pll0ctrl1; /* 0x10 */ + uint32_t reserved_pll0ctrl1[3]; /* 0x14-0x1c */ + mxs_reg_32(hw_clkctrl_cpu) /* 0x20 */ + mxs_reg_32(hw_clkctrl_hbus) /* 0x30 */ + mxs_reg_32(hw_clkctrl_xbus) /* 0x40 */ + mxs_reg_32(hw_clkctrl_xtal) /* 0x50 */ + mxs_reg_32(hw_clkctrl_pix) /* 0x60 */ + mxs_reg_32(hw_clkctrl_ssp0) /* 0x70 */ + mxs_reg_32(hw_clkctrl_gpmi) /* 0x80 */ + mxs_reg_32(hw_clkctrl_spdif) /* 0x90 */ + mxs_reg_32(hw_clkctrl_emi) /* 0xa0 */ + + uint32_t reserved1[4]; + + mxs_reg_32(hw_clkctrl_saif0) /* 0xc0 */ + mxs_reg_32(hw_clkctrl_tv) /* 0xd0 */ + mxs_reg_32(hw_clkctrl_etm) /* 0xe0 */ + mxs_reg_8(hw_clkctrl_frac0) /* 0xf0 */ + mxs_reg_8(hw_clkctrl_frac1) /* 0x100 */ + mxs_reg_32(hw_clkctrl_clkseq) /* 0x110 */ + mxs_reg_32(hw_clkctrl_reset) /* 0x120 */ + mxs_reg_32(hw_clkctrl_status) /* 0x130 */ + mxs_reg_32(hw_clkctrl_version) /* 0x140 */ +}; +#endif + +#define CLKCTRL_PLL0CTRL0_LFR_SEL_MASK (0x3 << 28) +#define CLKCTRL_PLL0CTRL0_LFR_SEL_OFFSET 28 +#define CLKCTRL_PLL0CTRL0_LFR_SEL_DEFAULT (0x0 << 28) +#define CLKCTRL_PLL0CTRL0_LFR_SEL_TIMES_2 (0x1 << 28) +#define CLKCTRL_PLL0CTRL0_LFR_SEL_TIMES_05 (0x2 << 28) +#define CLKCTRL_PLL0CTRL0_LFR_SEL_UNDEFINED (0x3 << 28) +#define CLKCTRL_PLL0CTRL0_CP_SEL_MASK (0x3 << 24) +#define CLKCTRL_PLL0CTRL0_CP_SEL_OFFSET 24 +#define CLKCTRL_PLL0CTRL0_CP_SEL_DEFAULT (0x0 << 24) +#define CLKCTRL_PLL0CTRL0_CP_SEL_TIMES_2 (0x1 << 24) +#define CLKCTRL_PLL0CTRL0_CP_SEL_TIMES_05 (0x2 << 24) +#define CLKCTRL_PLL0CTRL0_CP_SEL_UNDEFINED (0x3 << 24) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_MASK (0x3 << 20) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_OFFSET 20 +#define CLKCTRL_PLL0CTRL0_DIV_SEL_DEFAULT (0x0 << 20) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_LOWER (0x1 << 20) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_LOWEST (0x2 << 20) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_UNDEFINED (0x3 << 20) +#define CLKCTRL_PLL0CTRL0_EN_USB_CLKS (1 << 18) +#define CLKCTRL_PLL0CTRL0_POWER (1 << 16) + +#define CLKCTRL_PLL0CTRL1_LOCK (1 << 31) +#define CLKCTRL_PLL0CTRL1_FORCE_LOCK (1 << 30) +#define CLKCTRL_PLL0CTRL1_LOCK_COUNT_MASK 0xffff +#define CLKCTRL_PLL0CTRL1_LOCK_COUNT_OFFSET 0 + +#define CLKCTRL_CPU_BUSY_REF_XTAL (1 << 29) +#define CLKCTRL_CPU_BUSY_REF_CPU (1 << 28) +#define CLKCTRL_CPU_DIV_XTAL_FRAC_EN (1 << 26) +#define CLKCTRL_CPU_DIV_XTAL_MASK (0x3ff << 16) +#define CLKCTRL_CPU_DIV_XTAL_OFFSET 16 +#define CLKCTRL_CPU_INTERRUPT_WAIT (1 << 12) +#define CLKCTRL_CPU_DIV_CPU_FRAC_EN (1 << 10) +#define CLKCTRL_CPU_DIV_CPU_MASK 0x3f +#define CLKCTRL_CPU_DIV_CPU_OFFSET 0 + +#define CLKCTRL_HBUS_BUSY (1 << 29) +#define CLKCTRL_HBUS_DCP_AS_ENABLE (1 << 28) +#define CLKCTRL_HBUS_PXP_AS_ENABLE (1 << 27) +#define CLKCTRL_HBUS_APBHDMA_AS_ENABLE (1 << 26) +#define CLKCTRL_HBUS_APBXDMA_AS_ENABLE (1 << 25) +#define CLKCTRL_HBUS_TRAFFIC_JAM_AS_ENABLE (1 << 24) +#define CLKCTRL_HBUS_TRAFFIC_AS_ENABLE (1 << 23) +#define CLKCTRL_HBUS_CPU_DATA_AS_ENABLE (1 << 22) +#define CLKCTRL_HBUS_CPU_INSTR_AS_ENABLE (1 << 21) +#define CLKCTRL_HBUS_AUTO_SLOW_MODE (1 << 20) +#define CLKCTRL_HBUS_SLOW_DIV_MASK (0x7 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_OFFSET 16 +#define CLKCTRL_HBUS_SLOW_DIV_BY1 (0x0 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY2 (0x1 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY4 (0x2 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY8 (0x3 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY16 (0x4 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY32 (0x5 << 16) +#define CLKCTRL_HBUS_DIV_FRAC_EN (1 << 5) +#define CLKCTRL_HBUS_DIV_MASK 0x1f +#define CLKCTRL_HBUS_DIV_OFFSET 0 + +#define CLKCTRL_XBUS_BUSY (1 << 31) +#define CLKCTRL_XBUS_DIV_FRAC_EN (1 << 10) +#define CLKCTRL_XBUS_DIV_MASK 0x3ff +#define CLKCTRL_XBUS_DIV_OFFSET 0 + +#define CLKCTRL_XTAL_UART_CLK_GATE (1 << 31) +#define CLKCTRL_XTAL_FILT_CLK24M_GATE (1 << 30) +#define CLKCTRL_XTAL_PWM_CLK24M_GATE (1 << 29) +#define CLKCTRL_XTAL_DRI_CLK24M_GATE (1 << 28) +#define CLKCTRL_XTAL_DIGCTRL_CLK1M_GATE (1 << 27) +#define CLKCTRL_XTAL_TIMROT_CLK32K_GATE (1 << 26) +#define CLKCTRL_XTAL_DIV_UART_MASK 0x3 +#define CLKCTRL_XTAL_DIV_UART_OFFSET 0 + +#define CLKCTRL_PIX_CLKGATE (1 << 31) +#define CLKCTRL_PIX_BUSY (1 << 29) +#define CLKCTRL_PIX_DIV_FRAC_EN (1 << 12) +#define CLKCTRL_PIX_DIV_MASK 0xfff +#define CLKCTRL_PIX_DIV_OFFSET 0 + +#define CLKCTRL_SSP_CLKGATE (1 << 31) +#define CLKCTRL_SSP_BUSY (1 << 29) +#define CLKCTRL_SSP_DIV_FRAC_EN (1 << 9) +#define CLKCTRL_SSP_DIV_MASK 0x1ff +#define CLKCTRL_SSP_DIV_OFFSET 0 + +#define CLKCTRL_GPMI_CLKGATE (1 << 31) +#define CLKCTRL_GPMI_BUSY (1 << 29) +#define CLKCTRL_GPMI_DIV_FRAC_EN (1 << 10) +#define CLKCTRL_GPMI_DIV_MASK 0x3ff +#define CLKCTRL_GPMI_DIV_OFFSET 0 + +#define CLKCTRL_SPDIF_CLKGATE (1 << 31) + +#define CLKCTRL_EMI_CLKGATE (1 << 31) +#define CLKCTRL_EMI_SYNC_MODE_EN (1 << 30) +#define CLKCTRL_EMI_BUSY_REF_XTAL (1 << 29) +#define CLKCTRL_EMI_BUSY_REF_EMI (1 << 28) +#define CLKCTRL_EMI_BUSY_REF_CPU (1 << 27) +#define CLKCTRL_EMI_BUSY_SYNC_MODE (1 << 26) +#define CLKCTRL_EMI_BUSY_DCC_RESYNC (1 << 17) +#define CLKCTRL_EMI_DCC_RESYNC_ENABLE (1 << 16) +#define CLKCTRL_EMI_DIV_XTAL_MASK (0xf << 8) +#define CLKCTRL_EMI_DIV_XTAL_OFFSET 8 +#define CLKCTRL_EMI_DIV_EMI_MASK 0x3f +#define CLKCTRL_EMI_DIV_EMI_OFFSET 0 + +#define CLKCTRL_IR_CLKGATE (1 << 31) +#define CLKCTRL_IR_AUTO_DIV (1 << 29) +#define CLKCTRL_IR_IR_BUSY (1 << 28) +#define CLKCTRL_IR_IROV_BUSY (1 << 27) +#define CLKCTRL_IR_IROV_DIV_MASK (0x1ff << 16) +#define CLKCTRL_IR_IROV_DIV_OFFSET 16 +#define CLKCTRL_IR_IR_DIV_MASK 0x3ff +#define CLKCTRL_IR_IR_DIV_OFFSET 0 + +#define CLKCTRL_SAIF0_CLKGATE (1 << 31) +#define CLKCTRL_SAIF0_BUSY (1 << 29) +#define CLKCTRL_SAIF0_DIV_FRAC_EN (1 << 16) +#define CLKCTRL_SAIF0_DIV_MASK 0xffff +#define CLKCTRL_SAIF0_DIV_OFFSET 0 + +#define CLKCTRL_TV_CLK_TV108M_GATE (1 << 31) +#define CLKCTRL_TV_CLK_TV_GATE (1 << 30) + +#define CLKCTRL_ETM_CLKGATE (1 << 31) +#define CLKCTRL_ETM_BUSY (1 << 29) +#define CLKCTRL_ETM_DIV_FRAC_EN (1 << 6) +#define CLKCTRL_ETM_DIV_MASK 0x3f +#define CLKCTRL_ETM_DIV_OFFSET 0 + +#define CLKCTRL_FRAC_CLKGATE (1 << 7) +#define CLKCTRL_FRAC_STABLE (1 << 6) +#define CLKCTRL_FRAC_FRAC_MASK 0x3f +#define CLKCTRL_FRAC_FRAC_OFFSET 0 +#define CLKCTRL_FRAC0_CPU 0 +#define CLKCTRL_FRAC0_EMI 1 +#define CLKCTRL_FRAC0_PIX 2 +#define CLKCTRL_FRAC0_IO0 3 +#define CLKCTRL_FRAC1_VID 3 + +#define CLKCTRL_CLKSEQ_BYPASS_ETM (1 << 8) +#define CLKCTRL_CLKSEQ_BYPASS_CPU (1 << 7) +#define CLKCTRL_CLKSEQ_BYPASS_EMI (1 << 6) +#define CLKCTRL_CLKSEQ_BYPASS_SSP0 (1 << 5) +#define CLKCTRL_CLKSEQ_BYPASS_GPMI (1 << 4) +#define CLKCTRL_CLKSEQ_BYPASS_IR (1 << 3) +#define CLKCTRL_CLKSEQ_BYPASS_PIX (1 << 1) +#define CLKCTRL_CLKSEQ_BYPASS_SAIF (1 << 0) + +#define CLKCTRL_RESET_CHIP (1 << 1) +#define CLKCTRL_RESET_DIG (1 << 0) + +#define CLKCTRL_STATUS_CPU_LIMIT_MASK (0x3 << 30) +#define CLKCTRL_STATUS_CPU_LIMIT_OFFSET 30 + +#define CLKCTRL_VERSION_MAJOR_MASK (0xff << 24) +#define CLKCTRL_VERSION_MAJOR_OFFSET 24 +#define CLKCTRL_VERSION_MINOR_MASK (0xff << 16) +#define CLKCTRL_VERSION_MINOR_OFFSET 16 +#define CLKCTRL_VERSION_STEP_MASK 0xffff +#define CLKCTRL_VERSION_STEP_OFFSET 0 + +#endif /* __MX23_REGS_CLKCTRL_H__ */ diff --git a/arch/arm/mach-mxs/include/mach/regs-clkctrl-mx28.h b/arch/arm/mach-mxs/include/mach/regs-clkctrl-mx28.h new file mode 100644 index 0000000..aebb489 --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/regs-clkctrl-mx28.h @@ -0,0 +1,283 @@ +/* + * Freescale i.MX28 CLKCTRL Register Definitions + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MX28_REGS_CLKCTRL_H__ +#define __MX28_REGS_CLKCTRL_H__ + +#include + +#ifndef __ASSEMBLY__ +struct mxs_clkctrl_regs { + mxs_reg_32(hw_clkctrl_pll0ctrl0) /* 0x00 */ + uint32_t hw_clkctrl_pll0ctrl1; /* 0x10 */ + uint32_t reserved_pll0ctrl1[3]; /* 0x14-0x1c */ + mxs_reg_32(hw_clkctrl_pll1ctrl0) /* 0x20 */ + uint32_t hw_clkctrl_pll1ctrl1; /* 0x30 */ + uint32_t reserved_pll1ctrl1[3]; /* 0x34-0x3c */ + mxs_reg_32(hw_clkctrl_pll2ctrl0) /* 0x40 */ + mxs_reg_32(hw_clkctrl_cpu) /* 0x50 */ + mxs_reg_32(hw_clkctrl_hbus) /* 0x60 */ + mxs_reg_32(hw_clkctrl_xbus) /* 0x70 */ + mxs_reg_32(hw_clkctrl_xtal) /* 0x80 */ + mxs_reg_32(hw_clkctrl_ssp0) /* 0x90 */ + mxs_reg_32(hw_clkctrl_ssp1) /* 0xa0 */ + mxs_reg_32(hw_clkctrl_ssp2) /* 0xb0 */ + mxs_reg_32(hw_clkctrl_ssp3) /* 0xc0 */ + mxs_reg_32(hw_clkctrl_gpmi) /* 0xd0 */ + mxs_reg_32(hw_clkctrl_spdif) /* 0xe0 */ + mxs_reg_32(hw_clkctrl_emi) /* 0xf0 */ + mxs_reg_32(hw_clkctrl_saif0) /* 0x100 */ + mxs_reg_32(hw_clkctrl_saif1) /* 0x110 */ + mxs_reg_32(hw_clkctrl_lcdif) /* 0x120 */ + mxs_reg_32(hw_clkctrl_etm) /* 0x130 */ + mxs_reg_32(hw_clkctrl_enet) /* 0x140 */ + mxs_reg_32(hw_clkctrl_hsadc) /* 0x150 */ + mxs_reg_32(hw_clkctrl_flexcan) /* 0x160 */ + + uint32_t reserved[16]; + + mxs_reg_8(hw_clkctrl_frac0) /* 0x1b0 */ + mxs_reg_8(hw_clkctrl_frac1) /* 0x1c0 */ + mxs_reg_32(hw_clkctrl_clkseq) /* 0x1d0 */ + mxs_reg_32(hw_clkctrl_reset) /* 0x1e0 */ + mxs_reg_32(hw_clkctrl_status) /* 0x1f0 */ + mxs_reg_32(hw_clkctrl_version) /* 0x200 */ +}; +#endif + +#define CLKCTRL_PLL0CTRL0_LFR_SEL_MASK (0x3 << 28) +#define CLKCTRL_PLL0CTRL0_LFR_SEL_OFFSET 28 +#define CLKCTRL_PLL0CTRL0_LFR_SEL_DEFAULT (0x0 << 28) +#define CLKCTRL_PLL0CTRL0_LFR_SEL_TIMES_2 (0x1 << 28) +#define CLKCTRL_PLL0CTRL0_LFR_SEL_TIMES_05 (0x2 << 28) +#define CLKCTRL_PLL0CTRL0_LFR_SEL_UNDEFINED (0x3 << 28) +#define CLKCTRL_PLL0CTRL0_CP_SEL_MASK (0x3 << 24) +#define CLKCTRL_PLL0CTRL0_CP_SEL_OFFSET 24 +#define CLKCTRL_PLL0CTRL0_CP_SEL_DEFAULT (0x0 << 24) +#define CLKCTRL_PLL0CTRL0_CP_SEL_TIMES_2 (0x1 << 24) +#define CLKCTRL_PLL0CTRL0_CP_SEL_TIMES_05 (0x2 << 24) +#define CLKCTRL_PLL0CTRL0_CP_SEL_UNDEFINED (0x3 << 24) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_MASK (0x3 << 20) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_OFFSET 20 +#define CLKCTRL_PLL0CTRL0_DIV_SEL_DEFAULT (0x0 << 20) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_LOWER (0x1 << 20) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_LOWEST (0x2 << 20) +#define CLKCTRL_PLL0CTRL0_DIV_SEL_UNDEFINED (0x3 << 20) +#define CLKCTRL_PLL0CTRL0_EN_USB_CLKS (1 << 18) +#define CLKCTRL_PLL0CTRL0_POWER (1 << 17) + +#define CLKCTRL_PLL0CTRL1_LOCK (1 << 31) +#define CLKCTRL_PLL0CTRL1_FORCE_LOCK (1 << 30) +#define CLKCTRL_PLL0CTRL1_LOCK_COUNT_MASK 0xffff +#define CLKCTRL_PLL0CTRL1_LOCK_COUNT_OFFSET 0 + +#define CLKCTRL_PLL1CTRL0_CLKGATEEMI (1 << 31) +#define CLKCTRL_PLL1CTRL0_LFR_SEL_MASK (0x3 << 28) +#define CLKCTRL_PLL1CTRL0_LFR_SEL_OFFSET 28 +#define CLKCTRL_PLL1CTRL0_LFR_SEL_DEFAULT (0x0 << 28) +#define CLKCTRL_PLL1CTRL0_LFR_SEL_TIMES_2 (0x1 << 28) +#define CLKCTRL_PLL1CTRL0_LFR_SEL_TIMES_05 (0x2 << 28) +#define CLKCTRL_PLL1CTRL0_LFR_SEL_UNDEFINED (0x3 << 28) +#define CLKCTRL_PLL1CTRL0_CP_SEL_MASK (0x3 << 24) +#define CLKCTRL_PLL1CTRL0_CP_SEL_OFFSET 24 +#define CLKCTRL_PLL1CTRL0_CP_SEL_DEFAULT (0x0 << 24) +#define CLKCTRL_PLL1CTRL0_CP_SEL_TIMES_2 (0x1 << 24) +#define CLKCTRL_PLL1CTRL0_CP_SEL_TIMES_05 (0x2 << 24) +#define CLKCTRL_PLL1CTRL0_CP_SEL_UNDEFINED (0x3 << 24) +#define CLKCTRL_PLL1CTRL0_DIV_SEL_MASK (0x3 << 20) +#define CLKCTRL_PLL1CTRL0_DIV_SEL_OFFSET 20 +#define CLKCTRL_PLL1CTRL0_DIV_SEL_DEFAULT (0x0 << 20) +#define CLKCTRL_PLL1CTRL0_DIV_SEL_LOWER (0x1 << 20) +#define CLKCTRL_PLL1CTRL0_DIV_SEL_LOWEST (0x2 << 20) +#define CLKCTRL_PLL1CTRL0_DIV_SEL_UNDEFINED (0x3 << 20) +#define CLKCTRL_PLL1CTRL0_EN_USB_CLKS (1 << 18) +#define CLKCTRL_PLL1CTRL0_POWER (1 << 17) + +#define CLKCTRL_PLL1CTRL1_LOCK (1 << 31) +#define CLKCTRL_PLL1CTRL1_FORCE_LOCK (1 << 30) +#define CLKCTRL_PLL1CTRL1_LOCK_COUNT_MASK 0xffff +#define CLKCTRL_PLL1CTRL1_LOCK_COUNT_OFFSET 0 + +#define CLKCTRL_PLL2CTRL0_CLKGATE (1 << 31) +#define CLKCTRL_PLL2CTRL0_LFR_SEL_MASK (0x3 << 28) +#define CLKCTRL_PLL2CTRL0_LFR_SEL_OFFSET 28 +#define CLKCTRL_PLL2CTRL0_HOLD_RING_OFF_B (1 << 26) +#define CLKCTRL_PLL2CTRL0_CP_SEL_MASK (0x3 << 24) +#define CLKCTRL_PLL2CTRL0_CP_SEL_OFFSET 24 +#define CLKCTRL_PLL2CTRL0_POWER (1 << 23) + +#define CLKCTRL_CPU_BUSY_REF_XTAL (1 << 29) +#define CLKCTRL_CPU_BUSY_REF_CPU (1 << 28) +#define CLKCTRL_CPU_DIV_XTAL_FRAC_EN (1 << 26) +#define CLKCTRL_CPU_DIV_XTAL_MASK (0x3ff << 16) +#define CLKCTRL_CPU_DIV_XTAL_OFFSET 16 +#define CLKCTRL_CPU_INTERRUPT_WAIT (1 << 12) +#define CLKCTRL_CPU_DIV_CPU_FRAC_EN (1 << 10) +#define CLKCTRL_CPU_DIV_CPU_MASK 0x3f +#define CLKCTRL_CPU_DIV_CPU_OFFSET 0 + +#define CLKCTRL_HBUS_ASM_BUSY (1 << 31) +#define CLKCTRL_HBUS_DCP_AS_ENABLE (1 << 30) +#define CLKCTRL_HBUS_PXP_AS_ENABLE (1 << 29) +#define CLKCTRL_HBUS_ASM_EMIPORT_AS_ENABLE (1 << 27) +#define CLKCTRL_HBUS_APBHDMA_AS_ENABLE (1 << 26) +#define CLKCTRL_HBUS_APBXDMA_AS_ENABLE (1 << 25) +#define CLKCTRL_HBUS_TRAFFIC_JAM_AS_ENABLE (1 << 24) +#define CLKCTRL_HBUS_TRAFFIC_AS_ENABLE (1 << 23) +#define CLKCTRL_HBUS_CPU_DATA_AS_ENABLE (1 << 22) +#define CLKCTRL_HBUS_CPU_INSTR_AS_ENABLE (1 << 21) +#define CLKCTRL_HBUS_ASM_ENABLE (1 << 20) +#define CLKCTRL_HBUS_AUTO_CLEAR_DIV_ENABLE (1 << 19) +#define CLKCTRL_HBUS_SLOW_DIV_MASK (0x7 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_OFFSET 16 +#define CLKCTRL_HBUS_SLOW_DIV_BY1 (0x0 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY2 (0x1 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY4 (0x2 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY8 (0x3 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY16 (0x4 << 16) +#define CLKCTRL_HBUS_SLOW_DIV_BY32 (0x5 << 16) +#define CLKCTRL_HBUS_DIV_FRAC_EN (1 << 5) +#define CLKCTRL_HBUS_DIV_MASK 0x1f +#define CLKCTRL_HBUS_DIV_OFFSET 0 + +#define CLKCTRL_XBUS_BUSY (1 << 31) +#define CLKCTRL_XBUS_AUTO_CLEAR_DIV_ENABLE (1 << 11) +#define CLKCTRL_XBUS_DIV_FRAC_EN (1 << 10) +#define CLKCTRL_XBUS_DIV_MASK 0x3ff +#define CLKCTRL_XBUS_DIV_OFFSET 0 + +#define CLKCTRL_XTAL_UART_CLK_GATE (1 << 31) +#define CLKCTRL_XTAL_PWM_CLK24M_GATE (1 << 29) +#define CLKCTRL_XTAL_TIMROT_CLK32K_GATE (1 << 26) +#define CLKCTRL_XTAL_DIV_UART_MASK 0x3 +#define CLKCTRL_XTAL_DIV_UART_OFFSET 0 + +#define CLKCTRL_SSP_CLKGATE (1 << 31) +#define CLKCTRL_SSP_BUSY (1 << 29) +#define CLKCTRL_SSP_DIV_FRAC_EN (1 << 9) +#define CLKCTRL_SSP_DIV_MASK 0x1ff +#define CLKCTRL_SSP_DIV_OFFSET 0 + +#define CLKCTRL_GPMI_CLKGATE (1 << 31) +#define CLKCTRL_GPMI_BUSY (1 << 29) +#define CLKCTRL_GPMI_DIV_FRAC_EN (1 << 10) +#define CLKCTRL_GPMI_DIV_MASK 0x3ff +#define CLKCTRL_GPMI_DIV_OFFSET 0 + +#define CLKCTRL_SPDIF_CLKGATE (1 << 31) + +#define CLKCTRL_EMI_CLKGATE (1 << 31) +#define CLKCTRL_EMI_SYNC_MODE_EN (1 << 30) +#define CLKCTRL_EMI_BUSY_REF_XTAL (1 << 29) +#define CLKCTRL_EMI_BUSY_REF_EMI (1 << 28) +#define CLKCTRL_EMI_BUSY_REF_CPU (1 << 27) +#define CLKCTRL_EMI_BUSY_SYNC_MODE (1 << 26) +#define CLKCTRL_EMI_BUSY_DCC_RESYNC (1 << 17) +#define CLKCTRL_EMI_DCC_RESYNC_ENABLE (1 << 16) +#define CLKCTRL_EMI_DIV_XTAL_MASK (0xf << 8) +#define CLKCTRL_EMI_DIV_XTAL_OFFSET 8 +#define CLKCTRL_EMI_DIV_EMI_MASK 0x3f +#define CLKCTRL_EMI_DIV_EMI_OFFSET 0 + +#define CLKCTRL_SAIF0_CLKGATE (1 << 31) +#define CLKCTRL_SAIF0_BUSY (1 << 29) +#define CLKCTRL_SAIF0_DIV_FRAC_EN (1 << 16) +#define CLKCTRL_SAIF0_DIV_MASK 0xffff +#define CLKCTRL_SAIF0_DIV_OFFSET 0 + +#define CLKCTRL_SAIF1_CLKGATE (1 << 31) +#define CLKCTRL_SAIF1_BUSY (1 << 29) +#define CLKCTRL_SAIF1_DIV_FRAC_EN (1 << 16) +#define CLKCTRL_SAIF1_DIV_MASK 0xffff +#define CLKCTRL_SAIF1_DIV_OFFSET 0 + +#define CLKCTRL_DIS_LCDIF_CLKGATE (1 << 31) +#define CLKCTRL_DIS_LCDIF_BUSY (1 << 29) +#define CLKCTRL_DIS_LCDIF_DIV_FRAC_EN (1 << 13) +#define CLKCTRL_DIS_LCDIF_DIV_MASK 0x1fff +#define CLKCTRL_DIS_LCDIF_DIV_OFFSET 0 + +#define CLKCTRL_ETM_CLKGATE (1 << 31) +#define CLKCTRL_ETM_BUSY (1 << 29) +#define CLKCTRL_ETM_DIV_FRAC_EN (1 << 7) +#define CLKCTRL_ETM_DIV_MASK 0x7f +#define CLKCTRL_ETM_DIV_OFFSET 0 + +#define CLKCTRL_ENET_SLEEP (1 << 31) +#define CLKCTRL_ENET_DISABLE (1 << 30) +#define CLKCTRL_ENET_STATUS (1 << 29) +#define CLKCTRL_ENET_BUSY_TIME (1 << 27) +#define CLKCTRL_ENET_DIV_TIME_MASK (0x3f << 21) +#define CLKCTRL_ENET_DIV_TIME_OFFSET 21 +#define CLKCTRL_ENET_TIME_SEL_MASK (0x3 << 19) +#define CLKCTRL_ENET_TIME_SEL_OFFSET 19 +#define CLKCTRL_ENET_TIME_SEL_XTAL (0x0 << 19) +#define CLKCTRL_ENET_TIME_SEL_PLL (0x1 << 19) +#define CLKCTRL_ENET_TIME_SEL_RMII_CLK (0x2 << 19) +#define CLKCTRL_ENET_TIME_SEL_UNDEFINED (0x3 << 19) +#define CLKCTRL_ENET_CLK_OUT_EN (1 << 18) +#define CLKCTRL_ENET_RESET_BY_SW_CHIP (1 << 17) +#define CLKCTRL_ENET_RESET_BY_SW (1 << 16) + +#define CLKCTRL_HSADC_RESETB (1 << 30) +#define CLKCTRL_HSADC_FREQDIV_MASK (0x3 << 28) +#define CLKCTRL_HSADC_FREQDIV_OFFSET 28 + +#define CLKCTRL_FLEXCAN_STOP_CAN0 (1 << 30) +#define CLKCTRL_FLEXCAN_CAN0_STATUS (1 << 29) +#define CLKCTRL_FLEXCAN_STOP_CAN1 (1 << 28) +#define CLKCTRL_FLEXCAN_CAN1_STATUS (1 << 27) + +#define CLKCTRL_FRAC_CLKGATE (1 << 7) +#define CLKCTRL_FRAC_STABLE (1 << 6) +#define CLKCTRL_FRAC_FRAC_MASK 0x3f +#define CLKCTRL_FRAC_FRAC_OFFSET 0 +#define CLKCTRL_FRAC0_CPU 0 +#define CLKCTRL_FRAC0_EMI 1 +#define CLKCTRL_FRAC0_IO1 2 +#define CLKCTRL_FRAC0_IO0 3 +#define CLKCTRL_FRAC1_PIX 0 +#define CLKCTRL_FRAC1_HSADC 1 +#define CLKCTRL_FRAC1_GPMI 2 + +#define CLKCTRL_CLKSEQ_BYPASS_CPU (1 << 18) +#define CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF (1 << 14) +#define CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF_BYPASS (0x1 << 14) +#define CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF_PFD (0x0 << 14) +#define CLKCTRL_CLKSEQ_BYPASS_ETM (1 << 8) +#define CLKCTRL_CLKSEQ_BYPASS_EMI (1 << 7) +#define CLKCTRL_CLKSEQ_BYPASS_SSP3 (1 << 6) +#define CLKCTRL_CLKSEQ_BYPASS_SSP2 (1 << 5) +#define CLKCTRL_CLKSEQ_BYPASS_SSP1 (1 << 4) +#define CLKCTRL_CLKSEQ_BYPASS_SSP0 (1 << 3) +#define CLKCTRL_CLKSEQ_BYPASS_GPMI (1 << 2) +#define CLKCTRL_CLKSEQ_BYPASS_SAIF1 (1 << 1) +#define CLKCTRL_CLKSEQ_BYPASS_SAIF0 (1 << 0) + +#define CLKCTRL_RESET_WDOG_POR_DISABLE (1 << 5) +#define CLKCTRL_RESET_EXTERNAL_RESET_ENABLE (1 << 4) +#define CLKCTRL_RESET_THERMAL_RESET_ENABLE (1 << 3) +#define CLKCTRL_RESET_THERMAL_RESET_DEFAULT (1 << 2) +#define CLKCTRL_RESET_CHIP (1 << 1) +#define CLKCTRL_RESET_DIG (1 << 0) + +#define CLKCTRL_STATUS_CPU_LIMIT_MASK (0x3 << 30) +#define CLKCTRL_STATUS_CPU_LIMIT_OFFSET 30 + +#define CLKCTRL_VERSION_MAJOR_MASK (0xff << 24) +#define CLKCTRL_VERSION_MAJOR_OFFSET 24 +#define CLKCTRL_VERSION_MINOR_MASK (0xff << 16) +#define CLKCTRL_VERSION_MINOR_OFFSET 16 +#define CLKCTRL_VERSION_STEP_MASK 0xffff +#define CLKCTRL_VERSION_STEP_OFFSET 0 + +#endif /* __MX28_REGS_CLKCTRL_H__ */ diff --git a/arch/arm/mach-mxs/include/mach/regs-common.h b/arch/arm/mach-mxs/include/mach/regs-common.h new file mode 100644 index 0000000..e54a220 --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/regs-common.h @@ -0,0 +1,69 @@ +/* + * Freescale i.MXS Register Accessors + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MXS_REGS_COMMON_H__ +#define __MXS_REGS_COMMON_H__ + +/* + * The i.MXS has interesting feature when it comes to register access. There + * are four kinds of access to one particular register. Those are: + * + * 1) Common read/write access. To use this mode, just write to the address of + * the register. + * 2) Set bits only access. To set bits, write which bits you want to set to the + * address of the register + 0x4. + * 3) Clear bits only access. To clear bits, write which bits you want to clear + * to the address of the register + 0x8. + * 4) Toggle bits only access. To toggle bits, write which bits you want to + * toggle to the address of the register + 0xc. + * + * IMPORTANT NOTE: Not all registers support accesses 2-4! Also, not all bits + * can be set/cleared by pure write as in access type 1, some need to be + * explicitly set/cleared by using access type 2-3. + * + * The following macros and structures allow the user to either access the + * register in all aforementioned modes (by accessing reg_name, reg_name_set, + * reg_name_clr, reg_name_tog) or pass the register structure further into + * various functions with correct type information (by accessing reg_name_reg). + * + */ + +#define __mxs_reg_8(name) \ + uint8_t name[4]; \ + uint8_t name##_set[4]; \ + uint8_t name##_clr[4]; \ + uint8_t name##_tog[4]; \ + +#define __mxs_reg_32(name) \ + uint32_t name; \ + uint32_t name##_set; \ + uint32_t name##_clr; \ + uint32_t name##_tog; + +struct mxs_register_8 { + __mxs_reg_8(reg) +}; + +struct mxs_register_32 { + __mxs_reg_32(reg) +}; + +#define mxs_reg_8(name) \ + union { \ + struct { __mxs_reg_8(name) }; \ + struct mxs_register_8 name##_reg; \ + }; + +#define mxs_reg_32(name) \ + union { \ + struct { __mxs_reg_32(name) }; \ + struct mxs_register_32 name##_reg; \ + }; + +#endif /* __MXS_REGS_COMMON_H__ */ diff --git a/arch/arm/mach-mxs/include/mach/regs-lradc.h b/arch/arm/mach-mxs/include/mach/regs-lradc.h new file mode 100644 index 0000000..7f624be --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/regs-lradc.h @@ -0,0 +1,387 @@ +/* + * Freescale i.MX28 LRADC Register Definitions + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MX28_REGS_LRADC_H__ +#define __MX28_REGS_LRADC_H__ + +#include + +#ifndef __ASSEMBLY__ +struct mxs_lradc_regs { + mxs_reg_32(hw_lradc_ctrl0); + mxs_reg_32(hw_lradc_ctrl1); + mxs_reg_32(hw_lradc_ctrl2); + mxs_reg_32(hw_lradc_ctrl3); + mxs_reg_32(hw_lradc_status); + mxs_reg_32(hw_lradc_ch0); + mxs_reg_32(hw_lradc_ch1); + mxs_reg_32(hw_lradc_ch2); + mxs_reg_32(hw_lradc_ch3); + mxs_reg_32(hw_lradc_ch4); + mxs_reg_32(hw_lradc_ch5); + mxs_reg_32(hw_lradc_ch6); + mxs_reg_32(hw_lradc_ch7); + mxs_reg_32(hw_lradc_delay0); + mxs_reg_32(hw_lradc_delay1); + mxs_reg_32(hw_lradc_delay2); + mxs_reg_32(hw_lradc_delay3); + mxs_reg_32(hw_lradc_debug0); + mxs_reg_32(hw_lradc_debug1); + mxs_reg_32(hw_lradc_conversion); + mxs_reg_32(hw_lradc_ctrl4); + mxs_reg_32(hw_lradc_treshold0); + mxs_reg_32(hw_lradc_treshold1); + mxs_reg_32(hw_lradc_version); +}; +#endif + +#define LRADC_CTRL0_SFTRST (1 << 31) +#define LRADC_CTRL0_CLKGATE (1 << 30) +#define LRADC_CTRL0_ONCHIP_GROUNDREF (1 << 26) +#define LRADC_CTRL0_BUTTON1_DETECT_ENABLE (1 << 25) +#define LRADC_CTRL0_BUTTON0_DETECT_ENABLE (1 << 24) +#define LRADC_CTRL0_TOUCH_DETECT_ENABLE (1 << 23) +#define LRADC_CTRL0_TOUCH_SCREEN_TYPE (1 << 22) +#define LRADC_CTRL0_YNLRSW (1 << 21) +#define LRADC_CTRL0_YPLLSW_MASK (0x3 << 19) +#define LRADC_CTRL0_YPLLSW_OFFSET 19 +#define LRADC_CTRL0_XNURSW_MASK (0x3 << 17) +#define LRADC_CTRL0_XNURSW_OFFSET 17 +#define LRADC_CTRL0_XPULSW (1 << 16) +#define LRADC_CTRL0_SCHEDULE_MASK 0xff +#define LRADC_CTRL0_SCHEDULE_OFFSET 0 + +#define LRADC_CTRL1_BUTTON1_DETECT_IRQ_EN (1 << 28) +#define LRADC_CTRL1_BUTTON0_DETECT_IRQ_EN (1 << 27) +#define LRADC_CTRL1_THRESHOLD1_DETECT_IRQ_EN (1 << 26) +#define LRADC_CTRL1_THRESHOLD0_DETECT_IRQ_EN (1 << 25) +#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN (1 << 24) +#define LRADC_CTRL1_LRADC7_IRQ_EN (1 << 23) +#define LRADC_CTRL1_LRADC6_IRQ_EN (1 << 22) +#define LRADC_CTRL1_LRADC5_IRQ_EN (1 << 21) +#define LRADC_CTRL1_LRADC4_IRQ_EN (1 << 20) +#define LRADC_CTRL1_LRADC3_IRQ_EN (1 << 19) +#define LRADC_CTRL1_LRADC2_IRQ_EN (1 << 18) +#define LRADC_CTRL1_LRADC1_IRQ_EN (1 << 17) +#define LRADC_CTRL1_LRADC0_IRQ_EN (1 << 16) +#define LRADC_CTRL1_BUTTON1_DETECT_IRQ (1 << 12) +#define LRADC_CTRL1_BUTTON0_DETECT_IRQ (1 << 11) +#define LRADC_CTRL1_THRESHOLD1_DETECT_IRQ (1 << 10) +#define LRADC_CTRL1_THRESHOLD0_DETECT_IRQ (1 << 9) +#define LRADC_CTRL1_TOUCH_DETECT_IRQ (1 << 8) +#define LRADC_CTRL1_LRADC7_IRQ (1 << 7) +#define LRADC_CTRL1_LRADC6_IRQ (1 << 6) +#define LRADC_CTRL1_LRADC5_IRQ (1 << 5) +#define LRADC_CTRL1_LRADC4_IRQ (1 << 4) +#define LRADC_CTRL1_LRADC3_IRQ (1 << 3) +#define LRADC_CTRL1_LRADC2_IRQ (1 << 2) +#define LRADC_CTRL1_LRADC1_IRQ (1 << 1) +#define LRADC_CTRL1_LRADC0_IRQ (1 << 0) + +#define LRADC_CTRL2_DIVIDE_BY_TWO_MASK (0xff << 24) +#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET 24 +#define LRADC_CTRL2_TEMPSENSE_PWD (1 << 15) +#define LRADC_CTRL2_VTHSENSE_MASK (0x3 << 13) +#define LRADC_CTRL2_VTHSENSE_OFFSET 13 +#define LRADC_CTRL2_DISABLE_MUXAMP_BYPASS (1 << 12) +#define LRADC_CTRL2_TEMP_SENSOR_IENABLE1 (1 << 9) +#define LRADC_CTRL2_TEMP_SENSOR_IENABLE0 (1 << 8) +#define LRADC_CTRL2_TEMP_ISRC1_MASK (0xf << 4) +#define LRADC_CTRL2_TEMP_ISRC1_OFFSET 4 +#define LRADC_CTRL2_TEMP_ISRC1_300 (0xf << 4) +#define LRADC_CTRL2_TEMP_ISRC1_280 (0xe << 4) +#define LRADC_CTRL2_TEMP_ISRC1_260 (0xd << 4) +#define LRADC_CTRL2_TEMP_ISRC1_240 (0xc << 4) +#define LRADC_CTRL2_TEMP_ISRC1_220 (0xb << 4) +#define LRADC_CTRL2_TEMP_ISRC1_200 (0xa << 4) +#define LRADC_CTRL2_TEMP_ISRC1_180 (0x9 << 4) +#define LRADC_CTRL2_TEMP_ISRC1_160 (0x8 << 4) +#define LRADC_CTRL2_TEMP_ISRC1_140 (0x7 << 4) +#define LRADC_CTRL2_TEMP_ISRC1_120 (0x6 << 4) +#define LRADC_CTRL2_TEMP_ISRC1_100 (0x5 << 4) +#define LRADC_CTRL2_TEMP_ISRC1_80 (0x4 << 4) +#define LRADC_CTRL2_TEMP_ISRC1_60 (0x3 << 4) +#define LRADC_CTRL2_TEMP_ISRC1_40 (0x2 << 4) +#define LRADC_CTRL2_TEMP_ISRC1_20 (0x1 << 4) +#define LRADC_CTRL2_TEMP_ISRC1_ZERO (0x0 << 4) +#define LRADC_CTRL2_TEMP_ISRC0_MASK (0xf << 0) +#define LRADC_CTRL2_TEMP_ISRC0_OFFSET 0 +#define LRADC_CTRL2_TEMP_ISRC0_300 (0xf << 0) +#define LRADC_CTRL2_TEMP_ISRC0_280 (0xe << 0) +#define LRADC_CTRL2_TEMP_ISRC0_260 (0xd << 0) +#define LRADC_CTRL2_TEMP_ISRC0_240 (0xc << 0) +#define LRADC_CTRL2_TEMP_ISRC0_220 (0xb << 0) +#define LRADC_CTRL2_TEMP_ISRC0_200 (0xa << 0) +#define LRADC_CTRL2_TEMP_ISRC0_180 (0x9 << 0) +#define LRADC_CTRL2_TEMP_ISRC0_160 (0x8 << 0) +#define LRADC_CTRL2_TEMP_ISRC0_140 (0x7 << 0) +#define LRADC_CTRL2_TEMP_ISRC0_120 (0x6 << 0) +#define LRADC_CTRL2_TEMP_ISRC0_100 (0x5 << 0) +#define LRADC_CTRL2_TEMP_ISRC0_80 (0x4 << 0) +#define LRADC_CTRL2_TEMP_ISRC0_60 (0x3 << 0) +#define LRADC_CTRL2_TEMP_ISRC0_40 (0x2 << 0) +#define LRADC_CTRL2_TEMP_ISRC0_20 (0x1 << 0) +#define LRADC_CTRL2_TEMP_ISRC0_ZERO (0x0 << 0) + +#define LRADC_CTRL3_DISCARD_MASK (0x3 << 24) +#define LRADC_CTRL3_DISCARD_OFFSET 24 +#define LRADC_CTRL3_DISCARD_1_SAMPLE (0x1 << 24) +#define LRADC_CTRL3_DISCARD_2_SAMPLES (0x2 << 24) +#define LRADC_CTRL3_DISCARD_3_SAMPLES (0x3 << 24) +#define LRADC_CTRL3_FORCE_ANALOG_PWUP (1 << 23) +#define LRADC_CTRL3_FORCE_ANALOG_PWDN (1 << 22) +#define LRADC_CTRL3_CYCLE_TIME_MASK (0x3 << 8) +#define LRADC_CTRL3_CYCLE_TIME_OFFSET 8 +#define LRADC_CTRL3_CYCLE_TIME_6MHZ (0x0 << 8) +#define LRADC_CTRL3_CYCLE_TIME_4MHZ (0x1 << 8) +#define LRADC_CTRL3_CYCLE_TIME_3MHZ (0x2 << 8) +#define LRADC_CTRL3_CYCLE_TIME_2MHZ (0x3 << 8) +#define LRADC_CTRL3_HIGH_TIME_MASK (0x3 << 4) +#define LRADC_CTRL3_HIGH_TIME_OFFSET 4 +#define LRADC_CTRL3_HIGH_TIME_42NS (0x0 << 4) +#define LRADC_CTRL3_HIGH_TIME_83NS (0x1 << 4) +#define LRADC_CTRL3_HIGH_TIME_125NS (0x2 << 4) +#define LRADC_CTRL3_HIGH_TIME_250NS (0x3 << 4) +#define LRADC_CTRL3_DELAY_CLOCK (1 << 1) +#define LRADC_CTRL3_INVERT_CLOCK (1 << 0) + +#define LRADC_STATUS_BUTTON1_PRESENT (1 << 28) +#define LRADC_STATUS_BUTTON0_PRESENT (1 << 27) +#define LRADC_STATUS_TEMP1_PRESENT (1 << 26) +#define LRADC_STATUS_TEMP0_PRESENT (1 << 25) +#define LRADC_STATUS_TOUCH_PANEL_PRESENT (1 << 24) +#define LRADC_STATUS_CHANNEL7_PRESENT (1 << 23) +#define LRADC_STATUS_CHANNEL6_PRESENT (1 << 22) +#define LRADC_STATUS_CHANNEL5_PRESENT (1 << 21) +#define LRADC_STATUS_CHANNEL4_PRESENT (1 << 20) +#define LRADC_STATUS_CHANNEL3_PRESENT (1 << 19) +#define LRADC_STATUS_CHANNEL2_PRESENT (1 << 18) +#define LRADC_STATUS_CHANNEL1_PRESENT (1 << 17) +#define LRADC_STATUS_CHANNEL0_PRESENT (1 << 16) +#define LRADC_STATUS_BUTTON1_DETECT_RAW (1 << 2) +#define LRADC_STATUS_BUTTON0_DETECT_RAW (1 << 1) +#define LRADC_STATUS_TOUCH_DETECT_RAW (1 << 0) + +#define LRADC_CH_TOGGLE (1 << 31) +#define LRADC_CH7_TESTMODE_TOGGLE (1 << 30) +#define LRADC_CH_ACCUMULATE (1 << 29) +#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24) +#define LRADC_CH_NUM_SAMPLES_OFFSET 24 +#define LRADC_CH_VALUE_MASK 0x3ffff +#define LRADC_CH_VALUE_OFFSET 0 + +#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xff << 24) +#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24 +#define LRADC_DELAY_KICK (1 << 20) +#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16) +#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16 +#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11) +#define LRADC_DELAY_LOOP_COUNT_OFFSET 11 +#define LRADC_DELAY_DELAY_MASK 0x7ff +#define LRADC_DELAY_DELAY_OFFSET 0 + +#define LRADC_DEBUG0_READONLY_MASK (0xffff << 16) +#define LRADC_DEBUG0_READONLY_OFFSET 16 +#define LRADC_DEBUG0_STATE_MASK (0xfff << 0) +#define LRADC_DEBUG0_STATE_OFFSET 0 + +#define LRADC_DEBUG1_REQUEST_MASK (0xff << 16) +#define LRADC_DEBUG1_REQUEST_OFFSET 16 +#define LRADC_DEBUG1_TESTMODE_COUNT_MASK (0x1f << 8) +#define LRADC_DEBUG1_TESTMODE_COUNT_OFFSET 8 +#define LRADC_DEBUG1_TESTMODE6 (1 << 2) +#define LRADC_DEBUG1_TESTMODE5 (1 << 1) +#define LRADC_DEBUG1_TESTMODE (1 << 0) + +#define LRADC_CONVERSION_AUTOMATIC (1 << 20) +#define LRADC_CONVERSION_SCALE_FACTOR_MASK (0x3 << 16) +#define LRADC_CONVERSION_SCALE_FACTOR_OFFSET 16 +#define LRADC_CONVERSION_SCALE_FACTOR_NIMH (0x0 << 16) +#define LRADC_CONVERSION_SCALE_FACTOR_DUAL_NIMH (0x1 << 16) +#define LRADC_CONVERSION_SCALE_FACTOR_LI_ION (0x2 << 16) +#define LRADC_CONVERSION_SCALE_FACTOR_ALT_LI_ION (0x3 << 16) +#define LRADC_CONVERSION_SCALED_BATT_VOLTAGE_MASK 0x3ff +#define LRADC_CONVERSION_SCALED_BATT_VOLTAGE_OFFSET 0 + +#define LRADC_CTRL4_LRADC7SELECT_MASK (0xf << 28) +#define LRADC_CTRL4_LRADC7SELECT_OFFSET 28 +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL0 (0x0 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL1 (0x1 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL2 (0x2 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL3 (0x3 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL4 (0x4 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL5 (0x5 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL6 (0x6 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL7 (0x7 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL8 (0x8 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL9 (0x9 << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL10 (0xa << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL11 (0xb << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL12 (0xc << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL13 (0xd << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL14 (0xe << 28) +#define LRADC_CTRL4_LRADC7SELECT_CHANNEL15 (0xf << 28) +#define LRADC_CTRL4_LRADC6SELECT_MASK (0xf << 24) +#define LRADC_CTRL4_LRADC6SELECT_OFFSET 24 +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL0 (0x0 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL1 (0x1 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL2 (0x2 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL3 (0x3 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL4 (0x4 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL5 (0x5 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL6 (0x6 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL7 (0x7 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL8 (0x8 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL9 (0x9 << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL10 (0xa << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL11 (0xb << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL12 (0xc << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL13 (0xd << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL14 (0xe << 24) +#define LRADC_CTRL4_LRADC6SELECT_CHANNEL15 (0xf << 24) +#define LRADC_CTRL4_LRADC5SELECT_MASK (0xf << 20) +#define LRADC_CTRL4_LRADC5SELECT_OFFSET 20 +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL0 (0x0 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL1 (0x1 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL2 (0x2 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL3 (0x3 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL4 (0x4 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL5 (0x5 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL6 (0x6 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL7 (0x7 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL8 (0x8 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL9 (0x9 << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL10 (0xa << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL11 (0xb << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL12 (0xc << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL13 (0xd << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL14 (0xe << 20) +#define LRADC_CTRL4_LRADC5SELECT_CHANNEL15 (0xf << 20) +#define LRADC_CTRL4_LRADC4SELECT_MASK (0xf << 16) +#define LRADC_CTRL4_LRADC4SELECT_OFFSET 16 +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL0 (0x0 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL1 (0x1 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL2 (0x2 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL3 (0x3 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL4 (0x4 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL5 (0x5 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL6 (0x6 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL7 (0x7 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL8 (0x8 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL9 (0x9 << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL10 (0xa << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL11 (0xb << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL12 (0xc << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL13 (0xd << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL14 (0xe << 16) +#define LRADC_CTRL4_LRADC4SELECT_CHANNEL15 (0xf << 16) +#define LRADC_CTRL4_LRADC3SELECT_MASK (0xf << 12) +#define LRADC_CTRL4_LRADC3SELECT_OFFSET 12 +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL0 (0x0 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL1 (0x1 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL2 (0x2 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL3 (0x3 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL4 (0x4 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL5 (0x5 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL6 (0x6 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL7 (0x7 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL8 (0x8 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL9 (0x9 << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL10 (0xa << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL11 (0xb << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL12 (0xc << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL13 (0xd << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL14 (0xe << 12) +#define LRADC_CTRL4_LRADC3SELECT_CHANNEL15 (0xf << 12) +#define LRADC_CTRL4_LRADC2SELECT_MASK (0xf << 8) +#define LRADC_CTRL4_LRADC2SELECT_OFFSET 8 +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL0 (0x0 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL1 (0x1 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL2 (0x2 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL3 (0x3 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL4 (0x4 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL5 (0x5 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL6 (0x6 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL7 (0x7 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL8 (0x8 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL9 (0x9 << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL10 (0xa << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL11 (0xb << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL12 (0xc << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL13 (0xd << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL14 (0xe << 8) +#define LRADC_CTRL4_LRADC2SELECT_CHANNEL15 (0xf << 8) +#define LRADC_CTRL4_LRADC1SELECT_MASK (0xf << 4) +#define LRADC_CTRL4_LRADC1SELECT_OFFSET 4 +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL0 (0x0 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL1 (0x1 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL2 (0x2 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL3 (0x3 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL4 (0x4 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL5 (0x5 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL6 (0x6 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL7 (0x7 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL8 (0x8 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL9 (0x9 << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL10 (0xa << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL11 (0xb << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL12 (0xc << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL13 (0xd << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL14 (0xe << 4) +#define LRADC_CTRL4_LRADC1SELECT_CHANNEL15 (0xf << 4) +#define LRADC_CTRL4_LRADC0SELECT_MASK 0xf +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL0 (0x0 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL1 (0x1 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL2 (0x2 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL3 (0x3 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL4 (0x4 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL5 (0x5 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL6 (0x6 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL7 (0x7 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL8 (0x8 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL9 (0x9 << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL10 (0xa << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL11 (0xb << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL12 (0xc << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL13 (0xd << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL14 (0xe << 0) +#define LRADC_CTRL4_LRADC0SELECT_CHANNEL15 (0xf << 0) + +#define LRADC_THRESHOLD_ENABLE (1 << 24) +#define LRADC_THRESHOLD_BATTCHRG_DISABLE (1 << 23) +#define LRADC_THRESHOLD_CHANNEL_SEL_MASK (0x7 << 20) +#define LRADC_THRESHOLD_CHANNEL_SEL_OFFSET 20 +#define LRADC_THRESHOLD_CHANNEL_SEL_CHANNEL0 (0x0 << 20) +#define LRADC_THRESHOLD_CHANNEL_SEL_CHANNEL1 (0x1 << 20) +#define LRADC_THRESHOLD_CHANNEL_SEL_CHANNEL2 (0x2 << 20) +#define LRADC_THRESHOLD_CHANNEL_SEL_CHANNEL3 (0x3 << 20) +#define LRADC_THRESHOLD_CHANNEL_SEL_CHANNEL4 (0x4 << 20) +#define LRADC_THRESHOLD_CHANNEL_SEL_CHANNEL5 (0x5 << 20) +#define LRADC_THRESHOLD_CHANNEL_SEL_CHANNEL6 (0x6 << 20) +#define LRADC_THRESHOLD_CHANNEL_SEL_CHANNEL7 (0x7 << 20) +#define LRADC_THRESHOLD_SETTING_MASK (0x3 << 18) +#define LRADC_THRESHOLD_SETTING_OFFSET 18 +#define LRADC_THRESHOLD_SETTING_NO_COMPARE (0x0 << 18) +#define LRADC_THRESHOLD_SETTING_DETECT_LOW (0x1 << 18) +#define LRADC_THRESHOLD_SETTING_DETECT_HIGH (0x2 << 18) +#define LRADC_THRESHOLD_SETTING_RESERVED (0x3 << 18) +#define LRADC_THRESHOLD_VALUE_MASK 0x3ffff +#define LRADC_THRESHOLD_VALUE_OFFSET 0 + +#define LRADC_VERSION_MAJOR_MASK (0xff << 24) +#define LRADC_VERSION_MAJOR_OFFSET 24 +#define LRADC_VERSION_MINOR_MASK (0xff << 16) +#define LRADC_VERSION_MINOR_OFFSET 16 +#define LRADC_VERSION_STEP_MASK 0xffff +#define LRADC_VERSION_STEP_OFFSET 0 + +#endif /* __MX28_REGS_LRADC_H__ */ diff --git a/arch/arm/mach-mxs/include/mach/regs-power-mx28.h b/arch/arm/mach-mxs/include/mach/regs-power-mx28.h new file mode 100644 index 0000000..7b73496 --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/regs-power-mx28.h @@ -0,0 +1,408 @@ +/* + * Freescale i.MX28 Power Controller Register Definitions + * + * Copyright (C) 2011 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MX28_REGS_POWER_H__ +#define __MX28_REGS_POWER_H__ + +#include + +#ifndef __ASSEMBLY__ +struct mxs_power_regs { + mxs_reg_32(hw_power_ctrl) + mxs_reg_32(hw_power_5vctrl) + mxs_reg_32(hw_power_minpwr) + mxs_reg_32(hw_power_charge) + uint32_t hw_power_vdddctrl; + uint32_t reserved_vddd[3]; + uint32_t hw_power_vddactrl; + uint32_t reserved_vdda[3]; + uint32_t hw_power_vddioctrl; + uint32_t reserved_vddio[3]; + uint32_t hw_power_vddmemctrl; + uint32_t reserved_vddmem[3]; + uint32_t hw_power_dcdc4p2; + uint32_t reserved_dcdc4p2[3]; + uint32_t hw_power_misc; + uint32_t reserved_misc[3]; + uint32_t hw_power_dclimits; + uint32_t reserved_dclimits[3]; + mxs_reg_32(hw_power_loopctrl) + uint32_t hw_power_sts; + uint32_t reserved_sts[3]; + mxs_reg_32(hw_power_speed) + uint32_t hw_power_battmonitor; + uint32_t reserved_battmonitor[3]; + + uint32_t reserved[4]; + + mxs_reg_32(hw_power_reset) +}; +#endif + +#define MX23_POWER_CTRL_CLKGATE (1 << 30) +#define POWER_CTRL_PSWITCH_MID_TRAN (1 << 27) +#define POWER_CTRL_DCDC4P2_BO_IRQ (1 << 24) +#define POWER_CTRL_ENIRQ_DCDC4P2_BO (1 << 23) +#define POWER_CTRL_VDD5V_DROOP_IRQ (1 << 22) +#define POWER_CTRL_ENIRQ_VDD5V_DROOP (1 << 21) +#define POWER_CTRL_PSWITCH_IRQ (1 << 20) +#define POWER_CTRL_PSWITCH_IRQ_SRC (1 << 19) +#define POWER_CTRL_POLARITY_PSWITCH (1 << 18) +#define POWER_CTRL_ENIRQ_PSWITCH (1 << 17) +#define POWER_CTRL_POLARITY_DC_OK (1 << 16) +#define POWER_CTRL_DC_OK_IRQ (1 << 15) +#define POWER_CTRL_ENIRQ_DC_OK (1 << 14) +#define POWER_CTRL_BATT_BO_IRQ (1 << 13) +#define POWER_CTRL_ENIRQ_BATT_BO (1 << 12) +#define POWER_CTRL_VDDIO_BO_IRQ (1 << 11) +#define POWER_CTRL_ENIRQ_VDDIO_BO (1 << 10) +#define POWER_CTRL_VDDA_BO_IRQ (1 << 9) +#define POWER_CTRL_ENIRQ_VDDA_BO (1 << 8) +#define POWER_CTRL_VDDD_BO_IRQ (1 << 7) +#define POWER_CTRL_ENIRQ_VDDD_BO (1 << 6) +#define POWER_CTRL_POLARITY_VBUSVALID (1 << 5) +#define POWER_CTRL_VBUS_VALID_IRQ (1 << 4) +#define POWER_CTRL_ENIRQ_VBUS_VALID (1 << 3) +#define POWER_CTRL_POLARITY_VDD5V_GT_VDDIO (1 << 2) +#define POWER_CTRL_VDD5V_GT_VDDIO_IRQ (1 << 1) +#define POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO (1 << 0) + +#define MX28_POWER_5VCTRL_VBUSDROOP_TRSH_MASK (0x3 << 30) +#define MX28_POWER_5VCTRL_VBUSDROOP_TRSH_OFFSET 30 +#define MX28_POWER_5VCTRL_VBUSDROOP_TRSH_4V3 (0x0 << 30) +#define MX28_POWER_5VCTRL_VBUSDROOP_TRSH_4V4 (0x1 << 30) +#define MX28_POWER_5VCTRL_VBUSDROOP_TRSH_4V5 (0x2 << 30) +#define MX28_POWER_5VCTRL_VBUSDROOP_TRSH_4V7 (0x3 << 30) + +#define MX23_POWER_5VCTRL_VBUSDROOP_TRSH_MASK (0x3 << 28) +#define MX23_POWER_5VCTRL_VBUSDROOP_TRSH_OFFSET 28 +#define MX23_POWER_5VCTRL_VBUSDROOP_TRSH_4V3 (0x0 << 28) +#define MX23_POWER_5VCTRL_VBUSDROOP_TRSH_4V4 (0x1 << 28) +#define MX23_POWER_5VCTRL_VBUSDROOP_TRSH_4V5 (0x2 << 28) +#define MX23_POWER_5VCTRL_VBUSDROOP_TRSH_4V7 (0x3 << 28) + +#define POWER_5VCTRL_HEADROOM_ADJ_MASK (0x7 << 24) +#define POWER_5VCTRL_HEADROOM_ADJ_OFFSET 24 +#define MX28_POWER_5VCTRL_PWD_CHARGE_4P2_MASK (0x3 << 20) +#define MX23_POWER_5VCTRL_PWD_CHARGE_4P2_MASK (0x1 << 20) +#define POWER_5VCTRL_PWD_CHARGE_4P2_OFFSET 20 +#define POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK (0x3f << 12) +#define POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET 12 +#define POWER_5VCTRL_VBUSVALID_TRSH_MASK (0x7 << 8) +#define POWER_5VCTRL_VBUSVALID_TRSH_OFFSET 8 +#define POWER_5VCTRL_VBUSVALID_TRSH_2V9 (0x0 << 8) +#define POWER_5VCTRL_VBUSVALID_TRSH_4V0 (0x1 << 8) +#define POWER_5VCTRL_VBUSVALID_TRSH_4V1 (0x2 << 8) +#define POWER_5VCTRL_VBUSVALID_TRSH_4V2 (0x3 << 8) +#define POWER_5VCTRL_VBUSVALID_TRSH_4V3 (0x4 << 8) +#define POWER_5VCTRL_VBUSVALID_TRSH_4V4 (0x5 << 8) +#define POWER_5VCTRL_VBUSVALID_TRSH_4V5 (0x6 << 8) +#define POWER_5VCTRL_VBUSVALID_TRSH_4V6 (0x7 << 8) +#define POWER_5VCTRL_PWDN_5VBRNOUT (1 << 7) +#define POWER_5VCTRL_ENABLE_LINREG_ILIMIT (1 << 6) +#define POWER_5VCTRL_DCDC_XFER (1 << 5) +#define POWER_5VCTRL_VBUSVALID_5VDETECT (1 << 4) +#define POWER_5VCTRL_VBUSVALID_TO_B (1 << 3) +#define POWER_5VCTRL_ILIMIT_EQ_ZERO (1 << 2) +#define POWER_5VCTRL_PWRUP_VBUS_CMPS (1 << 1) +#define POWER_5VCTRL_ENABLE_DCDC (1 << 0) + +#define POWER_MINPWR_LOWPWR_4P2 (1 << 14) +#define MX23_POWER_MINPWR_VDAC_DUMP_CTRL (1 << 13) +#define POWER_MINPWR_PWD_BO (1 << 12) +#define POWER_MINPWR_USE_VDDXTAL_VBG (1 << 11) +#define POWER_MINPWR_PWD_ANA_CMPS (1 << 10) +#define POWER_MINPWR_ENABLE_OSC (1 << 9) +#define POWER_MINPWR_SELECT_OSC (1 << 8) +#define POWER_MINPWR_VBG_OFF (1 << 7) +#define POWER_MINPWR_DOUBLE_FETS (1 << 6) +#define POWER_MINPWR_HALFFETS (1 << 5) +#define POWER_MINPWR_LESSANA_I (1 << 4) +#define POWER_MINPWR_PWD_XTAL24 (1 << 3) +#define POWER_MINPWR_DC_STOPCLK (1 << 2) +#define POWER_MINPWR_EN_DC_PFM (1 << 1) +#define POWER_MINPWR_DC_HALFCLK (1 << 0) + +#define POWER_CHARGE_ADJ_VOLT_MASK (0x7 << 24) +#define POWER_CHARGE_ADJ_VOLT_OFFSET 24 +#define POWER_CHARGE_ADJ_VOLT_M025P (0x1 << 24) +#define POWER_CHARGE_ADJ_VOLT_P050P (0x2 << 24) +#define POWER_CHARGE_ADJ_VOLT_M075P (0x3 << 24) +#define POWER_CHARGE_ADJ_VOLT_P025P (0x4 << 24) +#define POWER_CHARGE_ADJ_VOLT_M050P (0x5 << 24) +#define POWER_CHARGE_ADJ_VOLT_P075P (0x6 << 24) +#define POWER_CHARGE_ADJ_VOLT_M100P (0x7 << 24) +#define POWER_CHARGE_ENABLE_LOAD (1 << 22) +#define MX23_POWER_CHARGE_ENABLE_CHARGER_RESISTORS (1 << 21) +#define POWER_CHARGE_ENABLE_FAULT_DETECT (1 << 20) +#define POWER_CHARGE_CHRG_STS_OFF (1 << 19) +#define MX28_POWER_CHARGE_LIION_4P1 (1 << 18) +#define MX23_POWER_CHARGE_USE_EXTERN_R (1 << 17) +#define POWER_CHARGE_PWD_BATTCHRG (1 << 16) +#define MX28_POWER_CHARGE_ENABLE_CHARGER_USB1 (1 << 13) +#define MX28_POWER_CHARGE_ENABLE_CHARGER_USB0 (1 << 12) +#define POWER_CHARGE_STOP_ILIMIT_MASK (0xf << 8) +#define POWER_CHARGE_STOP_ILIMIT_OFFSET 8 +#define POWER_CHARGE_STOP_ILIMIT_10MA (0x1 << 8) +#define POWER_CHARGE_STOP_ILIMIT_20MA (0x2 << 8) +#define POWER_CHARGE_STOP_ILIMIT_50MA (0x4 << 8) +#define POWER_CHARGE_STOP_ILIMIT_100MA (0x8 << 8) +#define POWER_CHARGE_BATTCHRG_I_MASK 0x3f +#define POWER_CHARGE_BATTCHRG_I_OFFSET 0 +#define POWER_CHARGE_BATTCHRG_I_10MA 0x01 +#define POWER_CHARGE_BATTCHRG_I_20MA 0x02 +#define POWER_CHARGE_BATTCHRG_I_50MA 0x04 +#define POWER_CHARGE_BATTCHRG_I_100MA 0x08 +#define POWER_CHARGE_BATTCHRG_I_200MA 0x10 +#define POWER_CHARGE_BATTCHRG_I_400MA 0x20 + +#define POWER_VDDDCTRL_ADJTN_MASK (0xf << 28) +#define POWER_VDDDCTRL_ADJTN_OFFSET 28 +#define POWER_VDDDCTRL_PWDN_BRNOUT (1 << 23) +#define POWER_VDDDCTRL_DISABLE_STEPPING (1 << 22) +#define POWER_VDDDCTRL_ENABLE_LINREG (1 << 21) +#define POWER_VDDDCTRL_DISABLE_FET (1 << 20) +#define POWER_VDDDCTRL_LINREG_OFFSET_MASK (0x3 << 16) +#define POWER_VDDDCTRL_LINREG_OFFSET_OFFSET 16 +#define POWER_VDDDCTRL_LINREG_OFFSET_0STEPS (0x0 << 16) +#define POWER_VDDDCTRL_LINREG_OFFSET_1STEPS_ABOVE (0x1 << 16) +#define POWER_VDDDCTRL_LINREG_OFFSET_1STEPS_BELOW (0x2 << 16) +#define POWER_VDDDCTRL_LINREG_OFFSET_2STEPS_BELOW (0x3 << 16) +#define POWER_VDDDCTRL_BO_OFFSET_MASK (0x7 << 8) +#define POWER_VDDDCTRL_BO_OFFSET_OFFSET 8 +#define POWER_VDDDCTRL_TRG_MASK 0x1f +#define POWER_VDDDCTRL_TRG_OFFSET 0 + +#define POWER_VDDACTRL_PWDN_BRNOUT (1 << 19) +#define POWER_VDDACTRL_DISABLE_STEPPING (1 << 18) +#define POWER_VDDACTRL_ENABLE_LINREG (1 << 17) +#define POWER_VDDACTRL_DISABLE_FET (1 << 16) +#define POWER_VDDACTRL_LINREG_OFFSET_MASK (0x3 << 12) +#define POWER_VDDACTRL_LINREG_OFFSET_OFFSET 12 +#define POWER_VDDACTRL_LINREG_OFFSET_0STEPS (0x0 << 12) +#define POWER_VDDACTRL_LINREG_OFFSET_1STEPS_ABOVE (0x1 << 12) +#define POWER_VDDACTRL_LINREG_OFFSET_1STEPS_BELOW (0x2 << 12) +#define POWER_VDDACTRL_LINREG_OFFSET_2STEPS_BELOW (0x3 << 12) +#define POWER_VDDACTRL_BO_OFFSET_MASK (0x7 << 8) +#define POWER_VDDACTRL_BO_OFFSET_OFFSET 8 +#define POWER_VDDACTRL_TRG_MASK 0x1f +#define POWER_VDDACTRL_TRG_OFFSET 0 + +#define POWER_VDDIOCTRL_ADJTN_MASK (0xf << 20) +#define POWER_VDDIOCTRL_ADJTN_OFFSET 20 +#define POWER_VDDIOCTRL_PWDN_BRNOUT (1 << 18) +#define POWER_VDDIOCTRL_DISABLE_STEPPING (1 << 17) +#define POWER_VDDIOCTRL_DISABLE_FET (1 << 16) +#define POWER_VDDIOCTRL_LINREG_OFFSET_MASK (0x3 << 12) +#define POWER_VDDIOCTRL_LINREG_OFFSET_OFFSET 12 +#define POWER_VDDIOCTRL_LINREG_OFFSET_0STEPS (0x0 << 12) +#define POWER_VDDIOCTRL_LINREG_OFFSET_1STEPS_ABOVE (0x1 << 12) +#define POWER_VDDIOCTRL_LINREG_OFFSET_1STEPS_BELOW (0x2 << 12) +#define POWER_VDDIOCTRL_LINREG_OFFSET_2STEPS_BELOW (0x3 << 12) +#define POWER_VDDIOCTRL_BO_OFFSET_MASK (0x7 << 8) +#define POWER_VDDIOCTRL_BO_OFFSET_OFFSET 8 +#define POWER_VDDIOCTRL_TRG_MASK 0x1f +#define POWER_VDDIOCTRL_TRG_OFFSET 0 + +#define POWER_VDDMEMCTRL_PULLDOWN_ACTIVE (1 << 10) +#define POWER_VDDMEMCTRL_ENABLE_ILIMIT (1 << 9) +#define POWER_VDDMEMCTRL_ENABLE_LINREG (1 << 8) +#define MX28_POWER_VDDMEMCTRL_BO_OFFSET_MASK (0x7 << 5) +#define MX28_POWER_VDDMEMCTRL_BO_OFFSET_OFFSET 5 +#define POWER_VDDMEMCTRL_TRG_MASK 0x1f +#define POWER_VDDMEMCTRL_TRG_OFFSET 0 + +#define POWER_DCDC4P2_DROPOUT_CTRL_MASK (0xf << 28) +#define POWER_DCDC4P2_DROPOUT_CTRL_OFFSET 28 +#define POWER_DCDC4P2_DROPOUT_CTRL_200MV (0x3 << 30) +#define POWER_DCDC4P2_DROPOUT_CTRL_100MV (0x2 << 30) +#define POWER_DCDC4P2_DROPOUT_CTRL_50MV (0x1 << 30) +#define POWER_DCDC4P2_DROPOUT_CTRL_25MV (0x0 << 30) +#define POWER_DCDC4P2_DROPOUT_CTRL_SRC_4P2 (0x0 << 28) +#define POWER_DCDC4P2_DROPOUT_CTRL_SRC_4P2_LT_BATT (0x1 << 28) +#define POWER_DCDC4P2_DROPOUT_CTRL_SRC_SEL (0x2 << 28) +#define POWER_DCDC4P2_ISTEAL_THRESH_MASK (0x3 << 24) +#define POWER_DCDC4P2_ISTEAL_THRESH_OFFSET 24 +#define POWER_DCDC4P2_ENABLE_4P2 (1 << 23) +#define POWER_DCDC4P2_ENABLE_DCDC (1 << 22) +#define POWER_DCDC4P2_HYST_DIR (1 << 21) +#define POWER_DCDC4P2_HYST_THRESH (1 << 20) +#define POWER_DCDC4P2_TRG_MASK (0x7 << 16) +#define POWER_DCDC4P2_TRG_OFFSET 16 +#define POWER_DCDC4P2_TRG_4V2 (0x0 << 16) +#define POWER_DCDC4P2_TRG_4V1 (0x1 << 16) +#define POWER_DCDC4P2_TRG_4V0 (0x2 << 16) +#define POWER_DCDC4P2_TRG_3V9 (0x3 << 16) +#define POWER_DCDC4P2_TRG_BATT (0x4 << 16) +#define POWER_DCDC4P2_BO_MASK (0x1f << 8) +#define POWER_DCDC4P2_BO_OFFSET 8 +#define POWER_DCDC4P2_CMPTRIP_MASK 0x1f +#define POWER_DCDC4P2_CMPTRIP_OFFSET 0 + +#define POWER_MISC_FREQSEL_MASK (0x7 << 4) +#define POWER_MISC_FREQSEL_OFFSET 4 +#define POWER_MISC_FREQSEL_20MHZ (0x1 << 4) +#define POWER_MISC_FREQSEL_24MHZ (0x2 << 4) +#define POWER_MISC_FREQSEL_19MHZ (0x3 << 4) +#define POWER_MISC_FREQSEL_14MHZ (0x4 << 4) +#define POWER_MISC_FREQSEL_18MHZ (0x5 << 4) +#define POWER_MISC_FREQSEL_21MHZ (0x6 << 4) +#define POWER_MISC_FREQSEL_17MHZ (0x7 << 4) +#define POWER_MISC_DISABLE_FET_BO_LOGIC (1 << 3) +#define POWER_MISC_DELAY_TIMING (1 << 2) +#define POWER_MISC_TEST (1 << 1) +#define POWER_MISC_SEL_PLLCLK (1 << 0) + +#define POWER_DCLIMITS_POSLIMIT_BUCK_MASK (0x7f << 8) +#define POWER_DCLIMITS_POSLIMIT_BUCK_OFFSET 8 +#define POWER_DCLIMITS_NEGLIMIT_MASK 0x7f +#define POWER_DCLIMITS_NEGLIMIT_OFFSET 0 + +#define POWER_LOOPCTRL_TOGGLE_DIF (1 << 20) +#define POWER_LOOPCTRL_HYST_SIGN (1 << 19) +#define POWER_LOOPCTRL_EN_CM_HYST (1 << 18) +#define POWER_LOOPCTRL_EN_DF_HYST (1 << 17) +#define POWER_LOOPCTRL_CM_HYST_THRESH (1 << 16) +#define POWER_LOOPCTRL_DF_HYST_THRESH (1 << 15) +#define POWER_LOOPCTRL_RCSCALE_THRESH (1 << 14) +#define POWER_LOOPCTRL_EN_RCSCALE_MASK (0x3 << 12) +#define POWER_LOOPCTRL_EN_RCSCALE_OFFSET 12 +#define POWER_LOOPCTRL_EN_RCSCALE_DIS (0x0 << 12) +#define POWER_LOOPCTRL_EN_RCSCALE_2X (0x1 << 12) +#define POWER_LOOPCTRL_EN_RCSCALE_4X (0x2 << 12) +#define POWER_LOOPCTRL_EN_RCSCALE_8X (0x3 << 12) +#define POWER_LOOPCTRL_DC_FF_MASK (0x7 << 8) +#define POWER_LOOPCTRL_DC_FF_OFFSET 8 +#define POWER_LOOPCTRL_DC_R_MASK (0xf << 4) +#define POWER_LOOPCTRL_DC_R_OFFSET 4 +#define POWER_LOOPCTRL_DC_C_MASK 0x3 +#define POWER_LOOPCTRL_DC_C_OFFSET 0 +#define POWER_LOOPCTRL_DC_C_MAX 0x0 +#define POWER_LOOPCTRL_DC_C_2X 0x1 +#define POWER_LOOPCTRL_DC_C_4X 0x2 +#define POWER_LOOPCTRL_DC_C_MIN 0x3 + +#define POWER_STS_PWRUP_SOURCE_MASK (0x3f << 24) +#define POWER_STS_PWRUP_SOURCE_OFFSET 24 +#define POWER_STS_PWRUP_SOURCE_5V (0x20 << 24) +#define POWER_STS_PWRUP_SOURCE_RTC (0x10 << 24) +#define POWER_STS_PWRUP_SOURCE_PSWITCH_HIGH (0x02 << 24) +#define POWER_STS_PWRUP_SOURCE_PSWITCH_MID (0x01 << 24) +#define POWER_STS_PSWITCH_MASK (0x3 << 20) +#define POWER_STS_PSWITCH_OFFSET 20 +#define MX28_POWER_STS_THERMAL_WARNING (1 << 19) +#define MX28_POWER_STS_VDDMEM_BO (1 << 18) +#define POWER_STS_AVALID0_STATUS (1 << 17) +#define POWER_STS_BVALID0_STATUS (1 << 16) +#define POWER_STS_VBUSVALID0_STATUS (1 << 15) +#define POWER_STS_SESSEND0_STATUS (1 << 14) +#define POWER_STS_BATT_BO (1 << 13) +#define POWER_STS_VDD5V_FAULT (1 << 12) +#define POWER_STS_CHRGSTS (1 << 11) +#define POWER_STS_DCDC_4P2_BO (1 << 10) +#define POWER_STS_DC_OK (1 << 9) +#define POWER_STS_VDDIO_BO (1 << 8) +#define POWER_STS_VDDA_BO (1 << 7) +#define POWER_STS_VDDD_BO (1 << 6) +#define POWER_STS_VDD5V_GT_VDDIO (1 << 5) +#define POWER_STS_VDD5V_DROOP (1 << 4) +#define POWER_STS_AVALID0 (1 << 3) +#define POWER_STS_BVALID0 (1 << 2) +#define POWER_STS_VBUSVALID0 (1 << 1) +#define POWER_STS_SESSEND0 (1 << 0) + +#define MX23_POWER_SPEED_STATUS_MASK (0xff << 16) +#define MX23_POWER_SPEED_STATUS_OFFSET 16 +#define MX28_POWER_SPEED_STATUS_MASK (0xffff << 8) +#define MX28_POWER_SPEED_STATUS_OFFSET 8 +#define MX28_POWER_SPEED_STATUS_SEL_MASK (0x3 << 6) +#define MX28_POWER_SPEED_STATUS_SEL_OFFSET 6 +#define MX28_POWER_SPEED_STATUS_SEL_DCDC_STAT (0x0 << 6) +#define MX28_POWER_SPEED_STATUS_SEL_CORE_STAT (0x1 << 6) +#define MX28_POWER_SPEED_STATUS_SEL_ARM_STAT (0x2 << 6) +#define POWER_SPEED_CTRL_MASK 0x3 +#define POWER_SPEED_CTRL_OFFSET 0 +#define POWER_SPEED_CTRL_SS_OFF 0x0 +#define POWER_SPEED_CTRL_SS_ON 0x1 +#define POWER_SPEED_CTRL_SS_ENABLE 0x3 + +#define POWER_BATTMONITOR_BATT_VAL_MASK (0x3ff << 16) +#define POWER_BATTMONITOR_BATT_VAL_OFFSET 16 +#define MX28_POWER_BATTMONITOR_PWDN_BATTBRNOUT_5VDETECT_EN (1 << 11) +#define POWER_BATTMONITOR_EN_BATADJ (1 << 10) +#define POWER_BATTMONITOR_PWDN_BATTBRNOUT (1 << 9) +#define POWER_BATTMONITOR_BRWNOUT_PWD (1 << 8) +#define POWER_BATTMONITOR_BRWNOUT_LVL_MASK 0x1f +#define POWER_BATTMONITOR_BRWNOUT_LVL_OFFSET 0 + +#define POWER_RESET_UNLOCK_MASK (0xffff << 16) +#define POWER_RESET_UNLOCK_OFFSET 16 +#define POWER_RESET_UNLOCK_KEY (0x3e77 << 16) +#define MX28_POWER_RESET_FASTFALL_PSWITCH_OFF (1 << 2) +#define POWER_RESET_PWD_OFF (1 << 1) +#define POWER_RESET_PWD (1 << 0) + +#define POWER_DEBUG_VBUSVALIDPIOLOCK (1 << 3) +#define POWER_DEBUG_AVALIDPIOLOCK (1 << 2) +#define POWER_DEBUG_BVALIDPIOLOCK (1 << 1) +#define POWER_DEBUG_SESSENDPIOLOCK (1 << 0) + +#define MX28_POWER_THERMAL_TEST (1 << 8) +#define MX28_POWER_THERMAL_PWD (1 << 7) +#define MX28_POWER_THERMAL_LOW_POWER (1 << 6) +#define MX28_POWER_THERMAL_OFFSET_ADJ_MASK (0x3 << 4) +#define MX28_POWER_THERMAL_OFFSET_ADJ_OFFSET 4 +#define MX28_POWER_THERMAL_OFFSET_ADJ_ENABLE (1 << 3) +#define MX28_POWER_THERMAL_TEMP_THRESHOLD_MASK 0x7 +#define MX28_POWER_THERMAL_TEMP_THRESHOLD_OFFSET 0 + +#define MX28_POWER_USB1CTRL_AVALID1 (1 << 3) +#define MX28_POWER_USB1CTRL_BVALID1 (1 << 2) +#define MX28_POWER_USB1CTRL_VBUSVALID1 (1 << 1) +#define MX28_POWER_USB1CTRL_SESSEND1 (1 << 0) + +#define POWER_SPECIAL_TEST_MASK 0xffffffff +#define POWER_SPECIAL_TEST_OFFSET 0 + +#define POWER_VERSION_MAJOR_MASK (0xff << 24) +#define POWER_VERSION_MAJOR_OFFSET 24 +#define POWER_VERSION_MINOR_MASK (0xff << 16) +#define POWER_VERSION_MINOR_OFFSET 16 +#define POWER_VERSION_STEP_MASK 0xffff +#define POWER_VERSION_STEP_OFFSET 0 + +#define MX28_POWER_ANACLKCTRL_CLKGATE_0 (1 << 31) +#define MX28_POWER_ANACLKCTRL_OUTDIV_MASK (0x7 << 28) +#define MX28_POWER_ANACLKCTRL_OUTDIV_OFFSET 28 +#define MX28_POWER_ANACLKCTRL_INVERT_OUTCLK (1 << 27) +#define MX28_POWER_ANACLKCTRL_CLKGATE_I (1 << 26) +#define MX28_POWER_ANACLKCTRL_DITHER_OFF (1 << 10) +#define MX28_POWER_ANACLKCTRL_SLOW_DITHER (1 << 9) +#define MX28_POWER_ANACLKCTRL_INVERT_INCLK (1 << 8) +#define MX28_POWER_ANACLKCTRL_INCLK_SHIFT_MASK (0x3 << 4) +#define MX28_POWER_ANACLKCTRL_INCLK_SHIFT_OFFSET 4 +#define MX28_POWER_ANACLKCTRL_INDIV_MASK 0x7 +#define MX28_POWER_ANACLKCTRL_INDIV_OFFSET 0 + +#define MX28_POWER_REFCTRL_FASTSETTLING (1 << 26) +#define MX28_POWER_REFCTRL_RAISE_REF (1 << 25) +#define MX28_POWER_REFCTRL_XTAL_BGR_BIAS (1 << 24) +#define MX28_POWER_REFCTRL_VBG_ADJ_MASK (0x7 << 20) +#define MX28_POWER_REFCTRL_VBG_ADJ_OFFSET 20 +#define MX28_POWER_REFCTRL_LOW_PWR (1 << 19) +#define MX28_POWER_REFCTRL_BIAS_CTRL_MASK (0x3 << 16) +#define MX28_POWER_REFCTRL_BIAS_CTRL_OFFSET 16 +#define MX28_POWER_REFCTRL_VDDXTAL_TO_VDDD (1 << 14) +#define MX28_POWER_REFCTRL_ADJ_ANA (1 << 13) +#define MX28_POWER_REFCTRL_ADJ_VAG (1 << 12) +#define MX28_POWER_REFCTRL_ANA_REFVAL_MASK (0xf << 8) +#define MX28_POWER_REFCTRL_ANA_REFVAL_OFFSET 8 +#define MX28_POWER_REFCTRL_VAG_VAL_MASK (0xf << 4) +#define MX28_POWER_REFCTRL_VAG_VAL_OFFSET 4 + +#endif /* __MX28_REGS_POWER_H__ */ diff --git a/arch/arm/mach-mxs/include/mach/regs-rtc.h b/arch/arm/mach-mxs/include/mach/regs-rtc.h new file mode 100644 index 0000000..bd8fdad --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/regs-rtc.h @@ -0,0 +1,134 @@ +/* + * Freescale i.MX28 RTC Register Definitions + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MX28_REGS_RTC_H__ +#define __MX28_REGS_RTC_H__ + +#include + +#ifndef __ASSEMBLY__ +struct mxs_rtc_regs { + mxs_reg_32(hw_rtc_ctrl) + mxs_reg_32(hw_rtc_stat) + mxs_reg_32(hw_rtc_milliseconds) + mxs_reg_32(hw_rtc_seconds) + mxs_reg_32(hw_rtc_rtc_alarm) + mxs_reg_32(hw_rtc_watchdog) + mxs_reg_32(hw_rtc_persistent0) + mxs_reg_32(hw_rtc_persistent1) + mxs_reg_32(hw_rtc_persistent2) + mxs_reg_32(hw_rtc_persistent3) + mxs_reg_32(hw_rtc_persistent4) + mxs_reg_32(hw_rtc_persistent5) + mxs_reg_32(hw_rtc_debug) + mxs_reg_32(hw_rtc_version) +}; +#endif + +#define RTC_CTRL_SFTRST (1 << 31) +#define RTC_CTRL_CLKGATE (1 << 30) +#define RTC_CTRL_SUPPRESS_COPY2ANALOG (1 << 6) +#define RTC_CTRL_FORCE_UPDATE (1 << 5) +#define RTC_CTRL_WATCHDOGEN (1 << 4) +#define RTC_CTRL_ONEMSEC_IRQ (1 << 3) +#define RTC_CTRL_ALARM_IRQ (1 << 2) +#define RTC_CTRL_ONEMSEC_IRQ_EN (1 << 1) +#define RTC_CTRL_ALARM_IRQ_EN (1 << 0) + +#define RTC_STAT_RTC_PRESENT (1 << 31) +#define RTC_STAT_ALARM_PRESENT (1 << 30) +#define RTC_STAT_WATCHDOG_PRESENT (1 << 29) +#define RTC_STAT_XTAL32000_PRESENT (1 << 28) +#define RTC_STAT_XTAL32768_PRESENT (1 << 27) +#define RTC_STAT_STALE_REGS_MASK (0xff << 16) +#define RTC_STAT_STALE_REGS_OFFSET 16 +#define RTC_STAT_NEW_REGS_MASK (0xff << 8) +#define RTC_STAT_NEW_REGS_OFFSET 8 + +#define RTC_MILLISECONDS_COUNT_MASK 0xffffffff +#define RTC_MILLISECONDS_COUNT_OFFSET 0 + +#define RTC_SECONDS_COUNT_MASK 0xffffffff +#define RTC_SECONDS_COUNT_OFFSET 0 + +#define RTC_ALARM_VALUE_MASK 0xffffffff +#define RTC_ALARM_VALUE_OFFSET 0 + +#define RTC_WATCHDOG_COUNT_MASK 0xffffffff +#define RTC_WATCHDOG_COUNT_OFFSET 0 + +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_MASK (0xf << 28) +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_OFFSET 28 +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_2V83 (0x0 << 28) +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_2V78 (0x1 << 28) +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_2V73 (0x2 << 28) +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_2V68 (0x3 << 28) +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_2V62 (0x4 << 28) +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_2V57 (0x5 << 28) +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_2V52 (0x6 << 28) +#define RTC_PERSISTENT0_ADJ_POSLIMITBUCK_2V48 (0x7 << 28) +#define RTC_PERSISTENT0_EXTERNAL_RESET (1 << 21) +#define RTC_PERSISTENT0_THERMAL_RESET (1 << 20) +#define RTC_PERSISTENT0_ENABLE_LRADC_PWRUP (1 << 18) +#define RTC_PERSISTENT0_AUTO_RESTART (1 << 17) +#define RTC_PERSISTENT0_DISABLE_PSWITCH (1 << 16) +#define RTC_PERSISTENT0_LOWERBIAS_MASK (0xf << 14) +#define RTC_PERSISTENT0_LOWERBIAS_OFFSET 14 +#define RTC_PERSISTENT0_LOWERBIAS_NOMINAL (0x0 << 14) +#define RTC_PERSISTENT0_LOWERBIAS_M25P (0x1 << 14) +#define RTC_PERSISTENT0_LOWERBIAS_M50P (0x3 << 14) +#define RTC_PERSISTENT0_DISABLE_XTALOK (1 << 13) +#define RTC_PERSISTENT0_MSEC_RES_MASK (0x1f << 8) +#define RTC_PERSISTENT0_MSEC_RES_OFFSET 8 +#define RTC_PERSISTENT0_MSEC_RES_1MS (0x01 << 8) +#define RTC_PERSISTENT0_MSEC_RES_2MS (0x02 << 8) +#define RTC_PERSISTENT0_MSEC_RES_4MS (0x04 << 8) +#define RTC_PERSISTENT0_MSEC_RES_8MS (0x08 << 8) +#define RTC_PERSISTENT0_MSEC_RES_16MS (0x10 << 8) +#define RTC_PERSISTENT0_ALARM_WAKE (1 << 7) +#define RTC_PERSISTENT0_XTAL32_FREQ (1 << 6) +#define RTC_PERSISTENT0_XTAL32KHZ_PWRUP (1 << 5) +#define RTC_PERSISTENT0_XTAL24KHZ_PWRUP (1 << 4) +#define RTC_PERSISTENT0_LCK_SECS (1 << 3) +#define RTC_PERSISTENT0_ALARM_EN (1 << 2) +#define RTC_PERSISTENT0_ALARM_WAKE_EN (1 << 1) +#define RTC_PERSISTENT0_CLOCKSOURCE (1 << 0) + +#define RTC_PERSISTENT1_GENERAL_MASK 0xffffffff +#define RTC_PERSISTENT1_GENERAL_OFFSET 0 +#define RTC_PERSISTENT1_GENERAL_OTG_ALT_ROLE 0x0080 +#define RTC_PERSISTENT1_GENERAL_OTG_HNP 0x0100 +#define RTC_PERSISTENT1_GENERAL_USB_LPM 0x0200 +#define RTC_PERSISTENT1_GENERAL_SKIP_CHECKDISK 0x0400 +#define RTC_PERSISTENT1_GENERAL_USB_BOOT_PLAYER 0x0800 +#define RTC_PERSISTENT1_GENERAL_ENUM_500MA_2X 0x1000 + +#define RTC_PERSISTENT2_GENERAL_MASK 0xffffffff +#define RTC_PERSISTENT2_GENERAL_OFFSET 0 + +#define RTC_PERSISTENT3_GENERAL_MASK 0xffffffff +#define RTC_PERSISTENT3_GENERAL_OFFSET 0 + +#define RTC_PERSISTENT4_GENERAL_MASK 0xffffffff +#define RTC_PERSISTENT4_GENERAL_OFFSET 0 + +#define RTC_PERSISTENT5_GENERAL_MASK 0xffffffff +#define RTC_PERSISTENT5_GENERAL_OFFSET 0 + +#define RTC_DEBUG_WATCHDOG_RESET_MASK (1 << 1) +#define RTC_DEBUG_WATCHDOG_RESET (1 << 0) + +#define RTC_VERSION_MAJOR_MASK (0xff << 24) +#define RTC_VERSION_MAJOR_OFFSET 24 +#define RTC_VERSION_MINOR_MASK (0xff << 16) +#define RTC_VERSION_MINOR_OFFSET 16 +#define RTC_VERSION_STEP_MASK 0xffff +#define RTC_VERSION_STEP_OFFSET 0 + +#endif /* __MX28_REGS_RTC_H__ */ diff --git a/arch/arm/mach-mxs/lradc-init.c b/arch/arm/mach-mxs/lradc-init.c new file mode 100644 index 0000000..682a475 --- /dev/null +++ b/arch/arm/mach-mxs/lradc-init.c @@ -0,0 +1,70 @@ +/* + * Freescale i.MX28 Battery measurement init + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +void mxs_lradc_init(void) +{ + struct mxs_lradc_regs *regs = (struct mxs_lradc_regs *)IMX_LRADC_BASE; + + writel(LRADC_CTRL0_SFTRST, ®s->hw_lradc_ctrl0_clr); + writel(LRADC_CTRL0_CLKGATE, ®s->hw_lradc_ctrl0_clr); + writel(LRADC_CTRL0_ONCHIP_GROUNDREF, ®s->hw_lradc_ctrl0_clr); + + clrsetbits_le32(®s->hw_lradc_ctrl3, + LRADC_CTRL3_CYCLE_TIME_MASK, + LRADC_CTRL3_CYCLE_TIME_6MHZ); + + clrsetbits_le32(®s->hw_lradc_ctrl4, + LRADC_CTRL4_LRADC7SELECT_MASK | + LRADC_CTRL4_LRADC6SELECT_MASK, + LRADC_CTRL4_LRADC7SELECT_CHANNEL7 | + LRADC_CTRL4_LRADC6SELECT_CHANNEL10); +} + +void mxs_lradc_enable_batt_measurement(void) +{ + struct mxs_lradc_regs *regs = (struct mxs_lradc_regs *)IMX_LRADC_BASE; + + /* Check if the channel is present at all. */ + if (!(readl(®s->hw_lradc_status) & LRADC_STATUS_CHANNEL7_PRESENT)) + return; + + writel(LRADC_CTRL1_LRADC7_IRQ_EN, ®s->hw_lradc_ctrl1_clr); + writel(LRADC_CTRL1_LRADC7_IRQ, ®s->hw_lradc_ctrl1_clr); + + clrsetbits_le32(®s->hw_lradc_conversion, + LRADC_CONVERSION_SCALE_FACTOR_MASK, + LRADC_CONVERSION_SCALE_FACTOR_LI_ION); + writel(LRADC_CONVERSION_AUTOMATIC, ®s->hw_lradc_conversion_set); + + /* Configure the channel. */ + writel((1 << 7) << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, + ®s->hw_lradc_ctrl2_clr); + writel(0xffffffff, ®s->hw_lradc_ch7_clr); + clrbits_le32(®s->hw_lradc_ch7, LRADC_CH_NUM_SAMPLES_MASK); + writel(LRADC_CH_ACCUMULATE, ®s->hw_lradc_ch7_clr); + + /* Schedule the channel. */ + writel(1 << 7, ®s->hw_lradc_ctrl0_set); + + /* Start the channel sampling. */ + writel(((1 << 7) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) | + ((1 << 3) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) | + 100, ®s->hw_lradc_delay3); + + writel(0xffffffff, ®s->hw_lradc_ch7_clr); + + writel(LRADC_DELAY_KICK, ®s->hw_lradc_delay3_set); +} diff --git a/arch/arm/mach-mxs/mem-init.c b/arch/arm/mach-mxs/mem-init.c new file mode 100644 index 0000000..9773f94 --- /dev/null +++ b/arch/arm/mach-mxs/mem-init.c @@ -0,0 +1,371 @@ +/* + * Freescale i.MX28 RAM init + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * Copyright 2013 Stefan Roese + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include +#include +#if defined CONFIG_ARCH_IMX23 +# include +#endif +#if defined CONFIG_ARCH_IMX28 +# include +#endif + +/* 1 second delay should be plenty of time for block reset. */ +#define RESET_MAX_TIMEOUT 1000000 + +#define MXS_BLOCK_SFTRST (1 << 31) +#define MXS_BLOCK_CLKGATE (1 << 30) + +int mxs_early_wait_mask_set(struct mxs_register_32 *reg, uint32_t mask, unsigned + int timeout) +{ + while (--timeout) { + if ((readl(®->reg) & mask) == mask) + break; + mxs_early_delay(1); + } + + return !timeout; +} + +int mxs_early_wait_mask_clr(struct mxs_register_32 *reg, uint32_t mask, unsigned + int timeout) +{ + while (--timeout) { + if ((readl(®->reg) & mask) == 0) + break; + mxs_early_delay(1); + } + + return !timeout; +} + +int mxs_early_reset_block(struct mxs_register_32 *reg) +{ + /* Clear SFTRST */ + writel(MXS_BLOCK_SFTRST, ®->reg_clr); + + if (mxs_early_wait_mask_clr(reg, MXS_BLOCK_SFTRST, RESET_MAX_TIMEOUT)) + return 1; + + /* Clear CLKGATE */ + writel(MXS_BLOCK_CLKGATE, ®->reg_clr); + + /* Set SFTRST */ + writel(MXS_BLOCK_SFTRST, ®->reg_set); + + /* Wait for CLKGATE being set */ + if (mxs_early_wait_mask_set(reg, MXS_BLOCK_CLKGATE, RESET_MAX_TIMEOUT)) + return 1; + + /* Clear SFTRST */ + writel(MXS_BLOCK_SFTRST, ®->reg_clr); + + if (mxs_early_wait_mask_clr(reg, MXS_BLOCK_SFTRST, RESET_MAX_TIMEOUT)) + return 1; + + /* Clear CLKGATE */ + writel(MXS_BLOCK_CLKGATE, ®->reg_clr); + + if (mxs_early_wait_mask_clr(reg, MXS_BLOCK_CLKGATE, RESET_MAX_TIMEOUT)) + return 1; + + return 0; +} + +uint32_t mx28_dram_vals[] = { +/* + * i.MX28 DDR2 at 200MHz + */ + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00010101, 0x01010101, + 0x000f0f01, 0x0f02020a, 0x00000000, 0x00010101, + 0x00000100, 0x00000100, 0x00000000, 0x00000002, + 0x01010000, 0x07080403, 0x06005003, 0x0a0000c8, + 0x02009c40, 0x0002030c, 0x0036a609, 0x031a0612, + 0x02030202, 0x00c8001c, 0x00000000, 0x00000000, + 0x00012100, 0xffff0303, 0x00012100, 0xffff0303, + 0x00012100, 0xffff0303, 0x00012100, 0xffff0303, + 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000612, 0x01000F02, + 0x06120612, 0x00000200, 0x00020007, 0xf4004a27, + 0xf4004a27, 0xf4004a27, 0xf4004a27, 0x07000300, + 0x07000300, 0x07400300, 0x07400300, 0x00000005, + 0x00000000, 0x00000000, 0x01000000, 0x01020408, + 0x08040201, 0x000f1133, 0x00000000, 0x00001f04, + 0x00001f04, 0x00001f04, 0x00001f04, 0x00001f04, + 0x00001f04, 0x00001f04, 0x00001f04, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00010000, 0x00030404, + 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x01010000, + 0x01000000, 0x03030000, 0x00010303, 0x01020202, + 0x00000000, 0x02040303, 0x21002103, 0x00061200, + 0x06120612, 0x04420442, 0x04420442, 0x00040004, + 0x00040004, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xffffffff +}; + +/* + * i.MX23 DDR at 133MHz + */ +uint32_t mx23_dram_vals[] = { + 0x01010001, 0x00010100, 0x01000101, 0x00000001, + 0x00000101, 0x00000000, 0x00010000, 0x01000001, + 0x00000000, 0x00000001, 0x07000200, 0x00070202, + 0x02020000, 0x04040a01, 0x00000201, 0x02040000, + 0x02000000, 0x19000f08, 0x0d0d0000, 0x02021313, + 0x02061521, 0x0000000a, 0x00080008, 0x00200020, + 0x00200020, 0x00200020, 0x000003f7, 0x00000000, + 0x00000000, 0x00000020, 0x00000020, 0x00c80000, + 0x000a23cd, 0x000000c8, 0x00006665, 0x00000000, + 0x00000101, 0x00040001, 0x00000000, 0x00000000, + 0x00010000 +}; + +static void mx28_initialize_dram_values(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mx28_dram_vals); i++) + writel(mx28_dram_vals[i], IMX_SDRAMC_BASE + (4 * i)); +} + +static void mx23_initialize_dram_values(void) +{ + int i; + + /* + * HW_DRAM_CTL27, HW_DRAM_CTL28 and HW_DRAM_CTL35 are not initialized as + * per FSL bootlets code. + * + * mx23 Reference Manual marks HW_DRAM_CTL27 and HW_DRAM_CTL28 as + * "reserved". + * HW_DRAM_CTL8 is setup as the last element. + * So skip the initialization of these HW_DRAM_CTL registers. + */ + for (i = 0; i < ARRAY_SIZE(mx23_dram_vals); i++) { + if (i == 8 || i == 27 || i == 28 || i == 35) + continue; + writel(mx23_dram_vals[i], IMX_SDRAMC_BASE + (4 * i)); + } + + /* + * Enable tRAS lockout in HW_DRAM_CTL08 ; it must be the last + * element to be set + */ + writel((1 << 24), IMX_SDRAMC_BASE + (4 * 8)); +} + +void mxs_mem_init_clock(unsigned char divider) +{ + struct mxs_clkctrl_regs *clkctrl_regs = + (struct mxs_clkctrl_regs *)IMX_CCM_BASE; + + /* Gate EMI clock */ + writeb(CLKCTRL_FRAC_CLKGATE, + &clkctrl_regs->hw_clkctrl_frac0_set[CLKCTRL_FRAC0_EMI]); + + /* Set fractional divider for ref_emi */ + writeb(CLKCTRL_FRAC_CLKGATE | (divider & CLKCTRL_FRAC_FRAC_MASK), + &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]); + + /* Ungate EMI clock */ + writeb(CLKCTRL_FRAC_CLKGATE, + &clkctrl_regs->hw_clkctrl_frac0_clr[CLKCTRL_FRAC0_EMI]); + + mxs_early_delay(11000); + + /* Set EMI clock divider for EMI clock to 411 / 2 = 205MHz */ + writel((2 << CLKCTRL_EMI_DIV_EMI_OFFSET) | + (1 << CLKCTRL_EMI_DIV_XTAL_OFFSET), + &clkctrl_regs->hw_clkctrl_emi); + + /* Unbypass EMI */ + writel(CLKCTRL_CLKSEQ_BYPASS_EMI, + &clkctrl_regs->hw_clkctrl_clkseq_clr); + + mxs_early_delay(10000); +} + +void mxs_mem_setup_cpu_and_hbus(void) +{ + struct mxs_clkctrl_regs *clkctrl_regs = + (struct mxs_clkctrl_regs *)IMX_CCM_BASE; + + /* Set fractional divider for ref_cpu to 480 * 18 / 19 = 454MHz + * and ungate CPU clock */ + writeb(19 & CLKCTRL_FRAC_FRAC_MASK, + (uint8_t *)&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]); + + /* Set CPU bypass */ + writel(CLKCTRL_CLKSEQ_BYPASS_CPU, + &clkctrl_regs->hw_clkctrl_clkseq_set); + + /* HBUS = 151MHz */ + writel(CLKCTRL_HBUS_DIV_MASK, &clkctrl_regs->hw_clkctrl_hbus_set); + writel(((~3) << CLKCTRL_HBUS_DIV_OFFSET) & CLKCTRL_HBUS_DIV_MASK, + &clkctrl_regs->hw_clkctrl_hbus_clr); + + mxs_early_delay(10000); + + /* CPU clock divider = 1 */ + clrsetbits_le32(&clkctrl_regs->hw_clkctrl_cpu, + CLKCTRL_CPU_DIV_CPU_MASK, 1); + + /* Disable CPU bypass */ + writel(CLKCTRL_CLKSEQ_BYPASS_CPU, + &clkctrl_regs->hw_clkctrl_clkseq_clr); + + mxs_early_delay(15000); +} + +void mxs_mem_setup_vdda(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + writel((0xc << POWER_VDDACTRL_TRG_OFFSET) | + (0x7 << POWER_VDDACTRL_BO_OFFSET_OFFSET) | + POWER_VDDACTRL_LINREG_OFFSET_1STEPS_BELOW, + &power_regs->hw_power_vddactrl); +} + +static void mx23_mem_setup_vddmem(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + /* We must wait before and after disabling the current limiter! */ + mxs_early_delay(10000); + + clrbits_le32(&power_regs->hw_power_vddmemctrl, + POWER_VDDMEMCTRL_ENABLE_ILIMIT); + + mxs_early_delay(10000); +} + +void mx23_mem_init(void) +{ + mxs_early_delay(11000); + + /* Fractional divider for ref_emi is 33 ; 480 * 18 / 33 = 266MHz */ + mxs_mem_init_clock(33); + + mxs_mem_setup_vdda(); + + /* + * Reset/ungate the EMI block. This is essential, otherwise the system + * suffers from memory instability. This thing is mx23 specific and is + * no longer present on mx28. + */ + mxs_early_reset_block((struct mxs_register_32 *)IMX_EMI_BASE); + + mx23_mem_setup_vddmem(); + + /* + * Configure the DRAM registers + */ + + /* Clear START and SREFRESH bit from DRAM_CTL8 */ + clrbits_le32(IMX_SDRAMC_BASE + 0x20, (1 << 16) | (1 << 8)); + + mx23_initialize_dram_values(); + + /* Set START bit in DRAM_CTL8 */ + setbits_le32(IMX_SDRAMC_BASE + 0x20, 1 << 16); + + clrbits_le32(IMX_SDRAMC_BASE + 0x40, 1 << 17); + + /* Wait for EMI_STAT bit DRAM_HALTED */ + for (;;) { + if (!(readl(IMX_EMI_BASE + 0x10) & (1 << 1))) + break; + mxs_early_delay(1000); + } + + /* Adjust EMI port priority. */ + clrsetbits_le32(0x80020000, 0x1f << 16, 0x2); + mxs_early_delay(20000); + + setbits_le32(IMX_SDRAMC_BASE + 0x40, 1 << 19); + setbits_le32(IMX_SDRAMC_BASE + 0x40, 1 << 11); + + mxs_early_delay(10000); + + mxs_mem_setup_cpu_and_hbus(); +} + +#define PINCTRL_EMI_DS_CTRL_DDR_MODE_DDR2 (0x3 << 16) + +void mx28_mem_init(void) +{ + mxs_early_delay(11000); + + /* Fractional divider for ref_emi is 21 ; 480 * 18 / 21 = 411MHz */ + mxs_mem_init_clock(21); + + mxs_mem_setup_vdda(); + + /* Set DDR2 mode */ + writel(PINCTRL_EMI_DS_CTRL_DDR_MODE_DDR2, + IMX_IOMUXC_BASE + 0x1b80); + + /* + * Configure the DRAM registers + */ + + /* Clear START bit from DRAM_CTL16 */ + clrbits_le32(IMX_SDRAMC_BASE + 0x40, 1); + + mx28_initialize_dram_values(); + + /* Clear SREFRESH bit from DRAM_CTL17 */ + clrbits_le32(IMX_SDRAMC_BASE + 0x44, 1); + + /* Set START bit in DRAM_CTL16 */ + setbits_le32(IMX_SDRAMC_BASE + 0x40, 1); + + /* Wait for bit 20 (DRAM init complete) in DRAM_CTL58 */ + while (!(readl(IMX_SDRAMC_BASE + 0xe8) & (1 << 20))) + ; + + mxs_early_delay(10000); + + mxs_mem_setup_cpu_and_hbus(); +} diff --git a/arch/arm/mach-mxs/mxs23img.cfg b/arch/arm/mach-mxs/mxs23img.cfg new file mode 100644 index 0000000..34e8848 --- /dev/null +++ b/arch/arm/mach-mxs/mxs23img.cfg @@ -0,0 +1,6 @@ +SECTION 0x0 BOOTABLE + TAG LAST + LOAD 0x1000 @PREP@ + CALL 0x1000 0x0 + LOAD 0x40002000 @BOOTLOADER@ + CALL 0x40002000 0x0 diff --git a/arch/arm/mach-mxs/mxs28img.cfg b/arch/arm/mach-mxs/mxs28img.cfg new file mode 100644 index 0000000..0ff2c35 --- /dev/null +++ b/arch/arm/mach-mxs/mxs28img.cfg @@ -0,0 +1,8 @@ +SECTION 0x0 BOOTABLE + TAG LAST + LOAD 0x1000 @PREP@ + LOAD IVT 0x8000 0x1000 + CALL HAB 0x8000 0x0 + LOAD 0x40002000 @BOOTLOADER@ + LOAD IVT 0x8000 0x40002000 + CALL HAB 0x8000 0x0 diff --git a/arch/arm/mach-mxs/ocotp.c b/arch/arm/mach-mxs/ocotp.c index c7c24e0..abdd445 100644 --- a/arch/arm/mach-mxs/ocotp.c +++ b/arch/arm/mach-mxs/ocotp.c @@ -27,7 +27,6 @@ #include #include -#include #include #define DRIVERNAME "ocotp" diff --git a/arch/arm/mach-mxs/power-init.c b/arch/arm/mach-mxs/power-init.c new file mode 100644 index 0000000..977c6e4 --- /dev/null +++ b/arch/arm/mach-mxs/power-init.c @@ -0,0 +1,1272 @@ +/* + * Freescale i.MX28 Boot PMIC init + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#include +#include +#ifdef CONFIG_ARCH_IMX23 +#include +#endif +#ifdef CONFIG_ARCH_IMX28 +#include +#endif +#include +#include +#include + +/* + * This delay function is intended to be used only in early stage of boot, where + * clock are not set up yet. The timer used here is reset on every boot and + * takes a few seconds to roll. The boot doesn't take that long, so to keep the + * code simple, it doesn't take rolling into consideration. + */ +void mxs_early_delay(int delay) +{ + void __iomem *digctl_regs = IOMEM(IMX_DIGCTL_BASE); + uint32_t st = readl(digctl_regs + 0xc0); + + st += delay; + + while (st > readl(digctl_regs + 0xc0)); +} + +static inline void charger_4p2_disable(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + if (cpu_is_mx28()) + writel(MX28_POWER_5VCTRL_PWD_CHARGE_4P2_MASK, + &power_regs->hw_power_5vctrl_set); + else + writel(MX23_POWER_5VCTRL_PWD_CHARGE_4P2_MASK, + &power_regs->hw_power_5vctrl_set); +} + +static inline void charger_4p2_enable(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + if (cpu_is_mx28()) + writel(MX28_POWER_5VCTRL_PWD_CHARGE_4P2_MASK, + &power_regs->hw_power_5vctrl_clr); + else + writel(MX23_POWER_5VCTRL_PWD_CHARGE_4P2_MASK, + &power_regs->hw_power_5vctrl_clr); +} + +/** + * mxs_power_clock2xtal() - Switch CPU core clock source to 24MHz XTAL + * + * This function switches the CPU core clock from PLL to 24MHz XTAL + * oscilator. This is necessary if the PLL is being reconfigured to + * prevent crash of the CPU core. + */ +static void mxs_power_clock2xtal(void) +{ + struct mxs_clkctrl_regs *clkctrl_regs = + (struct mxs_clkctrl_regs *)IMX_CCM_BASE; + + /* Set XTAL as CPU reference clock */ + writel(CLKCTRL_CLKSEQ_BYPASS_CPU, + &clkctrl_regs->hw_clkctrl_clkseq_set); +} + +/** + * mxs_power_clock2pll() - Switch CPU core clock source to PLL + * + * This function switches the CPU core clock from 24MHz XTAL oscilator + * to PLL. This can only be called once the PLL has re-locked and once + * the PLL is stable after reconfiguration. + */ +static void mxs_power_clock2pll(void) +{ + struct mxs_clkctrl_regs *clkctrl_regs = + (struct mxs_clkctrl_regs *)IMX_CCM_BASE; + + setbits_le32(&clkctrl_regs->hw_clkctrl_pll0ctrl0, + CLKCTRL_PLL0CTRL0_POWER); + mxs_early_delay(100); + setbits_le32(&clkctrl_regs->hw_clkctrl_clkseq, + CLKCTRL_CLKSEQ_BYPASS_CPU); +} + +/** + * mxs_power_set_auto_restart() - Set the auto-restart bit + * + * This function ungates the RTC block and sets the AUTO_RESTART + * bit to work around a design bug on MX28EVK Rev. A . + */ + +static void mxs_power_set_auto_restart(void) +{ + struct mxs_rtc_regs *rtc_regs = + (struct mxs_rtc_regs *)IMX_WDT_BASE; + + writel(RTC_CTRL_SFTRST, &rtc_regs->hw_rtc_ctrl_clr); + while (readl(&rtc_regs->hw_rtc_ctrl) & RTC_CTRL_SFTRST) + ; + + writel(RTC_CTRL_CLKGATE, &rtc_regs->hw_rtc_ctrl_clr); + while (readl(&rtc_regs->hw_rtc_ctrl) & RTC_CTRL_CLKGATE) + ; + + /* Do nothing if flag already set */ + if (readl(&rtc_regs->hw_rtc_persistent0) & RTC_PERSISTENT0_AUTO_RESTART) + return; + + while (readl(&rtc_regs->hw_rtc_stat) & RTC_STAT_NEW_REGS_MASK) + ; + + setbits_le32(&rtc_regs->hw_rtc_persistent0, + RTC_PERSISTENT0_AUTO_RESTART); + writel(RTC_CTRL_FORCE_UPDATE, &rtc_regs->hw_rtc_ctrl_set); + writel(RTC_CTRL_FORCE_UPDATE, &rtc_regs->hw_rtc_ctrl_clr); + while (readl(&rtc_regs->hw_rtc_stat) & RTC_STAT_NEW_REGS_MASK) + ; + while (readl(&rtc_regs->hw_rtc_stat) & RTC_STAT_STALE_REGS_MASK) + ; +} + +/** + * mxs_power_set_linreg() - Set linear regulators 25mV below DC-DC converter + * + * This function configures the VDDIO, VDDA and VDDD linear regulators output + * to be 25mV below the VDDIO, VDDA and VDDD output from the DC-DC switching + * converter. This is the recommended setting for the case where we use both + * linear regulators and DC-DC converter to power the VDDIO rail. + */ +static void mxs_power_set_linreg(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + /* Set linear regulator 25mV below switching converter */ + clrsetbits_le32(&power_regs->hw_power_vdddctrl, + POWER_VDDDCTRL_LINREG_OFFSET_MASK, + POWER_VDDDCTRL_LINREG_OFFSET_1STEPS_BELOW); + + clrsetbits_le32(&power_regs->hw_power_vddactrl, + POWER_VDDACTRL_LINREG_OFFSET_MASK, + POWER_VDDACTRL_LINREG_OFFSET_1STEPS_BELOW); + + clrsetbits_le32(&power_regs->hw_power_vddioctrl, + POWER_VDDIOCTRL_LINREG_OFFSET_MASK, + POWER_VDDIOCTRL_LINREG_OFFSET_1STEPS_BELOW); +} + +/** + * mxs_get_batt_volt() - Measure battery input voltage + * + * This function retrieves the battery input voltage and returns it. + */ +static int mxs_get_batt_volt(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + uint32_t volt = readl(&power_regs->hw_power_battmonitor); + + volt &= POWER_BATTMONITOR_BATT_VAL_MASK; + volt >>= POWER_BATTMONITOR_BATT_VAL_OFFSET; + volt *= 8; + + return volt; +} + +/** + * mxs_is_batt_ready() - Test if the battery provides enough voltage to boot + * + * This function checks if the battery input voltage is higher than 3.6V and + * therefore allows the system to successfully boot using this power source. + */ +static int mxs_is_batt_ready(void) +{ + return (mxs_get_batt_volt() >= 3600); +} + +/** + * mxs_is_batt_good() - Test if battery is operational at all + * + * This function starts recharging the battery and tests if the input current + * provided by the 5V input recharging the battery is also sufficient to power + * the DC-DC converter. + */ +static int mxs_is_batt_good(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + uint32_t volt = mxs_get_batt_volt(); + + if ((volt >= 2400) && (volt <= 4300)) + return 1; + + clrsetbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK, + 0x3 << POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET); + charger_4p2_enable(); + + clrsetbits_le32(&power_regs->hw_power_charge, + POWER_CHARGE_STOP_ILIMIT_MASK | POWER_CHARGE_BATTCHRG_I_MASK, + POWER_CHARGE_STOP_ILIMIT_10MA | 0x3); + + writel(POWER_CHARGE_PWD_BATTCHRG, &power_regs->hw_power_charge_clr); + charger_4p2_enable(); + + mxs_early_delay(500000); + + volt = mxs_get_batt_volt(); + + if (volt >= 3500) + return 0; + + if (volt >= 2400) + return 1; + + writel(POWER_CHARGE_STOP_ILIMIT_MASK | POWER_CHARGE_BATTCHRG_I_MASK, + &power_regs->hw_power_charge_clr); + writel(POWER_CHARGE_PWD_BATTCHRG, &power_regs->hw_power_charge_set); + + return 0; +} + +/** + * mxs_power_setup_5v_detect() - Start the 5V input detection comparator + * + * This function enables the 5V detection comparator and sets the 5V valid + * threshold to 4.4V . We use 4.4V threshold here to make sure that even + * under high load, the voltage drop on the 5V input won't be so critical + * to cause undervolt on the 4P2 linear regulator supplying the DC-DC + * converter and thus making the system crash. + */ +static void mxs_power_setup_5v_detect(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + /* Start 5V detection */ + clrsetbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_VBUSVALID_TRSH_MASK, + POWER_5VCTRL_VBUSVALID_TRSH_4V4 | + POWER_5VCTRL_PWRUP_VBUS_CMPS); +} + +/** + * mxs_src_power_init() - Preconfigure the power block + * + * This function configures reasonable values for the DC-DC control loop + * and battery monitor. + */ +static void mxs_src_power_init(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + /* Improve efficieny and reduce transient ripple */ + writel(POWER_LOOPCTRL_TOGGLE_DIF | POWER_LOOPCTRL_EN_CM_HYST | + POWER_LOOPCTRL_EN_DF_HYST, &power_regs->hw_power_loopctrl_set); + + clrsetbits_le32(&power_regs->hw_power_dclimits, + POWER_DCLIMITS_POSLIMIT_BUCK_MASK, + 0x30 << POWER_DCLIMITS_POSLIMIT_BUCK_OFFSET); + + setbits_le32(&power_regs->hw_power_battmonitor, + POWER_BATTMONITOR_EN_BATADJ); + + /* Increase the RCSCALE level for quick DCDC response to dynamic load */ + clrsetbits_le32(&power_regs->hw_power_loopctrl, + POWER_LOOPCTRL_EN_RCSCALE_MASK, + POWER_LOOPCTRL_RCSCALE_THRESH | + POWER_LOOPCTRL_EN_RCSCALE_8X); + + clrsetbits_le32(&power_regs->hw_power_minpwr, + POWER_MINPWR_HALFFETS, POWER_MINPWR_DOUBLE_FETS); + + /* 5V to battery handoff ... FIXME */ + setbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); + mxs_early_delay(30); + clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); +} + +/** + * mxs_power_init_4p2_params() - Configure the parameters of the 4P2 regulator + * + * This function configures the necessary parameters for the 4P2 linear + * regulator to supply the DC-DC converter from 5V input. + */ +static void mxs_power_init_4p2_params(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + /* Setup 4P2 parameters */ + clrsetbits_le32(&power_regs->hw_power_dcdc4p2, + POWER_DCDC4P2_CMPTRIP_MASK | POWER_DCDC4P2_TRG_MASK, + POWER_DCDC4P2_TRG_4V2 | (31 << POWER_DCDC4P2_CMPTRIP_OFFSET)); + + clrsetbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_HEADROOM_ADJ_MASK, + 0x4 << POWER_5VCTRL_HEADROOM_ADJ_OFFSET); + + clrsetbits_le32(&power_regs->hw_power_dcdc4p2, + POWER_DCDC4P2_DROPOUT_CTRL_MASK, + POWER_DCDC4P2_DROPOUT_CTRL_100MV | + POWER_DCDC4P2_DROPOUT_CTRL_SRC_SEL); + + clrsetbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK, + 0x3f << POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET); +} + +/** + * mxs_enable_4p2_dcdc_input() - Enable or disable the DCDC input from 4P2 + * @xfer: Select if the input shall be enabled or disabled + * + * This function enables or disables the 4P2 input into the DC-DC converter. + */ +static void mxs_enable_4p2_dcdc_input(int xfer) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + uint32_t tmp, vbus_thresh, vbus_5vdetect, pwd_bo; + uint32_t prev_5v_brnout, prev_5v_droop; + + prev_5v_brnout = readl(&power_regs->hw_power_5vctrl) & + POWER_5VCTRL_PWDN_5VBRNOUT; + prev_5v_droop = readl(&power_regs->hw_power_ctrl) & + POWER_CTRL_ENIRQ_VDD5V_DROOP; + + clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_PWDN_5VBRNOUT); + writel(POWER_RESET_UNLOCK_KEY | POWER_RESET_PWD_OFF, + &power_regs->hw_power_reset); + + clrbits_le32(&power_regs->hw_power_ctrl, POWER_CTRL_ENIRQ_VDD5V_DROOP); + + if (xfer && (readl(&power_regs->hw_power_5vctrl) & + POWER_5VCTRL_ENABLE_DCDC)) { + return; + } + + /* + * Recording orignal values that will be modified temporarlily + * to handle a chip bug. See chip errata for CQ ENGR00115837 + */ + tmp = readl(&power_regs->hw_power_5vctrl); + vbus_thresh = tmp & POWER_5VCTRL_VBUSVALID_TRSH_MASK; + vbus_5vdetect = tmp & POWER_5VCTRL_VBUSVALID_5VDETECT; + + pwd_bo = readl(&power_regs->hw_power_minpwr) & POWER_MINPWR_PWD_BO; + + /* + * Disable mechanisms that get erroneously tripped by when setting + * the DCDC4P2 EN_DCDC + */ + clrbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_VBUSVALID_5VDETECT | + POWER_5VCTRL_VBUSVALID_TRSH_MASK); + + writel(POWER_MINPWR_PWD_BO, &power_regs->hw_power_minpwr_set); + + if (xfer) { + setbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_DCDC_XFER); + mxs_early_delay(20); + clrbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_DCDC_XFER); + + setbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_ENABLE_DCDC); + } else { + setbits_le32(&power_regs->hw_power_dcdc4p2, + POWER_DCDC4P2_ENABLE_DCDC); + } + + mxs_early_delay(25); + + clrsetbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_VBUSVALID_TRSH_MASK, vbus_thresh); + + if (vbus_5vdetect) + writel(vbus_5vdetect, &power_regs->hw_power_5vctrl_set); + + if (!pwd_bo) + clrbits_le32(&power_regs->hw_power_minpwr, POWER_MINPWR_PWD_BO); + + while (readl(&power_regs->hw_power_ctrl) & POWER_CTRL_VBUS_VALID_IRQ) + writel(POWER_CTRL_VBUS_VALID_IRQ, + &power_regs->hw_power_ctrl_clr); + + if (prev_5v_brnout) { + writel(POWER_5VCTRL_PWDN_5VBRNOUT, + &power_regs->hw_power_5vctrl_set); + writel(POWER_RESET_UNLOCK_KEY, + &power_regs->hw_power_reset); + } else { + writel(POWER_5VCTRL_PWDN_5VBRNOUT, + &power_regs->hw_power_5vctrl_clr); + writel(POWER_RESET_UNLOCK_KEY | POWER_RESET_PWD_OFF, + &power_regs->hw_power_reset); + } + + while (readl(&power_regs->hw_power_ctrl) & POWER_CTRL_VDD5V_DROOP_IRQ) + writel(POWER_CTRL_VDD5V_DROOP_IRQ, + &power_regs->hw_power_ctrl_clr); + + if (prev_5v_droop) + clrbits_le32(&power_regs->hw_power_ctrl, + POWER_CTRL_ENIRQ_VDD5V_DROOP); + else + setbits_le32(&power_regs->hw_power_ctrl, + POWER_CTRL_ENIRQ_VDD5V_DROOP); +} + +/** + * mxs_power_init_4p2_regulator() - Start the 4P2 regulator + * + * This function enables the 4P2 regulator and switches the DC-DC converter + * to use the 4P2 input. + */ +static void mxs_power_init_4p2_regulator(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + uint32_t tmp, tmp2; + + setbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_ENABLE_4P2); + + writel(POWER_CHARGE_ENABLE_LOAD, &power_regs->hw_power_charge_set); + + writel(POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK, + &power_regs->hw_power_5vctrl_clr); + clrbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_TRG_MASK); + + /* Power up the 4p2 rail and logic/control */ + charger_4p2_enable(); + + /* + * Start charging up the 4p2 capacitor. We ramp of this charge + * gradually to avoid large inrush current from the 5V cable which can + * cause transients/problems + */ + mxs_enable_4p2_dcdc_input(0); + + if (readl(&power_regs->hw_power_ctrl) & POWER_CTRL_VBUS_VALID_IRQ) { + /* + * If we arrived here, we were unable to recover from mx23 chip + * errata 5837. 4P2 is disabled and sufficient battery power is + * not present. Exiting to not enable DCDC power during 5V + * connected state. + */ + clrbits_le32(&power_regs->hw_power_dcdc4p2, + POWER_DCDC4P2_ENABLE_DCDC); + charger_4p2_disable(); + hang(); + } + + /* + * Here we set the 4p2 brownout level to something very close to 4.2V. + * We then check the brownout status. If the brownout status is false, + * the voltage is already close to the target voltage of 4.2V so we + * can go ahead and set the 4P2 current limit to our max target limit. + * If the brownout status is true, we need to ramp us the current limit + * so that we don't cause large inrush current issues. We step up the + * current limit until the brownout status is false or until we've + * reached our maximum defined 4p2 current limit. + */ + clrsetbits_le32(&power_regs->hw_power_dcdc4p2, + POWER_DCDC4P2_BO_MASK, + 22 << POWER_DCDC4P2_BO_OFFSET); /* 4.15V */ + + if (!(readl(&power_regs->hw_power_sts) & POWER_STS_DCDC_4P2_BO)) { + setbits_le32(&power_regs->hw_power_5vctrl, + 0x3f << POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET); + } else { + tmp = (readl(&power_regs->hw_power_5vctrl) & + POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK) >> + POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET; + while (tmp < 0x3f) { + if (!(readl(&power_regs->hw_power_sts) & + POWER_STS_DCDC_4P2_BO)) { + tmp = readl(&power_regs->hw_power_5vctrl); + tmp |= POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK; + mxs_early_delay(100); + writel(tmp, &power_regs->hw_power_5vctrl); + break; + } else { + tmp++; + tmp2 = readl(&power_regs->hw_power_5vctrl); + tmp2 &= ~POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK; + tmp2 |= tmp << + POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET; + writel(tmp2, &power_regs->hw_power_5vctrl); + mxs_early_delay(100); + } + } + } + + clrbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_BO_MASK); + writel(POWER_CTRL_DCDC4P2_BO_IRQ, &power_regs->hw_power_ctrl_clr); +} + +/** + * mxs_power_init_dcdc_4p2_source() - Switch DC-DC converter to 4P2 source + * + * This function configures the DC-DC converter to be supplied from the 4P2 + * linear regulator. + */ +static void mxs_power_init_dcdc_4p2_source(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + if (!(readl(&power_regs->hw_power_dcdc4p2) & + POWER_DCDC4P2_ENABLE_DCDC)) { + hang(); + } + + mxs_enable_4p2_dcdc_input(1); + + if (readl(&power_regs->hw_power_ctrl) & POWER_CTRL_VBUS_VALID_IRQ) { + clrbits_le32(&power_regs->hw_power_dcdc4p2, + POWER_DCDC4P2_ENABLE_DCDC); + writel(POWER_5VCTRL_ENABLE_DCDC, + &power_regs->hw_power_5vctrl_clr); + charger_4p2_disable(); + } +} + +/** + * mxs_power_enable_4p2() - Power up the 4P2 regulator + * + * This function drives the process of powering up the 4P2 linear regulator + * and switching the DC-DC converter input over to the 4P2 linear regulator. + */ +static void mxs_power_enable_4p2(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + uint32_t vdddctrl, vddactrl, vddioctrl; + uint32_t tmp; + + vdddctrl = readl(&power_regs->hw_power_vdddctrl); + vddactrl = readl(&power_regs->hw_power_vddactrl); + vddioctrl = readl(&power_regs->hw_power_vddioctrl); + + setbits_le32(&power_regs->hw_power_vdddctrl, + POWER_VDDDCTRL_DISABLE_FET | POWER_VDDDCTRL_ENABLE_LINREG | + POWER_VDDDCTRL_PWDN_BRNOUT); + + setbits_le32(&power_regs->hw_power_vddactrl, + POWER_VDDACTRL_DISABLE_FET | POWER_VDDACTRL_ENABLE_LINREG | + POWER_VDDACTRL_PWDN_BRNOUT); + + setbits_le32(&power_regs->hw_power_vddioctrl, + POWER_VDDIOCTRL_DISABLE_FET | POWER_VDDIOCTRL_PWDN_BRNOUT); + + mxs_power_init_4p2_params(); + mxs_power_init_4p2_regulator(); + + /* Shutdown battery (none present) */ + if (!mxs_is_batt_ready()) { + clrbits_le32(&power_regs->hw_power_dcdc4p2, + POWER_DCDC4P2_BO_MASK); + writel(POWER_CTRL_DCDC4P2_BO_IRQ, + &power_regs->hw_power_ctrl_clr); + writel(POWER_CTRL_ENIRQ_DCDC4P2_BO, + &power_regs->hw_power_ctrl_clr); + } + + mxs_power_init_dcdc_4p2_source(); + + writel(vdddctrl, &power_regs->hw_power_vdddctrl); + mxs_early_delay(20); + writel(vddactrl, &power_regs->hw_power_vddactrl); + mxs_early_delay(20); + writel(vddioctrl, &power_regs->hw_power_vddioctrl); + + /* + * Check if FET is enabled on either powerout and if so, + * disable load. + */ + tmp = 0; + tmp |= !(readl(&power_regs->hw_power_vdddctrl) & + POWER_VDDDCTRL_DISABLE_FET); + tmp |= !(readl(&power_regs->hw_power_vddactrl) & + POWER_VDDACTRL_DISABLE_FET); + tmp |= !(readl(&power_regs->hw_power_vddioctrl) & + POWER_VDDIOCTRL_DISABLE_FET); + if (tmp) + writel(POWER_CHARGE_ENABLE_LOAD, + &power_regs->hw_power_charge_clr); +} + +/** + * mxs_boot_valid_5v() - Boot from 5V supply + * + * This function configures the power block to boot from valid 5V input. + * This is called only if the 5V is reliable and can properly supply the + * CPU. This function proceeds to configure the 4P2 converter to be supplied + * from the 5V input. + */ +static void mxs_boot_valid_5v(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + /* + * Use VBUSVALID level instead of VDD5V_GT_VDDIO level to trigger a 5V + * disconnect event. FIXME + */ + writel(POWER_5VCTRL_VBUSVALID_5VDETECT, + &power_regs->hw_power_5vctrl_set); + + /* Configure polarity to check for 5V disconnection. */ + writel(POWER_CTRL_POLARITY_VBUSVALID | + POWER_CTRL_POLARITY_VDD5V_GT_VDDIO, + &power_regs->hw_power_ctrl_clr); + + writel(POWER_CTRL_VBUS_VALID_IRQ | POWER_CTRL_VDD5V_GT_VDDIO_IRQ, + &power_regs->hw_power_ctrl_clr); + + mxs_power_enable_4p2(); +} + +/** + * mxs_powerdown() - Shut down the system + * + * This function powers down the CPU completely. + */ +static void mxs_powerdown(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + writel(POWER_RESET_UNLOCK_KEY, &power_regs->hw_power_reset); + writel(POWER_RESET_UNLOCK_KEY | POWER_RESET_PWD_OFF, + &power_regs->hw_power_reset); +} + +/** + * mxs_enable_battery_input() - Configure the power block to boot from battery input + * + * This function configures the power block to boot from the battery voltage + * supply. + */ +static void mxs_enable_battery_input(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_PWDN_5VBRNOUT); + clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_ENABLE_DCDC); + + clrbits_le32(&power_regs->hw_power_dcdc4p2, + POWER_DCDC4P2_ENABLE_DCDC | POWER_DCDC4P2_ENABLE_4P2); + writel(POWER_CHARGE_ENABLE_LOAD, &power_regs->hw_power_charge_clr); + + /* 5V to battery handoff. */ + setbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); + mxs_early_delay(30); + clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); + + writel(POWER_CTRL_ENIRQ_DCDC4P2_BO, &power_regs->hw_power_ctrl_clr); + + clrsetbits_le32(&power_regs->hw_power_minpwr, + POWER_MINPWR_HALFFETS, POWER_MINPWR_DOUBLE_FETS); + + mxs_power_set_linreg(); + + clrbits_le32(&power_regs->hw_power_vdddctrl, + POWER_VDDDCTRL_DISABLE_FET | POWER_VDDDCTRL_ENABLE_LINREG); + + clrbits_le32(&power_regs->hw_power_vddactrl, + POWER_VDDACTRL_DISABLE_FET | POWER_VDDACTRL_ENABLE_LINREG); + + clrbits_le32(&power_regs->hw_power_vddioctrl, + POWER_VDDIOCTRL_DISABLE_FET); + + charger_4p2_disable(); + + setbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_ENABLE_DCDC); + + clrsetbits_le32(&power_regs->hw_power_5vctrl, + POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK, + 0x8 << POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET); + + mxs_power_enable_4p2(); +} + +/** + * mxs_handle_5v_conflict() - Test if the 5V input is reliable + * + * This function tests if the 5V input can reliably supply the system. If it + * can, then proceed to configuring the system to boot from 5V source, otherwise + * try booting from battery supply. If we can not boot from battery supply + * either, shut down the system. + */ +static void mxs_handle_5v_conflict(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + uint32_t tmp; + + setbits_le32(&power_regs->hw_power_vddioctrl, + POWER_VDDIOCTRL_BO_OFFSET_MASK); + + for (;;) { + tmp = readl(&power_regs->hw_power_sts); + + if (tmp & POWER_STS_VDDIO_BO) { + /* + * VDDIO has a brownout, then the VDD5V_GT_VDDIO becomes + * unreliable + */ + mxs_powerdown(); + break; + } + + if (tmp & POWER_STS_VDD5V_GT_VDDIO) { + mxs_boot_valid_5v(); + break; + } else { + mxs_powerdown(); + break; + } + + if (tmp & POWER_STS_PSWITCH_MASK) { + mxs_enable_battery_input(); + break; + } + } +} + +/** + * mxs_5v_boot() - Configure the power block to boot from 5V input + * + * This function handles configuration of the power block when supplied by + * a 5V input. + */ +static void mxs_5v_boot(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + /* + * NOTE: In original IMX-Bootlets, this also checks for VBUSVALID, + * but their implementation always returns 1 so we omit it here. + */ + if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) { + mxs_boot_valid_5v(); + return; + } + + mxs_early_delay(1000); + if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) { + mxs_boot_valid_5v(); + return; + } + + mxs_handle_5v_conflict(); +} + +/** + * mxs_init_batt_bo() - Configure battery brownout threshold + * + * This function configures the battery input brownout threshold. The value + * at which the battery brownout happens is configured to 3.0V in the code. + */ +static void mxs_init_batt_bo(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + /* Brownout at 3V */ + clrsetbits_le32(&power_regs->hw_power_battmonitor, + POWER_BATTMONITOR_BRWNOUT_LVL_MASK, + 15 << POWER_BATTMONITOR_BRWNOUT_LVL_OFFSET); + + writel(POWER_CTRL_BATT_BO_IRQ, &power_regs->hw_power_ctrl_clr); + writel(POWER_CTRL_ENIRQ_BATT_BO, &power_regs->hw_power_ctrl_clr); +} + +/** + * mxs_switch_vddd_to_dcdc_source() - Switch VDDD rail to DC-DC converter + * + * This function turns off the VDDD linear regulator and therefore makes + * the VDDD rail be supplied only by the DC-DC converter. + */ +static void mxs_switch_vddd_to_dcdc_source(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + clrsetbits_le32(&power_regs->hw_power_vdddctrl, + POWER_VDDDCTRL_LINREG_OFFSET_MASK, + POWER_VDDDCTRL_LINREG_OFFSET_1STEPS_BELOW); + + clrbits_le32(&power_regs->hw_power_vdddctrl, + POWER_VDDDCTRL_DISABLE_FET | POWER_VDDDCTRL_ENABLE_LINREG | + POWER_VDDDCTRL_DISABLE_STEPPING); +} + +/** + * mxs_power_configure_power_source() - Configure power block source + * + * This function is the core of the power configuration logic. The function + * selects the power block input source and configures the whole power block + * accordingly. After the configuration is complete and the system is stable + * again, the function switches the CPU clock source back to PLL. Finally, + * the function switches the voltage rails to DC-DC converter. + */ +static void mxs_power_configure_power_source(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + struct mxs_lradc_regs *lradc_regs = + (struct mxs_lradc_regs *)IMX_LRADC_BASE; + + if (!(readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO)) { + /* 5V not detected, booting from battery. */ + mxs_enable_battery_input(); + return; + } + + if (mxs_is_batt_ready()) { + /* 5V source detected, good battery detected. */ + mxs_enable_battery_input(); + return; + } + + if (!mxs_is_batt_good()) { + /* 5V source detected, bad battery detected. */ + writel(LRADC_CONVERSION_AUTOMATIC, + &lradc_regs->hw_lradc_conversion_clr); + clrbits_le32(&power_regs->hw_power_battmonitor, + POWER_BATTMONITOR_BATT_VAL_MASK); + } + mxs_5v_boot(); +} + +/** + * mxs_enable_output_rail_protection() - Enable power rail protection + * + * This function enables overload protection on the power rails. This is + * triggered if the power rails' voltage drops rapidly due to overload and + * in such case, the supply to the powerrail is cut-off, protecting the + * CPU from damage. Note that under such condition, the system will likely + * crash or misbehave. + */ +static void mxs_enable_output_rail_protection(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + writel(POWER_CTRL_VDDD_BO_IRQ | POWER_CTRL_VDDA_BO_IRQ | + POWER_CTRL_VDDIO_BO_IRQ, &power_regs->hw_power_ctrl_clr); + + setbits_le32(&power_regs->hw_power_vdddctrl, + POWER_VDDDCTRL_PWDN_BRNOUT); + + setbits_le32(&power_regs->hw_power_vddactrl, + POWER_VDDACTRL_PWDN_BRNOUT); + + setbits_le32(&power_regs->hw_power_vddioctrl, + POWER_VDDIOCTRL_PWDN_BRNOUT); +} + +/** + * mxs_get_vddio_power_source_off() - Get VDDIO rail power source + * + * This function tests if the VDDIO rail is supplied by linear regulator + * or by the DC-DC converter. Returns 1 if powered by linear regulator, + * returns 0 if powered by the DC-DC converter. + */ +static int mxs_get_vddio_power_source_off(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + uint32_t tmp; + + if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) { + tmp = readl(&power_regs->hw_power_vddioctrl); + if (tmp & POWER_VDDIOCTRL_DISABLE_FET) { + if ((tmp & POWER_VDDIOCTRL_LINREG_OFFSET_MASK) == + POWER_VDDIOCTRL_LINREG_OFFSET_0STEPS) { + return 1; + } + } + + if (!(readl(&power_regs->hw_power_5vctrl) & + POWER_5VCTRL_ENABLE_DCDC)) { + if ((tmp & POWER_VDDIOCTRL_LINREG_OFFSET_MASK) == + POWER_VDDIOCTRL_LINREG_OFFSET_0STEPS) { + return 1; + } + } + } + + return 0; + +} + +/** + * mxs_get_vddd_power_source_off() - Get VDDD rail power source + * + * This function tests if the VDDD rail is supplied by linear regulator + * or by the DC-DC converter. Returns 1 if powered by linear regulator, + * returns 0 if powered by the DC-DC converter. + */ +static int mxs_get_vddd_power_source_off(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + uint32_t tmp; + + tmp = readl(&power_regs->hw_power_vdddctrl); + if (tmp & POWER_VDDDCTRL_DISABLE_FET) { + if ((tmp & POWER_VDDDCTRL_LINREG_OFFSET_MASK) == + POWER_VDDDCTRL_LINREG_OFFSET_0STEPS) { + return 1; + } + } + + if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) { + if (!(readl(&power_regs->hw_power_5vctrl) & + POWER_5VCTRL_ENABLE_DCDC)) { + return 1; + } + } + + if (!(tmp & POWER_VDDDCTRL_ENABLE_LINREG)) { + if ((tmp & POWER_VDDDCTRL_LINREG_OFFSET_MASK) == + POWER_VDDDCTRL_LINREG_OFFSET_1STEPS_BELOW) { + return 1; + } + } + + return 0; +} + +struct mxs_vddx_cfg { + uint32_t *reg; + uint8_t step_mV; + uint16_t lowest_mV; + int (*powered_by_linreg)(void); + uint32_t trg_mask; + uint32_t bo_irq; + uint32_t bo_enirq; + uint32_t bo_offset_mask; + uint32_t bo_offset_offset; +}; + +static const struct mxs_vddx_cfg mx23_vddio_cfg = { + .reg = &(((struct mxs_power_regs *)IMX_POWER_BASE)-> + hw_power_vddioctrl), + .step_mV = 25, + .lowest_mV = 2800, + .powered_by_linreg = mxs_get_vddio_power_source_off, + .trg_mask = POWER_VDDIOCTRL_TRG_MASK, + .bo_irq = POWER_CTRL_VDDIO_BO_IRQ, + .bo_enirq = POWER_CTRL_ENIRQ_VDDIO_BO, + .bo_offset_mask = POWER_VDDIOCTRL_BO_OFFSET_MASK, + .bo_offset_offset = POWER_VDDIOCTRL_BO_OFFSET_OFFSET, +}; + +static const struct mxs_vddx_cfg mx28_vddio_cfg = { + .reg = &(((struct mxs_power_regs *)IMX_POWER_BASE)-> + hw_power_vddioctrl), + .step_mV = 50, + .lowest_mV = 2800, + .powered_by_linreg = mxs_get_vddio_power_source_off, + .trg_mask = POWER_VDDIOCTRL_TRG_MASK, + .bo_irq = POWER_CTRL_VDDIO_BO_IRQ, + .bo_enirq = POWER_CTRL_ENIRQ_VDDIO_BO, + .bo_offset_mask = POWER_VDDIOCTRL_BO_OFFSET_MASK, + .bo_offset_offset = POWER_VDDIOCTRL_BO_OFFSET_OFFSET, +}; + +static const struct mxs_vddx_cfg mxs_vddd_cfg = { + .reg = &(((struct mxs_power_regs *)IMX_POWER_BASE)-> + hw_power_vdddctrl), + .step_mV = 25, + .lowest_mV = 800, + .powered_by_linreg = mxs_get_vddd_power_source_off, + .trg_mask = POWER_VDDDCTRL_TRG_MASK, + .bo_irq = POWER_CTRL_VDDD_BO_IRQ, + .bo_enirq = POWER_CTRL_ENIRQ_VDDD_BO, + .bo_offset_mask = POWER_VDDDCTRL_BO_OFFSET_MASK, + .bo_offset_offset = POWER_VDDDCTRL_BO_OFFSET_OFFSET, +}; + +static const struct mxs_vddx_cfg mxs_vddmem_cfg = { + .reg = &(((struct mxs_power_regs *)IMX_POWER_BASE)-> + hw_power_vddmemctrl), + .step_mV = 50, + .lowest_mV = 1700, + .powered_by_linreg = NULL, + .trg_mask = POWER_VDDMEMCTRL_TRG_MASK, + .bo_irq = 0, + .bo_enirq = 0, + .bo_offset_mask = 0, + .bo_offset_offset = 0, +}; + +/** + * mxs_power_set_vddx() - Configure voltage on DC-DC converter rail + * @cfg: Configuration data of the DC-DC converter rail + * @new_target: New target voltage of the DC-DC converter rail + * @new_brownout: New brownout trigger voltage + * + * This function configures the output voltage on the DC-DC converter rail. + * The rail is selected by the @cfg argument. The new voltage target is + * selected by the @new_target and the voltage is specified in mV. The + * new brownout value is selected by the @new_brownout argument and the + * value is also in mV. + */ +static void mxs_power_set_vddx(const struct mxs_vddx_cfg *cfg, + uint32_t new_target, uint32_t new_brownout) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + uint32_t cur_target, diff, bo_int = 0; + uint32_t powered_by_linreg = 0; + int adjust_up, tmp; + + new_brownout = DIV_ROUND_CLOSEST(new_target - new_brownout, cfg->step_mV); + + cur_target = readl(cfg->reg); + cur_target &= cfg->trg_mask; + cur_target *= cfg->step_mV; + cur_target += cfg->lowest_mV; + + adjust_up = new_target > cur_target; + if (cfg->powered_by_linreg) + powered_by_linreg = cfg->powered_by_linreg(); + + if (adjust_up && cfg->bo_irq) { + if (powered_by_linreg) { + bo_int = readl(cfg->reg); + clrbits_le32(cfg->reg, cfg->bo_enirq); + } + setbits_le32(cfg->reg, cfg->bo_offset_mask); + } + + do { + if (abs(new_target - cur_target) > 100) { + if (adjust_up) + diff = cur_target + 100; + else + diff = cur_target - 100; + } else { + diff = new_target; + } + + diff -= cfg->lowest_mV; + diff /= cfg->step_mV; + + clrsetbits_le32(cfg->reg, cfg->trg_mask, diff); + + if (powered_by_linreg || + (readl(&power_regs->hw_power_sts) & + POWER_STS_VDD5V_GT_VDDIO)) + mxs_early_delay(500); + else { + for (;;) { + tmp = readl(&power_regs->hw_power_sts); + if (tmp & POWER_STS_DC_OK) + break; + } + } + + cur_target = readl(cfg->reg); + cur_target &= cfg->trg_mask; + cur_target *= cfg->step_mV; + cur_target += cfg->lowest_mV; + } while (new_target > cur_target); + + if (cfg->bo_irq) { + if (adjust_up && powered_by_linreg) { + writel(cfg->bo_irq, &power_regs->hw_power_ctrl_clr); + if (bo_int & cfg->bo_enirq) + setbits_le32(cfg->reg, cfg->bo_enirq); + } + + clrsetbits_le32(cfg->reg, cfg->bo_offset_mask, + new_brownout << cfg->bo_offset_offset); + } +} + +/** + * mxs_setup_batt_detect() - Start the battery voltage measurement logic + * + * This function starts and configures the LRADC block. This allows the + * power initialization code to measure battery voltage and based on this + * knowledge, decide whether to boot at all, boot from battery or boot + * from 5V input. + */ +static void mxs_setup_batt_detect(void) +{ + mxs_lradc_init(); + mxs_lradc_enable_batt_measurement(); + mxs_early_delay(10); +} + +/** + * mx23_ungate_power() - Ungate the POWER block + * + * This function ungates clock to the power block. In case the power block + * was still gated at this point, it will not be possible to configure the + * block and therefore the power initialization would fail. This function + * is only needed on i.MX233, on i.MX28 the power block is always ungated. + */ +static void mx23_ungate_power(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + writel(MX23_POWER_CTRL_CLKGATE, &power_regs->hw_power_ctrl_clr); +} + +/** + * mx23_power_init() - The power block init main function + * + * This function calls all the power block initialization functions in + * proper sequence to start the power block. + */ +static void __mx23_power_init(int has_battery) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + mx23_ungate_power(); + + mxs_power_clock2xtal(); + mxs_power_set_auto_restart(); + mxs_power_set_linreg(); + mxs_power_setup_5v_detect(); + + mxs_setup_batt_detect(); + + mxs_src_power_init(); + + if (has_battery) + mxs_power_configure_power_source(); + else + mxs_enable_battery_input(); + + mxs_power_clock2pll(); + + mxs_init_batt_bo(); + + mxs_switch_vddd_to_dcdc_source(); + + /* Fire up the VDDMEM LinReg now that we're all set. */ + writel(POWER_VDDMEMCTRL_ENABLE_LINREG | POWER_VDDMEMCTRL_ENABLE_ILIMIT, + &power_regs->hw_power_vddmemctrl); + + mxs_enable_output_rail_protection(); + + mxs_power_set_vddx(&mx23_vddio_cfg, 3300, 3150); + mxs_power_set_vddx(&mxs_vddd_cfg, 1500, 1000); + mxs_power_set_vddx(&mxs_vddmem_cfg, 2500, 1700); + + writel(POWER_CTRL_VDDD_BO_IRQ | POWER_CTRL_VDDA_BO_IRQ | + POWER_CTRL_VDDIO_BO_IRQ | POWER_CTRL_VDD5V_DROOP_IRQ | + POWER_CTRL_VBUS_VALID_IRQ | POWER_CTRL_BATT_BO_IRQ | + POWER_CTRL_DCDC4P2_BO_IRQ, &power_regs->hw_power_ctrl_clr); + + writel(POWER_5VCTRL_PWDN_5VBRNOUT, &power_regs->hw_power_5vctrl_set); + + mxs_early_delay(1000); +} + +void mx23_power_init(void) +{ + __mx23_power_init(1); +} + +void mx23_power_init_battery_input(void) +{ + __mx23_power_init(0); +} + +/** + * mx28_power_init() - The power block init main function + * + * This function calls all the power block initialization functions in + * proper sequence to start the power block. + */ +static void __mx28_power_init(int has_battery) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + mxs_power_clock2xtal(); + mxs_power_set_auto_restart(); + mxs_power_set_linreg(); + mxs_power_setup_5v_detect(); + + mxs_setup_batt_detect(); + + mxs_src_power_init(); + + if (has_battery) + mxs_power_configure_power_source(); + else + mxs_enable_battery_input(); + + mxs_power_clock2pll(); + + mxs_init_batt_bo(); + + mxs_switch_vddd_to_dcdc_source(); + + mxs_enable_output_rail_protection(); + + mxs_power_set_vddx(&mx28_vddio_cfg, 3300, 3150); + mxs_power_set_vddx(&mxs_vddd_cfg, 1500, 1000); + + writel(POWER_CTRL_VDDD_BO_IRQ | POWER_CTRL_VDDA_BO_IRQ | + POWER_CTRL_VDDIO_BO_IRQ | POWER_CTRL_VDD5V_DROOP_IRQ | + POWER_CTRL_VBUS_VALID_IRQ | POWER_CTRL_BATT_BO_IRQ | + POWER_CTRL_DCDC4P2_BO_IRQ, &power_regs->hw_power_ctrl_clr); + + writel(POWER_5VCTRL_PWDN_5VBRNOUT, &power_regs->hw_power_5vctrl_set); + + mxs_early_delay(1000); +} + +void mx28_power_init(void) +{ + __mx28_power_init(1); +} + +void mx28_power_init_battery_input(void) +{ + __mx28_power_init(0); +} + +/** + * mxs_power_wait_pswitch() - Wait for power switch to be pressed + * + * This function waits until the power-switch was pressed to start booting + * the board. + */ +void mxs_power_wait_pswitch(void) +{ + struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)IMX_POWER_BASE; + + while (!(readl(&power_regs->hw_power_sts) & POWER_STS_PSWITCH_MASK)) + ; +} diff --git a/arch/arm/mach-mxs/soc-imx23.c b/arch/arm/mach-mxs/soc-imx23.c index 825ea20..b219865 100644 --- a/arch/arm/mach-mxs/soc-imx23.c +++ b/arch/arm/mach-mxs/soc-imx23.c @@ -16,7 +16,7 @@ #include #include -#include +#include #include #define HW_CLKCTRL_RESET 0x120 diff --git a/arch/arm/mach-mxs/soc-imx28.c b/arch/arm/mach-mxs/soc-imx28.c index 01bc20a..c7252f5 100644 --- a/arch/arm/mach-mxs/soc-imx28.c +++ b/arch/arm/mach-mxs/soc-imx28.c @@ -16,7 +16,7 @@ #include #include -#include +#include #include #define HW_CLKCTRL_RESET 0x1e0 diff --git a/arch/arm/mach-mxs/usb-imx23.c b/arch/arm/mach-mxs/usb-imx23.c index 8bed11d..e626396 100644 --- a/arch/arm/mach-mxs/usb-imx23.c +++ b/arch/arm/mach-mxs/usb-imx23.c @@ -15,7 +15,7 @@ */ #include #include -#include +#include #include #define USBPHY_PWD (IMX_USBPHY_BASE + 0x0) diff --git a/arch/arm/mach-mxs/usb-imx28.c b/arch/arm/mach-mxs/usb-imx28.c index 1c982a0..a87d4f6 100644 --- a/arch/arm/mach-mxs/usb-imx28.c +++ b/arch/arm/mach-mxs/usb-imx28.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #define POWER_CTRL (IMX_POWER_BASE + 0x0) #define POWER_CTRL_CLKGATE 0x40000000 diff --git a/common/Kconfig b/common/Kconfig index 4614965..00e4f36 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -882,6 +882,17 @@ bool "Trace initcalls" help If enabled this will print initcall traces. + +config PBL_CONSOLE + depends on DEBUG_LL + bool "Enable console support in PBL" + help + This enables printf/pr_* support in the PBL to get more + informational output earlier during startup. Note that + printf/pr_* need a valid C environment, so the binary + must be running at the address it's linked at and bss must + be cleared. On ARM that would be after setup_c(). + endmenu config HAS_DEBUG_LL diff --git a/common/console_common.c b/common/console_common.c index cc25f97..df1b085 100644 --- a/common/console_common.c +++ b/common/console_common.c @@ -101,7 +101,7 @@ { struct log_entry *log; - if (IS_ENABLED(CONFIG_LOGBUF)) { + if (IS_ENABLED(CONFIG_LOGBUF) && mem_malloc_is_initialized()) { if (barebox_log_max_messages > 0) log_clean(barebox_log_max_messages - 1); diff --git a/common/memory.c b/common/memory.c index 57c73ab..4725f6e 100644 --- a/common/memory.c +++ b/common/memory.c @@ -49,6 +49,13 @@ tlsf_pool tlsf_mem_pool; #endif +int mem_malloc_initialized; + +int mem_malloc_is_initialized(void) +{ + return mem_malloc_initialized; +} + void mem_malloc_init(void *start, void *end) { malloc_start = (unsigned long)start; @@ -57,6 +64,7 @@ #ifdef CONFIG_MALLOC_TLSF tlsf_mem_pool = tlsf_create(start, end - start + 1); #endif + mem_malloc_initialized = 1; } #if !defined __SANDBOX__ && !defined CONFIG_ARCH_EFI diff --git a/common/misc.c b/common/misc.c index 65f3306..6da71c7 100644 --- a/common/misc.c +++ b/common/misc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include int errno; @@ -188,3 +189,24 @@ BAREBOX_MAGICVAR_NAMED(global_hostname, global.hostname, "shortname of the board. Also used as hostname for DHCP requests"); + +void __noreturn panic(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + putchar('\n'); + va_end(args); + + dump_stack(); + + led_trigger(LED_TRIGGER_PANIC, TRIGGER_ENABLE); + + if (IS_ENABLED(CONFIG_PANIC_HANG)) { + hang(); + } else { + udelay(100000); /* allow messages to go out */ + reset_cpu(0); + } +} +EXPORT_SYMBOL(panic); diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index 1f84e9f..8bf27c1 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "clk.h" diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index aa2924c..77a13bc 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "clk.h" diff --git a/drivers/mci/mxs.c b/drivers/mci/mxs.c index d6565ca..367964c 100644 --- a/drivers/mci/mxs.c +++ b/drivers/mci/mxs.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/serial/serial_auart.c b/drivers/serial/serial_auart.c index ae6c5b8..87b2e33 100644 --- a/drivers/serial/serial_auart.c +++ b/drivers/serial/serial_auart.c @@ -47,7 +47,6 @@ #include #include -#include #define HW_UARTAPP_CTRL0 (0x00000000) diff --git a/drivers/serial/stm-serial.c b/drivers/serial/stm-serial.c index 39ff7ae..e5f57dc 100644 --- a/drivers/serial/stm-serial.c +++ b/drivers/serial/stm-serial.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #define UARTDBGDR 0x00 diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index 9a35e09..8932103 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/drivers/video/stm.c b/drivers/video/stm.c index 175e4b6..3c90c0d 100644 --- a/drivers/video/stm.c +++ b/drivers/video/stm.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #define HW_LCDIF_CTRL 0x00 diff --git a/images/.gitignore b/images/.gitignore index d27e71a..c5377d9 100644 --- a/images/.gitignore +++ b/images/.gitignore @@ -16,6 +16,8 @@ *.t124img.cfg *.mlo *.mlospi +*.mxsbs +*.mxssd pbl.lds barebox.x barebox.z diff --git a/images/Makefile b/images/Makefile index c55cbdc..7c3aaf7 100644 --- a/images/Makefile +++ b/images/Makefile @@ -103,6 +103,7 @@ include $(srctree)/images/Makefile.rockchip include $(srctree)/images/Makefile.socfpga include $(srctree)/images/Makefile.tegra +include $(srctree)/images/Makefile.mxs targets += $(image-y) pbl.lds barebox.x barebox.z targets += $(patsubst %,%.pblx,$(pblx-y)) @@ -119,5 +120,5 @@ clean-files := *.pbl *.pblb *.pblx *.map start_*.imximg *.img barebox.z start_*.kwbimg \ start_*.kwbuartimg *.socfpgaimg *.mlo *.t20img *.t20img.cfg *.t30img \ - *.t30img.cfg *.t124img *.t124img.cfg *.mlospi *.mlo + *.t30img.cfg *.t124img *.t124img.cfg *.mlospi *.mlo *.mxsbs *.mxssd clean-files += pbl.lds diff --git a/images/Makefile.mxs b/images/Makefile.mxs new file mode 100644 index 0000000..abff255 --- /dev/null +++ b/images/Makefile.mxs @@ -0,0 +1,43 @@ +# +# barebox image generation Makefile for MXS images +# + +# %.mxsbs - convert into MXS BootStream image +# ---------------------------------------------------------------- +quiet_cmd_mxs_bootstream = MXS-BOOTSTREAM $@ + cmd_mxs_bootstream = $(objtree)/scripts/mxsimage -c $(CFG_$(@F)) -b $< -o $@ -p $(word 2,$^) + +$(obj)/%.mxsbs: $(obj)/%.pblx $(obj)/prep_%.pblb FORCE + $(call if_changed,mxs_bootstream) + +# %.mxssd - convert into MXS SD card image +# ---------------------------------------------------------------- +quiet_cmd_mxs_sd = MXS-SD $@ + cmd_mxs_sd = $(objtree)/scripts/mxsboot sd $< $@ + +$(obj)/%.mxssd: $(obj)/% + $(call if_changed,mxs_sd) + +board = $(srctree)/arch/$(ARCH)/boards +mxs23cfg = $(srctree)/arch/arm/mach-mxs/mxs23img.cfg +mxs28cfg = $(srctree)/arch/arm/mach-mxs/mxs28img.cfg + +pblx-$(CONFIG_MACH_TX28) += start_barebox_karo_tx28 prep_start_barebox_karo_tx28 +PREP_start_barebox_karo_tx28.pblx.mxsbs = start_barebox_karo_tx28_prep +CFG_start_barebox_karo_tx28.mxsbs = $(mxs28cfg) +FILE_barebox-karo-tx28-bootstream.img = start_barebox_karo_tx28.mxsbs +image-$(CONFIG_MACH_TX28) += barebox-karo-tx28-bootstream.img +FILE_barebox-karo-tx28-sd.img = start_barebox_karo_tx28.mxsbs.mxssd +image-$(CONFIG_MACH_TX28) += barebox-karo-tx28-sd.img +FILE_barebox-karo-tx28-2nd.img = start_barebox_karo_tx28.pblx +image-$(CONFIG_MACH_TX28) += barebox-karo-tx28-2nd.img + +pblx-$(CONFIG_MACH_IMX233_OLINUXINO) += start_barebox_olinuxino_imx23 prep_start_barebox_olinuxino_imx23 +PREP_start_barebox_olinuxino_imx23.pblx.mxsbs = start_barebox_olinuxino_imx23_prep; +CFG_start_barebox_olinuxino_imx23.mxsbs = $(mxs23cfg) +FILE_barebox-olinuxino-imx23-bootstream.img = start_barebox_olinuxino_imx23.mxsbs +image-$(CONFIG_MACH_IMX233_OLINUXINO) += barebox-olinuxino-imx23-bootstream.img +FILE_barebox-olinuxino-imx23-sd.img = start_barebox_olinuxino_imx23.mxsbs.mxssd +image-$(CONFIG_MACH_IMX233_OLINUXINO) += barebox-olinuxino-imx23-sd.img +FILE_barebox-olinuxino-imx23-2nd.img = start_barebox_olinuxino_imx23.pblx +image-$(CONFIG_MACH_IMX233_OLINUXINO) += barebox-olinuxino-imx23-2nd.img diff --git a/include/malloc.h b/include/malloc.h index a36f3c0..0d3c9e9 100644 --- a/include/malloc.h +++ b/include/malloc.h @@ -11,4 +11,6 @@ void malloc_stats(void); void *sbrk(ptrdiff_t increment); +int mem_malloc_is_initialized(void); + #endif /* __MALLOC_H */ diff --git a/include/printk.h b/include/printk.h index 22c6c73..a27ad51 100644 --- a/include/printk.h +++ b/include/printk.h @@ -22,18 +22,23 @@ /* debugging and troubleshooting/diagnostic helpers. */ #ifndef CONFIG_CONSOLE_NONE -int pr_print(int level, const char *format, ...) - __attribute__ ((format(__printf__, 2, 3))); - int dev_printf(int level, const struct device_d *dev, const char *format, ...) __attribute__ ((format(__printf__, 3, 4))); #else -static inline int pr_print(int level, const char *format, ...) +static inline int dev_printf(int level, const struct device_d *dev, const char *format, ...) { return 0; } +#endif -static inline int dev_printf(int level, const struct device_d *dev, const char *format, ...) +#if (!defined(__PBL__) && !defined(CONFIG_CONSOLE_NONE)) || \ + (defined(__PBL__) && defined(CONFIG_PBL_CONSOLE)) +int pr_print(int level, const char *format, ...) + __attribute__ ((format(__printf__, 2, 3))); +#else +static int pr_print(int level, const char *format, ...) + __attribute__ ((format(__printf__, 2, 3))); +static inline int pr_print(int level, const char *format, ...) { return 0; } diff --git a/include/stdio.h b/include/stdio.h index 71dbae3..f190911 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -29,8 +29,6 @@ int console_puts(unsigned int ch, const char *s); void console_flush(void); - -int printf(const char *fmt, ...) __attribute__ ((format(__printf__, 1, 2))); int vprintf(const char *fmt, va_list args); #else static inline int tstc(void) @@ -52,13 +50,6 @@ static inline void console_flush(void) {} -static int printf(const char *fmt, ...) __attribute__ ((format(__printf__, 1, 2))); -static inline int printf(const char *fmt, ...) -{ - return 0; -} - - static inline int vprintf(const char *fmt, va_list args) { return 0; @@ -74,6 +65,17 @@ #endif +#if (!defined(__PBL__) && !defined(CONFIG_CONSOLE_NONE)) || \ + (defined(__PBL__) && defined(CONFIG_PBL_CONSOLE)) +int printf(const char *fmt, ...) __attribute__ ((format(__printf__, 1, 2))); +#else +static int printf(const char *fmt, ...) __attribute__ ((format(__printf__, 1, 2))); +static inline int printf(const char *fmt, ...) +{ + return 0; +} +#endif + static inline int puts(const char *s) { return console_puts(CONSOLE_STDOUT, s); diff --git a/lib/Makefile b/lib/Makefile index a6c7dfb..226570a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -4,8 +4,11 @@ obj-y += rbtree.o obj-y += display_options.o obj-y += string.o +obj-y += strtox.o obj-y += vsprintf.o +pbl-$(CONFIG_PBL_CONSOLE) += vsprintf.o obj-y += div64.o +pbl-y += div64.o obj-y += misc.o obj-$(CONFIG_PARAMETER) += parameter.o obj-y += xfuncs.o diff --git a/lib/strtox.c b/lib/strtox.c new file mode 100644 index 0000000..882865b --- /dev/null +++ b/lib/strtox.c @@ -0,0 +1,68 @@ +#include +#include + +unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base) +{ + unsigned long result = 0,value; + + if (*cp == '0') { + cp++; + if ((*cp == 'x') && isxdigit(cp[1])) { + base = 16; + cp++; + } + if (!base) { + base = 8; + } + } + if (!base) { + base = 10; + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} +EXPORT_SYMBOL(simple_strtoul); + +long simple_strtol(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoul(cp+1,endp,base); + return simple_strtoul(cp,endp,base); +} +EXPORT_SYMBOL(simple_strtol); + +unsigned long long simple_strtoull (const char *cp, char **endp, unsigned int base) +{ + unsigned long long result = 0, value; + + if (*cp == '0') { + cp++; + if ((*cp == 'x') && isxdigit (cp[1])) { + base = 16; + cp++; + } + if (!base) { + base = 8; + } + } + if (!base) { + base = 10; + } + while (isxdigit (*cp) && (value = isdigit (*cp) + ? *cp - '0' + : (islower (*cp) ? toupper (*cp) : *cp) - 'A' + 10) < base) { + result = result * base + value; + cp++; + } + if (endp) + *endp = (char *) cp; + return result; +} +EXPORT_SYMBOL(simple_strtoull); + diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 512c882..800ded7 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -18,75 +18,11 @@ #include #include -#include - -unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base) -{ - unsigned long result = 0,value; - - if (*cp == '0') { - cp++; - if ((*cp == 'x') && isxdigit(cp[1])) { - base = 16; - cp++; - } - if (!base) { - base = 8; - } - } - if (!base) { - base = 10; - } - while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) - ? toupper(*cp) : *cp)-'A'+10) < base) { - result = result*base + value; - cp++; - } - if (endp) - *endp = (char *)cp; - return result; -} -EXPORT_SYMBOL(simple_strtoul); - -long simple_strtol(const char *cp,char **endp,unsigned int base) -{ - if(*cp=='-') - return -simple_strtoul(cp+1,endp,base); - return simple_strtoul(cp,endp,base); -} -EXPORT_SYMBOL(simple_strtol); - -unsigned long long simple_strtoull (const char *cp, char **endp, unsigned int base) -{ - unsigned long long result = 0, value; - - if (*cp == '0') { - cp++; - if ((*cp == 'x') && isxdigit (cp[1])) { - base = 16; - cp++; - } - if (!base) { - base = 8; - } - } - if (!base) { - base = 10; - } - while (isxdigit (*cp) && (value = isdigit (*cp) - ? *cp - '0' - : (islower (*cp) ? toupper (*cp) : *cp) - 'A' + 10) < base) { - result = result * base + value; - cp++; - } - if (endp) - *endp = (char *) cp; - return result; -} -EXPORT_SYMBOL(simple_strtoull); /* we use this so that we can do without the ctype library */ #define is_digit(c) ((c) >= '0' && (c) <= '9') +#define is_alpha(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) +#define is_alnum(c) (is_digit(c) || is_alpha(c)) static int skip_atoi(const char **s) { @@ -239,6 +175,7 @@ return buf; } +#ifndef __PBL__ static char *symbol_string(char *buf, char *end, void *ptr, int field_width, int precision, int flags) { unsigned long value = (unsigned long) ptr; @@ -341,6 +278,17 @@ } return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags); } +#else +static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags) +{ + flags |= SMALL; + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags); +} +#endif /** * vsnprintf - Format a string and place it in a buffer @@ -417,7 +365,7 @@ /* get field width */ field_width = -1; - if (isdigit(*fmt)) + if (is_digit(*fmt)) field_width = skip_atoi(&fmt); else if (*fmt == '*') { ++fmt; @@ -433,7 +381,7 @@ precision = -1; if (*fmt == '.') { ++fmt; - if (isdigit(*fmt)) + if (is_digit(*fmt)) precision = skip_atoi(&fmt); else if (*fmt == '*') { ++fmt; @@ -488,7 +436,7 @@ va_arg(args, void *), field_width, precision, flags); /* Skip all alphanumeric pointer suffixes */ - while (isalnum(fmt[1])) + while (is_alnum(fmt[1])) fmt++; continue; @@ -680,24 +628,3 @@ return p; } EXPORT_SYMBOL(asprintf); - -void __noreturn panic(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - vprintf(fmt, args); - putchar('\n'); - va_end(args); - - dump_stack(); - - led_trigger(LED_TRIGGER_PANIC, TRIGGER_ENABLE); - - if (IS_ENABLED(CONFIG_PANIC_HANG)) { - hang(); - } else { - udelay(100000); /* allow messages to go out */ - reset_cpu(0); - } -} -EXPORT_SYMBOL(panic); diff --git a/pbl/Makefile b/pbl/Makefile index a2d7468..c5a08c1 100644 --- a/pbl/Makefile +++ b/pbl/Makefile @@ -4,3 +4,4 @@ pbl-y += misc.o pbl-y += string.o pbl-y += decomp.o +pbl-$(CONFIG_PBL_CONSOLE) += console.o diff --git a/pbl/console.c b/pbl/console.c new file mode 100644 index 0000000..3875e2a --- /dev/null +++ b/pbl/console.c @@ -0,0 +1,32 @@ +#include +#include + +int printf(const char *fmt, ...) +{ + va_list args; + uint i; + char printbuffer[CFG_PBSIZE]; + + va_start(args, fmt); + i = vsprintf(printbuffer, fmt, args); + va_end(args); + + puts_ll(printbuffer); + + return i; +} + +int pr_print(int level, const char *fmt, ...) +{ + va_list args; + uint i; + char printbuffer[CFG_PBSIZE]; + + va_start(args, fmt); + i = vsprintf(printbuffer, fmt, args); + va_end(args); + + puts_ll(printbuffer); + + return i; +} diff --git a/pbl/string.c b/pbl/string.c index b773f5c..927c92d 100644 --- a/pbl/string.c +++ b/pbl/string.c @@ -119,3 +119,17 @@ *xs++ = c; return s; } + +/** + * strnlen - Find the length of a length-limited string + * @s: The string to be sized + * @count: The maximum number of bytes to search + */ +size_t strnlen(const char * s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} diff --git a/scripts/.gitignore b/scripts/.gitignore index b574b22..6203589 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -22,3 +22,5 @@ bareboxstate bareboxstate-target mk-am35xx-spi-image +mxsimage +mxsboot diff --git a/scripts/Makefile b/scripts/Makefile index 5483a64..74c2213 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -19,6 +19,11 @@ hostprogs-$(CONFIG_ARCH_DAVINCI) += mkublheader hostprogs-$(CONFIG_ARCH_ZYNQ) += zynq_mkimage hostprogs-$(CONFIG_ARCH_SOCFPGA) += socfpga_mkimage +hostprogs-$(CONFIG_ARCH_MXS) += mxsimage mxsboot +HOSTLOADLIBES_mxsimage = `pkg-config --libs openssl` +HOSTCFLAGS_mxs-usb-loader.o = `pkg-config --cflags libusb-1.0` +HOSTLOADLIBES_mxs-usb-loader = `pkg-config --libs libusb-1.0` +hostprogs-$(CONFIG_ARCH_MXS_USBLOADER) += mxs-usb-loader subdir-y += mod subdir-$(CONFIG_OMAP4_USBBOOT) += omap4_usbboot diff --git a/scripts/mxs-usb-loader.c b/scripts/mxs-usb-loader.c new file mode 100644 index 0000000..8529274 --- /dev/null +++ b/scripts/mxs-usb-loader.c @@ -0,0 +1,236 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include +#include + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +void put32le(uint8_t * buf, uint32_t i) +{ + *buf++ = i & 0xff; + *buf++ = (i >> 8) & 0xff; + *buf++ = (i >> 16) & 0xff; + *buf++ = (i >> 24) & 0xff; +} + +void put32be(uint8_t * buf, uint32_t i) +{ + *buf++ = (i >> 24) & 0xff; + *buf++ = (i >> 16) & 0xff; + *buf++ = (i >> 8) & 0xff; + *buf++ = i & 0xff; +} + +enum dev_type_t { + HID_DEVICE, + RECOVERY_DEVICE, +}; + +struct dev_info_t { + uint16_t vendor_id; + uint16_t product_id; + unsigned xfer_size; + enum dev_type_t dev_type; +}; + +struct dev_info_t g_dev_info[] = { + {0x066f, 0x3780, 1024, HID_DEVICE}, /* i.MX233 / STMP3780 */ + {0x066f, 0x3770, 48, HID_DEVICE}, /* STMP3770 */ + {0x15A2, 0x004F, 1024, HID_DEVICE}, /* i.MX28 */ + {0x066f, 0x3600, 4096, RECOVERY_DEVICE}, /* STMP36xx */ +}; + +int send_hid(libusb_device_handle * dev, int xfer_size, uint8_t * data, + int size, int nr_xfers) +{ + int i; + + libusb_detach_kernel_driver(dev, 0); + libusb_detach_kernel_driver(dev, 4); + + libusb_claim_interface(dev, 0); + libusb_claim_interface(dev, 4); + + uint8_t *xfer_buf = malloc(1 + xfer_size); + uint8_t *p = xfer_buf; + + *p++ = 0x01; /* Report id */ + + /* Command block wrapper */ + *p++ = 'B'; /* Signature */ + *p++ = 'L'; + *p++ = 'T'; + *p++ = 'C'; + put32le(p, 0x1); /* Tag */ + p += 4; + put32le(p, size); /* Payload size */ + p += 4; + *p++ = 0; /* Flags (host to device) */ + p += 2; /* Reserved */ + + /* Command descriptor block */ + *p++ = 0x02; /* Firmware download */ + put32be(p, size); /* Download size */ + + int ret = libusb_control_transfer(dev, + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, 0x9, + 0x201, 0, + xfer_buf, xfer_size + 1, 1000); + if (ret < 0) { + printf("transfer error at init step\n"); + return 1; + } + + for (i = 0; i < nr_xfers; i++) { + xfer_buf[0] = 0x2; + memcpy(&xfer_buf[1], &data[i * xfer_size], xfer_size); + + ret = libusb_control_transfer(dev, + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, 0x9, + 0x202, 0, xfer_buf, xfer_size + 1, + 1000); + if (ret < 0) { + printf("transfer error at send step %d\n", i); + return 1; + } + } + + int recv_size; + ret = + libusb_interrupt_transfer(dev, 0x81, xfer_buf, xfer_size, + &recv_size, 1000); + if (ret < 0) { + printf("transfer error at final stage\n"); + return 1; + } + + return ret; +} + +int send_recovery(libusb_device_handle * dev, int xfer_size, uint8_t * data, + int size, int nr_xfers) +{ + (void)nr_xfers; + // there should be no kernel driver attached but in doubt... + libusb_detach_kernel_driver(dev, 0); + libusb_claim_interface(dev, 0); + + int sent = 0; + while (sent < size) { + int xfered; + int len = MIN(size - sent, xfer_size); + int ret = + libusb_bulk_transfer(dev, 1, data + sent, len, &xfered, + 1000); + if (ret < 0) { + printf("transfer error at send offset %d\n", sent); + return 1; + } + if (xfered == 0) { + printf("empty transfer at step offset %d\n", sent); + return 2; + } + sent += xfered; + } + return 0; +} + +int main(int argc, char **argv) +{ + if (argc != 3) { + printf("usage: %s \n", argv[0]); + printf + ("If is set to zero, the preferred one is used.\n"); + return 1; + } + + char *end; + int xfer_size = strtol(argv[1], &end, 0); + if (end != (argv[1] + strlen(argv[1]))) { + printf("Invalid transfer size !\n"); + return 1; + } + + libusb_device_handle *dev; + + libusb_init(NULL); + + libusb_set_debug(NULL, 3); + + unsigned i; + for (i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) { + dev = libusb_open_device_with_vid_pid(NULL, + g_dev_info[i].vendor_id, + g_dev_info[i].product_id); + if (dev == NULL) + continue; + if (xfer_size == 0) + xfer_size = g_dev_info[i].xfer_size; + printf("Found a match for %04x:%04x\n", + g_dev_info[i].vendor_id, g_dev_info[i].product_id); + break; + } + if (dev == NULL) { + printf("Cannot open device\n"); + return 1; + } + + FILE *f = fopen(argv[2], "r"); + if (f == NULL) { + perror("cannot open file"); + return 1; + } + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + printf("Transfer size: %d\n", xfer_size); + int nr_xfers = (size + xfer_size - 1) / xfer_size; + uint8_t *file_buf = malloc(nr_xfers * xfer_size); + memset(file_buf, 0xff, nr_xfers * xfer_size); // pad with 0xff + if (fread(file_buf, size, 1, f) != 1) { + perror("read error"); + fclose(f); + return 1; + } + fclose(f); + + switch (g_dev_info[i].dev_type) { + case HID_DEVICE: + send_hid(dev, xfer_size, file_buf, size, nr_xfers); + break; + case RECOVERY_DEVICE: + send_recovery(dev, xfer_size, file_buf, size, nr_xfers); + break; + default: + printf("unknown device type\n"); + break; + } + + return 0; +} diff --git a/scripts/mxsboot.c b/scripts/mxsboot.c new file mode 100644 index 0000000..2b90b25 --- /dev/null +++ b/scripts/mxsboot.c @@ -0,0 +1,641 @@ +/* + * Freescale i.MX28 image generator + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include "compiler.h" + +/* + * Default BCB layout. + * + * TWEAK this if you have blown any OCOTP fuses. + */ +#define STRIDE_PAGES 64 +#define STRIDE_COUNT 4 + +/* + * Layout for 256Mb big NAND with 2048b page size, 64b OOB size and + * 128kb erase size. + * + * TWEAK this if you have different kind of NAND chip. + */ +static uint32_t nand_writesize = 2048; +static uint32_t nand_oobsize = 64; +static uint32_t nand_erasesize = 128 * 1024; + +/* + * Sector on which the SigmaTel boot partition (0x53) starts. + */ +static uint32_t sd_sector = 2048; + +/* + * Each of the U-Boot bootstreams is at maximum 1MB big. + * + * TWEAK this if, for some wild reason, you need to boot bigger image. + */ +#define MAX_BOOTSTREAM_SIZE (1 * 1024 * 1024) + +/* i.MX28 NAND controller-specific constants. DO NOT TWEAK! */ +#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 +#define MXS_NAND_METADATA_SIZE 10 +#define MXS_NAND_COMMAND_BUFFER_SIZE 32 + +struct mx28_nand_fcb { + uint32_t checksum; + uint32_t fingerprint; + uint32_t version; + struct { + uint8_t data_setup; + uint8_t data_hold; + uint8_t address_setup; + uint8_t dsample_time; + uint8_t nand_timing_state; + uint8_t rea; + uint8_t rloh; + uint8_t rhoh; + } timing; + uint32_t page_data_size; + uint32_t total_page_size; + uint32_t sectors_per_block; + uint32_t number_of_nands; /* Ignored */ + uint32_t total_internal_die; /* Ignored */ + uint32_t cell_type; /* Ignored */ + uint32_t ecc_block_n_ecc_type; + uint32_t ecc_block_0_size; + uint32_t ecc_block_n_size; + uint32_t ecc_block_0_ecc_type; + uint32_t metadata_bytes; + uint32_t num_ecc_blocks_per_page; + uint32_t ecc_block_n_ecc_level_sdk; /* Ignored */ + uint32_t ecc_block_0_size_sdk; /* Ignored */ + uint32_t ecc_block_n_size_sdk; /* Ignored */ + uint32_t ecc_block_0_ecc_level_sdk; /* Ignored */ + uint32_t num_ecc_blocks_per_page_sdk; /* Ignored */ + uint32_t metadata_bytes_sdk; /* Ignored */ + uint32_t erase_threshold; + uint32_t boot_patch; + uint32_t patch_sectors; + uint32_t firmware1_starting_sector; + uint32_t firmware2_starting_sector; + uint32_t sectors_in_firmware1; + uint32_t sectors_in_firmware2; + uint32_t dbbt_search_area_start_address; + uint32_t badblock_marker_byte; + uint32_t badblock_marker_start_bit; + uint32_t bb_marker_physical_offset; +}; + +struct mx28_nand_dbbt { + uint32_t checksum; + uint32_t fingerprint; + uint32_t version; + uint32_t number_bb; + uint32_t number_2k_pages_bb; +}; + +struct mx28_nand_bbt { + uint32_t nand; + uint32_t number_bb; + uint32_t badblock[510]; +}; + +struct mx28_sd_drive_info { + uint32_t chip_num; + uint32_t drive_type; + uint32_t tag; + uint32_t first_sector_number; + uint32_t sector_count; +}; + +struct mx28_sd_config_block { + uint32_t signature; + uint32_t primary_boot_tag; + uint32_t secondary_boot_tag; + uint32_t num_copies; + struct mx28_sd_drive_info drv_info[1]; +}; + +static inline uint32_t mx28_nand_ecc_size_in_bits(uint32_t ecc_strength) +{ + return ecc_strength * 13; +} + +static inline uint32_t mx28_nand_get_ecc_strength(uint32_t page_data_size, + uint32_t page_oob_size) +{ + if (page_data_size == 2048) + return 8; + + if (page_data_size == 4096) { + if (page_oob_size == 128) + return 8; + + if (page_oob_size == 218) + return 16; + } + + return 0; +} + +static inline uint32_t mx28_nand_get_mark_offset(uint32_t page_data_size, + uint32_t ecc_strength) +{ + uint32_t chunk_data_size_in_bits; + uint32_t chunk_ecc_size_in_bits; + uint32_t chunk_total_size_in_bits; + uint32_t block_mark_chunk_number; + uint32_t block_mark_chunk_bit_offset; + uint32_t block_mark_bit_offset; + + chunk_data_size_in_bits = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 8; + chunk_ecc_size_in_bits = mx28_nand_ecc_size_in_bits(ecc_strength); + + chunk_total_size_in_bits = + chunk_data_size_in_bits + chunk_ecc_size_in_bits; + + /* Compute the bit offset of the block mark within the physical page. */ + block_mark_bit_offset = page_data_size * 8; + + /* Subtract the metadata bits. */ + block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; + + /* + * Compute the chunk number (starting at zero) in which the block mark + * appears. + */ + block_mark_chunk_number = + block_mark_bit_offset / chunk_total_size_in_bits; + + /* + * Compute the bit offset of the block mark within its chunk, and + * validate it. + */ + block_mark_chunk_bit_offset = block_mark_bit_offset - + (block_mark_chunk_number * chunk_total_size_in_bits); + + if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) + return 1; + + /* + * Now that we know the chunk number in which the block mark appears, + * we can subtract all the ECC bits that appear before it. + */ + block_mark_bit_offset -= + block_mark_chunk_number * chunk_ecc_size_in_bits; + + return block_mark_bit_offset; +} + +static inline uint32_t mx28_nand_mark_byte_offset(void) +{ + uint32_t ecc_strength; + ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize); + return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) >> 3; +} + +static inline uint32_t mx28_nand_mark_bit_offset(void) +{ + uint32_t ecc_strength; + ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize); + return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) & 0x7; +} + +static uint32_t mx28_nand_block_csum(uint8_t *block, uint32_t size) +{ + uint32_t csum = 0; + int i; + + for (i = 0; i < size; i++) + csum += block[i]; + + return csum ^ 0xffffffff; +} + +static struct mx28_nand_fcb *mx28_nand_get_fcb(uint32_t size) +{ + struct mx28_nand_fcb *fcb; + uint32_t bcb_size_bytes; + uint32_t stride_size_bytes; + uint32_t bootstream_size_pages; + uint32_t fw1_start_page; + uint32_t fw2_start_page; + + fcb = malloc(nand_writesize); + if (!fcb) { + printf("MX28 NAND: Unable to allocate FCB\n"); + return NULL; + } + + memset(fcb, 0, nand_writesize); + + fcb->fingerprint = 0x20424346; + fcb->version = 0x01000000; + + /* + * FIXME: These here are default values as found in kobs-ng. We should + * probably retrieve the data from NAND or something. + */ + fcb->timing.data_setup = 80; + fcb->timing.data_hold = 60; + fcb->timing.address_setup = 25; + fcb->timing.dsample_time = 6; + + fcb->page_data_size = nand_writesize; + fcb->total_page_size = nand_writesize + nand_oobsize; + fcb->sectors_per_block = nand_erasesize / nand_writesize; + + fcb->num_ecc_blocks_per_page = (nand_writesize / 512) - 1; + fcb->ecc_block_0_size = 512; + fcb->ecc_block_n_size = 512; + fcb->metadata_bytes = 10; + + if (nand_writesize == 2048) { + fcb->ecc_block_n_ecc_type = 4; + fcb->ecc_block_0_ecc_type = 4; + } else if (nand_writesize == 4096) { + if (nand_oobsize == 128) { + fcb->ecc_block_n_ecc_type = 4; + fcb->ecc_block_0_ecc_type = 4; + } else if (nand_oobsize == 218) { + fcb->ecc_block_n_ecc_type = 8; + fcb->ecc_block_0_ecc_type = 8; + } + } + + if (fcb->ecc_block_n_ecc_type == 0) { + printf("MX28 NAND: Unsupported NAND geometry\n"); + goto err; + } + + fcb->boot_patch = 0; + fcb->patch_sectors = 0; + + fcb->badblock_marker_byte = mx28_nand_mark_byte_offset(); + fcb->badblock_marker_start_bit = mx28_nand_mark_bit_offset(); + fcb->bb_marker_physical_offset = nand_writesize; + + stride_size_bytes = STRIDE_PAGES * nand_writesize; + bcb_size_bytes = stride_size_bytes * STRIDE_COUNT; + + bootstream_size_pages = (size + (nand_writesize - 1)) / + nand_writesize; + + fw1_start_page = 2 * bcb_size_bytes / nand_writesize; + fw2_start_page = (2 * bcb_size_bytes + MAX_BOOTSTREAM_SIZE) / + nand_writesize; + + fcb->firmware1_starting_sector = fw1_start_page; + fcb->firmware2_starting_sector = fw2_start_page; + fcb->sectors_in_firmware1 = bootstream_size_pages; + fcb->sectors_in_firmware2 = bootstream_size_pages; + + fcb->dbbt_search_area_start_address = STRIDE_PAGES * STRIDE_COUNT; + + return fcb; + +err: + free(fcb); + return NULL; +} + +static struct mx28_nand_dbbt *mx28_nand_get_dbbt(void) +{ + struct mx28_nand_dbbt *dbbt; + + dbbt = malloc(nand_writesize); + if (!dbbt) { + printf("MX28 NAND: Unable to allocate DBBT\n"); + return NULL; + } + + memset(dbbt, 0, nand_writesize); + + dbbt->fingerprint = 0x54424244; + dbbt->version = 0x1; + + return dbbt; +} + +static inline uint8_t mx28_nand_parity_13_8(const uint8_t b) +{ + uint32_t parity = 0, tmp; + + tmp = ((b >> 6) ^ (b >> 5) ^ (b >> 3) ^ (b >> 2)) & 1; + parity |= tmp << 0; + + tmp = ((b >> 7) ^ (b >> 5) ^ (b >> 4) ^ (b >> 2) ^ (b >> 1)) & 1; + parity |= tmp << 1; + + tmp = ((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 1) ^ (b >> 0)) & 1; + parity |= tmp << 2; + + tmp = ((b >> 7) ^ (b >> 4) ^ (b >> 3) ^ (b >> 0)) & 1; + parity |= tmp << 3; + + tmp = ((b >> 6) ^ (b >> 4) ^ (b >> 3) ^ + (b >> 2) ^ (b >> 1) ^ (b >> 0)) & 1; + parity |= tmp << 4; + + return parity; +} + +static uint8_t *mx28_nand_fcb_block(struct mx28_nand_fcb *fcb) +{ + uint8_t *block; + uint8_t *ecc; + int i; + + block = malloc(nand_writesize + nand_oobsize); + if (!block) { + printf("MX28 NAND: Unable to allocate FCB block\n"); + return NULL; + } + + memset(block, 0, nand_writesize + nand_oobsize); + + /* Update the FCB checksum */ + fcb->checksum = mx28_nand_block_csum(((uint8_t *)fcb) + 4, 508); + + /* Figure 12-11. in iMX28RM, rev. 1, says FCB is at offset 12 */ + memcpy(block + 12, fcb, sizeof(struct mx28_nand_fcb)); + + /* ECC is at offset 12 + 512 */ + ecc = block + 12 + 512; + + /* Compute the ECC parity */ + for (i = 0; i < sizeof(struct mx28_nand_fcb); i++) + ecc[i] = mx28_nand_parity_13_8(block[i + 12]); + + return block; +} + +static int mx28_nand_write_fcb(struct mx28_nand_fcb *fcb, uint8_t *buf) +{ + uint32_t offset; + uint8_t *fcbblock; + int ret = 0; + int i; + + fcbblock = mx28_nand_fcb_block(fcb); + if (!fcbblock) + return -1; + + for (i = 0; i < STRIDE_PAGES * STRIDE_COUNT; i += STRIDE_PAGES) { + offset = i * nand_writesize; + memcpy(buf + offset, fcbblock, nand_writesize + nand_oobsize); + /* Mark the NAND page is OK. */ + buf[offset + nand_writesize] = 0xff; + } + + free(fcbblock); + return ret; +} + +static int mx28_nand_write_dbbt(struct mx28_nand_dbbt *dbbt, uint8_t *buf) +{ + uint32_t offset; + int i = STRIDE_PAGES * STRIDE_COUNT; + + for (; i < 2 * STRIDE_PAGES * STRIDE_COUNT; i += STRIDE_PAGES) { + offset = i * nand_writesize; + memcpy(buf + offset, dbbt, sizeof(struct mx28_nand_dbbt)); + } + + return 0; +} + +static int mx28_nand_write_firmware(struct mx28_nand_fcb *fcb, int infd, + uint8_t *buf) +{ + int ret; + off_t size; + uint32_t offset1, offset2; + + size = lseek(infd, 0, SEEK_END); + lseek(infd, 0, SEEK_SET); + + offset1 = fcb->firmware1_starting_sector * nand_writesize; + offset2 = fcb->firmware2_starting_sector * nand_writesize; + + ret = read(infd, buf + offset1, size); + if (ret != size) + return -1; + + memcpy(buf + offset2, buf + offset1, size); + + return 0; +} + +static void usage(void) +{ + printf( + "Usage: mxsboot [ops] \n" + "Augment BootStream file with a proper header for i.MX28 boot\n" + "\n" + " type of image:\n" + " \"nand\" for NAND image\n" + " \"sd\" for SD image\n" + " input file, the u-boot.sb bootstream\n" + " output file, the bootable image\n" + "\n"); + printf( + "For NAND boot, these options are accepted:\n" + " -w NAND page size\n" + " -o NAND OOB size\n" + " -e NAND erase size\n" + "\n" + "For SD boot, these options are accepted:\n" + " -p Sector where the SGTL partition starts\n" + ); +} + +static int mx28_create_nand_image(int infd, int outfd) +{ + struct mx28_nand_fcb *fcb; + struct mx28_nand_dbbt *dbbt; + int ret = -1; + uint8_t *buf; + int size; + ssize_t wr_size; + + size = nand_writesize * 512 + 2 * MAX_BOOTSTREAM_SIZE; + + buf = malloc(size); + if (!buf) { + printf("Can not allocate output buffer of %d bytes\n", size); + goto err0; + } + + memset(buf, 0, size); + + fcb = mx28_nand_get_fcb(MAX_BOOTSTREAM_SIZE); + if (!fcb) { + printf("Unable to compile FCB\n"); + goto err1; + } + + dbbt = mx28_nand_get_dbbt(); + if (!dbbt) { + printf("Unable to compile DBBT\n"); + goto err2; + } + + ret = mx28_nand_write_fcb(fcb, buf); + if (ret) { + printf("Unable to write FCB to buffer\n"); + goto err3; + } + + ret = mx28_nand_write_dbbt(dbbt, buf); + if (ret) { + printf("Unable to write DBBT to buffer\n"); + goto err3; + } + + ret = mx28_nand_write_firmware(fcb, infd, buf); + if (ret) { + printf("Unable to write firmware to buffer\n"); + goto err3; + } + + wr_size = write(outfd, buf, size); + if (wr_size != size) { + ret = -1; + goto err3; + } + + ret = 0; + +err3: + free(dbbt); +err2: + free(fcb); +err1: + free(buf); +err0: + return ret; +} + +static int mx28_create_sd_image(int infd, int outfd) +{ + int ret = -1; + uint32_t *buf; + int size; + off_t fsize; + ssize_t wr_size; + struct mx28_sd_config_block *cb; + + fsize = lseek(infd, 0, SEEK_END); + lseek(infd, 0, SEEK_SET); + size = fsize + 4 * 512; + + buf = malloc(size); + if (!buf) { + printf("Can not allocate output buffer of %d bytes\n", size); + goto err0; + } + + ret = read(infd, (uint8_t *)buf + 4 * 512, fsize); + if (ret != fsize) { + ret = -1; + goto err1; + } + + cb = (struct mx28_sd_config_block *)buf; + + cb->signature = 0x00112233; + cb->primary_boot_tag = 0x1; + cb->secondary_boot_tag = 0x1; + cb->num_copies = 1; + cb->drv_info[0].chip_num = 0x0; + cb->drv_info[0].drive_type = 0x0; + cb->drv_info[0].tag = 0x1; + cb->drv_info[0].first_sector_number = sd_sector + 4; + cb->drv_info[0].sector_count = (size - 4) / 512; + + wr_size = write(outfd, buf, size); + if (wr_size != size) { + ret = -1; + goto err1; + } + + ret = 0; + +err1: + free(buf); +err0: + return ret; +} + +int main(int argc, char **argv) +{ + int infd, outfd; + int ret = 0; + int opt; + int sdimage = 0; + + while ((opt = getopt(argc, argv, "w:o:e:p:")) != -1) { + switch (opt) { + case 'w': + nand_writesize = strtoul(optarg, NULL, 0); + break; + case 'o': + nand_oobsize = strtoul(optarg, NULL, 0); + break; + case 'e': + nand_erasesize = strtoul(optarg, NULL, 0); + break; + case 'p': + sd_sector = strtoul(optarg, NULL, 0); + break; + } + } + + if (argc - optind < 3) { + usage(); + ret = 1; + goto err1; + } + + if (!strcmp(argv[optind], "sd")) + sdimage = 1; + else if (strcmp(argv[optind], "nand")) + return -1; + + infd = open(argv[optind + 1], O_RDONLY); + if (infd < 0) { + printf("Input BootStream file can not be opened\n"); + ret = 2; + goto err1; + } + + outfd = open(argv[optind + 2], O_CREAT | O_TRUNC | O_WRONLY, + S_IRUSR | S_IWUSR); + if (outfd < 0) { + printf("Output file can not be created\n"); + ret = 3; + goto err2; + } + + if (sdimage) + ret = mx28_create_sd_image(infd, outfd); + else + ret = mx28_create_nand_image(infd, outfd); + + close(outfd); +err2: + close(infd); +err1: + return ret; +} diff --git a/scripts/mxsimage.c b/scripts/mxsimage.c new file mode 100644 index 0000000..0a5f6a0 --- /dev/null +++ b/scripts/mxsimage.c @@ -0,0 +1,2561 @@ +/* + * Freescale i.MX23/i.MX28 SB image generator + * + * Copyright (C) 2012-2013 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SB_BLOCK_SIZE 16 + +#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static char *prepfile; +static char *bootloaderfile; + +struct sb_boot_image_version { + uint16_t major; + uint16_t pad0; + uint16_t minor; + uint16_t pad1; + uint16_t revision; + uint16_t pad2; +}; + +struct sb_boot_image_header { + union { + /* SHA1 of the header. */ + uint8_t digest[20]; + struct { + /* CBC-MAC initialization vector. */ + uint8_t iv[16]; + uint8_t extra[4]; + }; + }; + /* 'STMP' */ + uint8_t signature1[4]; + /* Major version of the image format. */ + uint8_t major_version; + /* Minor version of the image format. */ + uint8_t minor_version; + /* Flags associated with the image. */ + uint16_t flags; + /* Size of the image in 16b blocks. */ + uint32_t image_blocks; + /* Offset of the first tag in 16b blocks. */ + uint32_t first_boot_tag_block; + /* ID of the section to boot from. */ + uint32_t first_boot_section_id; + /* Amount of crypto keys. */ + uint16_t key_count; + /* Offset to the key dictionary in 16b blocks. */ + uint16_t key_dictionary_block; + /* Size of this header in 16b blocks. */ + uint16_t header_blocks; + /* Amount of section headers. */ + uint16_t section_count; + /* Section header size in 16b blocks. */ + uint16_t section_header_size; + /* Padding to align timestamp to uint64_t. */ + uint8_t padding0[2]; + /* 'sgtl' (since v1.1) */ + uint8_t signature2[4]; + /* Image generation date, in microseconds since 1.1.2000 . */ + uint64_t timestamp_us; + /* Product version. */ + struct sb_boot_image_version + product_version; + /* Component version. */ + struct sb_boot_image_version + component_version; + /* Drive tag for the system drive. (since v1.1) */ + uint16_t drive_tag; + /* Padding. */ + uint8_t padding1[6]; +}; + +#define SB_VERSION_MAJOR 1 +#define SB_VERSION_MINOR 1 + +/* Enable to HTLLC verbose boot report. */ +#define SB_IMAGE_FLAG_VERBOSE (1 << 0) + +struct sb_key_dictionary_key { + /* The CBC-MAC of image and sections header. */ + uint8_t cbc_mac[SB_BLOCK_SIZE]; + /* The AES key encrypted by image key (zero). */ + uint8_t key[SB_BLOCK_SIZE]; +}; + +struct sb_ivt_header { + uint32_t header; + uint32_t entry; + uint32_t reserved1; + uint32_t dcd; + uint32_t boot_data; + uint32_t self; + uint32_t csf; + uint32_t reserved2; +}; + +#define SB_HAB_IVT_TAG 0xd1UL +#define SB_HAB_DCD_TAG 0xd2UL + +#define SB_HAB_VERSION 0x40UL + +/* + * The "size" field in the IVT header is not naturally aligned, + * use this macro to fill first 4 bytes of the IVT header without + * causing issues on some systems (esp. M68k, PPC, MIPS-BE, ARM-BE). + */ +static inline uint32_t sb_hab_ivt_header(void) +{ + uint32_t ret = 0; + ret |= SB_HAB_IVT_TAG << 24; + ret |= sizeof(struct sb_ivt_header) << 16; + ret |= SB_HAB_VERSION; + return htonl(ret); +} + +struct sb_sections_header { + /* Section number. */ + uint32_t section_number; + /* Offset of this sections first instruction after "TAG". */ + uint32_t section_offset; + /* Size of the section in 16b blocks. */ + uint32_t section_size; + /* Section flags. */ + uint32_t section_flags; +}; + +#define SB_SECTION_FLAG_BOOTABLE (1 << 0) + +struct sb_command { + struct { + uint8_t checksum; + uint8_t tag; + uint16_t flags; +#define ROM_TAG_CMD_FLAG_ROM_LAST_TAG 0x1 +#define ROM_LOAD_CMD_FLAG_DCD_LOAD 0x1 /* MX28 only */ +#define ROM_JUMP_CMD_FLAG_HAB 0x1 /* MX28 only */ +#define ROM_CALL_CMD_FLAG_HAB 0x1 /* MX28 only */ + } header; + + union { + struct { + uint32_t reserved[3]; + } nop; + struct { + uint32_t section_number; + uint32_t section_length; + uint32_t section_flags; + } tag; + struct { + uint32_t address; + uint32_t count; + uint32_t crc32; + } load; + struct { + uint32_t address; + uint32_t count; + uint32_t pattern; + } fill; + struct { + uint32_t address; + uint32_t reserved; + /* Passed in register r0 before JUMP */ + uint32_t argument; + } jump; + struct { + uint32_t address; + uint32_t reserved; + /* Passed in register r0 before CALL */ + uint32_t argument; + } call; + struct { + uint32_t reserved1; + uint32_t reserved2; + uint32_t mode; + } mode; + + }; +}; + +/* + * Most of the mode names are same or at least similar + * on i.MX23 and i.MX28, but some of the mode names + * differ. The "name" field represents the mode name + * on i.MX28 as seen in Table 12-2 of the datasheet. + * The "altname" field represents the differently named + * fields on i.MX23 as seen in Table 35-3 of the + * datasheet. + */ +static const struct { + const char *name; + const char *altname; + const uint8_t mode; +} modetable[] = { + { "USB", NULL, 0x00 }, + { "I2C", NULL, 0x01 }, + { "SPI2_FLASH", "SPI1_FLASH", 0x02 }, + { "SPI3_FLASH", "SPI2_FLASH", 0x03 }, + { "NAND_BCH", NULL, 0x04 }, + { "JTAG", NULL, 0x06 }, + { "SPI3_EEPROM", "SPI2_EEPROM", 0x08 }, + { "SD_SSP0", NULL, 0x09 }, + { "SD_SSP1", NULL, 0x0A } +}; + +enum sb_tag { + ROM_NOP_CMD = 0x00, + ROM_TAG_CMD = 0x01, + ROM_LOAD_CMD = 0x02, + ROM_FILL_CMD = 0x03, + ROM_JUMP_CMD = 0x04, + ROM_CALL_CMD = 0x05, + ROM_MODE_CMD = 0x06 +}; + +struct sb_source_entry { + uint8_t tag; + uint32_t address; + uint32_t flags; + char *filename; +}; + +/* + * DCD block + * |-Write to address command block + * | 0xf00 == 0xf33d + * | 0xba2 == 0xb33f + * |-ORR address with mask command block + * | 0xf00 |= 0x1337 + * |-Write to address command block + * | 0xba2 == 0xd00d + * : + */ +#define SB_HAB_DCD_WRITE 0xccUL +#define SB_HAB_DCD_CHECK 0xcfUL +#define SB_HAB_DCD_NOOP 0xc0UL +#define SB_HAB_DCD_MASK_BIT (1 << 3) +#define SB_HAB_DCD_SET_BIT (1 << 4) + +/* Addr.n = Value.n */ +#define SB_DCD_WRITE \ + (SB_HAB_DCD_WRITE << 24) +/* Addr.n &= ~Value.n */ +#define SB_DCD_ANDC \ + ((SB_HAB_DCD_WRITE << 24) | SB_HAB_DCD_SET_BIT) +/* Addr.n |= Value.n */ +#define SB_DCD_ORR \ + ((SB_HAB_DCD_WRITE << 24) | SB_HAB_DCD_SET_BIT | SB_HAB_DCD_MASK_BIT) +/* (Addr.n & Value.n) == 0 */ +#define SB_DCD_CHK_EQZ \ + (SB_HAB_DCD_CHECK << 24) +/* (Addr.n & Value.n) == Value.n */ +#define SB_DCD_CHK_EQ \ + ((SB_HAB_DCD_CHECK << 24) | SB_HAB_DCD_SET_BIT) +/* (Addr.n & Value.n) != Value.n */ +#define SB_DCD_CHK_NEQ \ + ((SB_HAB_DCD_CHECK << 24) | SB_HAB_DCD_MASK_BIT) +/* (Addr.n & Value.n) != 0 */ +#define SB_DCD_CHK_NEZ \ + ((SB_HAB_DCD_CHECK << 24) | SB_HAB_DCD_SET_BIT | SB_HAB_DCD_MASK_BIT) +/* NOP */ +#define SB_DCD_NOOP \ + (SB_HAB_DCD_NOOP << 24) + +struct sb_dcd_ctx { + struct sb_dcd_ctx *dcd; + + uint32_t id; + + /* The DCD block. */ + uint32_t *payload; + /* Size of the whole DCD block. */ + uint32_t size; + + /* Pointer to previous DCD command block. */ + uint32_t *prev_dcd_head; +}; + +/* + * IMAGE + * |-SECTION + * | |-CMD + * | |-CMD + * | `-CMD + * |-SECTION + * | |-CMD + * : : + */ +struct sb_cmd_list { + char *cmd; + size_t len; + unsigned int lineno; +}; + +struct sb_cmd_ctx { + uint32_t size; + + struct sb_cmd_ctx *cmd; + + uint8_t *data; + uint32_t length; + + struct sb_command payload; + struct sb_command c_payload; +}; + +struct sb_section_ctx { + uint32_t size; + + /* Section flags */ + unsigned int boot:1; + + struct sb_section_ctx *sect; + + struct sb_cmd_ctx *cmd_head; + struct sb_cmd_ctx *cmd_tail; + + struct sb_sections_header payload; +}; + +struct sb_image_ctx { + unsigned int in_section:1; + unsigned int in_dcd:1; + /* Image configuration */ + unsigned int verbose_boot:1; + unsigned int silent_dump:1; + const char *input_filename; + const char *output_filename; + const char *cfg_filename; + uint8_t image_key[16]; + + /* Number of section in the image */ + unsigned int sect_count; + /* Bootable section */ + unsigned int sect_boot; + unsigned int sect_boot_found:1; + + struct sb_section_ctx *sect_head; + struct sb_section_ctx *sect_tail; + + struct sb_dcd_ctx *dcd_head; + struct sb_dcd_ctx *dcd_tail; + + EVP_CIPHER_CTX cipher_ctx; + EVP_MD_CTX md_ctx; + uint8_t digest[32]; + struct sb_key_dictionary_key sb_dict_key; + + struct sb_boot_image_header payload; +}; + +/* + * Instruction semantics: + * NOOP + * TAG [LAST] + * LOAD address file + * LOAD IVT address IVT_entry_point + * FILL address pattern length + * JUMP [HAB] address [r0_arg] + * CALL [HAB] address [r0_arg] + * MODE mode + * For i.MX23, mode = USB/I2C/SPI1_FLASH/SPI2_FLASH/NAND_BCH + * JTAG/SPI3_EEPROM/SD_SSP0/SD_SSP1 + * For i.MX28, mode = USB/I2C/SPI2_FLASH/SPI3_FLASH/NAND_BCH + * JTAG/SPI2_EEPROM/SD_SSP0/SD_SSP1 + */ + +static uint32_t crc_table[256]; +static int crc_table_valid; + +static void make_crc_table(void) +{ + uint32_t mask; + int i, j; + uint32_t poly; /* polynomial exclusive-or pattern */ + + if (crc_table_valid) + return; + + /* + * the polynomial used by PBL is 1 + x1 + x2 + x4 + x5 + x7 + x8 + x10 + * + x11 + x12 + x16 + x22 + x23 + x26 + x32. + */ + poly = 0x04c11db7; + + for (i = 0; i < 256; i++) { + mask = i << 24; + for (j = 0; j < 8; j++) { + if (mask & 0x80000000) + mask = (mask << 1) ^ poly; + else + mask <<= 1; + } + crc_table[i] = mask; + } + + crc_table_valid = 1; +} + +uint32_t pbl_crc32(uint32_t in_crc, const char *buf, uint32_t len) +{ + uint32_t crc32_val; + int i; + + make_crc_table(); + + crc32_val = ~in_crc; + + for (i = 0; i < len; i++) + crc32_val = (crc32_val << 8) ^ + crc_table[(crc32_val >> 24) ^ (*buf++ & 0xff)]; + + return crc32_val; +} +/* + * AES libcrypto + */ +static int sb_aes_init(struct sb_image_ctx *ictx, uint8_t *iv, int enc) +{ + EVP_CIPHER_CTX *ctx = &ictx->cipher_ctx; + int ret; + + /* If there is no init vector, init vector is all zeroes. */ + if (!iv) + iv = ictx->image_key; + + EVP_CIPHER_CTX_init(ctx); + ret = EVP_CipherInit(ctx, EVP_aes_128_cbc(), ictx->image_key, iv, enc); + if (ret == 1) + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ret; +} + +static int sb_aes_crypt(struct sb_image_ctx *ictx, uint8_t *in_data, + uint8_t *out_data, int in_len) +{ + EVP_CIPHER_CTX *ctx = &ictx->cipher_ctx; + int ret, outlen; + uint8_t *outbuf; + + outbuf = malloc(in_len); + if (!outbuf) + return -ENOMEM; + memset(outbuf, 0, sizeof(in_len)); + + ret = EVP_CipherUpdate(ctx, outbuf, &outlen, in_data, in_len); + if (!ret) { + ret = -EINVAL; + goto err; + } + + if (out_data) + memcpy(out_data, outbuf, outlen); + +err: + free(outbuf); + return ret; +} + +static int sb_aes_deinit(EVP_CIPHER_CTX *ctx) +{ + return EVP_CIPHER_CTX_cleanup(ctx); +} + +static int sb_aes_reinit(struct sb_image_ctx *ictx, int enc) +{ + int ret; + EVP_CIPHER_CTX *ctx = &ictx->cipher_ctx; + struct sb_boot_image_header *sb_header = &ictx->payload; + uint8_t *iv = sb_header->iv; + + ret = sb_aes_deinit(ctx); + if (!ret) + return ret; + return sb_aes_init(ictx, iv, enc); +} + +/* + * Debug + */ +static void soprintf(struct sb_image_ctx *ictx, const char *fmt, ...) +{ + va_list ap; + + if (ictx->silent_dump) + return; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); +} + +/* + * Code + */ +static time_t sb_get_timestamp(void) +{ + struct tm time_2000 = { + .tm_yday = 1, /* Jan. 1st */ + .tm_year = 100, /* 2000 */ + }; + time_t seconds_to_2000 = mktime(&time_2000); + time_t seconds_to_now = time(NULL); + + return seconds_to_now - seconds_to_2000; +} + +static int sb_get_time(time_t time, struct tm *tm) +{ + struct tm time_2000 = { + .tm_yday = 1, /* Jan. 1st */ + .tm_year = 0, /* 1900 */ + }; + const time_t seconds_to_2000 = mktime(&time_2000); + const time_t seconds_to_now = seconds_to_2000 + time; + struct tm *ret; + ret = gmtime_r(&seconds_to_now, tm); + return ret ? 0 : -EINVAL; +} + +static void sb_encrypt_sb_header(struct sb_image_ctx *ictx) +{ + EVP_MD_CTX *md_ctx = &ictx->md_ctx; + struct sb_boot_image_header *sb_header = &ictx->payload; + uint8_t *sb_header_ptr = (uint8_t *)sb_header; + + /* Encrypt the header, compute the digest. */ + sb_aes_crypt(ictx, sb_header_ptr, NULL, sizeof(*sb_header)); + EVP_DigestUpdate(md_ctx, sb_header_ptr, sizeof(*sb_header)); +} + +static void sb_encrypt_sb_sections_header(struct sb_image_ctx *ictx) +{ + EVP_MD_CTX *md_ctx = &ictx->md_ctx; + struct sb_section_ctx *sctx = ictx->sect_head; + struct sb_sections_header *shdr; + uint8_t *sb_sections_header_ptr; + const int size = sizeof(*shdr); + + while (sctx) { + shdr = &sctx->payload; + sb_sections_header_ptr = (uint8_t *)shdr; + + sb_aes_crypt(ictx, sb_sections_header_ptr, + ictx->sb_dict_key.cbc_mac, size); + EVP_DigestUpdate(md_ctx, sb_sections_header_ptr, size); + + sctx = sctx->sect; + }; +} + +static void sb_encrypt_key_dictionary_key(struct sb_image_ctx *ictx) +{ + EVP_MD_CTX *md_ctx = &ictx->md_ctx; + + sb_aes_crypt(ictx, ictx->image_key, ictx->sb_dict_key.key, + sizeof(ictx->sb_dict_key.key)); + EVP_DigestUpdate(md_ctx, &ictx->sb_dict_key, sizeof(ictx->sb_dict_key)); +} + +static void sb_decrypt_key_dictionary_key(struct sb_image_ctx *ictx) +{ + EVP_MD_CTX *md_ctx = &ictx->md_ctx; + + EVP_DigestUpdate(md_ctx, &ictx->sb_dict_key, sizeof(ictx->sb_dict_key)); + sb_aes_crypt(ictx, ictx->sb_dict_key.key, ictx->image_key, + sizeof(ictx->sb_dict_key.key)); +} + +static void sb_encrypt_tag(struct sb_image_ctx *ictx, + struct sb_cmd_ctx *cctx) +{ + EVP_MD_CTX *md_ctx = &ictx->md_ctx; + struct sb_command *cmd = &cctx->payload; + + sb_aes_crypt(ictx, (uint8_t *)cmd, + (uint8_t *)&cctx->c_payload, sizeof(*cmd)); + EVP_DigestUpdate(md_ctx, &cctx->c_payload, sizeof(*cmd)); +} + +static int sb_encrypt_image(struct sb_image_ctx *ictx) +{ + /* Start image-wide crypto. */ + EVP_MD_CTX_init(&ictx->md_ctx); + EVP_DigestInit(&ictx->md_ctx, EVP_sha1()); + + /* + * SB image header. + */ + sb_aes_init(ictx, NULL, 1); + sb_encrypt_sb_header(ictx); + + /* + * SB sections header. + */ + sb_encrypt_sb_sections_header(ictx); + + /* + * Key dictionary. + */ + sb_aes_reinit(ictx, 1); + sb_encrypt_key_dictionary_key(ictx); + + /* + * Section tags. + */ + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + struct sb_section_ctx *sctx = ictx->sect_head; + + while (sctx) { + cctx = sctx->cmd_head; + + sb_aes_reinit(ictx, 1); + + while (cctx) { + ccmd = &cctx->payload; + + sb_encrypt_tag(ictx, cctx); + + if (ccmd->header.tag == ROM_TAG_CMD) { + sb_aes_reinit(ictx, 1); + } else if (ccmd->header.tag == ROM_LOAD_CMD) { + sb_aes_crypt(ictx, cctx->data, cctx->data, + cctx->length); + EVP_DigestUpdate(&ictx->md_ctx, cctx->data, + cctx->length); + } + + cctx = cctx->cmd; + } + + sctx = sctx->sect; + }; + + /* + * Dump the SHA1 of the whole image. + */ + sb_aes_reinit(ictx, 1); + + EVP_DigestFinal(&ictx->md_ctx, ictx->digest, NULL); + sb_aes_crypt(ictx, ictx->digest, ictx->digest, sizeof(ictx->digest)); + + /* Stop the encryption session. */ + sb_aes_deinit(&ictx->cipher_ctx); + + return 0; +} + +static int sb_load_file(struct sb_cmd_ctx *cctx, const char *filename) +{ + long real_size, roundup_size; + uint8_t *data; + long ret; + unsigned long size; + FILE *fp; + + if (!filename) { + fprintf(stderr, "ERR: Missing filename!\n"); + return -EINVAL; + } + + fp = fopen(filename, "r"); + if (!fp) + goto err_open; + + ret = fseek(fp, 0, SEEK_END); + if (ret < 0) + goto err_file; + + real_size = ftell(fp); + if (real_size < 0) + goto err_file; + + ret = fseek(fp, 0, SEEK_SET); + if (ret < 0) + goto err_file; + + roundup_size = roundup(real_size, SB_BLOCK_SIZE); + data = calloc(1, roundup_size); + if (!data) + goto err_file; + + size = fread(data, 1, real_size, fp); + if (size != (unsigned long)real_size) + goto err_alloc; + + cctx->data = data; + cctx->length = roundup_size; + + fclose(fp); + return 0; + +err_alloc: + free(data); +err_file: + fclose(fp); +err_open: + fprintf(stderr, "ERR: Failed to load file \"%s\"\n", filename); + return -EINVAL; +} + +static uint8_t sb_command_checksum(struct sb_command *inst) +{ + uint8_t *inst_ptr = (uint8_t *)inst; + uint8_t csum = 0; + unsigned int i; + + for (i = 0; i < sizeof(struct sb_command); i++) + csum += inst_ptr[i]; + + return csum; +} + +static int sb_token_to_long(char *tok, uint32_t *rid) +{ + char *endptr; + unsigned long id; + + if (tok[0] != '0' || tok[1] != 'x') { + fprintf(stderr, "ERR: Invalid hexadecimal number!\n"); + return -EINVAL; + } + + tok += 2; + + errno = 0; + id = strtoul(tok, &endptr, 16); + if ((errno == ERANGE && id == ULONG_MAX) || (errno != 0 && id == 0)) { + fprintf(stderr, "ERR: Value can't be decoded!\n"); + return -EINVAL; + } + + /* Check for 32-bit overflow. */ + if (id > 0xffffffff) { + fprintf(stderr, "ERR: Value too big!\n"); + return -EINVAL; + } + + if (endptr == tok) { + fprintf(stderr, "ERR: Deformed value!\n"); + return -EINVAL; + } + + *rid = (uint32_t)id; + return 0; +} + +static int sb_grow_dcd(struct sb_dcd_ctx *dctx, unsigned int inc_size) +{ + uint32_t *tmp; + + if (!inc_size) + return 0; + + dctx->size += inc_size; + tmp = realloc(dctx->payload, dctx->size); + if (!tmp) + return -ENOMEM; + + dctx->payload = tmp; + + /* Assemble and update the HAB DCD header. */ + dctx->payload[0] = htonl((SB_HAB_DCD_TAG << 24) | + (dctx->size << 8) | + SB_HAB_VERSION); + + return 0; +} + +static int sb_build_dcd(struct sb_image_ctx *ictx, struct sb_cmd_list *cmd) +{ + struct sb_dcd_ctx *dctx; + + char *tok; + uint32_t id; + int ret; + + dctx = calloc(1, sizeof(*dctx)); + if (!dctx) + return -ENOMEM; + + ret = sb_grow_dcd(dctx, 4); + if (ret) + goto err_dcd; + + /* Read DCD block number. */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: DCD block without number!\n", + cmd->lineno); + ret = -EINVAL; + goto err_dcd; + } + + /* Parse the DCD block number. */ + ret = sb_token_to_long(tok, &id); + if (ret) { + fprintf(stderr, "#%i ERR: Malformed DCD block number!\n", + cmd->lineno); + goto err_dcd; + } + + dctx->id = id; + + /* + * The DCD block is now constructed. Append it to the list. + * WARNING: The DCD size is still not computed and will be + * updated while parsing it's commands. + */ + if (!ictx->dcd_head) { + ictx->dcd_head = dctx; + ictx->dcd_tail = dctx; + } else { + ictx->dcd_tail->dcd = dctx; + ictx->dcd_tail = dctx; + } + + return 0; + +err_dcd: + free(dctx->payload); + free(dctx); + return ret; +} + +static int sb_build_dcd_block(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd, + uint32_t type) +{ + char *tok; + uint32_t address, value, length; + int ret; + + struct sb_dcd_ctx *dctx = ictx->dcd_tail; + uint32_t *dcd; + + if (dctx->prev_dcd_head && (type != SB_DCD_NOOP) && + ((dctx->prev_dcd_head[0] & 0xff0000ff) == type)) { + /* Same instruction as before, just append it. */ + ret = sb_grow_dcd(dctx, 8); + if (ret) + return ret; + } else if (type == SB_DCD_NOOP) { + ret = sb_grow_dcd(dctx, 4); + if (ret) + return ret; + + /* Update DCD command block pointer. */ + dctx->prev_dcd_head = dctx->payload + + dctx->size / sizeof(*dctx->payload) - 1; + + /* NOOP has only 4 bytes and no payload. */ + goto noop; + } else { + /* + * Either a different instruction block started now + * or this is the first instruction block. + */ + ret = sb_grow_dcd(dctx, 12); + if (ret) + return ret; + + /* Update DCD command block pointer. */ + dctx->prev_dcd_head = dctx->payload + + dctx->size / sizeof(*dctx->payload) - 3; + } + + dcd = dctx->payload + dctx->size / sizeof(*dctx->payload) - 2; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing DCD address!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read DCD destination address. */ + ret = sb_token_to_long(tok, &address); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect DCD address!\n", + cmd->lineno); + goto err; + } + + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing DCD value!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read DCD operation value. */ + ret = sb_token_to_long(tok, &value); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect DCD value!\n", + cmd->lineno); + goto err; + } + + /* Fill in the new DCD entry. */ + dcd[0] = htonl(address); + dcd[1] = htonl(value); + +noop: + /* Update the DCD command block. */ + length = dctx->size - + ((dctx->prev_dcd_head - dctx->payload) * + sizeof(*dctx->payload)); + dctx->prev_dcd_head[0] = htonl(type | (length << 8)); + +err: + return ret; +} + +static int sb_build_section(struct sb_image_ctx *ictx, struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx; + struct sb_sections_header *shdr; + char *tok; + uint32_t bootable = 0; + uint32_t id; + int ret; + + sctx = calloc(1, sizeof(*sctx)); + if (!sctx) + return -ENOMEM; + + /* Read section number. */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Section without number!\n", + cmd->lineno); + ret = -EINVAL; + goto err_sect; + } + + /* Parse the section number. */ + ret = sb_token_to_long(tok, &id); + if (ret) { + fprintf(stderr, "#%i ERR: Malformed section number!\n", + cmd->lineno); + goto err_sect; + } + + /* Read section's BOOTABLE flag. */ + tok = strtok(NULL, " "); + if (tok && (strlen(tok) == 8) && !strncmp(tok, "BOOTABLE", 8)) + bootable = SB_SECTION_FLAG_BOOTABLE; + + sctx->boot = bootable; + + shdr = &sctx->payload; + shdr->section_number = id; + shdr->section_flags = bootable; + + /* + * The section is now constructed. Append it to the list. + * WARNING: The section size is still not computed and will + * be updated while parsing it's commands. + */ + ictx->sect_count++; + + /* Mark that this section is bootable one. */ + if (bootable) { + if (ictx->sect_boot_found) { + fprintf(stderr, + "#%i WARN: Multiple bootable section!\n", + cmd->lineno); + } else { + ictx->sect_boot = id; + ictx->sect_boot_found = 1; + } + } + + if (!ictx->sect_head) { + ictx->sect_head = sctx; + ictx->sect_tail = sctx; + } else { + ictx->sect_tail->sect = sctx; + ictx->sect_tail = sctx; + } + + return 0; + +err_sect: + free(sctx); + return ret; +} + +static int sb_build_command_nop(struct sb_image_ctx *ictx) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_NOP_CMD; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; +} + +static int sb_build_command_tag(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + /* Check for the LAST keyword. */ + tok = strtok(cmd->cmd, " "); + if (tok && !strcmp(tok, "LAST")) + ccmd->header.flags = ROM_TAG_CMD_FLAG_ROM_LAST_TAG; + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_TAG_CMD; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; +} + +static int sb_build_command_load(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + int ret, is_ivt = 0, is_dcd = 0; + uint32_t dest, dcd = 0; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing LOAD address or 'IVT'!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Check for "IVT" flag. */ + if (!strcmp(tok, "IVT")) + is_ivt = 1; + if (!strcmp(tok, "DCD")) + is_dcd = 1; + if (is_ivt || is_dcd) { + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing LOAD address!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + } + + /* Read load destination address. */ + ret = sb_token_to_long(tok, &dest); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect LOAD address!\n", + cmd->lineno); + goto err; + } + + /* Read filename or IVT entrypoint or DCD block ID. */ + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, + "#%i ERR: Missing LOAD filename or IVT ep or DCD block ID!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + if (is_ivt) { + /* Handle IVT. */ + struct sb_ivt_header *ivt; + uint32_t ivtep; + ret = sb_token_to_long(tok, &ivtep); + + if (ret) { + fprintf(stderr, + "#%i ERR: Incorrect IVT entry point!\n", + cmd->lineno); + goto err; + } + + ivt = calloc(1, sizeof(*ivt)); + if (!ivt) { + ret = -ENOMEM; + goto err; + } + + ivt->header = sb_hab_ivt_header(); + ivt->entry = ivtep; + ivt->self = dest; + + cctx->data = (uint8_t *)ivt; + cctx->length = sizeof(*ivt); + } else if (is_dcd) { + struct sb_dcd_ctx *dctx = ictx->dcd_head; + uint32_t dcdid; + uint8_t *payload; + uint32_t asize; + ret = sb_token_to_long(tok, &dcdid); + + if (ret) { + fprintf(stderr, + "#%i ERR: Incorrect DCD block ID!\n", + cmd->lineno); + goto err; + } + + while (dctx) { + if (dctx->id == dcdid) + break; + dctx = dctx->dcd; + } + + if (!dctx) { + fprintf(stderr, "#%i ERR: DCD block %08x not found!\n", + cmd->lineno, dcdid); + goto err; + } + + asize = roundup(dctx->size, SB_BLOCK_SIZE); + payload = calloc(1, asize); + if (!payload) { + ret = -ENOMEM; + goto err; + } + + memcpy(payload, dctx->payload, dctx->size); + + cctx->data = payload; + cctx->length = asize; + + /* Set the Load DCD flag. */ + dcd = ROM_LOAD_CMD_FLAG_DCD_LOAD; + } else { + const char *loadfile; + + /* Regular LOAD of a file. */ + if (!strcmp(tok, "@PREP@")) + loadfile = prepfile; + else if (!strcmp(tok, "@BOOTLOADER@")) + loadfile = bootloaderfile; + else + loadfile = tok; + + ret = sb_load_file(cctx, loadfile); + if (ret) { + fprintf(stderr, "#%i ERR: Cannot load '%s'!\n", + cmd->lineno, tok); + goto err; + } + } + + if (cctx->length & (SB_BLOCK_SIZE - 1)) { + fprintf(stderr, "#%i ERR: Unaligned payload!\n", + cmd->lineno); + } + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_LOAD_CMD; + ccmd->header.flags = dcd; + + ccmd->load.address = dest; + ccmd->load.count = cctx->length; + ccmd->load.crc32 = pbl_crc32(0, + (const char *)cctx->data, + cctx->length); + + cctx->size = sizeof(*ccmd) + cctx->length; + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; + +err: + free(cctx); + return ret; +} + +static int sb_build_command_fill(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + uint32_t address, pattern, length; + int ret; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing FILL address!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read fill destination address. */ + ret = sb_token_to_long(tok, &address); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect FILL address!\n", + cmd->lineno); + goto err; + } + + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing FILL pattern!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read fill pattern address. */ + ret = sb_token_to_long(tok, &pattern); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect FILL pattern!\n", + cmd->lineno); + goto err; + } + + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing FILL length!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* Read fill pattern address. */ + ret = sb_token_to_long(tok, &length); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect FILL length!\n", + cmd->lineno); + goto err; + } + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_FILL_CMD; + + ccmd->fill.address = address; + ccmd->fill.count = length; + ccmd->fill.pattern = pattern; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; + +err: + free(cctx); + return ret; +} + +static int sb_build_command_jump_call(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd, + unsigned int is_call) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + uint32_t dest, arg = 0x0; + uint32_t hab = 0; + int ret; + const char *cmdname = is_call ? "CALL" : "JUMP"; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, + "#%i ERR: Missing %s address or 'HAB'!\n", + cmd->lineno, cmdname); + ret = -EINVAL; + goto err; + } + + /* Check for "HAB" flag. */ + if (!strcmp(tok, "HAB")) { + hab = is_call ? ROM_CALL_CMD_FLAG_HAB : ROM_JUMP_CMD_FLAG_HAB; + tok = strtok(NULL, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing %s address!\n", + cmd->lineno, cmdname); + ret = -EINVAL; + goto err; + } + } + /* Read load destination address. */ + ret = sb_token_to_long(tok, &dest); + if (ret) { + fprintf(stderr, "#%i ERR: Incorrect %s address!\n", + cmd->lineno, cmdname); + goto err; + } + + tok = strtok(NULL, " "); + if (tok) { + ret = sb_token_to_long(tok, &arg); + if (ret) { + fprintf(stderr, + "#%i ERR: Incorrect %s argument!\n", + cmd->lineno, cmdname); + goto err; + } + } + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = is_call ? ROM_CALL_CMD : ROM_JUMP_CMD; + ccmd->header.flags = hab; + + ccmd->call.address = dest; + ccmd->call.argument = arg; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; + +err: + free(cctx); + return ret; +} + +static int sb_build_command_jump(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + return sb_build_command_jump_call(ictx, cmd, 0); +} + +static int sb_build_command_call(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + return sb_build_command_jump_call(ictx, cmd, 1); +} + +static int sb_build_command_mode(struct sb_image_ctx *ictx, + struct sb_cmd_list *cmd) +{ + struct sb_section_ctx *sctx = ictx->sect_tail; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + char *tok; + int ret; + unsigned int i; + uint32_t mode = 0xffffffff; + + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + + ccmd = &cctx->payload; + + /* + * Prepare the command. + */ + tok = strtok(cmd->cmd, " "); + if (!tok) { + fprintf(stderr, "#%i ERR: Missing MODE boot mode argument!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < ARRAY_SIZE(modetable); i++) { + if (!strcmp(tok, modetable[i].name)) { + mode = modetable[i].mode; + break; + } + + if (!modetable[i].altname) + continue; + + if (!strcmp(tok, modetable[i].altname)) { + mode = modetable[i].mode; + break; + } + } + + if (mode == 0xffffffff) { + fprintf(stderr, "#%i ERR: Invalid MODE boot mode argument!\n", + cmd->lineno); + ret = -EINVAL; + goto err; + } + + /* + * Construct the command. + */ + ccmd->header.checksum = 0x5a; + ccmd->header.tag = ROM_MODE_CMD; + + ccmd->mode.mode = mode; + + cctx->size = sizeof(*ccmd); + + /* + * Append the command to the last section. + */ + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + return 0; + +err: + free(cctx); + return ret; +} + +static int sb_prefill_image_header(struct sb_image_ctx *ictx) +{ + struct sb_boot_image_header *hdr = &ictx->payload; + + /* Fill signatures */ + memcpy(hdr->signature1, "STMP", 4); + memcpy(hdr->signature2, "sgtl", 4); + + /* SB Image version 1.1 */ + hdr->major_version = SB_VERSION_MAJOR; + hdr->minor_version = SB_VERSION_MINOR; + + /* Boot image major version */ + hdr->product_version.major = htons(0x999); + hdr->product_version.minor = htons(0x999); + hdr->product_version.revision = htons(0x999); + /* Boot image major version */ + hdr->component_version.major = htons(0x999); + hdr->component_version.minor = htons(0x999); + hdr->component_version.revision = htons(0x999); + + /* Drive tag must be 0x0 for i.MX23 */ + hdr->drive_tag = 0; + + hdr->header_blocks = + sizeof(struct sb_boot_image_header) / SB_BLOCK_SIZE; + hdr->section_header_size = + sizeof(struct sb_sections_header) / SB_BLOCK_SIZE; + hdr->timestamp_us = sb_get_timestamp() * 1000000; + + /* FIXME -- add proper config option */ + hdr->flags = ictx->verbose_boot ? SB_IMAGE_FLAG_VERBOSE : 0, + + /* FIXME -- We support only default key */ + hdr->key_count = 1; + + return 0; +} + +static int sb_postfill_image_header(struct sb_image_ctx *ictx) +{ + struct sb_boot_image_header *hdr = &ictx->payload; + struct sb_section_ctx *sctx = ictx->sect_head; + uint32_t kd_size, sections_blocks; + EVP_MD_CTX md_ctx; + + /* The main SB header size in blocks. */ + hdr->image_blocks = hdr->header_blocks; + + /* Size of the key dictionary, which has single zero entry. */ + kd_size = hdr->key_count * sizeof(struct sb_key_dictionary_key); + hdr->image_blocks += kd_size / SB_BLOCK_SIZE; + + /* Now count the payloads. */ + hdr->section_count = ictx->sect_count; + while (sctx) { + hdr->image_blocks += sctx->size / SB_BLOCK_SIZE; + sctx = sctx->sect; + } + + if (!ictx->sect_boot_found) { + fprintf(stderr, "ERR: No bootable section selected!\n"); + return -EINVAL; + } + hdr->first_boot_section_id = ictx->sect_boot; + + /* The n * SB section size in blocks. */ + sections_blocks = hdr->section_count * hdr->section_header_size; + hdr->image_blocks += sections_blocks; + + /* Key dictionary offset. */ + hdr->key_dictionary_block = hdr->header_blocks + sections_blocks; + + /* Digest of the whole image. */ + hdr->image_blocks += 2; + + /* Pointer past the dictionary. */ + hdr->first_boot_tag_block = + hdr->key_dictionary_block + kd_size / SB_BLOCK_SIZE; + + /* Compute header digest. */ + EVP_MD_CTX_init(&md_ctx); + + EVP_DigestInit(&md_ctx, EVP_sha1()); + EVP_DigestUpdate(&md_ctx, hdr->signature1, + sizeof(struct sb_boot_image_header) - + sizeof(hdr->digest)); + EVP_DigestFinal(&md_ctx, hdr->digest, NULL); + + return 0; +} + +static int sb_fixup_sections_and_tags(struct sb_image_ctx *ictx) +{ + /* Fixup the placement of sections. */ + struct sb_boot_image_header *ihdr = &ictx->payload; + struct sb_section_ctx *sctx = ictx->sect_head; + struct sb_sections_header *shdr; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + uint32_t offset = ihdr->first_boot_tag_block; + + while (sctx) { + shdr = &sctx->payload; + + /* Fill in the section TAG offset. */ + shdr->section_offset = offset + 1; + offset += shdr->section_size; + + /* Section length is measured from the TAG block. */ + shdr->section_size--; + + /* Fixup the TAG command. */ + cctx = sctx->cmd_head; + while (cctx) { + ccmd = &cctx->payload; + if (ccmd->header.tag == ROM_TAG_CMD) { + ccmd->tag.section_number = shdr->section_number; + ccmd->tag.section_length = shdr->section_size; + ccmd->tag.section_flags = shdr->section_flags; + } + + /* Update the command checksum. */ + ccmd->header.checksum = sb_command_checksum(ccmd); + + cctx = cctx->cmd; + } + + sctx = sctx->sect; + } + + return 0; +} + +static int sb_parse_line(struct sb_image_ctx *ictx, struct sb_cmd_list *cmd) +{ + char *tok; + char *line = cmd->cmd; + char *rptr = NULL; + int ret; + + /* Analyze the identifier on this line first. */ + tok = strtok_r(line, " ", &rptr); + if (!tok || (strlen(tok) == 0)) { + fprintf(stderr, "#%i ERR: Invalid line!\n", cmd->lineno); + return -EINVAL; + } + + cmd->cmd = rptr; + + /* DCD */ + if (!strcmp(tok, "DCD")) { + ictx->in_section = 0; + ictx->in_dcd = 1; + sb_build_dcd(ictx, cmd); + return 0; + } + + /* Section */ + if (!strcmp(tok, "SECTION")) { + ictx->in_section = 1; + ictx->in_dcd = 0; + sb_build_section(ictx, cmd); + return 0; + } + + if (!ictx->in_section && !ictx->in_dcd) { + fprintf(stderr, "#%i ERR: Data outside of a section!\n", + cmd->lineno); + return -EINVAL; + } + + if (ictx->in_section) { + /* Section commands */ + if (!strcmp(tok, "NOP")) { + ret = sb_build_command_nop(ictx); + } else if (!strcmp(tok, "TAG")) { + ret = sb_build_command_tag(ictx, cmd); + } else if (!strcmp(tok, "LOAD")) { + ret = sb_build_command_load(ictx, cmd); + } else if (!strcmp(tok, "FILL")) { + ret = sb_build_command_fill(ictx, cmd); + } else if (!strcmp(tok, "JUMP")) { + ret = sb_build_command_jump(ictx, cmd); + } else if (!strcmp(tok, "CALL")) { + ret = sb_build_command_call(ictx, cmd); + } else if (!strcmp(tok, "MODE")) { + ret = sb_build_command_mode(ictx, cmd); + } else { + fprintf(stderr, + "#%i ERR: Unsupported instruction '%s'!\n", + cmd->lineno, tok); + return -ENOTSUP; + } + } else if (ictx->in_dcd) { + char *lptr; + uint32_t ilen = '1'; + + tok = strtok_r(tok, ".", &lptr); + if (!tok || (strlen(tok) == 0) || (lptr && strlen(lptr) != 1)) { + fprintf(stderr, "#%i ERR: Invalid line!\n", + cmd->lineno); + return -EINVAL; + } + + if (lptr && + (lptr[0] != '1' && lptr[0] != '2' && lptr[0] != '4')) { + fprintf(stderr, "#%i ERR: Invalid instruction width!\n", + cmd->lineno); + return -EINVAL; + } + + if (lptr) + ilen = lptr[0] - '1'; + + /* DCD commands */ + if (!strcmp(tok, "WRITE")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_WRITE | ilen); + } else if (!strcmp(tok, "ANDC")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_ANDC | ilen); + } else if (!strcmp(tok, "ORR")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_ORR | ilen); + } else if (!strcmp(tok, "EQZ")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_CHK_EQZ | ilen); + } else if (!strcmp(tok, "EQ")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_CHK_EQ | ilen); + } else if (!strcmp(tok, "NEQ")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_CHK_NEQ | ilen); + } else if (!strcmp(tok, "NEZ")) { + ret = sb_build_dcd_block(ictx, cmd, + SB_DCD_CHK_NEZ | ilen); + } else if (!strcmp(tok, "NOOP")) { + ret = sb_build_dcd_block(ictx, cmd, SB_DCD_NOOP); + } else { + fprintf(stderr, + "#%i ERR: Unsupported instruction '%s'!\n", + cmd->lineno, tok); + return -ENOTSUP; + } + } else { + fprintf(stderr, "#%i ERR: Unsupported instruction '%s'!\n", + cmd->lineno, tok); + return -ENOTSUP; + } + + /* + * Here we have at least one section with one command, otherwise we + * would have failed already higher above. + * + * FIXME -- should the updating happen here ? + */ + if (ictx->in_section && !ret) { + ictx->sect_tail->size += ictx->sect_tail->cmd_tail->size; + ictx->sect_tail->payload.section_size = + ictx->sect_tail->size / SB_BLOCK_SIZE; + } + + return ret; +} + +static int sb_load_cmdfile(struct sb_image_ctx *ictx) +{ + struct sb_cmd_list cmd; + int lineno = 1, ret; + FILE *fp; + char *line = NULL; + ssize_t rlen; + size_t len; + + fp = fopen(ictx->cfg_filename, "r"); + if (!fp) + goto err_file; + + while ((rlen = getline(&line, &len, fp)) > 0) { + memset(&cmd, 0, sizeof(cmd)); + + /* Strip the trailing newline. */ + line[rlen - 1] = '\0'; + + cmd.cmd = line; + cmd.len = rlen; + cmd.lineno = lineno++; + + ret = sb_parse_line(ictx, &cmd); + if (ret) + goto out; + } + + ret = 0; +out: + free(line); + + fclose(fp); + + return ret; + +err_file: + fprintf(stderr, "ERR: Failed to load file \"%s\"\n", + ictx->cfg_filename); + return -EINVAL; +} + +static int sb_build_tree_from_cfg(struct sb_image_ctx *ictx) +{ + int ret; + + ret = sb_load_cmdfile(ictx); + if (ret) + return ret; + + ret = sb_prefill_image_header(ictx); + if (ret) + return ret; + + ret = sb_postfill_image_header(ictx); + if (ret) + return ret; + + ret = sb_fixup_sections_and_tags(ictx); + if (ret) + return ret; + + return 0; +} + +static int sb_verify_image_header(struct sb_image_ctx *ictx, + FILE *fp, long fsize) +{ + /* Verify static fields in the image header. */ + struct sb_boot_image_header *hdr = &ictx->payload; + const char *stat[2] = { "[PASS]", "[FAIL]" }; + struct tm tm; + int sz, ret = 0; + unsigned char digest[20]; + EVP_MD_CTX md_ctx; + unsigned long size; + + /* Start image-wide crypto. */ + EVP_MD_CTX_init(&ictx->md_ctx); + EVP_DigestInit(&ictx->md_ctx, EVP_sha1()); + + soprintf(ictx, "---------- Verifying SB Image Header ----------\n"); + + size = fread(&ictx->payload, 1, sizeof(ictx->payload), fp); + if (size != sizeof(ictx->payload)) { + fprintf(stderr, "ERR: SB image header too short!\n"); + return -EINVAL; + } + + /* Compute header digest. */ + EVP_MD_CTX_init(&md_ctx); + EVP_DigestInit(&md_ctx, EVP_sha1()); + EVP_DigestUpdate(&md_ctx, hdr->signature1, + sizeof(struct sb_boot_image_header) - + sizeof(hdr->digest)); + EVP_DigestFinal(&md_ctx, digest, NULL); + + sb_aes_init(ictx, NULL, 1); + sb_encrypt_sb_header(ictx); + + if (memcmp(digest, hdr->digest, 20)) + ret = -EINVAL; + soprintf(ictx, "%s Image header checksum: %s\n", stat[!!ret], + ret ? "BAD" : "OK"); + if (ret) + return ret; + + if (memcmp(hdr->signature1, "STMP", 4) || + memcmp(hdr->signature2, "sgtl", 4)) + ret = -EINVAL; + soprintf(ictx, "%s Signatures: '%.4s' '%.4s'\n", + stat[!!ret], hdr->signature1, hdr->signature2); + if (ret) + return ret; + + if ((hdr->major_version != SB_VERSION_MAJOR) || + ((hdr->minor_version != 1) && (hdr->minor_version != 2))) + ret = -EINVAL; + soprintf(ictx, "%s Image version: v%i.%i\n", stat[!!ret], + hdr->major_version, hdr->minor_version); + if (ret) + return ret; + + ret = sb_get_time(hdr->timestamp_us / 1000000, &tm); + soprintf(ictx, + "%s Creation time: %02i:%02i:%02i %02i/%02i/%04i\n", + stat[!!ret], tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_mday, tm.tm_mon, tm.tm_year + 2000); + if (ret) + return ret; + + soprintf(ictx, "%s Product version: %x.%x.%x\n", stat[0], + ntohs(hdr->product_version.major), + ntohs(hdr->product_version.minor), + ntohs(hdr->product_version.revision)); + soprintf(ictx, "%s Component version: %x.%x.%x\n", stat[0], + ntohs(hdr->component_version.major), + ntohs(hdr->component_version.minor), + ntohs(hdr->component_version.revision)); + + if (hdr->flags & ~SB_IMAGE_FLAG_VERBOSE) + ret = -EINVAL; + soprintf(ictx, "%s Image flags: %s\n", stat[!!ret], + hdr->flags & SB_IMAGE_FLAG_VERBOSE ? "Verbose_boot" : ""); + if (ret) + return ret; + + if (hdr->drive_tag != 0) + ret = -EINVAL; + soprintf(ictx, "%s Drive tag: %i\n", stat[!!ret], + hdr->drive_tag); + if (ret) + return ret; + + sz = sizeof(struct sb_boot_image_header) / SB_BLOCK_SIZE; + if (hdr->header_blocks != sz) + ret = -EINVAL; + soprintf(ictx, "%s Image header size (blocks): %i\n", stat[!!ret], + hdr->header_blocks); + if (ret) + return ret; + + sz = sizeof(struct sb_sections_header) / SB_BLOCK_SIZE; + if (hdr->section_header_size != sz) + ret = -EINVAL; + soprintf(ictx, "%s Section header size (blocks): %i\n", stat[!!ret], + hdr->section_header_size); + if (ret) + return ret; + + soprintf(ictx, "%s Sections count: %i\n", stat[!!ret], + hdr->section_count); + soprintf(ictx, "%s First bootable section %i\n", stat[!!ret], + hdr->first_boot_section_id); + + if (hdr->image_blocks != fsize / SB_BLOCK_SIZE) + ret = -EINVAL; + soprintf(ictx, "%s Image size (blocks): %i\n", stat[!!ret], + hdr->image_blocks); + if (ret) + return ret; + + sz = hdr->header_blocks + hdr->section_header_size * hdr->section_count; + if (hdr->key_dictionary_block != sz) + ret = -EINVAL; + soprintf(ictx, "%s Key dict offset (blocks): %i\n", stat[!!ret], + hdr->key_dictionary_block); + if (ret) + return ret; + + if (hdr->key_count != 1) + ret = -EINVAL; + soprintf(ictx, "%s Number of encryption keys: %i\n", stat[!!ret], + hdr->key_count); + if (ret) + return ret; + + sz = hdr->header_blocks + hdr->section_header_size * hdr->section_count; + sz += hdr->key_count * + sizeof(struct sb_key_dictionary_key) / SB_BLOCK_SIZE; + if (hdr->first_boot_tag_block != (unsigned)sz) + ret = -EINVAL; + soprintf(ictx, "%s First TAG block (blocks): %i\n", stat[!!ret], + hdr->first_boot_tag_block); + if (ret) + return ret; + + return 0; +} + +static void sb_decrypt_tag(struct sb_image_ctx *ictx, + struct sb_cmd_ctx *cctx) +{ + EVP_MD_CTX *md_ctx = &ictx->md_ctx; + struct sb_command *cmd = &cctx->payload; + + sb_aes_crypt(ictx, (uint8_t *)&cctx->c_payload, + (uint8_t *)&cctx->payload, sizeof(*cmd)); + EVP_DigestUpdate(md_ctx, &cctx->c_payload, sizeof(*cmd)); +} + +static int sb_verify_command(struct sb_image_ctx *ictx, + struct sb_cmd_ctx *cctx, FILE *fp, + unsigned long *tsize) +{ + struct sb_command *ccmd = &cctx->payload; + unsigned long size, asize; + char *csum, *flag = ""; + int ret; + unsigned int i; + uint8_t csn, csc = ccmd->header.checksum; + ccmd->header.checksum = 0x5a; + csn = sb_command_checksum(ccmd); + ccmd->header.checksum = csc; + + if (csc == csn) + ret = 0; + else + ret = -EINVAL; + csum = ret ? "checksum BAD" : "checksum OK"; + + switch (ccmd->header.tag) { + case ROM_NOP_CMD: + soprintf(ictx, " NOOP # %s\n", csum); + return ret; + case ROM_TAG_CMD: + if (ccmd->header.flags & ROM_TAG_CMD_FLAG_ROM_LAST_TAG) + flag = "LAST"; + soprintf(ictx, " TAG %s # %s\n", flag, csum); + sb_aes_reinit(ictx, 0); + return ret; + case ROM_LOAD_CMD: + soprintf(ictx, " LOAD addr=0x%08x length=0x%08x # %s\n", + ccmd->load.address, ccmd->load.count, csum); + + cctx->length = ccmd->load.count; + asize = roundup(cctx->length, SB_BLOCK_SIZE); + cctx->data = malloc(asize); + if (!cctx->data) + return -ENOMEM; + + size = fread(cctx->data, 1, asize, fp); + if (size != asize) { + fprintf(stderr, + "ERR: SB LOAD command payload too short!\n"); + return -EINVAL; + } + + *tsize += size; + + EVP_DigestUpdate(&ictx->md_ctx, cctx->data, asize); + sb_aes_crypt(ictx, cctx->data, cctx->data, asize); + + if (ccmd->load.crc32 != pbl_crc32(0, + (const char *)cctx->data, + asize)) { + fprintf(stderr, + "ERR: SB LOAD command payload CRC32 invalid!\n"); + return -EINVAL; + } + return 0; + case ROM_FILL_CMD: + soprintf(ictx, + " FILL addr=0x%08x length=0x%08x pattern=0x%08x # %s\n", + ccmd->fill.address, ccmd->fill.count, + ccmd->fill.pattern, csum); + return 0; + case ROM_JUMP_CMD: + if (ccmd->header.flags & ROM_JUMP_CMD_FLAG_HAB) + flag = " HAB"; + soprintf(ictx, + " JUMP%s addr=0x%08x r0_arg=0x%08x # %s\n", + flag, ccmd->fill.address, ccmd->jump.argument, csum); + return 0; + case ROM_CALL_CMD: + if (ccmd->header.flags & ROM_CALL_CMD_FLAG_HAB) + flag = " HAB"; + soprintf(ictx, + " CALL%s addr=0x%08x r0_arg=0x%08x # %s\n", + flag, ccmd->fill.address, ccmd->jump.argument, csum); + return 0; + case ROM_MODE_CMD: + for (i = 0; i < ARRAY_SIZE(modetable); i++) { + if (ccmd->mode.mode == modetable[i].mode) { + soprintf(ictx, " MODE %s # %s\n", + modetable[i].name, csum); + break; + } + } + fprintf(stderr, " MODE !INVALID! # %s\n", csum); + return 0; + } + + return ret; +} + +static int sb_verify_commands(struct sb_image_ctx *ictx, + struct sb_section_ctx *sctx, FILE *fp) +{ + unsigned long size, tsize = 0; + struct sb_cmd_ctx *cctx; + int ret; + + sb_aes_reinit(ictx, 0); + + while (tsize < sctx->size) { + cctx = calloc(1, sizeof(*cctx)); + if (!cctx) + return -ENOMEM; + if (!sctx->cmd_head) { + sctx->cmd_head = cctx; + sctx->cmd_tail = cctx; + } else { + sctx->cmd_tail->cmd = cctx; + sctx->cmd_tail = cctx; + } + + size = fread(&cctx->c_payload, 1, sizeof(cctx->c_payload), fp); + if (size != sizeof(cctx->c_payload)) { + fprintf(stderr, "ERR: SB command header too short!\n"); + return -EINVAL; + } + + tsize += size; + + sb_decrypt_tag(ictx, cctx); + + ret = sb_verify_command(ictx, cctx, fp, &tsize); + if (ret) + return -EINVAL; + } + + return 0; +} + +static int sb_verify_sections_cmds(struct sb_image_ctx *ictx, FILE *fp) +{ + struct sb_boot_image_header *hdr = &ictx->payload; + struct sb_sections_header *shdr; + unsigned int i; + int ret; + struct sb_section_ctx *sctx; + unsigned long size; + char *bootable = ""; + + soprintf(ictx, "----- Verifying SB Sections and Commands -----\n"); + + for (i = 0; i < hdr->section_count; i++) { + sctx = calloc(1, sizeof(*sctx)); + if (!sctx) + return -ENOMEM; + if (!ictx->sect_head) { + ictx->sect_head = sctx; + ictx->sect_tail = sctx; + } else { + ictx->sect_tail->sect = sctx; + ictx->sect_tail = sctx; + } + + size = fread(&sctx->payload, 1, sizeof(sctx->payload), fp); + if (size != sizeof(sctx->payload)) { + fprintf(stderr, "ERR: SB section header too short!\n"); + return -EINVAL; + } + } + + size = fread(&ictx->sb_dict_key, 1, sizeof(ictx->sb_dict_key), fp); + if (size != sizeof(ictx->sb_dict_key)) { + fprintf(stderr, "ERR: SB key dictionary too short!\n"); + return -EINVAL; + } + + sb_encrypt_sb_sections_header(ictx); + sb_aes_reinit(ictx, 0); + sb_decrypt_key_dictionary_key(ictx); + + sb_aes_reinit(ictx, 0); + + sctx = ictx->sect_head; + while (sctx) { + shdr = &sctx->payload; + + if (shdr->section_flags & SB_SECTION_FLAG_BOOTABLE) { + sctx->boot = 1; + bootable = " BOOTABLE"; + } + + sctx->size = (shdr->section_size * SB_BLOCK_SIZE) + + sizeof(struct sb_command); + soprintf(ictx, "SECTION 0x%x%s # size = %i bytes\n", + shdr->section_number, bootable, sctx->size); + + if (shdr->section_flags & ~SB_SECTION_FLAG_BOOTABLE) + fprintf(stderr, " WARN: Unknown section flag(s) %08x\n", + shdr->section_flags); + + if ((shdr->section_flags & SB_SECTION_FLAG_BOOTABLE) && + (hdr->first_boot_section_id != shdr->section_number)) { + fprintf(stderr, + " WARN: Bootable section does ID not match image header ID!\n"); + } + + ret = sb_verify_commands(ictx, sctx, fp); + if (ret) + return ret; + + sctx = sctx->sect; + } + + /* + * FIXME IDEA: + * check if the first TAG command is at sctx->section_offset + */ + return 0; +} + +static int sb_verify_image_end(struct sb_image_ctx *ictx, + FILE *fp, off_t filesz) +{ + uint8_t digest[32]; + unsigned long size; + off_t pos; + int ret; + + soprintf(ictx, "------------- Verifying image end -------------\n"); + + size = fread(digest, 1, sizeof(digest), fp); + if (size != sizeof(digest)) { + fprintf(stderr, "ERR: SB key dictionary too short!\n"); + return -EINVAL; + } + + pos = ftell(fp); + if (pos != filesz) { + fprintf(stderr, "ERR: Trailing data past the image!\n"); + return -EINVAL; + } + + /* Check the image digest. */ + EVP_DigestFinal(&ictx->md_ctx, ictx->digest, NULL); + + /* Decrypt the image digest from the input image. */ + sb_aes_reinit(ictx, 0); + sb_aes_crypt(ictx, digest, digest, sizeof(digest)); + + /* Check all of 20 bytes of the SHA1 hash. */ + ret = memcmp(digest, ictx->digest, 20) ? -EINVAL : 0; + + if (ret) + soprintf(ictx, "[FAIL] Full-image checksum: BAD\n"); + else + soprintf(ictx, "[PASS] Full-image checksum: OK\n"); + + return ret; +} + + +static int sb_build_tree_from_img(struct sb_image_ctx *ictx) +{ + long filesize; + int ret; + FILE *fp; + + if (!ictx->input_filename) { + fprintf(stderr, "ERR: Missing filename!\n"); + return -EINVAL; + } + + fp = fopen(ictx->input_filename, "r"); + if (!fp) + goto err_open; + + ret = fseek(fp, 0, SEEK_END); + if (ret < 0) + goto err_file; + + filesize = ftell(fp); + if (filesize < 0) + goto err_file; + + ret = fseek(fp, 0, SEEK_SET); + if (ret < 0) + goto err_file; + + if (filesize < (signed)sizeof(ictx->payload)) { + fprintf(stderr, "ERR: File too short!\n"); + goto err_file; + } + + /* Load and verify image header */ + ret = sb_verify_image_header(ictx, fp, filesize); + if (ret) + goto err_verify; + + /* Load and verify sections and commands */ + ret = sb_verify_sections_cmds(ictx, fp); + if (ret) + goto err_verify; + + ret = sb_verify_image_end(ictx, fp, filesize); + if (ret) + goto err_verify; + + ret = 0; + +err_verify: + soprintf(ictx, "-------------------- Result -------------------\n"); + soprintf(ictx, "Verification %s\n", ret ? "FAILED" : "PASSED"); + + /* Stop the encryption session. */ + sb_aes_deinit(&ictx->cipher_ctx); + + fclose(fp); + return ret; + +err_file: + fclose(fp); +err_open: + fprintf(stderr, "ERR: Failed to load file \"%s\"\n", + ictx->input_filename); + return -EINVAL; +} + +static void sb_free_image(struct sb_image_ctx *ictx) +{ + struct sb_section_ctx *sctx = ictx->sect_head, *s_head; + struct sb_dcd_ctx *dctx = ictx->dcd_head, *d_head; + struct sb_cmd_ctx *cctx, *c_head; + + while (sctx) { + s_head = sctx; + c_head = sctx->cmd_head; + + while (c_head) { + cctx = c_head; + c_head = c_head->cmd; + if (cctx->data) + free(cctx->data); + free(cctx); + } + + sctx = sctx->sect; + free(s_head); + } + + while (dctx) { + d_head = dctx; + dctx = dctx->dcd; + free(d_head->payload); + free(d_head); + } +} + +static int mxsimage_verify_print_header(char *file, int silent) +{ + int ret; + struct sb_image_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + + ctx.input_filename = file; + ctx.silent_dump = silent; + + ret = sb_build_tree_from_img(&ctx); + sb_free_image(&ctx); + + return ret; +} + +static int sb_build_image(struct sb_image_ctx *ictx) +{ + struct sb_boot_image_header *sb_header = &ictx->payload; + struct sb_section_ctx *sctx; + struct sb_cmd_ctx *cctx; + struct sb_command *ccmd; + struct sb_key_dictionary_key *sb_dict_key = &ictx->sb_dict_key; + int fd; + ssize_t now; + + uint8_t *image, *iptr; + + /* Calculate image size. */ + uint32_t size = sizeof(*sb_header) + + ictx->sect_count * sizeof(struct sb_sections_header) + + sizeof(*sb_dict_key) + sizeof(ictx->digest); + + sctx = ictx->sect_head; + while (sctx) { + size += sctx->size; + sctx = sctx->sect; + }; + + image = malloc(size); + if (!image) + return -ENOMEM; + iptr = image; + + memcpy(iptr, sb_header, sizeof(*sb_header)); + iptr += sizeof(*sb_header); + + sctx = ictx->sect_head; + while (sctx) { + memcpy(iptr, &sctx->payload, sizeof(struct sb_sections_header)); + iptr += sizeof(struct sb_sections_header); + sctx = sctx->sect; + }; + + memcpy(iptr, sb_dict_key, sizeof(*sb_dict_key)); + iptr += sizeof(*sb_dict_key); + + sctx = ictx->sect_head; + while (sctx) { + cctx = sctx->cmd_head; + while (cctx) { + ccmd = &cctx->payload; + + memcpy(iptr, &cctx->c_payload, sizeof(cctx->payload)); + iptr += sizeof(cctx->payload); + + if (ccmd->header.tag == ROM_LOAD_CMD) { + memcpy(iptr, cctx->data, cctx->length); + iptr += cctx->length; + } + + cctx = cctx->cmd; + } + + sctx = sctx->sect; + }; + + memcpy(iptr, ictx->digest, sizeof(ictx->digest)); + iptr += sizeof(ictx->digest); + + fd = open(ictx->output_filename, O_WRONLY | O_CREAT, 0644); + if (fd < 0) + return -errno; + + while (size) { + now = write(fd, image, size); + if (now < 0) + return -errno; + image += now; + size -= now; + } + + close(fd); + + return 0; +} + +static int mxsimage_generate(const char *configfile, const char *imagefile) +{ + int ret; + struct sb_image_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + + ctx.cfg_filename = configfile; + ctx.output_filename = imagefile; + ctx.verbose_boot = 1; + + ret = sb_build_tree_from_cfg(&ctx); + if (ret) + goto fail; + + ret = sb_encrypt_image(&ctx); + if (!ret) + ret = sb_build_image(&ctx); + +fail: + sb_free_image(&ctx); + + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret; + int opt; + char *configfile = NULL, *outfile = NULL, *verify = NULL; + + while ((opt = getopt(argc, argv, "p:b:c:o:v:")) != -1) { + switch (opt) { + case 'p': + prepfile = optarg; + break; + case 'b': + bootloaderfile = optarg; + break; + case 'c': + configfile = optarg; + break; + case 'o': + outfile = optarg; + break; + case 'v': + verify = optarg; + break; + default: + exit(1); + } + } + + if (verify) { + ret = mxsimage_verify_print_header(verify, 0); + if (ret) + exit(1); + else + exit(0); + } + + if (!configfile) { + fprintf(stderr, "Configfile missing\n"); + exit(1); + } + + if (!outfile) { + fprintf(stderr, "outfile missing\n"); + exit(1); + } + + ret = mxsimage_generate(configfile, outfile); + if (ret) + exit(1); + + return 0; +}