This patch adds DRAM initialisation support for the Allwinner A20 (sun7i) processor. This code will not been compiled until the build is hooked up in a later patch. It has been split out to keep the patches manageable. Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com> Signed-off-by: Emilio López <emilio@elopez.com.ar> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Henrik Nordstrom <henrik@henriknordstrom.net> Signed-off-by: Jens Kuske <jenskuske@gmail.com> Signed-off-by: Luke Leighton <lkcl@lkcl.net> Signed-off-by: Oliver Schinagl <oliver@schinagl.nl> Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com> Signed-off-by: Stefan Roese <sr@denx.de> Signed-off-by: Ian Campbell <ijc@hellion.org.uk> Reviewed-by: Marek Vasut <marex@denx.de> Cc: Tom Cubie <Mr.hipboi@gmail.com> Reviewed-by: Tom Rini <trini@ti.com>master
parent
fe1b4db05e
commit
286c3c3a5e
@ -0,0 +1,593 @@ |
||||
/*
|
||||
* 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 Allwiner A10 SPL work |
||||
* |
||||
* (C) Copyright 2007-2012 |
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com> |
||||
* Berg Xing <bergxing@allwinnertech.com> |
||||
* Tom Cubie <tangliang@allwinnertech.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
/*
|
||||
* 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 void await_completion(u32 *reg, u32 mask) |
||||
{ |
||||
unsigned long tmo = timer_get_us() + 1000000; |
||||
|
||||
while (readl(reg) & mask) { |
||||
if (timer_get_us() > tmo) |
||||
panic("Timeout initialising DRAM\n"); |
||||
} |
||||
} |
||||
|
||||
static void mctl_ddr3_reset(void) |
||||
{ |
||||
struct sunxi_dram_reg *dram = |
||||
(struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; |
||||
|
||||
clrbits_le32(&dram->mcr, DRAM_MCR_RESET); |
||||
udelay(2); |
||||
setbits_le32(&dram->mcr, DRAM_MCR_RESET); |
||||
} |
||||
|
||||
static void mctl_set_drive(void) |
||||
{ |
||||
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; |
||||
|
||||
clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3) | (0x3 << 28), |
||||
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_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); |
||||
} |
||||
|
||||
/*
|
||||
* 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, n, bus_width; |
||||
|
||||
bus_width = readl(&dram->dcr); |
||||
|
||||
if ((bus_width & DRAM_DCR_BUS_WIDTH_MASK) == |
||||
DRAM_DCR_BUS_WIDTH(DRAM_DCR_BUS_WIDTH_32BIT)) |
||||
n = DRAM_DCR_NR_DLLCR_32BIT; |
||||
else |
||||
n = DRAM_DCR_NR_DLLCR_16BIT; |
||||
|
||||
for (i = 1; i < n; 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 < n; i++) |
||||
clrbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET | |
||||
DRAM_DLLCR_DISABLE); |
||||
udelay(22); |
||||
|
||||
for (i = 1; i < n; i++) |
||||
clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_DISABLE, |
||||
DRAM_DLLCR_NRESET); |
||||
udelay(22); |
||||
} |
||||
|
||||
static u32 hpcr_value[32] = { |
||||
#ifdef CONFIG_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 reg_val; |
||||
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
||||
|
||||
/* setup DRAM PLL */ |
||||
reg_val = readl(&ccm->pll5_cfg); |
||||
reg_val &= ~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) */ |
||||
if (clk >= 540 && clk < 552) { |
||||
/* dram = 540MHz, pll5p = 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)); |
||||
reg_val |= CCM_PLL5_CTRL_P(1); |
||||
} else if (clk >= 512 && clk < 528) { |
||||
/* dram = 512MHz, pll5p = 384MHz */ |
||||
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)); |
||||
reg_val |= CCM_PLL5_CTRL_P(2); |
||||
} else if (clk >= 496 && clk < 504) { |
||||
/* dram = 496MHz, pll5p = 372MHz */ |
||||
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)); |
||||
reg_val |= CCM_PLL5_CTRL_P(2); |
||||
} else if (clk >= 468 && clk < 480) { |
||||
/* dram = 468MHz, pll5p = 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)); |
||||
reg_val |= CCM_PLL5_CTRL_P(1); |
||||
} else if (clk >= 396 && clk < 408) { |
||||
/* dram = 396MHz, pll5p = 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)); |
||||
reg_val |= CCM_PLL5_CTRL_P(1); |
||||
} 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_P(CCM_PLL5_CTRL_P_X(2)); |
||||
} |
||||
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_SUN4I) || defined(CONFIG_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 */ |
||||
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(2)) | |
||||
CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(2)); |
||||
writel(reg_val, &ccm->mbus_clk_cfg); |
||||
|
||||
/*
|
||||
* open DRAMC AHB & DLL register clock |
||||
* close it first |
||||
*/ |
||||
clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL); |
||||
udelay(22); |
||||
|
||||
/* then open it */ |
||||
setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL); |
||||
udelay(22); |
||||
} |
||||
|
||||
static int dramc_scan_readpipe(void) |
||||
{ |
||||
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; |
||||
u32 reg_val; |
||||
|
||||
/* data training trigger */ |
||||
#ifdef CONFIG_SUN7I |
||||
clrbits_le32(&dram->csr, DRAM_CSR_FAILED); |
||||
#endif |
||||
setbits_le32(&dram->ccr, DRAM_CCR_DATA_TRAINING); |
||||
|
||||
/* check whether data training process has completed */ |
||||
await_completion(&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 int dramc_scan_dll_para(void) |
||||
{ |
||||
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; |
||||
const u32 dqs_dly[7] = {0x3, 0x2, 0x1, 0x0, 0xe, 0xd, 0xc}; |
||||
const u32 clk_dly[15] = {0x07, 0x06, 0x05, 0x04, 0x03, |
||||
0x02, 0x01, 0x00, 0x08, 0x10, |
||||
0x18, 0x20, 0x28, 0x30, 0x38}; |
||||
u32 clk_dqs_count[15]; |
||||
u32 dqs_i, clk_i, cr_i; |
||||
u32 max_val, min_val; |
||||
u32 dqs_index, clk_index; |
||||
|
||||
/* Find DQS_DLY Pass Count for every CLK_DLY */ |
||||
for (clk_i = 0; clk_i < 15; clk_i++) { |
||||
clk_dqs_count[clk_i] = 0; |
||||
clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, |
||||
(clk_dly[clk_i] & 0x3f) << 6); |
||||
for (dqs_i = 0; dqs_i < 7; dqs_i++) { |
||||
for (cr_i = 1; cr_i < 5; cr_i++) { |
||||
clrsetbits_le32(&dram->dllcr[cr_i], |
||||
0x4f << 14, |
||||
(dqs_dly[dqs_i] & 0x4f) << 14); |
||||
} |
||||
udelay(2); |
||||
if (dramc_scan_readpipe() == 0) |
||||
clk_dqs_count[clk_i]++; |
||||
} |
||||
} |
||||
/* Test DQS_DLY Pass Count for every CLK_DLY from up to down */ |
||||
for (dqs_i = 15; dqs_i > 0; dqs_i--) { |
||||
max_val = 15; |
||||
min_val = 15; |
||||
for (clk_i = 0; clk_i < 15; clk_i++) { |
||||
if (clk_dqs_count[clk_i] == dqs_i) { |
||||
max_val = clk_i; |
||||
if (min_val == 15) |
||||
min_val = clk_i; |
||||
} |
||||
} |
||||
if (max_val < 15) |
||||
break; |
||||
} |
||||
|
||||
/* Check if Find a CLK_DLY failed */ |
||||
if (!dqs_i) |
||||
goto fail; |
||||
|
||||
/* Find the middle index of CLK_DLY */ |
||||
clk_index = (max_val + min_val) >> 1; |
||||
if ((max_val == (15 - 1)) && (min_val > 0)) |
||||
/* if CLK_DLY[MCTL_CLK_DLY_COUNT] is very good, then the middle
|
||||
* value can be more close to the max_val |
||||
*/ |
||||
clk_index = (15 + clk_index) >> 1; |
||||
else if ((max_val < (15 - 1)) && (min_val == 0)) |
||||
/* if CLK_DLY[0] is very good, then the middle value can be more
|
||||
* close to the min_val |
||||
*/ |
||||
clk_index >>= 1; |
||||
if (clk_dqs_count[clk_index] < dqs_i) |
||||
clk_index = min_val; |
||||
|
||||
/* Find the middle index of DQS_DLY for the CLK_DLY got above, and Scan
|
||||
* read pipe again |
||||
*/ |
||||
clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, |
||||
(clk_dly[clk_index] & 0x3f) << 6); |
||||
max_val = 7; |
||||
min_val = 7; |
||||
for (dqs_i = 0; dqs_i < 7; dqs_i++) { |
||||
clk_dqs_count[dqs_i] = 0; |
||||
for (cr_i = 1; cr_i < 5; cr_i++) { |
||||
clrsetbits_le32(&dram->dllcr[cr_i], |
||||
0x4f << 14, |
||||
(dqs_dly[dqs_i] & 0x4f) << 14); |
||||
} |
||||
udelay(2); |
||||
if (dramc_scan_readpipe() == 0) { |
||||
clk_dqs_count[dqs_i] = 1; |
||||
max_val = dqs_i; |
||||
if (min_val == 7) |
||||
min_val = dqs_i; |
||||
} |
||||
} |
||||
|
||||
if (max_val < 7) { |
||||
dqs_index = (max_val + min_val) >> 1; |
||||
if ((max_val == (7-1)) && (min_val > 0)) |
||||
dqs_index = (7 + dqs_index) >> 1; |
||||
else if ((max_val < (7-1)) && (min_val == 0)) |
||||
dqs_index >>= 1; |
||||
if (!clk_dqs_count[dqs_index]) |
||||
dqs_index = min_val; |
||||
for (cr_i = 1; cr_i < 5; cr_i++) { |
||||
clrsetbits_le32(&dram->dllcr[cr_i], |
||||
0x4f << 14, |
||||
(dqs_dly[dqs_index] & 0x4f) << 14); |
||||
} |
||||
udelay(2); |
||||
return dramc_scan_readpipe(); |
||||
} |
||||
|
||||
fail: |
||||
clrbits_le32(&dram->dllcr[0], 0x3f << 6); |
||||
for (cr_i = 1; cr_i < 5; cr_i++) |
||||
clrbits_le32(&dram->dllcr[cr_i], 0x4f << 14); |
||||
udelay(2); |
||||
|
||||
return dramc_scan_readpipe(); |
||||
} |
||||
|
||||
static void dramc_clock_output_en(u32 on) |
||||
{ |
||||
#if defined(CONFIG_SUN5I) || defined(CONFIG_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 |
||||
} |
||||
|
||||
static const u16 tRFC_table[2][6] = { |
||||
/* 256Mb 512Mb 1Gb 2Gb 4Gb 8Gb */ |
||||
/* DDR2 75ns 105ns 127.5ns 195ns 327.5ns invalid */ |
||||
{ 77, 108, 131, 200, 336, 336 }, |
||||
/* DDR3 invalid 90ns 110ns 160ns 300ns 350ns */ |
||||
{ 93, 93, 113, 164, 308, 359 } |
||||
}; |
||||
|
||||
static void dramc_set_autorefresh_cycle(u32 clk, u32 type, u32 density) |
||||
{ |
||||
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; |
||||
u32 tRFC, tREFI; |
||||
|
||||
tRFC = (tRFC_table[type][density] * clk + 1023) >> 10; |
||||
tREFI = (7987 * clk) >> 10; /* <= 7.8us */ |
||||
|
||||
writel(DRAM_DRR_TREFI(tREFI) | DRAM_DRR_TRFC(tRFC), &dram->drr); |
||||
} |
||||
|
||||
unsigned long dramc_init(struct dram_para *para) |
||||
{ |
||||
struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; |
||||
u32 reg_val; |
||||
u32 density; |
||||
int ret_val; |
||||
|
||||
/* check input dram parameter structure */ |
||||
if (!para) |
||||
return 0; |
||||
|
||||
/* setup DRAM relative clock */ |
||||
mctl_setup_dram_clock(para->clock); |
||||
|
||||
/* reset external DRAM */ |
||||
mctl_set_drive(); |
||||
|
||||
/* dram clock off */ |
||||
dramc_clock_output_en(0); |
||||
|
||||
mctl_itm_disable(); |
||||
mctl_enable_dll0(para->tpr3); |
||||
|
||||
/* configure external DRAM */ |
||||
reg_val = 0x0; |
||||
if (para->type == DRAM_MEMORY_TYPE_DDR3) |
||||
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); |
||||
|
||||
#ifdef CONFIG_SUN7I |
||||
setbits_le32(&dram->zqcr1, (0x1 << 24) | (0x1 << 1)); |
||||
if (para->tpr4 & 0x2) |
||||
clrsetbits_le32(&dram->zqcr1, (0x1 << 24), (0x1 << 1)); |
||||
dramc_clock_output_en(1); |
||||
#endif |
||||
|
||||
#if (defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I)) |
||||
/* set odt impendance divide ratio */ |
||||
reg_val = ((para->zq) >> 8) & 0xfffff; |
||||
reg_val |= ((para->zq) & 0xff) << 20; |
||||
reg_val |= (para->zq) & 0xf0000000; |
||||
writel(reg_val, &dram->zqcr0); |
||||
#endif |
||||
|
||||
#ifdef CONFIG_SUN7I |
||||
/* Set CKE Delay to about 1ms */ |
||||
setbits_le32(&dram->idcr, 0x1ffff); |
||||
#endif |
||||
|
||||
#ifdef CONFIG_SUN7I |
||||
if ((readl(&dram->ppwrsctl) & 0x1) != 0x1) |
||||
mctl_ddr3_reset(); |
||||
else |
||||
setbits_le32(&dram->mcr, DRAM_MCR_RESET); |
||||
#endif |
||||
|
||||
udelay(1); |
||||
|
||||
await_completion(&dram->ccr, DRAM_CCR_INIT); |
||||
|
||||
mctl_enable_dllx(para->tpr3); |
||||
|
||||
/* set refresh period */ |
||||
dramc_set_autorefresh_cycle(para->clock, para->type - 2, density); |
||||
|
||||
/* set timing parameters */ |
||||
writel(para->tpr0, &dram->tpr0); |
||||
writel(para->tpr1, &dram->tpr1); |
||||
writel(para->tpr2, &dram->tpr2); |
||||
|
||||
if (para->type == DRAM_MEMORY_TYPE_DDR3) { |
||||
reg_val = DRAM_MR_BURST_LENGTH(0x0); |
||||
#if (defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I)) |
||||
reg_val |= DRAM_MR_POWER_DOWN; |
||||
#endif |
||||
reg_val |= DRAM_MR_CAS_LAT(para->cas - 4); |
||||
reg_val |= DRAM_MR_WRITE_RECOVERY(0x5); |
||||
} else if (para->type == DRAM_MEMORY_TYPE_DDR2) { |
||||
reg_val = DRAM_MR_BURST_LENGTH(0x2); |
||||
reg_val |= DRAM_MR_CAS_LAT(para->cas); |
||||
reg_val |= DRAM_MR_WRITE_RECOVERY(0x5); |
||||
} |
||||
writel(reg_val, &dram->mr); |
||||
|
||||
writel(para->emr1, &dram->emr); |
||||
writel(para->emr2, &dram->emr2); |
||||
writel(para->emr3, &dram->emr3); |
||||
|
||||
/* set DQS window mode */ |
||||
clrsetbits_le32(&dram->ccr, DRAM_CCR_DQS_DRIFT_COMP, DRAM_CCR_DQS_GATE); |
||||
|
||||
#ifdef CONFIG_SUN7I |
||||
/* Command rate timing mode 2T & 1T */ |
||||
if (para->tpr4 & 0x1) |
||||
setbits_le32(&dram->ccr, DRAM_CCR_COMMAND_RATE_1T); |
||||
#endif |
||||
/* reset external DRAM */ |
||||
setbits_le32(&dram->ccr, DRAM_CCR_INIT); |
||||
await_completion(&dram->ccr, DRAM_CCR_INIT); |
||||
|
||||
#ifdef CONFIG_SUN7I |
||||
/* setup zq calibration manual */ |
||||
reg_val = readl(&dram->ppwrsctl); |
||||
if ((reg_val & 0x1) == 1) { |
||||
/* super_standby_flag = 1 */ |
||||
|
||||
reg_val = readl(0x01c20c00 + 0x120); /* rtc */ |
||||
reg_val &= 0x000fffff; |
||||
reg_val |= 0x17b00000; |
||||
writel(reg_val, &dram->zqcr0); |
||||
|
||||
/* exit self-refresh state */ |
||||
clrsetbits_le32(&dram->dcr, 0x1f << 27, 0x12 << 27); |
||||
/* check whether command has been executed */ |
||||
await_completion(&dram->dcr, 0x1 << 31); |
||||
|
||||
udelay(2); |
||||
|
||||
/* dram pad hold off */ |
||||
setbits_le32(&dram->ppwrsctl, 0x16510000); |
||||
|
||||
await_completion(&dram->ppwrsctl, 0x1); |
||||
|
||||
/* exit self-refresh state */ |
||||
clrsetbits_le32(&dram->dcr, 0x1f << 27, 0x12 << 27); |
||||
|
||||
/* check whether command has been executed */ |
||||
await_completion(&dram->dcr, 0x1 << 31); |
||||
|
||||
udelay(2); |
||||
|
||||
/* issue a refresh command */ |
||||
clrsetbits_le32(&dram->dcr, 0x1f << 27, 0x13 << 27); |
||||
await_completion(&dram->dcr, 0x1 << 31); |
||||
|
||||
udelay(2); |
||||
} |
||||
#endif |
||||
|
||||
/* scan read pipe value */ |
||||
mctl_itm_enable(); |
||||
if (para->tpr3 & (0x1 << 31)) { |
||||
ret_val = dramc_scan_dll_para(); |
||||
if (ret_val == 0) |
||||
para->tpr3 = |
||||
(((readl(&dram->dllcr[0]) >> 6) & 0x3f) << 16) | |
||||
(((readl(&dram->dllcr[1]) >> 14) & 0xf) << 0) | |
||||
(((readl(&dram->dllcr[2]) >> 14) & 0xf) << 4) | |
||||
(((readl(&dram->dllcr[3]) >> 14) & 0xf) << 8) | |
||||
(((readl(&dram->dllcr[4]) >> 14) & 0xf) << 12 |
||||
); |
||||
} else { |
||||
ret_val = dramc_scan_readpipe(); |
||||
} |
||||
|
||||
if (ret_val < 0) |
||||
return 0; |
||||
|
||||
/* configure all host port */ |
||||
mctl_configure_hostport(); |
||||
|
||||
return get_ram_size((long *)PHYS_SDRAM_0, PHYS_SDRAM_0_SIZE); |
||||
} |
@ -0,0 +1,179 @@ |
||||
/*
|
||||
* (C) Copyright 2007-2012 |
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com> |
||||
* Berg Xing <bergxing@allwinnertech.com> |
||||
* Tom Cubie <tangliang@allwinnertech.com> |
||||
* |
||||
* Sunxi platform dram register definition. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#ifndef _SUNXI_DRAM_H |
||||
#define _SUNXI_DRAM_H |
||||
|
||||
#include <linux/types.h> |
||||
|
||||
struct sunxi_dram_reg { |
||||
u32 ccr; /* 0x00 controller configuration register */ |
||||
u32 dcr; /* 0x04 dram configuration register */ |
||||
u32 iocr; /* 0x08 i/o configuration register */ |
||||
u32 csr; /* 0x0c controller status register */ |
||||
u32 drr; /* 0x10 dram refresh register */ |
||||
u32 tpr0; /* 0x14 dram timing parameters register 0 */ |
||||
u32 tpr1; /* 0x18 dram timing parameters register 1 */ |
||||
u32 tpr2; /* 0x1c dram timing parameters register 2 */ |
||||
u32 gdllcr; /* 0x20 global dll control register */ |
||||
u8 res0[0x28]; |
||||
u32 rslr0; /* 0x4c rank system latency register */ |
||||
u32 rslr1; /* 0x50 rank system latency register */ |
||||
u8 res1[0x8]; |
||||
u32 rdgr0; /* 0x5c rank dqs gating register */ |
||||
u32 rdgr1; /* 0x60 rank dqs gating register */ |
||||
u8 res2[0x34]; |
||||
u32 odtcr; /* 0x98 odt configuration register */ |
||||
u32 dtr0; /* 0x9c data training register 0 */ |
||||
u32 dtr1; /* 0xa0 data training register 1 */ |
||||
u32 dtar; /* 0xa4 data training address register */ |
||||
u32 zqcr0; /* 0xa8 zq control register 0 */ |
||||
u32 zqcr1; /* 0xac zq control register 1 */ |
||||
u32 zqsr; /* 0xb0 zq status register */ |
||||
u32 idcr; /* 0xb4 initializaton delay configure reg */ |
||||
u8 res3[0x138]; |
||||
u32 mr; /* 0x1f0 mode register */ |
||||
u32 emr; /* 0x1f4 extended mode register */ |
||||
u32 emr2; /* 0x1f8 extended mode register */ |
||||
u32 emr3; /* 0x1fc extended mode register */ |
||||
u32 dllctr; /* 0x200 dll control register */ |
||||
u32 dllcr[5]; /* 0x204 dll control register 0(byte 0) */ |
||||
/* 0x208 dll control register 1(byte 1) */ |
||||
/* 0x20c dll control register 2(byte 2) */ |
||||
/* 0x210 dll control register 3(byte 3) */ |
||||
/* 0x214 dll control register 4(byte 4) */ |
||||
u32 dqtr0; /* 0x218 dq timing register */ |
||||
u32 dqtr1; /* 0x21c dq timing register */ |
||||
u32 dqtr2; /* 0x220 dq timing register */ |
||||
u32 dqtr3; /* 0x224 dq timing register */ |
||||
u32 dqstr; /* 0x228 dqs timing register */ |
||||
u32 dqsbtr; /* 0x22c dqsb timing register */ |
||||
u32 mcr; /* 0x230 mode configure register */ |
||||
u8 res[0x8]; |
||||
u32 ppwrsctl; /* 0x23c pad power save control */ |
||||
u32 apr; /* 0x240 arbiter period register */ |
||||
u32 pldtr; /* 0x244 priority level data threshold reg */ |
||||
u8 res5[0x8]; |
||||
u32 hpcr[32]; /* 0x250 host port configure register */ |
||||
u8 res6[0x10]; |
||||
u32 csel; /* 0x2e0 controller select register */ |
||||
}; |
||||
|
||||
struct dram_para { |
||||
u32 clock; |
||||
u32 type; |
||||
u32 rank_num; |
||||
u32 density; |
||||
u32 io_width; |
||||
u32 bus_width; |
||||
u32 cas; |
||||
u32 zq; |
||||
u32 odt_en; |
||||
u32 size; |
||||
u32 tpr0; |
||||
u32 tpr1; |
||||
u32 tpr2; |
||||
u32 tpr3; |
||||
u32 tpr4; |
||||
u32 tpr5; |
||||
u32 emr1; |
||||
u32 emr2; |
||||
u32 emr3; |
||||
}; |
||||
|
||||
#define DRAM_CCR_COMMAND_RATE_1T (0x1 << 5) |
||||
#define DRAM_CCR_DQS_GATE (0x1 << 14) |
||||
#define DRAM_CCR_DQS_DRIFT_COMP (0x1 << 17) |
||||
#define DRAM_CCR_ITM_OFF (0x1 << 28) |
||||
#define DRAM_CCR_DATA_TRAINING (0x1 << 30) |
||||
#define DRAM_CCR_INIT (0x1 << 31) |
||||
|
||||
#define DRAM_MEMORY_TYPE_DDR1 1 |
||||
#define DRAM_MEMORY_TYPE_DDR2 2 |
||||
#define DRAM_MEMORY_TYPE_DDR3 3 |
||||
#define DRAM_MEMORY_TYPE_LPDDR2 4 |
||||
#define DRAM_MEMORY_TYPE_LPDDR 5 |
||||
#define DRAM_DCR_TYPE (0x1 << 0) |
||||
#define DRAM_DCR_TYPE_DDR2 0x0 |
||||
#define DRAM_DCR_TYPE_DDR3 0x1 |
||||
#define DRAM_DCR_IO_WIDTH(n) (((n) & 0x3) << 1) |
||||
#define DRAM_DCR_IO_WIDTH_MASK DRAM_DCR_IO_WIDTH(0x3) |
||||
#define DRAM_DCR_IO_WIDTH_8BIT 0x0 |
||||
#define DRAM_DCR_IO_WIDTH_16BIT 0x1 |
||||
#define DRAM_DCR_CHIP_DENSITY(n) (((n) & 0x7) << 3) |
||||
#define DRAM_DCR_CHIP_DENSITY_MASK DRAM_DCR_CHIP_DENSITY(0x7) |
||||
#define DRAM_DCR_CHIP_DENSITY_256M 0x0 |
||||
#define DRAM_DCR_CHIP_DENSITY_512M 0x1 |
||||
#define DRAM_DCR_CHIP_DENSITY_1024M 0x2 |
||||
#define DRAM_DCR_CHIP_DENSITY_2048M 0x3 |
||||
#define DRAM_DCR_CHIP_DENSITY_4096M 0x4 |
||||
#define DRAM_DCR_CHIP_DENSITY_8192M 0x5 |
||||
#define DRAM_DCR_BUS_WIDTH(n) (((n) & 0x7) << 6) |
||||
#define DRAM_DCR_BUS_WIDTH_MASK DRAM_DCR_BUS_WIDTH(0x7) |
||||
#define DRAM_DCR_BUS_WIDTH_32BIT 0x3 |
||||
#define DRAM_DCR_BUS_WIDTH_16BIT 0x1 |
||||
#define DRAM_DCR_BUS_WIDTH_8BIT 0x0 |
||||
#define DRAM_DCR_NR_DLLCR_32BIT 5 |
||||
#define DRAM_DCR_NR_DLLCR_16BIT 3 |
||||
#define DRAM_DCR_NR_DLLCR_8BIT 2 |
||||
#define DRAM_DCR_RANK_SEL(n) (((n) & 0x3) << 10) |
||||
#define DRAM_DCR_RANK_SEL_MASK DRAM_DCR_CMD_RANK(0x3) |
||||
#define DRAM_DCR_CMD_RANK_ALL (0x1 << 12) |
||||
#define DRAM_DCR_MODE(n) (((n) & 0x3) << 13) |
||||
#define DRAM_DCR_MODE_MASK DRAM_DCR_MODE(0x3) |
||||
#define DRAM_DCR_MODE_SEQ 0x0 |
||||
#define DRAM_DCR_MODE_INTERLEAVE 0x1 |
||||
|
||||
#define DRAM_CSR_FAILED (0x1 << 20) |
||||
|
||||
#define DRAM_DRR_TRFC(n) ((n) & 0xff) |
||||
#define DRAM_DRR_TREFI(n) (((n) & 0xffff) << 8) |
||||
#define DRAM_DRR_BURST(n) ((((n) - 1) & 0xf) << 24) |
||||
|
||||
#define DRAM_MCR_MODE_NORM(n) (((n) & 0x3) << 0) |
||||
#define DRAM_MCR_MODE_NORM_MASK DRAM_MCR_MOD_NORM(0x3) |
||||
#define DRAM_MCR_MODE_DQ_OUT(n) (((n) & 0x3) << 2) |
||||
#define DRAM_MCR_MODE_DQ_OUT_MASK DRAM_MCR_MODE_DQ_OUT(0x3) |
||||
#define DRAM_MCR_MODE_ADDR_OUT(n) (((n) & 0x3) << 4) |
||||
#define DRAM_MCR_MODE_ADDR_OUT_MASK DRAM_MCR_MODE_ADDR_OUT(0x3) |
||||
#define DRAM_MCR_MODE_DQ_IN_OUT(n) (((n) & 0x3) << 6) |
||||
#define DRAM_MCR_MODE_DQ_IN_OUT_MASK DRAM_MCR_MODE_DQ_IN_OUT(0x3) |
||||
#define DRAM_MCR_MODE_DQ_TURNON_DELAY(n) (((n) & 0x7) << 8) |
||||
#define DRAM_MCR_MODE_DQ_TURNON_DELAY_MASK DRAM_MCR_MODE_DQ_TURNON_DELAY(0x7) |
||||
#define DRAM_MCR_MODE_ADDR_IN (0x1 << 11) |
||||
#define DRAM_MCR_RESET (0x1 << 12) |
||||
#define DRAM_MCR_MODE_EN(n) (((n) & 0x3) << 13) |
||||
#define DRAM_MCR_MODE_EN_MASK DRAM_MCR_MOD_EN(0x3) |
||||
#define DRAM_MCR_DCLK_OUT (0x1 << 16) |
||||
|
||||
#define DRAM_DLLCR_NRESET (0x1 << 30) |
||||
#define DRAM_DLLCR_DISABLE (0x1 << 31) |
||||
|
||||
#define DRAM_ZQCR0_IMP_DIV(n) (((n) & 0xff) << 20) |
||||
#define DRAM_ZQCR0_IMP_DIV_MASK DRAM_ZQCR0_IMP_DIV(0xff) |
||||
|
||||
#define DRAM_IOCR_ODT_EN(n) ((((n) & 0x3) << 30) | ((n) & 0x3) << 0) |
||||
#define DRAM_IOCR_ODT_EN_MASK DRAM_IOCR_ODT_EN(0x3) |
||||
|
||||
#define DRAM_MR_BURST_LENGTH(n) (((n) & 0x7) << 0) |
||||
#define DRAM_MR_BURST_LENGTH_MASK DRAM_MR_BURST_LENGTH(0x7) |
||||
#define DRAM_MR_CAS_LAT(n) (((n) & 0x7) << 4) |
||||
#define DRAM_MR_CAS_LAT_MASK DRAM_MR_CAS_LAT(0x7) |
||||
#define DRAM_MR_WRITE_RECOVERY(n) (((n) & 0x7) << 9) |
||||
#define DRAM_MR_WRITE_RECOVERY_MASK DRAM_MR_WRITE_RECOVERY(0x7) |
||||
#define DRAM_MR_POWER_DOWN (0x1 << 12) |
||||
|
||||
#define DRAM_CSEL_MAGIC 0x16237495 |
||||
|
||||
unsigned long sunxi_dram_init(void); |
||||
unsigned long dramc_init(struct dram_para *para); |
||||
|
||||
#endif /* _SUNXI_DRAM_H */ |
Loading…
Reference in new issue