summaryrefslogtreecommitdiff
path: root/arch/arm/mach-mvebu
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-mvebu')
-rw-r--r--arch/arm/mach-mvebu/Makefile4
-rw-r--r--arch/arm/mach-mvebu/cpu.c103
-rw-r--r--arch/arm/mach-mvebu/dram.c180
-rw-r--r--arch/arm/mach-mvebu/include/mach/cpu.h13
-rw-r--r--arch/arm/mach-mvebu/include/mach/soc.h29
-rw-r--r--arch/arm/mach-mvebu/mbus.c57
6 files changed, 352 insertions, 34 deletions
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile
index 446ce04109..21c56a4d96 100644
--- a/arch/arm/mach-mvebu/Makefile
+++ b/arch/arm/mach-mvebu/Makefile
@@ -14,6 +14,10 @@ else
obj-y = cpu.o
obj-y += dram.o
+ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_SYS_MVEBU_DDR_A38X) += ../../../drivers/ddr/marvell/a38x/xor.o
+obj-$(CONFIG_SYS_MVEBU_DDR_AXP) += ../../../drivers/ddr/marvell/axp/xor.o
+endif
obj-y += gpio.o
obj-y += mbus.o
obj-y += timer.o
diff --git a/arch/arm/mach-mvebu/cpu.c b/arch/arm/mach-mvebu/cpu.c
index 9496d5fc5b..ea83e21d43 100644
--- a/arch/arm/mach-mvebu/cpu.c
+++ b/arch/arm/mach-mvebu/cpu.c
@@ -18,19 +18,13 @@
#define DDR_SIZE_CS_OFF(n) (0x0004 + ((n) << 3))
static struct mbus_win windows[] = {
- /* PCIE MEM address space */
- { DEFADR_PCI_MEM, 256 << 20, CPU_TARGET_PCIE13, CPU_ATTR_PCIE_MEM },
-
- /* PCIE IO address space */
- { DEFADR_PCI_IO, 64 << 10, CPU_TARGET_PCIE13, CPU_ATTR_PCIE_IO },
-
/* SPI */
- { DEFADR_SPIF, 8 << 20, CPU_TARGET_DEVICEBUS_BOOTROM_SPI,
- CPU_ATTR_SPIFLASH },
+ { MBUS_SPI_BASE, MBUS_SPI_SIZE,
+ CPU_TARGET_DEVICEBUS_BOOTROM_SPI, CPU_ATTR_SPIFLASH },
/* NOR */
- { DEFADR_BOOTROM, 8 << 20, CPU_TARGET_DEVICEBUS_BOOTROM_SPI,
- CPU_ATTR_BOOTROM },
+ { MBUS_BOOTROM_BASE, MBUS_BOOTROM_SIZE,
+ CPU_TARGET_DEVICEBUS_BOOTROM_SPI, CPU_ATTR_BOOTROM },
};
void reset_cpu(unsigned long ignored)
@@ -177,17 +171,69 @@ static void set_cbar(u32 addr)
asm("mcr p15, 4, %0, c15, c0" : : "r" (addr));
}
+#define MV_USB_PHY_BASE (MVEBU_AXP_USB_BASE + 0x800)
+#define MV_USB_PHY_PLL_REG(reg) (MV_USB_PHY_BASE | (((reg) & 0xF) << 2))
+#define MV_USB_X3_BASE(addr) (MVEBU_AXP_USB_BASE | BIT(11) | \
+ (((addr) & 0xF) << 6))
+#define MV_USB_X3_PHY_CHANNEL(dev, reg) (MV_USB_X3_BASE((dev) + 1) | \
+ (((reg) & 0xF) << 2))
-int arch_cpu_init(void)
+static void setup_usb_phys(void)
{
-#ifndef CONFIG_SPL_BUILD
+ int dev;
+
+ /*
+ * USB PLL init
+ */
+
+ /* Setup PLL frequency */
+ /* USB REF frequency = 25 MHz */
+ clrsetbits_le32(MV_USB_PHY_PLL_REG(1), 0x3ff, 0x605);
+
+ /* Power up PLL and PHY channel */
+ clrsetbits_le32(MV_USB_PHY_PLL_REG(2), 0, BIT(9));
+
+ /* Assert VCOCAL_START */
+ clrsetbits_le32(MV_USB_PHY_PLL_REG(1), 0, BIT(21));
+
+ mdelay(1);
+
/*
- * Only with disabled MMU its possible to switch the base
- * register address on Armada 38x. Without this the SDRAM
- * located at >= 0x4000.0000 is also not accessible, as its
- * still locked to cache.
+ * USB PHY init (change from defaults) specific for 40nm (78X30 78X60)
*/
- mmu_disable();
+
+ for (dev = 0; dev < 3; dev++) {
+ clrsetbits_le32(MV_USB_X3_PHY_CHANNEL(dev, 3), 0, BIT(15));
+
+ /* Assert REG_RCAL_START in channel REG 1 */
+ clrsetbits_le32(MV_USB_X3_PHY_CHANNEL(dev, 1), 0, BIT(12));
+ udelay(40);
+ clrsetbits_le32(MV_USB_X3_PHY_CHANNEL(dev, 1), BIT(12), 0);
+ }
+}
+
+int arch_cpu_init(void)
+{
+#ifndef CONFIG_SPL_BUILD
+ if (mvebu_soc_family() == MVEBU_SOC_A38X) {
+ struct pl310_regs *const pl310 =
+ (struct pl310_regs *)CONFIG_SYS_PL310_BASE;
+
+ /*
+ * Only with disabled MMU its possible to switch the base
+ * register address on Armada 38x. Without this the SDRAM
+ * located at >= 0x4000.0000 is also not accessible, as its
+ * still locked to cache.
+ *
+ * So to fully release / unlock this area from cache, we need
+ * to first flush all caches, then disable the MMU and
+ * disable the L2 cache.
+ */
+ icache_disable();
+ dcache_disable();
+ mmu_disable();
+ clrbits_le32(&pl310->pl310_ctrl, L2X0_CTRL_EN);
+ }
#endif
/* Linux expects the internal registers to be at 0xf1000000 */
@@ -227,10 +273,33 @@ int arch_cpu_init(void)
*/
mvebu_mbus_probe(windows, ARRAY_SIZE(windows));
+ if (mvebu_soc_family() == MVEBU_SOC_AXP) {
+ /* Enable GBE0, GBE1, LCD and NFC PUP */
+ clrsetbits_le32(ARMADA_XP_PUP_ENABLE, 0,
+ GE0_PUP_EN | GE1_PUP_EN | LCD_PUP_EN |
+ NAND_PUP_EN | SPI_PUP_EN);
+
+ /* Configure USB PLL and PHYs on AXP */
+ setup_usb_phys();
+ }
+
+ /* Enable NAND and NAND arbiter */
+ clrsetbits_le32(MVEBU_SOC_DEV_MUX_REG, 0, NAND_EN | NAND_ARBITER_EN);
+
+ /* Disable MBUS error propagation */
+ clrsetbits_le32(SOC_COHERENCY_FABRIC_CTRL_REG, MBUS_ERR_PROP_EN, 0);
+
return 0;
}
#endif /* CONFIG_ARCH_CPU_INIT */
+u32 mvebu_get_nand_clock(void)
+{
+ return CONFIG_SYS_MVEBU_PLL_CLOCK /
+ ((readl(MVEBU_CORE_DIV_CLK_CTRL(1)) &
+ NAND_ECC_DIVCKL_RATIO_MASK) >> NAND_ECC_DIVCKL_RATIO_OFFS);
+}
+
/*
* SOC specific misc init
*/
diff --git a/arch/arm/mach-mvebu/dram.c b/arch/arm/mach-mvebu/dram.c
index db18791a86..ddc5b7ee3e 100644
--- a/arch/arm/mach-mvebu/dram.c
+++ b/arch/arm/mach-mvebu/dram.c
@@ -12,6 +12,15 @@
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
+#ifdef CONFIG_SYS_MVEBU_DDR_A38X
+#include "../../../drivers/ddr/marvell/axp/xor.h"
+#include "../../../drivers/ddr/marvell/axp/xor_regs.h"
+#endif
+#ifdef CONFIG_SYS_MVEBU_DDR_AXP
+#include "../../../drivers/ddr/marvell/axp/xor.h"
+#include "../../../drivers/ddr/marvell/axp/xor_regs.h"
+#endif
+
DECLARE_GLOBAL_DATA_PTR;
struct sdram_bank {
@@ -28,6 +37,16 @@ struct sdram_addr_dec {
#define REG_CPUCS_WIN_WIN0_CS(x) (((x) & 0x3) << 2)
#define REG_CPUCS_WIN_SIZE(x) (((x) & 0xff) << 24)
+#define SDRAM_SIZE_MAX 0xc0000000
+
+#define SCRUB_MAGIC 0xbeefdead
+
+#define SCRB_XOR_UNIT 0
+#define SCRB_XOR_CHAN 1
+#define SCRB_XOR_WIN 0
+
+#define XEBARX_BASE_OFFS 16
+
/*
* mvebu_sdram_bar - reads SDRAM Base Address Register
*/
@@ -93,32 +112,146 @@ void mvebu_sdram_size_adjust(enum memory_bank bank)
mvebu_sdram_bs_set(bank, size);
}
-#ifndef CONFIG_SYS_BOARD_DRAM_INIT
+#if defined(CONFIG_SYS_MVEBU_DDR_A38X) || defined(CONFIG_SYS_MVEBU_DDR_AXP)
+static u32 xor_ctrl_save;
+static u32 xor_base_save;
+static u32 xor_mask_save;
+
+static void mv_xor_init2(u32 cs)
+{
+ u32 reg, base, size, base2;
+ u32 bank_attr[4] = { 0xe00, 0xd00, 0xb00, 0x700 };
+
+ xor_ctrl_save = reg_read(XOR_WINDOW_CTRL_REG(SCRB_XOR_UNIT,
+ SCRB_XOR_CHAN));
+ xor_base_save = reg_read(XOR_BASE_ADDR_REG(SCRB_XOR_UNIT,
+ SCRB_XOR_WIN));
+ xor_mask_save = reg_read(XOR_SIZE_MASK_REG(SCRB_XOR_UNIT,
+ SCRB_XOR_WIN));
+
+ /* Enable Window x for each CS */
+ reg = 0x1;
+ reg |= (0x3 << 16);
+ reg_write(XOR_WINDOW_CTRL_REG(SCRB_XOR_UNIT, SCRB_XOR_CHAN), reg);
+
+ base = 0;
+ size = mvebu_sdram_bs(cs) - 1;
+ if (size) {
+ base2 = ((base / (64 << 10)) << XEBARX_BASE_OFFS) |
+ bank_attr[cs];
+ reg_write(XOR_BASE_ADDR_REG(SCRB_XOR_UNIT, SCRB_XOR_WIN),
+ base2);
+
+ base += size + 1;
+ size = (size / (64 << 10)) << 16;
+ /* Window x - size - 256 MB */
+ reg_write(XOR_SIZE_MASK_REG(SCRB_XOR_UNIT, SCRB_XOR_WIN), size);
+ }
+
+ mv_xor_hal_init(0);
+
+ return;
+}
+
+static void mv_xor_finish2(void)
+{
+ reg_write(XOR_WINDOW_CTRL_REG(SCRB_XOR_UNIT, SCRB_XOR_CHAN),
+ xor_ctrl_save);
+ reg_write(XOR_BASE_ADDR_REG(SCRB_XOR_UNIT, SCRB_XOR_WIN),
+ xor_base_save);
+ reg_write(XOR_SIZE_MASK_REG(SCRB_XOR_UNIT, SCRB_XOR_WIN),
+ xor_mask_save);
+}
+
+static void dram_ecc_scrubbing(void)
+{
+ int cs;
+ u32 size, temp;
+ u32 total_mem = 0;
+ u64 total;
+ u32 start_addr;
+
+ /*
+ * The DDR training code from the bin_hdr / SPL already
+ * scrubbed the DDR till 0x1000000. And the main U-Boot
+ * is loaded to an address < 0x1000000. So we need to
+ * skip this range to not re-scrub this area again.
+ */
+ temp = reg_read(REG_SDRAM_CONFIG_ADDR);
+ temp |= (1 << REG_SDRAM_CONFIG_IERR_OFFS);
+ reg_write(REG_SDRAM_CONFIG_ADDR, temp);
+
+ for (cs = 0; cs < CONFIG_NR_DRAM_BANKS; cs++) {
+ size = mvebu_sdram_bs(cs) - 1;
+ if (size == 0)
+ continue;
+
+ total = (u64)size + 1;
+ total_mem += (u32)(total / (1 << 30));
+ start_addr = 0;
+ mv_xor_init2(cs);
+
+ /* Skip first 16 MiB */
+ if (0 == cs) {
+ start_addr = 0x1000000;
+ size -= start_addr;
+ }
+
+ mv_xor_mem_init(SCRB_XOR_CHAN, start_addr, size,
+ SCRUB_MAGIC, SCRUB_MAGIC);
+
+ /* Wait for previous transfer completion */
+ while (mv_xor_state_get(SCRB_XOR_CHAN) != MV_IDLE)
+ ;
+
+ mv_xor_finish2();
+ }
+
+ temp = reg_read(REG_SDRAM_CONFIG_ADDR);
+ temp &= ~(1 << REG_SDRAM_CONFIG_IERR_OFFS);
+ reg_write(REG_SDRAM_CONFIG_ADDR, temp);
+}
+
+static int ecc_enabled(void)
+{
+ if (reg_read(REG_SDRAM_CONFIG_ADDR) & (1 << REG_SDRAM_CONFIG_ECC_OFFS))
+ return 1;
+
+ return 0;
+}
+#else
+static void dram_ecc_scrubbing(void)
+{
+}
+
+static int ecc_enabled(void)
+{
+ return 0;
+}
+#endif
+
int dram_init(void)
{
+ u64 size = 0;
int i;
- gd->ram_size = 0;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
- gd->bd->bi_dram[i].start = mvebu_sdram_bar(i);
- gd->bd->bi_dram[i].size = mvebu_sdram_bs(i);
/*
* It is assumed that all memory banks are consecutive
* and without gaps.
* If the gap is found, ram_size will be reported for
* consecutive memory only
*/
- if (gd->bd->bi_dram[i].start != gd->ram_size)
+ if (mvebu_sdram_bar(i) != size)
break;
/*
* Don't report more than 3GiB of SDRAM, otherwise there is no
* address space left for the internal registers etc.
*/
- if ((gd->ram_size + gd->bd->bi_dram[i].size != 0) &&
- (gd->ram_size + gd->bd->bi_dram[i].size <= (3 << 30)))
- gd->ram_size += gd->bd->bi_dram[i].size;
-
+ size += mvebu_sdram_bs(i);
+ if (size > SDRAM_SIZE_MAX)
+ size = SDRAM_SIZE_MAX;
}
for (; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -130,6 +263,12 @@ int dram_init(void)
gd->bd->bi_dram[i].size = 0;
}
+
+ if (ecc_enabled())
+ dram_ecc_scrubbing();
+
+ gd->ram_size = size;
+
return 0;
}
@@ -139,6 +278,25 @@ int dram_init(void)
*/
void dram_init_banksize(void)
{
- dram_init();
+ u64 size = 0;
+ int i;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+ gd->bd->bi_dram[i].start = mvebu_sdram_bar(i);
+ gd->bd->bi_dram[i].size = mvebu_sdram_bs(i);
+
+ /* Clip the banksize to 1GiB if it exceeds the max size */
+ size += gd->bd->bi_dram[i].size;
+ if (size > SDRAM_SIZE_MAX)
+ mvebu_sdram_bs_set(i, 0x40000000);
+ }
+}
+
+void board_add_ram_info(int use_default)
+{
+ if (ecc_enabled())
+ printf(" (ECC");
+ else
+ printf(" (ECC not");
+ printf(" enabled)");
}
-#endif /* CONFIG_SYS_BOARD_DRAM_INIT */
diff --git a/arch/arm/mach-mvebu/include/mach/cpu.h b/arch/arm/mach-mvebu/include/mach/cpu.h
index 8bcdef689f..6fa41736e2 100644
--- a/arch/arm/mach-mvebu/include/mach/cpu.h
+++ b/arch/arm/mach-mvebu/include/mach/cpu.h
@@ -65,10 +65,14 @@ enum {
/*
* Default Device Address MAP BAR values
*/
-#define DEFADR_PCI_MEM 0x90000000
-#define DEFADR_PCI_IO 0xC0000000
-#define DEFADR_SPIF 0xF4000000
-#define DEFADR_BOOTROM 0xF8000000
+#define MBUS_PCI_MEM_BASE 0xE8000000
+#define MBUS_PCI_MEM_SIZE (128 << 20)
+#define MBUS_PCI_IO_BASE 0xF1100000
+#define MBUS_PCI_IO_SIZE (64 << 10)
+#define MBUS_SPI_BASE 0xF4000000
+#define MBUS_SPI_SIZE (8 << 20)
+#define MBUS_BOOTROM_BASE 0xF8000000
+#define MBUS_BOOTROM_SIZE (8 << 20)
struct mbus_win {
u32 base;
@@ -113,6 +117,7 @@ unsigned int mvebu_sdram_bs(enum memory_bank bank);
void mvebu_sdram_size_adjust(enum memory_bank bank);
int mvebu_mbus_probe(struct mbus_win windows[], int count);
int mvebu_soc_family(void);
+u32 mvebu_get_nand_clock(void);
int mv_sdh_init(unsigned long regbase, u32 max_clk, u32 min_clk, u32 quirks);
diff --git a/arch/arm/mach-mvebu/include/mach/soc.h b/arch/arm/mach-mvebu/include/mach/soc.h
index 125b5f278d..a8a6b27d80 100644
--- a/arch/arm/mach-mvebu/include/mach/soc.h
+++ b/arch/arm/mach-mvebu/include/mach/soc.h
@@ -11,6 +11,8 @@
#ifndef _MVEBU_SOC_H
#define _MVEBU_SOC_H
+#define BIT(x) (1 << (x))
+
#define SOC_MV78460_ID 0x7846
#define SOC_88F6810_ID 0x6810
#define SOC_88F6820_ID 0x6820
@@ -25,6 +27,9 @@
#define CONFIG_SYS_TCLK 250000000 /* 250MHz */
#endif
+/* Armada XP PLL frequency (used for NAND clock generation) */
+#define CONFIG_SYS_MVEBU_PLL_CLOCK 2000000000
+
/* SOC specific definations */
#define INTREG_BASE 0xd0000000
#define INTREG_BASE_ADDR_REG (INTREG_BASE + 0x20080)
@@ -53,19 +58,43 @@
#define MVEBU_GPIO1_BASE (MVEBU_REGISTER(0x18140))
#define MVEBU_GPIO2_BASE (MVEBU_REGISTER(0x18180))
#define MVEBU_SYSTEM_REG_BASE (MVEBU_REGISTER(0x18200))
+#define MVEBU_CLOCK_BASE (MVEBU_REGISTER(0x18700))
#define MVEBU_CPU_WIN_BASE (MVEBU_REGISTER(0x20000))
#define MVEBU_SDRAM_BASE (MVEBU_REGISTER(0x20180))
#define MVEBU_TIMER_BASE (MVEBU_REGISTER(0x20300))
#define MVEBU_EGIGA2_BASE (MVEBU_REGISTER(0x30000))
#define MVEBU_EGIGA3_BASE (MVEBU_REGISTER(0x34000))
#define MVEBU_REG_PCIE_BASE (MVEBU_REGISTER(0x40000))
+#define MVEBU_AXP_USB_BASE (MVEBU_REGISTER(0x50000))
#define MVEBU_USB20_BASE (MVEBU_REGISTER(0x58000))
#define MVEBU_EGIGA0_BASE (MVEBU_REGISTER(0x70000))
#define MVEBU_EGIGA1_BASE (MVEBU_REGISTER(0x74000))
#define MVEBU_AXP_SATA_BASE (MVEBU_REGISTER(0xa0000))
#define MVEBU_SATA0_BASE (MVEBU_REGISTER(0xa8000))
+#define MVEBU_NAND_BASE (MVEBU_REGISTER(0xd0000))
#define MVEBU_SDIO_BASE (MVEBU_REGISTER(0xd8000))
+#define SOC_COHERENCY_FABRIC_CTRL_REG (MVEBU_REGISTER(0x20200))
+#define MBUS_ERR_PROP_EN (1 << 8)
+
+#define MBUS_BRIDGE_WIN_CTRL_REG (MVEBU_REGISTER(0x20250))
+#define MBUS_BRIDGE_WIN_BASE_REG (MVEBU_REGISTER(0x20254))
+
+#define MVEBU_SOC_DEV_MUX_REG (MVEBU_SYSTEM_REG_BASE + 0x08)
+#define NAND_EN BIT(0)
+#define NAND_ARBITER_EN BIT(27)
+
+#define ARMADA_XP_PUP_ENABLE (MVEBU_SYSTEM_REG_BASE + 0x44c)
+#define GE0_PUP_EN BIT(0)
+#define GE1_PUP_EN BIT(1)
+#define LCD_PUP_EN BIT(2)
+#define NAND_PUP_EN BIT(4)
+#define SPI_PUP_EN BIT(5)
+
+#define MVEBU_CORE_DIV_CLK_CTRL(i) (MVEBU_CLOCK_BASE + ((i) * 0x8))
+#define NAND_ECC_DIVCKL_RATIO_OFFS 8
+#define NAND_ECC_DIVCKL_RATIO_MASK (0x3F << NAND_ECC_DIVCKL_RATIO_OFFS)
+
#define SDRAM_MAX_CS 4
#define SDRAM_ADDR_MASK 0xFF000000
diff --git a/arch/arm/mach-mvebu/mbus.c b/arch/arm/mach-mvebu/mbus.c
index 9b76bce91d..771cce6fa3 100644
--- a/arch/arm/mach-mvebu/mbus.c
+++ b/arch/arm/mach-mvebu/mbus.c
@@ -52,10 +52,9 @@
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
+#include <linux/compat.h>
#include <linux/mbus.h>
-#define BIT(nr) (1UL << (nr))
-
/* DDR target is the same on all platforms */
#define TARGET_DDR 0
@@ -407,6 +406,53 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size)
return 0;
}
+static void mvebu_mbus_get_lowest_base(struct mvebu_mbus_state *mbus,
+ phys_addr_t *base)
+{
+ int win;
+ *base = 0xffffffff;
+
+ for (win = 0; win < mbus->soc->num_wins; win++) {
+ u64 wbase;
+ u32 wsize;
+ u8 wtarget, wattr;
+ int enabled;
+
+ mvebu_mbus_read_window(mbus, win,
+ &enabled, &wbase, &wsize,
+ &wtarget, &wattr, NULL);
+
+ if (!enabled)
+ continue;
+
+ if (wbase < *base)
+ *base = wbase;
+ }
+}
+
+static void mvebu_config_mbus_bridge(struct mvebu_mbus_state *mbus)
+{
+ phys_addr_t base;
+ u32 val;
+ u32 size;
+
+ /* Set MBUS bridge base/ctrl */
+ mvebu_mbus_get_lowest_base(&mbus_state, &base);
+
+ size = 0xffffffff - base + 1;
+ if (!is_power_of_2(size)) {
+ /* Round up to next power of 2 */
+ size = 1 << (ffs(base) + 1);
+ base = 0xffffffff - size + 1;
+ }
+
+ /* Now write base and size */
+ writel(base, MBUS_BRIDGE_WIN_BASE_REG);
+ /* Align window size to 64KiB */
+ val = (size / (64 << 10)) - 1;
+ writel((val << 16) | 0x1, MBUS_BRIDGE_WIN_CTRL_REG);
+}
+
int mbus_dt_setup_win(struct mvebu_mbus_state *mbus,
u32 base, u32 size, u8 target, u8 attr)
{
@@ -426,6 +472,13 @@ int mbus_dt_setup_win(struct mvebu_mbus_state *mbus,
return -ENOMEM;
}
+ /*
+ * Re-configure the mbus bridge registers each time this function
+ * is called. Since it may get called from the board code in
+ * later boot stages as well.
+ */
+ mvebu_config_mbus_bridge(mbus);
+
return 0;
}