GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,43 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
#
# Based on some other Makefile
# (C) Copyright 2000-2003
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-y += board.o
obj-y += clock.o
obj-y += cpu_info.o
obj-y += dram_helpers.o
obj-y += pinmux.o
obj-$(CONFIG_SUN6I_P2WI) += p2wi.o
obj-$(CONFIG_SUN6I_PRCM) += prcm.o
obj-$(CONFIG_AXP_PMIC_BUS) += pmic_bus.o
obj-$(CONFIG_SUN8I_RSB) += rsb.o
obj-$(CONFIG_MACH_SUN4I) += clock_sun4i.o
obj-$(CONFIG_MACH_SUN5I) += clock_sun4i.o
obj-$(CONFIG_MACH_SUN6I) += clock_sun6i.o
obj-$(CONFIG_MACH_SUN7I) += clock_sun4i.o
obj-$(CONFIG_MACH_SUN50I) += clock_sun6i.o
ifdef CONFIG_MACH_SUN8I_A83T
obj-y += clock_sun8i_a83t.o
else
obj-$(CONFIG_MACH_SUN8I) += clock_sun6i.o
endif
obj-$(CONFIG_MACH_SUN9I) += clock_sun9i.o gtbus_sun9i.o
obj-$(CONFIG_MACH_SUN50I_H6) += clock_sun50i_h6.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_DRAM_SUN4I) += dram_sun4i.o
obj-$(CONFIG_DRAM_SUN6I) += dram_sun6i.o
obj-$(CONFIG_DRAM_SUN8I_A23) += dram_sun8i_a23.o
obj-$(CONFIG_DRAM_SUN8I_A33) += dram_sun8i_a33.o
obj-$(CONFIG_DRAM_SUN8I_A83T) += dram_sun8i_a83t.o
obj-$(CONFIG_DRAM_SUN9I) += dram_sun9i.o
obj-$(CONFIG_SPL_SPI_SUNXI) += spl_spi_sunxi.o
obj-$(CONFIG_SUNXI_DRAM_DW) += dram_sunxi_dw.o
obj-$(CONFIG_SUNXI_DRAM_DW) += dram_timings/
obj-$(CONFIG_DRAM_SUN50I_H6) += dram_sun50i_h6.o
obj-$(CONFIG_DRAM_SUN50I_H6) += dram_timings/
endif
@@ -0,0 +1,324 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* Some init for sunxi platform.
*/
#include <common.h>
#include <cpu_func.h>
#include <mmc.h>
#include <i2c.h>
#include <serial.h>
#include <spl.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/gpio.h>
#include <asm/arch/spl.h>
#include <asm/arch/sys_proto.h>
#include <asm/arch/timer.h>
#include <asm/arch/tzpc.h>
#include <asm/arch/mmc.h>
#include <linux/compiler.h>
struct fel_stash {
uint32_t sp;
uint32_t lr;
uint32_t cpsr;
uint32_t sctlr;
uint32_t vbar;
uint32_t cr;
};
struct fel_stash fel_stash __attribute__((section(".data")));
#ifdef CONFIG_ARM64
#include <asm/armv8/mmu.h>
static struct mm_region sunxi_mem_map[] = {
{
/* SRAM, MMIO regions */
.virt = 0x0UL,
.phys = 0x0UL,
.size = 0x40000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE
}, {
/* RAM */
.virt = 0x40000000UL,
.phys = 0x40000000UL,
.size = 0xC0000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
PTE_BLOCK_INNER_SHARE
}, {
/* List terminator */
0,
}
};
struct mm_region *mem_map = sunxi_mem_map;
#endif
static int gpio_init(void)
{
__maybe_unused uint val;
#if CONFIG_CONS_INDEX == 1 && defined(CONFIG_UART0_PORT_F)
#if defined(CONFIG_MACH_SUN4I) || \
defined(CONFIG_MACH_SUN7I) || \
defined(CONFIG_MACH_SUN8I_R40)
/* disable GPB22,23 as uart0 tx,rx to avoid conflict */
sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUNXI_GPIO_INPUT);
sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUNXI_GPIO_INPUT);
#endif
#if defined(CONFIG_MACH_SUN8I) && !defined(CONFIG_MACH_SUN8I_R40)
sunxi_gpio_set_cfgpin(SUNXI_GPF(2), SUN8I_GPF_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPF(4), SUN8I_GPF_UART0);
#else
sunxi_gpio_set_cfgpin(SUNXI_GPF(2), SUNXI_GPF_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPF(4), SUNXI_GPF_UART0);
#endif
sunxi_gpio_set_pull(SUNXI_GPF(4), 1);
#elif CONFIG_CONS_INDEX == 1 && (defined(CONFIG_MACH_SUN4I) || \
defined(CONFIG_MACH_SUN7I) || \
defined(CONFIG_MACH_SUN8I_R40))
sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUN4I_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUN4I_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(23), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN5I)
sunxi_gpio_set_cfgpin(SUNXI_GPB(19), SUN5I_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN5I_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(20), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN6I)
sunxi_gpio_set_cfgpin(SUNXI_GPH(20), SUN6I_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(21), SUN6I_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(21), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A33)
sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUN8I_A33_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN8I_A33_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(1), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUNXI_H3_H5)
sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN8I_H3_GPA_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN8I_H3_GPA_UART0);
sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I)
sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN50I_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I_H6)
sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H6_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H6_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A83T)
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_A83T_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_A83T_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_V3S)
sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN8I_V3S_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_V3S_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN9I)
sunxi_gpio_set_cfgpin(SUNXI_GPH(12), SUN9I_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(13), SUN9I_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(13), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 2 && defined(CONFIG_MACH_SUN5I)
sunxi_gpio_set_cfgpin(SUNXI_GPG(3), SUN5I_GPG_UART1);
sunxi_gpio_set_cfgpin(SUNXI_GPG(4), SUN5I_GPG_UART1);
sunxi_gpio_set_pull(SUNXI_GPG(4), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 3 && defined(CONFIG_MACH_SUN8I)
sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUN8I_GPB_UART2);
sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN8I_GPB_UART2);
sunxi_gpio_set_pull(SUNXI_GPB(1), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 5 && defined(CONFIG_MACH_SUN8I)
sunxi_gpio_set_cfgpin(SUNXI_GPL(2), SUN8I_GPL_R_UART);
sunxi_gpio_set_cfgpin(SUNXI_GPL(3), SUN8I_GPL_R_UART);
sunxi_gpio_set_pull(SUNXI_GPL(3), SUNXI_GPIO_PULL_UP);
#else
#error Unsupported console port number. Please fix pin mux settings in board.c
#endif
#ifdef CONFIG_MACH_SUN50I_H6
/* Update PIO power bias configuration by copy hardware detected value */
val = readl(SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL);
writel(val, SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL);
val = readl(SUNXI_R_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL);
writel(val, SUNXI_R_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL);
#endif
return 0;
}
#if defined(CONFIG_SPL_BOARD_LOAD_IMAGE) && defined(CONFIG_SPL_BUILD)
static int spl_board_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
{
debug("Returning to FEL sp=%x, lr=%x\n", fel_stash.sp, fel_stash.lr);
return_to_fel(fel_stash.sp, fel_stash.lr);
return 0;
}
SPL_LOAD_IMAGE_METHOD("FEL", 0, BOOT_DEVICE_BOARD, spl_board_load_image);
#endif
void s_init(void)
{
/*
* Undocumented magic taken from boot0, without this DRAM
* access gets messed up (seems cache related).
* The boot0 sources describe this as: "config ema for cache sram"
*/
#if defined CONFIG_MACH_SUN6I
setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800);
#elif defined CONFIG_MACH_SUN8I
__maybe_unused uint version;
/* Unlock sram version info reg, read it, relock */
setbits_le32(SUNXI_SRAMC_BASE + 0x24, (1 << 15));
version = readl(SUNXI_SRAMC_BASE + 0x24) >> 16;
clrbits_le32(SUNXI_SRAMC_BASE + 0x24, (1 << 15));
/*
* Ideally this would be a switch case, but we do not know exactly
* which versions there are and which version needs which settings,
* so reproduce the per SoC code from the BSP.
*/
#if defined CONFIG_MACH_SUN8I_A23
if (version == 0x1650)
setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800);
else /* 0x1661 ? */
setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0xc0);
#elif defined CONFIG_MACH_SUN8I_A33
if (version != 0x1667)
setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0xc0);
#endif
/* A83T BSP never modifies SUNXI_SRAMC_BASE + 0x44 */
/* No H3 BSP, boot0 seems to not modify SUNXI_SRAMC_BASE + 0x44 */
#endif
#if !defined(CONFIG_ARM_CORTEX_CPU_IS_UP) && !defined(CONFIG_ARM64)
/* Enable SMP mode for CPU0, by setting bit 6 of Auxiliary Ctl reg */
asm volatile(
"mrc p15, 0, r0, c1, c0, 1\n"
"orr r0, r0, #1 << 6\n"
"mcr p15, 0, r0, c1, c0, 1\n"
::: "r0");
#endif
#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I_H3
/* Enable non-secure access to some peripherals */
tzpc_init();
#endif
clock_init();
timer_init();
gpio_init();
#ifndef CONFIG_DM_I2C
i2c_init_board();
#endif
eth_init_board();
}
/* The sunxi internal brom will try to loader external bootloader
* from mmc0, nand flash, mmc2.
*/
uint32_t sunxi_get_boot_device(void)
{
int boot_source;
/*
* When booting from the SD card or NAND memory, the "eGON.BT0"
* signature is expected to be found in memory at the address 0x0004
* (see the "mksunxiboot" tool, which generates this header).
*
* When booting in the FEL mode over USB, this signature is patched in
* memory and replaced with something else by the 'fel' tool. This other
* signature is selected in such a way, that it can't be present in a
* valid bootable SD card image (because the BROM would refuse to
* execute the SPL in this case).
*
* This checks for the signature and if it is not found returns to
* the FEL code in the BROM to wait and receive the main u-boot
* binary over USB. If it is found, it determines where SPL was
* read from.
*/
if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */
return BOOT_DEVICE_BOARD;
boot_source = readb(SPL_ADDR + 0x28);
switch (boot_source) {
case SUNXI_BOOTED_FROM_MMC0:
case SUNXI_BOOTED_FROM_MMC0_HIGH:
return BOOT_DEVICE_MMC1;
case SUNXI_BOOTED_FROM_NAND:
return BOOT_DEVICE_NAND;
case SUNXI_BOOTED_FROM_MMC2:
case SUNXI_BOOTED_FROM_MMC2_HIGH:
return BOOT_DEVICE_MMC2;
case SUNXI_BOOTED_FROM_SPI:
return BOOT_DEVICE_SPI;
}
panic("Unknown boot source %d\n", boot_source);
return -1; /* Never reached */
}
#ifdef CONFIG_SPL_BUILD
u32 spl_boot_device(void)
{
return sunxi_get_boot_device();
}
void board_init_f(ulong dummy)
{
spl_init();
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)
{
#if defined(CONFIG_SUNXI_GEN_SUN4I) || defined(CONFIG_MACH_SUN8I_R40)
static const struct sunxi_wdog *wdog =
&((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->wdog;
/* Set the watchdog for its shortest interval (.5s) and wait */
writel(WDT_MODE_RESET_EN | WDT_MODE_EN, &wdog->mode);
writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->ctl);
while (1) {
/* sun5i sometimes gets stuck without this */
writel(WDT_MODE_RESET_EN | WDT_MODE_EN, &wdog->mode);
}
#elif defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_MACH_SUN50I_H6)
#if defined(CONFIG_MACH_SUN50I_H6)
/* WDOG is broken for some H6 rev. use the R_WDOG instead */
static const struct sunxi_wdog *wdog =
(struct sunxi_wdog *)SUNXI_R_WDOG_BASE;
#else
static const struct sunxi_wdog *wdog =
((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->wdog;
#endif
/* Set the watchdog for its shortest interval (.5s) and wait */
writel(WDT_CFG_RESET, &wdog->cfg);
writel(WDT_MODE_EN, &wdog->mode);
writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->ctl);
while (1) { }
#endif
}
#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) && !defined(CONFIG_ARM64)
void enable_caches(void)
{
/* Enable D-cache. I-cache is already enabled in start.S */
dcache_enable();
}
#endif
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2007-2012
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/gpio.h>
#include <asm/arch/prcm.h>
#include <asm/arch/gtbus.h>
#include <asm/arch/sys_proto.h>
__weak void clock_init_sec(void)
{
}
__weak void gtbus_init(void)
{
}
int clock_init(void)
{
#ifdef CONFIG_SPL_BUILD
clock_init_safe();
gtbus_init();
#endif
clock_init_uart();
clock_init_sec();
return 0;
}
/* These functions are shared between various SoCs so put them here. */
#if defined CONFIG_SUNXI_GEN_SUN6I && !defined CONFIG_MACH_SUN9I
int clock_twi_onoff(int port, int state)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
if (port == 5) {
if (state)
prcm_apb0_enable(
PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_I2C);
else
prcm_apb0_disable(
PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_I2C);
return 0;
}
/* set the apb clock gate and reset for twi */
if (state) {
setbits_le32(&ccm->apb2_gate,
CLK_GATE_OPEN << (APB2_GATE_TWI_SHIFT + port));
setbits_le32(&ccm->apb2_reset_cfg,
1 << (APB2_RESET_TWI_SHIFT + port));
} else {
clrbits_le32(&ccm->apb2_reset_cfg,
1 << (APB2_RESET_TWI_SHIFT + port));
clrbits_le32(&ccm->apb2_gate,
CLK_GATE_OPEN << (APB2_GATE_TWI_SHIFT + port));
}
return 0;
}
#endif
@@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* sun4i, sun5i and sun7i specific clock code
*
* (C) Copyright 2007-2012
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
*/
#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
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 << AXI_DIV_SHIFT |
AHB_DIV_2 << AHB_DIV_SHIFT |
APB0_DIV_1 << APB0_DIV_SHIFT |
CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT,
&ccm->cpu_ahb_apb0_cfg);
writel(PLL1_CFG_DEFAULT, &ccm->pll1_cfg);
sdelay(200);
writel(AXI_DIV_1 << AXI_DIV_SHIFT |
AHB_DIV_2 << AHB_DIV_SHIFT |
APB0_DIV_1 << APB0_DIV_SHIFT |
CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT,
&ccm->cpu_ahb_apb0_cfg);
#ifdef CONFIG_MACH_SUN7I
setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_DMA);
#endif
writel(PLL6_CFG_DEFAULT, &ccm->pll6_cfg);
#ifdef CONFIG_SUNXI_AHCI
setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_SATA);
setbits_le32(&ccm->pll6_cfg, 0x1 << CCM_PLL6_CTRL_SATA_EN_SHIFT);
#endif
}
#endif
void clock_init_uart(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* uart clock source is apb1 */
writel(APB1_CLK_SRC_OSC24M|
APB1_CLK_RATE_N_1|
APB1_CLK_RATE_M(1),
&ccm->apb1_clk_div_cfg);
/* open the clock for uart */
setbits_le32(&ccm->apb1_gate,
CLK_GATE_OPEN << (APB1_GATE_UART_SHIFT+CONFIG_CONS_INDEX - 1));
}
int clock_twi_onoff(int port, int state)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* set the apb clock gate for twi */
if (state)
setbits_le32(&ccm->apb1_gate,
CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT + port));
else
clrbits_le32(&ccm->apb1_gate,
CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT + port));
return 0;
}
#ifdef CONFIG_SPL_BUILD
#define PLL1_CFG(N, K, M, P) ( 1 << CCM_PLL1_CFG_ENABLE_SHIFT | \
0 << CCM_PLL1_CFG_VCO_RST_SHIFT | \
8 << CCM_PLL1_CFG_VCO_BIAS_SHIFT | \
0 << CCM_PLL1_CFG_PLL4_EXCH_SHIFT | \
16 << CCM_PLL1_CFG_BIAS_CUR_SHIFT | \
(P)<< CCM_PLL1_CFG_DIVP_SHIFT | \
2 << CCM_PLL1_CFG_LCK_TMR_SHIFT | \
(N)<< CCM_PLL1_CFG_FACTOR_N_SHIFT | \
(K)<< CCM_PLL1_CFG_FACTOR_K_SHIFT | \
0 << CCM_PLL1_CFG_SIG_DELT_PAT_IN_SHIFT | \
0 << CCM_PLL1_CFG_SIG_DELT_PAT_EN_SHIFT | \
(M)<< CCM_PLL1_CFG_FACTOR_M_SHIFT)
static struct {
u32 pll1_cfg;
unsigned int freq;
} pll1_para[] = {
/* This array must be ordered by frequency. */
{ PLL1_CFG(31, 1, 0, 0), 1488000000},
{ PLL1_CFG(30, 1, 0, 0), 1440000000},
{ PLL1_CFG(29, 1, 0, 0), 1392000000},
{ PLL1_CFG(28, 1, 0, 0), 1344000000},
{ PLL1_CFG(27, 1, 0, 0), 1296000000},
{ PLL1_CFG(26, 1, 0, 0), 1248000000},
{ PLL1_CFG(25, 1, 0, 0), 1200000000},
{ PLL1_CFG(24, 1, 0, 0), 1152000000},
{ PLL1_CFG(23, 1, 0, 0), 1104000000},
{ PLL1_CFG(22, 1, 0, 0), 1056000000},
{ PLL1_CFG(21, 1, 0, 0), 1008000000},
{ PLL1_CFG(20, 1, 0, 0), 960000000 },
{ PLL1_CFG(19, 1, 0, 0), 912000000 },
{ PLL1_CFG(16, 1, 0, 0), 768000000 },
/* Final catchall entry 384MHz*/
{ PLL1_CFG(16, 0, 0, 0), 0 },
};
void clock_set_pll1(unsigned 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;
if (! hz)
hz = 384000000;
/* Calculate system clock divisors */
axi = DIV_ROUND_UP(hz, 432000000); /* Max 450MHz */
ahb = DIV_ROUND_UP(hz/axi, 204000000); /* Max 250MHz */
apb0 = 2; /* Max 150MHz */
printf("CPU: %uHz, AXI/AHB/APB: %d/%d/%d\n", hz, axi, ahb, apb0);
/* 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 << AXI_DIV_SHIFT |
AHB_DIV_2 << AHB_DIV_SHIFT |
APB0_DIV_1 << APB0_DIV_SHIFT |
CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT,
&ccm->cpu_ahb_apb0_cfg);
sdelay(20);
/* Configure sys clock divisors */
writel(axi << AXI_DIV_SHIFT |
ahb << AHB_DIV_SHIFT |
apb0 << APB0_DIV_SHIFT |
CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT,
&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 << AXI_DIV_SHIFT |
ahb << AHB_DIV_SHIFT |
apb0 << APB0_DIV_SHIFT |
CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT,
&ccm->cpu_ahb_apb0_cfg);
sdelay(20);
}
#endif
void clock_set_pll3(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
if (clk == 0) {
clrbits_le32(&ccm->pll3_cfg, CCM_PLL3_CTRL_EN);
return;
}
/* PLL3 rate = 3000000 * m */
writel(CCM_PLL3_CTRL_EN | CCM_PLL3_CTRL_INTEGER_MODE |
CCM_PLL3_CTRL_M(clk / 3000000), &ccm->pll3_cfg);
}
unsigned int clock_get_pll3(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
uint32_t rval = readl(&ccm->pll3_cfg);
int m = ((rval & CCM_PLL3_CTRL_M_MASK) >> CCM_PLL3_CTRL_M_SHIFT);
return 3000000 * m;
}
unsigned int clock_get_pll5p(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
uint32_t rval = readl(&ccm->pll5_cfg);
int n = ((rval & CCM_PLL5_CTRL_N_MASK) >> CCM_PLL5_CTRL_N_SHIFT);
int k = ((rval & CCM_PLL5_CTRL_K_MASK) >> CCM_PLL5_CTRL_K_SHIFT) + 1;
int p = ((rval & CCM_PLL5_CTRL_P_MASK) >> CCM_PLL5_CTRL_P_SHIFT);
return (24000000 * n * k) >> p;
}
unsigned int clock_get_pll6(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
uint32_t rval = readl(&ccm->pll6_cfg);
int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT);
int k = ((rval & CCM_PLL6_CTRL_K_MASK) >> CCM_PLL6_CTRL_K_SHIFT) + 1;
return 24000000 * n * k / 2;
}
void clock_set_de_mod_clock(u32 *clk_cfg, unsigned int hz)
{
int pll = clock_get_pll5p();
int div = 1;
while ((pll / div) > hz)
div++;
writel(CCM_DE_CTRL_GATE | CCM_DE_CTRL_RST | CCM_DE_CTRL_PLL5P |
CCM_DE_CTRL_M(div), clk_cfg);
}
@@ -0,0 +1,94 @@
#include <common.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clock.h>
#ifdef CONFIG_SPL_BUILD
void clock_init_safe(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
clock_set_pll1(408000000);
writel(CCM_PLL6_DEFAULT, &ccm->pll6_cfg);
while (!(readl(&ccm->pll6_cfg) & CCM_PLL6_LOCK))
;
clrsetbits_le32(&ccm->cpu_axi_cfg, CCM_CPU_AXI_APB_MASK | CCM_CPU_AXI_AXI_MASK,
CCM_CPU_AXI_DEFAULT_FACTORS);
writel(CCM_PSI_AHB1_AHB2_DEFAULT, &ccm->psi_ahb1_ahb2_cfg);
writel(CCM_AHB3_DEFAULT, &ccm->ahb3_cfg);
writel(CCM_APB1_DEFAULT, &ccm->apb1_cfg);
/*
* The mux and factor are set, but the clock will be enabled in
* DRAM initialization code.
*/
writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), &ccm->mbus_cfg);
}
#endif
void clock_init_uart(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* uart clock source is apb2 */
writel(APB2_CLK_SRC_OSC24M|
APB2_CLK_RATE_N_1|
APB2_CLK_RATE_M(1),
&ccm->apb2_cfg);
/* open the clock for uart */
setbits_le32(&ccm->uart_gate_reset,
1 << (CONFIG_CONS_INDEX - 1));
/* deassert uart reset */
setbits_le32(&ccm->uart_gate_reset,
1 << (RESET_SHIFT + CONFIG_CONS_INDEX - 1));
}
#ifdef CONFIG_SPL_BUILD
void clock_set_pll1(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
u32 val;
/* Do not support clocks < 288MHz as they need factor P */
if (clk < 288000000) clk = 288000000;
/* Switch to 24MHz clock while changing PLL1 */
val = readl(&ccm->cpu_axi_cfg);
val &= ~CCM_CPU_AXI_MUX_MASK;
val |= CCM_CPU_AXI_MUX_OSC24M;
writel(val, &ccm->cpu_axi_cfg);
/* clk = 24*n/p, p is ignored if clock is >288MHz */
writel(CCM_PLL1_CTRL_EN | CCM_PLL1_LOCK_EN | CCM_PLL1_CLOCK_TIME_2 |
CCM_PLL1_CTRL_N(clk / 24000000), &ccm->pll1_cfg);
while (!(readl(&ccm->pll1_cfg) & CCM_PLL1_LOCK)) {}
/* Switch CPU to PLL1 */
val = readl(&ccm->cpu_axi_cfg);
val &= ~CCM_CPU_AXI_MUX_MASK;
val |= CCM_CPU_AXI_MUX_PLL_CPUX;
writel(val, &ccm->cpu_axi_cfg);
}
#endif
unsigned int clock_get_pll6(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
uint32_t rval = readl(&ccm->pll6_cfg);
int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT);
int div1 = ((rval & CCM_PLL6_CTRL_DIV1_MASK) >>
CCM_PLL6_CTRL_DIV1_SHIFT) + 1;
int div2 = ((rval & CCM_PLL6_CTRL_DIV2_MASK) >>
CCM_PLL6_CTRL_DIV2_SHIFT) + 1;
/* The register defines PLL6-4X, not plain PLL6 */
return 24000000 / 4 * n / div1 / div2;
}
@@ -0,0 +1,345 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* sun6i specific clock code
*
* (C) Copyright 2007-2012
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/prcm.h>
#include <asm/arch/sys_proto.h>
#ifdef CONFIG_SPL_BUILD
void clock_init_safe(void)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
#if !defined(CONFIG_MACH_SUNXI_H3_H5) && !defined(CONFIG_MACH_SUN50I)
struct sunxi_prcm_reg * const prcm =
(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
/* Set PLL ldo voltage without this PLL6 does not work properly */
clrsetbits_le32(&prcm->pll_ctrl1, PRCM_PLL_CTRL_LDO_KEY_MASK,
PRCM_PLL_CTRL_LDO_KEY);
clrsetbits_le32(&prcm->pll_ctrl1, ~PRCM_PLL_CTRL_LDO_KEY_MASK,
PRCM_PLL_CTRL_LDO_DIGITAL_EN | PRCM_PLL_CTRL_LDO_ANALOG_EN |
PRCM_PLL_CTRL_EXT_OSC_EN | PRCM_PLL_CTRL_LDO_OUT_L(1140));
clrbits_le32(&prcm->pll_ctrl1, PRCM_PLL_CTRL_LDO_KEY_MASK);
#endif
#if defined(CONFIG_MACH_SUN8I_R40) || defined(CONFIG_MACH_SUN50I)
/* Set PLL lock enable bits and switch to old lock mode */
writel(GENMASK(12, 0), &ccm->pll_lock_ctrl);
#endif
clock_set_pll1(408000000);
writel(PLL6_CFG_DEFAULT, &ccm->pll6_cfg);
while (!(readl(&ccm->pll6_cfg) & CCM_PLL6_CTRL_LOCK))
;
writel(AHB1_ABP1_DIV_DEFAULT, &ccm->ahb1_apb1_div);
writel(MBUS_CLK_DEFAULT, &ccm->mbus0_clk_cfg);
if (IS_ENABLED(CONFIG_MACH_SUN6I))
writel(MBUS_CLK_DEFAULT, &ccm->mbus1_clk_cfg);
#if defined(CONFIG_MACH_SUN8I_R40) && defined(CONFIG_SUNXI_AHCI)
setbits_le32(&ccm->sata_pll_cfg, CCM_SATA_PLL_DEFAULT);
setbits_le32(&ccm->ahb_reset0_cfg, 0x1 << AHB_GATE_OFFSET_SATA);
setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_SATA);
setbits_le32(&ccm->sata_clk_cfg, CCM_SATA_CTRL_ENABLE);
#endif
}
#endif
void clock_init_sec(void)
{
#ifdef CONFIG_MACH_SUNXI_H3_H5
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
struct sunxi_prcm_reg * const prcm =
(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
setbits_le32(&ccm->ccu_sec_switch,
CCM_SEC_SWITCH_MBUS_NONSEC |
CCM_SEC_SWITCH_BUS_NONSEC |
CCM_SEC_SWITCH_PLL_NONSEC);
setbits_le32(&prcm->prcm_sec_switch,
PRCM_SEC_SWITCH_APB0_CLK_NONSEC |
PRCM_SEC_SWITCH_PLL_CFG_NONSEC |
PRCM_SEC_SWITCH_PWR_GATE_NONSEC);
#endif
}
void clock_init_uart(void)
{
#if CONFIG_CONS_INDEX < 5
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* uart clock source is apb2 */
writel(APB2_CLK_SRC_OSC24M|
APB2_CLK_RATE_N_1|
APB2_CLK_RATE_M(1),
&ccm->apb2_div);
/* open the clock for uart */
setbits_le32(&ccm->apb2_gate,
CLK_GATE_OPEN << (APB2_GATE_UART_SHIFT +
CONFIG_CONS_INDEX - 1));
/* deassert uart reset */
setbits_le32(&ccm->apb2_reset_cfg,
1 << (APB2_RESET_UART_SHIFT +
CONFIG_CONS_INDEX - 1));
#else
/* enable R_PIO and R_UART clocks, and de-assert resets */
prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_UART);
#endif
}
#ifdef CONFIG_SPL_BUILD
void clock_set_pll1(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int p = 0;
int k = 1;
int m = 1;
if (clk > 1152000000) {
k = 2;
} else if (clk > 768000000) {
k = 4;
m = 2;
}
/* Switch to 24MHz clock while changing PLL1 */
writel(AXI_DIV_3 << AXI_DIV_SHIFT |
ATB_DIV_2 << ATB_DIV_SHIFT |
CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT,
&ccm->cpu_axi_cfg);
/*
* sun6i: PLL1 rate = ((24000000 * n * k) >> 0) / m (p is ignored)
* sun8i: PLL1 rate = ((24000000 * n * k) >> p) / m
*/
writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_P(p) |
CCM_PLL1_CTRL_N(clk / (24000000 * k / m)) |
CCM_PLL1_CTRL_K(k) | CCM_PLL1_CTRL_M(m), &ccm->pll1_cfg);
sdelay(200);
/* Switch CPU to PLL1 */
writel(AXI_DIV_3 << AXI_DIV_SHIFT |
ATB_DIV_2 << ATB_DIV_SHIFT |
CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT,
&ccm->cpu_axi_cfg);
}
#endif
void clock_set_pll3(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
#ifdef CONFIG_SUNXI_DE2
const int m = 4; /* 6 MHz steps to allow higher frequency for DE2 */
#else
const int m = 8; /* 3 MHz steps just like sun4i, sun5i and sun7i */
#endif
if (clk == 0) {
clrbits_le32(&ccm->pll3_cfg, CCM_PLL3_CTRL_EN);
return;
}
/* PLL3 rate = 24000000 * n / m */
writel(CCM_PLL3_CTRL_EN | CCM_PLL3_CTRL_INTEGER_MODE |
CCM_PLL3_CTRL_N(clk / (24000000 / m)) | CCM_PLL3_CTRL_M(m),
&ccm->pll3_cfg);
}
#ifdef CONFIG_SUNXI_DE2
void clock_set_pll3_factors(int m, int n)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* PLL3 rate = 24000000 * n / m */
writel(CCM_PLL3_CTRL_EN | CCM_PLL3_CTRL_INTEGER_MODE |
CCM_PLL3_CTRL_N(n) | CCM_PLL3_CTRL_M(m),
&ccm->pll3_cfg);
while (!(readl(&ccm->pll3_cfg) & CCM_PLL3_CTRL_LOCK))
;
}
#endif
void clock_set_pll5(unsigned int clk, bool sigma_delta_enable)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int max_n = 32;
int k = 1, m = 2;
#ifdef CONFIG_MACH_SUNXI_H3_H5
clrsetbits_le32(&ccm->pll5_tuning_cfg, CCM_PLL5_TUN_LOCK_TIME_MASK |
CCM_PLL5_TUN_INIT_FREQ_MASK,
CCM_PLL5_TUN_LOCK_TIME(2) | CCM_PLL5_TUN_INIT_FREQ(16));
#endif
if (sigma_delta_enable)
writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg);
/* PLL5 rate = 24000000 * n * k / m */
if (clk > 24000000 * k * max_n / m) {
m = 1;
if (clk > 24000000 * k * max_n / m)
k = 2;
}
writel(CCM_PLL5_CTRL_EN |
(sigma_delta_enable ? CCM_PLL5_CTRL_SIGMA_DELTA_EN : 0) |
CCM_PLL5_CTRL_UPD |
CCM_PLL5_CTRL_N(clk / (24000000 * k / m)) |
CCM_PLL5_CTRL_K(k) | CCM_PLL5_CTRL_M(m), &ccm->pll5_cfg);
udelay(5500);
}
#ifdef CONFIG_MACH_SUN6I
void clock_set_mipi_pll(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
unsigned int k, m, n, value, diff;
unsigned best_k = 0, best_m = 0, best_n = 0, best_diff = 0xffffffff;
unsigned int src = clock_get_pll3();
/* All calculations are in KHz to avoid overflows */
clk /= 1000;
src /= 1000;
/* Pick the closest lower clock */
for (k = 1; k <= 4; k++) {
for (m = 1; m <= 16; m++) {
for (n = 1; n <= 16; n++) {
value = src * n * k / m;
if (value > clk)
continue;
diff = clk - value;
if (diff < best_diff) {
best_diff = diff;
best_k = k;
best_m = m;
best_n = n;
}
if (diff == 0)
goto done;
}
}
}
done:
writel(CCM_MIPI_PLL_CTRL_EN | CCM_MIPI_PLL_CTRL_LDO_EN |
CCM_MIPI_PLL_CTRL_N(best_n) | CCM_MIPI_PLL_CTRL_K(best_k) |
CCM_MIPI_PLL_CTRL_M(best_m), &ccm->mipi_pll_cfg);
}
#endif
#ifdef CONFIG_SUNXI_DE2
void clock_set_pll10(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int m = 2; /* 12 MHz steps */
if (clk == 0) {
clrbits_le32(&ccm->pll10_cfg, CCM_PLL10_CTRL_EN);
return;
}
/* PLL10 rate = 24000000 * n / m */
writel(CCM_PLL10_CTRL_EN | CCM_PLL10_CTRL_INTEGER_MODE |
CCM_PLL10_CTRL_N(clk / (24000000 / m)) | CCM_PLL10_CTRL_M(m),
&ccm->pll10_cfg);
while (!(readl(&ccm->pll10_cfg) & CCM_PLL10_CTRL_LOCK))
;
}
#endif
#if defined(CONFIG_MACH_SUN8I_A33) || \
defined(CONFIG_MACH_SUN8I_R40) || \
defined(CONFIG_MACH_SUN50I)
void clock_set_pll11(unsigned int clk, bool sigma_delta_enable)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
if (sigma_delta_enable)
writel(CCM_PLL11_PATTERN, &ccm->pll11_pattern_cfg0);
writel(CCM_PLL11_CTRL_EN | CCM_PLL11_CTRL_UPD |
(sigma_delta_enable ? CCM_PLL11_CTRL_SIGMA_DELTA_EN : 0) |
CCM_PLL11_CTRL_N(clk / 24000000), &ccm->pll11_cfg);
while (readl(&ccm->pll11_cfg) & CCM_PLL11_CTRL_UPD)
;
}
#endif
unsigned int clock_get_pll3(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
uint32_t rval = readl(&ccm->pll3_cfg);
int n = ((rval & CCM_PLL3_CTRL_N_MASK) >> CCM_PLL3_CTRL_N_SHIFT) + 1;
int m = ((rval & CCM_PLL3_CTRL_M_MASK) >> CCM_PLL3_CTRL_M_SHIFT) + 1;
/* Multiply by 1000 after dividing by m to avoid integer overflows */
return (24000 * n / m) * 1000;
}
unsigned int clock_get_pll6(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
uint32_t rval = readl(&ccm->pll6_cfg);
int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT) + 1;
int k = ((rval & CCM_PLL6_CTRL_K_MASK) >> CCM_PLL6_CTRL_K_SHIFT) + 1;
return 24000000 * n * k / 2;
}
unsigned int clock_get_mipi_pll(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
uint32_t rval = readl(&ccm->mipi_pll_cfg);
unsigned int n = ((rval & CCM_MIPI_PLL_CTRL_N_MASK) >> CCM_MIPI_PLL_CTRL_N_SHIFT) + 1;
unsigned int k = ((rval & CCM_MIPI_PLL_CTRL_K_MASK) >> CCM_MIPI_PLL_CTRL_K_SHIFT) + 1;
unsigned int m = ((rval & CCM_MIPI_PLL_CTRL_M_MASK) >> CCM_MIPI_PLL_CTRL_M_SHIFT) + 1;
unsigned int src = clock_get_pll3();
/* Multiply by 1000 after dividing by m to avoid integer overflows */
return ((src / 1000) * n * k / m) * 1000;
}
void clock_set_de_mod_clock(u32 *clk_cfg, unsigned int hz)
{
int pll = clock_get_pll6() * 2;
int div = 1;
while ((pll / div) > hz)
div++;
writel(CCM_DE_CTRL_GATE | CCM_DE_CTRL_PLL6_2X | CCM_DE_CTRL_M(div),
clk_cfg);
}
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* A83 specific clock code
*
* (C) Copyright 2007-2012
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* (C) Copyright 2015 Vishnu Patekar <vishnupatekar0510@gmail.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/prcm.h>
#include <asm/arch/sys_proto.h>
#ifdef CONFIG_SPL_BUILD
void clock_init_safe(void)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
clock_set_pll1(408000000);
/* enable pll_hsic, default is 480M */
writel(PLL8_CFG_DEFAULT, &ccm->pll8_cfg);
writel(readl(&ccm->pll8_cfg) | (0x1 << 31), &ccm->pll8_cfg);
while (!(readl(&ccm->pll_stable_status) & (1 << 8))) {}
/* switch to default 24MHz before changing to hsic */
writel(0x0, &ccm->cci400_cfg);
sdelay(50);
writel(CCM_CCI400_CLK_SEL_HSIC, &ccm->cci400_cfg);
sdelay(100);
/* switch before changing pll6 */
clrsetbits_le32(&ccm->ahb1_apb1_div, AHB1_CLK_SRC_MASK,
AHB1_CLK_SRC_OSC24M);
writel(PLL6_CFG_DEFAULT, &ccm->pll6_cfg);
while (!(readl(&ccm->pll_stable_status) & (1 << 6))) {}
writel(AHB1_ABP1_DIV_DEFAULT, &ccm->ahb1_apb1_div);
writel(CCM_MBUS_RESET_RESET, &ccm->mbus_reset);
writel(MBUS_CLK_DEFAULT, &ccm->mbus_clk_cfg);
/* timestamp */
writel(1, 0x01720000);
}
#endif
void clock_init_uart(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* uart clock source is apb2 */
writel(APB2_CLK_SRC_OSC24M|
APB2_CLK_RATE_N_1|
APB2_CLK_RATE_M(1),
&ccm->apb2_div);
/* open the clock for uart */
setbits_le32(&ccm->apb2_gate,
CLK_GATE_OPEN << (APB2_GATE_UART_SHIFT +
CONFIG_CONS_INDEX - 1));
/* deassert uart reset */
setbits_le32(&ccm->apb2_reset_cfg,
1 << (APB2_RESET_UART_SHIFT +
CONFIG_CONS_INDEX - 1));
}
#ifdef CONFIG_SPL_BUILD
void clock_set_pll1(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int p = 0;
/* Switch to 24MHz clock while changing PLL1 */
writel(AXI_DIV_2 << AXI0_DIV_SHIFT |
AXI_DIV_2 << AXI1_DIV_SHIFT |
CPU_CLK_SRC_OSC24M << C0_CPUX_CLK_SRC_SHIFT |
CPU_CLK_SRC_OSC24M << C1_CPUX_CLK_SRC_SHIFT,
&ccm->cpu_axi_cfg);
/* clk = 24*n/p, p is ignored if clock is >288MHz */
writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_P(p) | CMM_PLL1_CLOCK_TIME_2 |
CCM_PLL1_CTRL_N(clk / 24000000),
&ccm->pll1_c0_cfg);
while (!(readl(&ccm->pll_stable_status) & 0x01)) {}
writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_P(p) | CMM_PLL1_CLOCK_TIME_2 |
CCM_PLL1_CTRL_N(clk / (24000000)),
&ccm->pll1_c1_cfg);
while (!(readl(&ccm->pll_stable_status) & 0x02)) {}
/* Switch CPU to PLL1 */
writel(AXI_DIV_2 << AXI0_DIV_SHIFT |
AXI_DIV_2 << AXI1_DIV_SHIFT |
CPU_CLK_SRC_PLL1 << C0_CPUX_CLK_SRC_SHIFT |
CPU_CLK_SRC_PLL1 << C1_CPUX_CLK_SRC_SHIFT,
&ccm->cpu_axi_cfg);
}
#endif
void clock_set_pll5(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
unsigned int div1 = 0, div2 = 0;
/* A83T PLL5 DDR rate = 24000000 * (n+1)/(div1+1)/(div2+1) */
writel(CCM_PLL5_CTRL_EN | CCM_PLL5_CTRL_UPD |
CCM_PLL5_CTRL_N(clk / (24000000)) |
div2 << CCM_PLL5_DIV2_SHIFT |
div1 << CCM_PLL5_DIV1_SHIFT, &ccm->pll5_cfg);
udelay(5500);
}
unsigned int clock_get_pll6(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
uint32_t rval = readl(&ccm->pll6_cfg);
int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT);
int div1 = ((rval & CCM_PLL6_CTRL_DIV1_MASK) >>
CCM_PLL6_CTRL_DIV1_SHIFT) + 1;
int div2 = ((rval & CCM_PLL6_CTRL_DIV2_MASK) >>
CCM_PLL6_CTRL_DIV2_SHIFT) + 1;
return 24000000 * n / div1 / div2;
}
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* sun9i specific clock code
*
* (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
*
* (C) Copyright 2016 Theobroma Systems Design und Consulting GmbH
* Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/prcm.h>
#include <asm/arch/sys_proto.h>
#ifdef CONFIG_SPL_BUILD
void clock_init_safe(void)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* Set up PLL12 (peripheral 1) */
clock_set_pll12(1200000000);
/* Set up PLL1 (cluster 0) and PLL2 (cluster 1) */
clock_set_pll1(408000000);
clock_set_pll2(408000000);
/* Set up PLL4 (peripheral 0) */
clock_set_pll4(960000000);
/* Set up dividers for AXI0 and APB0 on cluster 0: PLL1 / 2 = 204MHz */
writel(C0_CFG_AXI0_CLK_DIV_RATIO(2) |
C0_CFG_APB0_CLK_DIV_RATIO(2), &ccm->c0_cfg);
/* AHB0: 120 MHz (PLL_PERIPH0 / 8) */
writel(AHBx_SRC_PLL_PERIPH0 | AHBx_CLK_DIV_RATIO(8),
&ccm->ahb0_cfg);
/* AHB1: 240 MHz (PLL_PERIPH0 / 4) */
writel(AHBx_SRC_PLL_PERIPH0 | AHBx_CLK_DIV_RATIO(4),
&ccm->ahb1_cfg);
/* AHB2: 120 MHz (PLL_PERIPH0 / 8) */
writel(AHBx_SRC_PLL_PERIPH0 | AHBx_CLK_DIV_RATIO(8),
&ccm->ahb2_cfg);
/* APB0: 120 MHz (PLL_PERIPH0 / 8) */
writel(APB0_SRC_PLL_PERIPH0 | APB0_CLK_DIV_RATIO(8),
&ccm->apb0_cfg);
/* GTBUS: 400MHz (PERIPH0 div 3) */
writel(GTBUS_SRC_PLL_PERIPH1 | GTBUS_CLK_DIV_RATIO(3),
&ccm->gtbus_cfg);
/* CCI400: 480MHz (PERIPH1 div 2) */
writel(CCI400_SRC_PLL_PERIPH0 | CCI400_CLK_DIV_RATIO(2),
&ccm->cci400_cfg);
/* Deassert DMA reset and open clock gating for DMA */
setbits_le32(&ccm->ahb_reset1_cfg, (1 << 24));
setbits_le32(&ccm->apb1_gate, (1 << 24));
/* set enable-bit in TSTAMP_CTRL_REG */
writel(1, 0x01720000);
}
#endif
void clock_init_uart(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* open the clock for uart */
setbits_le32(&ccm->apb1_gate,
CLK_GATE_OPEN << (APB1_GATE_UART_SHIFT +
CONFIG_CONS_INDEX - 1));
/* deassert uart reset */
setbits_le32(&ccm->apb1_reset_cfg,
1 << (APB1_RESET_UART_SHIFT +
CONFIG_CONS_INDEX - 1));
}
#ifdef CONFIG_SPL_BUILD
void clock_set_pll1(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int p = 0;
/* Switch cluster 0 to 24MHz clock while changing PLL1 */
clrsetbits_le32(&ccm->cpu_clk_source, C0_CPUX_CLK_SRC_MASK,
C0_CPUX_CLK_SRC_OSC24M);
writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_P(p) |
CCM_PLL1_CLOCK_TIME_2 |
CCM_PLL1_CTRL_N(clk / 24000000),
&ccm->pll1_c0_cfg);
/*
* Don't bother with the stable-time registers, as it doesn't
* wait until the PLL is stable. Note, that even Allwinner
* just uses a delay loop (or rather the AVS timer) for this
* instead of the PLL_STABLE_STATUS register.
*/
sdelay(2000);
/* Switch cluster 0 back to PLL1 */
clrsetbits_le32(&ccm->cpu_clk_source, C0_CPUX_CLK_SRC_MASK,
C0_CPUX_CLK_SRC_PLL1);
}
void clock_set_pll2(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int p = 0;
/* Switch cluster 1 to 24MHz clock while changing PLL2 */
clrsetbits_le32(&ccm->cpu_clk_source, C1_CPUX_CLK_SRC_MASK,
C1_CPUX_CLK_SRC_OSC24M);
writel(CCM_PLL2_CTRL_EN | CCM_PLL2_CTRL_P(p) |
CCM_PLL2_CLOCK_TIME_2 | CCM_PLL2_CTRL_N(clk / 24000000),
&ccm->pll2_c1_cfg);
sdelay(2000);
/* Switch cluster 1 back to PLL2 */
clrsetbits_le32(&ccm->cpu_clk_source, C1_CPUX_CLK_SRC_MASK,
C1_CPUX_CLK_SRC_PLL2);
}
void clock_set_pll6(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int p = 0;
writel(CCM_PLL6_CTRL_EN | CCM_PLL6_CFG_UPDATE | CCM_PLL6_CTRL_P(p)
| CCM_PLL6_CTRL_N(clk / 24000000),
&ccm->pll6_ddr_cfg);
do { } while (!(readl(&ccm->pll_stable_status) & PLL_DDR_STATUS));
sdelay(2000);
}
void clock_set_pll12(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
if (readl(&ccm->pll12_periph1_cfg) & CCM_PLL12_CTRL_EN)
return;
writel(CCM_PLL12_CTRL_EN | CCM_PLL12_CTRL_N(clk / 24000000),
&ccm->pll12_periph1_cfg);
sdelay(2000);
}
void clock_set_pll4(unsigned int clk)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
writel(CCM_PLL4_CTRL_EN | CCM_PLL4_CTRL_N(clk / 24000000),
&ccm->pll4_periph0_cfg);
sdelay(2000);
}
#endif
int clock_twi_onoff(int port, int state)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
if (port > 4)
return -1;
/* set the apb reset and clock gate for twi */
if (state) {
setbits_le32(&ccm->apb1_gate,
CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT + port));
setbits_le32(&ccm->apb1_reset_cfg,
1 << (APB1_RESET_TWI_SHIFT + port));
} else {
clrbits_le32(&ccm->apb1_reset_cfg,
1 << (APB1_RESET_TWI_SHIFT + port));
clrbits_le32(&ccm->apb1_gate,
CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT + port));
}
return 0;
}
unsigned int clock_get_pll4_periph0(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
uint32_t rval = readl(&ccm->pll4_periph0_cfg);
int n = ((rval & CCM_PLL4_CTRL_N_MASK) >> CCM_PLL4_CTRL_N_SHIFT);
int p = ((rval & CCM_PLL4_CTRL_P_MASK) >> CCM_PLL4_CTRL_P_SHIFT);
int m = ((rval & CCM_PLL4_CTRL_M_MASK) >> CCM_PLL4_CTRL_M_SHIFT) + 1;
const int k = 1;
return ((24000000 * n * k) >> p) / m;
}
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clock.h>
#include <axp_pmic.h>
#include <errno.h>
#ifdef CONFIG_MACH_SUN6I
int sunxi_get_ss_bonding_id(void)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
static int bonding_id = -1;
if (bonding_id != -1)
return bonding_id;
/* Enable Security System */
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_SS);
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_SS);
bonding_id = readl(SUNXI_SS_BASE);
bonding_id = (bonding_id >> 16) & 0x7;
/* Disable Security System again */
clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_SS);
clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_SS);
return bonding_id;
}
#endif
#ifdef CONFIG_MACH_SUN8I
uint sunxi_get_sram_id(void)
{
uint id;
/* Unlock sram info reg, read it, relock */
setbits_le32(SUNXI_SRAMC_BASE + 0x24, (1 << 15));
id = readl(SUNXI_SRAMC_BASE + 0x24) >> 16;
clrbits_le32(SUNXI_SRAMC_BASE + 0x24, (1 << 15));
return id;
}
#endif
#ifdef CONFIG_DISPLAY_CPUINFO
int print_cpuinfo(void)
{
#ifdef CONFIG_MACH_SUN4I
puts("CPU: Allwinner A10 (SUN4I)\n");
#elif defined CONFIG_MACH_SUN5I
u32 val = readl(SUNXI_SID_BASE + 0x08);
switch ((val >> 12) & 0xf) {
case 0: puts("CPU: Allwinner A12 (SUN5I)\n"); break;
case 3: puts("CPU: Allwinner A13 (SUN5I)\n"); break;
case 7: puts("CPU: Allwinner A10s (SUN5I)\n"); break;
default: puts("CPU: Allwinner A1X (SUN5I)\n");
}
#elif defined CONFIG_MACH_SUN6I
switch (sunxi_get_ss_bonding_id()) {
case SUNXI_SS_BOND_ID_A31:
puts("CPU: Allwinner A31 (SUN6I)\n");
break;
case SUNXI_SS_BOND_ID_A31S:
puts("CPU: Allwinner A31s (SUN6I)\n");
break;
default:
printf("CPU: Allwinner A31? (SUN6I, id: %d)\n",
sunxi_get_ss_bonding_id());
}
#elif defined CONFIG_MACH_SUN7I
puts("CPU: Allwinner A20 (SUN7I)\n");
#elif defined CONFIG_MACH_SUN8I_A23
printf("CPU: Allwinner A23 (SUN8I %04x)\n", sunxi_get_sram_id());
#elif defined CONFIG_MACH_SUN8I_A33
printf("CPU: Allwinner A33 (SUN8I %04x)\n", sunxi_get_sram_id());
#elif defined CONFIG_MACH_SUN8I_A83T
printf("CPU: Allwinner A83T (SUN8I %04x)\n", sunxi_get_sram_id());
#elif defined CONFIG_MACH_SUN8I_H3
printf("CPU: Allwinner H3 (SUN8I %04x)\n", sunxi_get_sram_id());
#elif defined CONFIG_MACH_SUN8I_R40
printf("CPU: Allwinner R40 (SUN8I %04x)\n", sunxi_get_sram_id());
#elif defined CONFIG_MACH_SUN8I_V3S
printf("CPU: Allwinner V3s (SUN8I %04x)\n", sunxi_get_sram_id());
#elif defined CONFIG_MACH_SUN9I
puts("CPU: Allwinner A80 (SUN9I)\n");
#elif defined CONFIG_MACH_SUN50I
puts("CPU: Allwinner A64 (SUN50I)\n");
#elif defined CONFIG_MACH_SUN50I_H5
puts("CPU: Allwinner H5 (SUN50I)\n");
#elif defined CONFIG_MACH_SUN50I_H6
puts("CPU: Allwinner H6 (SUN50I)\n");
#else
#warning Please update cpu_info.c with correct CPU information
puts("CPU: SUNXI Family\n");
#endif
return 0;
}
#endif
#ifdef CONFIG_MACH_SUN8I_H3
#define SIDC_PRCTL 0x40
#define SIDC_RDKEY 0x60
#define SIDC_OP_LOCK 0xAC
uint32_t sun8i_efuse_read(uint32_t offset)
{
uint32_t reg_val;
reg_val = readl(SUNXI_SIDC_BASE + SIDC_PRCTL);
reg_val &= ~(((0x1ff) << 16) | 0x3);
reg_val |= (offset << 16);
writel(reg_val, SUNXI_SIDC_BASE + SIDC_PRCTL);
reg_val &= ~(((0xff) << 8) | 0x3);
reg_val |= (SIDC_OP_LOCK << 8) | 0x2;
writel(reg_val, SUNXI_SIDC_BASE + SIDC_PRCTL);
while (readl(SUNXI_SIDC_BASE + SIDC_PRCTL) & 0x2);
reg_val &= ~(((0x1ff) << 16) | ((0xff) << 8) | 0x3);
writel(reg_val, SUNXI_SIDC_BASE + SIDC_PRCTL);
reg_val = readl(SUNXI_SIDC_BASE + SIDC_RDKEY);
return reg_val;
}
#endif
int sunxi_get_sid(unsigned int *sid)
{
#ifdef CONFIG_AXP221_POWER
return axp_get_sid(sid);
#elif defined CONFIG_MACH_SUN8I_H3
/*
* H3 SID controller has a bug, which makes the initial value of
* SUNXI_SID_BASE at boot wrong.
* Read the value directly from SID controller, in order to get
* the correct value, and also refresh the wrong value at
* SUNXI_SID_BASE.
*/
int i;
for (i = 0; i< 4; i++)
sid[i] = sun8i_efuse_read(i * 4);
return 0;
#elif defined SUNXI_SID_BASE
int i;
for (i = 0; i< 4; i++)
sid[i] = readl((ulong)SUNXI_SID_BASE + 4 * i);
return 0;
#else
return -ENODEV;
#endif
}
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* DRAM init helper functions
*
* (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
*/
#include <common.h>
#include <time.h>
#include <asm/barriers.h>
#include <asm/io.h>
#include <asm/arch/dram.h>
/*
* Wait up to 1s for value to be set in given part of reg.
*/
void mctl_await_completion(u32 *reg, u32 mask, u32 val)
{
unsigned long tmo = timer_get_us() + 1000000;
while ((readl(reg) & mask) != val) {
if (timer_get_us() > tmo)
panic("Timeout initialising DRAM\n");
}
}
/*
* Test if memory at offset offset matches memory at begin of DRAM
*/
bool mctl_mem_matches(u32 offset)
{
/* Try to write different values to RAM at two addresses */
writel(0, CONFIG_SYS_SDRAM_BASE);
writel(0xaa55aa55, (ulong)CONFIG_SYS_SDRAM_BASE + offset);
dsb();
/* Check if the same value is actually observed when reading back */
return readl(CONFIG_SYS_SDRAM_BASE) ==
readl((ulong)CONFIG_SYS_SDRAM_BASE + offset);
}
@@ -0,0 +1,736 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* sunxi DRAM controller initialization
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
* (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
*
* Based on sun4i Linux kernel sources mach-sunxi/pm/standby/dram*.c
* and earlier U-Boot Allwinner A10 SPL work
*
* (C) Copyright 2007-2012
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Berg Xing <bergxing@allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*/
/*
* Unfortunately the only documentation we have on the sun7i DRAM
* controller is Allwinner boot0 + boot1 code, and that code uses
* magic numbers & shifts with no explanations. Hence this code is
* rather undocumented and full of magic.
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/timer.h>
#include <asm/arch/sys_proto.h>
#define CPU_CFG_CHIP_VER(n) ((n) << 6)
#define CPU_CFG_CHIP_VER_MASK CPU_CFG_CHIP_VER(0x3)
#define CPU_CFG_CHIP_REV_A 0x0
#define CPU_CFG_CHIP_REV_C1 0x1
#define CPU_CFG_CHIP_REV_C2 0x2
#define CPU_CFG_CHIP_REV_B 0x3
/*
* Wait up to 1s for mask to be clear in given reg.
*/
static inline void await_bits_clear(u32 *reg, u32 mask)
{
mctl_await_completion(reg, mask, 0);
}
/*
* Wait up to 1s for mask to be set in given reg.
*/
static inline void await_bits_set(u32 *reg, u32 mask)
{
mctl_await_completion(reg, mask, mask);
}
/*
* This performs the external DRAM reset by driving the RESET pin low and
* then high again. According to the DDR3 spec, the RESET pin needs to be
* kept low for at least 200 us.
*/
static void mctl_ddr3_reset(void)
{
struct sunxi_dram_reg *dram =
(struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
#ifdef CONFIG_MACH_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);
if ((reg_val & CPU_CFG_CHIP_VER_MASK) !=
CPU_CFG_CHIP_VER(CPU_CFG_CHIP_REV_A)) {
setbits_le32(&dram->mcr, DRAM_MCR_RESET);
udelay(200);
clrbits_le32(&dram->mcr, DRAM_MCR_RESET);
} else
#endif
{
clrbits_le32(&dram->mcr, DRAM_MCR_RESET);
udelay(200);
setbits_le32(&dram->mcr, DRAM_MCR_RESET);
}
/* After the RESET pin is de-asserted, the DDR3 spec requires to wait
* for additional 500 us before driving the CKE pin (Clock Enable)
* high. The duration of this delay can be configured in the SDR_IDCR
* (Initialization Delay Configuration Register) and applied
* automatically by the DRAM controller during the DDR3 initialization
* step. But SDR_IDCR has limited range on sun4i/sun5i hardware and
* can't provide sufficient delay at DRAM clock frequencies higher than
* 524 MHz (while Allwinner A13 supports DRAM clock frequency up to
* 533 MHz according to the datasheet). Additionally, there is no
* official documentation for the SDR_IDCR register anywhere, and
* there is always a chance that we are interpreting it wrong.
* Better be safe than sorry, so add an explicit delay here. */
udelay(500);
}
static void mctl_set_drive(void)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
#ifdef CONFIG_MACH_SUN7I
clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3) | (0x3 << 28),
#else
clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3),
#endif
DRAM_MCR_MODE_EN(0x3) |
0xffc);
}
static void mctl_itm_disable(void)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
clrsetbits_le32(&dram->ccr, DRAM_CCR_INIT, DRAM_CCR_ITM_OFF);
}
static void mctl_itm_enable(void)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
clrbits_le32(&dram->ccr, DRAM_CCR_ITM_OFF);
}
static void mctl_itm_reset(void)
{
mctl_itm_disable();
udelay(1); /* ITM reset needs a bit of delay */
mctl_itm_enable();
udelay(1);
}
static void mctl_enable_dll0(u32 phase)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
clrsetbits_le32(&dram->dllcr[0], 0x3f << 6,
((phase >> 16) & 0x3f) << 6);
clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET, DRAM_DLLCR_DISABLE);
udelay(2);
clrbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET | DRAM_DLLCR_DISABLE);
udelay(22);
clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_DISABLE, DRAM_DLLCR_NRESET);
udelay(22);
}
/* Get the number of DDR byte lanes */
static u32 mctl_get_number_of_lanes(void)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
if ((readl(&dram->dcr) & DRAM_DCR_BUS_WIDTH_MASK) ==
DRAM_DCR_BUS_WIDTH(DRAM_DCR_BUS_WIDTH_32BIT))
return 4;
else
return 2;
}
/*
* Note: This differs from pm/standby in that it checks the bus width
*/
static void mctl_enable_dllx(u32 phase)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
u32 i, number_of_lanes;
number_of_lanes = mctl_get_number_of_lanes();
for (i = 1; i <= number_of_lanes; i++) {
clrsetbits_le32(&dram->dllcr[i], 0xf << 14,
(phase & 0xf) << 14);
clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET,
DRAM_DLLCR_DISABLE);
phase >>= 4;
}
udelay(2);
for (i = 1; i <= number_of_lanes; i++)
clrbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET |
DRAM_DLLCR_DISABLE);
udelay(22);
for (i = 1; i <= number_of_lanes; i++)
clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_DISABLE,
DRAM_DLLCR_NRESET);
udelay(22);
}
static u32 hpcr_value[32] = {
#ifdef CONFIG_MACH_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_MACH_SUN4I
0x0301, 0x0301, 0x0301, 0x0301,
0x0301, 0x0301, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0x1031, 0x1031, 0x0735, 0x5031,
0x1035, 0x0731, 0x1031, 0x0735,
0x1035, 0x1031, 0x0731, 0x1035,
0x1031, 0x0301, 0x0301, 0x0731
#endif
#ifdef CONFIG_MACH_SUN7I
0x0301, 0x0301, 0x0301, 0x0301,
0x0301, 0x0301, 0x0301, 0x0301,
0, 0, 0, 0,
0, 0, 0, 0,
0x1031, 0x1031, 0x0735, 0x1035,
0x1035, 0x0731, 0x1031, 0x0735,
0x1035, 0x1031, 0x0731, 0x1035,
0x0001, 0x1031, 0, 0x1031
/* last row differs from boot0 source table
* 0x1031, 0x0301, 0x0301, 0x0731
* but boot0 code skips #28 and #30, and sets #29 and #31 to the
* value from #28 entry (0x1031)
*/
#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 mbus_clk)
{
u32 reg_val;
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
u32 pll5p_clk, pll6x_clk;
u32 pll5p_div, pll6x_div;
u32 pll5p_rate, pll6x_rate;
/* setup DRAM PLL */
reg_val = readl(&ccm->pll5_cfg);
reg_val &= ~CCM_PLL5_CTRL_M_MASK; /* set M to 0 (x1) */
reg_val &= ~CCM_PLL5_CTRL_K_MASK; /* set K to 0 (x1) */
reg_val &= ~CCM_PLL5_CTRL_N_MASK; /* set N to 0 (x0) */
reg_val &= ~CCM_PLL5_CTRL_P_MASK; /* set P to 0 (x1) */
#ifdef CONFIG_OLD_SUNXI_KERNEL_COMPAT
/* Old kernels are hardcoded to P=1 (divide by 2) */
reg_val |= CCM_PLL5_CTRL_P(1);
#endif
if (clk >= 540 && clk < 552) {
/* dram = 540MHz */
reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2));
reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3));
reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(15));
} else if (clk >= 512 && clk < 528) {
/* dram = 512MHz */
reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3));
reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(4));
reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(16));
} else if (clk >= 496 && clk < 504) {
/* dram = 496MHz */
reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3));
reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2));
reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(31));
} else if (clk >= 468 && clk < 480) {
/* dram = 468MHz */
reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2));
reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3));
reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(13));
} else if (clk >= 396 && clk < 408) {
/* dram = 396MHz */
reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2));
reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3));
reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(11));
} else {
/* any other frequency that is a multiple of 24 */
reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2));
reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2));
reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(clk / 24));
}
reg_val &= ~CCM_PLL5_CTRL_VCO_GAIN; /* PLL VCO Gain off */
reg_val |= CCM_PLL5_CTRL_EN; /* PLL On */
writel(reg_val, &ccm->pll5_cfg);
udelay(5500);
setbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_DDR_CLK);
#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN7I)
/* reset GPS */
clrbits_le32(&ccm->gps_clk_cfg, CCM_GPS_CTRL_RESET | CCM_GPS_CTRL_GATE);
setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS);
udelay(1);
clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS);
#endif
/* setup MBUS clock */
if (!mbus_clk)
mbus_clk = 300;
/* PLL5P and PLL6 are the potential clock sources for MBUS */
pll6x_clk = clock_get_pll6() / 1000000;
#ifdef CONFIG_MACH_SUN7I
pll6x_clk *= 2; /* sun7i uses PLL6*2, sun5i uses just PLL6 */
#endif
pll5p_clk = clock_get_pll5p() / 1000000;
pll6x_div = DIV_ROUND_UP(pll6x_clk, mbus_clk);
pll5p_div = DIV_ROUND_UP(pll5p_clk, mbus_clk);
pll6x_rate = pll6x_clk / pll6x_div;
pll5p_rate = pll5p_clk / pll5p_div;
if (pll6x_div <= 16 && pll6x_rate > pll5p_rate) {
/* use PLL6 as the MBUS clock source */
reg_val = CCM_MBUS_CTRL_GATE |
CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL6) |
CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) |
CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll6x_div));
} else if (pll5p_div <= 16) {
/* use PLL5P as the MBUS clock source */
reg_val = CCM_MBUS_CTRL_GATE |
CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL5) |
CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) |
CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll5p_div));
} else {
panic("Bad mbus_clk\n");
}
writel(reg_val, &ccm->mbus_clk_cfg);
/*
* open DRAMC AHB & DLL register clock
* close it first
*/
#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I)
clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL);
#else
clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM);
#endif
udelay(22);
/* then open it */
#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I)
setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL);
#else
setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM);
#endif
udelay(22);
}
/*
* The data from rslrX and rdgrX registers (X=rank) is stored
* in a single 32-bit value using the following format:
* bits [31:26] - DQS gating system latency for byte lane 3
* bits [25:24] - DQS gating phase select for byte lane 3
* bits [23:18] - DQS gating system latency for byte lane 2
* bits [17:16] - DQS gating phase select for byte lane 2
* bits [15:10] - DQS gating system latency for byte lane 1
* bits [ 9:8 ] - DQS gating phase select for byte lane 1
* bits [ 7:2 ] - DQS gating system latency for byte lane 0
* bits [ 1:0 ] - DQS gating phase select for byte lane 0
*/
static void mctl_set_dqs_gating_delay(int rank, u32 dqs_gating_delay)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
u32 lane, number_of_lanes = mctl_get_number_of_lanes();
/* rank0 gating system latency (3 bits per lane: cycles) */
u32 slr = readl(rank == 0 ? &dram->rslr0 : &dram->rslr1);
/* rank0 gating phase select (2 bits per lane: 90, 180, 270, 360) */
u32 dgr = readl(rank == 0 ? &dram->rdgr0 : &dram->rdgr1);
for (lane = 0; lane < number_of_lanes; lane++) {
u32 tmp = dqs_gating_delay >> (lane * 8);
slr &= ~(7 << (lane * 3));
slr |= ((tmp >> 2) & 7) << (lane * 3);
dgr &= ~(3 << (lane * 2));
dgr |= (tmp & 3) << (lane * 2);
}
writel(slr, rank == 0 ? &dram->rslr0 : &dram->rslr1);
writel(dgr, rank == 0 ? &dram->rdgr0 : &dram->rdgr1);
}
static int dramc_scan_readpipe(void)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
u32 reg_val;
/* data training trigger */
clrbits_le32(&dram->csr, DRAM_CSR_FAILED);
setbits_le32(&dram->ccr, DRAM_CCR_DATA_TRAINING);
/* check whether data training process has completed */
await_bits_clear(&dram->ccr, DRAM_CCR_DATA_TRAINING);
/* check data training result */
reg_val = readl(&dram->csr);
if (reg_val & DRAM_CSR_FAILED)
return -1;
return 0;
}
static void dramc_clock_output_en(u32 on)
{
#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I)
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
if (on)
setbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT);
else
clrbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT);
#endif
#ifdef CONFIG_MACH_SUN4I
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
if (on)
setbits_le32(&ccm->dram_clk_gate, CCM_DRAM_CTRL_DCLK_OUT);
else
clrbits_le32(&ccm->dram_clk_gate, CCM_DRAM_CTRL_DCLK_OUT);
#endif
}
/* tRFC in nanoseconds for different densities (from the DDR3 spec) */
static const u16 tRFC_DDR3_table[6] = {
/* 256Mb 512Mb 1Gb 2Gb 4Gb 8Gb */
90, 90, 110, 160, 300, 350
};
static void dramc_set_autorefresh_cycle(u32 clk, u32 density)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
u32 tRFC, tREFI;
tRFC = (tRFC_DDR3_table[density] * clk + 999) / 1000;
tREFI = (7987 * clk) >> 10; /* <= 7.8us */
writel(DRAM_DRR_TREFI(tREFI) | DRAM_DRR_TRFC(tRFC), &dram->drr);
}
/* Calculate the value for A11, A10, A9 bits in MR0 (write recovery) */
static u32 ddr3_write_recovery(u32 clk)
{
u32 twr_ns = 15; /* DDR3 spec says that it is 15ns for all speed bins */
u32 twr_ck = (twr_ns * clk + 999) / 1000;
if (twr_ck < 5)
return 1;
else if (twr_ck <= 8)
return twr_ck - 4;
else if (twr_ck <= 10)
return 5;
else
return 6;
}
/*
* If the dram->ppwrsctl (SDR_DPCR) register has the lowest bit set to 1, this
* means that DRAM is currently in self-refresh mode and retaining the old
* data. Since we have no idea what to do in this situation yet, just set this
* register to 0 and initialize DRAM in the same way as on any normal reboot
* (discarding whatever was stored there).
*
* Note: on sun7i hardware, the highest 16 bits need to be set to 0x1651 magic
* value for this write operation to have any effect. On sun5i hadware this
* magic value is not necessary. And on sun4i hardware the writes to this
* register seem to have no effect at all.
*/
static void mctl_disable_power_save(void)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
writel(0x16510000, &dram->ppwrsctl);
}
/*
* After the DRAM is powered up or reset, the DDR3 spec requires to wait at
* least 500 us before driving the CKE pin (Clock Enable) high. The dram->idct
* (SDR_IDCR) register appears to configure this delay, which gets applied
* right at the time when the DRAM initialization is activated in the
* 'mctl_ddr3_initialize' function.
*/
static void mctl_set_cke_delay(void)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
/* The CKE delay is represented in DRAM clock cycles, multiplied by N
* (where N=2 for sun4i/sun5i and N=3 for sun7i). Here it is set to
* the maximum possible value 0x1ffff, just like in the Allwinner's
* boot0 bootloader. The resulting delay value is somewhere between
* ~0.4 ms (sun5i with 648 MHz DRAM clock speed) and ~1.1 ms (sun7i
* with 360 MHz DRAM clock speed). */
setbits_le32(&dram->idcr, 0x1ffff);
}
/*
* This triggers the DRAM initialization. It performs sending the mode registers
* to the DRAM among other things. Very likely the ZQCL command is also getting
* executed (to do the initial impedance calibration on the DRAM side of the
* wire). The memory controller and the PHY must be already configured before
* calling this function.
*/
static void mctl_ddr3_initialize(void)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
setbits_le32(&dram->ccr, DRAM_CCR_INIT);
await_bits_clear(&dram->ccr, DRAM_CCR_INIT);
}
/*
* Perform impedance calibration on the DRAM controller side of the wire.
*/
static void mctl_set_impedance(u32 zq, bool odt_en)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
u32 reg_val;
u32 zprog = zq & 0xFF, zdata = (zq >> 8) & 0xFFFFF;
#ifndef CONFIG_MACH_SUN7I
/* Appears that some kind of automatically initiated default
* ZQ calibration is already in progress at this point on sun4i/sun5i
* hardware, but not on sun7i. So it is reasonable to wait for its
* completion before doing anything else. */
await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE);
#endif
/* ZQ calibration is not really useful unless ODT is enabled */
if (!odt_en)
return;
#ifdef CONFIG_MACH_SUN7I
/* Enabling ODT in SDR_IOCR on sun7i hardware results in a deadlock
* unless bit 24 is set in SDR_ZQCR1. Not much is known about the
* SDR_ZQCR1 register, but there are hints indicating that it might
* be related to periodic impedance re-calibration. This particular
* magic value is borrowed from the Allwinner boot0 bootloader, and
* using it helps to avoid troubles */
writel((1 << 24) | (1 << 1), &dram->zqcr1);
#endif
/* Needed at least for sun5i, because it does not self clear there */
clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL);
if (zdata) {
/* Set the user supplied impedance data */
reg_val = DRAM_ZQCR0_ZDEN | zdata;
writel(reg_val, &dram->zqcr0);
/* no need to wait, this takes effect immediately */
} else {
/* Do the calibration using the external resistor */
reg_val = DRAM_ZQCR0_ZCAL | DRAM_ZQCR0_IMP_DIV(zprog);
writel(reg_val, &dram->zqcr0);
/* Wait for the new impedance configuration to settle */
await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE);
}
/* Needed at least for sun5i, because it does not self clear there */
clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL);
/* Set I/O configure register */
writel(DRAM_IOCR_ODT_EN, &dram->iocr);
}
static unsigned long dramc_init_helper(struct dram_para *para)
{
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
u32 reg_val;
u32 density;
int ret_val;
/*
* only single rank DDR3 is supported by this code even though the
* hardware can theoretically support DDR2 and up to two ranks
*/
if (para->type != DRAM_MEMORY_TYPE_DDR3 || para->rank_num != 1)
return 0;
/* setup DRAM relative clock */
mctl_setup_dram_clock(para->clock, para->mbus_clock);
/* Disable any pad power save control */
mctl_disable_power_save();
mctl_set_drive();
/* dram clock off */
dramc_clock_output_en(0);
#ifdef CONFIG_MACH_SUN4I
/* select dram controller 1 */
writel(DRAM_CSEL_MAGIC, &dram->csel);
#endif
mctl_itm_disable();
mctl_enable_dll0(para->tpr3);
/* configure external DRAM */
reg_val = DRAM_DCR_TYPE_DDR3;
reg_val |= DRAM_DCR_IO_WIDTH(para->io_width >> 3);
if (para->density == 256)
density = DRAM_DCR_CHIP_DENSITY_256M;
else if (para->density == 512)
density = DRAM_DCR_CHIP_DENSITY_512M;
else if (para->density == 1024)
density = DRAM_DCR_CHIP_DENSITY_1024M;
else if (para->density == 2048)
density = DRAM_DCR_CHIP_DENSITY_2048M;
else if (para->density == 4096)
density = DRAM_DCR_CHIP_DENSITY_4096M;
else if (para->density == 8192)
density = DRAM_DCR_CHIP_DENSITY_8192M;
else
density = DRAM_DCR_CHIP_DENSITY_256M;
reg_val |= DRAM_DCR_CHIP_DENSITY(density);
reg_val |= DRAM_DCR_BUS_WIDTH((para->bus_width >> 3) - 1);
reg_val |= DRAM_DCR_RANK_SEL(para->rank_num - 1);
reg_val |= DRAM_DCR_CMD_RANK_ALL;
reg_val |= DRAM_DCR_MODE(DRAM_DCR_MODE_INTERLEAVE);
writel(reg_val, &dram->dcr);
dramc_clock_output_en(1);
mctl_set_impedance(para->zq, para->odt_en);
mctl_set_cke_delay();
mctl_ddr3_reset();
udelay(1);
await_bits_clear(&dram->ccr, DRAM_CCR_INIT);
mctl_enable_dllx(para->tpr3);
/* set refresh period */
dramc_set_autorefresh_cycle(para->clock, density);
/* set timing parameters */
writel(para->tpr0, &dram->tpr0);
writel(para->tpr1, &dram->tpr1);
writel(para->tpr2, &dram->tpr2);
reg_val = DRAM_MR_BURST_LENGTH(0x0);
#if (defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I))
reg_val |= DRAM_MR_POWER_DOWN;
#endif
reg_val |= DRAM_MR_CAS_LAT(para->cas - 4);
reg_val |= DRAM_MR_WRITE_RECOVERY(ddr3_write_recovery(para->clock));
writel(reg_val, &dram->mr);
writel(para->emr1, &dram->emr);
writel(para->emr2, &dram->emr2);
writel(para->emr3, &dram->emr3);
/* disable drift compensation and set passive DQS window mode */
clrsetbits_le32(&dram->ccr, DRAM_CCR_DQS_DRIFT_COMP, DRAM_CCR_DQS_GATE);
#ifdef CONFIG_MACH_SUN7I
/* Command rate timing mode 2T & 1T */
if (para->tpr4 & 0x1)
setbits_le32(&dram->ccr, DRAM_CCR_COMMAND_RATE_1T);
#endif
/* initialize external DRAM */
mctl_ddr3_initialize();
/* scan read pipe value */
mctl_itm_enable();
/* Hardware DQS gate training */
ret_val = dramc_scan_readpipe();
if (ret_val < 0)
return 0;
/* allow to override the DQS training results with a custom delay */
if (para->dqs_gating_delay)
mctl_set_dqs_gating_delay(0, para->dqs_gating_delay);
/* set the DQS gating window type */
if (para->active_windowing)
clrbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE);
else
setbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE);
mctl_itm_reset();
/* configure all host port */
mctl_configure_hostport();
return get_ram_size((long *)PHYS_SDRAM_0, PHYS_SDRAM_0_SIZE);
}
unsigned long dramc_init(struct dram_para *para)
{
unsigned long dram_size, actual_density;
/* If the dram configuration is not provided, use a default */
if (!para)
return 0;
/* if everything is known, then autodetection is not necessary */
if (para->io_width && para->bus_width && para->density)
return dramc_init_helper(para);
/* try to autodetect the DRAM bus width and density */
para->io_width = 16;
para->bus_width = 32;
#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN5I)
/* only A0-A14 address lines on A10/A13, limiting max density to 4096 */
para->density = 4096;
#else
/* all A0-A15 address lines on A20, which allow density 8192 */
para->density = 8192;
#endif
dram_size = dramc_init_helper(para);
if (!dram_size) {
/* if 32-bit bus width failed, try 16-bit bus width instead */
para->bus_width = 16;
dram_size = dramc_init_helper(para);
if (!dram_size) {
/* if 16-bit bus width also failed, then bail out */
return dram_size;
}
}
/* check if we need to adjust the density */
actual_density = (dram_size >> 17) * para->io_width / para->bus_width;
if (actual_density != para->density) {
/* update the density and re-initialize DRAM again */
para->density = actual_density;
dram_size = dramc_init_helper(para);
}
return dram_size;
}
@@ -0,0 +1,684 @@
/*
* sun50i H6 platform dram controller init
*
* (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
#include <linux/bitops.h>
#include <linux/kconfig.h>
/*
* The DRAM controller structure on H6 is similar to the ones on A23/A80:
* they all contains 3 parts, COM, CTL and PHY. (As a note on A33/A83T/H3/A64
* /H5/R40 CTL and PHY is composed).
*
* COM is allwinner-specific. On H6, the address mapping function is moved
* from COM to CTL (with the standard ADDRMAP registers on DesignWare memory
* controller).
*
* CTL (controller) and PHY is from DesignWare.
*
* The CTL part is a bit similar to the one on A23/A80 (because they all
* originate from DesignWare), but gets more registers added.
*
* The PHY part is quite new, not seen in any previous Allwinner SoCs, and
* not seen on other SoCs in U-Boot. The only SoC that is also known to have
* similar PHY is ZynqMP.
*/
static void mctl_sys_init(struct dram_para *para);
static void mctl_com_init(struct dram_para *para);
static void mctl_channel_init(struct dram_para *para);
static void mctl_core_init(struct dram_para *para)
{
mctl_sys_init(para);
mctl_com_init(para);
switch (para->type) {
case SUNXI_DRAM_TYPE_LPDDR3:
case SUNXI_DRAM_TYPE_DDR3:
mctl_set_timing_params(para);
break;
default:
panic("Unsupported DRAM type!");
};
mctl_channel_init(para);
}
/* PHY initialisation */
static void mctl_phy_pir_init(u32 val)
{
struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
writel(val, &mctl_phy->pir);
writel(val | BIT(0), &mctl_phy->pir); /* Start initialisation. */
mctl_await_completion(&mctl_phy->pgsr[0], BIT(0), BIT(0));
}
enum {
MBUS_PORT_CPU = 0,
MBUS_PORT_GPU = 1,
MBUS_PORT_MAHB = 2,
MBUS_PORT_DMA = 3,
MBUS_PORT_VE = 4,
MBUS_PORT_CE = 5,
MBUS_PORT_TSC0 = 6,
MBUS_PORT_NDFC0 = 8,
MBUS_PORT_CSI0 = 11,
MBUS_PORT_DI0 = 14,
MBUS_PORT_DI1 = 15,
MBUS_PORT_DE300 = 16,
MBUS_PORT_IOMMU = 25,
MBUS_PORT_VE2 = 26,
MBUS_PORT_USB3 = 37,
MBUS_PORT_PCIE = 38,
MBUS_PORT_VP9 = 39,
MBUS_PORT_HDCP2 = 40,
};
enum {
MBUS_QOS_LOWEST = 0,
MBUS_QOS_LOW,
MBUS_QOS_HIGH,
MBUS_QOS_HIGHEST
};
inline void mbus_configure_port(u8 port,
bool bwlimit,
bool priority,
u8 qos,
u8 waittime,
u8 acs,
u16 bwl0,
u16 bwl1,
u16 bwl2)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
const u32 cfg0 = ( (bwlimit ? (1 << 0) : 0)
| (priority ? (1 << 1) : 0)
| ((qos & 0x3) << 2)
| ((waittime & 0xf) << 4)
| ((acs & 0xff) << 8)
| (bwl0 << 16) );
const u32 cfg1 = ((u32)bwl2 << 16) | (bwl1 & 0xffff);
debug("MBUS port %d cfg0 %08x cfg1 %08x\n", port, cfg0, cfg1);
writel(cfg0, &mctl_com->master[port].cfg0);
writel(cfg1, &mctl_com->master[port].cfg1);
}
#define MBUS_CONF(port, bwlimit, qos, acs, bwl0, bwl1, bwl2) \
mbus_configure_port(MBUS_PORT_ ## port, bwlimit, false, \
MBUS_QOS_ ## qos, 0, acs, bwl0, bwl1, bwl2)
static void mctl_set_master_priority(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
/* enable bandwidth limit windows and set windows size 1us */
writel(399, &mctl_com->tmr);
writel(BIT(16), &mctl_com->bwcr);
MBUS_CONF( CPU, true, HIGHEST, 0, 256, 128, 100);
MBUS_CONF( GPU, true, HIGH, 0, 1536, 1400, 256);
MBUS_CONF( MAHB, true, HIGHEST, 0, 512, 256, 96);
MBUS_CONF( DMA, true, HIGH, 0, 256, 100, 80);
MBUS_CONF( VE, true, HIGH, 2, 8192, 5500, 5000);
MBUS_CONF( CE, true, HIGH, 2, 100, 64, 32);
MBUS_CONF( TSC0, true, HIGH, 2, 100, 64, 32);
MBUS_CONF(NDFC0, true, HIGH, 0, 256, 128, 64);
MBUS_CONF( CSI0, true, HIGH, 0, 256, 128, 100);
MBUS_CONF( DI0, true, HIGH, 0, 1024, 256, 64);
MBUS_CONF(DE300, true, HIGHEST, 6, 8192, 2800, 2400);
MBUS_CONF(IOMMU, true, HIGHEST, 0, 100, 64, 32);
MBUS_CONF( VE2, true, HIGH, 2, 8192, 5500, 5000);
MBUS_CONF( USB3, true, HIGH, 0, 256, 128, 64);
MBUS_CONF( PCIE, true, HIGH, 2, 100, 64, 32);
MBUS_CONF( VP9, true, HIGH, 2, 8192, 5500, 5000);
MBUS_CONF(HDCP2, true, HIGH, 2, 100, 64, 32);
}
static void mctl_sys_init(struct dram_para *para)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
/* Put all DRAM-related blocks to reset state */
clrbits_le32(&ccm->mbus_cfg, MBUS_ENABLE | MBUS_RESET);
clrbits_le32(&ccm->dram_gate_reset, BIT(0));
udelay(5);
writel(0, &ccm->dram_gate_reset);
clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
clrbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);
udelay(5);
/* Set PLL5 rate to doubled DRAM clock rate */
writel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN |
CCM_PLL5_CTRL_N(para->clk * 2 / 24 - 1), &ccm->pll5_cfg);
mctl_await_completion(&ccm->pll5_cfg, CCM_PLL5_LOCK, CCM_PLL5_LOCK);
/* Configure DRAM mod clock */
writel(DRAM_CLK_SRC_PLL5, &ccm->dram_clk_cfg);
setbits_le32(&ccm->dram_clk_cfg, DRAM_CLK_UPDATE);
writel(BIT(RESET_SHIFT), &ccm->dram_gate_reset);
udelay(5);
setbits_le32(&ccm->dram_gate_reset, BIT(0));
/* Disable all channels */
writel(0, &mctl_com->maer0);
writel(0, &mctl_com->maer1);
writel(0, &mctl_com->maer2);
/* Configure MBUS and enable DRAM mod reset */
setbits_le32(&ccm->mbus_cfg, MBUS_RESET);
setbits_le32(&ccm->mbus_cfg, MBUS_ENABLE);
setbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);
udelay(5);
/* Unknown hack from the BSP, which enables access of mctl_ctl regs */
writel(0x8000, &mctl_ctl->unk_0x00c);
}
static void mctl_set_addrmap(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
u8 cols = para->cols;
u8 rows = para->rows;
u8 ranks = para->ranks;
if (!para->bus_full_width)
cols -= 1;
/* Ranks */
if (ranks == 2)
mctl_ctl->addrmap[0] = rows + cols - 3;
else
mctl_ctl->addrmap[0] = 0x1F;
/* Banks, hardcoded to 8 banks now */
mctl_ctl->addrmap[1] = (cols - 2) | (cols - 2) << 8 | (cols - 2) << 16;
/* Columns */
mctl_ctl->addrmap[2] = 0;
switch (cols) {
case 7:
mctl_ctl->addrmap[3] = 0x1F1F1F00;
mctl_ctl->addrmap[4] = 0x1F1F;
break;
case 8:
mctl_ctl->addrmap[3] = 0x1F1F0000;
mctl_ctl->addrmap[4] = 0x1F1F;
break;
case 9:
mctl_ctl->addrmap[3] = 0x1F000000;
mctl_ctl->addrmap[4] = 0x1F1F;
break;
case 10:
mctl_ctl->addrmap[3] = 0;
mctl_ctl->addrmap[4] = 0x1F1F;
break;
case 11:
mctl_ctl->addrmap[3] = 0;
mctl_ctl->addrmap[4] = 0x1F00;
break;
case 12:
mctl_ctl->addrmap[3] = 0;
mctl_ctl->addrmap[4] = 0;
break;
default:
panic("Unsupported DRAM configuration: column number invalid\n");
}
/* Rows */
mctl_ctl->addrmap[5] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);
switch (rows) {
case 13:
mctl_ctl->addrmap[6] = (cols - 3) | 0x0F0F0F00;
mctl_ctl->addrmap[7] = 0x0F0F;
break;
case 14:
mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | 0x0F0F0000;
mctl_ctl->addrmap[7] = 0x0F0F;
break;
case 15:
mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | 0x0F000000;
mctl_ctl->addrmap[7] = 0x0F0F;
break;
case 16:
mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);
mctl_ctl->addrmap[7] = 0x0F0F;
break;
case 17:
mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);
mctl_ctl->addrmap[7] = (cols - 3) | 0x0F00;
break;
case 18:
mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);
mctl_ctl->addrmap[7] = (cols - 3) | ((cols - 3) << 8);
break;
default:
panic("Unsupported DRAM configuration: row number invalid\n");
}
/* Bank groups, DDR4 only */
mctl_ctl->addrmap[8] = 0x3F3F;
}
static void mctl_com_init(struct dram_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
u32 reg_val, tmp;
mctl_set_addrmap(para);
setbits_le32(&mctl_com->cr, BIT(31));
/* The bonding ID seems to be always 7. */
if (readl(SUNXI_SIDC_BASE + 0x100) == 7) /* bonding ID */
clrbits_le32(&mctl_com->cr, BIT(27));
else if (readl(SUNXI_SIDC_BASE + 0x100) == 3)
setbits_le32(&mctl_com->cr, BIT(27));
if (para->clk > 408)
reg_val = 0xf00;
else if (para->clk > 246)
reg_val = 0x1f00;
else
reg_val = 0x3f00;
clrsetbits_le32(&mctl_com->unk_0x008, 0x3f00, reg_val);
/* TODO: DDR4 */
reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(para->ranks);
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
reg_val |= MSTR_DEVICETYPE_LPDDR3;
if (para->type == SUNXI_DRAM_TYPE_DDR3)
reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
if (para->bus_full_width)
reg_val |= MSTR_BUSWIDTH_FULL;
else
reg_val |= MSTR_BUSWIDTH_HALF;
writel(reg_val | BIT(31), &mctl_ctl->mstr);
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
reg_val = DCR_LPDDR3 | DCR_DDR8BANK;
if (para->type == SUNXI_DRAM_TYPE_DDR3)
reg_val = DCR_DDR3 | DCR_DDR8BANK | DCR_DDR2T;
writel(reg_val | 0x400, &mctl_phy->dcr);
if (para->ranks == 2)
writel(0x0303, &mctl_ctl->odtmap);
else
writel(0x0201, &mctl_ctl->odtmap);
/* TODO: DDR4 */
if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
tmp = para->clk * 7 / 2000;
reg_val = 0x0400;
reg_val |= (tmp + 7) << 24;
reg_val |= (((para->clk < 400) ? 3 : 4) - tmp) << 16;
} else if (para->type == SUNXI_DRAM_TYPE_DDR3) {
reg_val = 0x06000400; /* TODO?: Use CL - CWL value in [7:0] */
} else {
panic("Only (LP)DDR3 supported (type = %d)\n", para->type);
}
writel(reg_val, &mctl_ctl->odtcfg);
if (!para->bus_full_width) {
writel(0x0, &mctl_phy->dx[2].gcr[0]);
writel(0x0, &mctl_phy->dx[3].gcr[0]);
}
}
static void mctl_bit_delay_set(struct dram_para *para)
{
struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
int i, j;
u32 val;
for (i = 0; i < 4; i++) {
val = readl(&mctl_phy->dx[i].bdlr0);
for (j = 0; j < 4; j++)
val += para->dx_write_delays[i][j] << (j * 8);
writel(val, &mctl_phy->dx[i].bdlr0);
val = readl(&mctl_phy->dx[i].bdlr1);
for (j = 0; j < 4; j++)
val += para->dx_write_delays[i][j + 4] << (j * 8);
writel(val, &mctl_phy->dx[i].bdlr1);
val = readl(&mctl_phy->dx[i].bdlr2);
for (j = 0; j < 4; j++)
val += para->dx_write_delays[i][j + 8] << (j * 8);
writel(val, &mctl_phy->dx[i].bdlr2);
}
clrbits_le32(&mctl_phy->pgcr[0], BIT(26));
for (i = 0; i < 4; i++) {
val = readl(&mctl_phy->dx[i].bdlr3);
for (j = 0; j < 4; j++)
val += para->dx_read_delays[i][j] << (j * 8);
writel(val, &mctl_phy->dx[i].bdlr3);
val = readl(&mctl_phy->dx[i].bdlr4);
for (j = 0; j < 4; j++)
val += para->dx_read_delays[i][j + 4] << (j * 8);
writel(val, &mctl_phy->dx[i].bdlr4);
val = readl(&mctl_phy->dx[i].bdlr5);
for (j = 0; j < 4; j++)
val += para->dx_read_delays[i][j + 8] << (j * 8);
writel(val, &mctl_phy->dx[i].bdlr5);
val = readl(&mctl_phy->dx[i].bdlr6);
val += (para->dx_read_delays[i][12] << 8) |
(para->dx_read_delays[i][13] << 16);
writel(val, &mctl_phy->dx[i].bdlr6);
}
setbits_le32(&mctl_phy->pgcr[0], BIT(26));
udelay(1);
if (para->type != SUNXI_DRAM_TYPE_LPDDR3)
return;
for (i = 1; i < 14; i++) {
val = readl(&mctl_phy->acbdlr[i]);
val += 0x0a0a0a0a;
writel(val, &mctl_phy->acbdlr[i]);
}
}
static void mctl_channel_init(struct dram_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
int i;
u32 val;
setbits_le32(&mctl_ctl->dfiupd[0], BIT(31) | BIT(30));
setbits_le32(&mctl_ctl->zqctl[0], BIT(31) | BIT(30));
writel(0x2f05, &mctl_ctl->sched[0]);
setbits_le32(&mctl_ctl->rfshctl3, BIT(0));
setbits_le32(&mctl_ctl->dfimisc, BIT(0));
setbits_le32(&mctl_ctl->unk_0x00c, BIT(8));
clrsetbits_le32(&mctl_phy->pgcr[1], 0x180, 0xc0);
/* TODO: non-LPDDR3 types */
clrsetbits_le32(&mctl_phy->pgcr[2], GENMASK(17, 0), ns_to_t(7800));
clrbits_le32(&mctl_phy->pgcr[6], BIT(0));
clrsetbits_le32(&mctl_phy->dxccr, 0xee0, 0x220);
/* TODO: VT compensation */
clrsetbits_le32(&mctl_phy->dsgcr, BIT(0), 0x440060);
clrbits_le32(&mctl_phy->vtcr[1], BIT(1));
for (i = 0; i < 4; i++)
clrsetbits_le32(&mctl_phy->dx[i].gcr[0], 0xe00, 0x800);
for (i = 0; i < 4; i++)
clrsetbits_le32(&mctl_phy->dx[i].gcr[2], 0xffff, 0x5555);
for (i = 0; i < 4; i++)
clrsetbits_le32(&mctl_phy->dx[i].gcr[3], 0x3030, 0x1010);
udelay(100);
if (para->ranks == 2)
setbits_le32(&mctl_phy->dtcr[1], 0x30000);
else
clrsetbits_le32(&mctl_phy->dtcr[1], 0x30000, 0x10000);
if (sunxi_dram_is_lpddr(para->type))
clrbits_le32(&mctl_phy->dtcr[1], BIT(1));
if (para->ranks == 2) {
writel(0x00010001, &mctl_phy->rankidr);
writel(0x20000, &mctl_phy->odtcr);
} else {
writel(0x0, &mctl_phy->rankidr);
writel(0x10000, &mctl_phy->odtcr);
}
/* set bits [3:0] to 1? 0 not valid in ZynqMP d/s */
if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
clrsetbits_le32(&mctl_phy->dtcr[0], 0xF0000000, 0x10000040);
else
clrsetbits_le32(&mctl_phy->dtcr[0], 0xF0000000, 0x10000000);
if (para->clk <= 792) {
if (para->clk <= 672) {
if (para->clk <= 600)
val = 0x300;
else
val = 0x400;
} else {
val = 0x500;
}
} else {
val = 0x600;
}
/* FIXME: NOT REVIEWED YET */
clrsetbits_le32(&mctl_phy->zq[0].zqcr, 0x700, val);
clrsetbits_le32(&mctl_phy->zq[0].zqpr[0], 0xff,
CONFIG_DRAM_ZQ & 0xff);
clrbits_le32(&mctl_phy->zq[0].zqor[0], 0xfffff);
setbits_le32(&mctl_phy->zq[0].zqor[0], (CONFIG_DRAM_ZQ >> 8) & 0xff);
setbits_le32(&mctl_phy->zq[0].zqor[0], (CONFIG_DRAM_ZQ & 0xf00) - 0x100);
setbits_le32(&mctl_phy->zq[0].zqor[0], (CONFIG_DRAM_ZQ & 0xff00) << 4);
clrbits_le32(&mctl_phy->zq[1].zqpr[0], 0xfffff);
setbits_le32(&mctl_phy->zq[1].zqpr[0], (CONFIG_DRAM_ZQ >> 16) & 0xff);
setbits_le32(&mctl_phy->zq[1].zqpr[0], ((CONFIG_DRAM_ZQ >> 8) & 0xf00) - 0x100);
setbits_le32(&mctl_phy->zq[1].zqpr[0], (CONFIG_DRAM_ZQ & 0xff0000) >> 4);
if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
for (i = 1; i < 14; i++)
writel(0x06060606, &mctl_phy->acbdlr[i]);
}
val = PIR_ZCAL | PIR_DCAL | PIR_PHYRST | PIR_DRAMINIT | PIR_QSGATE |
PIR_RDDSKW | PIR_WRDSKW | PIR_RDEYE | PIR_WREYE;
if (para->type == SUNXI_DRAM_TYPE_DDR3)
val |= PIR_DRAMRST | PIR_WL;
mctl_phy_pir_init(val);
/* TODO: DDR4 types ? */
for (i = 0; i < 4; i++)
writel(0x00000909, &mctl_phy->dx[i].gcr[5]);
for (i = 0; i < 4; i++) {
if (IS_ENABLED(CONFIG_DRAM_ODT_EN))
val = 0x0;
else
val = 0xaaaa;
clrsetbits_le32(&mctl_phy->dx[i].gcr[2], 0xffff, val);
if (IS_ENABLED(CONFIG_DRAM_ODT_EN))
val = 0x0;
else
val = 0x2020;
clrsetbits_le32(&mctl_phy->dx[i].gcr[3], 0x3030, val);
}
mctl_bit_delay_set(para);
udelay(1);
setbits_le32(&mctl_phy->pgcr[6], BIT(0));
clrbits_le32(&mctl_phy->pgcr[6], 0xfff8);
for (i = 0; i < 4; i++)
clrbits_le32(&mctl_phy->dx[i].gcr[3], ~0x3ffff);
udelay(10);
if (readl(&mctl_phy->pgsr[0]) & 0x400000)
{
/* Check for single rank and optionally half DQ. */
if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 2 &&
(readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 2) {
para->ranks = 1;
if ((readl(&mctl_phy->dx[2].rsr[0]) & 0x3) != 2 ||
(readl(&mctl_phy->dx[3].rsr[0]) & 0x3) != 2)
para->bus_full_width = 0;
/* Restart DRAM initialization from scratch. */
mctl_core_init(para);
return;
}
/*
* Check for dual rank and half DQ. NOTE: This combination
* is highly unlikely and was not tested. Condition is the
* same as in libdram, though.
*/
if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 0 &&
(readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 0) {
para->bus_full_width = 0;
/* Restart DRAM initialization from scratch. */
mctl_core_init(para);
return;
}
panic("This DRAM setup is currently not supported.\n");
}
if (readl(&mctl_phy->pgsr[0]) & 0xff00000) {
/* Oops! There's something wrong! */
debug("PLL = %x\n", readl(0x3001010));
debug("DRAM PHY PGSR0 = %x\n", readl(&mctl_phy->pgsr[0]));
for (i = 0; i < 4; i++)
debug("DRAM PHY DX%dRSR0 = %x\n", i, readl(&mctl_phy->dx[i].rsr[0]));
panic("Error while initializing DRAM PHY!\n");
}
if (sunxi_dram_is_lpddr(para->type))
clrsetbits_le32(&mctl_phy->dsgcr, 0xc0, 0x40);
clrbits_le32(&mctl_phy->pgcr[1], 0x40);
clrbits_le32(&mctl_ctl->dfimisc, BIT(0));
writel(1, &mctl_ctl->swctl);
mctl_await_completion(&mctl_ctl->swstat, 1, 1);
clrbits_le32(&mctl_ctl->rfshctl3, BIT(0));
setbits_le32(&mctl_com->unk_0x014, BIT(31));
writel(0xffffffff, &mctl_com->maer0);
writel(0x7ff, &mctl_com->maer1);
writel(0xffff, &mctl_com->maer2);
}
static void mctl_auto_detect_dram_size(struct dram_para *para)
{
/* TODO: non-(LP)DDR3 */
/* Detect rank number and half DQ by the code in mctl_channel_init. */
mctl_core_init(para);
/* detect row address bits */
para->cols = 8;
para->rows = 18;
mctl_core_init(para);
for (para->rows = 13; para->rows < 18; para->rows++) {
/* 8 banks, 8 bit per byte and 16/32 bit width */
if (mctl_mem_matches((1 << (para->rows + para->cols +
4 + para->bus_full_width))))
break;
}
/* detect column address bits */
para->cols = 11;
mctl_core_init(para);
for (para->cols = 8; para->cols < 11; para->cols++) {
/* 8 bits per byte and 16/32 bit width */
if (mctl_mem_matches(1 << (para->cols + 1 +
para->bus_full_width)))
break;
}
}
unsigned long mctl_calc_size(struct dram_para *para)
{
u8 width = para->bus_full_width ? 4 : 2;
/* TODO: non-(LP)DDR3 */
/* 8 banks */
return (1ULL << (para->cols + para->rows + 3)) * width * para->ranks;
}
#define SUN50I_H6_LPDDR3_DX_WRITE_DELAYS \
{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}
#define SUN50I_H6_LPDDR3_DX_READ_DELAYS \
{{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \
{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \
{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \
{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }}
#define SUN50I_H6_DDR3_DX_WRITE_DELAYS \
{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}
#define SUN50I_H6_DDR3_DX_READ_DELAYS \
{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}
unsigned long sunxi_dram_init(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct dram_para para = {
.clk = CONFIG_DRAM_CLK,
.ranks = 2,
.cols = 11,
.rows = 14,
.bus_full_width = 1,
#ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3
.type = SUNXI_DRAM_TYPE_LPDDR3,
.dx_read_delays = SUN50I_H6_LPDDR3_DX_READ_DELAYS,
.dx_write_delays = SUN50I_H6_LPDDR3_DX_WRITE_DELAYS,
#elif defined(CONFIG_SUNXI_DRAM_H6_DDR3_1333)
.type = SUNXI_DRAM_TYPE_DDR3,
.dx_read_delays = SUN50I_H6_DDR3_DX_READ_DELAYS,
.dx_write_delays = SUN50I_H6_DDR3_DX_WRITE_DELAYS,
#endif
};
unsigned long size;
/* RES_CAL_CTRL_REG in BSP U-boot*/
setbits_le32(0x7010310, BIT(8));
clrbits_le32(0x7010318, 0x3f);
mctl_auto_detect_dram_size(&para);
mctl_core_init(&para);
size = mctl_calc_size(&para);
clrsetbits_le32(&mctl_com->cr, 0xf0, (size >> (10 + 10 + 4)) & 0xf0);
mctl_set_master_priority();
return size;
};
@@ -0,0 +1,410 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Sun6i platform dram controller init.
*
* (C) Copyright 2007-2012
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Berg Xing <bergxing@allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/prcm.h>
#define DRAM_CLK (CONFIG_DRAM_CLK * 1000000)
struct dram_sun6i_para {
u8 bus_width;
u8 chan;
u8 rank;
u8 rows;
u16 page_size;
};
static void mctl_sys_init(void)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
const int dram_clk_div = 2;
clock_set_pll5(DRAM_CLK * dram_clk_div, false);
clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK,
CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST |
CCM_DRAMCLK_CFG_UPD);
mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
writel(MDFS_CLK_DEFAULT, &ccm->mdfs_clk_cfg);
/* deassert mctl reset */
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
/* enable mctl clock */
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
}
static void mctl_dll_init(int ch_index, struct dram_sun6i_para *para)
{
struct sunxi_mctl_phy_reg *mctl_phy;
if (ch_index == 0)
mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
else
mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE;
/* disable + reset dlls */
writel(MCTL_DLLCR_DISABLE, &mctl_phy->acdllcr);
writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx0dllcr);
writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx1dllcr);
if (para->bus_width == 32) {
writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx2dllcr);
writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx3dllcr);
}
udelay(2);
/* enable + reset dlls */
writel(0, &mctl_phy->acdllcr);
writel(0, &mctl_phy->dx0dllcr);
writel(0, &mctl_phy->dx1dllcr);
if (para->bus_width == 32) {
writel(0, &mctl_phy->dx2dllcr);
writel(0, &mctl_phy->dx3dllcr);
}
udelay(22);
/* enable and release reset of dlls */
writel(MCTL_DLLCR_NRESET, &mctl_phy->acdllcr);
writel(MCTL_DLLCR_NRESET, &mctl_phy->dx0dllcr);
writel(MCTL_DLLCR_NRESET, &mctl_phy->dx1dllcr);
if (para->bus_width == 32) {
writel(MCTL_DLLCR_NRESET, &mctl_phy->dx2dllcr);
writel(MCTL_DLLCR_NRESET, &mctl_phy->dx3dllcr);
}
udelay(22);
}
static bool mctl_rank_detect(u32 *gsr0, int rank)
{
const u32 done = MCTL_DX_GSR0_RANK0_TRAIN_DONE << rank;
const u32 err = MCTL_DX_GSR0_RANK0_TRAIN_ERR << rank;
mctl_await_completion(gsr0, done, done);
mctl_await_completion(gsr0 + 0x10, done, done);
return !(readl(gsr0) & err) && !(readl(gsr0 + 0x10) & err);
}
static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg *mctl_ctl;
struct sunxi_mctl_phy_reg *mctl_phy;
if (ch_index == 0) {
mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
} else {
mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL1_BASE;
mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE;
}
writel(MCTL_MCMD_NOP, &mctl_ctl->mcmd);
mctl_await_completion(&mctl_ctl->mcmd, MCTL_MCMD_BUSY, 0);
/* PHY initialization */
writel(MCTL_PGCR, &mctl_phy->pgcr);
writel(MCTL_MR0, &mctl_phy->mr0);
writel(MCTL_MR1, &mctl_phy->mr1);
writel(MCTL_MR2, &mctl_phy->mr2);
writel(MCTL_MR3, &mctl_phy->mr3);
writel((MCTL_TITMSRST << 18) | (MCTL_TDLLLOCK << 6) | MCTL_TDLLSRST,
&mctl_phy->ptr0);
writel((MCTL_TDINIT1 << 19) | MCTL_TDINIT0, &mctl_phy->ptr1);
writel((MCTL_TDINIT3 << 17) | MCTL_TDINIT2, &mctl_phy->ptr2);
writel((MCTL_TCCD << 31) | (MCTL_TRC << 25) | (MCTL_TRRD << 21) |
(MCTL_TRAS << 16) | (MCTL_TRCD << 12) | (MCTL_TRP << 8) |
(MCTL_TWTR << 5) | (MCTL_TRTP << 2) | (MCTL_TMRD << 0),
&mctl_phy->dtpr0);
writel((MCTL_TDQSCKMAX << 27) | (MCTL_TDQSCK << 24) |
(MCTL_TRFC << 16) | (MCTL_TRTODT << 11) |
((MCTL_TMOD - 12) << 9) | (MCTL_TFAW << 3) | (0 << 2) |
(MCTL_TAOND << 0), &mctl_phy->dtpr1);
writel((MCTL_TDLLK << 19) | (MCTL_TCKE << 15) | (MCTL_TXPDLL << 10) |
(MCTL_TEXSR << 0), &mctl_phy->dtpr2);
writel(1, &mctl_ctl->dfitphyupdtype0);
writel(MCTL_DCR_DDR3, &mctl_phy->dcr);
writel(MCTL_DSGCR, &mctl_phy->dsgcr);
writel(MCTL_DXCCR, &mctl_phy->dxccr);
writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx0gcr);
writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx1gcr);
writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx2gcr);
writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx3gcr);
mctl_await_completion(&mctl_phy->pgsr, 0x03, 0x03);
writel(CONFIG_DRAM_ZQ, &mctl_phy->zq0cr1);
setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS);
writel(MCTL_PIR_STEP1, &mctl_phy->pir);
udelay(10);
mctl_await_completion(&mctl_phy->pgsr, 0x1f, 0x1f);
/* rank detect */
if (!mctl_rank_detect(&mctl_phy->dx0gsr0, 1)) {
para->rank = 1;
clrbits_le32(&mctl_phy->pgcr, MCTL_PGCR_RANK);
}
/*
* channel detect, check channel 1 dx0 and dx1 have rank 0, if not
* assume nothing is connected to channel 1.
*/
if (ch_index == 1 && !mctl_rank_detect(&mctl_phy->dx0gsr0, 0)) {
para->chan = 1;
clrbits_le32(&mctl_com->ccr, MCTL_CCR_CH1_CLK_EN);
return;
}
/* bus width detect, if dx2 and dx3 don't have rank 0, assume 16 bit */
if (!mctl_rank_detect(&mctl_phy->dx2gsr0, 0)) {
para->bus_width = 16;
para->page_size = 2048;
setbits_le32(&mctl_phy->dx2dllcr, MCTL_DLLCR_DISABLE);
setbits_le32(&mctl_phy->dx3dllcr, MCTL_DLLCR_DISABLE);
clrbits_le32(&mctl_phy->dx2gcr, MCTL_DX_GCR_EN);
clrbits_le32(&mctl_phy->dx3gcr, MCTL_DX_GCR_EN);
}
setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS);
writel(MCTL_PIR_STEP2, &mctl_phy->pir);
udelay(10);
mctl_await_completion(&mctl_phy->pgsr, 0x11, 0x11);
if (readl(&mctl_phy->pgsr) & MCTL_PGSR_TRAIN_ERR_MASK)
panic("Training error initialising DRAM\n");
/* Move to configure state */
writel(MCTL_SCTL_CONFIG, &mctl_ctl->sctl);
mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x01);
/* Set number of clks per micro-second */
writel(DRAM_CLK / 1000000, &mctl_ctl->togcnt1u);
/* Set number of clks per 100 nano-seconds */
writel(DRAM_CLK / 10000000, &mctl_ctl->togcnt100n);
/* Set memory timing registers */
writel(MCTL_TREFI, &mctl_ctl->trefi);
writel(MCTL_TMRD, &mctl_ctl->tmrd);
writel(MCTL_TRFC, &mctl_ctl->trfc);
writel((MCTL_TPREA << 16) | MCTL_TRP, &mctl_ctl->trp);
writel(MCTL_TRTW, &mctl_ctl->trtw);
writel(MCTL_TAL, &mctl_ctl->tal);
writel(MCTL_TCL, &mctl_ctl->tcl);
writel(MCTL_TCWL, &mctl_ctl->tcwl);
writel(MCTL_TRAS, &mctl_ctl->tras);
writel(MCTL_TRC, &mctl_ctl->trc);
writel(MCTL_TRCD, &mctl_ctl->trcd);
writel(MCTL_TRRD, &mctl_ctl->trrd);
writel(MCTL_TRTP, &mctl_ctl->trtp);
writel(MCTL_TWR, &mctl_ctl->twr);
writel(MCTL_TWTR, &mctl_ctl->twtr);
writel(MCTL_TEXSR, &mctl_ctl->texsr);
writel(MCTL_TXP, &mctl_ctl->txp);
writel(MCTL_TXPDLL, &mctl_ctl->txpdll);
writel(MCTL_TZQCS, &mctl_ctl->tzqcs);
writel(MCTL_TZQCSI, &mctl_ctl->tzqcsi);
writel(MCTL_TDQS, &mctl_ctl->tdqs);
writel(MCTL_TCKSRE, &mctl_ctl->tcksre);
writel(MCTL_TCKSRX, &mctl_ctl->tcksrx);
writel(MCTL_TCKE, &mctl_ctl->tcke);
writel(MCTL_TMOD, &mctl_ctl->tmod);
writel(MCTL_TRSTL, &mctl_ctl->trstl);
writel(MCTL_TZQCL, &mctl_ctl->tzqcl);
writel(MCTL_TMRR, &mctl_ctl->tmrr);
writel(MCTL_TCKESR, &mctl_ctl->tckesr);
writel(MCTL_TDPD, &mctl_ctl->tdpd);
/* Unknown magic performed by boot0 */
setbits_le32(&mctl_ctl->dfiodtcfg, 1 << 3);
clrbits_le32(&mctl_ctl->dfiodtcfg1, 0x1f);
/* Select 16/32-bits mode for MCTL */
if (para->bus_width == 16)
setbits_le32(&mctl_ctl->ppcfg, 1);
/* Set DFI timing registers */
writel(MCTL_TCWL, &mctl_ctl->dfitphywrl);
writel(MCTL_TCL - 1, &mctl_ctl->dfitrdden);
writel(MCTL_DFITPHYRDL, &mctl_ctl->dfitphyrdl);
writel(MCTL_DFISTCFG0, &mctl_ctl->dfistcfg0);
writel(MCTL_MCFG_DDR3, &mctl_ctl->mcfg);
/* DFI update configuration register */
writel(MCTL_DFIUPDCFG_UPD, &mctl_ctl->dfiupdcfg);
/* Move to access state */
writel(MCTL_SCTL_ACCESS, &mctl_ctl->sctl);
mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x03);
}
static void mctl_com_init(struct dram_sun6i_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_phy_reg * const mctl_phy1 =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE;
struct sunxi_prcm_reg * const prcm =
(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
writel(MCTL_CR_UNKNOWN | MCTL_CR_CHANNEL(para->chan) | MCTL_CR_DDR3 |
((para->bus_width == 32) ? MCTL_CR_BUSW32 : MCTL_CR_BUSW16) |
MCTL_CR_PAGE_SIZE(para->page_size) | MCTL_CR_ROW(para->rows) |
MCTL_CR_BANK(1) | MCTL_CR_RANK(para->rank), &mctl_com->cr);
/* Unknown magic performed by boot0 */
setbits_le32(&mctl_com->dbgcr, (1 << 6));
if (para->chan == 1) {
/* Shutdown channel 1 */
setbits_le32(&mctl_phy1->aciocr, MCTL_ACIOCR_DISABLE);
setbits_le32(&mctl_phy1->dxccr, MCTL_DXCCR_DISABLE);
clrbits_le32(&mctl_phy1->dsgcr, MCTL_DSGCR_ENABLE);
/*
* CH0 ?? this is what boot0 does. Leave as is until we can
* confirm this.
*/
setbits_le32(&prcm->vdd_sys_pwroff,
PRCM_VDD_SYS_DRAM_CH0_PAD_HOLD_PWROFF);
}
}
static void mctl_port_cfg(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* enable DRAM AXI clock for CPU access */
setbits_le32(&ccm->axi_gate, 1 << AXI_GATE_OFFSET_DRAM);
/* Bunch of magic writes performed by boot0 */
writel(0x00400302, &mctl_com->rmcr[0]);
writel(0x01000307, &mctl_com->rmcr[1]);
writel(0x00400302, &mctl_com->rmcr[2]);
writel(0x01000307, &mctl_com->rmcr[3]);
writel(0x01000307, &mctl_com->rmcr[4]);
writel(0x01000303, &mctl_com->rmcr[6]);
writel(0x01000303, &mctl_com->mmcr[0]);
writel(0x00400310, &mctl_com->mmcr[1]);
writel(0x01000307, &mctl_com->mmcr[2]);
writel(0x01000303, &mctl_com->mmcr[3]);
writel(0x01800303, &mctl_com->mmcr[4]);
writel(0x01800303, &mctl_com->mmcr[5]);
writel(0x01800303, &mctl_com->mmcr[6]);
writel(0x01800303, &mctl_com->mmcr[7]);
writel(0x01000303, &mctl_com->mmcr[8]);
writel(0x00000002, &mctl_com->mmcr[15]);
writel(0x00000310, &mctl_com->mbagcr[0]);
writel(0x00400310, &mctl_com->mbagcr[1]);
writel(0x00400310, &mctl_com->mbagcr[2]);
writel(0x00000307, &mctl_com->mbagcr[3]);
writel(0x00000317, &mctl_com->mbagcr[4]);
writel(0x00000307, &mctl_com->mbagcr[5]);
}
unsigned long sunxi_dram_init(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
u32 offset;
int bank, bus, columns;
/* Set initial parameters, these get modified by the autodetect code */
struct dram_sun6i_para para = {
.bus_width = 32,
.chan = 2,
.rank = 2,
.page_size = 4096,
.rows = 16,
};
/* A31s only has one channel */
if (sunxi_get_ss_bonding_id() == SUNXI_SS_BOND_ID_A31S)
para.chan = 1;
mctl_sys_init();
mctl_dll_init(0, &para);
setbits_le32(&mctl_com->ccr, MCTL_CCR_CH0_CLK_EN);
if (para.chan == 2) {
mctl_dll_init(1, &para);
setbits_le32(&mctl_com->ccr, MCTL_CCR_CH1_CLK_EN);
}
setbits_le32(&mctl_com->ccr, MCTL_CCR_MASTER_CLK_EN);
mctl_channel_init(0, &para);
if (para.chan == 2)
mctl_channel_init(1, &para);
mctl_com_init(&para);
mctl_port_cfg();
/*
* Change to 1 ch / sequence / 8192 byte pages / 16 rows /
* 8 bit banks / 1 rank mode.
*/
clrsetbits_le32(&mctl_com->cr,
MCTL_CR_CHANNEL_MASK | MCTL_CR_PAGE_SIZE_MASK |
MCTL_CR_ROW_MASK | MCTL_CR_BANK_MASK | MCTL_CR_RANK_MASK,
MCTL_CR_CHANNEL(1) | MCTL_CR_SEQUENCE |
MCTL_CR_PAGE_SIZE(8192) | MCTL_CR_ROW(16) |
MCTL_CR_BANK(1) | MCTL_CR_RANK(1));
/* Detect and set page size */
for (columns = 7; columns < 20; columns++) {
if (mctl_mem_matches(1 << columns))
break;
}
bus = (para.bus_width == 32) ? 2 : 1;
columns -= bus;
para.page_size = (1 << columns) * (bus << 1);
clrsetbits_le32(&mctl_com->cr, MCTL_CR_PAGE_SIZE_MASK,
MCTL_CR_PAGE_SIZE(para.page_size));
/* Detect and set rows */
for (para.rows = 11; para.rows < 16; para.rows++) {
offset = 1 << (para.rows + columns + bus);
if (mctl_mem_matches(offset))
break;
}
clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK,
MCTL_CR_ROW(para.rows));
/* Detect bank size */
offset = 1 << (para.rows + columns + bus + 2);
bank = mctl_mem_matches(offset) ? 0 : 1;
/* Restore interleave, chan and rank values, set bank size */
clrsetbits_le32(&mctl_com->cr,
MCTL_CR_CHANNEL_MASK | MCTL_CR_SEQUENCE |
MCTL_CR_BANK_MASK | MCTL_CR_RANK_MASK,
MCTL_CR_CHANNEL(para.chan) | MCTL_CR_BANK(bank) |
MCTL_CR_RANK(para.rank));
return 1 << (para.rank + para.rows + bank + columns + para.chan + bus);
}
@@ -0,0 +1,342 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Sun8i platform dram controller init.
*
* (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
*/
/*
* Note this code uses a lot of magic hex values, that is because this code
* simply replays the init sequence as done by the Allwinner boot0 code, so
* we do not know what these values mean. There are no symbolic constants for
* these magic values, since we do not know how to name them and making up
* names for them is not useful.
*
* The register-layout of the sunxi_mctl_phy_reg-s looks a lot like the one
* found in the TI Keystone2 documentation:
* http://www.ti.com/lit/ug/spruhn7a/spruhn7a.pdf
* "Table4-2 DDR3 PHY Registers"
* This may be used as a (possible) reference for future work / cleanups.
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/prcm.h>
static const struct dram_para dram_para = {
.clock = CONFIG_DRAM_CLK,
.type = 3,
.zq = CONFIG_DRAM_ZQ,
.odt_en = IS_ENABLED(CONFIG_DRAM_ODT_EN),
.odt_correction = CONFIG_DRAM_ODT_CORRECTION,
.para1 = 0, /* not used (only used when tpr13 bit 31 is set */
.para2 = 0, /* not used (only used when tpr13 bit 31 is set */
.mr0 = 6736,
.mr1 = 4,
.mr2 = 16,
.mr3 = 0,
/* tpr0 - 10 contain timing constants or-ed together in u32 vals */
.tpr0 = 0x2ab83def,
.tpr1 = 0x18082356,
.tpr2 = 0x00034156,
.tpr3 = 0x448c5533,
.tpr4 = 0x08010d00,
.tpr5 = 0x0340b20f,
.tpr6 = 0x20d118cc,
.tpr7 = 0x14062485,
.tpr8 = 0x220d1d52,
.tpr9 = 0x1e078c22,
.tpr10 = 0x3c,
.tpr11 = 0, /* not used */
.tpr12 = 0, /* not used */
.tpr13 = 0x30000,
};
static void mctl_sys_init(void)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* enable pll5, note the divide by 2 is deliberate! */
clock_set_pll5(dram_para.clock * 1000000 / 2,
dram_para.tpr13 & 0x40000);
/* deassert ahb mctl reset */
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
/* enable ahb mctl clock */
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
}
static void mctl_apply_odt_correction(u32 *reg, int correction)
{
int val;
val = (readl(reg) >> 8) & 0xff;
val += correction;
/* clamp */
if (val < 0)
val = 0;
else if (val > 255)
val = 255;
clrsetbits_le32(reg, 0xff00, val << 8);
}
static void mctl_init(u32 *bus_width)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
if (dram_para.tpr13 & 0x20)
writel(0x40b, &mctl_phy->dcr);
else
writel(0x1000040b, &mctl_phy->dcr);
if (dram_para.clock >= 480)
writel(0x5c000, &mctl_phy->dllgcr);
else
writel(0xdc000, &mctl_phy->dllgcr);
writel(0x0a003e3f, &mctl_phy->pgcr0);
writel(0x03008421, &mctl_phy->pgcr1);
writel(dram_para.mr0, &mctl_phy->mr0);
writel(dram_para.mr1, &mctl_phy->mr1);
writel(dram_para.mr2, &mctl_phy->mr2);
writel(dram_para.mr3, &mctl_phy->mr3);
if (!(dram_para.tpr13 & 0x10000)) {
clrsetbits_le32(&mctl_phy->dx0gcr, 0x3800, 0x2000);
clrsetbits_le32(&mctl_phy->dx1gcr, 0x3800, 0x2000);
}
/*
* All the masking and shifting below converts what I assume are DDR
* timing constants from Allwinner dram_para tpr format to the actual
* timing registers format.
*/
writel((dram_para.tpr0 & 0x000fffff), &mctl_phy->ptr2);
writel((dram_para.tpr1 & 0x1fffffff), &mctl_phy->ptr3);
writel((dram_para.tpr0 & 0x3ff00000) >> 2 |
(dram_para.tpr2 & 0x0003ffff), &mctl_phy->ptr4);
writel(dram_para.tpr3, &mctl_phy->dtpr0);
writel(dram_para.tpr4, &mctl_phy->dtpr2);
writel(0x01000081, &mctl_phy->dtcr);
if (dram_para.clock <= 240 || !dram_para.odt_en) {
clrbits_le32(&mctl_phy->dx0gcr, 0x600);
clrbits_le32(&mctl_phy->dx1gcr, 0x600);
}
if (dram_para.clock <= 240) {
writel(0, &mctl_phy->odtcr);
writel(0, &mctl_ctl->odtmap);
}
writel(((dram_para.tpr5 & 0x0f00) << 12) |
((dram_para.tpr5 & 0x00f8) << 9) |
((dram_para.tpr5 & 0x0007) << 8),
&mctl_ctl->rfshctl0);
writel(((dram_para.tpr5 & 0x0003f000) << 12) |
((dram_para.tpr5 & 0x00fc0000) >> 2) |
((dram_para.tpr5 & 0x3f000000) >> 16) |
((dram_para.tpr6 & 0x0000003f) >> 0),
&mctl_ctl->dramtmg0);
writel(((dram_para.tpr6 & 0x000007c0) << 10) |
((dram_para.tpr6 & 0x0000f800) >> 3) |
((dram_para.tpr6 & 0x003f0000) >> 16),
&mctl_ctl->dramtmg1);
writel(((dram_para.tpr6 & 0x0fc00000) << 2) |
((dram_para.tpr7 & 0x0000001f) << 16) |
((dram_para.tpr7 & 0x000003e0) << 3) |
((dram_para.tpr7 & 0x0000fc00) >> 10),
&mctl_ctl->dramtmg2);
writel(((dram_para.tpr7 & 0x03ff0000) >> 16) |
((dram_para.tpr6 & 0xf0000000) >> 16),
&mctl_ctl->dramtmg3);
writel(((dram_para.tpr7 & 0x3c000000) >> 2 ) |
((dram_para.tpr8 & 0x00000007) << 16) |
((dram_para.tpr8 & 0x00000038) << 5) |
((dram_para.tpr8 & 0x000003c0) >> 6),
&mctl_ctl->dramtmg4);
writel(((dram_para.tpr8 & 0x00003c00) << 14) |
((dram_para.tpr8 & 0x0003c000) << 2) |
((dram_para.tpr8 & 0x00fc0000) >> 10) |
((dram_para.tpr8 & 0x0f000000) >> 24),
&mctl_ctl->dramtmg5);
writel(0x00000008, &mctl_ctl->dramtmg8);
writel(((dram_para.tpr8 & 0xf0000000) >> 4) |
((dram_para.tpr9 & 0x00007c00) << 6) |
((dram_para.tpr9 & 0x000003e0) << 3) |
((dram_para.tpr9 & 0x0000001f) >> 0),
&mctl_ctl->pitmg0);
setbits_le32(&mctl_ctl->pitmg1, 0x80000);
writel(((dram_para.tpr9 & 0x003f8000) << 9) | 0x2001,
&mctl_ctl->sched);
writel((dram_para.mr0 << 16) | dram_para.mr1, &mctl_ctl->init3);
writel((dram_para.mr2 << 16) | dram_para.mr3, &mctl_ctl->init4);
writel(0x00000000, &mctl_ctl->pimisc);
writel(0x80000000, &mctl_ctl->upd0);
writel(((dram_para.tpr9 & 0xffc00000) >> 22) |
((dram_para.tpr10 & 0x00000fff) << 16),
&mctl_ctl->rfshtmg);
if (dram_para.tpr13 & 0x20)
writel(0x01040001, &mctl_ctl->mstr);
else
writel(0x01040401, &mctl_ctl->mstr);
if (!(dram_para.tpr13 & 0x20000)) {
writel(0x00000002, &mctl_ctl->pwrctl);
writel(0x00008001, &mctl_ctl->pwrtmg);
}
writel(0x00000001, &mctl_ctl->rfshctl3);
writel(0x00000001, &mctl_ctl->pimisc);
/* deassert dram_clk_cfg reset */
setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
setbits_le32(&mctl_com->ccr, 0x80000);
/* zq stuff */
writel((dram_para.zq >> 8) & 0xff, &mctl_phy->zqcr1);
writel(0x00000003, &mctl_phy->pir);
udelay(10);
mctl_await_completion(&mctl_phy->pgsr0, 0x09, 0x09);
writel(readl(&mctl_phy->zqsr0) | 0x10000000, &mctl_phy->zqcr2);
writel(dram_para.zq & 0xff, &mctl_phy->zqcr1);
/* A23-v1.0 SDK uses 0xfdf3, A23-v2.0 SDK uses 0x5f3 */
writel(0x000005f3, &mctl_phy->pir);
udelay(10);
mctl_await_completion(&mctl_phy->pgsr0, 0x03, 0x03);
if (readl(&mctl_phy->dx1gsr0) & 0x1000000) {
*bus_width = 8;
writel(0, &mctl_phy->dx1gcr);
writel(dram_para.zq & 0xff, &mctl_phy->zqcr1);
writel(0x5f3, &mctl_phy->pir);
udelay(10000);
setbits_le32(&mctl_ctl->mstr, 0x1000);
} else
*bus_width = 16;
if (dram_para.odt_correction) {
mctl_apply_odt_correction(&mctl_phy->dx0lcdlr1,
dram_para.odt_correction);
mctl_apply_odt_correction(&mctl_phy->dx1lcdlr1,
dram_para.odt_correction);
}
mctl_await_completion(&mctl_ctl->statr, 0x01, 0x01);
writel(0x08003e3f, &mctl_phy->pgcr0);
writel(0x00000000, &mctl_ctl->rfshctl3);
}
unsigned long sunxi_dram_init(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
const u32 columns = 13;
u32 bus, bus_width, offset, page_size, rows;
mctl_sys_init();
mctl_init(&bus_width);
if (bus_width == 16) {
page_size = 8;
bus = 1;
} else {
page_size = 7;
bus = 0;
}
if (!(dram_para.tpr13 & 0x80000000)) {
/* Detect and set rows */
writel(0x000310f4 | MCTL_CR_PAGE_SIZE(page_size),
&mctl_com->cr);
setbits_le32(&mctl_com->swonr, 0x0003ffff);
for (rows = 11; rows < 16; rows++) {
offset = 1 << (rows + columns + bus);
if (mctl_mem_matches(offset))
break;
}
clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK,
MCTL_CR_ROW(rows));
} else {
rows = (dram_para.para1 >> 16) & 0xff;
writel(((dram_para.para2 & 0x000000f0) << 11) |
((rows - 1) << 4) |
((dram_para.para1 & 0x0f000000) >> 22) |
0x31000 | MCTL_CR_PAGE_SIZE(page_size),
&mctl_com->cr);
setbits_le32(&mctl_com->swonr, 0x0003ffff);
}
/* Setup DRAM master priority? If this is left out things still work */
writel(0x00000008, &mctl_com->mcr0_0);
writel(0x0001000d, &mctl_com->mcr1_0);
writel(0x00000004, &mctl_com->mcr0_1);
writel(0x00000080, &mctl_com->mcr1_1);
writel(0x00000004, &mctl_com->mcr0_2);
writel(0x00000019, &mctl_com->mcr1_2);
writel(0x00000004, &mctl_com->mcr0_3);
writel(0x00000080, &mctl_com->mcr1_3);
writel(0x00000004, &mctl_com->mcr0_4);
writel(0x01010040, &mctl_com->mcr1_4);
writel(0x00000004, &mctl_com->mcr0_5);
writel(0x0001002f, &mctl_com->mcr1_5);
writel(0x00000004, &mctl_com->mcr0_6);
writel(0x00010020, &mctl_com->mcr1_6);
writel(0x00000004, &mctl_com->mcr0_7);
writel(0x00010020, &mctl_com->mcr1_7);
writel(0x00000008, &mctl_com->mcr0_8);
writel(0x00000001, &mctl_com->mcr1_8);
writel(0x00000008, &mctl_com->mcr0_9);
writel(0x00000005, &mctl_com->mcr1_9);
writel(0x00000008, &mctl_com->mcr0_10);
writel(0x00000003, &mctl_com->mcr1_10);
writel(0x00000008, &mctl_com->mcr0_11);
writel(0x00000005, &mctl_com->mcr1_11);
writel(0x00000008, &mctl_com->mcr0_12);
writel(0x00000003, &mctl_com->mcr1_12);
writel(0x00000008, &mctl_com->mcr0_13);
writel(0x00000004, &mctl_com->mcr1_13);
writel(0x00000008, &mctl_com->mcr0_14);
writel(0x00000002, &mctl_com->mcr1_14);
writel(0x00000008, &mctl_com->mcr0_15);
writel(0x00000003, &mctl_com->mcr1_15);
writel(0x00010138, &mctl_com->bwcr);
return 1 << (rows + columns + bus);
}
@@ -0,0 +1,361 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Sun8i a33 platform dram controller init.
*
* (C) Copyright 2007-2015 Allwinner Technology Co.
* Jerry Wang <wangflord@allwinnertech.com>
* (C) Copyright 2015 Vishnu Patekar <vishnupatekar0510@gmail.com>
* (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/prcm.h>
/* PLL runs at 2x dram-clk, controller runs at PLL / 4 (dram-clk / 2) */
#define DRAM_CLK_MUL 2
#define DRAM_CLK_DIV 4
#define DRAM_SIGMA_DELTA_ENABLE 1
struct dram_para {
u8 cs1;
u8 seq;
u8 bank;
u8 rank;
u8 rows;
u8 bus_width;
u16 page_size;
};
static void mctl_set_cr(struct dram_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
writel(MCTL_CR_CS1_CONTROL(para->cs1) | MCTL_CR_UNKNOWN |
MCTL_CR_CHANNEL(1) | MCTL_CR_DDR3 |
(para->seq ? MCTL_CR_SEQUENCE : 0) |
((para->bus_width == 16) ? MCTL_CR_BUSW16 : MCTL_CR_BUSW8) |
MCTL_CR_PAGE_SIZE(para->page_size) | MCTL_CR_ROW(para->rows) |
MCTL_CR_BANK(para->bank) | MCTL_CR_RANK(para->rank),
&mctl_com->cr);
}
static void auto_detect_dram_size(struct dram_para *para)
{
u8 orig_rank = para->rank;
int rows, columns;
/* Row detect */
para->page_size = 512;
para->seq = 1;
para->rows = 16;
para->rank = 1;
mctl_set_cr(para);
for (rows = 11 ; rows < 16 ; rows++) {
if (mctl_mem_matches(1 << (rows + 9))) /* row-column */
break;
}
/* Column (page size) detect */
para->rows = 11;
para->page_size = 8192;
mctl_set_cr(para);
for (columns = 9 ; columns < 13 ; columns++) {
if (mctl_mem_matches(1 << columns))
break;
}
para->seq = 0;
para->rank = orig_rank;
para->rows = rows;
para->page_size = 1 << columns;
mctl_set_cr(para);
}
static inline int ns_to_t(int nanoseconds)
{
const unsigned int ctrl_freq =
CONFIG_DRAM_CLK * DRAM_CLK_MUL / DRAM_CLK_DIV;
return (ctrl_freq * nanoseconds + 999) / 1000;
}
static void auto_set_timing_para(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
u32 reg_val;
u8 tccd = 2;
u8 tfaw = ns_to_t(50);
u8 trrd = max(ns_to_t(10), 4);
u8 trcd = ns_to_t(15);
u8 trc = ns_to_t(53);
u8 txp = max(ns_to_t(8), 3);
u8 twtr = max(ns_to_t(8), 4);
u8 trtp = max(ns_to_t(8), 4);
u8 twr = max(ns_to_t(15), 3);
u8 trp = ns_to_t(15);
u8 tras = ns_to_t(38);
u16 trefi = ns_to_t(7800) / 32;
u16 trfc = ns_to_t(350);
/* Fixed timing parameters */
u8 tmrw = 0;
u8 tmrd = 4;
u8 tmod = 12;
u8 tcke = 3;
u8 tcksrx = 5;
u8 tcksre = 5;
u8 tckesr = 4;
u8 trasmax = 24;
u8 tcl = 6; /* CL 12 */
u8 tcwl = 4; /* CWL 8 */
u8 t_rdata_en = 4;
u8 wr_latency = 2;
u32 tdinit0 = (500 * CONFIG_DRAM_CLK) + 1; /* 500us */
u32 tdinit1 = (360 * CONFIG_DRAM_CLK) / 1000 + 1; /* 360ns */
u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
u8 twtp = tcwl + 2 + twr; /* WL + BL / 2 + tWR */
u8 twr2rd = tcwl + 2 + twtr; /* WL + BL / 2 + tWTR */
u8 trd2wr = tcl + 2 + 1 - tcwl; /* RL + BL / 2 + 2 - WL */
/* Set work mode register */
mctl_set_cr(para);
/* Set mode register */
writel(MCTL_MR0, &mctl_ctl->mr0);
writel(MCTL_MR1, &mctl_ctl->mr1);
writel(MCTL_MR2, &mctl_ctl->mr2);
writel(MCTL_MR3, &mctl_ctl->mr3);
/* Set dram timing */
reg_val = (twtp << 24) | (tfaw << 16) | (trasmax << 8) | (tras << 0);
writel(reg_val, &mctl_ctl->dramtmg0);
reg_val = (txp << 16) | (trtp << 8) | (trc << 0);
writel(reg_val, &mctl_ctl->dramtmg1);
reg_val = (tcwl << 24) | (tcl << 16) | (trd2wr << 8) | (twr2rd << 0);
writel(reg_val, &mctl_ctl->dramtmg2);
reg_val = (tmrw << 16) | (tmrd << 12) | (tmod << 0);
writel(reg_val, &mctl_ctl->dramtmg3);
reg_val = (trcd << 24) | (tccd << 16) | (trrd << 8) | (trp << 0);
writel(reg_val, &mctl_ctl->dramtmg4);
reg_val = (tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | (tcke << 0);
writel(reg_val, &mctl_ctl->dramtmg5);
/* Set two rank timing and exit self-refresh timing */
reg_val = readl(&mctl_ctl->dramtmg8);
reg_val &= ~(0xff << 8);
reg_val &= ~(0xff << 0);
reg_val |= (0x33 << 8);
reg_val |= (0x10 << 0);
writel(reg_val, &mctl_ctl->dramtmg8);
/* Set phy interface time */
reg_val = (0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8)
| (wr_latency << 0);
/* PHY interface write latency and read latency configure */
writel(reg_val, &mctl_ctl->pitmg0);
/* Set phy time PTR0-2 use default */
writel(((tdinit0 << 0) | (tdinit1 << 20)), &mctl_ctl->ptr3);
writel(((tdinit2 << 0) | (tdinit3 << 20)), &mctl_ctl->ptr4);
/* Set refresh timing */
reg_val = (trefi << 16) | (trfc << 0);
writel(reg_val, &mctl_ctl->rfshtmg);
}
static void mctl_set_pir(u32 val)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
writel(val, &mctl_ctl->pir);
mctl_await_completion(&mctl_ctl->pgsr0, 0x1, 0x1);
}
static void mctl_data_train_cfg(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
if (para->rank == 2)
clrsetbits_le32(&mctl_ctl->dtcr, 0x3 << 24, 0x3 << 24);
else
clrsetbits_le32(&mctl_ctl->dtcr, 0x3 << 24, 0x1 << 24);
}
static int mctl_train_dram(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
mctl_data_train_cfg(para);
mctl_set_pir(0x5f3);
return ((readl(&mctl_ctl->pgsr0) >> 20) & 0xff) ? -EIO : 0;
}
static int mctl_channel_init(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
u32 low_data_lines_status; /* Training status of datalines 0 - 7 */
u32 high_data_lines_status; /* Training status of datalines 8 - 15 */
auto_set_timing_para(para);
/* Disable dram VTC */
clrbits_le32(&mctl_ctl->pgcr0, 0x3f << 0);
/* Set ODT */
if ((CONFIG_DRAM_CLK > 400) && IS_ENABLED(CONFIG_DRAM_ODT_EN)) {
setbits_le32(DXnGCR0(0), 0x3 << 9);
setbits_le32(DXnGCR0(1), 0x3 << 9);
} else {
clrbits_le32(DXnGCR0(0), 0x3 << 9);
clrbits_le32(DXnGCR0(1), 0x3 << 9);
}
/* set PLL configuration */
if (CONFIG_DRAM_CLK >= 480)
setbits_le32(&mctl_ctl->pllgcr, 0x1 << 18);
else
setbits_le32(&mctl_ctl->pllgcr, 0x3 << 18);
/* Auto detect dram config, set 2 rank and 16bit bus-width */
para->cs1 = 0;
para->rank = 2;
para->bus_width = 16;
mctl_set_cr(para);
/* Open DQS gating */
clrbits_le32(&mctl_ctl->pgcr2, (0x3 << 6));
clrbits_le32(&mctl_ctl->dqsgmr, (0x1 << 8) | (0x7));
mctl_data_train_cfg(para);
/* ZQ calibration */
writel(CONFIG_DRAM_ZQ & 0xff, &mctl_ctl->zqcr1);
/* CA calibration */
mctl_set_pir(0x00000003);
/* More ZQ calibration */
writel(readl(&mctl_ctl->zqsr0) | 0x10000000, &mctl_ctl->zqcr2);
writel((CONFIG_DRAM_ZQ >> 8) & 0xff, &mctl_ctl->zqcr1);
/* DQS gate training */
if (mctl_train_dram(para) != 0) {
low_data_lines_status = (readl(DXnGSR0(0)) >> 24) & 0x03;
high_data_lines_status = (readl(DXnGSR0(1)) >> 24) & 0x03;
if (low_data_lines_status == 0x3)
return -EIO;
/* DRAM has only one rank */
para->rank = 1;
mctl_set_cr(para);
if (low_data_lines_status == high_data_lines_status)
goto done; /* 16 bit bus, 1 rank */
if (!(low_data_lines_status & high_data_lines_status)) {
/* Retry 16 bit bus-width with CS1 set */
para->cs1 = 1;
mctl_set_cr(para);
if (mctl_train_dram(para) == 0)
goto done;
}
/* Try 8 bit bus-width */
writel(0x0, DXnGCR0(1)); /* Disable high DQ */
para->cs1 = 0;
para->bus_width = 8;
mctl_set_cr(para);
if (mctl_train_dram(para) != 0)
return -EIO;
}
done:
/* Check the dramc status */
mctl_await_completion(&mctl_ctl->statr, 0x1, 0x1);
/* Close DQS gating */
setbits_le32(&mctl_ctl->pgcr2, 0x3 << 6);
/* Enable master access */
writel(0xffffffff, &mctl_com->maer);
return 0;
}
static void mctl_sys_init(struct dram_para *para)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
clrsetbits_le32(&ccm->dram_pll_cfg, CCM_DRAMPLL_CFG_SRC_MASK,
CCM_DRAMPLL_CFG_SRC_PLL11);
clock_set_pll11(CONFIG_DRAM_CLK * 1000000 * DRAM_CLK_MUL,
DRAM_SIGMA_DELTA_ENABLE);
clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV_MASK,
CCM_DRAMCLK_CFG_DIV(DRAM_CLK_DIV) |
CCM_DRAMCLK_CFG_RST | CCM_DRAMCLK_CFG_UPD);
mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
setbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
setbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE);
/* Set dram master access priority */
writel(0x0, &mctl_com->mapr);
writel(0x0f802f01, &mctl_ctl->sched);
writel(0x0000400f, &mctl_ctl->clken); /* normal */
udelay(250);
}
unsigned long sunxi_dram_init(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct dram_para para = {
.cs1 = 0,
.bank = 1,
.rank = 2,
.rows = 15,
.bus_width = 16,
.page_size = 2048,
};
mctl_sys_init(&para);
if (mctl_channel_init(&para) != 0)
return 0;
auto_detect_dram_size(&para);
/* Enable master software clk */
writel(readl(&mctl_com->swonr) | 0x3ffff, &mctl_com->swonr);
/* Set DRAM ODT MAP */
if (para.rank == 2)
writel(0x00000303, &mctl_ctl->odtmap);
else
writel(0x00000201, &mctl_ctl->odtmap);
return para.page_size * (para.bus_width / 8) *
(1 << (para.bank + para.rank + para.rows));
}
@@ -0,0 +1,471 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Sun8i a33 platform dram controller init.
*
* (C) Copyright 2007-2015 Allwinner Technology Co.
* Jerry Wang <wangflord@allwinnertech.com>
* (C) Copyright 2015 Vishnu Patekar <vishnupatekar0510@gmail.com>
* (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/prcm.h>
#define DRAM_CLK_MUL 2
#define DRAM_CLK_DIV 1
struct dram_para {
u8 cs1;
u8 seq;
u8 bank;
u8 rank;
u8 rows;
u8 bus_width;
u8 dram_type;
u16 page_size;
};
static void mctl_set_cr(struct dram_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
writel(MCTL_CR_CS1_CONTROL(para->cs1) | MCTL_CR_UNKNOWN |
MCTL_CR_CHANNEL(1) | MCTL_CR_DRAM_TYPE(para->dram_type) |
(para->seq ? MCTL_CR_SEQUENCE : 0) |
((para->bus_width == 16) ? MCTL_CR_BUSW16 : MCTL_CR_BUSW8) |
MCTL_CR_PAGE_SIZE(para->page_size) | MCTL_CR_ROW(para->rows) |
MCTL_CR_BANK(para->bank) | MCTL_CR_RANK(para->rank),
&mctl_com->cr);
}
static void auto_detect_dram_size(struct dram_para *para)
{
u8 orig_rank = para->rank;
int rows, columns;
/* Row detect */
para->page_size = 512;
para->seq = 1;
para->rows = 16;
para->rank = 1;
mctl_set_cr(para);
for (rows = 11 ; rows < 16 ; rows++) {
if (mctl_mem_matches(1 << (rows + 9))) /* row-column */
break;
}
/* Column (page size) detect */
para->rows = 11;
para->page_size = 8192;
mctl_set_cr(para);
for (columns = 9 ; columns < 13 ; columns++) {
if (mctl_mem_matches(1 << columns))
break;
}
para->seq = 0;
para->rank = orig_rank;
para->rows = rows;
para->page_size = 1 << columns;
mctl_set_cr(para);
}
static inline int ns_to_t(int nanoseconds)
{
const unsigned int ctrl_freq =
CONFIG_DRAM_CLK * DRAM_CLK_MUL / DRAM_CLK_DIV;
return (ctrl_freq * nanoseconds + 999) / 1000;
}
static void auto_set_timing_para(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
u32 reg_val;
u8 tccd = 2;
u8 tfaw = ns_to_t(50);
u8 trrd = max(ns_to_t(10), 4);
u8 trcd = ns_to_t(15);
u8 trc = ns_to_t(53);
u8 txp = max(ns_to_t(8), 3);
u8 twtr = max(ns_to_t(8), 4);
u8 trtp = max(ns_to_t(8), 4);
u8 twr = max(ns_to_t(15), 3);
u8 trp = ns_to_t(15);
u8 tras = ns_to_t(38);
u16 trefi = ns_to_t(7800) / 32;
u16 trfc = ns_to_t(350);
/* Fixed timing parameters */
u8 tmrw = 0;
u8 tmrd = 4;
u8 tmod = 12;
u8 tcke = 3;
u8 tcksrx = 5;
u8 tcksre = 5;
u8 tckesr = 4;
u8 trasmax = 24;
u8 tcl = 6; /* CL 12 */
u8 tcwl = 4; /* CWL 8 */
u8 t_rdata_en = 4;
u8 wr_latency = 2;
u32 tdinit0 = (500 * CONFIG_DRAM_CLK) + 1; /* 500us */
u32 tdinit1 = (360 * CONFIG_DRAM_CLK) / 1000 + 1; /* 360ns */
u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
u8 twtp = tcwl + 2 + twr; /* WL + BL / 2 + tWR */
u8 twr2rd = tcwl + 2 + twtr; /* WL + BL / 2 + tWTR */
u8 trd2wr = tcl + 2 + 1 - tcwl; /* RL + BL / 2 + 2 - WL */
/* Set work mode register */
mctl_set_cr(para);
/* Set mode register */
if (para->dram_type == DRAM_TYPE_DDR3) {
writel(MCTL_MR0, &mctl_ctl->mr0);
writel(MCTL_MR1, &mctl_ctl->mr1);
writel(MCTL_MR2, &mctl_ctl->mr2);
writel(MCTL_MR3, &mctl_ctl->mr3);
} else if (para->dram_type == DRAM_TYPE_LPDDR3) {
writel(MCTL_LPDDR3_MR0, &mctl_ctl->mr0);
writel(MCTL_LPDDR3_MR1, &mctl_ctl->mr1);
writel(MCTL_LPDDR3_MR2, &mctl_ctl->mr2);
writel(MCTL_LPDDR3_MR3, &mctl_ctl->mr3);
/* timing parameters for LPDDR3 */
tfaw = max(ns_to_t(50), 4);
trrd = max(ns_to_t(10), 2);
trcd = max(ns_to_t(24), 2);
trc = ns_to_t(70);
txp = max(ns_to_t(8), 2);
twtr = max(ns_to_t(8), 2);
trtp = max(ns_to_t(8), 2);
trp = max(ns_to_t(27), 2);
tras = ns_to_t(42);
trefi = ns_to_t(3900) / 32;
trfc = ns_to_t(210);
tmrw = 5;
tmrd = 5;
tckesr = 5;
tcwl = 3; /* CWL 8 */
t_rdata_en = 5;
tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */
tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 200us */
tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
twtp = tcwl + 4 + twr + 1; /* CWL + BL/2 + tWR */
twr2rd = tcwl + 4 + 1 + twtr; /* WL + BL / 2 + tWTR */
trd2wr = tcl + 4 + 5 - tcwl + 1; /* RL + BL / 2 + 2 - WL */
}
/* Set dram timing */
reg_val = (twtp << 24) | (tfaw << 16) | (trasmax << 8) | (tras << 0);
writel(reg_val, &mctl_ctl->dramtmg0);
reg_val = (txp << 16) | (trtp << 8) | (trc << 0);
writel(reg_val, &mctl_ctl->dramtmg1);
reg_val = (tcwl << 24) | (tcl << 16) | (trd2wr << 8) | (twr2rd << 0);
writel(reg_val, &mctl_ctl->dramtmg2);
reg_val = (tmrw << 16) | (tmrd << 12) | (tmod << 0);
writel(reg_val, &mctl_ctl->dramtmg3);
reg_val = (trcd << 24) | (tccd << 16) | (trrd << 8) | (trp << 0);
writel(reg_val, &mctl_ctl->dramtmg4);
reg_val = (tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | (tcke << 0);
writel(reg_val, &mctl_ctl->dramtmg5);
/* Set two rank timing and exit self-refresh timing */
reg_val = readl(&mctl_ctl->dramtmg8);
reg_val &= ~(0xff << 8);
reg_val &= ~(0xff << 0);
reg_val |= (0x33 << 8);
reg_val |= (0x8 << 0);
writel(reg_val, &mctl_ctl->dramtmg8);
/* Set phy interface time */
reg_val = (0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8)
| (wr_latency << 0);
/* PHY interface write latency and read latency configure */
writel(reg_val, &mctl_ctl->pitmg0);
/* Set phy time PTR0-2 use default */
writel(((tdinit0 << 0) | (tdinit1 << 20)), &mctl_ctl->ptr3);
writel(((tdinit2 << 0) | (tdinit3 << 20)), &mctl_ctl->ptr4);
/* Set refresh timing */
reg_val = (trefi << 16) | (trfc << 0);
writel(reg_val, &mctl_ctl->rfshtmg);
}
static void mctl_set_pir(u32 val)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
writel(val, &mctl_ctl->pir);
mctl_await_completion(&mctl_ctl->pgsr0, 0x1, 0x1);
}
static void mctl_data_train_cfg(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
if (para->rank == 2)
clrsetbits_le32(&mctl_ctl->dtcr, 0x3 << 24, 0x3 << 24);
else
clrsetbits_le32(&mctl_ctl->dtcr, 0x3 << 24, 0x1 << 24);
}
static int mctl_train_dram(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
mctl_data_train_cfg(para);
mctl_set_pir(0x5f3);
return ((readl(&mctl_ctl->pgsr0) >> 20) & 0xff) ? -EIO : 0;
}
static void set_master_priority(void)
{
writel(0x00a0000d, MCTL_MASTER_CFG0(0));
writel(0x00500064, MCTL_MASTER_CFG1(0));
writel(0x07000009, MCTL_MASTER_CFG0(1));
writel(0x00000600, MCTL_MASTER_CFG1(1));
writel(0x01000009, MCTL_MASTER_CFG0(3));
writel(0x00000064, MCTL_MASTER_CFG1(3));
writel(0x08000009, MCTL_MASTER_CFG0(4));
writel(0x00000640, MCTL_MASTER_CFG1(4));
writel(0x20000308, MCTL_MASTER_CFG0(8));
writel(0x00001000, MCTL_MASTER_CFG1(8));
writel(0x02800009, MCTL_MASTER_CFG0(9));
writel(0x00000100, MCTL_MASTER_CFG1(9));
writel(0x01800009, MCTL_MASTER_CFG0(5));
writel(0x00000100, MCTL_MASTER_CFG1(5));
writel(0x01800009, MCTL_MASTER_CFG0(7));
writel(0x00000100, MCTL_MASTER_CFG1(7));
writel(0x00640009, MCTL_MASTER_CFG0(6));
writel(0x00000032, MCTL_MASTER_CFG1(6));
writel(0x0100000d, MCTL_MASTER_CFG0(2));
writel(0x00500080, MCTL_MASTER_CFG1(2));
}
static int mctl_channel_init(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
u32 low_data_lines_status; /* Training status of datalines 0 - 7 */
u32 high_data_lines_status; /* Training status of datalines 8 - 15 */
u32 i, rval;
auto_set_timing_para(para);
/* Set dram master access priority */
writel(0x000101a0, &mctl_com->bwcr);
/* set cpu high priority */
writel(0x1, &mctl_com->mapr);
set_master_priority();
udelay(250);
/* Disable dram VTC */
clrbits_le32(&mctl_ctl->pgcr0, 0x3f << 0 | 0x1 << 30);
clrsetbits_le32(&mctl_ctl->pgcr1, 0x1 << 24, 0x1 << 26);
writel(0x94be6fa3, MCTL_PROTECT);
udelay(100);
clrsetbits_le32(MX_UPD2, 0xfff << 16, 0x50 << 16);
writel(0x0, MCTL_PROTECT);
udelay(100);
/* Set ODT */
if (IS_ENABLED(CONFIG_DRAM_ODT_EN))
rval = 0x0;
else
rval = 0x2;
for (i = 0 ; i < 11 ; i++) {
clrsetbits_le32(DATX0IOCR(i), (0x3 << 24) | (0x3 << 16),
rval << 24);
clrsetbits_le32(DATX1IOCR(i), (0x3 << 24) | (0x3 << 16),
rval << 24);
clrsetbits_le32(DATX2IOCR(i), (0x3 << 24) | (0x3 << 16),
rval << 24);
clrsetbits_le32(DATX3IOCR(i), (0x3 << 24) | (0x3 << 16),
rval << 24);
}
for (i = 0; i < 31; i++)
clrsetbits_le32(CAIOCR(i), 0x3 << 26 | 0x3 << 16, 0x2 << 26);
/* set PLL configuration */
if (CONFIG_DRAM_CLK >= 480)
setbits_le32(&mctl_ctl->pllgcr, 0x1 << 19);
else
setbits_le32(&mctl_ctl->pllgcr, 0x3 << 19);
/* Auto detect dram config, set 2 rank and 16bit bus-width */
para->cs1 = 0;
para->rank = 2;
para->bus_width = 16;
mctl_set_cr(para);
/* Open DQS gating */
clrbits_le32(&mctl_ctl->pgcr2, (0x3 << 6));
clrbits_le32(&mctl_ctl->dqsgmr, (0x1 << 8) | (0x7));
if (para->dram_type == DRAM_TYPE_LPDDR3)
clrsetbits_le32(&mctl_ctl->dxccr, (0x1 << 27) | (0x3<<6) ,
0x1 << 31);
if (readl(&mctl_com->cr) & 0x1)
writel(0x00000303, &mctl_ctl->odtmap);
else
writel(0x00000201, &mctl_ctl->odtmap);
mctl_data_train_cfg(para);
/* ZQ calibration */
clrsetbits_le32(ZQnPR(0), 0x000000ff, CONFIG_DRAM_ZQ & 0xff);
clrsetbits_le32(ZQnPR(1), 0x000000ff, (CONFIG_DRAM_ZQ >> 8) & 0xff);
/* CA calibration */
if (para->dram_type == DRAM_TYPE_DDR3)
mctl_set_pir(0x0201f3 | 0x1<<10);
else
mctl_set_pir(0x020173 | 0x1<<10);
/* DQS gate training */
if (mctl_train_dram(para) != 0) {
low_data_lines_status = (readl(DXnGSR0(0)) >> 24) & 0x03;
high_data_lines_status = (readl(DXnGSR0(1)) >> 24) & 0x03;
if (low_data_lines_status == 0x3)
return -EIO;
/* DRAM has only one rank */
para->rank = 1;
mctl_set_cr(para);
if (low_data_lines_status == high_data_lines_status)
goto done; /* 16 bit bus, 1 rank */
if (!(low_data_lines_status & high_data_lines_status)) {
/* Retry 16 bit bus-width with CS1 set */
para->cs1 = 1;
mctl_set_cr(para);
if (mctl_train_dram(para) == 0)
goto done;
}
/* Try 8 bit bus-width */
writel(0x0, DXnGCR0(1)); /* Disable high DQ */
para->cs1 = 0;
para->bus_width = 8;
mctl_set_cr(para);
if (mctl_train_dram(para) != 0)
return -EIO;
}
done:
/* Check the dramc status */
mctl_await_completion(&mctl_ctl->statr, 0x1, 0x1);
/* Close DQS gating */
setbits_le32(&mctl_ctl->pgcr2, 0x3 << 6);
/* set PGCR3,CKE polarity */
writel(0x00aa0060, &mctl_ctl->pgcr3);
/* Enable master access */
writel(0xffffffff, &mctl_com->maer);
return 0;
}
static void mctl_sys_init(struct dram_para *para)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
clrbits_le32(&ccm->mbus_clk_cfg, MBUS_CLK_GATE);
clrbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
udelay(1000);
clrbits_le32(&ccm->dram_clk_cfg, 0x01<<31);
clock_set_pll5(CONFIG_DRAM_CLK * 1000000 * DRAM_CLK_MUL);
clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV_MASK,
CCM_DRAMCLK_CFG_DIV(DRAM_CLK_DIV) |
CCM_DRAMCLK_CFG_RST | CCM_DRAMCLK_CFG_UPD);
mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
setbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
setbits_le32(&ccm->mbus_clk_cfg, MBUS_CLK_GATE);
para->rank = 2;
para->bus_width = 16;
mctl_set_cr(para);
/* Set dram master access priority */
writel(0x0000e00f, &mctl_ctl->clken); /* normal */
udelay(250);
}
unsigned long sunxi_dram_init(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct dram_para para = {
.cs1 = 0,
.bank = 1,
.rank = 1,
.rows = 15,
.bus_width = 16,
.page_size = 2048,
};
#if defined(CONFIG_MACH_SUN8I_A83T)
#if (CONFIG_DRAM_TYPE == 3) || (CONFIG_DRAM_TYPE == 7)
para.dram_type = CONFIG_DRAM_TYPE;
#else
#error Unsupported DRAM type, Please set DRAM type (3:DDR3, 7:LPDDR3)
#endif
#endif
setbits_le32(SUNXI_PRCM_BASE + 0x1e0, 0x1 << 8);
writel(0, (SUNXI_PRCM_BASE + 0x1e8));
udelay(10);
mctl_sys_init(&para);
if (mctl_channel_init(&para) != 0)
return 0;
auto_detect_dram_size(&para);
/* Enable master software clk */
writel(readl(&mctl_com->swonr) | 0x3ffff, &mctl_com->swonr);
/* Set DRAM ODT MAP */
if (para.rank == 2)
writel(0x00000303, &mctl_ctl->odtmap);
else
writel(0x00000201, &mctl_ctl->odtmap);
return para.page_size * (para.bus_width / 8) *
(1 << (para.bank + para.rank + para.rows));
}
@@ -0,0 +1,958 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* sun9i dram controller initialisation
*
* (C) Copyright 2007-2015
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Jerry Wang <wangflord@allwinnertech.com>
*
* (C) Copyright 2016 Theobroma Systems Design und Consulting GmbH
* Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <ram.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/sys_proto.h>
#define DRAM_CLK (CONFIG_DRAM_CLK * 1000000)
/*
* The following amounts to an extensive rewrite of the code received from
* Allwinner as part of the open-source bootloader release (refer to
* https://github.com/allwinner-zh/bootloader.git) and augments the upstream
* sources (which act as the primary reference point for the inner workings
* of the 'underdocumented' DRAM controller in the A80) using the following
* documentation for other memory controllers based on the (Synopsys)
* Designware IP (DDR memory protocol controller and DDR PHY)
* * TI Keystone II Architecture: DDR3 Memory Controller, User's Guide
* Document 'SPRUHN7C', Oct 2013 (revised March 2015)
* * Xilinx Zynq UltraScale+ MPSoC Register Reference
* document ug1087 (v1.0)
* Note that the Zynq-documentation provides a very close match for the DDR
* memory protocol controller (and provides a very good guide to the rounding
* rules for various timings), whereas the TI Keystone II document should be
* referred to for DDR PHY specifics only.
*
* The DRAM controller in the A80 runs at half the frequency of the DDR PHY
* (i.e. the rules for MEMC_FREQ_RATIO=2 from the Zynq-documentation apply).
*
* Known limitations
* =================
* In the current state, the following features are not fully supported and
* a number of simplifying assumptions have been made:
* 1) Only DDR3 support is implemented, as our test platform (the A80-Q7
* module) is designed to accomodate DDR3/DDR3L.
* 2) Only 2T-mode has been implemented and tested.
* 3) The controller supports two different clocking strategies (PLL6 can
* either be 2*CK or CK/2)... we only support the 2*CK clock at this
* time and haven't verified whether the alternative clocking strategy
* works. If you are interested in porting this over/testing this,
* please refer to cases where bit 0 of 'dram_tpr8' is tested in the
* original code from Allwinner.
* 4) Support for 2 ranks per controller is not implemented (as we don't
* the hardware to test it).
*
* Future directions
* =================
* The driver should be driven from a device-tree based configuration that
* can dynamically provide the necessary timing parameters (i.e. target
* frequency and speed-bin information)---the data structures used in the
* calculation of the timing parameters are already designed to capture
* similar information as the device tree would provide.
*
* To enable a device-tree based configuration of the sun9i platform, we
* will need to enable CONFIG_TPL and bootstrap in 3 stages: initially
* into SRAM A1 (40KB) and next into SRAM A2 (160KB)---which would be the
* stage to initialise the platform via the device-tree---before having
* the full U-Boot run from DDR.
*/
/*
* A number of DDR3 timings are given as "the greater of a fixed number of
* clock cycles (CK) or nanoseconds. We express these using a structure
* that holds a cycle count and a duration in picoseconds (so we can model
* sub-ns timings, such as 7.5ns without losing precision or resorting to
* rounding up early.
*/
struct dram_sun9i_timing {
u32 ck;
u32 ps;
};
/* */
struct dram_sun9i_cl_cwl_timing {
u32 CL;
u32 CWL;
u32 tCKmin; /* in ps */
u32 tCKmax; /* in ps */
};
struct dram_sun9i_para {
u32 dram_type;
u8 bus_width;
u8 chan;
u8 rank;
u8 rows;
u16 page_size;
/* Timing information for each speed-bin */
struct dram_sun9i_cl_cwl_timing *cl_cwl_table;
u32 cl_cwl_numentries;
/*
* For the timings, we try to keep the order and grouping used in
* JEDEC Standard No. 79-3F
*/
/* timings */
u32 tREFI; /* in ns */
u32 tRFC; /* in ns */
u32 tRAS; /* in ps */
/* command and address timing */
u32 tDLLK; /* in nCK */
struct dram_sun9i_timing tRTP;
struct dram_sun9i_timing tWTR;
u32 tWR; /* in nCK */
u32 tMRD; /* in nCK */
struct dram_sun9i_timing tMOD;
u32 tRCD; /* in ps */
u32 tRP; /* in ps */
u32 tRC; /* in ps */
u32 tCCD; /* in nCK */
struct dram_sun9i_timing tRRD;
u32 tFAW; /* in ps */
/* calibration timing */
/* struct dram_sun9i_timing tZQinit; */
struct dram_sun9i_timing tZQoper;
struct dram_sun9i_timing tZQCS;
/* reset timing */
/* struct dram_sun9i_timing tXPR; */
/* self-refresh timings */
struct dram_sun9i_timing tXS;
u32 tXSDLL; /* in nCK */
/* struct dram_sun9i_timing tCKESR; */
struct dram_sun9i_timing tCKSRE;
struct dram_sun9i_timing tCKSRX;
/* power-down timings */
struct dram_sun9i_timing tXP;
struct dram_sun9i_timing tXPDLL;
struct dram_sun9i_timing tCKE;
/* write leveling timings */
u32 tWLMRD; /* min, in nCK */
/* u32 tWLDQSEN; min, in nCK */
u32 tWLO; /* max, in ns */
/* u32 tWLOE; max, in ns */
/* u32 tCKDPX; in nCK */
/* u32 tCKCSX; in nCK */
};
static void mctl_sys_init(void);
#define SCHED_RDWR_IDLE_GAP(n) ((n & 0xff) << 24)
#define SCHED_GO2CRITICAL_HYSTERESIS(n) ((n & 0xff) << 16)
#define SCHED_LPR_NUM_ENTRIES(n) ((n & 0xff) << 8)
#define SCHED_PAGECLOSE (1 << 2)
#define SCHED_PREFER_WRITE (1 << 1)
#define SCHED_FORCE_LOW_PRI_N (1 << 0)
#define SCHED_CONFIG (SCHED_RDWR_IDLE_GAP(0xf) | \
SCHED_GO2CRITICAL_HYSTERESIS(0x80) | \
SCHED_LPR_NUM_ENTRIES(0x20) | \
SCHED_FORCE_LOW_PRI_N)
#define PERFHPR0_CONFIG 0x0000001f
#define PERFHPR1_CONFIG 0x1f00001f
#define PERFLPR0_CONFIG 0x000000ff
#define PERFLPR1_CONFIG 0x0f0000ff
#define PERFWR0_CONFIG 0x000000ff
#define PERFWR1_CONFIG 0x0f0001ff
static void mctl_ctl_sched_init(unsigned long base)
{
struct sunxi_mctl_ctl_reg *mctl_ctl =
(struct sunxi_mctl_ctl_reg *)base;
/* Needs to be done before the global clk enable... */
writel(SCHED_CONFIG, &mctl_ctl->sched);
writel(PERFHPR0_CONFIG, &mctl_ctl->perfhpr0);
writel(PERFHPR1_CONFIG, &mctl_ctl->perfhpr1);
writel(PERFLPR0_CONFIG, &mctl_ctl->perflpr0);
writel(PERFLPR1_CONFIG, &mctl_ctl->perflpr1);
writel(PERFWR0_CONFIG, &mctl_ctl->perfwr0);
writel(PERFWR1_CONFIG, &mctl_ctl->perfwr1);
}
static void mctl_sys_init(void)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
debug("Setting PLL6 to %d\n", DRAM_CLK * 2);
clock_set_pll6(DRAM_CLK * 2);
/* Original dram init code which may come in handy later
********************************************************
clock_set_pll6(use_2channelPLL ? (DRAM_CLK * 2) :
(DRAM_CLK / 2), false);
if ((para->dram_clk <= 400)|((para->dram_tpr8 & 0x1)==0)) {
* PLL6 should be 2*CK *
* ccm_setup_pll6_ddr_clk(PLL6_DDR_CLK); *
ccm_setup_pll6_ddr_clk((1000000 * (para->dram_clk) * 2), 0);
} else {
* PLL6 should be CK/2 *
ccm_setup_pll6_ddr_clk((1000000 * (para->dram_clk) / 2), 1);
}
if (para->dram_tpr13 & (0xf<<18)) {
*
* bit21:bit18=0001:pll swing 0.4
* bit21:bit18=0010:pll swing 0.3
* bit21:bit18=0100:pll swing 0.2
* bit21:bit18=1000:pll swing 0.1
*
dram_dbg("DRAM fre extend open !\n");
reg_val=mctl_read_w(CCM_PLL6_DDR_REG);
reg_val&=(0x1<<16);
reg_val=reg_val>>16;
if(para->dram_tpr13 & (0x1<<18))
{
mctl_write_w(CCM_PLL_BASE + 0x114,
(0x3333U|(0x3<<17)|(reg_val<<19)|(0x120U<<20)|
(0x2U<<29)|(0x1U<<31)));
}
else if(para->dram_tpr13 & (0x1<<19))
{
mctl_write_w(CCM_PLL_BASE + 0x114,
(0x6666U|(0x3U<<17)|(reg_val<<19)|(0xD8U<<20)|
(0x2U<<29)|(0x1U<<31)));
}
else if(para->dram_tpr13 & (0x1<<20))
{
mctl_write_w(CCM_PLL_BASE + 0x114,
(0x9999U|(0x3U<<17)|(reg_val<<19)|(0x90U<<20)|
(0x2U<<29)|(0x1U<<31)));
}
else if(para->dram_tpr13 & (0x1<<21))
{
mctl_write_w(CCM_PLL_BASE + 0x114,
(0xccccU|(0x3U<<17)|(reg_val<<19)|(0x48U<<20)|
(0x2U<<29)|(0x1U<<31)));
}
//frequency extend open
reg_val = mctl_read_w(CCM_PLL6_DDR_REG);
reg_val |= ((0x1<<24)|(0x1<<30));
mctl_write_w(CCM_PLL6_DDR_REG, reg_val);
while(mctl_read_w(CCM_PLL6_DDR_REG) & (0x1<<30));
}
aw_delay(0x20000); //make some delay
********************************************************
*/
/* assert mctl reset */
clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
/* stop mctl clock */
clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
sdelay(2000);
/* deassert mctl reset */
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
/* enable mctl clock */
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
/* set up the transactions scheduling before enabling the global clk */
mctl_ctl_sched_init(SUNXI_DRAM_CTL0_BASE);
mctl_ctl_sched_init(SUNXI_DRAM_CTL1_BASE);
sdelay(1000);
debug("2\n");
/* (3 << 12): PLL_DDR */
writel((3 << 12) | (1 << 16), &ccm->dram_clk_cfg);
do {
debug("Waiting for DRAM_CLK_CFG\n");
sdelay(10000);
} while (readl(&ccm->dram_clk_cfg) & (1 << 16));
setbits_le32(&ccm->dram_clk_cfg, (1 << 31));
/* TODO: we only support the common case ... i.e. 2*CK */
setbits_le32(&mctl_com->ccr, (1 << 14) | (1 << 30));
writel(2, &mctl_com->rmcr); /* controller clock is PLL6/4 */
sdelay(2000);
/* Original dram init code which may come in handy later
********************************************************
if ((para->dram_clk <= 400) | ((para->dram_tpr8 & 0x1) == 0)) {
* PLL6 should be 2*CK *
* gating 2 channel pll *
reg_val = mctl_read_w(MC_CCR);
reg_val |= ((0x1 << 14) | (0x1U << 30));
mctl_write_w(MC_CCR, reg_val);
mctl_write_w(MC_RMCR, 0x2); * controller clock use pll6/4 *
} else {
* enable 2 channel pll *
reg_val = mctl_read_w(MC_CCR);
reg_val &= ~((0x1 << 14) | (0x1U << 30));
mctl_write_w(MC_CCR, reg_val);
mctl_write_w(MC_RMCR, 0x0); * controller clock use pll6 *
}
reg_val = mctl_read_w(MC_CCR);
reg_val &= ~((0x1<<15)|(0x1U<<31));
mctl_write_w(MC_CCR, reg_val);
aw_delay(20);
//aw_delay(0x10);
********************************************************
*/
clrbits_le32(&mctl_com->ccr, MCTL_CCR_CH0_CLK_EN | MCTL_CCR_CH1_CLK_EN);
sdelay(1000);
setbits_le32(&mctl_com->ccr, MCTL_CCR_CH0_CLK_EN);
/* TODO if (para->chan == 2) */
setbits_le32(&mctl_com->ccr, MCTL_CCR_CH1_CLK_EN);
}
static void mctl_com_init(struct dram_sun9i_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
/* TODO: hard-wired for DDR3 now */
writel(((para->chan == 2) ? MCTL_CR_CHANNEL_DUAL :
MCTL_CR_CHANNEL_SINGLE)
| MCTL_CR_DRAMTYPE_DDR3 | MCTL_CR_BANK(1)
| MCTL_CR_ROW(para->rows)
| ((para->bus_width == 32) ? MCTL_CR_BUSW32 : MCTL_CR_BUSW16)
| MCTL_CR_PAGE_SIZE(para->page_size) | MCTL_CR_RANK(para->rank),
&mctl_com->cr);
debug("CR: %d\n", readl(&mctl_com->cr));
}
static u32 mctl_channel_init(u32 ch_index, struct dram_sun9i_para *para)
{
struct sunxi_mctl_ctl_reg *mctl_ctl;
struct sunxi_mctl_phy_reg *mctl_phy;
u32 CL = 0;
u32 CWL = 0;
u16 mr[4] = { 0, };
#define PS2CYCLES_FLOOR(n) ((n * CONFIG_DRAM_CLK) / 1000000)
#define PS2CYCLES_ROUNDUP(n) ((n * CONFIG_DRAM_CLK + 999999) / 1000000)
#define NS2CYCLES_FLOOR(n) ((n * CONFIG_DRAM_CLK) / 1000)
#define NS2CYCLES_ROUNDUP(n) ((n * CONFIG_DRAM_CLK + 999) / 1000)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
/*
* Convert the values to cycle counts (nCK) from what is provided
* by the definition of each speed bin.
*/
/* const u32 tREFI = NS2CYCLES_FLOOR(para->tREFI); */
const u32 tREFI = NS2CYCLES_FLOOR(para->tREFI);
const u32 tRFC = NS2CYCLES_ROUNDUP(para->tRFC);
const u32 tRCD = PS2CYCLES_ROUNDUP(para->tRCD);
const u32 tRP = PS2CYCLES_ROUNDUP(para->tRP);
const u32 tRC = PS2CYCLES_ROUNDUP(para->tRC);
const u32 tRAS = PS2CYCLES_ROUNDUP(para->tRAS);
/* command and address timing */
const u32 tDLLK = para->tDLLK;
const u32 tRTP = MAX(para->tRTP.ck, PS2CYCLES_ROUNDUP(para->tRTP.ps));
const u32 tWTR = MAX(para->tWTR.ck, PS2CYCLES_ROUNDUP(para->tWTR.ps));
const u32 tWR = NS2CYCLES_FLOOR(para->tWR);
const u32 tMRD = para->tMRD;
const u32 tMOD = MAX(para->tMOD.ck, PS2CYCLES_ROUNDUP(para->tMOD.ps));
const u32 tCCD = para->tCCD;
const u32 tRRD = MAX(para->tRRD.ck, PS2CYCLES_ROUNDUP(para->tRRD.ps));
const u32 tFAW = PS2CYCLES_ROUNDUP(para->tFAW);
/* calibration timings */
/* const u32 tZQinit = MAX(para->tZQinit.ck,
PS2CYCLES_ROUNDUP(para->tZQinit.ps)); */
const u32 tZQoper = MAX(para->tZQoper.ck,
PS2CYCLES_ROUNDUP(para->tZQoper.ps));
const u32 tZQCS = MAX(para->tZQCS.ck,
PS2CYCLES_ROUNDUP(para->tZQCS.ps));
/* reset timing */
/* const u32 tXPR = MAX(para->tXPR.ck,
PS2CYCLES_ROUNDUP(para->tXPR.ps)); */
/* power-down timings */
const u32 tXP = MAX(para->tXP.ck, PS2CYCLES_ROUNDUP(para->tXP.ps));
const u32 tXPDLL = MAX(para->tXPDLL.ck,
PS2CYCLES_ROUNDUP(para->tXPDLL.ps));
const u32 tCKE = MAX(para->tCKE.ck, PS2CYCLES_ROUNDUP(para->tCKE.ps));
/*
* self-refresh timings (keep below power-down timings, as tCKESR
* needs to be calculated based on the nCK value of tCKE)
*/
const u32 tXS = MAX(para->tXS.ck, PS2CYCLES_ROUNDUP(para->tXS.ps));
const u32 tXSDLL = para->tXSDLL;
const u32 tCKSRE = MAX(para->tCKSRE.ck,
PS2CYCLES_ROUNDUP(para->tCKSRE.ps));
const u32 tCKESR = tCKE + 1;
const u32 tCKSRX = MAX(para->tCKSRX.ck,
PS2CYCLES_ROUNDUP(para->tCKSRX.ps));
/* write leveling timings */
const u32 tWLMRD = para->tWLMRD;
/* const u32 tWLDQSEN = para->tWLDQSEN; */
const u32 tWLO = PS2CYCLES_FLOOR(para->tWLO);
/* const u32 tWLOE = PS2CYCLES_FLOOR(para->tWLOE); */
const u32 tRASmax = tREFI * 9;
int i;
for (i = 0; i < para->cl_cwl_numentries; ++i) {
const u32 tCK = 1000000 / CONFIG_DRAM_CLK;
if ((para->cl_cwl_table[i].tCKmin <= tCK) &&
(tCK < para->cl_cwl_table[i].tCKmax)) {
CL = para->cl_cwl_table[i].CL;
CWL = para->cl_cwl_table[i].CWL;
debug("found CL/CWL: CL = %d, CWL = %d\n", CL, CWL);
break;
}
}
if ((CL == 0) && (CWL == 0)) {
printf("failed to find valid CL/CWL for operating point %d MHz\n",
CONFIG_DRAM_CLK);
return 0;
}
if (ch_index == 0) {
mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
} else {
mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL1_BASE;
mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE;
}
if (para->dram_type == DRAM_TYPE_DDR3) {
mr[0] = DDR3_MR0_PPD_FAST_EXIT | DDR3_MR0_WR(tWR) |
DDR3_MR0_CL(CL);
mr[1] = DDR3_MR1_RTT120OHM;
mr[2] = DDR3_MR2_TWL(CWL);
mr[3] = 0;
/*
* DRAM3 initialisation requires holding CKE LOW for
* at least 500us prior to starting the initialisation
* sequence and at least 10ns after driving CKE HIGH
* before the initialisation sequence may be started).
*
* Refer to Micron document "TN-41-07: DDR3 Power-Up,
* Initialization, and Reset DDR3 Initialization
* Routine" for details).
*/
writel(MCTL_INIT0_POST_CKE_x1024(1) |
MCTL_INIT0_PRE_CKE_x1024(
(500 * CONFIG_DRAM_CLK + 1023) / 1024), /* 500us */
&mctl_ctl->init[0]);
writel(MCTL_INIT1_DRAM_RSTN_x1024(1),
&mctl_ctl->init[1]);
/* INIT2 is not used for DDR3 */
writel(MCTL_INIT3_MR(mr[0]) | MCTL_INIT3_EMR(mr[1]),
&mctl_ctl->init[3]);
writel(MCTL_INIT4_EMR2(mr[2]) | MCTL_INIT4_EMR3(mr[3]),
&mctl_ctl->init[4]);
writel(MCTL_INIT5_DEV_ZQINIT_x32(512 / 32), /* 512 cycles */
&mctl_ctl->init[5]);
} else {
/* !!! UNTESTED !!! */
/*
* LPDDR2 and/or LPDDR3 require a 200us minimum delay
* after driving CKE HIGH in the initialisation sequence.
*/
writel(MCTL_INIT0_POST_CKE_x1024(
(200 * CONFIG_DRAM_CLK + 1023) / 1024),
&mctl_ctl->init[0]);
writel(MCTL_INIT1_DRAM_RSTN_x1024(1),
&mctl_ctl->init[1]);
writel(MCTL_INIT2_IDLE_AFTER_RESET_x32(
(CONFIG_DRAM_CLK + 31) / 32) /* 1us */
| MCTL_INIT2_MIN_STABLE_CLOCK_x1(5), /* 5 cycles */
&mctl_ctl->init[2]);
writel(MCTL_INIT3_MR(mr[1]) | MCTL_INIT3_EMR(mr[2]),
&mctl_ctl->init[3]);
writel(MCTL_INIT4_EMR2(mr[3]),
&mctl_ctl->init[4]);
writel(MCTL_INIT5_DEV_ZQINIT_x32(
(CONFIG_DRAM_CLK + 31) / 32) /* 1us */
| MCTL_INIT5_MAX_AUTO_INIT_x1024(
(10 * CONFIG_DRAM_CLK + 1023) / 1024),
&mctl_ctl->init[5]);
}
/* (DDR3) We always use a burst-length of 8. */
#define MCTL_BL 8
/* wr2pre: WL + BL/2 + tWR */
#define WR2PRE (MCTL_BL/2 + CWL + tWTR)
/* wr2rd = CWL + BL/2 + tWTR */
#define WR2RD (MCTL_BL/2 + CWL + tWTR)
/*
* rd2wr = RL + BL/2 + 2 - WL (for DDR3)
* rd2wr = RL + BL/2 + RU(tDQSCKmax/tCK) + 1 - WL (for LPDDR2/LPDDR3)
*/
#define RD2WR (CL + MCTL_BL/2 + 2 - CWL)
#define MCTL_PHY_TRTW 0
#define MCTL_PHY_TRTODT 0
#define MCTL_DIV2(n) ((n + 1)/2)
#define MCTL_DIV32(n) (n/32)
#define MCTL_DIV1024(n) (n/1024)
writel((MCTL_DIV2(WR2PRE) << 24) | (MCTL_DIV2(tFAW) << 16) |
(MCTL_DIV1024(tRASmax) << 8) | (MCTL_DIV2(tRAS) << 0),
&mctl_ctl->dramtmg[0]);
writel((MCTL_DIV2(tXP) << 16) | (MCTL_DIV2(tRTP) << 8) |
(MCTL_DIV2(tRC) << 0),
&mctl_ctl->dramtmg[1]);
writel((MCTL_DIV2(CWL) << 24) | (MCTL_DIV2(CL) << 16) |
(MCTL_DIV2(RD2WR) << 8) | (MCTL_DIV2(WR2RD) << 0),
&mctl_ctl->dramtmg[2]);
/*
* Note: tMRW is located at bit 16 (and up) in DRAMTMG3...
* this is only relevant for LPDDR2/LPDDR3
*/
writel((MCTL_DIV2(tMRD) << 12) | (MCTL_DIV2(tMOD) << 0),
&mctl_ctl->dramtmg[3]);
writel((MCTL_DIV2(tRCD) << 24) | (MCTL_DIV2(tCCD) << 16) |
(MCTL_DIV2(tRRD) << 8) | (MCTL_DIV2(tRP) << 0),
&mctl_ctl->dramtmg[4]);
writel((MCTL_DIV2(tCKSRX) << 24) | (MCTL_DIV2(tCKSRE) << 16) |
(MCTL_DIV2(tCKESR) << 8) | (MCTL_DIV2(tCKE) << 0),
&mctl_ctl->dramtmg[5]);
/* These timings are relevant for LPDDR2/LPDDR3 only */
/* writel((MCTL_TCKDPDE << 24) | (MCTL_TCKDPX << 16) |
(MCTL_TCKCSX << 0), &mctl_ctl->dramtmg[6]); */
/* printf("DRAMTMG7 reset value: 0x%x\n",
readl(&mctl_ctl->dramtmg[7])); */
/* DRAMTMG7 reset value: 0x202 */
/* DRAMTMG7 should contain t_ckpde and t_ckpdx: check reset values!!! */
/* printf("DRAMTMG8 reset value: 0x%x\n",
readl(&mctl_ctl->dramtmg[8])); */
/* DRAMTMG8 reset value: 0x44 */
writel((MCTL_DIV32(tXSDLL) << 0), &mctl_ctl->dramtmg[8]);
writel((MCTL_DIV32(tREFI) << 16) | (MCTL_DIV2(tRFC) << 0),
&mctl_ctl->rfshtmg);
if (para->dram_type == DRAM_TYPE_DDR3) {
writel((2 << 24) | ((MCTL_DIV2(CL) - 2) << 16) |
(1 << 8) | ((MCTL_DIV2(CWL) - 2) << 0),
&mctl_ctl->dfitmg[0]);
} else {
/* TODO */
}
/* TODO: handle the case of the write latency domain going to 0 ... */
/*
* Disable dfi_init_complete_en (the triggering of the SDRAM
* initialisation when the PHY initialisation completes).
*/
clrbits_le32(&mctl_ctl->dfimisc, MCTL_DFIMISC_DFI_INIT_COMPLETE_EN);
/* Disable the automatic generation of DLL calibration requests */
setbits_le32(&mctl_ctl->dfiupd[0], MCTL_DFIUPD0_DIS_AUTO_CTRLUPD);
/* A80-Q7: 2T, 1 rank, DDR3, full-32bit-DQ */
/* TODO: make 2T and BUSWIDTH configurable */
writel(MCTL_MSTR_DEVICETYPE(para->dram_type) |
MCTL_MSTR_BURSTLENGTH(para->dram_type) |
MCTL_MSTR_ACTIVERANKS(para->rank) |
MCTL_MSTR_2TMODE | MCTL_MSTR_BUSWIDTH32,
&mctl_ctl->mstr);
if (para->dram_type == DRAM_TYPE_DDR3) {
writel(MCTL_ZQCTRL0_TZQCL(MCTL_DIV2(tZQoper)) |
(MCTL_DIV2(tZQCS)), &mctl_ctl->zqctrl[0]);
/*
* TODO: is the following really necessary as the bottom
* half should already be 0x100 and the upper half should
* be ignored for a DDR3 device???
*/
writel(MCTL_ZQCTRL1_TZQSI_x1024(0x100),
&mctl_ctl->zqctrl[1]);
} else {
writel(MCTL_ZQCTRL0_TZQCL(0x200) | MCTL_ZQCTRL0_TZQCS(0x40),
&mctl_ctl->zqctrl[0]);
writel(MCTL_ZQCTRL1_TZQRESET(0x28) |
MCTL_ZQCTRL1_TZQSI_x1024(0x100),
&mctl_ctl->zqctrl[1]);
}
/* Assert dfi_init_complete signal */
setbits_le32(&mctl_ctl->dfimisc, MCTL_DFIMISC_DFI_INIT_COMPLETE_EN);
/* Disable auto-refresh */
setbits_le32(&mctl_ctl->rfshctl3, MCTL_RFSHCTL3_DIS_AUTO_REFRESH);
/* PHY initialisation */
/* TODO: make 2T and 8-bank mode configurable */
writel(MCTL_PHY_DCR_BYTEMASK | MCTL_PHY_DCR_2TMODE |
MCTL_PHY_DCR_DDR8BNK | MCTL_PHY_DRAMMODE_DDR3,
&mctl_phy->dcr);
/* For LPDDR2 or LPDDR3, set DQSGX to 0 before training. */
if (para->dram_type != DRAM_TYPE_DDR3)
clrbits_le32(&mctl_phy->dsgcr, (3 << 6));
writel(mr[0], &mctl_phy->mr0);
writel(mr[1], &mctl_phy->mr1);
writel(mr[2], &mctl_phy->mr2);
writel(mr[3], &mctl_phy->mr3);
/*
* The DFI PHY is running at full rate. We thus use the actual
* timings in clock cycles here.
*/
writel((tRC << 26) | (tRRD << 22) | (tRAS << 16) |
(tRCD << 12) | (tRP << 8) | (tWTR << 4) | (tRTP << 0),
&mctl_phy->dtpr[0]);
writel((tMRD << 0) | ((tMOD - 12) << 2) | (tFAW << 5) |
(tRFC << 11) | (tWLMRD << 20) | (tWLO << 26),
&mctl_phy->dtpr[1]);
writel((tXS << 0) | (MAX(tXP, tXPDLL) << 10) |
(tCKE << 15) | (tDLLK << 19) |
(MCTL_PHY_TRTODT << 29) | (MCTL_PHY_TRTW << 30) |
(((tCCD - 4) & 0x1) << 31),
&mctl_phy->dtpr[2]);
/* tDQSCK and tDQSCKmax are used LPDDR2/LPDDR3 */
/* writel((tDQSCK << 0) | (tDQSCKMAX << 3), &mctl_phy->dtpr[3]); */
/*
* We use the same values used by Allwinner's Boot0 for the PTR
* (PHY timing register) configuration that is tied to the PHY
* implementation.
*/
writel(0x42C21590, &mctl_phy->ptr[0]);
writel(0xD05612C0, &mctl_phy->ptr[1]);
if (para->dram_type == DRAM_TYPE_DDR3) {
const unsigned int tdinit0 = 500 * CONFIG_DRAM_CLK; /* 500us */
const unsigned int tdinit1 = (360 * CONFIG_DRAM_CLK + 999) /
1000; /* 360ns */
const unsigned int tdinit2 = 200 * CONFIG_DRAM_CLK; /* 200us */
const unsigned int tdinit3 = CONFIG_DRAM_CLK; /* 1us */
writel((tdinit1 << 20) | tdinit0, &mctl_phy->ptr[3]);
writel((tdinit3 << 18) | tdinit2, &mctl_phy->ptr[4]);
} else {
/* LPDDR2 or LPDDR3 */
const unsigned int tdinit0 = (100 * CONFIG_DRAM_CLK + 999) /
1000; /* 100ns */
const unsigned int tdinit1 = 200 * CONFIG_DRAM_CLK; /* 200us */
const unsigned int tdinit2 = 22 * CONFIG_DRAM_CLK; /* 11us */
const unsigned int tdinit3 = 2 * CONFIG_DRAM_CLK; /* 2us */
writel((tdinit1 << 20) | tdinit0, &mctl_phy->ptr[3]);
writel((tdinit3 << 18) | tdinit2, &mctl_phy->ptr[4]);
}
/* TEST ME */
writel(0x00203131, &mctl_phy->acmdlr);
/* TODO: can we enable this for 2 ranks, even when we don't know yet */
writel(MCTL_DTCR_DEFAULT | MCTL_DTCR_RANKEN(para->rank),
&mctl_phy->dtcr);
/* TODO: half width */
debug("DX2GCR0 reset: 0x%x\n", readl(&mctl_phy->dx[2].gcr[0]));
writel(0x7C000285, &mctl_phy->dx[2].gcr[0]);
writel(0x7C000285, &mctl_phy->dx[3].gcr[0]);
clrsetbits_le32(&mctl_phy->zq[0].pr, 0xff,
(CONFIG_DRAM_ZQ >> 0) & 0xff); /* CK/CA */
clrsetbits_le32(&mctl_phy->zq[1].pr, 0xff,
(CONFIG_DRAM_ZQ >> 8) & 0xff); /* DX0/DX1 */
clrsetbits_le32(&mctl_phy->zq[2].pr, 0xff,
(CONFIG_DRAM_ZQ >> 16) & 0xff); /* DX2/DX3 */
/* TODO: make configurable & implement non-ODT path */
if (1) {
int lane;
for (lane = 0; lane < 4; ++lane) {
clrbits_le32(&mctl_phy->dx[lane].gcr[2], 0xffff);
clrbits_le32(&mctl_phy->dx[lane].gcr[3],
(0x3<<12) | (0x3<<4));
}
} else {
/* TODO: check */
int lane;
for (lane = 0; lane < 4; ++lane) {
clrsetbits_le32(&mctl_phy->dx[lane].gcr[2], 0xffff,
0xaaaa);
if (para->dram_type == DRAM_TYPE_DDR3)
setbits_le32(&mctl_phy->dx[lane].gcr[3],
(0x3<<12) | (0x3<<4));
else
setbits_le32(&mctl_phy->dx[lane].gcr[3],
0x00000012);
}
}
writel(0x04058D02, &mctl_phy->zq[0].cr); /* CK/CA */
writel(0x04058D02, &mctl_phy->zq[1].cr); /* DX0/DX1 */
writel(0x04058D02, &mctl_phy->zq[2].cr); /* DX2/DX3 */
/* Disable auto-refresh prior to data training */
setbits_le32(&mctl_ctl->rfshctl3, MCTL_RFSHCTL3_DIS_AUTO_REFRESH);
setbits_le32(&mctl_phy->dsgcr, 0xf << 24); /* unclear what this is... */
/* TODO: IODDRM (IO DDR-MODE) for DDR3L */
clrsetbits_le32(&mctl_phy->pgcr[1],
MCTL_PGCR1_ZCKSEL_MASK,
MCTL_PGCR1_IODDRM_DDR3 | MCTL_PGCR1_INHVT_EN);
setbits_le32(&mctl_phy->pllcr, 0x3 << 19); /* PLL frequency select */
/* TODO: single-channel PLL mode??? missing */
setbits_le32(&mctl_phy->pllcr,
MCTL_PLLGCR_PLL_BYPASS | MCTL_PLLGCR_PLL_POWERDOWN);
/* setbits_le32(&mctl_phy->pir, MCTL_PIR_PLL_BYPASS); included below */
/* Disable VT compensation */
clrbits_le32(&mctl_phy->pgcr[0], 0x3f);
/* TODO: "other" PLL mode ... 0x20000 seems to be the PLL Bypass */
if (para->dram_type == DRAM_TYPE_DDR3)
clrsetbits_le32(&mctl_phy->pir, MCTL_PIR_MASK, 0x20df3);
else
clrsetbits_le32(&mctl_phy->pir, MCTL_PIR_MASK, 0x2c573);
sdelay(10000); /* XXX necessary? */
/* Wait for the INIT bit to clear itself... */
while ((readl(&mctl_phy->pir) & MCTL_PIR_INIT) != MCTL_PIR_INIT) {
/* not done yet -- keep spinning */
debug("MCTL_PIR_INIT not set\n");
sdelay(1000);
/* TODO: implement timeout */
}
/* TODO: not used --- there's a "2rank debug" section here */
/* Original dram init code which may come in handy later
********************************************************
* LPDDR2 and LPDDR3 *
if ((para->dram_type) == 6 || (para->dram_type) == 7) {
reg_val = mctl_read_w(P0_DSGCR + ch_offset);
reg_val &= (~(0x3<<6)); * set DQSGX to 1 *
reg_val |= (0x1<<6); * dqs gate extend *
mctl_write_w(P0_DSGCR + ch_offset, reg_val);
dram_dbg("DQS Gate Extend Enable!\n", ch_index);
}
* Disable ZCAL after initial--for nand dma debug--20140330 by YSZ *
if (para->dram_tpr13 & (0x1<<31)) {
reg_val = mctl_read_w(P0_ZQ0CR + ch_offset);
reg_val |= (0x7<<11);
mctl_write_w(P0_ZQ0CR + ch_offset, reg_val);
}
********************************************************
*/
/*
* TODO: more 2-rank support
* (setting the "dqs gate delay to average between 2 rank")
*/
/* check if any errors are set */
if (readl(&mctl_phy->pgsr[0]) & MCTL_PGSR0_ERRORS) {
debug("Channel %d unavailable!\n", ch_index);
return 0;
} else{
/* initial OK */
debug("Channel %d OK!\n", ch_index);
/* return 1; */
}
while ((readl(&mctl_ctl->stat) & 0x1) != 0x1) {
debug("Waiting for INIT to be done (controller to come up into 'normal operating' mode\n");
sdelay(100000);
/* init not done */
/* TODO: implement time-out */
}
debug("done\n");
/* "DDR is controller by contoller" */
clrbits_le32(&mctl_phy->pgcr[3], (1 << 25));
/* TODO: is the following necessary? */
debug("DFIMISC before writing 0: 0x%x\n", readl(&mctl_ctl->dfimisc));
writel(0, &mctl_ctl->dfimisc);
/* Enable auto-refresh */
clrbits_le32(&mctl_ctl->rfshctl3, MCTL_RFSHCTL3_DIS_AUTO_REFRESH);
debug("channel_init complete\n");
return 1;
}
signed int DRAMC_get_dram_size(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
unsigned int reg_val;
unsigned int dram_size;
unsigned int temp;
reg_val = readl(&mctl_com->cr);
temp = (reg_val >> 8) & 0xf; /* page size code */
dram_size = (temp - 6); /* (1 << dram_size) * 512Bytes */
temp = (reg_val >> 4) & 0xf; /* row width code */
dram_size += (temp + 1); /* (1 << dram_size) * 512Bytes */
temp = (reg_val >> 2) & 0x3; /* bank number code */
dram_size += (temp + 2); /* (1 << dram_size) * 512Bytes */
temp = reg_val & 0x3; /* rank number code */
dram_size += temp; /* (1 << dram_size) * 512Bytes */
temp = (reg_val >> 19) & 0x1; /* channel number code */
dram_size += temp; /* (1 << dram_size) * 512Bytes */
dram_size = dram_size - 11; /* (1 << dram_size) MBytes */
return 1 << dram_size;
}
unsigned long sunxi_dram_init(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct dram_sun9i_cl_cwl_timing cl_cwl[] = {
{ .CL = 5, .CWL = 5, .tCKmin = 3000, .tCKmax = 3300 },
{ .CL = 6, .CWL = 5, .tCKmin = 2500, .tCKmax = 3300 },
{ .CL = 8, .CWL = 6, .tCKmin = 1875, .tCKmax = 2500 },
{ .CL = 10, .CWL = 7, .tCKmin = 1500, .tCKmax = 1875 },
{ .CL = 11, .CWL = 8, .tCKmin = 1250, .tCKmax = 1500 }
};
/* Set initial parameters, these get modified by the autodetect code */
struct dram_sun9i_para para = {
.dram_type = DRAM_TYPE_DDR3,
.bus_width = 32,
.chan = 2,
.rank = 1,
/* .rank = 2, */
.page_size = 4096,
/* .rows = 16, */
.rows = 15,
/* CL/CWL table for the speed bin */
.cl_cwl_table = cl_cwl,
.cl_cwl_numentries = sizeof(cl_cwl) /
sizeof(struct dram_sun9i_cl_cwl_timing),
/* timings */
.tREFI = 7800, /* 7.8us (up to 85 degC) */
.tRFC = 260, /* 260ns for 4GBit devices */
/* 350ns @ 8GBit */
.tRCD = 13750,
.tRP = 13750,
.tRC = 48750,
.tRAS = 35000,
.tDLLK = 512,
.tRTP = { .ck = 4, .ps = 7500 },
.tWTR = { .ck = 4, .ps = 7500 },
.tWR = 15,
.tMRD = 4,
.tMOD = { .ck = 12, .ps = 15000 },
.tCCD = 4,
.tRRD = { .ck = 4, .ps = 7500 },
.tFAW = 40,
/* calibration timing */
/* .tZQinit = { .ck = 512, .ps = 640000 }, */
.tZQoper = { .ck = 256, .ps = 320000 },
.tZQCS = { .ck = 64, .ps = 80000 },
/* reset timing */
/* .tXPR = { .ck = 5, .ps = 10000 }, */
/* self-refresh timings */
.tXS = { .ck = 5, .ps = 10000 },
.tXSDLL = 512,
.tCKSRE = { .ck = 5, .ps = 10000 },
.tCKSRX = { .ck = 5, .ps = 10000 },
/* power-down timings */
.tXP = { .ck = 3, .ps = 6000 },
.tXPDLL = { .ck = 10, .ps = 24000 },
.tCKE = { .ck = 3, .ps = 5000 },
/* write leveling timings */
.tWLMRD = 40,
/* .tWLDQSEN = 25, */
.tWLO = 7500,
/* .tWLOE = 2000, */
};
/*
* Disable A80 internal 240 ohm resistor.
*
* This code sequence is adapated from Allwinner's Boot0 (see
* https://github.com/allwinner-zh/bootloader.git), as there
* is no documentation for these two registers in the R_PRCM
* block.
*/
setbits_le32(SUNXI_PRCM_BASE + 0x1e0, (0x3 << 8));
writel(0, SUNXI_PRCM_BASE + 0x1e8);
mctl_sys_init();
if (!mctl_channel_init(0, &para))
return 0;
/* dual-channel */
if (!mctl_channel_init(1, &para)) {
/* disable channel 1 */
clrsetbits_le32(&mctl_com->cr, MCTL_CR_CHANNEL_MASK,
MCTL_CR_CHANNEL_SINGLE);
/* disable channel 1 global clock */
clrbits_le32(&mctl_com->cr, MCTL_CCR_CH1_CLK_EN);
}
mctl_com_init(&para);
/* return the proper RAM size */
return DRAMC_get_dram_size() << 20;
}
@@ -0,0 +1,767 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* sun8i H3 platform dram controller init
*
* (C) Copyright 2007-2015 Allwinner Technology Co.
* Jerry Wang <wangflord@allwinnertech.com>
* (C) Copyright 2015 Vishnu Patekar <vishnupatekar0510@gmail.com>
* (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
* (C) Copyright 2015 Jens Kuske <jenskuske@gmail.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
#include <linux/kconfig.h>
static void mctl_phy_init(u32 val)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
writel(val | PIR_INIT, &mctl_ctl->pir);
mctl_await_completion(&mctl_ctl->pgsr[0], PGSR_INIT_DONE, 0x1);
}
static void mctl_set_bit_delays(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
int i, j;
clrbits_le32(&mctl_ctl->pgcr[0], 1 << 26);
for (i = 0; i < NR_OF_BYTE_LANES; i++)
for (j = 0; j < LINES_PER_BYTE_LANE; j++)
writel(DXBDLR_WRITE_DELAY(para->dx_write_delays[i][j]) |
DXBDLR_READ_DELAY(para->dx_read_delays[i][j]),
&mctl_ctl->dx[i].bdlr[j]);
for (i = 0; i < 31; i++)
writel(ACBDLR_WRITE_DELAY(para->ac_delays[i]),
&mctl_ctl->acbdlr[i]);
#ifdef CONFIG_MACH_SUN8I_R40
/* DQSn, DMn, DQn output enable bit delay */
for (i = 0; i < 4; i++)
writel(0x6 << 24, &mctl_ctl->dx[i].sdlr);
#endif
setbits_le32(&mctl_ctl->pgcr[0], 1 << 26);
}
enum {
MBUS_PORT_CPU = 0,
MBUS_PORT_GPU = 1,
MBUS_PORT_UNUSED = 2,
MBUS_PORT_DMA = 3,
MBUS_PORT_VE = 4,
MBUS_PORT_CSI = 5,
MBUS_PORT_NAND = 6,
MBUS_PORT_SS = 7,
MBUS_PORT_TS = 8,
MBUS_PORT_DI = 9,
MBUS_PORT_DE = 10,
MBUS_PORT_DE_CFD = 11,
MBUS_PORT_UNKNOWN1 = 12,
MBUS_PORT_UNKNOWN2 = 13,
MBUS_PORT_UNKNOWN3 = 14,
};
enum {
MBUS_QOS_LOWEST = 0,
MBUS_QOS_LOW,
MBUS_QOS_HIGH,
MBUS_QOS_HIGHEST
};
inline void mbus_configure_port(u8 port,
bool bwlimit,
bool priority,
u8 qos, /* MBUS_QOS_LOWEST .. MBUS_QOS_HIGEST */
u8 waittime, /* 0 .. 0xf */
u8 acs, /* 0 .. 0xff */
u16 bwl0, /* 0 .. 0xffff, bandwidth limit in MB/s */
u16 bwl1,
u16 bwl2)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
const u32 cfg0 = ( (bwlimit ? (1 << 0) : 0)
| (priority ? (1 << 1) : 0)
| ((qos & 0x3) << 2)
| ((waittime & 0xf) << 4)
| ((acs & 0xff) << 8)
| (bwl0 << 16) );
const u32 cfg1 = ((u32)bwl2 << 16) | (bwl1 & 0xffff);
debug("MBUS port %d cfg0 %08x cfg1 %08x\n", port, cfg0, cfg1);
writel(cfg0, &mctl_com->mcr[port][0]);
writel(cfg1, &mctl_com->mcr[port][1]);
}
#define MBUS_CONF(port, bwlimit, qos, acs, bwl0, bwl1, bwl2) \
mbus_configure_port(MBUS_PORT_ ## port, bwlimit, false, \
MBUS_QOS_ ## qos, 0, acs, bwl0, bwl1, bwl2)
static void mctl_set_master_priority_h3(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
/* enable bandwidth limit windows and set windows size 1us */
writel((1 << 16) | (400 << 0), &mctl_com->bwcr);
/* set cpu high priority */
writel(0x00000001, &mctl_com->mapr);
MBUS_CONF( CPU, true, HIGHEST, 0, 512, 256, 128);
MBUS_CONF( GPU, true, HIGH, 0, 1536, 1024, 256);
MBUS_CONF(UNUSED, true, HIGHEST, 0, 512, 256, 96);
MBUS_CONF( DMA, true, HIGHEST, 0, 256, 128, 32);
MBUS_CONF( VE, true, HIGH, 0, 1792, 1600, 256);
MBUS_CONF( CSI, true, HIGHEST, 0, 256, 128, 32);
MBUS_CONF( NAND, true, HIGH, 0, 256, 128, 64);
MBUS_CONF( SS, true, HIGHEST, 0, 256, 128, 64);
MBUS_CONF( TS, true, HIGHEST, 0, 256, 128, 64);
MBUS_CONF( DI, true, HIGH, 0, 1024, 256, 64);
MBUS_CONF( DE, true, HIGHEST, 3, 8192, 6120, 1024);
MBUS_CONF(DE_CFD, true, HIGH, 0, 1024, 288, 64);
}
static void mctl_set_master_priority_a64(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
/* enable bandwidth limit windows and set windows size 1us */
writel(399, &mctl_com->tmr);
writel((1 << 16), &mctl_com->bwcr);
/* Port 2 is reserved per Allwinner's linux-3.10 source, yet they
* initialise it */
MBUS_CONF( CPU, true, HIGHEST, 0, 160, 100, 80);
MBUS_CONF( GPU, false, HIGH, 0, 1536, 1400, 256);
MBUS_CONF(UNUSED, true, HIGHEST, 0, 512, 256, 96);
MBUS_CONF( DMA, true, HIGH, 0, 256, 80, 100);
MBUS_CONF( VE, true, HIGH, 0, 1792, 1600, 256);
MBUS_CONF( CSI, true, HIGH, 0, 256, 128, 0);
MBUS_CONF( NAND, true, HIGH, 0, 256, 128, 64);
MBUS_CONF( SS, true, HIGHEST, 0, 256, 128, 64);
MBUS_CONF( TS, true, HIGHEST, 0, 256, 128, 64);
MBUS_CONF( DI, true, HIGH, 0, 1024, 256, 64);
MBUS_CONF( DE, true, HIGH, 2, 8192, 6144, 2048);
MBUS_CONF(DE_CFD, true, HIGH, 0, 1280, 144, 64);
writel(0x81000004, &mctl_com->mdfs_bwlr[2]);
}
static void mctl_set_master_priority_h5(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
/* enable bandwidth limit windows and set windows size 1us */
writel(399, &mctl_com->tmr);
writel((1 << 16), &mctl_com->bwcr);
/* set cpu high priority */
writel(0x00000001, &mctl_com->mapr);
/* Port 2 is reserved per Allwinner's linux-3.10 source, yet
* they initialise it */
MBUS_CONF( CPU, true, HIGHEST, 0, 300, 260, 150);
MBUS_CONF( GPU, true, HIGHEST, 0, 600, 400, 200);
MBUS_CONF(UNUSED, true, HIGHEST, 0, 512, 256, 96);
MBUS_CONF( DMA, true, HIGHEST, 0, 256, 128, 32);
MBUS_CONF( VE, true, HIGHEST, 0, 1900, 1500, 1000);
MBUS_CONF( CSI, true, HIGHEST, 0, 150, 120, 100);
MBUS_CONF( NAND, true, HIGH, 0, 256, 128, 64);
MBUS_CONF( SS, true, HIGHEST, 0, 256, 128, 64);
MBUS_CONF( TS, true, HIGHEST, 0, 256, 128, 64);
MBUS_CONF( DI, true, HIGH, 0, 1024, 256, 64);
MBUS_CONF( DE, true, HIGHEST, 3, 3400, 2400, 1024);
MBUS_CONF(DE_CFD, true, HIGHEST, 0, 600, 400, 200);
}
static void mctl_set_master_priority_r40(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
/* enable bandwidth limit windows and set windows size 1us */
writel(399, &mctl_com->tmr);
writel((1 << 16), &mctl_com->bwcr);
/* set cpu high priority */
writel(0x00000001, &mctl_com->mapr);
/* Port 2 is reserved per Allwinner's linux-3.10 source, yet
* they initialise it */
MBUS_CONF( CPU, true, HIGHEST, 0, 300, 260, 150);
MBUS_CONF( GPU, true, HIGHEST, 0, 600, 400, 200);
MBUS_CONF( UNUSED, true, HIGHEST, 0, 512, 256, 96);
MBUS_CONF( DMA, true, HIGHEST, 0, 256, 128, 32);
MBUS_CONF( VE, true, HIGHEST, 0, 1900, 1500, 1000);
MBUS_CONF( CSI, true, HIGHEST, 0, 150, 120, 100);
MBUS_CONF( NAND, true, HIGH, 0, 256, 128, 64);
MBUS_CONF( SS, true, HIGHEST, 0, 256, 128, 64);
MBUS_CONF( TS, true, HIGHEST, 0, 256, 128, 64);
MBUS_CONF( DI, true, HIGH, 0, 1024, 256, 64);
/*
* The port names are probably wrong, but no correct sources
* are available.
*/
MBUS_CONF( DE, true, HIGH, 0, 128, 48, 0);
MBUS_CONF( DE_CFD, true, HIGH, 0, 384, 256, 0);
MBUS_CONF(UNKNOWN1, true, HIGHEST, 0, 512, 384, 256);
MBUS_CONF(UNKNOWN2, true, HIGHEST, 2, 8192, 6144, 1024);
MBUS_CONF(UNKNOWN3, true, HIGH, 0, 1280, 144, 64);
}
static void mctl_set_master_priority(uint16_t socid)
{
switch (socid) {
case SOCID_H3:
mctl_set_master_priority_h3();
return;
case SOCID_A64:
mctl_set_master_priority_a64();
return;
case SOCID_H5:
mctl_set_master_priority_h5();
return;
case SOCID_R40:
mctl_set_master_priority_r40();
return;
}
}
static u32 bin_to_mgray(int val)
{
static const u8 lookup_table[32] = {
0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05,
0x0c, 0x0d, 0x0e, 0x0f, 0x0a, 0x0b, 0x08, 0x09,
0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x1f, 0x1c, 0x1d,
0x14, 0x15, 0x16, 0x17, 0x12, 0x13, 0x10, 0x11,
};
return lookup_table[clamp(val, 0, 31)];
}
static int mgray_to_bin(u32 val)
{
static const u8 lookup_table[32] = {
0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05,
0x0e, 0x0f, 0x0c, 0x0d, 0x08, 0x09, 0x0a, 0x0b,
0x1e, 0x1f, 0x1c, 0x1d, 0x18, 0x19, 0x1a, 0x1b,
0x10, 0x11, 0x12, 0x13, 0x16, 0x17, 0x14, 0x15,
};
return lookup_table[val & 0x1f];
}
static void mctl_h3_zq_calibration_quirk(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
int zq_count;
#if defined CONFIG_SUNXI_DRAM_DW_16BIT
zq_count = 4;
#else
zq_count = 6;
#endif
if ((readl(SUNXI_SRAMC_BASE + 0x24) & 0xff) == 0 &&
(readl(SUNXI_SRAMC_BASE + 0xf0) & 0x1) == 0) {
u32 reg_val;
clrsetbits_le32(&mctl_ctl->zqcr, 0xffff,
CONFIG_DRAM_ZQ & 0xffff);
writel(PIR_CLRSR, &mctl_ctl->pir);
mctl_phy_init(PIR_ZCAL);
reg_val = readl(&mctl_ctl->zqdr[0]);
reg_val &= (0x1f << 16) | (0x1f << 0);
reg_val |= reg_val << 8;
writel(reg_val, &mctl_ctl->zqdr[0]);
reg_val = readl(&mctl_ctl->zqdr[1]);
reg_val &= (0x1f << 16) | (0x1f << 0);
reg_val |= reg_val << 8;
writel(reg_val, &mctl_ctl->zqdr[1]);
writel(reg_val, &mctl_ctl->zqdr[2]);
} else {
int i;
u16 zq_val[6];
u8 val;
writel(0x0a0a0a0a, &mctl_ctl->zqdr[2]);
for (i = 0; i < zq_count; i++) {
u8 zq = (CONFIG_DRAM_ZQ >> (i * 4)) & 0xf;
writel((zq << 20) | (zq << 16) | (zq << 12) |
(zq << 8) | (zq << 4) | (zq << 0),
&mctl_ctl->zqcr);
writel(PIR_CLRSR, &mctl_ctl->pir);
mctl_phy_init(PIR_ZCAL);
zq_val[i] = readl(&mctl_ctl->zqdr[0]) & 0xff;
writel(REPEAT_BYTE(zq_val[i]), &mctl_ctl->zqdr[2]);
writel(PIR_CLRSR, &mctl_ctl->pir);
mctl_phy_init(PIR_ZCAL);
val = readl(&mctl_ctl->zqdr[0]) >> 24;
zq_val[i] |= bin_to_mgray(mgray_to_bin(val) - 1) << 8;
}
writel((zq_val[1] << 16) | zq_val[0], &mctl_ctl->zqdr[0]);
writel((zq_val[3] << 16) | zq_val[2], &mctl_ctl->zqdr[1]);
if (zq_count > 4)
writel((zq_val[5] << 16) | zq_val[4],
&mctl_ctl->zqdr[2]);
}
}
static void mctl_set_cr(uint16_t socid, struct dram_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
writel(MCTL_CR_BL8 | MCTL_CR_INTERLEAVED |
#if defined CONFIG_SUNXI_DRAM_DDR3
MCTL_CR_DDR3 | MCTL_CR_2T |
#elif defined CONFIG_SUNXI_DRAM_DDR2
MCTL_CR_DDR2 | MCTL_CR_2T |
#elif defined CONFIG_SUNXI_DRAM_LPDDR3
MCTL_CR_LPDDR3 | MCTL_CR_1T |
#else
#error Unsupported DRAM type!
#endif
(para->bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) |
MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) |
(para->dual_rank ? MCTL_CR_DUAL_RANK : MCTL_CR_SINGLE_RANK) |
MCTL_CR_PAGE_SIZE(para->page_size) |
MCTL_CR_ROW_BITS(para->row_bits), &mctl_com->cr);
if (socid == SOCID_R40) {
if (para->dual_rank)
panic("Dual rank memory not supported\n");
/* Mux pin to A15 address line for single rank memory. */
setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15);
}
}
static void mctl_sys_init(uint16_t socid, struct dram_para *para)
{
struct sunxi_ccm_reg * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
clrbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE);
clrbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
if (socid == SOCID_A64 || socid == SOCID_R40)
clrbits_le32(&ccm->pll11_cfg, CCM_PLL11_CTRL_EN);
udelay(10);
clrbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
udelay(1000);
if (socid == SOCID_A64 || socid == SOCID_R40) {
clock_set_pll11(CONFIG_DRAM_CLK * 2 * 1000000, false);
clrsetbits_le32(&ccm->dram_clk_cfg,
CCM_DRAMCLK_CFG_DIV_MASK |
CCM_DRAMCLK_CFG_SRC_MASK,
CCM_DRAMCLK_CFG_DIV(1) |
CCM_DRAMCLK_CFG_SRC_PLL11 |
CCM_DRAMCLK_CFG_UPD);
} else if (socid == SOCID_H3 || socid == SOCID_H5) {
clock_set_pll5(CONFIG_DRAM_CLK * 2 * 1000000, false);
clrsetbits_le32(&ccm->dram_clk_cfg,
CCM_DRAMCLK_CFG_DIV_MASK |
CCM_DRAMCLK_CFG_SRC_MASK,
CCM_DRAMCLK_CFG_DIV(1) |
CCM_DRAMCLK_CFG_SRC_PLL5 |
CCM_DRAMCLK_CFG_UPD);
}
mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0);
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
setbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET);
setbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE);
setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
udelay(10);
writel(socid == SOCID_H5 ? 0x8000 : 0xc00e, &mctl_ctl->clken);
udelay(500);
}
/* These are more guessed based on some Allwinner code. */
#define DX_GCR_ODT_DYNAMIC (0x0 << 4)
#define DX_GCR_ODT_ALWAYS_ON (0x1 << 4)
#define DX_GCR_ODT_OFF (0x2 << 4)
static int mctl_channel_init(uint16_t socid, struct dram_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
unsigned int i;
mctl_set_cr(socid, para);
mctl_set_timing_params(socid, para);
mctl_set_master_priority(socid);
/* setting VTC, default disable all VT */
clrbits_le32(&mctl_ctl->pgcr[0], (1 << 30) | 0x3f);
if (socid == SOCID_H5)
setbits_le32(&mctl_ctl->pgcr[1], (1 << 24) | (1 << 26));
else
clrsetbits_le32(&mctl_ctl->pgcr[1], 1 << 24, 1 << 26);
/* increase DFI_PHY_UPD clock */
writel(PROTECT_MAGIC, &mctl_com->protect);
udelay(100);
clrsetbits_le32(&mctl_ctl->upd2, 0xfff << 16, 0x50 << 16);
writel(0x0, &mctl_com->protect);
udelay(100);
/* set dramc odt */
for (i = 0; i < 4; i++) {
u32 clearmask = (0x3 << 4) | (0x1 << 1) | (0x3 << 2) |
(0x3 << 12) | (0x3 << 14);
u32 setmask = IS_ENABLED(CONFIG_DRAM_ODT_EN) ?
DX_GCR_ODT_DYNAMIC : DX_GCR_ODT_OFF;
if (socid == SOCID_H5) {
clearmask |= 0x2 << 8;
setmask |= 0x4 << 8;
}
clrsetbits_le32(&mctl_ctl->dx[i].gcr, clearmask, setmask);
}
/* AC PDR should always ON */
clrsetbits_le32(&mctl_ctl->aciocr, socid == SOCID_H5 ? (0x1 << 11) : 0,
0x1 << 1);
/* set DQS auto gating PD mode */
setbits_le32(&mctl_ctl->pgcr[2], 0x3 << 6);
if (socid == SOCID_H3) {
/* dx ddr_clk & hdr_clk dynamic mode */
clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12));
/* dphy & aphy phase select 270 degree */
clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
(0x1 << 10) | (0x2 << 8));
} else if (socid == SOCID_A64 || socid == SOCID_H5) {
/* dphy & aphy phase select ? */
clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
(0x0 << 10) | (0x3 << 8));
} else if (socid == SOCID_R40) {
/* dx ddr_clk & hdr_clk dynamic mode (tpr13[9] == 0) */
clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12));
/* dphy & aphy phase select ? */
clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
(0x0 << 10) | (0x3 << 8));
}
/* set half DQ */
if (!para->bus_full_width) {
#if defined CONFIG_SUNXI_DRAM_DW_32BIT
writel(0x0, &mctl_ctl->dx[2].gcr);
writel(0x0, &mctl_ctl->dx[3].gcr);
#elif defined CONFIG_SUNXI_DRAM_DW_16BIT
writel(0x0, &mctl_ctl->dx[1].gcr);
#else
#error Unsupported DRAM bus width!
#endif
}
/* data training configuration */
clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24,
(para->dual_rank ? 0x3 : 0x1) << 24);
mctl_set_bit_delays(para);
udelay(50);
if (socid == SOCID_H3) {
mctl_h3_zq_calibration_quirk(para);
mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE);
} else if (socid == SOCID_A64 || socid == SOCID_H5) {
clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, CONFIG_DRAM_ZQ);
mctl_phy_init(PIR_ZCAL | PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE);
/* no PIR_QSGATE for H5 ???? */
} else if (socid == SOCID_R40) {
clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, CONFIG_DRAM_ZQ);
mctl_phy_init(PIR_ZCAL | PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
PIR_DRAMRST | PIR_DRAMINIT);
}
/* detect ranks and bus width */
if (readl(&mctl_ctl->pgsr[0]) & (0xfe << 20)) {
/* only one rank */
if (((readl(&mctl_ctl->dx[0].gsr[0]) >> 24) & 0x2)
#if defined CONFIG_SUNXI_DRAM_DW_32BIT
|| ((readl(&mctl_ctl->dx[1].gsr[0]) >> 24) & 0x2)
#endif
) {
clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24, 0x1 << 24);
para->dual_rank = 0;
}
/* only half DQ width */
#if defined CONFIG_SUNXI_DRAM_DW_32BIT
if (((readl(&mctl_ctl->dx[2].gsr[0]) >> 24) & 0x1) ||
((readl(&mctl_ctl->dx[3].gsr[0]) >> 24) & 0x1)) {
writel(0x0, &mctl_ctl->dx[2].gcr);
writel(0x0, &mctl_ctl->dx[3].gcr);
para->bus_full_width = 0;
}
#elif defined CONFIG_SUNXI_DRAM_DW_16BIT
if ((readl(&mctl_ctl->dx[1].gsr[0]) >> 24) & 0x1) {
writel(0x0, &mctl_ctl->dx[1].gcr);
para->bus_full_width = 0;
}
#endif
mctl_set_cr(socid, para);
udelay(20);
/* re-train */
mctl_phy_init(PIR_QSGATE);
if (readl(&mctl_ctl->pgsr[0]) & (0xfe << 20))
return 1;
}
/* check the dramc status */
mctl_await_completion(&mctl_ctl->statr, 0x1, 0x1);
/* liuke added for refresh debug */
setbits_le32(&mctl_ctl->rfshctl0, 0x1 << 31);
udelay(10);
clrbits_le32(&mctl_ctl->rfshctl0, 0x1 << 31);
udelay(10);
/* set PGCR3, CKE polarity */
if (socid == SOCID_H3)
writel(0x00aa0060, &mctl_ctl->pgcr[3]);
else if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40)
writel(0xc0aa0060, &mctl_ctl->pgcr[3]);
/* power down zq calibration module for power save */
setbits_le32(&mctl_ctl->zqcr, ZQCR_PWRDOWN);
/* enable master access */
writel(0xffffffff, &mctl_com->maer);
return 0;
}
static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para)
{
/* detect row address bits */
para->page_size = 512;
para->row_bits = 16;
para->bank_bits = 2;
mctl_set_cr(socid, para);
for (para->row_bits = 11; para->row_bits < 16; para->row_bits++)
if (mctl_mem_matches((1 << (para->row_bits + para->bank_bits)) * para->page_size))
break;
/* detect bank address bits */
para->bank_bits = 3;
mctl_set_cr(socid, para);
for (para->bank_bits = 2; para->bank_bits < 3; para->bank_bits++)
if (mctl_mem_matches((1 << para->bank_bits) * para->page_size))
break;
/* detect page size */
para->page_size = 8192;
mctl_set_cr(socid, para);
for (para->page_size = 512; para->page_size < 8192; para->page_size *= 2)
if (mctl_mem_matches(para->page_size))
break;
}
/*
* The actual values used here are taken from Allwinner provided boot0
* binaries, though they are probably board specific, so would likely benefit
* from invidual tuning for each board. Apparently a lot of boards copy from
* some Allwinner reference design, so we go with those generic values for now
* in the hope that they are reasonable for most (all?) boards.
*/
#define SUN8I_H3_DX_READ_DELAYS \
{{ 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0 }, \
{ 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \
{ 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0 }, \
{ 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }}
#define SUN8I_H3_DX_WRITE_DELAYS \
{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6 }}
#define SUN8I_H3_AC_DELAYS \
{ 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0 }
#define SUN8I_R40_DX_READ_DELAYS \
{{ 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \
{ 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \
{ 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \
{ 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 } }
#define SUN8I_R40_DX_WRITE_DELAYS \
{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 }, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 } }
#define SUN8I_R40_AC_DELAYS \
{ 0, 0, 3, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0 }
#define SUN50I_A64_DX_READ_DELAYS \
{{ 16, 16, 16, 16, 17, 16, 16, 17, 16, 1, 0 }, \
{ 17, 17, 17, 17, 17, 17, 17, 17, 17, 1, 0 }, \
{ 16, 17, 17, 16, 16, 16, 16, 16, 16, 0, 0 }, \
{ 17, 17, 17, 17, 17, 17, 17, 17, 17, 1, 0 }}
#define SUN50I_A64_DX_WRITE_DELAYS \
{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15 }, \
{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 10, 10 }, \
{ 1, 0, 1, 1, 1, 1, 1, 1, 0, 11, 11 }, \
{ 1, 0, 0, 1, 1, 1, 1, 1, 0, 12, 12 }}
#define SUN50I_A64_AC_DELAYS \
{ 5, 5, 13, 10, 2, 5, 3, 3, \
0, 3, 3, 3, 1, 0, 0, 0, \
3, 4, 0, 3, 4, 1, 4, 0, \
1, 1, 0, 1, 13, 5, 4 }
#define SUN8I_H5_DX_READ_DELAYS \
{{ 14, 15, 17, 17, 17, 17, 17, 18, 17, 3, 3 }, \
{ 21, 21, 12, 22, 21, 21, 21, 21, 21, 3, 3 }, \
{ 16, 19, 19, 17, 22, 22, 21, 22, 19, 3, 3 }, \
{ 21, 21, 22, 22, 20, 21, 19, 19, 19, 3, 3 } }
#define SUN8I_H5_DX_WRITE_DELAYS \
{{ 1, 2, 3, 4, 3, 4, 4, 4, 6, 6, 6 }, \
{ 6, 6, 6, 5, 5, 5, 5, 5, 6, 6, 6 }, \
{ 0, 2, 4, 2, 6, 5, 5, 5, 6, 6, 6 }, \
{ 3, 3, 3, 2, 2, 1, 1, 1, 4, 4, 4 } }
#define SUN8I_H5_AC_DELAYS \
{ 0, 0, 5, 5, 0, 0, 0, 0, \
0, 0, 0, 0, 3, 3, 3, 3, \
3, 3, 3, 3, 3, 3, 3, 3, \
3, 3, 3, 3, 2, 0, 0 }
unsigned long sunxi_dram_init(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct dram_para para = {
.dual_rank = 1,
.bus_full_width = 1,
.row_bits = 15,
.bank_bits = 3,
.page_size = 4096,
#if defined(CONFIG_MACH_SUN8I_H3)
.dx_read_delays = SUN8I_H3_DX_READ_DELAYS,
.dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS,
.ac_delays = SUN8I_H3_AC_DELAYS,
#elif defined(CONFIG_MACH_SUN8I_R40)
.dx_read_delays = SUN8I_R40_DX_READ_DELAYS,
.dx_write_delays = SUN8I_R40_DX_WRITE_DELAYS,
.ac_delays = SUN8I_R40_AC_DELAYS,
#elif defined(CONFIG_MACH_SUN50I)
.dx_read_delays = SUN50I_A64_DX_READ_DELAYS,
.dx_write_delays = SUN50I_A64_DX_WRITE_DELAYS,
.ac_delays = SUN50I_A64_AC_DELAYS,
#elif defined(CONFIG_MACH_SUN50I_H5)
.dx_read_delays = SUN8I_H5_DX_READ_DELAYS,
.dx_write_delays = SUN8I_H5_DX_WRITE_DELAYS,
.ac_delays = SUN8I_H5_AC_DELAYS,
#endif
};
/*
* Let the compiler optimize alternatives away by passing this value into
* the static functions. This saves us #ifdefs, but still keeps the binary
* small.
*/
#if defined(CONFIG_MACH_SUN8I_H3)
uint16_t socid = SOCID_H3;
#elif defined(CONFIG_MACH_SUN8I_R40)
uint16_t socid = SOCID_R40;
/* Currently we cannot support R40 with dual rank memory */
para.dual_rank = 0;
#elif defined(CONFIG_MACH_SUN8I_V3S)
/* TODO: set delays and mbus priority for V3s */
uint16_t socid = SOCID_H3;
#elif defined(CONFIG_MACH_SUN50I)
uint16_t socid = SOCID_A64;
#elif defined(CONFIG_MACH_SUN50I_H5)
uint16_t socid = SOCID_H5;
#endif
mctl_sys_init(socid, &para);
if (mctl_channel_init(socid, &para))
return 0;
if (para.dual_rank)
writel(0x00000303, &mctl_ctl->odtmap);
else
writel(0x00000201, &mctl_ctl->odtmap);
udelay(1);
/* odt delay */
if (socid == SOCID_H3)
writel(0x0c000400, &mctl_ctl->odtcfg);
if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40) {
/* VTF enable (tpr13[8] == 1) */
setbits_le32(&mctl_ctl->vtfcr,
(socid != SOCID_A64 ? 3 : 2) << 8);
/* DQ hold disable (tpr13[26] == 1) */
clrbits_le32(&mctl_ctl->pgcr[2], (1 << 13));
}
/* clear credit value */
setbits_le32(&mctl_com->cccr, 1 << 31);
udelay(10);
mctl_auto_detect_dram_size(socid, &para);
mctl_set_cr(socid, &para);
return (1UL << (para.row_bits + para.bank_bits)) * para.page_size *
(para.dual_rank ? 2 : 1);
}
@@ -0,0 +1,5 @@
obj-$(CONFIG_SUNXI_DRAM_DDR3_1333) += ddr3_1333.o
obj-$(CONFIG_SUNXI_DRAM_LPDDR3_STOCK) += lpddr3_stock.o
obj-$(CONFIG_SUNXI_DRAM_DDR2_V3S) += ddr2_v3s.o
obj-$(CONFIG_SUNXI_DRAM_H6_LPDDR3) += h6_lpddr3.o
obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o
@@ -0,0 +1,84 @@
#include <common.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
void mctl_set_timing_params(uint16_t socid, struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
u8 tccd = 1;
u8 tfaw = ns_to_t(50);
u8 trrd = max(ns_to_t(10), 2);
u8 trcd = ns_to_t(20);
u8 trc = ns_to_t(65);
u8 txp = 2;
u8 twtr = max(ns_to_t(8), 2);
u8 trtp = max(ns_to_t(8), 2);
u8 twr = max(ns_to_t(15), 3);
u8 trp = ns_to_t(15);
u8 tras = ns_to_t(45);
u16 trefi = ns_to_t(7800) / 32;
u16 trfc = ns_to_t(328);
u8 tmrw = 0;
u8 tmrd = 2;
u8 tmod = 12;
u8 tcke = 3;
u8 tcksrx = 5;
u8 tcksre = 5;
u8 tckesr = 4;
u8 trasmax = 27;
u8 tcl = 3; /* CL 6 */
u8 tcwl = 3; /* CWL 6 */
u8 t_rdata_en = 1;
u8 wr_latency = 1;
u32 tdinit0 = (400 * CONFIG_DRAM_CLK) + 1; /* 400us */
u32 tdinit1 = (500 * CONFIG_DRAM_CLK) / 1000 + 1; /* 500ns */
u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
u8 twtp = tcwl + 2 + twr; /* WL + BL / 2 + tWR */
u8 twr2rd = tcwl + 2 + twtr; /* WL + BL / 2 + tWTR */
u8 trd2wr = tcl + 2 + 1 - tcwl; /* RL + BL / 2 + 2 - WL */
/* set mode register */
writel(0x263, &mctl_ctl->mr[0]);
writel(0x4, &mctl_ctl->mr[1]);
writel(0x0, &mctl_ctl->mr[2]);
writel(0x0, &mctl_ctl->mr[3]);
/* set DRAM timing */
writel(DRAMTMG0_TWTP(twtp) | DRAMTMG0_TFAW(tfaw) |
DRAMTMG0_TRAS_MAX(trasmax) | DRAMTMG0_TRAS(tras),
&mctl_ctl->dramtmg[0]);
writel(DRAMTMG1_TXP(txp) | DRAMTMG1_TRTP(trtp) | DRAMTMG1_TRC(trc),
&mctl_ctl->dramtmg[1]);
writel(DRAMTMG2_TCWL(tcwl) | DRAMTMG2_TCL(tcl) |
DRAMTMG2_TRD2WR(trd2wr) | DRAMTMG2_TWR2RD(twr2rd),
&mctl_ctl->dramtmg[2]);
writel(DRAMTMG3_TMRW(tmrw) | DRAMTMG3_TMRD(tmrd) | DRAMTMG3_TMOD(tmod),
&mctl_ctl->dramtmg[3]);
writel(DRAMTMG4_TRCD(trcd) | DRAMTMG4_TCCD(tccd) | DRAMTMG4_TRRD(trrd) |
DRAMTMG4_TRP(trp), &mctl_ctl->dramtmg[4]);
writel(DRAMTMG5_TCKSRX(tcksrx) | DRAMTMG5_TCKSRE(tcksre) |
DRAMTMG5_TCKESR(tckesr) | DRAMTMG5_TCKE(tcke),
&mctl_ctl->dramtmg[5]);
/* set two rank timing */
clrsetbits_le32(&mctl_ctl->dramtmg[8], (0xff << 8) | (0xff << 0),
(0x66 << 8) | (0x10 << 0));
/* set PHY interface timing, write latency and read latency configure */
writel((0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) |
(wr_latency << 0), &mctl_ctl->pitmg[0]);
/* set PHY timing, PTR0-2 use default */
writel(PTR3_TDINIT0(tdinit0) | PTR3_TDINIT1(tdinit1), &mctl_ctl->ptr[3]);
writel(PTR4_TDINIT2(tdinit2) | PTR4_TDINIT3(tdinit3), &mctl_ctl->ptr[4]);
/* set refresh timing */
writel(RFSHTMG_TREFI(trefi) | RFSHTMG_TRFC(trfc), &mctl_ctl->rfshtmg);
}
@@ -0,0 +1,87 @@
#include <common.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
void mctl_set_timing_params(uint16_t socid, struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
u8 tccd = 2;
u8 tfaw = ns_to_t(50);
u8 trrd = max(ns_to_t(10), 4);
u8 trcd = ns_to_t(15);
u8 trc = ns_to_t(53);
u8 txp = max(ns_to_t(8), 3);
u8 twtr = max(ns_to_t(8), 4);
u8 trtp = max(ns_to_t(8), 4);
u8 twr = max(ns_to_t(15), 3);
u8 trp = ns_to_t(15);
u8 tras = ns_to_t(38);
u16 trefi = ns_to_t(7800) / 32;
u16 trfc = ns_to_t(350);
u8 tmrw = 0;
u8 tmrd = 4;
u8 tmod = 12;
u8 tcke = 3;
u8 tcksrx = 5;
u8 tcksre = 5;
u8 tckesr = 4;
u8 trasmax = 24;
u8 tcl = 6; /* CL 12 */
u8 tcwl = 4; /* CWL 8 */
u8 t_rdata_en = 4;
u8 wr_latency = 2;
u32 tdinit0 = (500 * CONFIG_DRAM_CLK) + 1; /* 500us */
u32 tdinit1 = (360 * CONFIG_DRAM_CLK) / 1000 + 1; /* 360ns */
u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
u8 twtp = tcwl + 2 + twr; /* WL + BL / 2 + tWR */
u8 twr2rd = tcwl + 2 + twtr; /* WL + BL / 2 + tWTR */
u8 trd2wr = tcl + 2 + 1 - tcwl; /* RL + BL / 2 + 2 - WL */
/* set mode register */
writel(0x1c70, &mctl_ctl->mr[0]); /* CL=11, WR=12 */
writel(0x40, &mctl_ctl->mr[1]);
writel(0x18, &mctl_ctl->mr[2]); /* CWL=8 */
writel(0x0, &mctl_ctl->mr[3]);
if (socid == SOCID_R40)
writel(0x3, &mctl_ctl->lp3mr11); /* odt_en[7:4] */
/* set DRAM timing */
writel(DRAMTMG0_TWTP(twtp) | DRAMTMG0_TFAW(tfaw) |
DRAMTMG0_TRAS_MAX(trasmax) | DRAMTMG0_TRAS(tras),
&mctl_ctl->dramtmg[0]);
writel(DRAMTMG1_TXP(txp) | DRAMTMG1_TRTP(trtp) | DRAMTMG1_TRC(trc),
&mctl_ctl->dramtmg[1]);
writel(DRAMTMG2_TCWL(tcwl) | DRAMTMG2_TCL(tcl) |
DRAMTMG2_TRD2WR(trd2wr) | DRAMTMG2_TWR2RD(twr2rd),
&mctl_ctl->dramtmg[2]);
writel(DRAMTMG3_TMRW(tmrw) | DRAMTMG3_TMRD(tmrd) | DRAMTMG3_TMOD(tmod),
&mctl_ctl->dramtmg[3]);
writel(DRAMTMG4_TRCD(trcd) | DRAMTMG4_TCCD(tccd) | DRAMTMG4_TRRD(trrd) |
DRAMTMG4_TRP(trp), &mctl_ctl->dramtmg[4]);
writel(DRAMTMG5_TCKSRX(tcksrx) | DRAMTMG5_TCKSRE(tcksre) |
DRAMTMG5_TCKESR(tckesr) | DRAMTMG5_TCKE(tcke),
&mctl_ctl->dramtmg[5]);
/* set two rank timing */
clrsetbits_le32(&mctl_ctl->dramtmg[8], (0xff << 8) | (0xff << 0),
((socid == SOCID_H5 ? 0x33 : 0x66) << 8) | (0x10 << 0));
/* set PHY interface timing, write latency and read latency configure */
writel((0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) |
(wr_latency << 0), &mctl_ctl->pitmg[0]);
/* set PHY timing, PTR0-2 use default */
writel(PTR3_TDINIT0(tdinit0) | PTR3_TDINIT1(tdinit1), &mctl_ctl->ptr[3]);
writel(PTR4_TDINIT2(tdinit2) | PTR4_TDINIT3(tdinit3), &mctl_ctl->ptr[4]);
/* set refresh timing */
writel(RFSHTMG_TREFI(trefi) | RFSHTMG_TRFC(trfc), &mctl_ctl->rfshtmg);
}
@@ -0,0 +1,144 @@
/*
* sun50i H6 DDR3-1333 timings, as programmed by Allwinner's boot0
* for some TV boxes with the H6 and DDR3 memory.
*
* The chips are probably able to be driven by a faster clock, but boot0
* uses a more conservative timing (as usual).
*
* (C) Copyright 2018,2019 Arm Ltd.
* based on previous work by:
* (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io>
*
* References used:
* - JEDEC DDR3 SDRAM standard: JESD79-3F.pdf
* - Samsung K4B2G0446D datasheet
* - ZynqMP UG1087 register DDRC/PHY documentation
*
* Many thanks to Jernej Skrabec for contributing some fixes!
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
/*
* Only the first four are used for DDR3(?)
* MR0: BL8, seq. read burst, no test, fast exit (DLL on), no DLL reset,
* CAS latency (CL): 11, write recovery (WR): 12
* MR1: DLL enabled, output strength RZQ/6, Rtt_norm RZQ/2,
* write levelling disabled, TDQS disabled, output buffer enabled
* MR2: manual full array self refresh, dynamic ODT off,
* CAS write latency (CWL): 8
*/
static u32 mr_ddr3[7] = {
0x00001c70, 0x00000040, 0x00000018, 0x00000000,
0x00000000, 0x00000400, 0x00000848,
};
/* TODO: flexible timing */
void mctl_set_timing_params(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
int i;
u8 tccd = 2; /* JEDEC: 4nCK */
u8 tfaw = ns_to_t(50); /* JEDEC: 30 ns w/ 1K pages */
u8 trrd = max(ns_to_t(6), 4); /* JEDEC: max(6 ns, 4nCK) */
u8 trcd = ns_to_t(15); /* JEDEC: 13.5 ns */
u8 trc = ns_to_t(53); /* JEDEC: 49.5 ns */
u8 txp = max(ns_to_t(6), 3); /* JEDEC: max(6 ns, 3nCK) */
u8 twtr = max(ns_to_t(8), 2); /* JEDEC: max(7.5 ns, 4nCK) */
u8 trtp = max(ns_to_t(8), 2); /* JEDEC: max(7.5 ns, 4nCK) */
u8 twr = ns_to_t(15); /* JEDEC: 15 ns */
u8 trp = ns_to_t(15); /* JEDEC: >= 13.75 ns */
u8 tras = ns_to_t(38); /* JEDEC >= 36 ns, <= 9*trefi */
u8 twtr_sa = 2; /* ? */
u8 tcksrea = 4; /* ? */
u16 trefi = ns_to_t(7800) / 32; /* JEDEC: 7.8us@Tcase <= 85C */
u16 trfc = ns_to_t(350); /* JEDEC: 160 ns for 2Gb */
u16 txsr = 4; /* ? */
u8 tmrw = 0; /* ? */
u8 tmrd = 4; /* JEDEC: 4nCK */
u8 tmod = max(ns_to_t(15), 12); /* JEDEC: max(15 ns, 12nCK) */
u8 tcke = max(ns_to_t(6), 3); /* JEDEC: max(5.625 ns, 3nCK) */
u8 tcksrx = max(ns_to_t(10), 5); /* JEDEC: max(10 ns, 5nCK) */
u8 tcksre = max(ns_to_t(10), 5); /* JEDEC: max(10 ns, 5nCK) */
u8 tckesr = tcke + 1; /* JEDEC: tCKE(min) + 1nCK */
u8 trasmax = 24; /* JEDEC: tREFI * 9 */
u8 txs = ns_to_t(360) / 32; /* JEDEC: max(5nCK,tRFC+10ns) */
u8 txsdll = 4; /* JEDEC: 512 nCK */
u8 txsabort = 4; /* ? */
u8 txsfast = 4; /* ? */
u8 tcl = 6; /* JEDEC: CL / 2 => 6 */
u8 tcwl = 4; /* JEDEC: 8 */
u8 t_rdata_en = 7; /* ? */
u32 tdinit0 = (500 * CONFIG_DRAM_CLK) + 1; /* 500us */
u32 tdinit1 = (360 * CONFIG_DRAM_CLK) / 1000 + 1;
u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1;
u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
u8 twtp = tcwl + 2 + twr; /* (WL + BL / 2 + tWR) / 2 */
u8 twr2rd = tcwl + 2 + twtr; /* (WL + BL / 2 + tWTR) / 2 */
u8 trd2wr = 5; /* (RL + BL / 2 + 2 - WL) / 2 */
if (tcl + 1 >= trtp + trp)
trtp = tcl + 2 - trp;
/* set mode registers */
for (i = 0; i < ARRAY_SIZE(mr_ddr3); i++)
writel(mr_ddr3[i], &mctl_phy->mr[i]);
/* set DRAM timing */
writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
&mctl_ctl->dramtmg[0]);
writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]);
writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd,
&mctl_ctl->dramtmg[2]);
writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]);
writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp,
&mctl_ctl->dramtmg[4]);
writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke,
&mctl_ctl->dramtmg[5]);
/* Value suggested by ZynqMP manual and used by libdram */
writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]);
writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs,
&mctl_ctl->dramtmg[8]);
writel(txsr, &mctl_ctl->dramtmg[14]);
clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30));
writel(0, &mctl_ctl->dfimisc);
clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
/*
* Set timing registers of the PHY.
* Note: the PHY is clocked 2x from the DRAM frequency.
*/
writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1),
&mctl_phy->dtpr[0]);
writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]);
writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]);
writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8),
&mctl_phy->dtpr[3]);
writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]);
writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]);
writel(0x0505, &mctl_phy->dtpr[6]);
/* Configure DFI timing */
writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000,
&mctl_ctl->dfitmg0);
writel(0x040201, &mctl_ctl->dfitmg1);
/* Configure PHY timing. Zynq uses different registers. */
writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]);
writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]);
/* set refresh timing */
writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
}
@@ -0,0 +1,132 @@
/*
* sun50i H6 LPDDR3 timings
*
* (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
static u32 mr_lpddr3[12] = {
0x00000000, 0x00000043, 0x0000001a, 0x00000001,
0x00000000, 0x00000000, 0x00000048, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000003,
};
/* TODO: flexible timing */
void mctl_set_timing_params(struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
struct sunxi_mctl_phy_reg * const mctl_phy =
(struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
int i;
u8 tccd = 2;
u8 tfaw = max(ns_to_t(50), 4);
u8 trrd = max(ns_to_t(10), 2);
u8 trcd = max(ns_to_t(24), 2);
u8 trc = ns_to_t(70);
u8 txp = max(ns_to_t(8), 2);
u8 twtr = max(ns_to_t(8), 2);
u8 trtp = max(ns_to_t(8), 2);
u8 twr = max(ns_to_t(15), 2);
u8 trp = ns_to_t(18);
u8 tras = ns_to_t(42);
u8 twtr_sa = ns_to_t(5);
u8 tcksrea = ns_to_t(11);
u16 trefi = ns_to_t(3900) / 32;
u16 trfc = ns_to_t(210);
u16 txsr = ns_to_t(220);
if (CONFIG_DRAM_CLK % 400 == 0) {
/* Round up these parameters */
twtr_sa++;
tcksrea++;
}
u8 tmrw = 5;
u8 tmrd = 5;
u8 tmod = 12;
u8 tcke = 3;
u8 tcksrx = 5;
u8 tcksre = 5;
u8 tckesr = 5;
u8 trasmax = CONFIG_DRAM_CLK / 60;
u8 txs = 4;
u8 txsdll = 4;
u8 txsabort = 4;
u8 txsfast = 4;
u8 tcl = 5; /* CL 10 */
u8 tcwl = 3; /* CWL 6 */
u8 t_rdata_en = twtr_sa + 8;
u32 tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
u32 tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */
u32 tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 11us */
u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
u8 twtp = tcwl + 4 + twr + 1;
/*
* The code below for twr2rd and trd2wr follows the IP core's
* document from ZynqMP and i.MX7. The BSP has both number
* substracted by 2.
*/
u8 twr2rd = tcwl + 4 + 1 + twtr;
u8 trd2wr = tcl + 4 + (tcksrea >> 1) - tcwl + 1;
/* set mode registers */
for (i = 0; i < ARRAY_SIZE(mr_lpddr3); i++)
writel(mr_lpddr3[i], &mctl_phy->mr[i]);
/* set DRAM timing */
writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
&mctl_ctl->dramtmg[0]);
writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]);
writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd,
&mctl_ctl->dramtmg[2]);
writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]);
writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp,
&mctl_ctl->dramtmg[4]);
writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke,
&mctl_ctl->dramtmg[5]);
/* Value suggested by ZynqMP manual and used by libdram */
writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]);
writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs,
&mctl_ctl->dramtmg[8]);
writel(txsr, &mctl_ctl->dramtmg[14]);
clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30));
writel(0, &mctl_ctl->dfimisc);
clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
/*
* Set timing registers of the PHY.
* Note: the PHY is clocked 2x from the DRAM frequency.
*/
writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1),
&mctl_phy->dtpr[0]);
writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]);
writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]);
writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8),
&mctl_phy->dtpr[3]);
writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]);
writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]);
writel(0x0505, &mctl_phy->dtpr[6]);
/* Configure DFI timing */
writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000,
&mctl_ctl->dfitmg0);
writel(0x040201, &mctl_ctl->dfitmg1);
/* Configure PHY timing */
writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]);
writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]);
/* set refresh timing */
writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
}
@@ -0,0 +1,83 @@
#include <common.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
void mctl_set_timing_params(uint16_t socid, struct dram_para *para)
{
struct sunxi_mctl_ctl_reg * const mctl_ctl =
(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
u8 tccd = 2;
u8 tfaw = max(ns_to_t(50), 4);
u8 trrd = max(ns_to_t(10), 2);
u8 trcd = max(ns_to_t(24), 2);
u8 trc = ns_to_t(70);
u8 txp = max(ns_to_t(8), 2);
u8 twtr = max(ns_to_t(8), 2);
u8 trtp = max(ns_to_t(8), 2);
u8 twr = max(ns_to_t(15), 3);
u8 trp = max(ns_to_t(27), 2);
u8 tras = ns_to_t(42);
u16 trefi = ns_to_t(3900) / 32;
u16 trfc = ns_to_t(210);
u8 tmrw = 5;
u8 tmrd = 5;
u8 tmod = 12;
u8 tcke = 3;
u8 tcksrx = 5;
u8 tcksre = 5;
u8 tckesr = 5;
u8 trasmax = 24;
u8 tcl = 6; /* CL 12 */
u8 tcwl = 3; /* CWL 6 */
u8 t_rdata_en = 5;
u8 wr_latency = 2;
u32 tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
u32 tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */
u32 tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 11us */
u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
u8 twtp = tcwl + 4 + twr + 1;
u8 twr2rd = tcwl + 4 + 1 + twtr;
u8 trd2wr = tcl + 4 + 5 - tcwl + 1;
/* set mode register */
writel(0xc3, &mctl_ctl->mr[1]); /* nWR=8, BL8 */
writel(0xa, &mctl_ctl->mr[2]); /* RL=12, WL=6 */
writel(0x2, &mctl_ctl->mr[3]); /* 40 0hms PD/PU */
/* set DRAM timing */
writel(DRAMTMG0_TWTP(twtp) | DRAMTMG0_TFAW(tfaw) |
DRAMTMG0_TRAS_MAX(trasmax) | DRAMTMG0_TRAS(tras),
&mctl_ctl->dramtmg[0]);
writel(DRAMTMG1_TXP(txp) | DRAMTMG1_TRTP(trtp) | DRAMTMG1_TRC(trc),
&mctl_ctl->dramtmg[1]);
writel(DRAMTMG2_TCWL(tcwl) | DRAMTMG2_TCL(tcl) |
DRAMTMG2_TRD2WR(trd2wr) | DRAMTMG2_TWR2RD(twr2rd),
&mctl_ctl->dramtmg[2]);
writel(DRAMTMG3_TMRW(tmrw) | DRAMTMG3_TMRD(tmrd) | DRAMTMG3_TMOD(tmod),
&mctl_ctl->dramtmg[3]);
writel(DRAMTMG4_TRCD(trcd) | DRAMTMG4_TCCD(tccd) | DRAMTMG4_TRRD(trrd) |
DRAMTMG4_TRP(trp), &mctl_ctl->dramtmg[4]);
writel(DRAMTMG5_TCKSRX(tcksrx) | DRAMTMG5_TCKSRE(tcksre) |
DRAMTMG5_TCKESR(tckesr) | DRAMTMG5_TCKE(tcke),
&mctl_ctl->dramtmg[5]);
/* set two rank timing */
clrsetbits_le32(&mctl_ctl->dramtmg[8], (0xff << 8) | (0xff << 0),
(0x66 << 8) | (0x10 << 0));
/* set PHY interface timing, write latency and read latency configure */
writel((0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) |
(wr_latency << 0), &mctl_ctl->pitmg[0]);
/* set PHY timing, PTR0-2 use default */
writel(PTR3_TDINIT0(tdinit0) | PTR3_TDINIT1(tdinit1), &mctl_ctl->ptr[3]);
writel(PTR4_TDINIT2(tdinit2) | PTR4_TDINIT3(tdinit3), &mctl_ctl->ptr[4]);
/* set refresh timing */
writel(RFSHTMG_TREFI(trefi) | RFSHTMG_TRFC(trfc), &mctl_ctl->rfshtmg);
}
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* GTBUS initialisation for sun9i
*
* (C) Copyright 2016 Theobroma Systems Design und Consulting GmbH
* Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/gtbus_sun9i.h>
#include <asm/arch/sys_proto.h>
#ifdef CONFIG_SPL_BUILD
void gtbus_init(void)
{
struct sunxi_gtbus_reg * const gtbus =
(struct sunxi_gtbus_reg *)SUNXI_GTBUS_BASE;
/*
* We use the same setting that Allwinner used in Boot0 for now.
* It may be advantageous to adjust these for various workloads
* (e.g. headless use cases that focus on IO throughput).
*/
writel((GT_PRIO_HIGH << GT_PORT_FE0) |
(GT_PRIO_HIGH << GT_PORT_BE1) |
(GT_PRIO_HIGH << GT_PORT_BE2) |
(GT_PRIO_HIGH << GT_PORT_IEP0) |
(GT_PRIO_HIGH << GT_PORT_FE1) |
(GT_PRIO_HIGH << GT_PORT_BE0) |
(GT_PRIO_HIGH << GT_PORT_FE2) |
(GT_PRIO_HIGH << GT_PORT_IEP1),
&gtbus->mst_read_prio_cfg[0]);
writel(GP_MST_CFG_DEFAULT, &gtbus->mst_cfg[GT_PORT_FE0]);
writel(GP_MST_CFG_DEFAULT, &gtbus->mst_cfg[GT_PORT_FE0]);
writel(GP_MST_CFG_DEFAULT, &gtbus->mst_cfg[GT_PORT_BE1]);
writel(GP_MST_CFG_DEFAULT, &gtbus->mst_cfg[GT_PORT_BE2]);
writel(GP_MST_CFG_DEFAULT, &gtbus->mst_cfg[GT_PORT_IEP0]);
writel(GP_MST_CFG_DEFAULT, &gtbus->mst_cfg[GT_PORT_FE1]);
writel(GP_MST_CFG_DEFAULT, &gtbus->mst_cfg[GT_PORT_BE0]);
writel(GP_MST_CFG_DEFAULT, &gtbus->mst_cfg[GT_PORT_FE2]);
writel(GP_MST_CFG_DEFAULT, &gtbus->mst_cfg[GT_PORT_IEP1]);
}
#endif
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Sunxi A31 Power Management Unit
*
* (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl>
* http://linux-sunxi.org
*
* Based on sun6i sources and earlier U-Boot Allwinner A10 SPL work
*
* (C) Copyright 2006-2013
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Berg Xing <bergxing@allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*/
#include <common.h>
#include <errno.h>
#include <time.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <asm/arch/p2wi.h>
#include <asm/arch/prcm.h>
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
void p2wi_init(void)
{
struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE;
/* Enable p2wi and PIO clk, and de-assert their resets */
prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK);
sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA);
/* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */
writel(P2WI_CTRL_RESET, &p2wi->ctrl);
sdelay(0x100);
writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8),
&p2wi->cc);
}
int p2wi_change_to_p2wi_mode(u8 slave_addr, u8 ctrl_reg, u8 init_data)
{
struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE;
unsigned long tmo = timer_get_us() + 1000000;
writel(P2WI_PM_DEV_ADDR(slave_addr) |
P2WI_PM_CTRL_ADDR(ctrl_reg) |
P2WI_PM_INIT_DATA(init_data) |
P2WI_PM_INIT_SEND,
&p2wi->pm);
while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) {
if (timer_get_us() > tmo)
return -ETIME;
}
return 0;
}
static int p2wi_await_trans(void)
{
struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE;
unsigned long tmo = timer_get_us() + 1000000;
int ret;
u8 reg;
while (1) {
reg = readl(&p2wi->status);
if (reg & P2WI_STAT_TRANS_ERR) {
ret = -EIO;
break;
}
if (reg & P2WI_STAT_TRANS_DONE) {
ret = 0;
break;
}
if (timer_get_us() > tmo) {
ret = -ETIME;
break;
}
}
writel(reg, &p2wi->status); /* Clear status bits */
return ret;
}
int p2wi_read(const u8 addr, u8 *data)
{
struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE;
int ret;
writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
writel(P2WI_DATA_NUM_BYTES(1) |
P2WI_DATA_NUM_BYTES_READ, &p2wi->numbytes);
writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
ret = p2wi_await_trans();
*data = readl(&p2wi->data0) & P2WI_DATA_BYTE_1_MASK;
return ret;
}
int p2wi_write(const u8 addr, u8 data)
{
struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE;
writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
writel(P2WI_DATA_BYTE_1(data), &p2wi->data0);
writel(P2WI_DATA_NUM_BYTES(1), &p2wi->numbytes);
writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
return p2wi_await_trans();
}
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/gpio.h>
void sunxi_gpio_set_cfgbank(struct sunxi_gpio *pio, int bank_offset, u32 val)
{
u32 index = GPIO_CFG_INDEX(bank_offset);
u32 offset = GPIO_CFG_OFFSET(bank_offset);
clrsetbits_le32(&pio->cfg[0] + index, 0xf << offset, val << offset);
}
void sunxi_gpio_set_cfgpin(u32 pin, u32 val)
{
u32 bank = GPIO_BANK(pin);
struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
sunxi_gpio_set_cfgbank(pio, pin, val);
}
int sunxi_gpio_get_cfgbank(struct sunxi_gpio *pio, int bank_offset)
{
u32 index = GPIO_CFG_INDEX(bank_offset);
u32 offset = GPIO_CFG_OFFSET(bank_offset);
u32 cfg;
cfg = readl(&pio->cfg[0] + index);
cfg >>= offset;
return cfg & 0xf;
}
int sunxi_gpio_get_cfgpin(u32 pin)
{
u32 bank = GPIO_BANK(pin);
struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
return sunxi_gpio_get_cfgbank(pio, pin);
}
int sunxi_gpio_set_drv(u32 pin, u32 val)
{
u32 bank = GPIO_BANK(pin);
u32 index = GPIO_DRV_INDEX(pin);
u32 offset = GPIO_DRV_OFFSET(pin);
struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
clrsetbits_le32(&pio->drv[0] + index, 0x3 << offset, val << offset);
return 0;
}
int sunxi_gpio_set_pull(u32 pin, u32 val)
{
u32 bank = GPIO_BANK(pin);
u32 index = GPIO_PULL_INDEX(pin);
u32 offset = GPIO_PULL_OFFSET(pin);
struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
clrsetbits_le32(&pio->pull[0] + index, 0x3 << offset, val << offset);
return 0;
}
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
*
* Sunxi PMIC bus access helpers
*
* The axp152 & axp209 use an i2c bus, the axp221 uses the p2wi bus and the
* axp223 uses the rsb bus, these functions abstract this.
*/
#include <common.h>
#include <asm/arch/p2wi.h>
#include <asm/arch/rsb.h>
#include <i2c.h>
#include <asm/arch/pmic_bus.h>
#define AXP152_I2C_ADDR 0x30
#define AXP209_I2C_ADDR 0x34
#define AXP221_CHIP_ADDR 0x68
#define AXP221_CTRL_ADDR 0x3e
#define AXP221_INIT_DATA 0x3e
/* AXP818 device and runtime addresses are same as AXP223 */
#define AXP223_DEVICE_ADDR 0x3a3
#define AXP223_RUNTIME_ADDR 0x2d
int pmic_bus_init(void)
{
/* This cannot be 0 because it is used in SPL before BSS is ready */
static int needs_init = 1;
__maybe_unused int ret;
if (!needs_init)
return 0;
#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER
# ifdef CONFIG_MACH_SUN6I
p2wi_init();
ret = p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR,
AXP221_INIT_DATA);
# elif defined CONFIG_MACH_SUN8I_R40
/* Nothing. R40 uses the AXP221s in I2C mode */
ret = 0;
# else
ret = rsb_init();
if (ret)
return ret;
ret = rsb_set_device_address(AXP223_DEVICE_ADDR, AXP223_RUNTIME_ADDR);
# endif
if (ret)
return ret;
#endif
needs_init = 0;
return 0;
}
int pmic_bus_read(u8 reg, u8 *data)
{
#ifdef CONFIG_AXP152_POWER
return i2c_read(AXP152_I2C_ADDR, reg, 1, data, 1);
#elif defined CONFIG_AXP209_POWER
return i2c_read(AXP209_I2C_ADDR, reg, 1, data, 1);
#elif defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER
# ifdef CONFIG_MACH_SUN6I
return p2wi_read(reg, data);
# elif defined CONFIG_MACH_SUN8I_R40
return i2c_read(AXP209_I2C_ADDR, reg, 1, data, 1);
# else
return rsb_read(AXP223_RUNTIME_ADDR, reg, data);
# endif
#endif
}
int pmic_bus_write(u8 reg, u8 data)
{
#ifdef CONFIG_AXP152_POWER
return i2c_write(AXP152_I2C_ADDR, reg, 1, &data, 1);
#elif defined CONFIG_AXP209_POWER
return i2c_write(AXP209_I2C_ADDR, reg, 1, &data, 1);
#elif defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER
# ifdef CONFIG_MACH_SUN6I
return p2wi_write(reg, data);
# elif defined CONFIG_MACH_SUN8I_R40
return i2c_write(AXP209_I2C_ADDR, reg, 1, &data, 1);
# else
return rsb_write(AXP223_RUNTIME_ADDR, reg, data);
# endif
#endif
}
int pmic_bus_setbits(u8 reg, u8 bits)
{
int ret;
u8 val;
ret = pmic_bus_read(reg, &val);
if (ret)
return ret;
if ((val & bits) == bits)
return 0;
val |= bits;
return pmic_bus_write(reg, val);
}
int pmic_bus_clrbits(u8 reg, u8 bits)
{
int ret;
u8 val;
ret = pmic_bus_read(reg, &val);
if (ret)
return ret;
if (!(val & bits))
return 0;
val &= ~bits;
return pmic_bus_write(reg, val);
}
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Sunxi A31 Power Management Unit
*
* (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl>
* http://linux-sunxi.org
*
* Based on sun6i sources and earlier U-Boot Allwinner A10 SPL work
*
* (C) Copyright 2006-2013
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Berg Xing <bergxing@allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/prcm.h>
#include <asm/arch/sys_proto.h>
/* APB0 clock gate and reset bit offsets are the same. */
void prcm_apb0_enable(u32 flags)
{
struct sunxi_prcm_reg *prcm =
(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
/* open the clock for module */
setbits_le32(&prcm->apb0_gate, flags);
/* deassert reset for module */
setbits_le32(&prcm->apb0_reset, flags);
}
void prcm_apb0_disable(u32 flags)
{
struct sunxi_prcm_reg *prcm =
(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
/* assert reset for module */
clrbits_le32(&prcm->apb0_reset, flags);
/* close the clock for module */
clrbits_le32(&prcm->apb0_gate, flags);
}
@@ -0,0 +1,47 @@
@
@ ARMv8 RMR reset sequence on Allwinner SoCs.
@
@ All 64-bit capable Allwinner SoCs reset in AArch32 (and continue to
@ exectute the Boot ROM in this state), so we need to switch to AArch64
@ at some point.
@ Section G6.2.133 of the ARMv8 ARM describes the Reset Management Register
@ (RMR), which triggers a warm-reset of a core and can request to switch
@ into a different execution state (AArch32 or AArch64).
@ The address at which execution starts after the reset is held in the
@ RVBAR system register, which is architecturally read-only.
@ Allwinner provides a writable alias of this register in MMIO space, so
@ we can easily set the start address of AArch64 code.
@ This code below switches to AArch64 and starts execution at the specified
@ start address. It needs to be assembled by an ARM(32) assembler and
@ the machine code must be inserted as verbatim .word statements into the
@ beginning of the AArch64 U-Boot code.
@ To get the encoded bytes, use:
@ ${CROSS_COMPILE}gcc -c -o rmr_switch.o rmr_switch.S
@ ${CROSS_COMPILE}objdump -d rmr_switch.o
@
@ The resulting words should be inserted into the U-Boot file at
@ arch/arm/include/asm/arch-sunxi/boot0.h.
@
@ This file is not build by the U-Boot build system, but provided only as a
@ reference and to be able to regenerate a (probably fixed) version of this
@ code found in encoded form in boot0.h.
#include <config.h>
.text
#ifndef CONFIG_MACH_SUN50I_H6
ldr r1, =0x017000a0 @ MMIO mapped RVBAR[0] register
#else
ldr r1, =0x09010040 @ MMIO mapped RVBAR[0] register
#endif
ldr r0, =0x57aA7add @ start address, to be replaced
str r0, [r1]
dsb sy
isb sy
mrc 15, 0, r0, cr12, cr0, 2 @ read RMR register
orr r0, r0, #3 @ request reset in AArch64
mcr 15, 0, r0, cr12, cr0, 2 @ write RMR register
isb sy
1: wfi
b 1b
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
*
* Based on allwinner u-boot sources rsb code which is:
* (C) Copyright 2007-2013
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* lixiang <lixiang@allwinnertech.com>
*/
#include <common.h>
#include <errno.h>
#include <time.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <asm/arch/prcm.h>
#include <asm/arch/rsb.h>
static int rsb_set_device_mode(void);
static void rsb_cfg_io(void)
{
#ifdef CONFIG_MACH_SUN8I
sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL_R_RSB);
sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL_R_RSB);
sunxi_gpio_set_pull(SUNXI_GPL(0), 1);
sunxi_gpio_set_pull(SUNXI_GPL(1), 1);
sunxi_gpio_set_drv(SUNXI_GPL(0), 2);
sunxi_gpio_set_drv(SUNXI_GPL(1), 2);
#elif defined CONFIG_MACH_SUN9I
sunxi_gpio_set_cfgpin(SUNXI_GPN(0), SUN9I_GPN_R_RSB);
sunxi_gpio_set_cfgpin(SUNXI_GPN(1), SUN9I_GPN_R_RSB);
sunxi_gpio_set_pull(SUNXI_GPN(0), 1);
sunxi_gpio_set_pull(SUNXI_GPN(1), 1);
sunxi_gpio_set_drv(SUNXI_GPN(0), 2);
sunxi_gpio_set_drv(SUNXI_GPN(1), 2);
#else
#error unsupported MACH_SUNXI
#endif
}
static void rsb_set_clk(void)
{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
u32 div = 0;
u32 cd_odly = 0;
/* Source is Hosc24M, set RSB clk to 3Mhz */
div = 24000000 / 3000000 / 2 - 1;
cd_odly = div >> 1;
if (!cd_odly)
cd_odly = 1;
writel((cd_odly << 8) | div, &rsb->ccr);
}
int rsb_init(void)
{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
/* Enable RSB and PIO clk, and de-assert their resets */
prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB);
/* Setup external pins */
rsb_cfg_io();
writel(RSB_CTRL_SOFT_RST, &rsb->ctrl);
rsb_set_clk();
return rsb_set_device_mode();
}
static int rsb_await_trans(void)
{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
unsigned long tmo = timer_get_us() + 1000000;
u32 stat;
int ret;
while (1) {
stat = readl(&rsb->stat);
if (stat & RSB_STAT_LBSY_INT) {
ret = -EBUSY;
break;
}
if (stat & RSB_STAT_TERR_INT) {
ret = -EIO;
break;
}
if (stat & RSB_STAT_TOVER_INT) {
ret = 0;
break;
}
if (timer_get_us() > tmo) {
ret = -ETIME;
break;
}
}
writel(stat, &rsb->stat); /* Clear status bits */
return ret;
}
static int rsb_set_device_mode(void)
{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
unsigned long tmo = timer_get_us() + 1000000;
writel(RSB_DMCR_DEVICE_MODE_START | RSB_DMCR_DEVICE_MODE_DATA,
&rsb->dmcr);
while (readl(&rsb->dmcr) & RSB_DMCR_DEVICE_MODE_START) {
if (timer_get_us() > tmo)
return -ETIME;
}
return rsb_await_trans();
}
static int rsb_do_trans(void)
{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
setbits_le32(&rsb->ctrl, RSB_CTRL_START_TRANS);
return rsb_await_trans();
}
int rsb_set_device_address(u16 device_addr, u16 runtime_addr)
{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_addr) |
RSB_DEVADDR_DEVICE_ADDR(device_addr), &rsb->devaddr);
writel(RSB_CMD_SET_RTSADDR, &rsb->cmd);
return rsb_do_trans();
}
int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data)
{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr);
writel(reg_addr, &rsb->addr);
writel(data, &rsb->data);
writel(RSB_CMD_BYTE_WRITE, &rsb->cmd);
return rsb_do_trans();
}
int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data)
{
struct sunxi_rsb_reg * const rsb =
(struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
int ret;
writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr);
writel(reg_addr, &rsb->addr);
writel(RSB_CMD_BYTE_READ, &rsb->cmd);
ret = rsb_do_trans();
if (ret)
return ret;
*data = readl(&rsb->data) & 0xff;
return 0;
}
@@ -0,0 +1,311 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
*/
#include <common.h>
#include <spl.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <linux/libfdt.h>
#ifdef CONFIG_SPL_OS_BOOT
#error CONFIG_SPL_OS_BOOT is not supported yet
#endif
/*
* This is a very simple U-Boot image loading implementation, trying to
* replicate what the boot ROM is doing when loading the SPL. Because we
* know the exact pins where the SPI Flash is connected and also know
* that the Read Data Bytes (03h) command is supported, the hardware
* configuration is very simple and we don't need the extra flexibility
* of the SPI framework. Moreover, we rely on the default settings of
* the SPI controler hardware registers and only adjust what needs to
* be changed. This is good for the code size and this implementation
* adds less than 400 bytes to the SPL.
*
* There are two variants of the SPI controller in Allwinner SoCs:
* A10/A13/A20 (sun4i variant) and everything else (sun6i variant).
* Both of them are supported.
*
* The pin mixing part is SoC specific and only A10/A13/A20/H3/A64 are
* supported at the moment.
*/
/*****************************************************************************/
/* SUN4I variant of the SPI controller */
/*****************************************************************************/
#define SUN4I_SPI0_CCTL (0x01C05000 + 0x1C)
#define SUN4I_SPI0_CTL (0x01C05000 + 0x08)
#define SUN4I_SPI0_RX (0x01C05000 + 0x00)
#define SUN4I_SPI0_TX (0x01C05000 + 0x04)
#define SUN4I_SPI0_FIFO_STA (0x01C05000 + 0x28)
#define SUN4I_SPI0_BC (0x01C05000 + 0x20)
#define SUN4I_SPI0_TC (0x01C05000 + 0x24)
#define SUN4I_CTL_ENABLE BIT(0)
#define SUN4I_CTL_MASTER BIT(1)
#define SUN4I_CTL_TF_RST BIT(8)
#define SUN4I_CTL_RF_RST BIT(9)
#define SUN4I_CTL_XCH BIT(10)
/*****************************************************************************/
/* SUN6I variant of the SPI controller */
/*****************************************************************************/
#define SUN6I_SPI0_CCTL (0x01C68000 + 0x24)
#define SUN6I_SPI0_GCR (0x01C68000 + 0x04)
#define SUN6I_SPI0_TCR (0x01C68000 + 0x08)
#define SUN6I_SPI0_FIFO_STA (0x01C68000 + 0x1C)
#define SUN6I_SPI0_MBC (0x01C68000 + 0x30)
#define SUN6I_SPI0_MTC (0x01C68000 + 0x34)
#define SUN6I_SPI0_BCC (0x01C68000 + 0x38)
#define SUN6I_SPI0_TXD (0x01C68000 + 0x200)
#define SUN6I_SPI0_RXD (0x01C68000 + 0x300)
#define SUN6I_CTL_ENABLE BIT(0)
#define SUN6I_CTL_MASTER BIT(1)
#define SUN6I_CTL_SRST BIT(31)
#define SUN6I_TCR_XCH BIT(31)
/*****************************************************************************/
#define CCM_AHB_GATING0 (0x01C20000 + 0x60)
#define CCM_SPI0_CLK (0x01C20000 + 0xA0)
#define SUN6I_BUS_SOFT_RST_REG0 (0x01C20000 + 0x2C0)
#define AHB_RESET_SPI0_SHIFT 20
#define AHB_GATE_OFFSET_SPI0 20
#define SPI0_CLK_DIV_BY_2 0x1000
#define SPI0_CLK_DIV_BY_4 0x1001
/*****************************************************************************/
/*
* Allwinner A10/A20 SoCs were using pins PC0,PC1,PC2,PC23 for booting
* from SPI Flash, everything else is using pins PC0,PC1,PC2,PC3.
*/
static void spi0_pinmux_setup(unsigned int pin_function)
{
unsigned int pin;
for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(2); pin++)
sunxi_gpio_set_cfgpin(pin, pin_function);
if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I))
sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_function);
else
sunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_function);
}
/*
* Setup 6 MHz from OSC24M (because the BROM is doing the same).
*/
static void spi0_enable_clock(void)
{
/* Deassert SPI0 reset on SUN6I */
if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
setbits_le32(SUN6I_BUS_SOFT_RST_REG0,
(1 << AHB_RESET_SPI0_SHIFT));
/* Open the SPI0 gate */
setbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));
/* Divide by 4 */
writel(SPI0_CLK_DIV_BY_4, IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) ?
SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL);
/* 24MHz from OSC24M */
writel((1 << 31), CCM_SPI0_CLK);
if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) {
/* Enable SPI in the master mode and do a soft reset */
setbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER |
SUN6I_CTL_ENABLE |
SUN6I_CTL_SRST);
/* Wait for completion */
while (readl(SUN6I_SPI0_GCR) & SUN6I_CTL_SRST)
;
} else {
/* Enable SPI in the master mode and reset FIFO */
setbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER |
SUN4I_CTL_ENABLE |
SUN4I_CTL_TF_RST |
SUN4I_CTL_RF_RST);
}
}
static void spi0_disable_clock(void)
{
/* Disable the SPI0 controller */
if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
clrbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER |
SUN6I_CTL_ENABLE);
else
clrbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER |
SUN4I_CTL_ENABLE);
/* Disable the SPI0 clock */
writel(0, CCM_SPI0_CLK);
/* Close the SPI0 gate */
clrbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));
/* Assert SPI0 reset on SUN6I */
if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
clrbits_le32(SUN6I_BUS_SOFT_RST_REG0,
(1 << AHB_RESET_SPI0_SHIFT));
}
static void spi0_init(void)
{
unsigned int pin_function = SUNXI_GPC_SPI0;
if (IS_ENABLED(CONFIG_MACH_SUN50I))
pin_function = SUN50I_GPC_SPI0;
spi0_pinmux_setup(pin_function);
spi0_enable_clock();
}
static void spi0_deinit(void)
{
/* New SoCs can disable pins, older could only set them as input */
unsigned int pin_function = SUNXI_GPIO_INPUT;
if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
pin_function = SUNXI_GPIO_DISABLE;
spi0_disable_clock();
spi0_pinmux_setup(pin_function);
}
/*****************************************************************************/
#define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */
static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize,
ulong spi_ctl_reg,
ulong spi_ctl_xch_bitmask,
ulong spi_fifo_reg,
ulong spi_tx_reg,
ulong spi_rx_reg,
ulong spi_bc_reg,
ulong spi_tc_reg,
ulong spi_bcc_reg)
{
writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */
writel(4, spi_tc_reg); /* Transfer counter (bytes to send) */
if (spi_bcc_reg)
writel(4, spi_bcc_reg); /* SUN6I also needs this */
/* Send the Read Data Bytes (03h) command header */
writeb(0x03, spi_tx_reg);
writeb((u8)(addr >> 16), spi_tx_reg);
writeb((u8)(addr >> 8), spi_tx_reg);
writeb((u8)(addr), spi_tx_reg);
/* Start the data transfer */
setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask);
/* Wait until everything is received in the RX FIFO */
while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize)
;
/* Skip 4 bytes */
readl(spi_rx_reg);
/* Read the data */
while (bufsize-- > 0)
*buf++ = readb(spi_rx_reg);
/* tSHSL time is up to 100 ns in various SPI flash datasheets */
udelay(1);
}
static void spi0_read_data(void *buf, u32 addr, u32 len)
{
u8 *buf8 = buf;
u32 chunk_len;
while (len > 0) {
chunk_len = len;
if (chunk_len > SPI_READ_MAX_SIZE)
chunk_len = SPI_READ_MAX_SIZE;
if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) {
sunxi_spi0_read_data(buf8, addr, chunk_len,
SUN6I_SPI0_TCR,
SUN6I_TCR_XCH,
SUN6I_SPI0_FIFO_STA,
SUN6I_SPI0_TXD,
SUN6I_SPI0_RXD,
SUN6I_SPI0_MBC,
SUN6I_SPI0_MTC,
SUN6I_SPI0_BCC);
} else {
sunxi_spi0_read_data(buf8, addr, chunk_len,
SUN4I_SPI0_CTL,
SUN4I_CTL_XCH,
SUN4I_SPI0_FIFO_STA,
SUN4I_SPI0_TX,
SUN4I_SPI0_RX,
SUN4I_SPI0_BC,
SUN4I_SPI0_TC,
0);
}
len -= chunk_len;
buf8 += chunk_len;
addr += chunk_len;
}
}
static ulong spi_load_read(struct spl_load_info *load, ulong sector,
ulong count, void *buf)
{
spi0_read_data(buf, sector, count);
return count;
}
/*****************************************************************************/
static int spl_spi_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
{
int ret = 0;
struct image_header *header;
header = (struct image_header *)(CONFIG_SYS_TEXT_BASE);
spi0_init();
spi0_read_data((void *)header, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40);
if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
image_get_magic(header) == FDT_MAGIC) {
struct spl_load_info load;
debug("Found FIT image\n");
load.dev = NULL;
load.priv = NULL;
load.filename = NULL;
load.bl_len = 1;
load.read = spi_load_read;
ret = spl_load_simple_fit(spl_image, &load,
CONFIG_SYS_SPI_U_BOOT_OFFS, header);
} else {
ret = spl_parse_image_header(spl_image, header);
if (ret)
return ret;
spi0_read_data((void *)spl_image->load_addr,
CONFIG_SYS_SPI_U_BOOT_OFFS, spl_image->size);
}
spi0_deinit();
return ret;
}
/* Use priorty 0 to override the default if it happens to be linked in */
SPL_LOAD_IMAGE_METHOD("sunxi SPI", 0, BOOT_DEVICE_SPI, spl_spi_load_image);