summaryrefslogtreecommitdiff
path: root/arch/arm/cpu
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/cpu')
-rw-r--r--arch/arm/cpu/armv7/omap4/emif.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/arch/arm/cpu/armv7/omap4/emif.c b/arch/arm/cpu/armv7/omap4/emif.c
index ceead9eb02..1234a7e32a 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;