[U-Boot] [PATCH v4 07/12] omap4: automatic sdram detection

Aneesh V
2011-07-18T15:46:46Z

Identify SDRAM devices connected to EMIF automatically:
LPDDR2 devices have some Mode Registers that provide details
about the device such as the type, density, bus width
etc. EMIF has the capability to read these registers. If there
are no devices connected to a given chip-select reading mode
registers will return junk values. After reading as many such
registers as possible and matching with expected ranges of
values the driver can identify if there is a device connected
to the respective CS. If we identify that a device is connected
the values read give us complete details about the device.

This along with the base AC timings specified by JESD209-2
allows us to do a complete automatic initialization of
SDRAM that works on all boards.

Please note that the default AC timings specified by JESD209-2
will be safe for all devices but not necessarily optimal. However,
for the Elpida devices used on Panda and SDP the default timings
are both safe and optimal.

Signed-off-by: Aneesh V <aneesh@ti.com>
---
V3:
* Code re-organization for better readability
* Ensured that un-necessary code is compiled out when
CONFIG_SYS_AUTOMATIC_SDRAM_DETECTION is not defined
---
arch/arm/cpu/armv7/omap4/emif.c | 238 +++++++++++++++++++++++++++++++++++++++
include/configs/omap4_panda.h | 1 +
include/configs/omap4_sdp4430.h | 1 +
3 files changed, 240 insertions(+), 0 deletions(-)

diff --git a/arch/arm/cpu/armv7/omap4/emif.c b/arch/arm/cpu/armv7/omap4/emif.c
index ceead9e..1234a7e 100644
--- a/arch/arm/cpu/armv7/omap4/emif.c
+++ b/arch/arm/cpu/armv7/omap4/emif.c
@@ -792,6 +792,244 @@ void emif_get_device_timings(u32 emif_nr,
}
#endif /* CONFIG_SYS_DEFAULT_LPDDR2_TIMINGS */

+#ifdef CONFIG_SYS_AUTOMATIC_SDRAM_DETECTION
+const char *get_lpddr2_type(u8 type_id)
+{
+ switch (type_id) {
+ case LPDDR2_TYPE_S4:
+ return "LPDDR2-S4";
+ case LPDDR2_TYPE_S2:
+ return "LPDDR2-S2";
+ default:
+ return NULL;
+ }
+}
+
+const char *get_lpddr2_io_width(u8 width_id)
+{
+ switch (width_id) {
+ case LPDDR2_IO_WIDTH_8:
+ return "x8";
+ case LPDDR2_IO_WIDTH_16:
+ return "x16";
+ case LPDDR2_IO_WIDTH_32:
+ return "x32";
+ default:
+ return NULL;
+ }
+}
+
+const char *get_lpddr2_manufacturer(u32 manufacturer)
+{
+ switch (manufacturer) {
+ case LPDDR2_MANUFACTURER_SAMSUNG:
+ return "Samsung";
+ case LPDDR2_MANUFACTURER_QIMONDA:
+ return "Qimonda";
+ case LPDDR2_MANUFACTURER_ELPIDA:
+ return "Elpida";
+ case LPDDR2_MANUFACTURER_ETRON:
+ return "Etron";
+ case LPDDR2_MANUFACTURER_NANYA:
+ return "Nanya";
+ case LPDDR2_MANUFACTURER_HYNIX:
+ return "Hynix";
+ case LPDDR2_MANUFACTURER_MOSEL:
+ return "Mosel";
+ case LPDDR2_MANUFACTURER_WINBOND:
+ return "Winbond";
+ case LPDDR2_MANUFACTURER_ESMT:
+ return "ESMT";
+ case LPDDR2_MANUFACTURER_SPANSION:
+ return "Spansion";
+ case LPDDR2_MANUFACTURER_SST:
+ return "SST";
+ case LPDDR2_MANUFACTURER_ZMOS:
+ return "ZMOS";
+ case LPDDR2_MANUFACTURER_INTEL:
+ return "Intel";
+ case LPDDR2_MANUFACTURER_NUMONYX:
+ return "Numonyx";
+ case LPDDR2_MANUFACTURER_MICRON:
+ return "Micron";
+ default:
+ return NULL;
+ }
+}
+
+static void display_sdram_details(u32 emif_nr, u32 cs,
+ struct lpddr2_device_details *device)
+{
+ const char *mfg_str;
+ const char *type_str;
+ char density_str[10];
+ u32 density;
+
+ debug("EMIF%d CS%d\t", emif_nr, cs);
+
+ if (!device) {
+ debug("None\n");
+ return;
+ }
+
+ mfg_str = get_lpddr2_manufacturer(device->manufacturer);
+ type_str = get_lpddr2_type(device->type);
+
+ density = lpddr2_density_2_size_in_mbytes[device->density];
+ if ((density / 1024 * 1024) == density) {
+ density /= 1024;
+ sprintf(density_str, "%d GB", density);
+ } else
+ sprintf(density_str, "%d MB", density);
+ if (mfg_str && type_str)
+ debug("%s\t\t%s\t%s\n", mfg_str, type_str, density_str);
+}
+
+static u8 is_lpddr2_sdram_present(u32 base, u32 cs,
+ struct lpddr2_device_details *lpddr2_device)
+{
+ u32 mr = 0, temp;
+
+ mr = get_mr(base, cs, LPDDR2_MR0);
+ if (mr > 0xFF) {
+ /* Mode register value bigger than 8 bit */
+ return 0;
+ }
+
+ temp = (mr & LPDDR2_MR0_DI_MASK) >> LPDDR2_MR0_DI_SHIFT;
+ if (temp) {
+ /* Not SDRAM */
+ return 0;
+ }
+ temp = (mr & LPDDR2_MR0_DNVI_MASK) >> LPDDR2_MR0_DNVI_SHIFT;
+
+ if (temp) {
+ /* DNV supported - But DNV is only supported for NVM */
+ return 0;
+ }
+
+ mr = get_mr(base, cs, LPDDR2_MR4);
+ if (mr > 0xFF) {
+ /* Mode register value bigger than 8 bit */
+ return 0;
+ }
+
+ mr = get_mr(base, cs, LPDDR2_MR5);
+ if (mr >= 0xFF) {
+ /* Mode register value bigger than 8 bit */
+ return 0;
+ }
+
+ if (!get_lpddr2_manufacturer(mr)) {
+ /* Manufacturer not identified */
+ return 0;
+ }
+ lpddr2_device->manufacturer = mr;
+
+ mr = get_mr(base, cs, LPDDR2_MR6);
+ if (mr >= 0xFF) {
+ /* Mode register value bigger than 8 bit */
+ return 0;
+ }
+
+ mr = get_mr(base, cs, LPDDR2_MR7);
+ if (mr >= 0xFF) {
+ /* Mode register value bigger than 8 bit */
+ return 0;
+ }
+
+ mr = get_mr(base, cs, LPDDR2_MR8);
+ if (mr >= 0xFF) {
+ /* Mode register value bigger than 8 bit */
+ return 0;
+ }
+
+ temp = (mr & MR8_TYPE_MASK) >> MR8_TYPE_SHIFT;
+ if (!get_lpddr2_type(temp)) {
+ /* Not SDRAM */
+ return 0;
+ }
+ lpddr2_device->type = temp;
+
+ temp = (mr & MR8_DENSITY_MASK) >> MR8_DENSITY_SHIFT;
+ if (temp > LPDDR2_DENSITY_32Gb) {
+ /* Density not supported */
+ return 0;
+ }
+ lpddr2_device->density = temp;
+
+ temp = (mr & MR8_IO_WIDTH_MASK) >> MR8_IO_WIDTH_SHIFT;
+ if (!get_lpddr2_io_width(temp)) {
+ /* IO width unsupported value */
+ return 0;
+ }
+ lpddr2_device->io_width = temp;
+
+ /*
+ * If all the above tests pass we should
+ * have a device on this chip-select
+ */
+ return 1;
+}
+
+static struct lpddr2_device_details *get_lpddr2_details(u32 base, u8 cs,
+ struct lpddr2_device_details *lpddr2_dev_details)
+{
+ u32 phy;
+ struct emif_reg_struct *emif = (struct emif_reg_struct *)base;
+
+ if (!lpddr2_dev_details)
+ return NULL;
+
+ /* Do the minimum init for mode register accesses */
+ if (!running_from_sdram()) {
+ phy = get_ddr_phy_ctrl_1(get_sys_clk_freq() / 2, RL_BOOT);
+ writel(phy, &emif->emif_ddr_phy_ctrl_1);
+ }
+
+ if (!(is_lpddr2_sdram_present(base, cs, lpddr2_dev_details)))
+ return NULL;
+
+ display_sdram_details(emif_num(base), cs, lpddr2_dev_details);
+
+ return lpddr2_dev_details;
+}
+
+void emif_get_device_details(u32 emif_nr,
+ struct lpddr2_device_details *cs0_device_details,
+ struct lpddr2_device_details *cs1_device_details)
+{
+ u32 base = (emif_nr == 1) ? OMAP44XX_EMIF1 : OMAP44XX_EMIF2;
+
+ if (running_from_sdram()) {
+ /*
+ * We can not do automatic discovery running from SDRAM
+ * Most likely we came here by mistake. Indicate error
+ * by returning NULL
+ */
+ cs0_device_details = NULL;
+ cs1_device_details = NULL;
+ } else {
+ /*
+ * Automatically find the device details:
+ *
+ * Reset the PHY after each call to get_lpddr2_details().
+ * If there is nothing connected to a given chip select
+ * (typically CS1) mode register reads will mess up with
+ * the PHY state and subsequent initialization won't work.
+ * PHY reset brings back PHY to a good state.
+ */
+ cs0_device_details =
+ get_lpddr2_details(base, CS0, cs0_device_details);
+ emif_reset_phy(base);
+
+ cs1_device_details =
+ get_lpddr2_details(base, CS1, cs1_device_details);
+ emif_reset_phy(base);
+ }
+}
+#endif /* CONFIG_SYS_AUTOMATIC_SDRAM_DETECTION */
+
static void do_sdram_init(u32 base)
{
const struct emif_regs *regs;
diff --git a/include/configs/omap4_panda.h b/include/configs/omap4_panda.h
index 9f82ef4..a8dd861 100644
--- a/include/configs/omap4_panda.h
+++ b/include/configs/omap4_panda.h
@@ -239,6 +239,7 @@

/* Defines for SDRAM init */
#ifndef CONFIG_SYS_EMIF_PRECALCULATED_TIMING_REGS
+#define CONFIG_SYS_AUTOMATIC_SDRAM_DETECTION
#define CONFIG_SYS_DEFAULT_LPDDR2_TIMINGS
#endif

diff --git a/include/configs/omap4_sdp4430.h b/include/configs/omap4_sdp4430.h
index 6df1c3d..534f89a 100644
--- a/include/configs/omap4_sdp4430.h
+++ b/include/configs/omap4_sdp4430.h
@@ -245,6 +245,7 @@

/* Defines for SDRAM init */
#ifndef CONFIG_SYS_EMIF_PRECALCULATED_TIMING_REGS
+#define CONFIG_SYS_AUTOMATIC_SDRAM_DETECTION
#define CONFIG_SYS_DEFAULT_LPDDR2_TIMINGS
#endif

--
1.7.0.4