|
@@ -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;
|