On 18/12/2021 00:47, Dmitry Osipenko wrote: > Print out memory type and LPDDR2 configuration on Tegra30, making it > similar to the memory info printed by the Tegra20 memory driver. This > info is useful for debugging purposes. > > Tested-by: Svyatoslav Ryhel <clamor95@xxxxxxxxx> # T30 ASUS TF201 LPDDR2 > Signed-off-by: Dmitry Osipenko <digetx@xxxxxxxxx> > --- > drivers/memory/tegra/Kconfig | 1 + > drivers/memory/tegra/tegra30-emc.c | 131 ++++++++++++++++++++++++++--- > 2 files changed, 122 insertions(+), 10 deletions(-) > > diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig > index 7951764b4efe..3fe83d7c2bf8 100644 > --- a/drivers/memory/tegra/Kconfig > +++ b/drivers/memory/tegra/Kconfig > @@ -28,6 +28,7 @@ config TEGRA30_EMC > default y > depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST > select PM_OPP > + select DDR > help > This driver is for the External Memory Controller (EMC) found on > Tegra30 chips. The EMC controls the external DRAM on the board. > diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c > index 80f98d717e13..4c0432704f46 100644 > --- a/drivers/memory/tegra/tegra30-emc.c > +++ b/drivers/memory/tegra/tegra30-emc.c > @@ -9,6 +9,7 @@ > * Copyright (C) 2019 GRATE-DRIVER project > */ > > +#include <linux/bitfield.h> > #include <linux/clk.h> > #include <linux/clk/tegra.h> > #include <linux/debugfs.h> > @@ -31,11 +32,15 @@ > #include <soc/tegra/common.h> > #include <soc/tegra/fuse.h> > > +#include "../jedec_ddr.h" > +#include "../of_memory.h" > + > #include "mc.h" > > #define EMC_INTSTATUS 0x000 > #define EMC_INTMASK 0x004 > #define EMC_DBG 0x008 > +#define EMC_ADR_CFG 0x010 > #define EMC_CFG 0x00c > #define EMC_REFCTRL 0x020 > #define EMC_TIMING_CONTROL 0x028 > @@ -81,6 +86,7 @@ > #define EMC_EMRS 0x0d0 > #define EMC_SELF_REF 0x0e0 > #define EMC_MRW 0x0e8 > +#define EMC_MRR 0x0ec > #define EMC_XM2DQSPADCTRL3 0x0f8 > #define EMC_FBIO_SPARE 0x100 > #define EMC_FBIO_CFG5 0x104 > @@ -208,6 +214,13 @@ > > #define EMC_REFRESH_OVERFLOW_INT BIT(3) > #define EMC_CLKCHANGE_COMPLETE_INT BIT(4) > +#define EMC_MRR_DIVLD_INT BIT(5) > + > +#define EMC_MRR_DEV_SELECTN GENMASK(31, 30) > +#define EMC_MRR_MRR_MA GENMASK(23, 16) > +#define EMC_MRR_MRR_DATA GENMASK(15, 0) > + > +#define EMC_ADR_CFG_EMEM_NUMDEV BIT(0) > > enum emc_dram_type { > DRAM_TYPE_DDR3, > @@ -378,6 +391,8 @@ struct tegra_emc { > > /* protect shared rate-change code path */ > struct mutex rate_lock; > + > + bool mrr_error; > }; > > static int emc_seq_update_timing(struct tegra_emc *emc) > @@ -1008,12 +1023,18 @@ static int emc_load_timings_from_dt(struct tegra_emc *emc, > return 0; > } > > -static struct device_node *emc_find_node_by_ram_code(struct device *dev) > +static struct device_node *emc_find_node_by_ram_code(struct tegra_emc *emc) > { > + struct device *dev = emc->dev; > struct device_node *np; > u32 value, ram_code; > int err; > > + if (emc->mrr_error) { > + dev_warn(dev, "memory timings skipped due to MRR error\n"); > + return NULL; > + } > + > if (of_get_child_count(dev->of_node) == 0) { > dev_info_once(dev, "device-tree doesn't have memory timings\n"); > return NULL; > @@ -1035,11 +1056,73 @@ static struct device_node *emc_find_node_by_ram_code(struct device *dev) > return NULL; > } > > +static int emc_read_lpddr_mode_register(struct tegra_emc *emc, > + unsigned int emem_dev, > + unsigned int register_addr, > + unsigned int *register_data) > +{ > + u32 memory_dev = emem_dev + 1; > + u32 val, mr_mask = 0xff; > + int err; > + > + /* clear data-valid interrupt status */ > + writel_relaxed(EMC_MRR_DIVLD_INT, emc->regs + EMC_INTSTATUS); > + > + /* issue mode register read request */ > + val = FIELD_PREP(EMC_MRR_DEV_SELECTN, memory_dev); > + val |= FIELD_PREP(EMC_MRR_MRR_MA, register_addr); > + > + writel_relaxed(val, emc->regs + EMC_MRR); > + > + /* wait for the LPDDR2 data-valid interrupt */ > + err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, val, > + val & EMC_MRR_DIVLD_INT, > + 1, 100); > + if (err) { > + dev_err(emc->dev, "mode register %u read failed: %d\n", > + register_addr, err); > + emc->mrr_error = true; > + return err; > + } > + > + /* read out mode register data */ > + val = readl_relaxed(emc->regs + EMC_MRR); > + *register_data = FIELD_GET(EMC_MRR_MRR_DATA, val) & mr_mask; > + > + return 0; > +} > + > +static void emc_read_lpddr_sdram_info(struct tegra_emc *emc, > + unsigned int emem_dev) > +{ > + union lpddr2_basic_config4 basic_conf4; > + unsigned int manufacturer_id; > + unsigned int revision_id1; > + unsigned int revision_id2; > + > + /* these registers are standard for all LPDDR JEDEC memory chips */ > + emc_read_lpddr_mode_register(emc, emem_dev, 5, &manufacturer_id); > + emc_read_lpddr_mode_register(emc, emem_dev, 6, &revision_id1); > + emc_read_lpddr_mode_register(emc, emem_dev, 7, &revision_id2); > + emc_read_lpddr_mode_register(emc, emem_dev, 8, &basic_conf4.value); > + > + dev_info(emc->dev, "SDRAM[dev%u]: manufacturer: 0x%x (%s) rev1: 0x%x rev2: 0x%x prefetch: S%u density: %uMbit iowidth: %ubit\n", > + emem_dev, manufacturer_id, > + lpddr2_jedec_manufacturer(manufacturer_id), > + revision_id1, revision_id2, > + 4 >> basic_conf4.arch_type, > + 64 << basic_conf4.density, > + 32 >> basic_conf4.io_width); > +} > + Quickly looking, these two functions are exactly the same as ones in tegra20-emc.c . Later you might come up with another set for other SoCs, so it looks it is worth to share these. Best regards, Krzysztof