Message ID | 1353843857.17518.23.camel@home.hno.se |
---|---|
State | Changes Requested |
Delegated to: | Albert ARIBAUD |
Headers | show |
Hi Henrik, On Sun, Nov 25, 2012 at 12:44:17PM +0100, Henrik Nordström wrote: > Allwinner sunxi family of SoCs boots from MMC0/NAND/NOR/MMC2 loading > boot code into an embedded 32KB SRAM. > > This patch adds support for booting u-boot SPL from MMC0 > > We first initializes the console UART, then DRAM controller with board > specific DRAM configuration details, configure CPU core voltage and > clocks before loading the full u-boot image into DRAM. > > From: Henrik Nordstrom <henrik@henriknordstrom.net> > Signed-off-by: Tom Cubie <tangliang@allwinnertech.com> > Signed-off-by: Stefan Roese <sr@denx.de> > Signed-off-by: Henrik Nordstrom <henrik@henriknordstrom.net> > --- > Makefile | 11 + > arch/arm/cpu/armv7/sunxi/board.c | 38 +++ > arch/arm/cpu/armv7/sunxi/clock.c | 99 +++++++ > arch/arm/cpu/armv7/sunxi/dram.c | 445 +++++++++++++++++++++++++++++++ > arch/arm/cpu/armv7/sunxi/u-boot-spl.lds | 63 +++++ > arch/arm/include/asm/arch-sunxi/spl.h | 34 +++ > board/sunxi/board.c | 46 ++++ > include/configs/sunxi-common.h | 30 ++- > spl/Makefile | 10 + > 9 files changed, 775 insertions(+), 1 deletions(-) > create mode 100644 arch/arm/cpu/armv7/sunxi/dram.c > create mode 100644 arch/arm/cpu/armv7/sunxi/u-boot-spl.lds > create mode 100644 arch/arm/include/asm/arch-sunxi/spl.h > > diff --git a/Makefile b/Makefile > index 81fe532..d1b7f97 100644 > --- a/Makefile > +++ b/Makefile > @@ -517,6 +517,16 @@ $(obj)u-boot.spr: $(obj)u-boot.img $(obj)spl/u-boot-spl.bin > conv=notrunc 2>/dev/null > cat $(obj)spl/u-boot-spl-pad.img $(obj)u-boot.img > $@ > > +# sunxi: Combined object with SPL U-Boot with sunxi header (sunxi-spl.bin) > +# and the full-blown U-Boot attached to it > +$(obj)u-boot-sunxi-with-spl.bin: $(obj)spl/sunxi-spl.bin $(obj)u-boot.bin > + tr "\000" "\377" < /dev/zero | dd ibs=1 count=$(CONFIG_SPL_PAD_TO) \ > + of=$(obj)spl/sunxi-spl-pad.bin 2>/dev/null > + dd if=$(obj)spl/sunxi-spl.bin of=$(obj)spl/sunxi-spl-pad.bin \ > + conv=notrunc 2>/dev/null > + cat $(obj)spl/sunxi-spl-pad.bin $(obj)u-boot.bin > $@ > + rm $(obj)spl/sunxi-spl-pad.bin > + > ifeq ($(SOC),tegra20) > ifeq ($(CONFIG_OF_SEPARATE),y) > nodtb=dtb > @@ -854,6 +864,7 @@ clobber: tidy > @[ ! -d $(obj)nand_spl ] || find $(obj)nand_spl -name "*" -type l -print | xargs rm -f > @rm -f $(obj)dts/*.tmp > @rm -f $(obj)spl/u-boot-spl{,-pad}.ais > + @rm -f $(obj)spl/sun?i-spl.bin > > mrproper \ > distclean: clobber unconfig > diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c > index 29cc4bd..6dc2bd0 100644 > --- a/arch/arm/cpu/armv7/sunxi/board.c > +++ b/arch/arm/cpu/armv7/sunxi/board.c > @@ -27,12 +27,37 @@ > #include <common.h> > #include <asm/io.h> > #include <serial.h> > +#include <i2c.h> > #include <asm/gpio.h> > #include <asm/arch/clock.h> > #include <asm/arch/timer.h> > #include <asm/arch/gpio.h> > #include <asm/arch/sys_proto.h> > #include <netdev.h> > +#ifdef CONFIG_SPL_BUILD > +#include <spl.h> > +#endif > + > +#ifdef CONFIG_SPL_BUILD > +/* Pointer to the global data structure for SPL */ > +DECLARE_GLOBAL_DATA_PTR; > + > +/* The sunxi internal brom will try to loader external bootloader > + * from mmc0, nannd flash, mmc2. > + * Unfortunately we can't check how SPL was loaded so assume > + * it's always the first SD/MMC controller > + */ > +u32 spl_boot_device(void) > +{ > + return BOOT_DEVICE_MMC1; > +} > + > +/* No confiration data available in SPL yet. Hardcode bootmode */ > +u32 spl_boot_mode(void) > +{ > + return MMCSD_MODE_RAW; > +} > +#endif > > int gpio_init(void) > { > @@ -65,6 +90,19 @@ void s_init(void) > #endif > clock_init(); > gpio_init(); > + > +#ifdef CONFIG_SPL_BUILD > + gd = &gdata; > + preloader_console_init(); > + > +#ifdef CONFIG_SPL_I2C_SUPPORT > + /* Needed early by sunxi_board_init if PMU is enabled */ > + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); > +#endif > + > + sunxi_board_init(); > +#endif > + > } > > void reset_cpu(ulong addr) > diff --git a/arch/arm/cpu/armv7/sunxi/clock.c b/arch/arm/cpu/armv7/sunxi/clock.c > index b9bbb7d..91cfae3 100644 > --- a/arch/arm/cpu/armv7/sunxi/clock.c > +++ b/arch/arm/cpu/armv7/sunxi/clock.c > @@ -24,13 +24,34 @@ > #include <common.h> > #include <asm/io.h> > #include <asm/arch/clock.h> > +#include <asm/arch/gpio.h> > #include <asm/arch/sys_proto.h> > > +#ifdef CONFIG_SPL_BUILD > +static void clock_init_safe(void) > +{ > + struct sunxi_ccm_reg * const ccm = > + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; > + > + /* Set safe defaults until PMU is configured */ > + writel(AXI_DIV_1 << 0 | AHB_DIV_2 << 4 | APB0_DIV_1 << 8 | > + CPU_CLK_SRC_OSC24M << 16, &ccm->cpu_ahb_apb0_cfg); > + writel(0xa1005000, &ccm->pll1_cfg); > + sdelay(200); > + writel(AXI_DIV_1 << 0 | AHB_DIV_2 << 4 | APB0_DIV_1 << 8 | > + CPU_CLK_SRC_PLL1 << 16, &ccm->cpu_ahb_apb0_cfg); > +} > +#endif > + > int clock_init(void) > { > struct sunxi_ccm_reg *const ccm = > (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; > > +#ifdef CONFIG_SPL_BUILD > + clock_init_safe(); > +#endif > + > /* uart clock source is apb1 */ > sr32(&ccm->apb1_clk_div_cfg, 24, 2, APB1_CLK_SRC_OSC24M); > sr32(&ccm->apb1_clk_div_cfg, 16, 2, APB1_FACTOR_N); > @@ -70,3 +91,81 @@ int clock_twi_onoff(int port, int state) > > return 0; > } > + > +#ifdef CONFIG_SPL_BUILD > +#define PLL1_CFG(N, K, M, P) (1 << 31 | 0 << 30 | 8 << 26 | 0 << 25 | \ > + 16 << 20 | (P) << 16 | 2 << 13 | (N) << 8 | \ > + (K) << 4 | 0 << 3 | 0 << 2 | (M) << 0) > +#define RDIV(a, b) ((a + (b) - 1) / (b)) > + > +struct { > + u32 pll1_cfg; > + unsigned int freq; > +} pll1_para[] = { > + { PLL1_CFG(16, 0, 0, 0), 384000000 }, > + { PLL1_CFG(16, 1, 0, 0), 768000000 }, > + { PLL1_CFG(20, 1, 0, 0), 960000000 }, > + { PLL1_CFG(21, 1, 0, 0), 1008000000}, > + { PLL1_CFG(22, 1, 0, 0), 1056000000}, > + { PLL1_CFG(23, 1, 0, 0), 1104000000}, > + { PLL1_CFG(24, 1, 0, 0), 1152000000}, > + { PLL1_CFG(25, 1, 0, 0), 1200000000}, > + { PLL1_CFG(26, 1, 0, 0), 1248000000}, > + { PLL1_CFG(27, 1, 0, 0), 1296000000}, > + { PLL1_CFG(28, 1, 0, 0), 1344000000}, > + { PLL1_CFG(29, 1, 0, 0), 1392000000}, > + { PLL1_CFG(30, 1, 0, 0), 1440000000}, > + { PLL1_CFG(31, 1, 0, 0), 1488000000}, > + { PLL1_CFG(31, 1, 0, 0), ~0}, > +}; > + > +void clock_set_pll1(int hz) > +{ > + int i = 0; > + int axi, ahb, apb0; > + struct sunxi_ccm_reg * const ccm = > + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; > + > + /* Find target frequency */ > + while (pll1_para[i].freq < hz) > + i++; > + > + hz = pll1_para[i].freq; > + > + /* Calculate system clock divisors */ > + axi = RDIV(hz, 432000000); /* Max 450MHz */ > + ahb = RDIV(hz/axi, 204000000); /* Max 250MHz */ > + apb0 = 2; /* Max 150MHz */ > + > + /* Map divisors to register values */ > + axi = axi - 1; > + if (ahb > 4) > + ahb = 3; > + else if (ahb > 2) > + ahb = 2; > + else if (ahb > 1) > + ahb = 1; > + else > + ahb = 0; > + > + apb0 = apb0 - 1; > + > + /* Switch to 24MHz clock while changing PLL1 */ > + writel(AXI_DIV_1 << 0 | AHB_DIV_2 << 4 | APB0_DIV_1 << 8 | > + CPU_CLK_SRC_OSC24M << 16, &ccm->cpu_ahb_apb0_cfg); > + sdelay(20); > + > + /* Configure sys clock divisors */ > + writel(axi << 0 | ahb << 4 | apb0 << 8 | CPU_CLK_SRC_OSC24M << 16, > + &ccm->cpu_ahb_apb0_cfg); > + > + /* Configure PLL1 at the desired frequency */ > + writel(pll1_para[i].pll1_cfg, &ccm->pll1_cfg); > + sdelay(200); > + > + /* Switch CPU to PLL1 */ > + writel(axi << 0 | ahb << 4 | apb0 << 8 | CPU_CLK_SRC_PLL1 << 16, > + &ccm->cpu_ahb_apb0_cfg); > + sdelay(20); > +} > +#endif > diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c > new file mode 100644 > index 0000000..f169b7b > --- /dev/null > +++ b/arch/arm/cpu/armv7/sunxi/dram.c > @@ -0,0 +1,445 @@ > +/* > + * sunxi DRAM controller initialization > + * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net> > + * > + * Based on sun4i Linux kernel sources mach-sunxi/pm/standby/dram*.c > + * and earlier U-Boot Allwiner A10 SPL work > + * > + * (C) Copyright 2007-2012 > + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> > + * Berg Xing <bergxing@allwinnertech.com> > + * Tom Cubie <tangliang@allwinnertech.com> > + * > + * 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/arch/dram.h> > +#include <asm/arch/timer.h> > +#include <asm/arch/clock.h> > +#include <asm/arch/sys_proto.h> > + > +static void mctl_ddr3_reset(void) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + > +#ifdef CONFIG_SUN4I > + struct sunxi_timer_reg *timer = (struct sunxi_timer_reg *)SUNXI_TIMER_BASE; > + u32 reg_val; > + > + writel(0, &timer->cpu_cfg); > + reg_val = readl(&timer->cpu_cfg); > + reg_val >>= 6; > + reg_val &= 0x3; > + > + if (reg_val != 0) { > + setbits_le32(&dram->mcr, 0x1 << 12); > + sdelay(0x100); > + clrbits_le32(&dram->mcr, 0x1 << 12); > + } else > +#endif > + { > + clrbits_le32(&dram->mcr, 0x1 << 12); > + sdelay(0x100); > + setbits_le32(&dram->mcr, 0x1 << 12); > + } > +} > + > +static void mctl_set_drive(void) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + > + clrsetbits_le32(&dram->mcr, 0x3, (0x6 << 12) | 0xFFC); > +} > + > +static void mctl_itm_disable(void) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + > + setbits_le32(&dram->ccr, 0x1 << 28); > +} > + > +static void mctl_itm_enable(void) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + > + clrbits_le32(&dram->ccr, 0x1 << 28); > +} > + > +static void mctl_enable_dll0(void) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + > + clrsetbits_le32(&dram->dllcr[0], 0x40000000, 0x80000000); > + sdelay(0x100); > + > + clrbits_le32(&dram->dllcr[0], 0xC0000000); > + sdelay(0x1000); > + > + clrsetbits_le32(&dram->dllcr[0], 0x80000000, 0x40000000); > + sdelay(0x1000); > +} > + > +/* > + * Note: This differs from pm/standby in that it checks the bus width > + */ > +static void mctl_enable_dllx(void) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + u32 i, n, bus_width; > + > + bus_width = readl(&dram->dcr); > + bus_width >>= 6; > + bus_width &= 7; > + > + if (bus_width == 3) > + n = 5; > + else > + n = 3; > + > + for (i = 1; i < n; i++) > + clrsetbits_le32(&dram->dllcr[i], 0x40000000, 0x80000000); > + sdelay(0x100); > + > + for (i = 1; i < n; i++) > + clrbits_le32(&dram->dllcr[i], 0xC0000000); > + sdelay(0x1000); > + > + for (i = 1; i < n; i++) > + clrsetbits_le32(&dram->dllcr[i], 0x80000000, 0x40000000); > + sdelay(0x1000); > +} > + > +static u32 hpcr_value[32] = { > +#ifdef CONFIG_SUN5I > + 0, 0, 0, 0, > + 0, 0, 0, 0, > + 0, 0, 0, 0, > + 0, 0, 0, 0, > + 0x1031, 0x1031, 0x0735, 0x1035, > + 0x1035, 0x0731, 0x1031, 0, > + 0x0301, 0x0301, 0x0301, 0x0301, > + 0x0301, 0x0301, 0x0301, 0 > +#endif > +#ifdef CONFIG_SUN4I > + 0x0301, 0x0301, 0x0301, 0x0301, > + 0x0301, 0x0301, 0, 0, > + 0, 0, 0, 0, > + 0, 0, 0, 0, > + 0x1031, 0x1031, 0x0735, 0x1035, > + 0x1035, 0x0731, 0x1031, 0x0735, > + 0x1035, 0x1031, 0x0731, 0x1035, > + 0x1031, 0x0301, 0x0301, 0x0731 > +#endif > +}; > + > +static void mctl_configure_hostport(void) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + u32 i; > + > + for (i = 0; i < 32; i++) > + writel(hpcr_value[i], &dram->hpcr[i]); > +} > + > +static void mctl_setup_dram_clock(u32 clk) > +{ > + u32 reg_val; > + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; > + > + /* setup DRAM PLL */ > + reg_val = readl(&ccm->pll5_cfg); > + reg_val &= ~0x3; > + reg_val |= 0x1; /* m factor */ > + reg_val &= ~(0x3 << 4); > + reg_val |= 0x1 << 4; /* k factor */ > + reg_val &= ~(0x1f << 8); > + reg_val |= ((clk / 24) & 0x1f) << 8; /* n factor */ > + reg_val &= ~(0x3 << 16); > + reg_val |= 0x1 << 16; /* p factor */ > + reg_val &= ~(0x1 << 29); /* PLL on */ > + reg_val |= (u32) 0x1 << 31; /* PLL En */ > + writel(reg_val, &ccm->pll5_cfg); > + sdelay(0x100000); > + > + setbits_le32(&ccm->pll5_cfg, 0x1 << 29); > + > +#ifdef CONFIG_SUN4I > + /* reset GPS */ > + clrbits_le32(&ccm->gps_clk_cfg, 0x3); > + setbits_le32(&ccm->ahb_gate0, 0x1 << 26); > + sdelay(0x20); > + clrbits_le32(&ccm->ahb_gate0, 0x1 << 26); > +#endif > + > + /* setup MBUS clock */ > + reg_val = (0x1 << 31) | (0x2 << 24) | (0x1); > + writel(reg_val, &ccm->mbus_clk_cfg); > + > + /* > + * open DRAMC AHB & DLL register clock > + * close it first > + */ > +#ifdef CONFIG_SUN5I > + clrbits_le32(&ccm->ahb_gate0, 0x3 << 14); > +#else > + clrbits_le32(&ccm->ahb_gate0, 0x1 << 14); > +#endif > + sdelay(0x1000); > + > + /* then open it */ > +#ifdef CONFIG_SUN5I > + setbits_le32(&ccm->ahb_gate0, 0x3 << 14); > +#else > + setbits_le32(&ccm->ahb_gate0, 0x1 << 14); > +#endif > + sdelay(0x1000); > +} > + > +static int dramc_scan_readpipe(void) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + u32 reg_val; > + > + /* data training trigger */ > + setbits_le32(&dram->ccr, 0x1 << 30); > + > + /* check whether data training process is end */ > + while (readl(&dram->ccr) & (0x1 << 30)) > + ; > + > + /* check data training result */ > + reg_val = readl(&dram->csr); > + if (reg_val & (0x1 << 20)) > + return -1; > + > + return 0; > +} > + > +static void dramc_clock_output_en(u32 on) > +{ > +#ifdef CONFIG_SUN5I > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + > + if (on) > + setbits_le32(&dram->mcr, 0x1 << SUN5I_DRAM_MCR_DCLK_OUT_OFFSET); > + else > + clrbits_le32(&dram->mcr, 0x1 << SUN5I_DRAM_MCR_DCLK_OUT_OFFSET); > +#endif > +#ifdef CONFIG_SUN4I > + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; > + if (on) > + setbits_le32(&ccm->dram_clk_cfg, 0x1 << SUN4I_CCM_SDRAM_DCLK_OUT_OFFSET); > + else > + clrbits_le32(&ccm->dram_clk_cfg, 0x1 << SUN4I_CCM_SDRAM_DCLK_OUT_OFFSET); > +#endif > +} > + > +#ifdef CONFIG_SUN4I > +static void dramc_set_autorefresh_cycle(u32 clk) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + u32 reg_val; > + u32 tmp_val; > + u32 dram_size; > + > + if (clk < 600) { > + dram_size = readl(&dram->dcr); > + dram_size >>= 3; > + dram_size &= 0x7; > + if (dram_size <= 0x2) > + reg_val = (131 * clk) >> 10; > + else > + reg_val = (336 * clk) >> 10; > + > + tmp_val = (7987 * clk) >> 10; > + tmp_val = tmp_val * 9 - 200; > + reg_val |= tmp_val << 8; > + reg_val |= 0x8 << 24; > + writel(reg_val, &dram->drr); > + } else { > + writel(0x0, &dram->drr); > + } > +} > +#endif /* SUN4I */ > + > +#ifdef CONFIG_SUN5I > +static void dramc_set_autorefresh_cycle(u32 clk) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + u32 reg_val; > + u32 tmp_val; > + reg_val = 131; > + > + tmp_val = (7987 * clk) >> 10; > + tmp_val = tmp_val * 9 - 200; > + reg_val |= tmp_val << 8; > + reg_val |= 0x8 << 24; > + writel(reg_val, &dram->drr); > +} > +#endif /* SUN5I */ > + > +int dramc_init(struct dram_para *para) > +{ > + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; > + u32 reg_val; > + int ret_val; > + > + /* check input dram parameter structure */ > + if (!para) > + return -1; > + > + /* setup DRAM relative clock */ > + mctl_setup_dram_clock(para->clock); > + > +#ifdef CONFIG_SUN5I > + /* Disable any pad power save control */ > + writel(0, &dram->ppwrsctl); > +#endif > + > + /* reset external DRAM */ > + mctl_ddr3_reset(); > + mctl_set_drive(); > + > + /* dram clock off */ > + dramc_clock_output_en(0); > + > +#ifdef CONFIG_SUN4I > + /* select dram controller 1 */ > + writel(0x16237495, &dram->csel); > +#endif > + > + mctl_itm_disable(); > + mctl_enable_dll0(); > + > + /* configure external DRAM */ > + reg_val = 0; > + if (para->type == 3) > + reg_val |= 0x1; > + reg_val |= (para->io_width >> 3) << 1; > + > + if (para->density == 256) > + reg_val |= 0x0 << 3; > + else if (para->density == 512) > + reg_val |= 0x1 << 3; > + else if (para->density == 1024) > + reg_val |= 0x2 << 3; > + else if (para->density == 2048) > + reg_val |= 0x3 << 3; > + else if (para->density == 4096) > + reg_val |= 0x4 << 3; > + else if (para->density == 8192) > + reg_val |= 0x5 << 3; > + else > + reg_val |= 0x0 << 3; > + > + reg_val |= ((para->bus_width >> 3) - 1) << 6; > + > + reg_val |= (para->rank_num - 1) << 10; > + > + reg_val |= 0x1 << 12; > + reg_val |= ((0x1) & 0x3) << 13; > + > + writel(reg_val, &dram->dcr); > + > +#ifdef CONFIG_SUN5I > + /* set odt impendance divide ratio */ > + reg_val = ((para->zq) >> 8) & 0xfffff; > + reg_val |= ((para->zq) & 0xff) << 20; > + reg_val |= (para->zq) & 0xf0000000; > + writel(reg_val, &dram->zqcr0); > +#endif > + > + /* dram clock on */ > + dramc_clock_output_en(1); > + > + sdelay(0x10); > + > + while (readl(&dram->ccr) & (0x1U << 31)) > + ; > + > + mctl_enable_dllx(); > + > +#ifdef CONFIG_SUN4I > + /* set odt impendance divide ratio */ > + reg_val = ((para->zq) >> 8) & 0xfffff; > + reg_val |= ((para->zq) & 0xff) << 20; > + reg_val |= (para->zq) & 0xf0000000; > + writel(reg_val, &dram->zqcr0); > +#endif > + > +#ifdef CONFIG_SUN4I > + /* set I/O configure register */ > + reg_val = 0x00cc0000; > + reg_val |= (para->odt_en) & 0x3; > + reg_val |= ((para->odt_en) & 0x3) << 30; > + writel(reg_val, &dram->iocr); > +#endif > + > + /* set refresh period */ > + dramc_set_autorefresh_cycle(para->clock); > + > + /* set timing parameters */ > + writel(para->tpr0, &dram->tpr0); > + writel(para->tpr1, &dram->tpr1); > + writel(para->tpr2, &dram->tpr2); > + > + /* set mode register */ > + if (para->type == 3) { > + /* ddr3 */ > + reg_val = 0x0; > +#ifdef CONFIG_SUN5I > + reg_val |= 0x1000; > +#endif > + reg_val |= (para->cas - 4) << 4; > + reg_val |= 0x5 << 9; > + } else if (para->type == 2) { > + /* ddr2 */ > + reg_val = 0x2; > + reg_val |= para->cas << 4; > + reg_val |= 0x5 << 9; > + } > + writel(reg_val, &dram->mr); > + > + writel(para->emr1, &dram->emr); > + writel(para->emr2, &dram->emr2); > + writel(para->emr3, &dram->emr3); > + > + /* set DQS window mode */ > + clrsetbits_le32(&dram->ccr, 0x1U << 17, 0x1U << 14); > + > + /* initial external DRAM */ > + setbits_le32(&dram->ccr, 0x1U << 31); > + > + while (readl(&dram->ccr) & (0x1U << 31)) > + ; > + > + /* scan read pipe value */ > + mctl_itm_enable(); > + ret_val = dramc_scan_readpipe(); > + > + if (ret_val < 0) > + return 0; > + > + /* configure all host port */ > + mctl_configure_hostport(); > + > + return get_ram_size((long *)PHYS_SDRAM_1, 1 << 30); > +} > diff --git a/arch/arm/cpu/armv7/sunxi/u-boot-spl.lds b/arch/arm/cpu/armv7/sunxi/u-boot-spl.lds > new file mode 100644 > index 0000000..cb418e1 > --- /dev/null > +++ b/arch/arm/cpu/armv7/sunxi/u-boot-spl.lds > @@ -0,0 +1,63 @@ > +/* > + * (C) Copyright 2002 > + * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de> > + * > + * (C) Copyright 2010 > + * Texas Instruments, <www.ti.com> > + * Aneesh V <aneesh@ti.com> > + * > + * 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 > + */ > + > +MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\ > + LENGTH = CONFIG_SPL_MAX_SIZE } > +MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \ > + LENGTH = CONFIG_SPL_BSS_MAX_SIZE } > + > +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") > +OUTPUT_ARCH(arm) > +ENTRY(_start) > +SECTIONS > +{ > + .text : > + { > + __start = .; > + arch/arm/cpu/armv7/start.o (.text) > + *(.text*) > + } > .sram > + > + . = ALIGN(4); > + .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram > + > + . = ALIGN(4); > + .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram > + > + . = ALIGN(4); > + __image_copy_end = .; > + _end = .; > + > + .bss : > + { > + . = ALIGN(4); > + __bss_start = .; > + *(.bss*) > + . = ALIGN(4); > + __bss_end__ = .; > + } > .sdram > +} > diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h > new file mode 100644 > index 0000000..404e16a > --- /dev/null > +++ b/arch/arm/include/asm/arch-sunxi/spl.h > @@ -0,0 +1,34 @@ > +/* > + * (C) Copyright 2012 > + * Texas Instruments, <www.ti.com> > + * > + * 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 _ASM_ARCH_SPL_H_ > +#define _ASM_SPL_H_ > + > +#define BOOT_DEVICE_NONE 0 > +#define BOOT_DEVICE_XIP 1 > +#define BOOT_DEVICE_NAND 2 > +#define BOOT_DEVICE_ONE_NAND 3 > +#define BOOT_DEVICE_MMC2 5 /*emmc*/ Add spaces in comment above. > +#define BOOT_DEVICE_MMC1 6 > +#define BOOT_DEVICE_XIPWAIT 7 > +#define BOOT_DEVICE_MMC2_2 0xFF > +#endif > diff --git a/board/sunxi/board.c b/board/sunxi/board.c > index 50fb40f..b917a0a 100644 > --- a/board/sunxi/board.c > +++ b/board/sunxi/board.c > @@ -64,3 +64,49 @@ int board_mmc_init(bd_t *bis) > return 0; > } > #endif > + > +#ifdef CONFIG_SPL_BUILD > +void sunxi_board_init(void) > +{ > + int power_failed = 0; > + int ramsize; > + > + printf("DRAM:"); > + ramsize = sunxi_dram_init(); > + if (!ramsize) { > + printf(" ?"); > + ramsize = sunxi_dram_init(); > + } > + if (!ramsize) { > + printf(" ?"); > + ramsize = sunxi_dram_init(); > + } > + printf(" %dMB\n", ramsize>>20); > + if (!ramsize) > + hang(); > + > +#ifdef CONFIG_AXP209_POWER > + power_failed |= axp209_init(); > + power_failed |= axp209_set_dcdc2(1400); > + power_failed |= axp209_set_dcdc3(1250); > + power_failed |= axp209_set_ldo2(3000); > + power_failed |= axp209_set_ldo3(2800); > + power_failed |= axp209_set_ldo4(2800); > +#endif > + > + /* > + * Only clock up the CPU to full speed if we are reasonably > + * assured it's being powered with suitable core voltage > + */ > + if (!power_failed) > + clock_set_pll1(1008000000); > +} > + > +#ifdef CONFIG_SPL_DISPLAY_PRINT > +void spl_display_print(void) > +{ > + printf("Board: %s\n", CONFIG_SYS_BOARD_NAME); > +} > +#endif > + > +#endif > diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h > index bc1f200..b0dcfdb 100644 > --- a/include/configs/sunxi-common.h > +++ b/include/configs/sunxi-common.h > @@ -134,7 +134,7 @@ > */ > #define CONFIG_SYS_NO_FLASH > > -#define CONFIG_SYS_MONITOR_LEN (256 << 10) /* 256 KB */ > +#define CONFIG_SYS_MONITOR_LEN (512 << 10) /* 512 KB */ > #define CONFIG_IDENT_STRING " Allwinner Technology " > > #define CONFIG_ENV_OFFSET (544 << 10) /* (8 + 24 + 512)KB */ > @@ -190,6 +190,30 @@ > #define CONFIG_CMD_EXT4 /* with this we can access ext4 bootfs */ > #define CONFIG_CMD_ZFS /* with this we can access ZFS bootfs */ > > +#define CONFIG_SPL_FRAMEWORK > +#define CONFIG_SPL_BSS_START_ADDR 0x50000000 > +#define CONFIG_SPL_BSS_MAX_SIZE 0x80000 /* 512 KB */ > + > +#define CONFIG_SPL_TEXT_BASE 0x20 /* sram start+header */ > +#define CONFIG_SPL_MAX_SIZE 0x8000 /* 32 KB */ > + > +#define CONFIG_SPL_LIBCOMMON_SUPPORT > +#define CONFIG_SPL_LIBDISK_SUPPORT > +#define CONFIG_SPL_SERIAL_SUPPORT > +#define CONFIG_SPL_LIBGENERIC_SUPPORT > +#define CONFIG_SPL_MMC_SUPPORT > +#define CONFIG_SPL_DISPLAY_PRINT > + > +/* end of 24KB in sram */ > +#define LOW_LEVEL_SRAM_STACK 0x00006000 > +#define CONFIG_SPL_STACK LOW_LEVEL_SRAM_STACK > +#define CONFIG_SPL_LDSCRIPT "arch/arm/cpu/armv7/sunxi/u-boot-spl.lds" > + > +/* 32KB offset */ > +#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR 64 > +/* SPL starts at offset 8KiB im MMC and has the size of 24KiB */ > +#define CONFIG_SPL_PAD_TO 24576 /* decimal for 'dd' */ > + > #undef CONFIG_CMD_FPGA > #undef CONFIG_CMD_NET > #undef CONFIG_CMD_NFS > @@ -210,4 +234,8 @@ > #define CONFIG_SUNXI_GPIO > #define CONFIG_CMD_GPIO > > +/* PMU */ > +#define CONFIG_SPL_POWER_SUPPORT > +#define CONFIG_AXP209_POWER > + > #endif /* __CONFIG_H */ > diff --git a/spl/Makefile b/spl/Makefile > index 3195390..74d27b1 100644 > --- a/spl/Makefile > +++ b/spl/Makefile > @@ -126,6 +126,10 @@ ifdef CONFIG_SAMSUNG > ALL-y += $(obj)$(BOARD)-spl.bin > endif > > +ifdef CONFIG_SUNXI > +ALL-y += $(obj)sunxi-spl.bin > +endif > + > all: $(ALL-y) > > ifdef CONFIG_SAMSUNG > @@ -134,6 +138,12 @@ $(obj)$(BOARD)-spl.bin: $(obj)u-boot-spl.bin > $(obj)u-boot-spl.bin $(obj)$(BOARD)-spl.bin > endif > > +ifdef CONFIG_SUNXI > +$(obj)sunxi-spl.bin: $(obj)u-boot-spl.bin > + $(OBJTREE)/tools/mksunxiboot \ > + $(obj)u-boot-spl.bin $(obj)sunxi-spl.bin > +endif > + > $(obj)u-boot-spl.bin: $(obj)u-boot-spl > $(OBJCOPY) $(OBJCFLAGS) -O binary $< $@ > > -- > 1.7.7.6 Luka
diff --git a/Makefile b/Makefile index 81fe532..d1b7f97 100644 --- a/Makefile +++ b/Makefile @@ -517,6 +517,16 @@ $(obj)u-boot.spr: $(obj)u-boot.img $(obj)spl/u-boot-spl.bin conv=notrunc 2>/dev/null cat $(obj)spl/u-boot-spl-pad.img $(obj)u-boot.img > $@ +# sunxi: Combined object with SPL U-Boot with sunxi header (sunxi-spl.bin) +# and the full-blown U-Boot attached to it +$(obj)u-boot-sunxi-with-spl.bin: $(obj)spl/sunxi-spl.bin $(obj)u-boot.bin + tr "\000" "\377" < /dev/zero | dd ibs=1 count=$(CONFIG_SPL_PAD_TO) \ + of=$(obj)spl/sunxi-spl-pad.bin 2>/dev/null + dd if=$(obj)spl/sunxi-spl.bin of=$(obj)spl/sunxi-spl-pad.bin \ + conv=notrunc 2>/dev/null + cat $(obj)spl/sunxi-spl-pad.bin $(obj)u-boot.bin > $@ + rm $(obj)spl/sunxi-spl-pad.bin + ifeq ($(SOC),tegra20) ifeq ($(CONFIG_OF_SEPARATE),y) nodtb=dtb @@ -854,6 +864,7 @@ clobber: tidy @[ ! -d $(obj)nand_spl ] || find $(obj)nand_spl -name "*" -type l -print | xargs rm -f @rm -f $(obj)dts/*.tmp @rm -f $(obj)spl/u-boot-spl{,-pad}.ais + @rm -f $(obj)spl/sun?i-spl.bin mrproper \ distclean: clobber unconfig diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c index 29cc4bd..6dc2bd0 100644 --- a/arch/arm/cpu/armv7/sunxi/board.c +++ b/arch/arm/cpu/armv7/sunxi/board.c @@ -27,12 +27,37 @@ #include <common.h> #include <asm/io.h> #include <serial.h> +#include <i2c.h> #include <asm/gpio.h> #include <asm/arch/clock.h> #include <asm/arch/timer.h> #include <asm/arch/gpio.h> #include <asm/arch/sys_proto.h> #include <netdev.h> +#ifdef CONFIG_SPL_BUILD +#include <spl.h> +#endif + +#ifdef CONFIG_SPL_BUILD +/* Pointer to the global data structure for SPL */ +DECLARE_GLOBAL_DATA_PTR; + +/* The sunxi internal brom will try to loader external bootloader + * from mmc0, nannd flash, mmc2. + * Unfortunately we can't check how SPL was loaded so assume + * it's always the first SD/MMC controller + */ +u32 spl_boot_device(void) +{ + return BOOT_DEVICE_MMC1; +} + +/* No confiration data available in SPL yet. Hardcode bootmode */ +u32 spl_boot_mode(void) +{ + return MMCSD_MODE_RAW; +} +#endif int gpio_init(void) { @@ -65,6 +90,19 @@ void s_init(void) #endif clock_init(); gpio_init(); + +#ifdef CONFIG_SPL_BUILD + gd = &gdata; + preloader_console_init(); + +#ifdef CONFIG_SPL_I2C_SUPPORT + /* Needed early by sunxi_board_init if PMU is enabled */ + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif + + sunxi_board_init(); +#endif + } void reset_cpu(ulong addr) diff --git a/arch/arm/cpu/armv7/sunxi/clock.c b/arch/arm/cpu/armv7/sunxi/clock.c index b9bbb7d..91cfae3 100644 --- a/arch/arm/cpu/armv7/sunxi/clock.c +++ b/arch/arm/cpu/armv7/sunxi/clock.c @@ -24,13 +24,34 @@ #include <common.h> #include <asm/io.h> #include <asm/arch/clock.h> +#include <asm/arch/gpio.h> #include <asm/arch/sys_proto.h> +#ifdef CONFIG_SPL_BUILD +static void clock_init_safe(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* Set safe defaults until PMU is configured */ + writel(AXI_DIV_1 << 0 | AHB_DIV_2 << 4 | APB0_DIV_1 << 8 | + CPU_CLK_SRC_OSC24M << 16, &ccm->cpu_ahb_apb0_cfg); + writel(0xa1005000, &ccm->pll1_cfg); + sdelay(200); + writel(AXI_DIV_1 << 0 | AHB_DIV_2 << 4 | APB0_DIV_1 << 8 | + CPU_CLK_SRC_PLL1 << 16, &ccm->cpu_ahb_apb0_cfg); +} +#endif + int clock_init(void) { struct sunxi_ccm_reg *const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; +#ifdef CONFIG_SPL_BUILD + clock_init_safe(); +#endif + /* uart clock source is apb1 */ sr32(&ccm->apb1_clk_div_cfg, 24, 2, APB1_CLK_SRC_OSC24M); sr32(&ccm->apb1_clk_div_cfg, 16, 2, APB1_FACTOR_N); @@ -70,3 +91,81 @@ int clock_twi_onoff(int port, int state) return 0; } + +#ifdef CONFIG_SPL_BUILD +#define PLL1_CFG(N, K, M, P) (1 << 31 | 0 << 30 | 8 << 26 | 0 << 25 | \ + 16 << 20 | (P) << 16 | 2 << 13 | (N) << 8 | \ + (K) << 4 | 0 << 3 | 0 << 2 | (M) << 0) +#define RDIV(a, b) ((a + (b) - 1) / (b)) + +struct { + u32 pll1_cfg; + unsigned int freq; +} pll1_para[] = { + { PLL1_CFG(16, 0, 0, 0), 384000000 }, + { PLL1_CFG(16, 1, 0, 0), 768000000 }, + { PLL1_CFG(20, 1, 0, 0), 960000000 }, + { PLL1_CFG(21, 1, 0, 0), 1008000000}, + { PLL1_CFG(22, 1, 0, 0), 1056000000}, + { PLL1_CFG(23, 1, 0, 0), 1104000000}, + { PLL1_CFG(24, 1, 0, 0), 1152000000}, + { PLL1_CFG(25, 1, 0, 0), 1200000000}, + { PLL1_CFG(26, 1, 0, 0), 1248000000}, + { PLL1_CFG(27, 1, 0, 0), 1296000000}, + { PLL1_CFG(28, 1, 0, 0), 1344000000}, + { PLL1_CFG(29, 1, 0, 0), 1392000000}, + { PLL1_CFG(30, 1, 0, 0), 1440000000}, + { PLL1_CFG(31, 1, 0, 0), 1488000000}, + { PLL1_CFG(31, 1, 0, 0), ~0}, +}; + +void clock_set_pll1(int hz) +{ + int i = 0; + int axi, ahb, apb0; + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* Find target frequency */ + while (pll1_para[i].freq < hz) + i++; + + hz = pll1_para[i].freq; + + /* Calculate system clock divisors */ + axi = RDIV(hz, 432000000); /* Max 450MHz */ + ahb = RDIV(hz/axi, 204000000); /* Max 250MHz */ + apb0 = 2; /* Max 150MHz */ + + /* Map divisors to register values */ + axi = axi - 1; + if (ahb > 4) + ahb = 3; + else if (ahb > 2) + ahb = 2; + else if (ahb > 1) + ahb = 1; + else + ahb = 0; + + apb0 = apb0 - 1; + + /* Switch to 24MHz clock while changing PLL1 */ + writel(AXI_DIV_1 << 0 | AHB_DIV_2 << 4 | APB0_DIV_1 << 8 | + CPU_CLK_SRC_OSC24M << 16, &ccm->cpu_ahb_apb0_cfg); + sdelay(20); + + /* Configure sys clock divisors */ + writel(axi << 0 | ahb << 4 | apb0 << 8 | CPU_CLK_SRC_OSC24M << 16, + &ccm->cpu_ahb_apb0_cfg); + + /* Configure PLL1 at the desired frequency */ + writel(pll1_para[i].pll1_cfg, &ccm->pll1_cfg); + sdelay(200); + + /* Switch CPU to PLL1 */ + writel(axi << 0 | ahb << 4 | apb0 << 8 | CPU_CLK_SRC_PLL1 << 16, + &ccm->cpu_ahb_apb0_cfg); + sdelay(20); +} +#endif diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c new file mode 100644 index 0000000..f169b7b --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/dram.c @@ -0,0 +1,445 @@ +/* + * sunxi DRAM controller initialization + * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net> + * + * Based on sun4i Linux kernel sources mach-sunxi/pm/standby/dram*.c + * and earlier U-Boot Allwiner A10 SPL work + * + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing <bergxing@allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + * + * 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/arch/dram.h> +#include <asm/arch/timer.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +static void mctl_ddr3_reset(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + +#ifdef CONFIG_SUN4I + struct sunxi_timer_reg *timer = (struct sunxi_timer_reg *)SUNXI_TIMER_BASE; + u32 reg_val; + + writel(0, &timer->cpu_cfg); + reg_val = readl(&timer->cpu_cfg); + reg_val >>= 6; + reg_val &= 0x3; + + if (reg_val != 0) { + setbits_le32(&dram->mcr, 0x1 << 12); + sdelay(0x100); + clrbits_le32(&dram->mcr, 0x1 << 12); + } else +#endif + { + clrbits_le32(&dram->mcr, 0x1 << 12); + sdelay(0x100); + setbits_le32(&dram->mcr, 0x1 << 12); + } +} + +static void mctl_set_drive(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + clrsetbits_le32(&dram->mcr, 0x3, (0x6 << 12) | 0xFFC); +} + +static void mctl_itm_disable(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + setbits_le32(&dram->ccr, 0x1 << 28); +} + +static void mctl_itm_enable(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + clrbits_le32(&dram->ccr, 0x1 << 28); +} + +static void mctl_enable_dll0(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + clrsetbits_le32(&dram->dllcr[0], 0x40000000, 0x80000000); + sdelay(0x100); + + clrbits_le32(&dram->dllcr[0], 0xC0000000); + sdelay(0x1000); + + clrsetbits_le32(&dram->dllcr[0], 0x80000000, 0x40000000); + sdelay(0x1000); +} + +/* + * Note: This differs from pm/standby in that it checks the bus width + */ +static void mctl_enable_dllx(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 i, n, bus_width; + + bus_width = readl(&dram->dcr); + bus_width >>= 6; + bus_width &= 7; + + if (bus_width == 3) + n = 5; + else + n = 3; + + for (i = 1; i < n; i++) + clrsetbits_le32(&dram->dllcr[i], 0x40000000, 0x80000000); + sdelay(0x100); + + for (i = 1; i < n; i++) + clrbits_le32(&dram->dllcr[i], 0xC0000000); + sdelay(0x1000); + + for (i = 1; i < n; i++) + clrsetbits_le32(&dram->dllcr[i], 0x80000000, 0x40000000); + sdelay(0x1000); +} + +static u32 hpcr_value[32] = { +#ifdef CONFIG_SUN5I + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x1031, 0x1031, 0x0735, 0x1035, + 0x1035, 0x0731, 0x1031, 0, + 0x0301, 0x0301, 0x0301, 0x0301, + 0x0301, 0x0301, 0x0301, 0 +#endif +#ifdef CONFIG_SUN4I + 0x0301, 0x0301, 0x0301, 0x0301, + 0x0301, 0x0301, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x1031, 0x1031, 0x0735, 0x1035, + 0x1035, 0x0731, 0x1031, 0x0735, + 0x1035, 0x1031, 0x0731, 0x1035, + 0x1031, 0x0301, 0x0301, 0x0731 +#endif +}; + +static void mctl_configure_hostport(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 i; + + for (i = 0; i < 32; i++) + writel(hpcr_value[i], &dram->hpcr[i]); +} + +static void mctl_setup_dram_clock(u32 clk) +{ + u32 reg_val; + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* setup DRAM PLL */ + reg_val = readl(&ccm->pll5_cfg); + reg_val &= ~0x3; + reg_val |= 0x1; /* m factor */ + reg_val &= ~(0x3 << 4); + reg_val |= 0x1 << 4; /* k factor */ + reg_val &= ~(0x1f << 8); + reg_val |= ((clk / 24) & 0x1f) << 8; /* n factor */ + reg_val &= ~(0x3 << 16); + reg_val |= 0x1 << 16; /* p factor */ + reg_val &= ~(0x1 << 29); /* PLL on */ + reg_val |= (u32) 0x1 << 31; /* PLL En */ + writel(reg_val, &ccm->pll5_cfg); + sdelay(0x100000); + + setbits_le32(&ccm->pll5_cfg, 0x1 << 29); + +#ifdef CONFIG_SUN4I + /* reset GPS */ + clrbits_le32(&ccm->gps_clk_cfg, 0x3); + setbits_le32(&ccm->ahb_gate0, 0x1 << 26); + sdelay(0x20); + clrbits_le32(&ccm->ahb_gate0, 0x1 << 26); +#endif + + /* setup MBUS clock */ + reg_val = (0x1 << 31) | (0x2 << 24) | (0x1); + writel(reg_val, &ccm->mbus_clk_cfg); + + /* + * open DRAMC AHB & DLL register clock + * close it first + */ +#ifdef CONFIG_SUN5I + clrbits_le32(&ccm->ahb_gate0, 0x3 << 14); +#else + clrbits_le32(&ccm->ahb_gate0, 0x1 << 14); +#endif + sdelay(0x1000); + + /* then open it */ +#ifdef CONFIG_SUN5I + setbits_le32(&ccm->ahb_gate0, 0x3 << 14); +#else + setbits_le32(&ccm->ahb_gate0, 0x1 << 14); +#endif + sdelay(0x1000); +} + +static int dramc_scan_readpipe(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + + /* data training trigger */ + setbits_le32(&dram->ccr, 0x1 << 30); + + /* check whether data training process is end */ + while (readl(&dram->ccr) & (0x1 << 30)) + ; + + /* check data training result */ + reg_val = readl(&dram->csr); + if (reg_val & (0x1 << 20)) + return -1; + + return 0; +} + +static void dramc_clock_output_en(u32 on) +{ +#ifdef CONFIG_SUN5I + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + if (on) + setbits_le32(&dram->mcr, 0x1 << SUN5I_DRAM_MCR_DCLK_OUT_OFFSET); + else + clrbits_le32(&dram->mcr, 0x1 << SUN5I_DRAM_MCR_DCLK_OUT_OFFSET); +#endif +#ifdef CONFIG_SUN4I + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + if (on) + setbits_le32(&ccm->dram_clk_cfg, 0x1 << SUN4I_CCM_SDRAM_DCLK_OUT_OFFSET); + else + clrbits_le32(&ccm->dram_clk_cfg, 0x1 << SUN4I_CCM_SDRAM_DCLK_OUT_OFFSET); +#endif +} + +#ifdef CONFIG_SUN4I +static void dramc_set_autorefresh_cycle(u32 clk) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + u32 tmp_val; + u32 dram_size; + + if (clk < 600) { + dram_size = readl(&dram->dcr); + dram_size >>= 3; + dram_size &= 0x7; + if (dram_size <= 0x2) + reg_val = (131 * clk) >> 10; + else + reg_val = (336 * clk) >> 10; + + tmp_val = (7987 * clk) >> 10; + tmp_val = tmp_val * 9 - 200; + reg_val |= tmp_val << 8; + reg_val |= 0x8 << 24; + writel(reg_val, &dram->drr); + } else { + writel(0x0, &dram->drr); + } +} +#endif /* SUN4I */ + +#ifdef CONFIG_SUN5I +static void dramc_set_autorefresh_cycle(u32 clk) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + u32 tmp_val; + reg_val = 131; + + tmp_val = (7987 * clk) >> 10; + tmp_val = tmp_val * 9 - 200; + reg_val |= tmp_val << 8; + reg_val |= 0x8 << 24; + writel(reg_val, &dram->drr); +} +#endif /* SUN5I */ + +int dramc_init(struct dram_para *para) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + int ret_val; + + /* check input dram parameter structure */ + if (!para) + return -1; + + /* setup DRAM relative clock */ + mctl_setup_dram_clock(para->clock); + +#ifdef CONFIG_SUN5I + /* Disable any pad power save control */ + writel(0, &dram->ppwrsctl); +#endif + + /* reset external DRAM */ + mctl_ddr3_reset(); + mctl_set_drive(); + + /* dram clock off */ + dramc_clock_output_en(0); + +#ifdef CONFIG_SUN4I + /* select dram controller 1 */ + writel(0x16237495, &dram->csel); +#endif + + mctl_itm_disable(); + mctl_enable_dll0(); + + /* configure external DRAM */ + reg_val = 0; + if (para->type == 3) + reg_val |= 0x1; + reg_val |= (para->io_width >> 3) << 1; + + if (para->density == 256) + reg_val |= 0x0 << 3; + else if (para->density == 512) + reg_val |= 0x1 << 3; + else if (para->density == 1024) + reg_val |= 0x2 << 3; + else if (para->density == 2048) + reg_val |= 0x3 << 3; + else if (para->density == 4096) + reg_val |= 0x4 << 3; + else if (para->density == 8192) + reg_val |= 0x5 << 3; + else + reg_val |= 0x0 << 3; + + reg_val |= ((para->bus_width >> 3) - 1) << 6; + + reg_val |= (para->rank_num - 1) << 10; + + reg_val |= 0x1 << 12; + reg_val |= ((0x1) & 0x3) << 13; + + writel(reg_val, &dram->dcr); + +#ifdef CONFIG_SUN5I + /* set odt impendance divide ratio */ + reg_val = ((para->zq) >> 8) & 0xfffff; + reg_val |= ((para->zq) & 0xff) << 20; + reg_val |= (para->zq) & 0xf0000000; + writel(reg_val, &dram->zqcr0); +#endif + + /* dram clock on */ + dramc_clock_output_en(1); + + sdelay(0x10); + + while (readl(&dram->ccr) & (0x1U << 31)) + ; + + mctl_enable_dllx(); + +#ifdef CONFIG_SUN4I + /* set odt impendance divide ratio */ + reg_val = ((para->zq) >> 8) & 0xfffff; + reg_val |= ((para->zq) & 0xff) << 20; + reg_val |= (para->zq) & 0xf0000000; + writel(reg_val, &dram->zqcr0); +#endif + +#ifdef CONFIG_SUN4I + /* set I/O configure register */ + reg_val = 0x00cc0000; + reg_val |= (para->odt_en) & 0x3; + reg_val |= ((para->odt_en) & 0x3) << 30; + writel(reg_val, &dram->iocr); +#endif + + /* set refresh period */ + dramc_set_autorefresh_cycle(para->clock); + + /* set timing parameters */ + writel(para->tpr0, &dram->tpr0); + writel(para->tpr1, &dram->tpr1); + writel(para->tpr2, &dram->tpr2); + + /* set mode register */ + if (para->type == 3) { + /* ddr3 */ + reg_val = 0x0; +#ifdef CONFIG_SUN5I + reg_val |= 0x1000; +#endif + reg_val |= (para->cas - 4) << 4; + reg_val |= 0x5 << 9; + } else if (para->type == 2) { + /* ddr2 */ + reg_val = 0x2; + reg_val |= para->cas << 4; + reg_val |= 0x5 << 9; + } + writel(reg_val, &dram->mr); + + writel(para->emr1, &dram->emr); + writel(para->emr2, &dram->emr2); + writel(para->emr3, &dram->emr3); + + /* set DQS window mode */ + clrsetbits_le32(&dram->ccr, 0x1U << 17, 0x1U << 14); + + /* initial external DRAM */ + setbits_le32(&dram->ccr, 0x1U << 31); + + while (readl(&dram->ccr) & (0x1U << 31)) + ; + + /* scan read pipe value */ + mctl_itm_enable(); + ret_val = dramc_scan_readpipe(); + + if (ret_val < 0) + return 0; + + /* configure all host port */ + mctl_configure_hostport(); + + return get_ram_size((long *)PHYS_SDRAM_1, 1 << 30); +} diff --git a/arch/arm/cpu/armv7/sunxi/u-boot-spl.lds b/arch/arm/cpu/armv7/sunxi/u-boot-spl.lds new file mode 100644 index 0000000..cb418e1 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/u-boot-spl.lds @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de> + * + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * Aneesh V <aneesh@ti.com> + * + * 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 + */ + +MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\ + LENGTH = CONFIG_SPL_MAX_SIZE } +MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \ + LENGTH = CONFIG_SPL_BSS_MAX_SIZE } + +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + .text : + { + __start = .; + arch/arm/cpu/armv7/start.o (.text) + *(.text*) + } > .sram + + . = ALIGN(4); + .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram + + . = ALIGN(4); + .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram + + . = ALIGN(4); + __image_copy_end = .; + _end = .; + + .bss : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + . = ALIGN(4); + __bss_end__ = .; + } > .sdram +} diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h new file mode 100644 index 0000000..404e16a --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/spl.h @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2012 + * Texas Instruments, <www.ti.com> + * + * 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 _ASM_ARCH_SPL_H_ +#define _ASM_SPL_H_ + +#define BOOT_DEVICE_NONE 0 +#define BOOT_DEVICE_XIP 1 +#define BOOT_DEVICE_NAND 2 +#define BOOT_DEVICE_ONE_NAND 3 +#define BOOT_DEVICE_MMC2 5 /*emmc*/ +#define BOOT_DEVICE_MMC1 6 +#define BOOT_DEVICE_XIPWAIT 7 +#define BOOT_DEVICE_MMC2_2 0xFF +#endif diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 50fb40f..b917a0a 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -64,3 +64,49 @@ int board_mmc_init(bd_t *bis) return 0; } #endif + +#ifdef CONFIG_SPL_BUILD +void sunxi_board_init(void) +{ + int power_failed = 0; + int ramsize; + + printf("DRAM:"); + ramsize = sunxi_dram_init(); + if (!ramsize) { + printf(" ?"); + ramsize = sunxi_dram_init(); + } + if (!ramsize) { + printf(" ?"); + ramsize = sunxi_dram_init(); + } + printf(" %dMB\n", ramsize>>20); + if (!ramsize) + hang(); + +#ifdef CONFIG_AXP209_POWER + power_failed |= axp209_init(); + power_failed |= axp209_set_dcdc2(1400); + power_failed |= axp209_set_dcdc3(1250); + power_failed |= axp209_set_ldo2(3000); + power_failed |= axp209_set_ldo3(2800); + power_failed |= axp209_set_ldo4(2800); +#endif + + /* + * Only clock up the CPU to full speed if we are reasonably + * assured it's being powered with suitable core voltage + */ + if (!power_failed) + clock_set_pll1(1008000000); +} + +#ifdef CONFIG_SPL_DISPLAY_PRINT +void spl_display_print(void) +{ + printf("Board: %s\n", CONFIG_SYS_BOARD_NAME); +} +#endif + +#endif diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index bc1f200..b0dcfdb 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -134,7 +134,7 @@ */ #define CONFIG_SYS_NO_FLASH -#define CONFIG_SYS_MONITOR_LEN (256 << 10) /* 256 KB */ +#define CONFIG_SYS_MONITOR_LEN (512 << 10) /* 512 KB */ #define CONFIG_IDENT_STRING " Allwinner Technology " #define CONFIG_ENV_OFFSET (544 << 10) /* (8 + 24 + 512)KB */ @@ -190,6 +190,30 @@ #define CONFIG_CMD_EXT4 /* with this we can access ext4 bootfs */ #define CONFIG_CMD_ZFS /* with this we can access ZFS bootfs */ +#define CONFIG_SPL_FRAMEWORK +#define CONFIG_SPL_BSS_START_ADDR 0x50000000 +#define CONFIG_SPL_BSS_MAX_SIZE 0x80000 /* 512 KB */ + +#define CONFIG_SPL_TEXT_BASE 0x20 /* sram start+header */ +#define CONFIG_SPL_MAX_SIZE 0x8000 /* 32 KB */ + +#define CONFIG_SPL_LIBCOMMON_SUPPORT +#define CONFIG_SPL_LIBDISK_SUPPORT +#define CONFIG_SPL_SERIAL_SUPPORT +#define CONFIG_SPL_LIBGENERIC_SUPPORT +#define CONFIG_SPL_MMC_SUPPORT +#define CONFIG_SPL_DISPLAY_PRINT + +/* end of 24KB in sram */ +#define LOW_LEVEL_SRAM_STACK 0x00006000 +#define CONFIG_SPL_STACK LOW_LEVEL_SRAM_STACK +#define CONFIG_SPL_LDSCRIPT "arch/arm/cpu/armv7/sunxi/u-boot-spl.lds" + +/* 32KB offset */ +#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR 64 +/* SPL starts at offset 8KiB im MMC and has the size of 24KiB */ +#define CONFIG_SPL_PAD_TO 24576 /* decimal for 'dd' */ + #undef CONFIG_CMD_FPGA #undef CONFIG_CMD_NET #undef CONFIG_CMD_NFS @@ -210,4 +234,8 @@ #define CONFIG_SUNXI_GPIO #define CONFIG_CMD_GPIO +/* PMU */ +#define CONFIG_SPL_POWER_SUPPORT +#define CONFIG_AXP209_POWER + #endif /* __CONFIG_H */ diff --git a/spl/Makefile b/spl/Makefile index 3195390..74d27b1 100644 --- a/spl/Makefile +++ b/spl/Makefile @@ -126,6 +126,10 @@ ifdef CONFIG_SAMSUNG ALL-y += $(obj)$(BOARD)-spl.bin endif +ifdef CONFIG_SUNXI +ALL-y += $(obj)sunxi-spl.bin +endif + all: $(ALL-y) ifdef CONFIG_SAMSUNG @@ -134,6 +138,12 @@ $(obj)$(BOARD)-spl.bin: $(obj)u-boot-spl.bin $(obj)u-boot-spl.bin $(obj)$(BOARD)-spl.bin endif +ifdef CONFIG_SUNXI +$(obj)sunxi-spl.bin: $(obj)u-boot-spl.bin + $(OBJTREE)/tools/mksunxiboot \ + $(obj)u-boot-spl.bin $(obj)sunxi-spl.bin +endif + $(obj)u-boot-spl.bin: $(obj)u-boot-spl $(OBJCOPY) $(OBJCFLAGS) -O binary $< $@