Memory controller drivers for v5.18 - Tegra SoC
1. Correct Tegra20 EMC memory device mask. 2. Minor improvements. -----BEGIN PGP SIGNATURE----- iQJEBAABCgAuFiEE3dJiKD0RGyM7briowTdm5oaLg9cFAmIc+xkQHGtyemtAa2Vy bmVsLm9yZwAKCRDBN2bmhouD1zROD/9J0QlxhDaJv3B0dpJsAzq1/4YptmdjGC9m 6HJY7wuf1+LR0fJ7ry5KeHU+iYNALuEicZgQErum5+opLJ8kGcZqBA7vtqd40sbz xHDad9keIElZYVdt+mEO3eq4Mi2h/i6zU+3E9pYXNYwqp/bB27GhIX6mHo0wXe2r 8QmFpSlwuhPQSeyx2l01pbPlvnb/bxe0hXUY2hXQ0V849bOAFHxBCoNKoHmKS19w VXZBpUX+LXTQDEaJX7x32Apgi3tXAqHNI6hIRif48v6E3mE243hIkaBGQ6DhdArQ AdDub9pxqEJ8xR/OaEW0063Y1SsxY6Ac+PnszOfKPnufzAnmzwaVO8aohOTosZyg uZcjav4l1+8zHKGG3zdFSDdKXM97jp5S48uq/uZy/GDHmKw32IUZqHFwPxLXt5Gn 0Vq0naiHXXiK1ax/ook01N5ex+sg5NunpQ09OQzIu62BbOBn2yB5TTy+rE1TBwsW fI8/s99X/T/+Mkv9enlLkKk2HqSXbxCRnvAZf4Gc6mZgM/nJlrbWvzGi2oiMYckd c7V3E7q2DCqfOsy1bl7j2Yi1JrAL7bSVtiP2V4K6wq7oI+NwTwmDEATw4eHiPgeL MGH9ehWrjjhFuVER7CVU1YQLMxcaWTKtYpfw06unR0NAwlhrrqxN63Tpl7oPlcE5 hs7tYy+YOw== =vszm -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmId7okACgkQmmx57+YA GNkPlQ/6AuiYn1E5T3ELdAWsTJhByu7T7Hcj0+f4JqdWwn3t+UWngLgCBKW8dMuB nHKNBiEUXgsF3ts7+EK5HCHef+DjCPozmb/W6Lj3EK7L/cChigB13XPWJi5DBA2t 80+R3T2CemYaDUXlRri1myPT6m7YEvkPzvC8sESTYJOv21kbmG8o895LgmJf9czG k6+adkG18xI0Z40GKoQWTzh8R86FIy5JZZ+7VjEPsngy7rTanLr5hxzBL1i60vtd luamZ6uPG8LkSgX8TEh0/wpznqEZDmr0XrbDK1xmd/USAkVxc79QTL1hUNel8SU6 RYmL738NliGQQhnbxxSZYfW4BhqdGKjM6xjt+g3nSFQYkL3RChxBFa25xX9Zd68Q FodRTAqZc8p8K0LTSbeQV7qL/ziZRcoaGi1L76R3l5Zm3pnx4XT/YVc5wh6tf3UJ T9uRWAb0DPuLFW+cxwf0JRVsGVdhxWVFxCQK22vSlTK0fkwGtN1iKBc3Fnf2SYJ/ U0lvwqRBH6MZT8J7RS05a/HI2H7CVQJK99vgj7MLmxWB1rh6Q6uuJR9eutdccyu5 A2rFseuEE7gpV++xLqNAPRbrVYm1hnQdL98868j62WKeMQDU4caUzCE5uOwmut+R EpmfhTWaB9k+xhepu26BWcVcCfu7St0By4Gs8G7mjyTJdOL0tAs= =fMbE -----END PGP SIGNATURE----- Merge tag 'memory-controller-drv-tegra-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into arm/drivers Memory controller drivers for v5.18 - Tegra SoC 1. Correct Tegra20 EMC memory device mask. 2. Minor improvements. * tag 'memory-controller-drv-tegra-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl: memory: tegra: Constify struct thermal_cooling_device_ops memory: tegra20-emc: Correct memory device mask memory: tegra30-emc: Print additional memory info Link: https://lore.kernel.org/r/20220228164313.52931-3-krzysztof.kozlowski@canonical.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
42ba417307
|
@ -28,6 +28,7 @@ config TEGRA30_EMC
|
||||||
default y
|
default y
|
||||||
depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST
|
depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST
|
||||||
select PM_OPP
|
select PM_OPP
|
||||||
|
select DDR
|
||||||
help
|
help
|
||||||
This driver is for the External Memory Controller (EMC) found on
|
This driver is for the External Memory Controller (EMC) found on
|
||||||
Tegra30 chips. The EMC controls the external DRAM on the board.
|
Tegra30 chips. The EMC controls the external DRAM on the board.
|
||||||
|
|
|
@ -540,7 +540,7 @@ static int emc_read_lpddr_mode_register(struct tegra_emc *emc,
|
||||||
unsigned int register_addr,
|
unsigned int register_addr,
|
||||||
unsigned int *register_data)
|
unsigned int *register_data)
|
||||||
{
|
{
|
||||||
u32 memory_dev = emem_dev + 1;
|
u32 memory_dev = emem_dev ? 1 : 2;
|
||||||
u32 val, mr_mask = 0xff;
|
u32 val, mr_mask = 0xff;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
|
|
@ -711,7 +711,7 @@ static int tegra210_emc_cd_set_state(struct thermal_cooling_device *cd,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct thermal_cooling_device_ops tegra210_emc_cd_ops = {
|
static const struct thermal_cooling_device_ops tegra210_emc_cd_ops = {
|
||||||
.get_max_state = tegra210_emc_cd_max_state,
|
.get_max_state = tegra210_emc_cd_max_state,
|
||||||
.get_cur_state = tegra210_emc_cd_get_state,
|
.get_cur_state = tegra210_emc_cd_get_state,
|
||||||
.set_cur_state = tegra210_emc_cd_set_state,
|
.set_cur_state = tegra210_emc_cd_set_state,
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
* Copyright (C) 2019 GRATE-DRIVER project
|
* Copyright (C) 2019 GRATE-DRIVER project
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/clk/tegra.h>
|
#include <linux/clk/tegra.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
|
@ -31,11 +32,15 @@
|
||||||
#include <soc/tegra/common.h>
|
#include <soc/tegra/common.h>
|
||||||
#include <soc/tegra/fuse.h>
|
#include <soc/tegra/fuse.h>
|
||||||
|
|
||||||
|
#include "../jedec_ddr.h"
|
||||||
|
#include "../of_memory.h"
|
||||||
|
|
||||||
#include "mc.h"
|
#include "mc.h"
|
||||||
|
|
||||||
#define EMC_INTSTATUS 0x000
|
#define EMC_INTSTATUS 0x000
|
||||||
#define EMC_INTMASK 0x004
|
#define EMC_INTMASK 0x004
|
||||||
#define EMC_DBG 0x008
|
#define EMC_DBG 0x008
|
||||||
|
#define EMC_ADR_CFG 0x010
|
||||||
#define EMC_CFG 0x00c
|
#define EMC_CFG 0x00c
|
||||||
#define EMC_REFCTRL 0x020
|
#define EMC_REFCTRL 0x020
|
||||||
#define EMC_TIMING_CONTROL 0x028
|
#define EMC_TIMING_CONTROL 0x028
|
||||||
|
@ -81,6 +86,7 @@
|
||||||
#define EMC_EMRS 0x0d0
|
#define EMC_EMRS 0x0d0
|
||||||
#define EMC_SELF_REF 0x0e0
|
#define EMC_SELF_REF 0x0e0
|
||||||
#define EMC_MRW 0x0e8
|
#define EMC_MRW 0x0e8
|
||||||
|
#define EMC_MRR 0x0ec
|
||||||
#define EMC_XM2DQSPADCTRL3 0x0f8
|
#define EMC_XM2DQSPADCTRL3 0x0f8
|
||||||
#define EMC_FBIO_SPARE 0x100
|
#define EMC_FBIO_SPARE 0x100
|
||||||
#define EMC_FBIO_CFG5 0x104
|
#define EMC_FBIO_CFG5 0x104
|
||||||
|
@ -208,6 +214,13 @@
|
||||||
|
|
||||||
#define EMC_REFRESH_OVERFLOW_INT BIT(3)
|
#define EMC_REFRESH_OVERFLOW_INT BIT(3)
|
||||||
#define EMC_CLKCHANGE_COMPLETE_INT BIT(4)
|
#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 {
|
enum emc_dram_type {
|
||||||
DRAM_TYPE_DDR3,
|
DRAM_TYPE_DDR3,
|
||||||
|
@ -378,6 +391,8 @@ struct tegra_emc {
|
||||||
|
|
||||||
/* protect shared rate-change code path */
|
/* protect shared rate-change code path */
|
||||||
struct mutex rate_lock;
|
struct mutex rate_lock;
|
||||||
|
|
||||||
|
bool mrr_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int emc_seq_update_timing(struct tegra_emc *emc)
|
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;
|
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;
|
struct device_node *np;
|
||||||
u32 value, ram_code;
|
u32 value, ram_code;
|
||||||
int err;
|
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) {
|
if (of_get_child_count(dev->of_node) == 0) {
|
||||||
dev_info_once(dev, "device-tree doesn't have memory timings\n");
|
dev_info_once(dev, "device-tree doesn't have memory timings\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1035,11 +1056,73 @@ static struct device_node *emc_find_node_by_ram_code(struct device *dev)
|
||||||
return NULL;
|
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 : 2;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
static int emc_setup_hw(struct tegra_emc *emc)
|
static int emc_setup_hw(struct tegra_emc *emc)
|
||||||
{
|
{
|
||||||
|
u32 fbio_cfg5, emc_cfg, emc_dbg, emc_adr_cfg;
|
||||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
||||||
u32 fbio_cfg5, emc_cfg, emc_dbg;
|
static bool print_sdram_info_once;
|
||||||
enum emc_dram_type dram_type;
|
enum emc_dram_type dram_type;
|
||||||
|
const char *dram_type_str;
|
||||||
|
unsigned int emem_numdev;
|
||||||
|
|
||||||
fbio_cfg5 = readl_relaxed(emc->regs + EMC_FBIO_CFG5);
|
fbio_cfg5 = readl_relaxed(emc->regs + EMC_FBIO_CFG5);
|
||||||
dram_type = fbio_cfg5 & EMC_FBIO_CFG5_DRAM_TYPE_MASK;
|
dram_type = fbio_cfg5 & EMC_FBIO_CFG5_DRAM_TYPE_MASK;
|
||||||
|
@ -1076,6 +1159,34 @@ static int emc_setup_hw(struct tegra_emc *emc)
|
||||||
emc_dbg &= ~EMC_DBG_FORCE_UPDATE;
|
emc_dbg &= ~EMC_DBG_FORCE_UPDATE;
|
||||||
writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
|
writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
|
||||||
|
|
||||||
|
switch (dram_type) {
|
||||||
|
case DRAM_TYPE_DDR1:
|
||||||
|
dram_type_str = "DDR1";
|
||||||
|
break;
|
||||||
|
case DRAM_TYPE_LPDDR2:
|
||||||
|
dram_type_str = "LPDDR2";
|
||||||
|
break;
|
||||||
|
case DRAM_TYPE_DDR2:
|
||||||
|
dram_type_str = "DDR2";
|
||||||
|
break;
|
||||||
|
case DRAM_TYPE_DDR3:
|
||||||
|
dram_type_str = "DDR3";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
emc_adr_cfg = readl_relaxed(emc->regs + EMC_ADR_CFG);
|
||||||
|
emem_numdev = FIELD_GET(EMC_ADR_CFG_EMEM_NUMDEV, emc_adr_cfg) + 1;
|
||||||
|
|
||||||
|
dev_info_once(emc->dev, "%u %s %s attached\n", emem_numdev,
|
||||||
|
dram_type_str, emem_numdev == 2 ? "devices" : "device");
|
||||||
|
|
||||||
|
if (dram_type == DRAM_TYPE_LPDDR2 && !print_sdram_info_once) {
|
||||||
|
while (emem_numdev--)
|
||||||
|
emc_read_lpddr_sdram_info(emc, emem_numdev);
|
||||||
|
|
||||||
|
print_sdram_info_once = true;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1538,14 +1649,6 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
||||||
emc->clk_nb.notifier_call = emc_clk_change_notify;
|
emc->clk_nb.notifier_call = emc_clk_change_notify;
|
||||||
emc->dev = &pdev->dev;
|
emc->dev = &pdev->dev;
|
||||||
|
|
||||||
np = emc_find_node_by_ram_code(&pdev->dev);
|
|
||||||
if (np) {
|
|
||||||
err = emc_load_timings_from_dt(emc, np);
|
|
||||||
of_node_put(np);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
emc->regs = devm_platform_ioremap_resource(pdev, 0);
|
emc->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||||
if (IS_ERR(emc->regs))
|
if (IS_ERR(emc->regs))
|
||||||
return PTR_ERR(emc->regs);
|
return PTR_ERR(emc->regs);
|
||||||
|
@ -1554,6 +1657,14 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
np = emc_find_node_by_ram_code(emc);
|
||||||
|
if (np) {
|
||||||
|
err = emc_load_timings_from_dt(emc, np);
|
||||||
|
of_node_put(np);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
err = platform_get_irq(pdev, 0);
|
err = platform_get_irq(pdev, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
Loading…
Reference in New Issue