This adds support for initializing DDR3 RAMs on the i.MX53 type SDRAM controller. The code automatically detects size/layout of the connected RAM, detects the bus width and which chipselects are populated. While I believe this code is not 100% generic, it is far too sophisticated to stay in a single board directory. I'm sure other boards could make use of this aswell. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
711 lines
22 KiB
C
711 lines
22 KiB
C
/*
|
|
* esdctl-v4.c - i.MX sdram controller functions for i.MX53
|
|
*
|
|
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
|
|
*
|
|
* See file CREDITS for list of people who contributed to this
|
|
* project.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <io.h>
|
|
#include <mach/esdctl-v4.h>
|
|
#include <mach/imx53-regs.h>
|
|
#include <asm/system.h>
|
|
|
|
void imx_esdctlv4_do_write_leveling(void)
|
|
{
|
|
u32 val;
|
|
void __iomem *base = (void *)MX53_ESDCTL_BASE_ADDR;
|
|
|
|
/* switch RAMs to write-leveling mode */
|
|
|
|
val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_DIS |
|
|
ESDCTL_V4_DDR3_MR1_AL_DISABLE | ESDCTL_V4_DDR3_MR1_WL |
|
|
ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
|
|
ESDCTL_V4_ESDSCR_CMD_CS0 | ESDCTL_V4_ESDSCR_WL_EN |
|
|
ESDCTL_V4_DDR3_REG_MR1;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_DIS |
|
|
ESDCTL_V4_DDR3_MR1_AL_DISABLE | ESDCTL_V4_DDR3_MR1_WL |
|
|
ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
|
|
ESDCTL_V4_ESDSCR_CMD_CS1 | ESDCTL_V4_ESDSCR_WL_EN |
|
|
ESDCTL_V4_DDR3_REG_MR1;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* start write leveling */
|
|
writel(ESDCTL_V4_WLGCR_HW_WL_EN, base + ESDCTL_V4_WLGCR);
|
|
|
|
do {
|
|
val = readl(base + ESDCTL_V4_WLGCR);
|
|
} while (val & ESDCTL_V4_WLGCR_HW_WL_EN);
|
|
|
|
val &= ESDCTL_V4_WLGCR_WL_HW_ERR3 | ESDCTL_V4_WLGCR_WL_HW_ERR2 |
|
|
ESDCTL_V4_WLGCR_WL_HW_ERR1 | ESDCTL_V4_WLGCR_WL_HW_ERR0;
|
|
|
|
if (val)
|
|
hang();
|
|
|
|
/*
|
|
* manual still talks about HW_WLx_CYC fields that
|
|
* must be updated from HW_WLx_RES field by SW, but none
|
|
* of these fields seem to exist. Closest equivalents
|
|
* are WL_CYC_DELx-fields but no RES-fields can be found.
|
|
*/
|
|
|
|
/* configure RAMs back to normal operation */
|
|
val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_RZQ2 |
|
|
ESDCTL_V4_DDR3_MR1_AL_DISABLE | ESDCTL_V4_ESDSCR_CON_REQ |
|
|
ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_ESDSCR_CMD_CS0 |
|
|
ESDCTL_V4_DDR3_REG_MR1;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_RZQ2 |
|
|
ESDCTL_V4_DDR3_MR1_AL_DISABLE | ESDCTL_V4_ESDSCR_CON_REQ |
|
|
ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_ESDSCR_CMD_CS1 |
|
|
ESDCTL_V4_DDR3_REG_MR1;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
}
|
|
|
|
void imx_esdctlv4_do_dqs_gating(void)
|
|
{
|
|
u32 val;
|
|
void __iomem *base = (void *)MX53_ESDCTL_BASE_ADDR;
|
|
|
|
/* configure ESDCTL comparator to use MPR pattern */
|
|
writel(ESDCTL_V4_PDCMPR2_MPR_FULL_CMP | ESDCTL_V4_PDCMPR2_MPR_CMP,
|
|
base + ESDCTL_V4_PDCMPR2);
|
|
|
|
/* pre-charge all RAM banks */
|
|
writel(ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS0 |
|
|
ESDCTL_V4_ESDSCR_CMD_PRE_ALL, base + ESDCTL_V4_ESDSCR);
|
|
writel(ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS1 |
|
|
ESDCTL_V4_ESDSCR_CMD_PRE_ALL, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* configure RAMs to generate MPR pattern */
|
|
writel(ESDCTL_V4_DDR3_MR3_MPR_ENABLE | ESDCTL_V4_DDR3_MR3_MPR_PATTERN |
|
|
ESDCTL_V4_ESDSCR_CMD_CS0 | ESDCTL_V4_ESDSCR_CON_REQ |
|
|
ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3,
|
|
base + ESDCTL_V4_ESDSCR);
|
|
writel(ESDCTL_V4_DDR3_MR3_MPR_ENABLE | ESDCTL_V4_DDR3_MR3_MPR_PATTERN |
|
|
ESDCTL_V4_ESDSCR_CMD_CS1 | ESDCTL_V4_ESDSCR_CON_REQ |
|
|
ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3,
|
|
base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* start HW DQS gating */
|
|
writel(ESDCTL_V4_ESDDGCTRL0_HW_DG_EN, base + ESDCTL_V4_DGCTRL0);
|
|
|
|
do {
|
|
val = readl(base + ESDCTL_V4_DGCTRL0);
|
|
} while (val & ESDCTL_V4_ESDDGCTRL0_HW_DG_EN);
|
|
|
|
if (val & ESDCTL_V4_ESDDGCTRL0_HW_DG_ERR)
|
|
hang();
|
|
|
|
/* configure RAMs back to normal operation */
|
|
val = ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CMD_CS0 |
|
|
ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
|
|
ESDCTL_V4_DDR3_REG_MR3;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
val = ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CMD_CS1 |
|
|
ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
|
|
ESDCTL_V4_DDR3_REG_MR3;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
}
|
|
|
|
void imx_esdctlv4_do_zq_calibration(void)
|
|
{
|
|
u32 val;
|
|
void __iomem *base = (void *)MX53_ESDCTL_BASE_ADDR;
|
|
|
|
/*
|
|
* configure ZQ parameters
|
|
* Note: TZQ_CS is only required to be 64cy by Jedec and RAM
|
|
* manufacturers and i.MX53TO1 provides a 64cy setting, but
|
|
* TO2 according to official documentation not...
|
|
*/
|
|
val = ESDCTL_V4_ESDZQHWC_ZQ_PARA_EN |
|
|
(ESDCTL_V4_ESDZQHWC_128CYC << ESDCTL_V4_ESDZQHWC_TZQ_CS_SHIFT) |
|
|
(ESDCTL_V4_ESDZQHWC_256CYC << ESDCTL_V4_ESDZQHWC_TZQ_OPER_SHIFT) |
|
|
(ESDCTL_V4_ESDZQHWC_512CYC << ESDCTL_V4_ESDZQHWC_TZQ_INIT_SHIFT) |
|
|
(0 << ESDCTL_V4_ESDZQHWC_ZQ_HW_PD_RES_SHIFT) |
|
|
(0 << ESDCTL_V4_ESDZQHWC_ZQ_HW_PU_RES_SHIFT) |
|
|
(0 << ESDCTL_V4_ESDZQHWC_ZQ_HW_PER_SHIFT) |
|
|
ESDCTL_V4_ESDZQHWC_ZQ_MODE_BOTH_PER |
|
|
ESDCTL_V4_ESDZQHWC_ZQ_HW_FOR;
|
|
|
|
/* force ZQ calibration */
|
|
writel(val, base + ESDCTL_V4_ZQHWCTRL);
|
|
|
|
while (readl(base + ESDCTL_V4_ZQHWCTRL) & ESDCTL_V4_ESDZQHWC_ZQ_HW_FOR);
|
|
}
|
|
|
|
/*
|
|
* start-up a DDR3 SDRAM chip
|
|
*/
|
|
void imx_esdctlv4_start_ddr3_sdram(int cs)
|
|
{
|
|
void __iomem *base = (void *)MX53_ESDCTL_BASE_ADDR;
|
|
u32 val;
|
|
u32 val_cs1;
|
|
|
|
if (cs)
|
|
val_cs1 = ESDCTL_V4_ESDSCR_CMD_CS1;
|
|
else
|
|
val_cs1 = ESDCTL_V4_ESDSCR_CMD_CS0;
|
|
|
|
/* initialize MR2 */
|
|
val = ESDCTL_V4_DDR3_MR2_RTTWR_OFF | ESDCTL_V4_DDR3_MR2_SRT_NORMAL |
|
|
ESDCTL_V4_DDR3_MR2_CWL_5 | ESDCTL_V4_DDR3_MR2_PASR_1_1 |
|
|
ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
|
|
ESDCTL_V4_DDR3_REG_MR2 | val_cs1;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* initialize MR3 */
|
|
val = ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CON_REQ |
|
|
ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3 | val_cs1;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* initialize MR1 */
|
|
val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_RZQ2 |
|
|
ESDCTL_V4_DDR3_MR1_AL_DISABLE | ESDCTL_V4_ESDSCR_CON_REQ |
|
|
ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR1 | val_cs1;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* initialize MR0 */
|
|
val = ESDCTL_V4_DDR3_MR0_PPD_SLOW | ESDCTL_V4_DDR3_MR0_WR_6 |
|
|
ESDCTL_V4_DDR3_MR0_CL_6 | ESDCTL_V4_DDR3_MR0_BL_FIXED8 |
|
|
ESDCTL_V4_DDR3_MR0_RBT_NIBBLE | ESDCTL_V4_DDR3_DLL_RESET |
|
|
ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
|
|
ESDCTL_V4_DDR3_REG_MR0 | val_cs1;
|
|
|
|
if (cs)
|
|
val = ESDCTL_V4_ESDSCR_DLL_RST1;
|
|
else
|
|
val = ESDCTL_V4_ESDSCR_DLL_RST0;
|
|
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* perform ZQ calibration */
|
|
val = 0x04000000 | ESDCTL_V4_ESDSCR_CON_REQ |
|
|
ESDCTL_V4_ESDSCR_CMD_ZQCALIB_OLD;
|
|
val |= val_cs1;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
}
|
|
|
|
void imx_esdctlv4_do_read_delay_line_calibration(void)
|
|
{
|
|
void __iomem *base = (void *)MX53_ESDCTL_BASE_ADDR;
|
|
u32 val;
|
|
|
|
/* configure ESDCTL comparator to use MPR pattern */
|
|
val = ESDCTL_V4_PDCMPR2_MPR_FULL_CMP | ESDCTL_V4_PDCMPR2_MPR_CMP;
|
|
writel(val, base + ESDCTL_V4_PDCMPR2);
|
|
|
|
/* pre-charge all RAM banks */
|
|
val = ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS0 |
|
|
ESDCTL_V4_ESDSCR_CMD_PRE_ALL;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
val = ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS1 |
|
|
ESDCTL_V4_ESDSCR_CMD_PRE_ALL;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* configure RAMs to generate MPR pattern */
|
|
val = ESDCTL_V4_DDR3_MR3_MPR_ENABLE | ESDCTL_V4_DDR3_MR3_MPR_PATTERN |
|
|
ESDCTL_V4_ESDSCR_CMD_CS0 | ESDCTL_V4_ESDSCR_CON_REQ |
|
|
ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
val = ESDCTL_V4_DDR3_MR3_MPR_ENABLE | ESDCTL_V4_DDR3_MR3_MPR_PATTERN |
|
|
ESDCTL_V4_ESDSCR_CMD_CS1 | ESDCTL_V4_ESDSCR_CON_REQ |
|
|
ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* start read delay-line calibration */
|
|
writel(ESDCTL_V4_RDDLHWCTL_HW_RDL_EN, base + ESDCTL_V4_RDDLHWCTL);
|
|
|
|
do {
|
|
val = readl(base + ESDCTL_V4_RDDLHWCTL);
|
|
} while (val & ESDCTL_V4_RDDLHWCTL_HW_RDL_EN);
|
|
|
|
val &= ESDCTL_V4_RDDLHWCTL_HW_RDL_ERR3 |
|
|
ESDCTL_V4_RDDLHWCTL_HW_RDL_ERR2 |
|
|
ESDCTL_V4_RDDLHWCTL_HW_RDL_ERR1 |
|
|
ESDCTL_V4_RDDLHWCTL_HW_RDL_ERR0;
|
|
|
|
if (val)
|
|
hang();
|
|
|
|
/* configure RAMs back to normal operation */
|
|
val = ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CMD_CS0 |
|
|
ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
|
|
ESDCTL_V4_DDR3_REG_MR3;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
val = ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CMD_CS1 |
|
|
ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
|
|
ESDCTL_V4_DDR3_REG_MR3;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
}
|
|
|
|
void imx_esdctlv4_do_write_delay_line_calibration(void)
|
|
{
|
|
void __iomem *base = (void *)MX53_ESDCTL_BASE_ADDR;
|
|
void __iomem *adr;
|
|
u32 val;
|
|
|
|
/*
|
|
* configure ESCTL to normal operation so we can
|
|
* write the compare values
|
|
*/
|
|
writel(ESDCTL_V4_ESDSCR_CMD_NOP, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* write test-pattern to RAM */
|
|
|
|
/* ESCTL uses this address for calibration */
|
|
adr = (void *)MX53_CSD0_BASE_ADDR + 0x10000000;
|
|
writel(0, adr + 0x00);
|
|
writel(0, adr + 0x0c);
|
|
writel(0, adr + 0x10);
|
|
writel(0, adr + 0x1c);
|
|
writel(0, adr + 0x20);
|
|
writel(0, adr + 0x2c);
|
|
writel(0, adr + 0x30);
|
|
writel(0, adr + 0x4c);
|
|
writel(0xffffffff, adr + 0x04);
|
|
writel(0xffffffff, adr + 0x08);
|
|
writel(0xffffffff, adr + 0x14);
|
|
writel(0xffffffff, adr + 0x18);
|
|
writel(0xffffffff, adr + 0x24);
|
|
writel(0xffffffff, adr + 0x28);
|
|
writel(0xffffffff, adr + 0x34);
|
|
writel(0xffffffff, adr + 0x48);
|
|
|
|
/* pre-charge all RAM banks */
|
|
val = ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS0 |
|
|
ESDCTL_V4_ESDSCR_CMD_PRE_ALL;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
val = ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS1 |
|
|
ESDCTL_V4_ESDSCR_CMD_PRE_ALL;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* write test-pattern to ESCTL */
|
|
writel(0x00ffff00, base + ESDCTL_V4_PDCMPR1);
|
|
writel(0, base + ESDCTL_V4_PDCMPR2);
|
|
|
|
/* start write delay-line calibration */
|
|
writel(ESDCTL_V4_WRDLHWCTL_HW_WDL_EN, base + ESDCTL_V4_WRDLHWCTL);
|
|
|
|
do {
|
|
val = readl(base + ESDCTL_V4_WRDLHWCTL);
|
|
} while (val & ESDCTL_V4_WRDLHWCTL_HW_WDL_EN);
|
|
|
|
val &= ESDCTL_V4_WRDLHWCTL_HW_WDL_ERR3 |
|
|
ESDCTL_V4_WRDLHWCTL_HW_WDL_ERR2 |
|
|
ESDCTL_V4_WRDLHWCTL_HW_WDL_ERR1 |
|
|
ESDCTL_V4_WRDLHWCTL_HW_WDL_ERR0;
|
|
|
|
if (val)
|
|
hang();
|
|
}
|
|
|
|
#define SDRAM_COMPARE_CONST1 0x12345678
|
|
#define SDRAM_COMPARE_CONST2 0xAAAAAAAA
|
|
|
|
/*
|
|
* write magic values to RAM for testing purposes
|
|
*/
|
|
static void imx_esdctlv4_write_magic_values(void *adr)
|
|
{
|
|
/*
|
|
* Freescale asks for first access to be a write to properly
|
|
* initialize DQS pin-state and keepers
|
|
*/
|
|
writel(SDRAM_COMPARE_CONST1, adr);
|
|
|
|
/*
|
|
* write two magic constants to RAM, to test for bus-size and
|
|
* row-/column-configuraion
|
|
*/
|
|
writel(SDRAM_COMPARE_CONST1, adr);
|
|
writel(SDRAM_COMPARE_CONST2, adr + 4);
|
|
dsb();
|
|
}
|
|
|
|
/*
|
|
* check if given DRAM addresses match expected values for row/col configuration
|
|
*/
|
|
static u32 check_ram_address_line(void *adr, u32 compare, u32 mask)
|
|
{
|
|
u32 val;
|
|
|
|
val = readl(adr); /* load data from RAM-address to check */
|
|
val &= mask; /* mask data for our bus-size */
|
|
|
|
if (compare == val) /* compare read data with expected value */
|
|
return 1; /* if data is identical, update ESDCTL configuration */
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* determine RAM chip-density and configure tRFC and tXS accordingly
|
|
*/
|
|
void imx_esdctlv4_set_tRFC_timing(void)
|
|
{
|
|
void __iomem *base = (void *)MX53_ESDCTL_BASE_ADDR;
|
|
u32 val, trfc, r2, esdcfg;
|
|
|
|
/* determine chip-density */
|
|
val = readl(base + ESDCTL_V4_ESDMISC);
|
|
if ((val & ESDCTL_V4_ESDMISC_BANKS_MASK) == ESDCTL_V4_ESDMISC_BANKS_4)
|
|
r2 = 2;
|
|
else
|
|
r2 = 3;
|
|
|
|
val = readl(base + ESDCTL_V4_ESDCTL0);
|
|
if ((val & ESDCTL_V4_ESDCTLx_DSIZ_MASK) == ESDCTL_V4_ESDCTLx_DSIZ_32B)
|
|
r2 += 1;
|
|
|
|
switch (val & ESDCTL_V4_ESDCTLx_ROW_MASK) {
|
|
case ESDCTL_V4_ESDCTLx_ROW_11: r2 += 1; break;
|
|
case ESDCTL_V4_ESDCTLx_ROW_12: r2 += 2; break;
|
|
case ESDCTL_V4_ESDCTLx_ROW_13: r2 += 3; break;
|
|
case ESDCTL_V4_ESDCTLx_ROW_14: r2 += 4; break;
|
|
case ESDCTL_V4_ESDCTLx_ROW_15: r2 += 5; break;
|
|
case ESDCTL_V4_ESDCTLx_ROW_16: r2 += 6; break;
|
|
}
|
|
|
|
switch (val & ESDCTL_V4_ESDCTLx_COL_MASK) {
|
|
case ESDCTL_V4_ESDCTLx_COL_8: r2 += 8; break;
|
|
case ESDCTL_V4_ESDCTLx_COL_9: r2 += 9; break;
|
|
case ESDCTL_V4_ESDCTLx_COL_10: r2 += 10; break;
|
|
case ESDCTL_V4_ESDCTLx_COL_11: r2 += 11; break;
|
|
case ESDCTL_V4_ESDCTLx_COL_12: r2 += 12; break;
|
|
}
|
|
|
|
/* save current tRFC timing */
|
|
esdcfg = readl(base + ESDCTL_V4_ESDCFG0);
|
|
|
|
trfc = (esdcfg & ESDCTL_V4_ESDCFG0_tRFC_MASK) >> ESDCTL_V4_ESDCFG0_tRFC_SHIFT;
|
|
|
|
/* clear tRFC and tXS timings */
|
|
esdcfg &= ~(ESDCTL_V4_ESDCFG0_tRFC_MASK | ESDCTL_V4_ESDCFG0_tXS_MASK);
|
|
|
|
/*
|
|
* determine tRFC depending on density
|
|
* (the timings and density-associations are taken
|
|
* from JEDEC JESD79-3E DDR3-RAM specification)
|
|
*/
|
|
if (r2 >= 16)
|
|
trfc = 36 - 1;
|
|
if (r2 >= 17)
|
|
trfc = 44 - 1;
|
|
if (r2 >= 18)
|
|
trfc = 64 - 1;
|
|
if (r2 >= 19)
|
|
trfc = 120 - 1;
|
|
if (r2 >= 20)
|
|
trfc = 140 - 1;
|
|
|
|
/* calculate new tRFC and tXS timings */
|
|
esdcfg |= (trfc << ESDCTL_V4_ESDCFG0_tRFC_SHIFT) |
|
|
(trfc + 4) << ESDCTL_V4_ESDCFG0_tXS_SHIFT;
|
|
|
|
writel(esdcfg, base + ESDCTL_V4_ESDCFG0);
|
|
}
|
|
|
|
/*
|
|
* disable chip-selects that are not equipped
|
|
*/
|
|
void imx_esdctlv4_detect_sdrams(void)
|
|
{
|
|
void __iomem *base = (void *)MX53_ESDCTL_BASE_ADDR;
|
|
u32 esdctl0;
|
|
|
|
esdctl0 = readl(base + ESDCTL_V4_ESDCTL0);
|
|
|
|
writel(MX53_CSD0_BASE_ADDR, MX53_CSD0_BASE_ADDR);
|
|
writel(MX53_CSD1_BASE_ADDR, MX53_CSD1_BASE_ADDR);
|
|
|
|
if (readl(MX53_CSD0_BASE_ADDR) != MX53_CSD0_BASE_ADDR)
|
|
esdctl0 &= ~ESDCTL_V4_ESDCTLx_SDE0;
|
|
if (readl(MX53_CSD1_BASE_ADDR) != MX53_CSD1_BASE_ADDR)
|
|
esdctl0 &= ~ESDCTL_V4_ESDCTLx_SDE1;
|
|
|
|
writel(esdctl0, base + ESDCTL_V4_ESDCTL0);
|
|
}
|
|
|
|
void imx_esdctlv4_init(void)
|
|
{
|
|
void __iomem *base = (void *)MX53_ESDCTL_BASE_ADDR;
|
|
u32 val, r1, esdctl0, mask, rows, cols;
|
|
|
|
/*
|
|
* assume worst-case here: 4Gb chips. this will be optimized
|
|
* further down, when we can determine the actual chip density
|
|
* in imx_esdctlv4_set_tRFC_timing()
|
|
*/
|
|
val = (((140 - 1) << ESDCTL_V4_ESDCFG0_tRFC_SHIFT) |
|
|
((144 - 1) << ESDCTL_V4_ESDCFG0_tXS_SHIFT) |
|
|
((3 - 1) << ESDCTL_V4_ESDCFG0_tXP_SHIFT) |
|
|
((10 - 1) << ESDCTL_V4_ESDCFG0_tXPDLL_SHIFT) |
|
|
((20 - 1) << ESDCTL_V4_ESDCFG0_tFAW_SHIFT) |
|
|
((6 - 3) << ESDCTL_V4_ESDCFG0_tCL_SHIFT));
|
|
writel(val, base + ESDCTL_V4_ESDCFG0);
|
|
|
|
val = (((6 - 1) << ESDCTL_V4_ESDCFG1_tRCD_SHIFT) |
|
|
((6 - 1) << ESDCTL_V4_ESDCFG1_tRP_SHIFT) |
|
|
((21 - 1) << ESDCTL_V4_ESDCFG1_tRC_SHIFT) |
|
|
((15 - 1) << ESDCTL_V4_ESDCFG1_tRAS_SHIFT) |
|
|
(1 << ESDCTL_V4_ESDCFG1_tRPA_SHIFT) |
|
|
((6 - 1) << ESDCTL_V4_ESDCFG1_tWR_SHIFT) |
|
|
((4 - 1) << ESDCTL_V4_ESDCFG1_tMRD_SHIFT) |
|
|
((5 - 2) << ESDCTL_V4_ESDCFG1_tCWL_SHIFT));
|
|
writel(val, base + ESDCTL_V4_ESDCFG1);
|
|
|
|
val = (((512 - 1) << ESDCTL_V4_ESDCFG2_tDLLK_SHIFT) |
|
|
((4 - 1) << ESDCTL_V4_ESDCFG2_tRTP_SHIFT) |
|
|
((4 - 1) << ESDCTL_V4_ESDCFG2_tWTR_SHIFT) |
|
|
((4 - 1) << ESDCTL_V4_ESDCFG2_tRRD_SHIFT));
|
|
writel(val, base + ESDCTL_V4_ESDCFG2);
|
|
|
|
/*
|
|
* we don't touch the ESDRWD register as documentation claims
|
|
* the power-on defaults are minimum required values and we don't
|
|
* want to interfere with changes of these in new chip or
|
|
* BootROM revisions.
|
|
*/
|
|
|
|
val = (((3 - 1) << ESDCTL_V4_ESDOTC_tAOFPD_SHIFT) |
|
|
((3 - 1) << ESDCTL_V4_ESDOTC_tAONPD_SHIFT) |
|
|
((4 - 1) << ESDCTL_V4_ESDOTC_tANPD_SHIFT) |
|
|
((4 - 1) << ESDCTL_V4_ESDOTC_tAXPD_SHIFT) |
|
|
(3 << ESDCTL_V4_ESDOTC_tODTLon_SHIFT) |
|
|
(3 << ESDCTL_V4_ESDOTC_tODT_idle_off_SHIFT));
|
|
writel(val, base + ESDCTL_V4_ESDOTC);
|
|
|
|
/* currently we only support DDR3 RAM, which always has 8 banks */
|
|
val = ESDCTL_V4_ESDMISC_WALAT_0 | ESDCTL_V4_ESDMISC_RALAT_2 |
|
|
ESDCTL_V4_ESDMISC_MIF3_MODE_EFAM |
|
|
ESDCTL_V4_ESDMISC_DDR_DDR3 |
|
|
ESDCTL_V4_ESDMISC_BANKS_8;
|
|
writel(val, base + ESDCTL_V4_ESDMISC);
|
|
|
|
val = (((144 - 1) << ESDCTL_V4_ESDOR_tXPR_SHIFT) |
|
|
((13 + 1) << ESDCTL_V4_ESDOR_SDE_to_RST_SHIFT) |
|
|
((32 + 1) << ESDCTL_V4_ESDOR_RST_to_CKE_SHIFT));
|
|
writel(val, base + ESDCTL_V4_ESDOR);
|
|
|
|
/*
|
|
* we assume maximum address line configuration here (32b, 16rows, 12cols,
|
|
* both chips) once the RAM is initialized, we determine actual configuration
|
|
*/
|
|
val = ESDCTL_V4_ESDCTLx_SDE0 | ESDCTL_V4_ESDCTLx_SDE1 |
|
|
ESDCTL_V4_ESDCTLx_ROW_16 | ESDCTL_V4_ESDCTLx_COL_8 |
|
|
ESDCTL_V4_ESDCTLx_BL_8_8 | ESDCTL_V4_ESDCTLx_DSIZ_32B;
|
|
writel(val, base + ESDCTL_V4_ESDCTL0);
|
|
|
|
imx_esdctlv4_start_ddr3_sdram(0);
|
|
imx_esdctlv4_start_ddr3_sdram(1);
|
|
|
|
val = (ESDCTL_V4_ESDPDC_PRCT_DISABLE << ESDCTL_V4_ESDPDC_PRCT1_SHIFT) |
|
|
(ESDCTL_V4_ESDPDC_PRCT_DISABLE << ESDCTL_V4_ESDPDC_PRCT0_SHIFT) |
|
|
((3 - 1) << ESDCTL_V4_ESDPDC_tCKE_SHIFT) |
|
|
(ESDCTL_V4_ESDPDC_PWDT_DISABLE << ESDCTL_V4_ESDPDC_PWDT1_SHIFT) |
|
|
(ESDCTL_V4_ESDPDC_PWDT_DISABLE << ESDCTL_V4_ESDPDC_PWDT0_SHIFT) |
|
|
(5 << ESDCTL_V4_ESDPDC_tCKSRX_SHIFT) |
|
|
(5 << ESDCTL_V4_ESDPDC_tCKSRE_SHIFT);
|
|
writel(val, base + ESDCTL_V4_ESDPDC);
|
|
|
|
/* configure ODT */
|
|
val = (ESDCTL_V4_ESDODTC_RTT_120 << ESDCTL_V4_ESDODTC_ODT3_INT_RES_SHIFT) |
|
|
(ESDCTL_V4_ESDODTC_RTT_120 << ESDCTL_V4_ESDODTC_ODT2_INT_RES_SHIFT) |
|
|
(ESDCTL_V4_ESDODTC_RTT_120 << ESDCTL_V4_ESDODTC_ODT1_INT_RES_SHIFT) |
|
|
(ESDCTL_V4_ESDODTC_RTT_120 << ESDCTL_V4_ESDODTC_ODT0_INT_RES_SHIFT) |
|
|
ESDCTL_V4_ESDODTC_ODT_RD_PAS_EN |
|
|
ESDCTL_V4_ESDODTC_ODT_WR_ACT_EN |
|
|
ESDCTL_V4_ESDODTC_ODT_WR_PAS_EN;
|
|
writel(val, base + ESDCTL_V4_ODTCTRL);
|
|
|
|
/*
|
|
* ensure refresh and ZQ calibration are turned off as
|
|
* nothing may interfere with the first few calibrations
|
|
*/
|
|
writel(ESDCTL_V4_ESDREF_REF_SEL_MASK, base + ESDCTL_V4_ESDREF);
|
|
|
|
writel(ESDCTL_V4_ESDZQHWC_ZQ_MODE_NO_CAL, base + ESDCTL_V4_ZQHWCTRL);
|
|
|
|
/*
|
|
* ensure that read/write delays are configured to
|
|
* (working) 1/4 cycle default delay, proper delays
|
|
* will be calibrated below.
|
|
*/
|
|
writel(0x30303030, base + ESDCTL_V4_RDDLCTL);
|
|
writel(0x40404040, base + ESDCTL_V4_WRDLCTL);
|
|
|
|
/* DQS Gating */
|
|
imx_esdctlv4_do_dqs_gating();
|
|
|
|
/* Write Leveling */
|
|
imx_esdctlv4_do_write_leveling();
|
|
|
|
/*
|
|
* ZQ calibration (this will also enable regular
|
|
* automatic ZQ calibration again)
|
|
*/
|
|
imx_esdctlv4_do_zq_calibration();
|
|
|
|
/* configure and enable refresh */
|
|
val = (0 << ESDCTL_V4_ESDREF_REF_CNT_SHIFT) |
|
|
ESDCTL_V4_ESDREF_REF_SEL_64K |
|
|
((2 - 1) << ESDCTL_V4_ESDREF_REFR_SHIFT);
|
|
writel(val, base + ESDCTL_V4_ESDREF);
|
|
|
|
|
|
/* Now do proper Delay Line Calibration */
|
|
imx_esdctlv4_do_read_delay_line_calibration();
|
|
imx_esdctlv4_do_write_delay_line_calibration();
|
|
|
|
/* enable regular operation of ESDCTL */
|
|
writel(ESDCTL_V4_ESDSCR_CMD_NOP, base + ESDCTL_V4_ESDSCR);
|
|
|
|
dsb();
|
|
|
|
/*
|
|
* detect RAM configuration (Note: both CSDx must be
|
|
* equipped with identical RAMs, so we only need to detect
|
|
* the configuration of CSD0 and anything on CSD1)
|
|
*/
|
|
esdctl0 = readl(base + ESDCTL_V4_ESDCTL0);
|
|
|
|
imx_esdctlv4_write_magic_values((void *)MX53_CSD0_BASE_ADDR);
|
|
|
|
/* check for bus-configuration first */
|
|
esdctl0 &= ~ESDCTL_V4_ESDCTLx_DSIZ_MASK;
|
|
|
|
/* assume we're on a 32b bus */
|
|
mask = 0xffffffff;
|
|
|
|
/* data correct? */
|
|
if (readl(MX53_CSD0_BASE_ADDR) == SDRAM_COMPARE_CONST1) {
|
|
esdctl0 |= ESDCTL_V4_ESDCTLx_DSIZ_32B; /* yep, indeed 32b bus */
|
|
goto sdram_bussize_found;
|
|
}
|
|
|
|
/*
|
|
* ok, last possibility is 16b bus on low data-lines, check that
|
|
* (i.MX25 also suports 16b on high data-lines, but i.MX53 doesn't)
|
|
*/
|
|
if (readl(MX53_CSD0_BASE_ADDR) << 16 == SDRAM_COMPARE_CONST1 << 16) {
|
|
esdctl0 |= ESDCTL_V4_ESDCTLx_DSIZ_16B_LOW;
|
|
mask >>= 16;
|
|
goto sdram_bussize_found;
|
|
}
|
|
|
|
/* nope, no working SDRAM, leave. */
|
|
hang();
|
|
|
|
sdram_bussize_found:
|
|
|
|
/* Now determine actual row/column configuration of the RAMs */
|
|
|
|
/* mask unused bits from our compare constant */
|
|
r1 = SDRAM_COMPARE_CONST1 & mask;
|
|
/*
|
|
* So far we asssumed that we have 16 rows, check for copies of our
|
|
* SDRAM_COMPARE_CONST1 due to missing row lines...
|
|
*/
|
|
|
|
if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 25), r1, mask))
|
|
rows = ESDCTL_V4_ESDCTLx_ROW_15;
|
|
if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 24), r1, mask))
|
|
rows = ESDCTL_V4_ESDCTLx_ROW_14;
|
|
if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 23), r1, mask))
|
|
rows = ESDCTL_V4_ESDCTLx_ROW_13;
|
|
if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 22), r1, mask))
|
|
rows = ESDCTL_V4_ESDCTLx_ROW_12;
|
|
if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 21), r1, mask))
|
|
rows = ESDCTL_V4_ESDCTLx_ROW_11;
|
|
|
|
esdctl0 &= ~ESDCTL_V4_ESDCTLx_ROW_MASK;
|
|
esdctl0 |= rows;
|
|
|
|
/*
|
|
* To detect columns we have to switch from the (max rows, min cols)
|
|
* configuration we used so far to a (min rows, max cols) configuration
|
|
*/
|
|
|
|
/* switch ESDCTL to configuration mode */
|
|
val = ESDCTL_V4_ESDSCR_CMD_NOP | ESDCTL_V4_ESDSCR_CON_REQ;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* reconfigure row-/column-lines */
|
|
val = readl(base + ESDCTL_V4_ESDCTL0);
|
|
val &= ~(ESDCTL_V4_ESDCTLx_ROW_MASK | ESDCTL_V4_ESDCTLx_COL_MASK);
|
|
val |= ESDCTL_V4_ESDCTLx_ROW_11 | ESDCTL_V4_ESDCTLx_COL_12;
|
|
writel(val, base + ESDCTL_V4_ESDCTL0);
|
|
|
|
dsb();
|
|
|
|
/* switch ESDCTL back to normal operation */
|
|
writel(ESDCTL_V4_ESDSCR_CMD_NOP, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/*
|
|
* not quite sure why, but the row-/col-reconfiguration destroys the
|
|
* contents of the RAM so we have to write our magic values back
|
|
* (maybe because refresh is suspended during that time)
|
|
*/
|
|
imx_esdctlv4_write_magic_values((void *)MX53_CSD0_BASE_ADDR);
|
|
|
|
/*
|
|
* So far we asssumed that we have 12 columns, check for copies of our
|
|
* SDRAM_COMPARE_CONST1 due to missing column lines...
|
|
*/
|
|
|
|
if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 13), r1, mask))
|
|
cols = ESDCTL_V4_ESDCTLx_COL_11;
|
|
if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 12), r1, mask))
|
|
cols = ESDCTL_V4_ESDCTLx_COL_10;
|
|
if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 11), r1, mask))
|
|
cols = ESDCTL_V4_ESDCTLx_COL_9;
|
|
if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 10), r1, mask))
|
|
cols = ESDCTL_V4_ESDCTLx_COL_8;
|
|
|
|
esdctl0 &= ~ESDCTL_V4_ESDCTLx_COL_MASK;
|
|
esdctl0 |= cols;
|
|
|
|
/* setup proper row-/column-configuration */
|
|
|
|
/* switch ESDCTL to configuration mode */
|
|
val = ESDCTL_V4_ESDSCR_CMD_NOP | ESDCTL_V4_ESDSCR_CON_REQ;
|
|
writel(val, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* reconfigure row-/column-lines */
|
|
writel(esdctl0, base + ESDCTL_V4_ESDCTL0);
|
|
|
|
/* configure densitiy dependent timing parameters */
|
|
imx_esdctlv4_set_tRFC_timing();
|
|
|
|
dsb();
|
|
|
|
/* switch ESDCTL back to normal operation */
|
|
writel(ESDCTL_V4_ESDSCR_CMD_NOP, base + ESDCTL_V4_ESDSCR);
|
|
|
|
/* see at which CSx we actually have working RAM */
|
|
imx_esdctlv4_detect_sdrams();
|
|
}
|