summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorMarek Vasut <marek.vasut@gmail.com>2011-12-02 03:47:40 +0000
committerAlbert ARIBAUD <albert.u.boot@aribaud.net>2011-12-09 17:30:10 +0100
commitc944a3ef8224a2d5936b31ebd72a6ebd842409d1 (patch)
treecb1c87948ae461cdded93c479983e9c472937637 /arch
parent74cf05fc53fc620c9d08eb14ad959c2a065f90c7 (diff)
downloadu-boot-imx-c944a3ef8224a2d5936b31ebd72a6ebd842409d1.zip
u-boot-imx-c944a3ef8224a2d5936b31ebd72a6ebd842409d1.tar.gz
u-boot-imx-c944a3ef8224a2d5936b31ebd72a6ebd842409d1.tar.bz2
i.MX28: Move SPL to arch/arm/cpu/arm926ejs/mx28
This moves SPL to common location so it can be reused by multiple boards. Also, this commit adjusts M28 SoM to avoid breakage due to the move. Signed-off-by: Marek Vasut <marek.vasut@gmail.com> Cc: Wolfgang Denk <wd@denx.de> Cc: Detlev Zundel <dzu@denx.de> Cc: Stefano Babic <sbabic@denx.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/cpu/arm926ejs/mx28/Makefile9
-rw-r--r--arch/arm/cpu/arm926ejs/mx28/mx28_init.h41
-rw-r--r--arch/arm/cpu/arm926ejs/mx28/spl_boot.c87
-rw-r--r--arch/arm/cpu/arm926ejs/mx28/spl_mem_init.c240
-rw-r--r--arch/arm/cpu/arm926ejs/mx28/spl_power_init.c913
-rw-r--r--arch/arm/cpu/arm926ejs/mx28/start.S234
-rw-r--r--arch/arm/cpu/arm926ejs/mx28/u-boot-spl.lds87
-rw-r--r--arch/arm/include/asm/arch-mx28/sys_proto.h6
8 files changed, 1617 insertions, 0 deletions
diff --git a/arch/arm/cpu/arm926ejs/mx28/Makefile b/arch/arm/cpu/arm926ejs/mx28/Makefile
index 7845310..372de8a 100644
--- a/arch/arm/cpu/arm926ejs/mx28/Makefile
+++ b/arch/arm/cpu/arm926ejs/mx28/Makefile
@@ -27,6 +27,10 @@ LIB = $(obj)lib$(SOC).o
COBJS = clock.o mx28.o iomux.o timer.o
+ifdef CONFIG_SPL_BUILD
+COBJS += memsize.o spl_boot.o spl_mem_init.o spl_power_init.o
+endif
+
SRCS := $(START:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
START := $(addprefix $(obj),$(START))
@@ -36,6 +40,11 @@ all: $(obj).depend $(LIB)
$(LIB): $(OBJS)
$(call cmd_link_o_target, $(OBJS))
+ifdef CONFIG_SPL_BUILD
+memsize.c:
+ ln -sf $(TOPDIR)/common/memsize.c $@
+endif
+
#########################################################################
# defines $(obj).depend target
diff --git a/arch/arm/cpu/arm926ejs/mx28/mx28_init.h b/arch/arm/cpu/arm926ejs/mx28/mx28_init.h
new file mode 100644
index 0000000..98d3631
--- /dev/null
+++ b/arch/arm/cpu/arm926ejs/mx28/mx28_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/arch/arm/cpu/arm926ejs/mx28/spl_boot.c b/arch/arm/cpu/arm926ejs/mx28/spl_boot.c
new file mode 100644
index 0000000..3cd4c24
--- /dev/null
+++ b/arch/arm/cpu/arm926ejs/mx28/spl_boot.c
@@ -0,0 +1,87 @@
+/*
+ * 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 <asm/arch/imx-regs.h>
+
+#include "mx28_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))
+ ;
+}
+
+void mx28_common_spl_init(const iomux_cfg_t *iomux_setup,
+ const unsigned int iomux_size)
+{
+ mxs_iomux_setup_multiple_pads(iomux_setup, iomux_size);
+ 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/arch/arm/cpu/arm926ejs/mx28/spl_mem_init.c b/arch/arm/cpu/arm926ejs/mx28/spl_mem_init.c
new file mode 100644
index 0000000..00493b8
--- /dev/null
+++ b/arch/arm/cpu/arm926ejs/mx28/spl_mem_init.c
@@ -0,0 +1,240 @@
+/*
+ * 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 "mx28_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);
+}
+
+#define HW_DIGCTRL_SCRATCH0 0x8001c280
+#define HW_DIGCTRL_SCRATCH1 0x8001c290
+void data_abort_memdetect_handler(void) __attribute__((naked));
+void data_abort_memdetect_handler(void)
+{
+ asm volatile("subs pc, r14, #4");
+}
+
+void mx28_mem_get_size(void)
+{
+ uint32_t sz, da;
+ uint32_t *vt = (uint32_t *)0x20;
+
+ /* Replace the DABT handler. */
+ da = vt[4];
+ vt[4] = (uint32_t)&data_abort_memdetect_handler;
+
+ sz = get_ram_size((long *)PHYS_SDRAM_1, PHYS_SDRAM_1_SIZE);
+ writel(sz, HW_DIGCTRL_SCRATCH0);
+ writel(sz, HW_DIGCTRL_SCRATCH1);
+
+ /* Restore the old DABT handler. */
+ vt[4] = da;
+}
+
+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();
+
+ mx28_mem_get_size();
+}
diff --git a/arch/arm/cpu/arm926ejs/mx28/spl_power_init.c b/arch/arm/cpu/arm926ejs/mx28/spl_power_init.c
new file mode 100644
index 0000000..a4dc2a3
--- /dev/null
+++ b/arch/arm/cpu/arm926ejs/mx28/spl_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 "mx28_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/arch/arm/cpu/arm926ejs/mx28/start.S b/arch/arm/cpu/arm926ejs/mx28/start.S
new file mode 100644
index 0000000..94696d6
--- /dev/null
+++ b/arch/arm/cpu/arm926ejs/mx28/start.S
@@ -0,0 +1,234 @@
+/*
+ * 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
+ b undefined_instruction
+ b software_interrupt
+ b prefetch_abort
+ b data_abort
+ b not_used
+ b irq
+ b fiq
+
+/*
+ * Vector table, located at address 0x20.
+ * This table allows the code running AFTER SPL, the U-Boot, to install it's
+ * interrupt handlers here. The problem is that the U-Boot is loaded into RAM,
+ * including it's interrupt vectoring table and the table at 0x0 is still the
+ * SPLs. So if interrupt happens in U-Boot, the SPLs interrupt vectoring table
+ * is still used.
+ */
+_vt_reset:
+ .word _reset
+_vt_undefined_instruction:
+ .word _hang
+_vt_software_interrupt:
+ .word _hang
+_vt_prefetch_abort:
+ .word _hang
+_vt_data_abort:
+ .word _hang
+_vt_not_used:
+ .word _reset
+_vt_irq:
+ .word _hang
+_vt_fiq:
+ .word _hang
+
+reset:
+ ldr pc, _vt_reset
+undefined_instruction:
+ ldr pc, _vt_undefined_instruction
+software_interrupt:
+ ldr pc, _vt_software_interrupt
+prefetch_abort:
+ ldr pc, _vt_prefetch_abort
+data_abort:
+ ldr pc, _vt_data_abort
+not_used:
+ ldr pc, _vt_not_used
+irq:
+ ldr pc, _vt_irq
+fiq:
+ ldr pc, _vt_fiq
+
+ .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 */
+
+ .align 5
+#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
+
+_hang:
+ ldr sp, _TEXT_BASE /* switch to abort stack */
+1:
+ bl 1b /* hang and never return */
diff --git a/arch/arm/cpu/arm926ejs/mx28/u-boot-spl.lds b/arch/arm/cpu/arm926ejs/mx28/u-boot-spl.lds
new file mode 100644
index 0000000..893320f
--- /dev/null
+++ b/arch/arm/cpu/arm926ejs/mx28/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 :
+ {
+ arch/arm/cpu/arm926ejs/mx28/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/arch/arm/include/asm/arch-mx28/sys_proto.h b/arch/arm/include/asm/arch-mx28/sys_proto.h
index a226ea4..be1f7db 100644
--- a/arch/arm/include/asm/arch-mx28/sys_proto.h
+++ b/arch/arm/include/asm/arch-mx28/sys_proto.h
@@ -29,4 +29,10 @@ int mx28_wait_mask_clr(struct mx28_register *reg, uint32_t mask, int timeout);
int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int));
+#ifdef CONFIG_SPL_BUILD
+#include <asm/arch/iomux-mx28.h>
+void mx28_common_spl_init(const iomux_cfg_t *iomux_setup,
+ const unsigned int iomux_size);
+#endif
+
#endif /* __MX28_H__ */