diff options
Diffstat (limited to 'arch/arm/cpu')
28 files changed, 2455 insertions, 299 deletions
diff --git a/arch/arm/cpu/arm926ejs/mx28/Makefile b/arch/arm/cpu/arm926ejs/mx28/Makefile index 7845310cf..a2e3f771c 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 += 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)) 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 000000000..98d363199 --- /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 000000000..dfb8309e7 --- /dev/null +++ b/arch/arm/cpu/arm926ejs/mx28/spl_boot.c @@ -0,0 +1,78 @@ +/* + * 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 (;;) + ; +} + +void serial_putc(const char c) {} +void serial_puts(const char *s) {} +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 000000000..00493b8bf --- /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 000000000..a4dc2a31b --- /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 000000000..94696d6cd --- /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 000000000..0fccd5296 --- /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) + } + + .bss : { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + . = ALIGN(4); + __bss_end__ = .; + } + + _end = .; + + /DISCARD/ : { *(.dynstr*) } + /DISCARD/ : { *(.dynsym*) } + /DISCARD/ : { *(.dynamic*) } + /DISCARD/ : { *(.hash*) } + /DISCARD/ : { *(.plt*) } + /DISCARD/ : { *(.interp*) } + /DISCARD/ : { *(.gnu*) } +} diff --git a/arch/arm/cpu/armv7/s5pc2xx/Makefile b/arch/arm/cpu/armv7/exynos/Makefile index 124c38018..124c38018 100644 --- a/arch/arm/cpu/armv7/s5pc2xx/Makefile +++ b/arch/arm/cpu/armv7/exynos/Makefile diff --git a/arch/arm/cpu/armv7/s5pc2xx/clock.c b/arch/arm/cpu/armv7/exynos/clock.c index 5ecd47596..64de26273 100644 --- a/arch/arm/cpu/armv7/s5pc2xx/clock.c +++ b/arch/arm/cpu/armv7/exynos/clock.c @@ -30,11 +30,11 @@ #define CONFIG_SYS_CLK_FREQ_C210 24000000 #endif -/* s5pc210: return pll clock frequency */ -static unsigned long s5pc210_get_pll_clk(int pllreg) +/* exynos4: return pll clock frequency */ +static unsigned long exynos4_get_pll_clk(int pllreg) { - struct s5pc210_clock *clk = - (struct s5pc210_clock *)samsung_get_base_clock(); + struct exynos4_clock *clk = + (struct exynos4_clock *)samsung_get_base_clock(); unsigned long r, m, p, s, k = 0, mask, fout; unsigned int freq; @@ -96,11 +96,11 @@ static unsigned long s5pc210_get_pll_clk(int pllreg) return fout; } -/* s5pc210: return ARM clock frequency */ -static unsigned long s5pc210_get_arm_clk(void) +/* exynos4: return ARM clock frequency */ +static unsigned long exynos4_get_arm_clk(void) { - struct s5pc210_clock *clk = - (struct s5pc210_clock *)samsung_get_base_clock(); + struct exynos4_clock *clk = + (struct exynos4_clock *)samsung_get_base_clock(); unsigned long div; unsigned long dout_apll; unsigned int apll_ratio; @@ -115,11 +115,11 @@ static unsigned long s5pc210_get_arm_clk(void) return dout_apll; } -/* s5pc210: return pwm clock frequency */ -static unsigned long s5pc210_get_pwm_clk(void) +/* exynos4: return pwm clock frequency */ +static unsigned long exynos4_get_pwm_clk(void) { - struct s5pc210_clock *clk = - (struct s5pc210_clock *)samsung_get_base_clock(); + struct exynos4_clock *clk = + (struct exynos4_clock *)samsung_get_base_clock(); unsigned long pclk, sclk; unsigned int sel; unsigned int ratio; @@ -158,11 +158,11 @@ static unsigned long s5pc210_get_pwm_clk(void) return pclk; } -/* s5pc210: return uart clock frequency */ -static unsigned long s5pc210_get_uart_clk(int dev_index) +/* exynos4: return uart clock frequency */ +static unsigned long exynos4_get_uart_clk(int dev_index) { - struct s5pc210_clock *clk = - (struct s5pc210_clock *)samsung_get_base_clock(); + struct exynos4_clock *clk = + (struct exynos4_clock *)samsung_get_base_clock(); unsigned long uclk, sclk; unsigned int sel; unsigned int ratio; @@ -205,11 +205,11 @@ static unsigned long s5pc210_get_uart_clk(int dev_index) return uclk; } -/* s5pc210: set the mmc clock */ -static void s5pc210_set_mmc_clk(int dev_index, unsigned int div) +/* exynos4: set the mmc clock */ +static void exynos4_set_mmc_clk(int dev_index, unsigned int div) { - struct s5pc210_clock *clk = - (struct s5pc210_clock *)samsung_get_base_clock(); + struct exynos4_clock *clk = + (struct exynos4_clock *)samsung_get_base_clock(); unsigned int addr; unsigned int val; @@ -234,25 +234,25 @@ static void s5pc210_set_mmc_clk(int dev_index, unsigned int div) unsigned long get_pll_clk(int pllreg) { - return s5pc210_get_pll_clk(pllreg); + return exynos4_get_pll_clk(pllreg); } unsigned long get_arm_clk(void) { - return s5pc210_get_arm_clk(); + return exynos4_get_arm_clk(); } unsigned long get_pwm_clk(void) { - return s5pc210_get_pwm_clk(); + return exynos4_get_pwm_clk(); } unsigned long get_uart_clk(int dev_index) { - return s5pc210_get_uart_clk(dev_index); + return exynos4_get_uart_clk(dev_index); } void set_mmc_clk(int dev_index, unsigned int div) { - s5pc210_set_mmc_clk(dev_index, div); + exynos4_set_mmc_clk(dev_index, div); } diff --git a/arch/arm/cpu/armv7/s5pc2xx/soc.c b/arch/arm/cpu/armv7/exynos/soc.c index dcfcec22d..dcfcec22d 100644 --- a/arch/arm/cpu/armv7/s5pc2xx/soc.c +++ b/arch/arm/cpu/armv7/exynos/soc.c diff --git a/arch/arm/cpu/armv7/imx-common/Makefile b/arch/arm/cpu/armv7/imx-common/Makefile new file mode 100644 index 000000000..e5ff375c1 --- /dev/null +++ b/arch/arm/cpu/armv7/imx-common/Makefile @@ -0,0 +1,47 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2011 Freescale Semiconductor, Inc. +# +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)libimx-common.o + +COBJS = timer.o cpu.o speed.o + +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) + +all: $(obj).depend $(LIB) + +$(LIB): $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/arm/cpu/armv7/imx-common/cpu.c b/arch/arm/cpu/armv7/imx-common/cpu.c new file mode 100644 index 000000000..1e30ae501 --- /dev/null +++ b/arch/arm/cpu/armv7/imx-common/cpu.c @@ -0,0 +1,108 @@ +/* + * (C) Copyright 2007 + * Sascha Hauer, Pengutronix + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + * + * 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 <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +#ifdef CONFIG_FSL_ESDHC +#include <fsl_esdhc.h> +#endif + +static char *get_reset_cause(void) +{ + u32 cause; + struct src *src_regs = (struct src *)SRC_BASE_ADDR; + + cause = readl(&src_regs->srsr); + writel(cause, &src_regs->srsr); + + switch (cause) { + case 0x00001: + return "POR"; + case 0x00004: + return "CSU"; + case 0x00008: + return "IPP USER"; + case 0x00010: + return "WDOG"; + case 0x00020: + return "JTAG HIGH-Z"; + case 0x00040: + return "JTAG SW"; + case 0x10000: + return "WARM BOOT"; + default: + return "unknown reset"; + } +} + +#if defined(CONFIG_DISPLAY_CPUINFO) +int print_cpuinfo(void) +{ + u32 cpurev; + + cpurev = get_cpu_rev(); + printf("CPU: Freescale i.MX%x family rev%d.%d at %d MHz\n", + (cpurev & 0xFF000) >> 12, + (cpurev & 0x000F0) >> 4, + (cpurev & 0x0000F) >> 0, + mxc_get_clock(MXC_ARM_CLK) / 1000000); + printf("Reset cause: %s\n", get_reset_cause()); + return 0; +} +#endif + +int cpu_eth_init(bd_t *bis) +{ + int rc = -ENODEV; + +#if defined(CONFIG_FEC_MXC) + rc = fecmxc_initialize(bis); +#endif + + return rc; +} + +/* + * Initializes on-chip MMC controllers. + * to override, implement board_mmc_init() + */ +int cpu_mmc_init(bd_t *bis) +{ +#ifdef CONFIG_FSL_ESDHC + return fsl_esdhc_mmc_init(bis); +#else + return 0; +#endif +} + +void reset_cpu(ulong addr) +{ + __raw_writew(4, WDOG1_BASE_ADDR); +} diff --git a/arch/arm/cpu/armv7/mx5/speed.c b/arch/arm/cpu/armv7/imx-common/speed.c index 2187e8ee5..2187e8ee5 100644 --- a/arch/arm/cpu/armv7/mx5/speed.c +++ b/arch/arm/cpu/armv7/imx-common/speed.c diff --git a/arch/arm/cpu/armv7/mx5/timer.c b/arch/arm/cpu/armv7/imx-common/timer.c index 2544b0876..98e9f4ad6 100644..100755 --- a/arch/arm/cpu/armv7/mx5/timer.c +++ b/arch/arm/cpu/armv7/imx-common/timer.c @@ -39,10 +39,11 @@ struct mxc_gpt { static struct mxc_gpt *cur_gpt = (struct mxc_gpt *)GPT1_BASE_ADDR; /* General purpose timers bitfields */ -#define GPTCR_SWR (1<<15) /* Software reset */ -#define GPTCR_FRR (1<<9) /* Freerun / restart */ -#define GPTCR_CLKSOURCE_32 (4<<6) /* Clock source */ -#define GPTCR_TEN (1) /* Timer enable */ +#define GPTCR_SWR (1 << 15) /* Software reset */ +#define GPTCR_FRR (1 << 9) /* Freerun / restart */ +#define GPTCR_CLKSOURCE_32 (4 << 6) /* Clock source */ +#define GPTCR_TEN 1 /* Timer enable */ +#define CLK_32KHZ 32768 /* 32Khz input */ DECLARE_GLOBAL_DATA_PTR; @@ -68,7 +69,7 @@ int timer_init(void) __raw_writel(i | GPTCR_CLKSOURCE_32 | GPTCR_TEN, &cur_gpt->control); val = __raw_readl(&cur_gpt->counter); - lastinc = val / (CONFIG_SYS_MX5_CLK32 / CONFIG_SYS_HZ); + lastinc = val / (CLK_32KHZ / CONFIG_SYS_HZ); timestamp = 0; return 0; @@ -77,11 +78,11 @@ int timer_init(void) ulong get_timer_masked(void) { ulong val = __raw_readl(&cur_gpt->counter); - val /= (CONFIG_SYS_MX5_CLK32 / CONFIG_SYS_HZ); + val /= (CLK_32KHZ / CONFIG_SYS_HZ); if (val >= lastinc) timestamp += (val - lastinc); else - timestamp += ((0xFFFFFFFF / (CONFIG_SYS_MX5_CLK32 / CONFIG_SYS_HZ)) + timestamp += ((0xFFFFFFFF / (CLK_32KHZ / CONFIG_SYS_HZ)) - lastinc) + val; lastinc = val; return timestamp; @@ -96,7 +97,7 @@ ulong get_timer(ulong base) void __udelay(unsigned long usec) { unsigned long now, start, tmo; - tmo = usec * (CONFIG_SYS_MX5_CLK32 / 1000) / 1000; + tmo = usec * (CLK_32KHZ / 1000) / 1000; if (!tmo) tmo = 1; diff --git a/arch/arm/cpu/armv7/mx5/Makefile b/arch/arm/cpu/armv7/mx5/Makefile index e8be9c9fa..ecd118421 100644 --- a/arch/arm/cpu/armv7/mx5/Makefile +++ b/arch/arm/cpu/armv7/mx5/Makefile @@ -27,7 +27,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(SOC).o -COBJS = soc.o clock.o iomux.o timer.o speed.o +COBJS = soc.o clock.o iomux.o SOBJS = lowlevel_init.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) diff --git a/arch/arm/cpu/armv7/mx5/soc.c b/arch/arm/cpu/armv7/mx5/soc.c index cf12ba88f..1533dd872 100644 --- a/arch/arm/cpu/armv7/mx5/soc.c +++ b/arch/arm/cpu/armv7/mx5/soc.c @@ -31,10 +31,6 @@ #include <asm/errno.h> #include <asm/io.h> -#ifdef CONFIG_FSL_ESDHC -#include <fsl_esdhc.h> -#endif - #if !(defined(CONFIG_MX51) || defined(CONFIG_MX53)) #error "CPU_TYPE not defined" #endif @@ -75,61 +71,6 @@ u32 get_cpu_rev(void) return system_rev; } -static char *get_reset_cause(void) -{ - u32 cause; - struct src *src_regs = (struct src *)SRC_BASE_ADDR; - - cause = readl(&src_regs->srsr); - writel(cause, &src_regs->srsr); - - switch (cause) { - case 0x00001: - return "POR"; - case 0x00004: - return "CSU"; - case 0x00008: - return "IPP USER"; - case 0x00010: - return "WDOG"; - case 0x00020: - return "JTAG HIGH-Z"; - case 0x00040: - return "JTAG SW"; - case 0x10000: - return "WARM BOOT"; - default: - return "unknown reset"; - } -} - -#if defined(CONFIG_DISPLAY_CPUINFO) -int print_cpuinfo(void) -{ - u32 cpurev; - - cpurev = get_cpu_rev(); - printf("CPU: Freescale i.MX%x family rev%d.%d at %d MHz\n", - (cpurev & 0xFF000) >> 12, - (cpurev & 0x000F0) >> 4, - (cpurev & 0x0000F) >> 0, - mxc_get_clock(MXC_ARM_CLK) / 1000000); - printf("Reset cause: %s\n", get_reset_cause()); - return 0; -} -#endif - -int cpu_eth_init(bd_t *bis) -{ - int rc = -ENODEV; - -#if defined(CONFIG_FEC_MXC) - rc = fecmxc_initialize(bis); -#endif - - return rc; -} - #if defined(CONFIG_FEC_MXC) void imx_get_mac_from_fuse(unsigned char *mac) { @@ -144,19 +85,6 @@ void imx_get_mac_from_fuse(unsigned char *mac) } #endif -/* - * Initializes on-chip MMC controllers. - * to override, implement board_mmc_init() - */ -int cpu_mmc_init(bd_t *bis) -{ -#ifdef CONFIG_FSL_ESDHC - return fsl_esdhc_mmc_init(bis); -#else - return 0; -#endif -} - void set_chipselect_size(int const cs_size) { unsigned int reg; @@ -187,8 +115,3 @@ void set_chipselect_size(int const cs_size) writel(reg, &iomuxc_regs->gpr1); } - -void reset_cpu(ulong addr) -{ - __raw_writew(4, WDOG1_BASE_ADDR); -} diff --git a/arch/arm/cpu/armv7/mx6/Makefile b/arch/arm/cpu/armv7/mx6/Makefile new file mode 100644 index 000000000..b0da028da --- /dev/null +++ b/arch/arm/cpu/armv7/mx6/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2011 Freescale Semiconductor, Inc. +# +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(SOC).o + +COBJS = soc.o clock.o iomux-v3.o +SOBJS = lowlevel_init.o + +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) + +all: $(obj).depend $(LIB) + +$(LIB): $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/arm/cpu/armv7/mx6/clock.c b/arch/arm/cpu/armv7/mx6/clock.c new file mode 100644 index 000000000..b14353597 --- /dev/null +++ b/arch/arm/cpu/armv7/mx6/clock.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * + * 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 <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/ccm_regs.h> +#include <asm/arch/clock.h> + +enum pll_clocks { + PLL_SYS, /* System PLL */ + PLL_BUS, /* System Bus PLL*/ + PLL_USBOTG, /* OTG USB PLL */ + PLL_ENET, /* ENET PLL */ +}; + +struct imx_ccm_reg *imx_ccm = (struct imx_ccm_reg *)CCM_BASE_ADDR; + +static u32 decode_pll(enum pll_clocks pll, u32 infreq) +{ + u32 div; + + switch (pll) { + case PLL_SYS: + div = __raw_readl(&imx_ccm->analog_pll_sys); + div &= BM_ANADIG_PLL_SYS_DIV_SELECT; + + return infreq * (div >> 1); + case PLL_BUS: + div = __raw_readl(&imx_ccm->analog_pll_528); + div &= BM_ANADIG_PLL_528_DIV_SELECT; + + return infreq * (20 + (div << 1)); + case PLL_USBOTG: + div = __raw_readl(&imx_ccm->analog_usb1_pll_480_ctrl); + div &= BM_ANADIG_USB1_PLL_480_CTRL_DIV_SELECT; + + return infreq * (20 + (div << 1)); + case PLL_ENET: + div = __raw_readl(&imx_ccm->analog_pll_enet); + div &= BM_ANADIG_PLL_ENET_DIV_SELECT; + + return (div == 3 ? 125000000 : 25000000 * (div << 1)); + default: + return 0; + } + /* NOTREACHED */ +} + +static u32 get_mcu_main_clk(void) +{ + u32 reg, freq; + + reg = __raw_readl(&imx_ccm->cacrr); + reg &= MXC_CCM_CACRR_ARM_PODF_MASK; + reg >>= MXC_CCM_CACRR_ARM_PODF_OFFSET; + freq = decode_pll(PLL_SYS, CONFIG_SYS_MX6_HCLK); + + return freq / (reg + 1); +} + +static u32 get_periph_clk(void) +{ + u32 reg, freq = 0; + + reg = __raw_readl(&imx_ccm->cbcdr); + if (reg & MXC_CCM_CBCDR_PERIPH_CLK_SEL) { + reg = __raw_readl(&imx_ccm->cbcmr); + reg &= MXC_CCM_CBCMR_PERIPH_CLK2_SEL_MASK; + reg >>= MXC_CCM_CBCMR_PERIPH_CLK2_SEL_OFFSET; + + switch (reg) { + case 0: + freq = decode_pll(PLL_USBOTG, CONFIG_SYS_MX6_HCLK); + break; + case 1: + case 2: + freq = CONFIG_SYS_MX6_HCLK; + break; + default: + break; + } + } else { + reg = __raw_readl(&imx_ccm->cbcmr); + reg &= MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK; + reg >>= MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET; + + switch (reg) { + case 0: + freq = decode_pll(PLL_BUS, CONFIG_SYS_MX6_HCLK); + break; + case 1: + freq = PLL2_PFD2_FREQ; + break; + case 2: + freq = PLL2_PFD0_FREQ; + break; + case 3: + freq = PLL2_PFD2_DIV_FREQ; + break; + default: + break; + } + } + + return freq; +} + + +static u32 get_ahb_clk(void) +{ + u32 reg, ahb_podf; + + reg = __raw_readl(&imx_ccm->cbcdr); + reg &= MXC_CCM_CBCDR_AHB_PODF_MASK; + ahb_podf = reg >> MXC_CCM_CBCDR_AHB_PODF_OFFSET; + + return get_periph_clk() / (ahb_podf + 1); +} + +static u32 get_ipg_clk(void) +{ + u32 reg, ipg_podf; + + reg = __raw_readl(&imx_ccm->cbcdr); + reg &= MXC_CCM_CBCDR_IPG_PODF_MASK; + ipg_podf = reg >> MXC_CCM_CBCDR_IPG_PODF_OFFSET; + + return get_ahb_clk() / (ipg_podf + 1); +} + +static u32 get_ipg_per_clk(void) +{ + u32 reg, perclk_podf; + + reg = __raw_readl(&imx_ccm->cscmr1); + perclk_podf = reg & MXC_CCM_CSCMR1_PERCLK_PODF_MASK; + + return get_ipg_clk() / (perclk_podf + 1); +} + +static u32 get_uart_clk(void) +{ + u32 reg, uart_podf; + + reg = __raw_readl(&imx_ccm->cscdr1); + reg &= MXC_CCM_CSCDR1_UART_CLK_PODF_MASK; + uart_podf = reg >> MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET; + + return PLL3_80M / (uart_podf + 1); +} + +static u32 get_cspi_clk(void) +{ + u32 reg, cspi_podf; + + reg = __raw_readl(&imx_ccm->cscdr2); + reg &= MXC_CCM_CSCDR2_ECSPI_CLK_PODF_MASK; + cspi_podf = reg >> MXC_CCM_CSCDR2_ECSPI_CLK_PODF_OFFSET; + + return PLL3_60M / (cspi_podf + 1); +} + +static u32 get_axi_clk(void) +{ + u32 root_freq, axi_podf; + u32 cbcdr = __raw_readl(&imx_ccm->cbcdr); + + axi_podf = cbcdr & MXC_CCM_CBCDR_AXI_PODF_MASK; + axi_podf >>= MXC_CCM_CBCDR_AXI_PODF_OFFSET; + + if (cbcdr & MXC_CCM_CBCDR_AXI_SEL) { + if (cbcdr & MXC_CCM_CBCDR_AXI_ALT_SEL) + root_freq = PLL2_PFD2_FREQ; + else + root_freq = PLL3_PFD1_FREQ; + } else + root_freq = get_periph_clk(); + + return root_freq / (axi_podf + 1); +} + +static u32 get_emi_slow_clk(void) +{ + u32 emi_clk_sel, emi_slow_pof, cscmr1, root_freq = 0; + + cscmr1 = __raw_readl(&imx_ccm->cscmr1); + emi_clk_sel = cscmr1 & MXC_CCM_CSCMR1_ACLK_EMI_SLOW_MASK; + emi_clk_sel >>= MXC_CCM_CSCMR1_ACLK_EMI_SLOW_OFFSET; + emi_slow_pof = cscmr1 & MXC_CCM_CSCMR1_ACLK_EMI_SLOW_PODF_MASK; + emi_slow_pof >>= MXC_CCM_CSCMR1_ACLK_EMI_PODF_OFFSET; + + switch (emi_clk_sel) { + case 0: + root_freq = get_axi_clk(); + break; + case 1: + root_freq = decode_pll(PLL_USBOTG, CONFIG_SYS_MX6_HCLK); + break; + case 2: + root_freq = PLL2_PFD2_FREQ; + break; + case 3: + root_freq = PLL2_PFD0_FREQ; + break; + } + + return root_freq / (emi_slow_pof + 1); +} + +static u32 get_mmdc_ch0_clk(void) +{ + u32 cbcdr = __raw_readl(&imx_ccm->cbcdr); + u32 mmdc_ch0_podf = (cbcdr & MXC_CCM_CBCDR_MMDC_CH0_PODF_MASK) >> + MXC_CCM_CBCDR_MMDC_CH0_PODF_OFFSET; + + return get_periph_clk() / (mmdc_ch0_podf + 1); +} + +static u32 get_usdhc_clk(u32 port) +{ + u32 root_freq = 0, usdhc_podf = 0, clk_sel = 0; + u32 cscmr1 = __raw_readl(&imx_ccm->cscmr1); + u32 cscdr1 = __raw_readl(&imx_ccm->cscdr1); + + switch (port) { + case 0: + usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC1_PODF_MASK) >> + MXC_CCM_CSCDR1_USDHC1_PODF_OFFSET; + clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC1_CLK_SEL; + + break; + case 1: + usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC2_PODF_MASK) >> + MXC_CCM_CSCDR1_USDHC2_PODF_OFFSET; + clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC2_CLK_SEL; + + break; + case 2: + usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC3_PODF_MASK) >> + MXC_CCM_CSCDR1_USDHC3_PODF_OFFSET; + clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC3_CLK_SEL; + + break; + case 3: + usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC4_PODF_MASK) >> + MXC_CCM_CSCDR1_USDHC4_PODF_OFFSET; + clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC4_CLK_SEL; + + break; + default: + break; + } + + if (clk_sel) + root_freq = PLL2_PFD0_FREQ; + else + root_freq = PLL2_PFD2_FREQ; + + return root_freq / (usdhc_podf + 1); +} + +u32 imx_get_uartclk(void) +{ + return get_uart_clk(); +} + +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_ARM_CLK: + return get_mcu_main_clk(); + case MXC_PER_CLK: + return get_periph_clk(); + case MXC_AHB_CLK: + return get_ahb_clk(); + case MXC_IPG_CLK: + return get_ipg_clk(); + case MXC_IPG_PERCLK: + return get_ipg_per_clk(); + case MXC_UART_CLK: + return get_uart_clk(); + case MXC_CSPI_CLK: + return get_cspi_clk(); + case MXC_AXI_CLK: + return get_axi_clk(); + case MXC_EMI_SLOW_CLK: + return get_emi_slow_clk(); + case MXC_DDR_CLK: + return get_mmdc_ch0_clk(); + case MXC_ESDHC_CLK: + return get_usdhc_clk(0); + case MXC_ESDHC2_CLK: + return get_usdhc_clk(1); + case MXC_ESDHC3_CLK: + return get_usdhc_clk(2); + case MXC_ESDHC4_CLK: + return get_usdhc_clk(3); + case MXC_SATA_CLK: + return get_ahb_clk(); + default: + break; + } + + return -1; +} + +/* + * Dump some core clockes. + */ +int do_mx6_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + u32 freq; + freq = decode_pll(PLL_SYS, CONFIG_SYS_MX6_HCLK); + printf("PLL_SYS %8d MHz\n", freq / 1000000); + freq = decode_pll(PLL_BUS, CONFIG_SYS_MX6_HCLK); + printf("PLL_BUS %8d MHz\n", freq / 1000000); + freq = decode_pll(PLL_USBOTG, CONFIG_SYS_MX6_HCLK); + printf("PLL_OTG %8d MHz\n", freq / 1000000); + freq = decode_pll(PLL_ENET, CONFIG_SYS_MX6_HCLK); + printf("PLL_NET %8d MHz\n", freq / 1000000); + + printf("\n"); + printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); + printf("UART %8d kHz\n", mxc_get_clock(MXC_UART_CLK) / 1000); + printf("CSPI %8d kHz\n", mxc_get_clock(MXC_CSPI_CLK) / 1000); + printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); + printf("AXI %8d kHz\n", mxc_get_clock(MXC_AXI_CLK) / 1000); + printf("DDR %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); + printf("USDHC1 %8d kHz\n", mxc_get_clock(MXC_ESDHC_CLK) / 1000); + printf("USDHC2 %8d kHz\n", mxc_get_clock(MXC_ESDHC2_CLK) / 1000); + printf("USDHC3 %8d kHz\n", mxc_get_clock(MXC_ESDHC3_CLK) / 1000); + printf("USDHC4 %8d kHz\n", mxc_get_clock(MXC_ESDHC4_CLK) / 1000); + printf("EMI SLOW %8d kHz\n", mxc_get_clock(MXC_EMI_SLOW_CLK) / 1000); + printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000); + + return 0; +} + +/***************************************************/ + +U_BOOT_CMD( + clocks, CONFIG_SYS_MAXARGS, 1, do_mx6_showclocks, + "display clocks", + "" +); diff --git a/arch/arm/cpu/armv7/mx6/iomux-v3.c b/arch/arm/cpu/armv7/mx6/iomux-v3.c new file mode 100644 index 000000000..878553216 --- /dev/null +++ b/arch/arm/cpu/armv7/mx6/iomux-v3.c @@ -0,0 +1,71 @@ +/* + * Based on the iomux-v3.c from Linux kernel: + * Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de> + * Copyright (C) 2009 by Jan Weitzel Phytec Messtechnik GmbH, + * <armlinux@phytec.de> + * + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include <common.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/mx6x_pins.h> +#include <asm/arch/iomux-v3.h> + +static void *base = (void *)IOMUXC_BASE_ADDR; + +/* + * configures a single pad in the iomuxer + */ +int imx_iomux_v3_setup_pad(iomux_v3_cfg_t pad) +{ + u32 mux_ctrl_ofs = (pad & MUX_CTRL_OFS_MASK) >> MUX_CTRL_OFS_SHIFT; + u32 mux_mode = (pad & MUX_MODE_MASK) >> MUX_MODE_SHIFT; + u32 sel_input_ofs = + (pad & MUX_SEL_INPUT_OFS_MASK) >> MUX_SEL_INPUT_OFS_SHIFT; + u32 sel_input = + (pad & MUX_SEL_INPUT_MASK) >> MUX_SEL_INPUT_SHIFT; + u32 pad_ctrl_ofs = + (pad & MUX_PAD_CTRL_OFS_MASK) >> MUX_PAD_CTRL_OFS_SHIFT; + u32 pad_ctrl = (pad & MUX_PAD_CTRL_MASK) >> MUX_PAD_CTRL_SHIFT; + + if (mux_ctrl_ofs) + __raw_writel(mux_mode, base + mux_ctrl_ofs); + + if (sel_input_ofs) + __raw_writel(sel_input, base + sel_input_ofs); + + if (!(pad_ctrl & NO_PAD_CTRL) && pad_ctrl_ofs) + __raw_writel(pad_ctrl, base + pad_ctrl_ofs); + + return 0; +} + +int imx_iomux_v3_setup_multiple_pads(iomux_v3_cfg_t *pad_list, unsigned count) +{ + iomux_v3_cfg_t *p = pad_list; + int i; + int ret; + + for (i = 0; i < count; i++) { + ret = imx_iomux_v3_setup_pad(*p); + if (ret) + return ret; + p++; + } + return 0; +} diff --git a/arch/arm/cpu/armv7/mx6/lowlevel_init.S b/arch/arm/cpu/armv7/mx6/lowlevel_init.S new file mode 100644 index 000000000..1864356d0 --- /dev/null +++ b/arch/arm/cpu/armv7/mx6/lowlevel_init.S @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * + * 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 + */ +.section ".text.init", "x" + +.globl lowlevel_init +lowlevel_init: + + mov pc, lr diff --git a/arch/arm/cpu/armv7/mx6/soc.c b/arch/arm/cpu/armv7/mx6/soc.c new file mode 100644 index 000000000..dff5e4efd --- /dev/null +++ b/arch/arm/cpu/armv7/mx6/soc.c @@ -0,0 +1,82 @@ +/* + * (C) Copyright 2007 + * Sascha Hauer, Pengutronix + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + * + * 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 <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +u32 get_cpu_rev(void) +{ + int system_rev = 0x61000 | CHIP_REV_1_0; + + return system_rev; +} + +#ifdef CONFIG_ARCH_CPU_INIT +void init_aips(void) +{ + u32 reg = AIPS1_BASE_ADDR; + + /* + * Set all MPROTx to be non-bufferable, trusted for R/W, + * not forced to user-mode. + */ + writel(0x77777777, reg + 0x00); + writel(0x77777777, reg + 0x04); + + reg = AIPS2_BASE_ADDR; + writel(0x77777777, reg + 0x00); + writel(0x77777777, reg + 0x04); +} + +int arch_cpu_init(void) +{ + init_aips(); + + return 0; +} +#endif + +#if defined(CONFIG_FEC_MXC) +void imx_get_mac_from_fuse(unsigned char *mac) +{ + struct iim_regs *iim = (struct iim_regs *)IMX_IIM_BASE; + struct fuse_bank *bank = &iim->bank[4]; + struct fuse_bank4_regs *fuse = + (struct fuse_bank4_regs *)bank->fuse_regs; + + u32 mac_lo = readl(&fuse->mac_addr_low); + u32 mac_hi = readl(&fuse->mac_addr_high); + + *(u32 *)mac = mac_lo; + + mac[4] = mac_hi & 0xff; + mac[5] = (mac_hi >> 8) & 0xff; + +} +#endif diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S index d23dc9d71..6a77c71e1 100644 --- a/arch/arm/cpu/armv7/start.S +++ b/arch/arm/cpu/armv7/start.S @@ -82,18 +82,6 @@ _end_vect: _TEXT_BASE: .word CONFIG_SYS_TEXT_BASE -#ifdef CONFIG_TEGRA2 -/* - * Tegra2 uses 2 separate CPUs - the AVP (ARM7TDMI) and the CPU (dual A9s). - * U-Boot runs on the AVP first, setting things up for the CPU (PLLs, - * muxes, clocks, clamps, etc.). Then the AVP halts, and expects the CPU - * to pick up its reset vector, which points here. - */ -.globl _armboot_start -_armboot_start: - .word _start -#endif - /* * These are defined in the board-specific linker script. */ @@ -162,6 +150,7 @@ reset: /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT + bl cpu_init_cp15 bl cpu_init_crit #endif @@ -299,17 +288,16 @@ jump_2_ram: _board_init_r_ofs: .word board_init_r - _start - -#ifndef CONFIG_SKIP_LOWLEVEL_INIT /************************************************************************* * - * CPU_init_critical registers + * cpu_init_cp15 * - * setup important registers - * setup memory timing + * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless + * CONFIG_SYS_ICACHE_OFF is defined. * *************************************************************************/ -cpu_init_crit: +.globl cpu_init_cp15 +cpu_init_cp15: /* * Invalidate L1 I/D */ @@ -334,7 +322,19 @@ cpu_init_crit: orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache #endif mcr p15, 0, r0, c1, c0, 0 + mov pc, lr @ back to my caller + +#ifndef CONFIG_SKIP_LOWLEVEL_INIT +/************************************************************************* + * + * CPU_init_critical registers + * + * setup important registers + * setup memory timing + * + *************************************************************************/ +cpu_init_crit: /* * Jump to board specific initialization... * The Mask ROM will have already initialized diff --git a/arch/arm/cpu/armv7/tegra2/Makefile b/arch/arm/cpu/armv7/tegra2/Makefile index f0dc2ffa8..955c3b6dc 100644 --- a/arch/arm/cpu/armv7/tegra2/Makefile +++ b/arch/arm/cpu/armv7/tegra2/Makefile @@ -23,6 +23,11 @@ # MA 02111-1307 USA # +# The AVP is ARMv4T architecture so we must use special compiler +# flags for any startup files it might use. +CFLAGS_arch/arm/cpu/armv7/tegra2/ap20.o += -march=armv4t +CFLAGS_arch/arm/cpu/armv7/tegra2/clock.o += -march=armv4t + include $(TOPDIR)/config.mk LIB = $(obj)lib$(SOC).o diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c index 5cb4b1b6a..4c44bb363 100644 --- a/arch/arm/cpu/armv7/tegra2/ap20.c +++ b/arch/arm/cpu/armv7/tegra2/ap20.c @@ -31,7 +31,12 @@ #include <asm/arch/scu.h> #include <common.h> -u32 s_first_boot = 1; +/* Returns 1 if the current CPU executing is a Cortex-A9, else 0 */ +static int ap20_cpu_is_cortexa9(void) +{ + u32 id = readb(NV_PA_PG_UP_BASE + PG_UP_TAG_0); + return id == (PG_UP_TAG_0_PID_CPU & 0xff); +} void init_pllx(void) { @@ -283,38 +288,37 @@ void init_pmc_scratch(void) writel(CONFIG_SYS_BOARD_ODMDATA, &pmc->pmc_scratch20); } -void cpu_start(void) +void tegra2_start(void) { struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; - /* enable JTAG */ - writel(0xC0, &pmt->pmt_cfg_ctl); + /* If we are the AVP, start up the first Cortex-A9 */ + if (!ap20_cpu_is_cortexa9()) { + /* enable JTAG */ + writel(0xC0, &pmt->pmt_cfg_ctl); - if (s_first_boot) { /* - * Need to set this before cold-booting, - * otherwise we'll end up in an infinite loop. - */ - s_first_boot = 0; - cold_boot(); + * If we are ARM7 - give it a different stack. We are about to + * start up the A9 which will want to use this one. + */ + asm volatile("ldr sp, =%c0\n" + : : "i"(AVP_EARLY_BOOT_STACK_LIMIT)); + + start_cpu((u32)_start); + halt_avp(); + /* not reached */ } -} -void tegra2_start() -{ - if (s_first_boot) { - /* Init Debug UART Port (115200 8n1) */ - uart_init(); + /* Init PMC scratch memory */ + init_pmc_scratch(); - /* Init PMC scratch memory */ - init_pmc_scratch(); - } + enable_scu(); -#ifdef CONFIG_ENABLE_CORTEXA9 - /* take the mpcore out of reset */ - cpu_start(); + /* enable SMP mode and FW for CPU0, by writing to Auxiliary Ctl reg */ + asm volatile( + "mrc p15, 0, r0, c1, c0, 1\n" + "orr r0, r0, #0x41\n" + "mcr p15, 0, r0, c1, c0, 1\n"); - /* configure cache */ - cache_configure(); -#endif + /* FIXME: should have ap20's L2 disabled too? */ } diff --git a/arch/arm/cpu/armv7/tegra2/ap20.h b/arch/arm/cpu/armv7/tegra2/ap20.h index 49fe340a2..a4b4d73a4 100644 --- a/arch/arm/cpu/armv7/tegra2/ap20.h +++ b/arch/arm/cpu/armv7/tegra2/ap20.h @@ -95,10 +95,8 @@ #define HALT_COP_EVENT_IRQ_1 (1 << 11) #define HALT_COP_EVENT_FIQ_1 (1 << 9) -/* Prototypes */ - +/* Start up the tegra2 SOC */ void tegra2_start(void); -void uart_init(void); -void udelay(unsigned long); -void cold_boot(void); -void cache_configure(void); + +/* This is the main entry into U-Boot, used by the Cortex-A9 */ +extern void _start(void); diff --git a/arch/arm/cpu/armv7/tegra2/board.c b/arch/arm/cpu/armv7/tegra2/board.c index 751102d18..59dce8f8d 100644 --- a/arch/arm/cpu/armv7/tegra2/board.c +++ b/arch/arm/cpu/armv7/tegra2/board.c @@ -23,6 +23,7 @@ #include <common.h> #include <asm/io.h> +#include "ap20.h" #include <asm/arch/sys_proto.h> #include <asm/arch/tegra2.h> #include <asm/arch/pmc.h> @@ -54,28 +55,10 @@ unsigned int query_sdram_size(void) } } -void s_init(void) -{ -#ifndef CONFIG_ICACHE_OFF - icache_enable(); -#endif - invalidate_dcache(); -} - int dram_init(void) { - unsigned long rs; - /* We do not initialise DRAM here. We just query the size */ - gd->bd->bi_dram[0].start = PHYS_SDRAM_1; - gd->bd->bi_dram[0].size = gd->ram_size = query_sdram_size(); - - /* Now check it dynamically */ - rs = get_ram_size(CONFIG_SYS_SDRAM_BASE, gd->ram_size); - if (rs) { - printf("dynamic ram_size = %lu\n", rs); - gd->bd->bi_dram[0].size = gd->ram_size = rs; - } + gd->ram_size = query_sdram_size(); return 0; } @@ -86,3 +69,17 @@ int checkboard(void) return 0; } #endif /* CONFIG_DISPLAY_BOARDINFO */ + +#ifdef CONFIG_ARCH_CPU_INIT +/* + * Note this function is executed by the ARM7TDMI AVP. It does not return + * in this case. It is also called once the A9 starts up, but does nothing in + * that case. + */ +int arch_cpu_init(void) +{ + /* Fire up the Cortex A9 */ + tegra2_start(); + return 0; +} +#endif diff --git a/arch/arm/cpu/armv7/tegra2/config.mk b/arch/arm/cpu/armv7/tegra2/config.mk index 96c07955a..8f9bdc9ff 100644 --- a/arch/arm/cpu/armv7/tegra2/config.mk +++ b/arch/arm/cpu/armv7/tegra2/config.mk @@ -24,5 +24,8 @@ # MA 02111-1307 USA # -# Use ARMv4 for Tegra2 - initial code runs on the AVP, which is an ARM7TDI. -PLATFORM_CPPFLAGS += -march=armv4 +# Tegra has an ARMv4T CPU which runs board_init_f(), so we must build this +# file with compatible flags +ifdef CONFIG_TEGRA2 +CFLAGS_arch/arm/lib/board.o += -march=armv4t +endif diff --git a/arch/arm/cpu/armv7/tegra2/lowlevel_init.S b/arch/arm/cpu/armv7/tegra2/lowlevel_init.S index f24a2ff57..6b866476c 100644 --- a/arch/arm/cpu/armv7/tegra2/lowlevel_init.S +++ b/arch/arm/cpu/armv7/tegra2/lowlevel_init.S @@ -26,14 +26,6 @@ #include <config.h> #include <version.h> - -_TEXT_BASE: - .word CONFIG_SYS_TEXT_BASE @ sdram load addr from config file - -.global invalidate_dcache -invalidate_dcache: - mov pc, lr - .align 5 .global reset_cpu reset_cpu: @@ -47,113 +39,3 @@ _loop_forever: b _loop_forever rstctl: .word PRM_RSTCTRL - -.globl lowlevel_init -lowlevel_init: - ldr sp, SRAM_STACK - str ip, [sp] - mov ip, lr - bl s_init @ go setup pll, mux & memory - ldr ip, [sp] - mov lr, ip - - mov pc, lr @ back to arch calling code - - -.globl startup_cpu -startup_cpu: - @ Initialize the AVP, clocks, and memory controller - @ SDRAM is guaranteed to be on at this point - - ldr r0, =cold_boot @ R0 = reset vector for CPU - bl start_cpu @ start the CPU - - @ Transfer control to the AVP code - bl halt_avp - - @ Should never get here -_loop_forever2: - b _loop_forever2 - -.globl cache_configure -cache_configure: - stmdb r13!,{r14} - @ invalidate instruction cache - mov r1, #0 - mcr p15, 0, r1, c7, c5, 0 - - @ invalidate the i&d tlb entries - mcr p15, 0, r1, c8, c5, 0 - mcr p15, 0, r1, c8, c6, 0 - - @ enable instruction cache - mrc p15, 0, r1, c1, c0, 0 - orr r1, r1, #(1<<12) - mcr p15, 0, r1, c1, c0, 0 - - bl enable_scu - - @ enable SMP mode and FW for CPU0, by writing to Auxiliary Ctl reg - mrc p15, 0, r0, c1, c0, 1 - orr r0, r0, #0x41 - mcr p15, 0, r0, c1, c0, 1 - - @ Now flush the Dcache - mov r0, #0 - @ 256 cache lines - mov r1, #256 - -invalidate_loop: - add r1, r1, #-1 - mov r0, r1, lsl #5 - @ invalidate d-cache using line (way0) - mcr p15, 0, r0, c7, c6, 2 - - orr r2, r0, #(1<<30) - @ invalidate d-cache using line (way1) - mcr p15, 0, r2, c7, c6, 2 - - orr r2, r0, #(2<<30) - @ invalidate d-cache using line (way2) - mcr p15, 0, r2, c7, c6, 2 - - orr r2, r0, #(3<<30) - @ invalidate d-cache using line (way3) - mcr p15, 0, r2, c7, c6, 2 - cmp r1, #0 - bne invalidate_loop - - @ FIXME: should have ap20's L2 disabled too? -invalidate_done: - ldmia r13!,{pc} - -.globl cold_boot -cold_boot: - msr cpsr_c, #0xD3 - @ Check current processor: CPU or AVP? - @ If CPU, go to CPU boot code, else continue on AVP path - - ldr r0, =NV_PA_PG_UP_BASE - ldr r1, [r0] - ldr r2, =PG_UP_TAG_AVP - - @ are we the CPU? - ldr sp, CPU_STACK - cmp r1, r2 - @ yep, we are the CPU - bne _armboot_start - - @ AVP initialization follows this path - ldr sp, AVP_STACK - @ Init AVP and start CPU - b startup_cpu - - @ the literal pools origin - .ltorg - -SRAM_STACK: - .word LOW_LEVEL_SRAM_STACK -AVP_STACK: - .word EARLY_AVP_STACK -CPU_STACK: - .word EARLY_CPU_STACK |