[U-Boot] [PATCH 14/20] M28: Add MMC SPL

Stefano Babic
2011-11-10T13:13:58Z

On 11/09/2011 10:18 AM, Marek Vasut wrote:
> This patch adds SPL code for the M28 board.
>
> Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
> Cc: Andy Fleming <afleming@gmail.com>
> Cc: Stefano Babic <sbabic@denx.de>
> Cc: Wolfgang Denk <wd@denx.de>
> Cc: Detlev Zundel <dzu@denx.de>
> ---
> board/denx/m28evk/Makefile | 8 +
> board/denx/m28evk/m28_init.h | 41 ++
> board/denx/m28evk/mem_init.c | 213 +++++++++
> board/denx/m28evk/mmc_boot.c | 273 ++++++++++++
> board/denx/m28evk/power_init.c | 913 ++++++++++++++++++++++++++++++++++++++
> board/denx/m28evk/start.S | 396 +++++++++++++++++
> board/denx/m28evk/u-boot-spl.lds | 87 ++++
> board/denx/m28evk/u-boot.bd | 14 +
> include/configs/m28evk.h | 8 +
> 9 files changed, 1953 insertions(+), 0 deletions(-)
> create mode 100644 board/denx/m28evk/m28_init.h
> create mode 100644 board/denx/m28evk/mem_init.c
> create mode 100644 board/denx/m28evk/mmc_boot.c
> create mode 100644 board/denx/m28evk/power_init.c
> create mode 100644 board/denx/m28evk/start.S
> create mode 100644 board/denx/m28evk/u-boot-spl.lds
> create mode 100644 board/denx/m28evk/u-boot.bd
>
> diff --git a/board/denx/m28evk/Makefile b/board/denx/m28evk/Makefile
> index e6aba9a..47229e6 100644
> --- a/board/denx/m28evk/Makefile
> +++ b/board/denx/m28evk/Makefile
> @@ -25,7 +25,13 @@ include $(TOPDIR)/config.mk
>
> LIB = $(obj)lib$(BOARD).o
>
> +ifndef CONFIG_SPL_BUILD
> COBJS := m28evk.o
> +endif
> +
> +ifdef CONFIG_SPL_BUILD
> +COBJS := mem_init.o mmc_boot.o power_init.o
> +endif
>
> SRCS := $(COBJS:.o=.c)
> OBJS := $(addprefix $(obj),$(COBJS))
> @@ -33,6 +39,8 @@ OBJS := $(addprefix $(obj),$(COBJS))
> $(LIB): $(obj).depend $(OBJS)
> $(call cmd_link_o_target, $(OBJS))
>
> +all: $(ALL)
> +
> #########################################################################
>
> # defines $(obj).depend target
> diff --git a/board/denx/m28evk/m28_init.h b/board/denx/m28evk/m28_init.h
> new file mode 100644
> index 0000000..98d3631
> --- /dev/null
> +++ b/board/denx/m28evk/m28_init.h
> @@ -0,0 +1,41 @@
> +/*
> + * Freescale i.MX28 SPL functions
> + *
> + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
> + * on behalf of DENX Software Engineering GmbH
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#ifndef __M28_INIT_H__
> +#define __M28_INIT_H__
> +
> +void early_delay(int delay);
> +
> +void mx28_power_init(void);
> +
> +#ifdef CONFIG_SPL_MX28_PSWITCH_WAIT
> +void mx28_power_wait_pswitch(void);
> +#else
> +static inline void mx28_power_wait_pswitch(void) { }
> +#endif
> +
> +void mx28_mem_init(void);
> +
> +#endif /* __M28_INIT_H__ */
> diff --git a/board/denx/m28evk/mem_init.c b/board/denx/m28evk/mem_init.c
> new file mode 100644
> index 0000000..066fe0d
> --- /dev/null
> +++ b/board/denx/m28evk/mem_init.c
> @@ -0,0 +1,213 @@
> +/*
> + * Freescale i.MX28 RAM init
> + *
> + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
> + * on behalf of DENX Software Engineering GmbH
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include <common.h>
> +#include <config.h>
> +#include <asm/io.h>
> +#include <asm/arch/iomux-mx28.h>
> +#include <asm/arch/imx-regs.h>
> +
> +#include "m28_init.h"
> +
> +uint32_t dram_vals[] = {
> + 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, 0x05060302, 0x06005003, 0x0a0000c8,
> + 0x02009c40, 0x0000030c, 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, 0xf5014b27, 0xf5014b27, 0xf5014b27, 0xf5014b27,
> + 0x07000300, 0x07000300, 0x07000300, 0x07000300, 0x00000006,
> + 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, 0x00020304, 0x00000004,
> + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
> + 0x00000000, 0x01010000, 0x01000000, 0x03030000, 0x00010303,
> + 0x01020202, 0x00000000, 0x02040303, 0x21002103, 0x00061200,
> + 0x06120612, 0x04320432, 0x04320432, 0x00040004, 0x00040004,
> + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010001
> +};
> +
> +void init_m28_200mhz_ddr2(void)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dram_vals); i++)
> + writel(dram_vals[i], MXS_DRAM_BASE + (4 * i));
> +}
> +
> +void mx28_mem_init_clock(void)
> +{
> + struct mx28_clkctrl_regs *clkctrl_regs =
> + (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
> +
> + /* Gate EMI clock */
> + writel(CLKCTRL_FRAC0_CLKGATEEMI,
> + &clkctrl_regs->hw_clkctrl_frac0_set);
> +
> + /* EMI = 205MHz */
> + writel(CLKCTRL_FRAC0_EMIFRAC_MASK,
> + &clkctrl_regs->hw_clkctrl_frac0_set);
> + writel((0x2a << CLKCTRL_FRAC0_EMIFRAC_OFFSET) &
> + CLKCTRL_FRAC0_EMIFRAC_MASK,
> + &clkctrl_regs->hw_clkctrl_frac0_clr);
> +
> + /* Ungate EMI clock */
> + writel(CLKCTRL_FRAC0_CLKGATEEMI,
> + &clkctrl_regs->hw_clkctrl_frac0_clr);
> +
> + early_delay(11000);
> +
> + 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);
> +
> + early_delay(10000);
> +}
> +
> +void mx28_mem_setup_cpu_and_hbus(void)
> +{
> + struct mx28_clkctrl_regs *clkctrl_regs =
> + (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
> +
> + /* CPU = 454MHz and ungate CPU clock */
> + clrsetbits_le32(&clkctrl_regs->hw_clkctrl_frac0,
> + CLKCTRL_FRAC0_CPUFRAC_MASK | CLKCTRL_FRAC0_CLKGATECPU,
> + 19 << CLKCTRL_FRAC0_CPUFRAC_OFFSET);
> +
> + /* 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);
> +
> + 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);
> +}
> +
> +void mx28_mem_setup_vdda(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +}
> +
> +void mx28_mem_setup_vddd(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_POWER_BASE;
> +
> + writel((0x1c << POWER_VDDDCTRL_TRG_OFFSET) |
> + (0x7 << POWER_VDDDCTRL_BO_OFFSET_OFFSET) |
> + POWER_VDDDCTRL_LINREG_OFFSET_1STEPS_BELOW,
> + &power_regs->hw_power_vdddctrl);
> +}
> +
> +void mx28_mem_init(void)
> +{
> + struct mx28_clkctrl_regs *clkctrl_regs =
> + (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
> + struct mx28_pinctrl_regs *pinctrl_regs =
> + (struct mx28_pinctrl_regs *)MXS_PINCTRL_BASE;
> +
> + /* Set DDR2 mode */
> + writel(PINCTRL_EMI_DS_CTRL_DDR_MODE_DDR2,
> + &pinctrl_regs->hw_pinctrl_emi_ds_ctrl_set);
> +
> + /* Power up PLL0 */
> + writel(CLKCTRL_PLL0CTRL0_POWER,
> + &clkctrl_regs->hw_clkctrl_pll0ctrl0_set);
> +
> + early_delay(11000);
> +
> + mx28_mem_init_clock();
> +
> + mx28_mem_setup_vdda();
> +
> + /*
> + * Configure the DRAM registers
> + */
> +
> + /* Clear START bit from DRAM_CTL16 */
> + clrbits_le32(MXS_DRAM_BASE + 0x40, 1);
> +
> + init_m28_200mhz_ddr2();
> +
> + /* Clear SREFRESH bit from DRAM_CTL17 */
> + clrbits_le32(MXS_DRAM_BASE + 0x44, 1);
> +
> + /* Set START bit in DRAM_CTL16 */
> + setbits_le32(MXS_DRAM_BASE + 0x40, 1);
> +
> + /* Wait for bit 20 (DRAM init complete) in DRAM_CTL58 */
> + while (!(readl(MXS_DRAM_BASE + 0xe8) & (1 << 20)))
> + ;
> +
> + mx28_mem_setup_vddd();
> +
> + early_delay(10000);
> +
> + mx28_mem_setup_cpu_and_hbus();
> +}
> diff --git a/board/denx/m28evk/mmc_boot.c b/board/denx/m28evk/mmc_boot.c
> new file mode 100644
> index 0000000..86d3ab5
> --- /dev/null
> +++ b/board/denx/m28evk/mmc_boot.c
> @@ -0,0 +1,273 @@
> +/*
> + * Freescale i.MX28 Boot setup
> + *
> + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
> + * on behalf of DENX Software Engineering GmbH
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include <common.h>
> +#include <config.h>
> +#include <asm/io.h>
> +#include <asm/arch/iomux-mx28.h>
> +
> +#include "m28_init.h"
> +
> +/*
> + * 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.
> + */
> +#define HW_DIGCTRL_MICROSECONDS 0x8001c0c0
> +void early_delay(int delay)
> +{
> + uint32_t st = readl(HW_DIGCTRL_MICROSECONDS);
> + st += delay;
> + while (st > readl(HW_DIGCTRL_MICROSECONDS))
> + ;
> +}
> +
> +#define MUX_CONFIG_LED (MXS_PAD_3V3 | MXS_PAD_4MA | MXS_PAD_NOPULL)
> +#define MUX_CONFIG_LCD (MXS_PAD_3V3 | MXS_PAD_4MA)
> +#define MUX_CONFIG_TSC (MXS_PAD_3V3 | MXS_PAD_8MA | MXS_PAD_PULLUP)
> +#define MUX_CONFIG_SSP0 (MXS_PAD_3V3 | MXS_PAD_12MA | MXS_PAD_PULLUP)
> +#define MUX_CONFIG_SSP2 (MXS_PAD_3V3 | MXS_PAD_4MA | MXS_PAD_PULLUP)
> +#define MUX_CONFIG_GPMI (MXS_PAD_1V8 | MXS_PAD_4MA | MXS_PAD_NOPULL)
> +#define MUX_CONFIG_ENET (MXS_PAD_3V3 | MXS_PAD_4MA | MXS_PAD_PULLUP)
> +#define MUX_CONFIG_EMI (MXS_PAD_3V3 | MXS_PAD_12MA | MXS_PAD_NOPULL)
> +
> +const iomux_cfg_t iomux_setup[] = {
> + /* LED */
> + MX28_PAD_ENET0_RXD3__GPIO_4_10 | MUX_CONFIG_LED,
> +
> + /* framebuffer */
> + MX28_PAD_LCD_D00__LCD_D0 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D01__LCD_D1 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D02__LCD_D2 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D03__LCD_D3 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D04__LCD_D4 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D05__LCD_D5 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D06__LCD_D6 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D07__LCD_D7 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D08__LCD_D8 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D09__LCD_D9 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D10__LCD_D10 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D11__LCD_D11 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D12__LCD_D12 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D13__LCD_D13 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D14__LCD_D14 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D15__LCD_D15 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D16__LCD_D16 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D17__LCD_D17 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D18__LCD_D18 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D19__LCD_D19 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D20__LCD_D20 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D21__LCD_D21 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D22__LCD_D22 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_D23__LCD_D23 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_RD_E__LCD_VSYNC | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_WR_RWN__LCD_HSYNC | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_RS__LCD_DOTCLK | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_CS__LCD_CS | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_VSYNC__LCD_VSYNC | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_HSYNC__LCD_HSYNC | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_DOTCLK__LCD_DOTCLK | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_ENABLE__GPIO_1_31 | MUX_CONFIG_LCD,
> + MX28_PAD_LCD_RESET__GPIO_3_30 | MUX_CONFIG_LCD,
> +
> + /* UART1 */
> + MX28_PAD_PWM0__DUART_RX,
> + MX28_PAD_PWM1__DUART_TX,
> + MX28_PAD_AUART0_TX__DUART_RTS,
> + MX28_PAD_AUART0_RX__DUART_CTS,
> +
> + /* UART2 */
> + MX28_PAD_AUART1_RX__AUART1_RX,
> + MX28_PAD_AUART1_TX__AUART1_TX,
> + MX28_PAD_AUART1_RTS__AUART1_RTS,
> + MX28_PAD_AUART1_CTS__AUART1_CTS,
> +
> + /* CAN */
> + MX28_PAD_GPMI_RDY2__CAN0_TX,
> + MX28_PAD_GPMI_RDY3__CAN0_RX,
> +
> + /* I2C */
> + MX28_PAD_I2C0_SCL__I2C0_SCL,
> + MX28_PAD_I2C0_SDA__I2C0_SDA,
> +
> + /* TSC2007 */
> + MX28_PAD_SAIF0_MCLK__GPIO_3_20 | MUX_CONFIG_TSC,
> +
> + /* MMC0 */
> + MX28_PAD_SSP0_DATA0__SSP0_D0 | MUX_CONFIG_SSP0,
> + MX28_PAD_SSP0_DATA1__SSP0_D1 | MUX_CONFIG_SSP0,
> + MX28_PAD_SSP0_DATA2__SSP0_D2 | MUX_CONFIG_SSP0,
> + MX28_PAD_SSP0_DATA3__SSP0_D3 | MUX_CONFIG_SSP0,
> + MX28_PAD_SSP0_DATA4__SSP0_D4 | MUX_CONFIG_SSP0,
> + MX28_PAD_SSP0_DATA5__SSP0_D5 | MUX_CONFIG_SSP0,
> + MX28_PAD_SSP0_DATA6__SSP0_D6 | MUX_CONFIG_SSP0,
> + MX28_PAD_SSP0_DATA7__SSP0_D7 | MUX_CONFIG_SSP0,
> + MX28_PAD_SSP0_CMD__SSP0_CMD | MUX_CONFIG_SSP0,
> + MX28_PAD_SSP0_DETECT__SSP0_CARD_DETECT |
> + (MXS_PAD_3V3 | MXS_PAD_12MA | MXS_PAD_NOPULL),
> + MX28_PAD_SSP0_SCK__SSP0_SCK |
> + (MXS_PAD_3V3 | MXS_PAD_12MA | MXS_PAD_NOPULL),
> + MX28_PAD_PWM3__GPIO_3_28 | MUX_CONFIG_SSP0, /* Power .. FIXME */
> + MX28_PAD_AUART2_CTS__GPIO_3_10, /* WP ... FIXME */
> +
> + /* GPMI NAND */
> + MX28_PAD_GPMI_D00__GPMI_D0 | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_D01__GPMI_D1 | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_D02__GPMI_D2 | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_D03__GPMI_D3 | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_D04__GPMI_D4 | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_D05__GPMI_D5 | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_D06__GPMI_D6 | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_D07__GPMI_D7 | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_CE0N__GPMI_CE0N | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_RDY0__GPMI_READY0 | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_RDN__GPMI_RDN |
> + (MXS_PAD_1V8 | MXS_PAD_8MA | MXS_PAD_PULLUP),
> + MX28_PAD_GPMI_WRN__GPMI_WRN | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_ALE__GPMI_ALE | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_CLE__GPMI_CLE | MUX_CONFIG_GPMI,
> + MX28_PAD_GPMI_RESETN__GPMI_RESETN | MUX_CONFIG_GPMI,
> +
> + /* FEC Ethernet */
> + MX28_PAD_ENET0_MDC__ENET0_MDC | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_MDIO__ENET0_MDIO | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_RX_EN__ENET0_RX_EN | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_TX_EN__ENET0_TX_EN | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_RXD0__ENET0_RXD0 | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_RXD1__ENET0_RXD1 | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_TXD0__ENET0_TXD0 | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_TXD1__ENET0_TXD1 | MUX_CONFIG_ENET,
> + MX28_PAD_ENET_CLK__CLKCTRL_ENET | MUX_CONFIG_ENET,
> +
> + MX28_PAD_ENET0_COL__ENET1_TX_EN | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_CRS__ENET1_RX_EN | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_RXD2__ENET1_RXD0 | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_RXD3__ENET1_RXD1 | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_TXD2__ENET1_TXD0 | MUX_CONFIG_ENET,
> + MX28_PAD_ENET0_TXD3__ENET1_TXD1 | MUX_CONFIG_ENET,
> +
> + /* I2C */
> + MX28_PAD_I2C0_SCL__I2C0_SCL,
> + MX28_PAD_I2C0_SDA__I2C0_SDA,
> +
> + /* EMI */
> + MX28_PAD_EMI_D00__EMI_DATA0 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D01__EMI_DATA1 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D02__EMI_DATA2 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D03__EMI_DATA3 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D04__EMI_DATA4 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D05__EMI_DATA5 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D06__EMI_DATA6 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D07__EMI_DATA7 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D08__EMI_DATA8 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D09__EMI_DATA9 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D10__EMI_DATA10 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D11__EMI_DATA11 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D12__EMI_DATA12 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D13__EMI_DATA13 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D14__EMI_DATA14 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_D15__EMI_DATA15 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_ODT0__EMI_ODT0 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_DQM0__EMI_DQM0 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_ODT1__EMI_ODT1 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_DQM1__EMI_DQM1 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_DDR_OPEN_FB__EMI_DDR_OPEN_FEEDBACK | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_CLK__EMI_CLK | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_DQS0__EMI_DQS0 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_DQS1__EMI_DQS1 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_DDR_OPEN__EMI_DDR_OPEN | MUX_CONFIG_EMI,
> +
> + MX28_PAD_EMI_A00__EMI_ADDR0 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A01__EMI_ADDR1 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A02__EMI_ADDR2 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A03__EMI_ADDR3 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A04__EMI_ADDR4 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A05__EMI_ADDR5 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A06__EMI_ADDR6 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A07__EMI_ADDR7 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A08__EMI_ADDR8 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A09__EMI_ADDR9 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A10__EMI_ADDR10 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A11__EMI_ADDR11 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A12__EMI_ADDR12 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A13__EMI_ADDR13 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_A14__EMI_ADDR14 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_BA0__EMI_BA0 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_BA1__EMI_BA1 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_BA2__EMI_BA2 | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_CASN__EMI_CASN | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_RASN__EMI_RASN | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_WEN__EMI_WEN | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_CE0N__EMI_CE0N | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_CE1N__EMI_CE1N | MUX_CONFIG_EMI,
> + MX28_PAD_EMI_CKE__EMI_CKE | MUX_CONFIG_EMI,
> +
> + /* SPI2 (for flash) */
> + MX28_PAD_SSP2_SCK__SSP2_SCK | MUX_CONFIG_SSP2,
> + MX28_PAD_SSP2_MOSI__SSP2_CMD | MUX_CONFIG_SSP2,
> + MX28_PAD_SSP2_MISO__SSP2_D0 | MUX_CONFIG_SSP2,
> + MX28_PAD_SSP2_SS0__SSP2_D3 |
> + (MXS_PAD_3V3 | MXS_PAD_8MA | MXS_PAD_PULLUP),
> +};
> +
> +void board_init_ll(void)
> +{
> + mxs_iomux_setup_multiple_pads(iomux_setup, ARRAY_SIZE(iomux_setup));
> + mx28_power_init();
> + mx28_mem_init();
> + mx28_power_wait_pswitch();
> +}
> +
> +/* Support aparatus */
> +inline void board_init_f(unsigned long bootflag)
> +{
> + for (;;)
> + ;
> +}
> +
> +inline void board_init_r(gd_t *id, ulong dest_addr)
> +{
> + for (;;)
> + ;
> +}
> +
> +inline int printf(const char *fmt, ...)
> +{
> + return 0;
> +}
> +
> +inline void __coloured_LED_init(void) {}
> +inline void __red_LED_on(void) {}
> +void coloured_LED_init(void)
> + __attribute__((weak, alias("__coloured_LED_init")));
> +void red_LED_on(void)
> + __attribute__((weak, alias("__red_LED_on")));
> +void hang(void) __attribute__ ((noreturn));
> +void hang(void)
> +{
> + for (;;)
> + ;
> +}
> diff --git a/board/denx/m28evk/power_init.c b/board/denx/m28evk/power_init.c
> new file mode 100644
> index 0000000..27322b4
> --- /dev/null
> +++ b/board/denx/m28evk/power_init.c
> @@ -0,0 +1,913 @@
> +/*
> + * Freescale i.MX28 Boot PMIC init
> + *
> + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
> + * on behalf of DENX Software Engineering GmbH
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include <common.h>
> +#include <config.h>
> +#include <asm/io.h>
> +#include <asm/arch/imx-regs.h>
> +
> +#include "m28_init.h"
> +
> +void mx28_power_clock2xtal(void)
> +{
> + struct mx28_clkctrl_regs *clkctrl_regs =
> + (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
> +
> + /* Set XTAL as CPU reference clock */
> + writel(CLKCTRL_CLKSEQ_BYPASS_CPU,
> + &clkctrl_regs->hw_clkctrl_clkseq_set);
> +}
> +
> +void mx28_power_clock2pll(void)
> +{
> + struct mx28_clkctrl_regs *clkctrl_regs =
> + (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
> +
> + writel(CLKCTRL_PLL0CTRL0_POWER,
> + &clkctrl_regs->hw_clkctrl_pll0ctrl0_set);
> + early_delay(100);
> + writel(CLKCTRL_CLKSEQ_BYPASS_CPU,
> + &clkctrl_regs->hw_clkctrl_clkseq_clr);
> +}
> +
> +void mx28_power_clear_auto_restart(void)
> +{
> + struct mx28_rtc_regs *rtc_regs =
> + (struct mx28_rtc_regs *)MXS_RTC_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)
> + ;
> +
> + /*
> + * Due to the hardware design bug of mx28 EVK-A
> + * we need to set the AUTO_RESTART bit.
> + */
> + 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)
> + ;
> +}
> +
> +void mx28_power_set_linreg(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +}
> +
> +void mx28_power_setup_5v_detect(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +}
> +
> +void mx28_src_power_init(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> + early_delay(30);
> + clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER);
> +}
> +
> +void mx28_power_init_4p2_params(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +}
> +
> +void mx28_enable_4p2_dcdc_input(int xfer)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> + 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);
> + }
> +
> + 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)
> + clrbits_le32(&power_regs->hw_power_ctrl,
> + POWER_CTRL_VBUS_VALID_IRQ);
> +
> + 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)
> + clrbits_le32(&power_regs->hw_power_ctrl,
> + POWER_CTRL_VDD5V_DROOP_IRQ);
> +
> + 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);
> +}
> +
> +void mx28_power_init_4p2_regulator(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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 */
> + writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK,
> + &power_regs->hw_power_5vctrl_clr);
> +
> + /*
> + * 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
> + */
> + mx28_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);
> + writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK,
> + &power_regs->hw_power_5vctrl_set);
> + 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;
> + 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);
> + 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);
> +}
> +
> +void mx28_power_init_dcdc_4p2_source(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_POWER_BASE;
> +
> + if (!(readl(&power_regs->hw_power_dcdc4p2) &
> + POWER_DCDC4P2_ENABLE_DCDC)) {
> + hang();
> + }
> +
> + mx28_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);
> + writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK,
> + &power_regs->hw_power_5vctrl_set);
> + }
> +}
> +
> +void mx28_power_enable_4p2(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +
> + mx28_power_init_4p2_params();
> + mx28_power_init_4p2_regulator();
> +
> + /* Shutdown battery (none present) */
> + 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);
> +
> + mx28_power_init_dcdc_4p2_source();
> +
> + writel(vdddctrl, &power_regs->hw_power_vdddctrl);
> + early_delay(20);
> + writel(vddactrl, &power_regs->hw_power_vddactrl);
> + 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);
> +}
> +
> +void mx28_boot_valid_5v(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +
> + mx28_power_enable_4p2();
> +}
> +
> +void mx28_powerdown(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +}
> +
> +void mx28_handle_5v_conflict(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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) {
> + mx28_powerdown();
> + break;
> + }
> +
> + if (tmp & POWER_STS_VDD5V_GT_VDDIO) {
> + mx28_boot_valid_5v();
> + break;
> + } else {
> + mx28_powerdown();
> + break;
> + }
> + }
> +}
> +
> +int mx28_get_batt_volt(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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;
> +}
> +
> +int mx28_is_batt_ready(void)
> +{
> + return (mx28_get_batt_volt() >= 3600);
> +}
> +
> +void mx28_5v_boot(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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) {
> + mx28_boot_valid_5v();
> + return;
> + }
> +
> + early_delay(1000);
> + if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) {
> + mx28_boot_valid_5v();
> + return;
> + }
> +
> + mx28_handle_5v_conflict();
> +}
> +
> +void mx28_init_batt_bo(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +}
> +
> +void mx28_switch_vddd_to_dcdc_source(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +}
> +
> +int mx28_is_batt_good(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_POWER_BASE;
> + uint32_t volt;
> +
> + volt = readl(&power_regs->hw_power_battmonitor);
> + volt &= POWER_BATTMONITOR_BATT_VAL_MASK;
> + volt >>= POWER_BATTMONITOR_BATT_VAL_OFFSET;
> + volt *= 8;
> +
> + 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);
> + writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK,
> + &power_regs->hw_power_5vctrl_clr);
> +
> + 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);
> + writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK,
> + &power_regs->hw_power_5vctrl_clr);
> +
> + early_delay(500000);
> +
> + volt = readl(&power_regs->hw_power_battmonitor);
> + volt &= POWER_BATTMONITOR_BATT_VAL_MASK;
> + volt >>= POWER_BATTMONITOR_BATT_VAL_OFFSET;
> + volt *= 8;
> +
> + 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;
> +}
> +
> +void mx28_power_configure_power_source(void)
> +{
> + mx28_src_power_init();
> +
> + mx28_5v_boot();
> + mx28_power_clock2pll();
> +
> + mx28_init_batt_bo();
> + mx28_switch_vddd_to_dcdc_source();
> +}
> +
> +void mx28_enable_output_rail_protection(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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);
> +}
> +
> +int mx28_get_vddio_power_source_off(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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_VDDDCTRL_LINREG_OFFSET_0STEPS) {
> + return 1;
> + }
> + }
> +
> + if (!(readl(&power_regs->hw_power_5vctrl) &
> + POWER_5VCTRL_ENABLE_DCDC)) {
> + if ((tmp & POWER_VDDIOCTRL_LINREG_OFFSET_MASK) ==
> + POWER_VDDDCTRL_LINREG_OFFSET_0STEPS) {
> + return 1;
> + }
> + }
> + }
> +
> + return 0;
> +
> +}
> +
> +int mx28_get_vddd_power_source_off(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_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;
> +}
> +
> +void mx28_power_set_vddio(uint32_t new_target, uint32_t new_brownout)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_POWER_BASE;
> + uint32_t cur_target, diff, bo_int = 0;
> + uint32_t powered_by_linreg = 0;
> +
> + new_brownout = new_target - new_brownout;
> +
> + cur_target = readl(&power_regs->hw_power_vddioctrl);
> + cur_target &= POWER_VDDIOCTRL_TRG_MASK;
> + cur_target *= 50; /* 50 mV step*/
> + cur_target += 2800; /* 2800 mV lowest */
> +
> + powered_by_linreg = mx28_get_vddio_power_source_off();
> + if (new_target > cur_target) {
> +
> + if (powered_by_linreg) {
> + bo_int = readl(&power_regs->hw_power_vddioctrl);
> + clrbits_le32(&power_regs->hw_power_vddioctrl,
> + POWER_CTRL_ENIRQ_VDDIO_BO);
> + }
> +
> + setbits_le32(&power_regs->hw_power_vddioctrl,
> + POWER_VDDIOCTRL_BO_OFFSET_MASK);
> + do {
> + if (new_target - cur_target > 100)
> + diff = cur_target + 100;
> + else
> + diff = new_target;
> +
> + diff -= 2800;
> + diff /= 50;
> +
> + clrsetbits_le32(&power_regs->hw_power_vddioctrl,
> + POWER_VDDIOCTRL_TRG_MASK, diff);
> +
> + if (powered_by_linreg)
> + early_delay(1500);
> + else {
> + while (!(readl(&power_regs->hw_power_sts) &
> + POWER_STS_DC_OK))
> + ;
> +
> + }
> +
> + cur_target = readl(&power_regs->hw_power_vddioctrl);
> + cur_target &= POWER_VDDIOCTRL_TRG_MASK;
> + cur_target *= 50; /* 50 mV step*/
> + cur_target += 2800; /* 2800 mV lowest */
> + } while (new_target > cur_target);
> +
> + if (powered_by_linreg) {
> + writel(POWER_CTRL_VDDIO_BO_IRQ,
> + &power_regs->hw_power_ctrl_clr);
> + if (bo_int & POWER_CTRL_ENIRQ_VDDIO_BO)
> + setbits_le32(&power_regs->hw_power_vddioctrl,
> + POWER_CTRL_ENIRQ_VDDIO_BO);
> + }
> + } else {
> + do {
> + if (cur_target - new_target > 100)
> + diff = cur_target - 100;
> + else
> + diff = new_target;
> +
> + diff -= 2800;
> + diff /= 50;
> +
> + clrsetbits_le32(&power_regs->hw_power_vddioctrl,
> + POWER_VDDIOCTRL_TRG_MASK, diff);
> +
> + if (powered_by_linreg)
> + early_delay(1500);
> + else {
> + while (!(readl(&power_regs->hw_power_sts) &
> + POWER_STS_DC_OK))
> + ;
> +
> + }
> +
> + cur_target = readl(&power_regs->hw_power_vddioctrl);
> + cur_target &= POWER_VDDIOCTRL_TRG_MASK;
> + cur_target *= 50; /* 50 mV step*/
> + cur_target += 2800; /* 2800 mV lowest */
> + } while (new_target < cur_target);
> + }
> +
> + clrsetbits_le32(&power_regs->hw_power_vddioctrl,
> + POWER_VDDDCTRL_BO_OFFSET_MASK,
> + new_brownout << POWER_VDDDCTRL_BO_OFFSET_OFFSET);
> +}
> +
> +void mx28_power_set_vddd(uint32_t new_target, uint32_t new_brownout)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_POWER_BASE;
> + uint32_t cur_target, diff, bo_int = 0;
> + uint32_t powered_by_linreg = 0;
> +
> + new_brownout = new_target - new_brownout;
> +
> + cur_target = readl(&power_regs->hw_power_vdddctrl);
> + cur_target &= POWER_VDDDCTRL_TRG_MASK;
> + cur_target *= 25; /* 25 mV step*/
> + cur_target += 800; /* 800 mV lowest */
> +
> + powered_by_linreg = mx28_get_vddd_power_source_off();
> + if (new_target > cur_target) {
> + if (powered_by_linreg) {
> + bo_int = readl(&power_regs->hw_power_vdddctrl);
> + clrbits_le32(&power_regs->hw_power_vdddctrl,
> + POWER_CTRL_ENIRQ_VDDD_BO);
> + }
> +
> + setbits_le32(&power_regs->hw_power_vdddctrl,
> + POWER_VDDDCTRL_BO_OFFSET_MASK);
> +
> + do {
> + if (new_target - cur_target > 100)
> + diff = cur_target + 100;
> + else
> + diff = new_target;
> +
> + diff -= 800;
> + diff /= 25;
> +
> + clrsetbits_le32(&power_regs->hw_power_vdddctrl,
> + POWER_VDDDCTRL_TRG_MASK, diff);
> +
> + if (powered_by_linreg)
> + early_delay(1500);
> + else {
> + while (!(readl(&power_regs->hw_power_sts) &
> + POWER_STS_DC_OK))
> + ;
> +
> + }
> +
> + cur_target = readl(&power_regs->hw_power_vdddctrl);
> + cur_target &= POWER_VDDDCTRL_TRG_MASK;
> + cur_target *= 25; /* 25 mV step*/
> + cur_target += 800; /* 800 mV lowest */
> + } while (new_target > cur_target);
> +
> + if (powered_by_linreg) {
> + writel(POWER_CTRL_VDDD_BO_IRQ,
> + &power_regs->hw_power_ctrl_clr);
> + if (bo_int & POWER_CTRL_ENIRQ_VDDD_BO)
> + setbits_le32(&power_regs->hw_power_vdddctrl,
> + POWER_CTRL_ENIRQ_VDDD_BO);
> + }
> + } else {
> + do {
> + if (cur_target - new_target > 100)
> + diff = cur_target - 100;
> + else
> + diff = new_target;
> +
> + diff -= 800;
> + diff /= 25;
> +
> + clrsetbits_le32(&power_regs->hw_power_vdddctrl,
> + POWER_VDDDCTRL_TRG_MASK, diff);
> +
> + if (powered_by_linreg)
> + early_delay(1500);
> + else {
> + while (!(readl(&power_regs->hw_power_sts) &
> + POWER_STS_DC_OK))
> + ;
> +
> + }
> +
> + cur_target = readl(&power_regs->hw_power_vdddctrl);
> + cur_target &= POWER_VDDDCTRL_TRG_MASK;
> + cur_target *= 25; /* 25 mV step*/
> + cur_target += 800; /* 800 mV lowest */
> + } while (new_target < cur_target);
> + }
> +
> + clrsetbits_le32(&power_regs->hw_power_vdddctrl,
> + POWER_VDDDCTRL_BO_OFFSET_MASK,
> + new_brownout << POWER_VDDDCTRL_BO_OFFSET_OFFSET);
> +}
> +
> +void mx28_power_init(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_POWER_BASE;
> +
> + mx28_power_clock2xtal();
> + mx28_power_clear_auto_restart();
> + mx28_power_set_linreg();
> + mx28_power_setup_5v_detect();
> + mx28_power_configure_power_source();
> + mx28_enable_output_rail_protection();
> +
> + mx28_power_set_vddio(3300, 3150);
> +
> + mx28_power_set_vddd(1350, 1200);
> +
> + 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);
> +
> + early_delay(1000);
> +}
> +
> +#ifdef CONFIG_SPL_MX28_PSWITCH_WAIT
> +void mx28_power_wait_pswitch(void)
> +{
> + struct mx28_power_regs *power_regs =
> + (struct mx28_power_regs *)MXS_POWER_BASE;
> +
> + while (!(readl(&power_regs->hw_power_sts) & POWER_STS_PSWITCH_MASK))
> + ;
> +}
> +#endif
> diff --git a/board/denx/m28evk/start.S b/board/denx/m28evk/start.S
> new file mode 100644
> index 0000000..cf67599
> --- /dev/null
> +++ b/board/denx/m28evk/start.S
> @@ -0,0 +1,396 @@
> +/*
> + * armboot - Startup Code for ARM926EJS CPU-core
> + *
> + * Copyright (c) 2003 Texas Instruments
> + *
> + * ----- Adapted for OMAP1610 OMAP730 from ARM925t code ------
> + *
> + * Copyright (c) 2001 Marius Groger <mag@sysgo.de>
> + * Copyright (c) 2002 Alex Zupke <azu@sysgo.de>
> + * Copyright (c) 2002 Gary Jennejohn <garyj@denx.de>
> + * Copyright (c) 2003 Richard Woodruff <r-woodruff2@ti.com>
> + * Copyright (c) 2003 Kshitij <kshitij@ti.com>
> + * Copyright (c) 2010 Albert Aribaud <albert.u.boot@aribaud.net>
> + *
> + * Change to support call back into iMX28 bootrom
> + * Copyright (c) 2011 Marek Vasut <marek.vasut@gmail.com>
> + * on behalf of DENX Software Engineering GmbH
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include <asm-offsets.h>
> +#include <config.h>
> +#include <common.h>
> +#include <version.h>
> +
> +#if defined(CONFIG_OMAP1610)
> +#include <./configs/omap1510.h>
> +#elif defined(CONFIG_OMAP730)
> +#include <./configs/omap730.h>
> +#endif
> +
> +/*
> + *************************************************************************
> + *
> + * Jump vector table as in table 3.1 in [1]
> + *
> + *************************************************************************
> + */
> +
> +
> +.globl _start
> +_start:
> + b reset
> +#ifdef CONFIG_SPL_BUILD
> +/* No exception handlers in preloader */
> + ldr pc, _hang
> + ldr pc, _hang
> + ldr pc, _hang
> + ldr pc, _hang
> + b reset
> + ldr pc, _hang
> + ldr pc, _hang
> +
> +_hang:
> + .word do_hang
> +/* pad to 64 byte boundary */
> + .word 0x12345678
> + .word 0x12345678
> + .word 0x12345678
> + .word 0x12345678
> + .word 0x12345678
> + .word 0x12345678
> + .word 0x12345678
> +#else
> + ldr pc, _undefined_instruction
> + ldr pc, _software_interrupt
> + ldr pc, _prefetch_abort
> + ldr pc, _data_abort
> + ldr pc, _not_used
> + ldr pc, _irq
> + ldr pc, _fiq
> +
> +_undefined_instruction:
> + .word undefined_instruction
> +_software_interrupt:
> + .word software_interrupt
> +_prefetch_abort:
> + .word prefetch_abort
> +_data_abort:
> + .word data_abort
> +_not_used:
> + .word not_used
> +_irq:
> + .word irq
> +_fiq:
> + .word fiq
> +
> +#endif /* CONFIG_SPL_BUILD */
> + .balignl 16,0xdeadbeef
> +
> +
> +/*
> + *************************************************************************
> + *
> + * Startup Code (reset vector)
> + *
> + * do important init only if we don't start from memory!
> + * setup Memory and board specific bits prior to relocation.
> + * relocate armboot to ram
> + * setup stack
> + *
> + *************************************************************************
> + */
> +
> +.globl _TEXT_BASE
> +_TEXT_BASE:
> + .word CONFIG_SYS_TEXT_BASE
> +
> +/*
> + * These are defined in the board-specific linker script.
> + * Subtracting _start from them lets the linker put their
> + * relative position in the executable instead of leaving
> + * them null.
> + */
> +.globl _bss_start_ofs
> +_bss_start_ofs:
> + .word __bss_start - _start
> +
> +.globl _bss_end_ofs
> +_bss_end_ofs:
> + .word __bss_end__ - _start
> +
> +.globl _end_ofs
> +_end_ofs:
> + .word _end - _start
> +
> +#ifdef CONFIG_USE_IRQ
> +/* IRQ stack memory (calculated at run-time) */
> +.globl IRQ_STACK_START
> +IRQ_STACK_START:
> + .word 0x0badc0de
> +
> +/* IRQ stack memory (calculated at run-time) */
> +.globl FIQ_STACK_START
> +FIQ_STACK_START:
> + .word 0x0badc0de
> +#endif
> +
> +/* IRQ stack memory (calculated at run-time) + 8 bytes */
> +.globl IRQ_STACK_START_IN
> +IRQ_STACK_START_IN:
> + .word 0x0badc0de
> +
> +/*
> + * the actual reset code
> + */
> +
> +reset:
> + /*
> + * Store all registers on old stack pointer, this will allow us later to
> + * return to the BootROM and let the BootROM load U-Boot into RAM.
> + */
> + push {r0-r12,r14}
> +
> + /*
> + * set the cpu to SVC32 mode
> + */
> + mrs r0,cpsr
> + bic r0,r0,#0x1f
> + orr r0,r0,#0xd3
> + msr cpsr,r0
> +
> + /*
> + * we do sys-critical inits only at reboot,
> + * not when booting from ram!
> + */
> +#ifndef CONFIG_SKIP_LOWLEVEL_INIT
> + bl cpu_init_crit
> +#endif
> +
> + bl board_init_ll
> +
> + pop {r0-r12,r14}
> + bx lr
> +
> +/*
> + *************************************************************************
> + *
> + * CPU_init_critical registers
> + *
> + * setup important registers
> + * setup memory timing
> + *
> + *************************************************************************
> + */
> +#ifndef CONFIG_SKIP_LOWLEVEL_INIT
> +cpu_init_crit:
> + /*
> + * flush v4 I/D caches
> + */
> + mov r0, #0
> + mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
> + mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
> +
> + /*
> + * disable MMU stuff and caches
> + */
> + mrc p15, 0, r0, c1, c0, 0
> + bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */
> + bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */
> + orr r0, r0, #0x00000002 /* set bit 2 (A) Align */
> + orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */
> + mcr p15, 0, r0, c1, c0, 0
> +
> + mov pc, lr /* back to my caller */
> +#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
> +
> +#ifndef CONFIG_SPL_BUILD
> +/*
> + *************************************************************************
> + *
> + * Interrupt handling
> + *
> + *************************************************************************
> + */
> +
> +@
> +@ IRQ stack frame.
> +@
> +#define S_FRAME_SIZE 72
> +
> +#define S_OLD_R0 68
> +#define S_PSR 64
> +#define S_PC 60
> +#define S_LR 56
> +#define S_SP 52
> +
> +#define S_IP 48
> +#define S_FP 44
> +#define S_R10 40
> +#define S_R9 36
> +#define S_R8 32
> +#define S_R7 28
> +#define S_R6 24
> +#define S_R5 20
> +#define S_R4 16
> +#define S_R3 12
> +#define S_R2 8
> +#define S_R1 4
> +#define S_R0 0
> +
> +#define MODE_SVC 0x13
> +#define I_BIT 0x80
> +
> +/*
> + * use bad_save_user_regs for abort/prefetch/undef/swi ...
> + * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
> + */
> +
> + .macro bad_save_user_regs
> + @ carve out a frame on current user stack
> + sub sp, sp, #S_FRAME_SIZE
> + stmia sp, {r0 - r12} @ Save user registers (now in svc mode) r0-r12
> + ldr r2, IRQ_STACK_START_IN
> + @ get values for "aborted" pc and cpsr (into parm regs)
> + ldmia r2, {r2 - r3}
> + add r0, sp, #S_FRAME_SIZE @ grab pointer to old stack
> + add r5, sp, #S_SP
> + mov r1, lr
> + stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
> + mov r0, sp @ save current stack into r0 (param register)
> + .endm
> +
> + .macro irq_save_user_regs
> + sub sp, sp, #S_FRAME_SIZE
> + stmia sp, {r0 - r12} @ Calling r0-r12
> + @ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good.
> + add r8, sp, #S_PC
> + stmdb r8, {sp, lr}^ @ Calling SP, LR
> + str lr, [r8, #0] @ Save calling PC
> + mrs r6, spsr
> + str r6, [r8, #4] @ Save CPSR
> + str r0, [r8, #8] @ Save OLD_R0
> + mov r0, sp
> + .endm
> +
> + .macro irq_restore_user_regs
> + ldmia sp, {r0 - lr}^ @ Calling r0 - lr
> + mov r0, r0
> + ldr lr, [sp, #S_PC] @ Get PC
> + add sp, sp, #S_FRAME_SIZE
> + subs pc, lr, #4 @ return & move spsr_svc into cpsr
> + .endm
> +
> + .macro get_bad_stack
> + ldr r13, IRQ_STACK_START_IN @ setup our mode stack
> +
> + str lr, [r13] @ save caller lr in position 0 of saved stack
> + mrs lr, spsr @ get the spsr
> + str lr, [r13, #4] @ save spsr in position 1 of saved stack
> + mov r13, #MODE_SVC @ prepare SVC-Mode
> + @ msr spsr_c, r13
> + msr spsr, r13 @ switch modes, make sure moves will execute
> + mov lr, pc @ capture return pc
> + movs pc, lr @ jump to next instruction & switch modes.
> + .endm
> +
> + .macro get_irq_stack @ setup IRQ stack
> + ldr sp, IRQ_STACK_START
> + .endm
> +
> + .macro get_fiq_stack @ setup FIQ stack
> + ldr sp, FIQ_STACK_START
> + .endm
> +#endif /* CONFIG_SPL_BUILD */
> +
> +/*
> + * exception handlers
> + */
> +#ifdef CONFIG_SPL_BUILD
> + .align 5
> +do_hang:
> + ldr sp, _TEXT_BASE /* switch to abort stack */
> +1:
> + bl 1b /* hang and never return */
> +#else /* !CONFIG_SPL_BUILD */
> + .align 5
> +undefined_instruction:
> + get_bad_stack
> + bad_save_user_regs
> + bl do_undefined_instruction
> +
> + .align 5
> +software_interrupt:
> + get_bad_stack
> + bad_save_user_regs
> + bl do_software_interrupt
> +
> + .align 5
> +prefetch_abort:
> + get_bad_stack
> + bad_save_user_regs
> + bl do_prefetch_abort
> +
> + .align 5
> +data_abort:
> + get_bad_stack
> + bad_save_user_regs
> + bl do_data_abort
> +
> + .align 5
> +not_used:
> + get_bad_stack
> + bad_save_user_regs
> + bl do_not_used
> +
> +#ifdef CONFIG_USE_IRQ
> +
> + .align 5
> +irq:
> + get_irq_stack
> + irq_save_user_regs
> + bl do_irq
> + irq_restore_user_regs
> +
> + .align 5
> +fiq:
> + get_fiq_stack
> + /* someone ought to write a more effiction fiq_save_user_regs */
> + irq_save_user_regs
> + bl do_fiq
> + irq_restore_user_regs
> +
> +#else
> +
> + .align 5
> +irq:
> + get_bad_stack
> + bad_save_user_regs
> + bl do_irq
> +
> + .align 5
> +fiq:
> + get_bad_stack
> + bad_save_user_regs
> + bl do_fiq
> +
> +#endif
> +#endif /* CONFIG_SPL_BUILD */
> diff --git a/board/denx/m28evk/u-boot-spl.lds b/board/denx/m28evk/u-boot-spl.lds
> new file mode 100644
> index 0000000..e296a92
> --- /dev/null
> +++ b/board/denx/m28evk/u-boot-spl.lds
> @@ -0,0 +1,87 @@
> +/*
> + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
> + * on behalf of DENX Software Engineering GmbH
> + *
> + * January 2004 - Changed to support H4 device
> + * Copyright (c) 2004-2008 Texas Instruments
> + *
> + * (C) Copyright 2002
> + * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
> +OUTPUT_ARCH(arm)
> +ENTRY(_start)
> +SECTIONS
> +{
> + . = 0x00000000;
> +
> + . = ALIGN(4);
> + .text :
> + {
> + board/denx/m28evk/start.o (.text)
> + *(.text)
> + }
> +
> + . = ALIGN(4);
> + .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
> +
> + . = ALIGN(4);
> + .data : {
> + *(.data)
> + }
> +
> + . = ALIGN(4);
> + __u_boot_cmd_start = .;
> + .u_boot_cmd : { *(.u_boot_cmd) }
> + __u_boot_cmd_end = .;
> +
> + . = ALIGN(4);
> +
> + .rel.dyn : {
> + __rel_dyn_start = .;
> + *(.rel*)
> + __rel_dyn_end = .;
> + }
> +
> + .dynsym : {
> + __dynsym_start = .;
> + *(.dynsym)
> + }
> +
> + _end = .;
> +
> + .bss __rel_dyn_start (OVERLAY) : {
> + __bss_start = .;
> + *(.bss)
> + . = ALIGN(4);
> + __bss_end__ = .;
> + }
> +
> + /DISCARD/ : { *(.bss*) }
> + /DISCARD/ : { *(.dynstr*) }
> + /DISCARD/ : { *(.dynsym*) }
> + /DISCARD/ : { *(.dynamic*) }
> + /DISCARD/ : { *(.hash*) }
> + /DISCARD/ : { *(.plt*) }
> + /DISCARD/ : { *(.interp*) }
> + /DISCARD/ : { *(.gnu*) }
> +}
> diff --git a/board/denx/m28evk/u-boot.bd b/board/denx/m28evk/u-boot.bd
> new file mode 100644
> index 0000000..3ce7f92
> --- /dev/null
> +++ b/board/denx/m28evk/u-boot.bd
> @@ -0,0 +1,14 @@
> +sources {
> + u_boot_spl="spl/u-boot-spl.bin";
> + u_boot="u-boot.bin";
> +}
> +
> +section (0) {
> + load u_boot_spl > 0x0000;
> + load ivt (entry = 0x0014) > 0x8000;
> + hab call 0x8000;
> +
> + load u_boot > 0x40000100;
> + load ivt (entry = 0x40000100) > 0x8000;
> + hab call 0x8000;
> +}
> diff --git a/include/configs/m28evk.h b/include/configs/m28evk.h
> index d88e09e..59e3e05 100644
> --- a/include/configs/m28evk.h
> +++ b/include/configs/m28evk.h
> @@ -43,6 +43,14 @@
> #define CONFIG_ARCH_CPU_INIT
>
> /*
> + * SPL
> + */
> +#define CONFIG_SPL
> +#define CONFIG_SPL_NO_CPU_SUPPORT_CODE
> +#define CONFIG_SPL_START_S_PATH "board/denx/m28evk"
> +#define CONFIG_SPL_LDSCRIPT "board/denx/m28evk/u-boot-spl.lds"
> +
> +/*
> * U-Boot Commands
> */
> #include <config_cmd_default.h>

Applied to u-boot-imx, thanks.

Best regards,
Stefano Babic

--
=====================================================================
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office at denx.de
=====================================================================