summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2026-04-01 23:44:55 +0200
committerArnd Bergmann <arnd@arndb.de>2026-04-01 23:44:56 +0200
commitc7091fd5ebda16a67a121f64cf37014332de965d (patch)
treeb12fbc8d9dbd2936526b6928d0b8fb755ac4147b
parentfb5fee1cbc2902981e33a6082de55007531e5b45 (diff)
parent82169065ffb07577075a5088b313d78673ded331 (diff)
Merge tag 'memory-controller-drv-7.1' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into soc/drivers
Memory controller drivers for v7.1 1. TegraMC: - Few fixes for older issues - missing clock on Tegra264, missing enabling of DLL for Tegra30 and Tegra124. - Simplify the code in a few places. - Rework handling interrupts on different variants and add support for error logging on Tegra 264. 2. Drop Baikal SoC bt1-l2-ctl driver, because SoC support is being removed tree-wide. * tag 'memory-controller-drv-7.1' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl: memory: tegra: Add MC error logging support for Tegra264 memory: tegra: Prepare for supporting multiple intmask registers memory: tegra: Group SoC specific fields memory: tegra: Add support for multiple IRQs memory: tegra: Group register and fields memory: tegra: Group error handling related registers memory: tegra-mc: Use %pe format memory: tegra-mc: Simplify printing PTR_ERR with dev_err_probe memory: tegra-mc: Drop tegra_mc_setup_latency_allowance() return value memory: renesas-rpc-if: Simplify printing PTR_ERR with dev_err_probe memory: brcmstb_memc: Expand LPDDR4 check to cover for LPDDR5 dt-bindings: cache: bt1-l2-ctl: Remove unused bindings memory: bt1-l2-ctl: Remove not-going-to-be-supported code for Baikal SoC memory: tegra30-emc: Fix dll_change check memory: tegra124-emc: Fix dll_change check memory: tegra: Add support for DBB clock on Tegra264 Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r--Documentation/devicetree/bindings/cache/baikal,bt1-l2-ctl.yaml63
-rw-r--r--drivers/memory/Kconfig11
-rw-r--r--drivers/memory/Makefile1
-rw-r--r--drivers/memory/brcmstb_memc.c8
-rw-r--r--drivers/memory/bt1-l2-ctl.c323
-rw-r--r--drivers/memory/renesas-rpc-if.c8
-rw-r--r--drivers/memory/tegra/mc.c135
-rw-r--r--drivers/memory/tegra/mc.h153
-rw-r--r--drivers/memory/tegra/tegra114.c18
-rw-r--r--drivers/memory/tegra/tegra124-emc.c2
-rw-r--r--drivers/memory/tegra/tegra124.c40
-rw-r--r--drivers/memory/tegra/tegra186-emc.c8
-rw-r--r--drivers/memory/tegra/tegra186.c22
-rw-r--r--drivers/memory/tegra/tegra194.c22
-rw-r--r--drivers/memory/tegra/tegra20.c31
-rw-r--r--drivers/memory/tegra/tegra210.c21
-rw-r--r--drivers/memory/tegra/tegra234.c22
-rw-r--r--drivers/memory/tegra/tegra264.c420
-rw-r--r--drivers/memory/tegra/tegra30-emc.c6
-rw-r--r--drivers/memory/tegra/tegra30.c18
-rw-r--r--include/soc/tegra/mc.h40
21 files changed, 808 insertions, 564 deletions
diff --git a/Documentation/devicetree/bindings/cache/baikal,bt1-l2-ctl.yaml b/Documentation/devicetree/bindings/cache/baikal,bt1-l2-ctl.yaml
deleted file mode 100644
index ec4f367bc0b4..000000000000
--- a/Documentation/devicetree/bindings/cache/baikal,bt1-l2-ctl.yaml
+++ /dev/null
@@ -1,63 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/cache/baikal,bt1-l2-ctl.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Baikal-T1 L2-cache Control Block
-
-maintainers:
- - Serge Semin <fancer.lancer@gmail.com>
-
-description: |
- By means of the System Controller Baikal-T1 SoC exposes a few settings to
- tune the MIPS P5600 CM2 L2 cache performance up. In particular it's possible
- to change the Tag, Data and Way-select RAM access latencies. Baikal-T1
- L2-cache controller block is responsible for the tuning. Its DT node is
- supposed to be a child of the system controller.
-
-properties:
- compatible:
- const: baikal,bt1-l2-ctl
-
- reg:
- maxItems: 1
-
- baikal,l2-ws-latency:
- $ref: /schemas/types.yaml#/definitions/uint32
- description: Cycles of latency for Way-select RAM accesses
- default: 0
- minimum: 0
- maximum: 3
-
- baikal,l2-tag-latency:
- $ref: /schemas/types.yaml#/definitions/uint32
- description: Cycles of latency for Tag RAM accesses
- default: 0
- minimum: 0
- maximum: 3
-
- baikal,l2-data-latency:
- $ref: /schemas/types.yaml#/definitions/uint32
- description: Cycles of latency for Data RAM accesses
- default: 1
- minimum: 0
- maximum: 3
-
-additionalProperties: false
-
-required:
- - compatible
-
-examples:
- - |
- l2@1f04d028 {
- compatible = "baikal,bt1-l2-ctl";
- reg = <0x1f04d028 0x004>;
-
- baikal,l2-ws-latency = <1>;
- baikal,l2-tag-latency = <1>;
- baikal,l2-data-latency = <2>;
- };
-...
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 79df0d22e218..e5527020ff33 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -64,17 +64,6 @@ config BRCMSTB_MEMC
controller and specifically control the Self Refresh Power Down
(SRPD) inactivity timeout.
-config BT1_L2_CTL
- bool "Baikal-T1 CM2 L2-RAM Cache Control Block"
- depends on MIPS_BAIKAL_T1 || COMPILE_TEST
- select MFD_SYSCON
- help
- Baikal-T1 CPU is based on the MIPS P5600 Warrior IP-core. The CPU
- resides Coherency Manager v2 with embedded 1MB L2-cache. It's
- possible to tune the L2 cache performance up by setting the data,
- tags and way-select latencies of RAM access. This driver provides a
- dt properties-based and sysfs interface for it.
-
config TI_AEMIF
tristate "Texas Instruments AEMIF driver"
depends on ARCH_DAVINCI || ARCH_KEYSTONE || COMPILE_TEST
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index c1959661bf63..3ee883c8759a 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o
obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o
obj-$(CONFIG_BRCMSTB_DPFE) += brcmstb_dpfe.o
obj-$(CONFIG_BRCMSTB_MEMC) += brcmstb_memc.o
-obj-$(CONFIG_BT1_L2_CTL) += bt1-l2-ctl.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o
diff --git a/drivers/memory/brcmstb_memc.c b/drivers/memory/brcmstb_memc.c
index ba73470b1b13..c28fe9093616 100644
--- a/drivers/memory/brcmstb_memc.c
+++ b/drivers/memory/brcmstb_memc.c
@@ -14,6 +14,7 @@
#define REG_MEMC_CNTRLR_CONFIG 0x00
#define CNTRLR_CONFIG_LPDDR4_SHIFT 5
+#define CNTRLR_CONFIG_LPDDR5_SHIFT 6
#define CNTRLR_CONFIG_MASK 0xf
#define REG_MEMC_SRPD_CFG_21 0x20
#define REG_MEMC_SRPD_CFG_20 0x34
@@ -34,14 +35,15 @@ struct brcmstb_memc {
u32 srpd_offset;
};
-static int brcmstb_memc_uses_lpddr4(struct brcmstb_memc *memc)
+static int brcmstb_memc_uses_lpddr45(struct brcmstb_memc *memc)
{
void __iomem *config = memc->ddr_ctrl + REG_MEMC_CNTRLR_CONFIG;
u32 reg;
reg = readl_relaxed(config) & CNTRLR_CONFIG_MASK;
- return reg == CNTRLR_CONFIG_LPDDR4_SHIFT;
+ return reg == CNTRLR_CONFIG_LPDDR4_SHIFT ||
+ reg == CNTRLR_CONFIG_LPDDR5_SHIFT;
}
static int brcmstb_memc_srpd_config(struct brcmstb_memc *memc,
@@ -95,7 +97,7 @@ static ssize_t srpd_store(struct device *dev, struct device_attribute *attr,
* dynamic tuning process will also get affected by the inactivity
* timeout, thus making it non functional.
*/
- if (brcmstb_memc_uses_lpddr4(memc))
+ if (brcmstb_memc_uses_lpddr45(memc))
return -EOPNOTSUPP;
ret = kstrtouint(buf, 10, &val);
diff --git a/drivers/memory/bt1-l2-ctl.c b/drivers/memory/bt1-l2-ctl.c
deleted file mode 100644
index 0fd96abc172a..000000000000
--- a/drivers/memory/bt1-l2-ctl.c
+++ /dev/null
@@ -1,323 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
- *
- * Authors:
- * Serge Semin <Sergey.Semin@baikalelectronics.ru>
- *
- * Baikal-T1 CM2 L2-cache Control Block driver.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/bitfield.h>
-#include <linux/types.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
-#include <linux/sysfs.h>
-#include <linux/of.h>
-
-#define L2_CTL_REG 0x028
-#define L2_CTL_DATA_STALL_FLD 0
-#define L2_CTL_DATA_STALL_MASK GENMASK(1, L2_CTL_DATA_STALL_FLD)
-#define L2_CTL_TAG_STALL_FLD 2
-#define L2_CTL_TAG_STALL_MASK GENMASK(3, L2_CTL_TAG_STALL_FLD)
-#define L2_CTL_WS_STALL_FLD 4
-#define L2_CTL_WS_STALL_MASK GENMASK(5, L2_CTL_WS_STALL_FLD)
-#define L2_CTL_SET_CLKRATIO BIT(13)
-#define L2_CTL_CLKRATIO_LOCK BIT(31)
-
-#define L2_CTL_STALL_MIN 0
-#define L2_CTL_STALL_MAX 3
-#define L2_CTL_STALL_SET_DELAY_US 1
-#define L2_CTL_STALL_SET_TOUT_US 1000
-
-/*
- * struct l2_ctl - Baikal-T1 L2 Control block private data.
- * @dev: Pointer to the device structure.
- * @sys_regs: Baikal-T1 System Controller registers map.
- */
-struct l2_ctl {
- struct device *dev;
-
- struct regmap *sys_regs;
-};
-
-/*
- * enum l2_ctl_stall - Baikal-T1 L2-cache-RAM stall identifier.
- * @L2_WSSTALL: Way-select latency.
- * @L2_TAGSTALL: Tag latency.
- * @L2_DATASTALL: Data latency.
- */
-enum l2_ctl_stall {
- L2_WS_STALL,
- L2_TAG_STALL,
- L2_DATA_STALL
-};
-
-/*
- * struct l2_ctl_device_attribute - Baikal-T1 L2-cache device attribute.
- * @dev_attr: Actual sysfs device attribute.
- * @id: L2-cache stall field identifier.
- */
-struct l2_ctl_device_attribute {
- struct device_attribute dev_attr;
- enum l2_ctl_stall id;
-};
-
-#define to_l2_ctl_dev_attr(_dev_attr) \
- container_of(_dev_attr, struct l2_ctl_device_attribute, dev_attr)
-
-#define L2_CTL_ATTR_RW(_name, _prefix, _id) \
- struct l2_ctl_device_attribute l2_ctl_attr_##_name = \
- { __ATTR(_name, 0644, _prefix##_show, _prefix##_store), _id }
-
-static int l2_ctl_get_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 *val)
-{
- u32 data = 0;
- int ret;
-
- ret = regmap_read(l2->sys_regs, L2_CTL_REG, &data);
- if (ret)
- return ret;
-
- switch (id) {
- case L2_WS_STALL:
- *val = FIELD_GET(L2_CTL_WS_STALL_MASK, data);
- break;
- case L2_TAG_STALL:
- *val = FIELD_GET(L2_CTL_TAG_STALL_MASK, data);
- break;
- case L2_DATA_STALL:
- *val = FIELD_GET(L2_CTL_DATA_STALL_MASK, data);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int l2_ctl_set_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 val)
-{
- u32 mask = 0, data = 0;
- int ret;
-
- val = clamp_val(val, L2_CTL_STALL_MIN, L2_CTL_STALL_MAX);
-
- switch (id) {
- case L2_WS_STALL:
- data = FIELD_PREP(L2_CTL_WS_STALL_MASK, val);
- mask = L2_CTL_WS_STALL_MASK;
- break;
- case L2_TAG_STALL:
- data = FIELD_PREP(L2_CTL_TAG_STALL_MASK, val);
- mask = L2_CTL_TAG_STALL_MASK;
- break;
- case L2_DATA_STALL:
- data = FIELD_PREP(L2_CTL_DATA_STALL_MASK, val);
- mask = L2_CTL_DATA_STALL_MASK;
- break;
- default:
- return -EINVAL;
- }
-
- data |= L2_CTL_SET_CLKRATIO;
- mask |= L2_CTL_SET_CLKRATIO;
-
- ret = regmap_update_bits(l2->sys_regs, L2_CTL_REG, mask, data);
- if (ret)
- return ret;
-
- return regmap_read_poll_timeout(l2->sys_regs, L2_CTL_REG, data,
- data & L2_CTL_CLKRATIO_LOCK,
- L2_CTL_STALL_SET_DELAY_US,
- L2_CTL_STALL_SET_TOUT_US);
-}
-
-static void l2_ctl_clear_data(void *data)
-{
- struct l2_ctl *l2 = data;
- struct platform_device *pdev = to_platform_device(l2->dev);
-
- platform_set_drvdata(pdev, NULL);
-}
-
-static struct l2_ctl *l2_ctl_create_data(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct l2_ctl *l2;
- int ret;
-
- l2 = devm_kzalloc(dev, sizeof(*l2), GFP_KERNEL);
- if (!l2)
- return ERR_PTR(-ENOMEM);
-
- ret = devm_add_action(dev, l2_ctl_clear_data, l2);
- if (ret) {
- dev_err(dev, "Can't add L2 CTL data clear action\n");
- return ERR_PTR(ret);
- }
-
- l2->dev = dev;
- platform_set_drvdata(pdev, l2);
-
- return l2;
-}
-
-static int l2_ctl_find_sys_regs(struct l2_ctl *l2)
-{
- l2->sys_regs = syscon_node_to_regmap(l2->dev->of_node->parent);
- if (IS_ERR(l2->sys_regs)) {
- dev_err(l2->dev, "Couldn't get L2 CTL register map\n");
- return PTR_ERR(l2->sys_regs);
- }
-
- return 0;
-}
-
-static int l2_ctl_of_parse_property(struct l2_ctl *l2, enum l2_ctl_stall id,
- const char *propname)
-{
- int ret = 0;
- u32 data;
-
- if (!of_property_read_u32(l2->dev->of_node, propname, &data)) {
- ret = l2_ctl_set_latency(l2, id, data);
- if (ret)
- dev_err(l2->dev, "Invalid value of '%s'\n", propname);
- }
-
- return ret;
-}
-
-static int l2_ctl_of_parse(struct l2_ctl *l2)
-{
- int ret;
-
- ret = l2_ctl_of_parse_property(l2, L2_WS_STALL, "baikal,l2-ws-latency");
- if (ret)
- return ret;
-
- ret = l2_ctl_of_parse_property(l2, L2_TAG_STALL, "baikal,l2-tag-latency");
- if (ret)
- return ret;
-
- return l2_ctl_of_parse_property(l2, L2_DATA_STALL,
- "baikal,l2-data-latency");
-}
-
-static ssize_t l2_ctl_latency_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr);
- struct l2_ctl *l2 = dev_get_drvdata(dev);
- u32 data;
- int ret;
-
- ret = l2_ctl_get_latency(l2, devattr->id, &data);
- if (ret)
- return ret;
-
- return sysfs_emit(buf, "%u\n", data);
-}
-
-static ssize_t l2_ctl_latency_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr);
- struct l2_ctl *l2 = dev_get_drvdata(dev);
- u32 data;
- int ret;
-
- if (kstrtouint(buf, 0, &data) < 0)
- return -EINVAL;
-
- ret = l2_ctl_set_latency(l2, devattr->id, data);
- if (ret)
- return ret;
-
- return count;
-}
-
-static L2_CTL_ATTR_RW(l2_ws_latency, l2_ctl_latency, L2_WS_STALL);
-static L2_CTL_ATTR_RW(l2_tag_latency, l2_ctl_latency, L2_TAG_STALL);
-static L2_CTL_ATTR_RW(l2_data_latency, l2_ctl_latency, L2_DATA_STALL);
-
-static struct attribute *l2_ctl_sysfs_attrs[] = {
- &l2_ctl_attr_l2_ws_latency.dev_attr.attr,
- &l2_ctl_attr_l2_tag_latency.dev_attr.attr,
- &l2_ctl_attr_l2_data_latency.dev_attr.attr,
- NULL
-};
-ATTRIBUTE_GROUPS(l2_ctl_sysfs);
-
-static void l2_ctl_remove_sysfs(void *data)
-{
- struct l2_ctl *l2 = data;
-
- device_remove_groups(l2->dev, l2_ctl_sysfs_groups);
-}
-
-static int l2_ctl_init_sysfs(struct l2_ctl *l2)
-{
- int ret;
-
- ret = device_add_groups(l2->dev, l2_ctl_sysfs_groups);
- if (ret) {
- dev_err(l2->dev, "Failed to create L2 CTL sysfs nodes\n");
- return ret;
- }
-
- ret = devm_add_action_or_reset(l2->dev, l2_ctl_remove_sysfs, l2);
- if (ret)
- dev_err(l2->dev, "Can't add L2 CTL sysfs remove action\n");
-
- return ret;
-}
-
-static int l2_ctl_probe(struct platform_device *pdev)
-{
- struct l2_ctl *l2;
- int ret;
-
- l2 = l2_ctl_create_data(pdev);
- if (IS_ERR(l2))
- return PTR_ERR(l2);
-
- ret = l2_ctl_find_sys_regs(l2);
- if (ret)
- return ret;
-
- ret = l2_ctl_of_parse(l2);
- if (ret)
- return ret;
-
- ret = l2_ctl_init_sysfs(l2);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static const struct of_device_id l2_ctl_of_match[] = {
- { .compatible = "baikal,bt1-l2-ctl" },
- { }
-};
-MODULE_DEVICE_TABLE(of, l2_ctl_of_match);
-
-static struct platform_driver l2_ctl_driver = {
- .probe = l2_ctl_probe,
- .driver = {
- .name = "bt1-l2-ctl",
- .of_match_table = l2_ctl_of_match
- }
-};
-module_platform_driver(l2_ctl_driver);
-
-MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
-MODULE_DESCRIPTION("Baikal-T1 L2-cache driver");
diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c
index 58ccc1c02e90..0fb568456164 100644
--- a/drivers/memory/renesas-rpc-if.c
+++ b/drivers/memory/renesas-rpc-if.c
@@ -1005,11 +1005,9 @@ static int rpcif_probe(struct platform_device *pdev)
return PTR_ERR(rpc->base);
rpc->info = of_device_get_match_data(dev);
rpc->regmap = devm_regmap_init(dev, NULL, rpc, rpc->info->regmap_config);
- if (IS_ERR(rpc->regmap)) {
- dev_err(dev, "failed to init regmap for rpcif, error %ld\n",
- PTR_ERR(rpc->regmap));
- return PTR_ERR(rpc->regmap);
- }
+ if (IS_ERR(rpc->regmap))
+ return dev_err_probe(dev, PTR_ERR(rpc->regmap),
+ "failed to init regmap for rpcif\n");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap");
rpc->dirmap = devm_ioremap_resource(dev, res);
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index 6edb210287dc..d620660da331 100644
--- a/drivers/memory/tegra/mc.c
+++ b/drivers/memory/tegra/mc.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2014-2025 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/clk.h>
@@ -56,6 +56,23 @@ static const struct of_device_id tegra_mc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
+const struct tegra_mc_regs tegra20_mc_regs = {
+ .cfg_channel_enable = 0xdf8,
+ .err_status = 0x08,
+ .err_add = 0x0c,
+ .err_add_hi = 0x11fc,
+ .err_vpr_status = 0x654,
+ .err_vpr_add = 0x658,
+ .err_sec_status = 0x67c,
+ .err_sec_add = 0x680,
+ .err_mts_status = 0x9b0,
+ .err_mts_add = 0x9b4,
+ .err_gen_co_status = 0xc00,
+ .err_gen_co_add = 0xc04,
+ .err_route_status = 0x9c0,
+ .err_route_add = 0x9c4,
+};
+
static void tegra_mc_devm_action_put_device(void *data)
{
struct tegra_mc *mc = data;
@@ -381,12 +398,16 @@ unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
}
EXPORT_SYMBOL_GPL(tegra_mc_get_emem_device_count);
+const irq_handler_t tegra30_mc_irq_handlers[] = {
+ tegra30_mc_handle_irq
+};
+
#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \
defined(CONFIG_ARCH_TEGRA_114_SOC) || \
defined(CONFIG_ARCH_TEGRA_124_SOC) || \
defined(CONFIG_ARCH_TEGRA_132_SOC) || \
defined(CONFIG_ARCH_TEGRA_210_SOC)
-static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
+static void tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
{
unsigned long long tick;
unsigned int i;
@@ -414,8 +435,6 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
/* latch new values */
mc_writel(mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL);
-
- return 0;
}
static int load_one_timing(struct tegra_mc *mc,
@@ -509,32 +528,24 @@ int tegra30_mc_probe(struct tegra_mc *mc)
int err;
mc->clk = devm_clk_get_optional(mc->dev, "mc");
- if (IS_ERR(mc->clk)) {
- dev_err(mc->dev, "failed to get MC clock: %ld\n", PTR_ERR(mc->clk));
- return PTR_ERR(mc->clk);
- }
+ if (IS_ERR(mc->clk))
+ return dev_err_probe(mc->dev, PTR_ERR(mc->clk),
+ "failed to get MC clock\n");
/* ensure that debug features are disabled */
mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG);
- err = tegra_mc_setup_latency_allowance(mc);
- if (err < 0) {
- dev_err(mc->dev, "failed to setup latency allowance: %d\n", err);
- return err;
- }
+ tegra_mc_setup_latency_allowance(mc);
err = tegra_mc_setup_timings(mc);
- if (err < 0) {
- dev_err(mc->dev, "failed to setup timings: %d\n", err);
- return err;
- }
+ if (err < 0)
+ return dev_err_probe(mc->dev, err, "failed to setup timings\n");
return 0;
}
const struct tegra_mc_ops tegra30_mc_ops = {
.probe = tegra30_mc_probe,
- .handle_irq = tegra30_mc_handle_irq,
};
#endif
@@ -575,9 +586,9 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
}
/* mask all interrupts to avoid flooding */
- status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmask;
+ status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmasks[0].mask;
} else {
- status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
+ status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmasks[0].mask;
}
if (!status)
@@ -600,37 +611,37 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
switch (intmask) {
case MC_INT_DECERR_VPR:
- status_reg = MC_ERR_VPR_STATUS;
- addr_reg = MC_ERR_VPR_ADR;
+ status_reg = mc->soc->regs->err_vpr_status;
+ addr_reg = mc->soc->regs->err_vpr_add;
break;
case MC_INT_SECERR_SEC:
- status_reg = MC_ERR_SEC_STATUS;
- addr_reg = MC_ERR_SEC_ADR;
+ status_reg = mc->soc->regs->err_sec_status;
+ addr_reg = mc->soc->regs->err_sec_add;
break;
case MC_INT_DECERR_MTS:
- status_reg = MC_ERR_MTS_STATUS;
- addr_reg = MC_ERR_MTS_ADR;
+ status_reg = mc->soc->regs->err_mts_status;
+ addr_reg = mc->soc->regs->err_mts_add;
break;
case MC_INT_DECERR_GENERALIZED_CARVEOUT:
- status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS;
- addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR;
+ status_reg = mc->soc->regs->err_gen_co_status;
+ addr_reg = mc->soc->regs->err_gen_co_add;
break;
case MC_INT_DECERR_ROUTE_SANITY:
- status_reg = MC_ERR_ROUTE_SANITY_STATUS;
- addr_reg = MC_ERR_ROUTE_SANITY_ADR;
+ status_reg = mc->soc->regs->err_route_status;
+ addr_reg = mc->soc->regs->err_route_add;
break;
default:
- status_reg = MC_ERR_STATUS;
- addr_reg = MC_ERR_ADR;
+ status_reg = mc->soc->regs->err_status;
+ addr_reg = mc->soc->regs->err_add;
#ifdef CONFIG_PHYS_ADDR_T_64BIT
if (mc->soc->has_addr_hi_reg)
- addr_hi_reg = MC_ERR_ADR_HI;
+ addr_hi_reg = mc->soc->regs->err_add_hi;
#endif
break;
}
@@ -647,9 +658,12 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
addr = mc_ch_readl(mc, channel, addr_hi_reg);
else
addr = mc_readl(mc, addr_hi_reg);
- } else {
+ } else if (mc->soc->mc_addr_hi_mask) {
addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
- MC_ERR_STATUS_ADR_HI_MASK);
+ mc->soc->mc_addr_hi_mask);
+ } else {
+ dev_err_ratelimited(mc->dev, "Unable to determine high address!");
+ return IRQ_NONE;
}
addr <<= 32;
}
@@ -674,11 +688,11 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
}
}
- type = (value & MC_ERR_STATUS_TYPE_MASK) >>
+ type = (value & mc->soc->mc_err_status_type_mask) >>
MC_ERR_STATUS_TYPE_SHIFT;
- desc = tegra_mc_error_names[type];
+ desc = tegra20_mc_error_names[type];
- switch (value & MC_ERR_STATUS_TYPE_MASK) {
+ switch (value & mc->soc->mc_err_status_type_mask) {
case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE:
perm[0] = ' ';
perm[1] = '[';
@@ -744,9 +758,10 @@ const char *const tegra_mc_status_names[32] = {
[16] = "MTS carveout violation",
[17] = "Generalized carveout violation",
[20] = "Route Sanity error",
+ [21] = "GIC_MSI error",
};
-const char *const tegra_mc_error_names[8] = {
+const char *const tegra20_mc_error_names[8] = {
[2] = "EMEM decode error",
[3] = "TrustZone violation",
[4] = "Carveout violation",
@@ -883,7 +898,7 @@ static void tegra_mc_num_channel_enabled(struct tegra_mc *mc)
unsigned int i;
u32 value;
- value = mc_ch_readl(mc, 0, MC_EMEM_ADR_CFG_CHANNEL_ENABLE);
+ value = mc_ch_readl(mc, 0, mc->soc->regs->cfg_channel_enable);
if (value <= 0) {
mc->num_channels = mc->soc->num_channels;
return;
@@ -935,25 +950,32 @@ static int tegra_mc_probe(struct platform_device *pdev)
tegra_mc_num_channel_enabled(mc);
- if (mc->soc->ops && mc->soc->ops->handle_irq) {
- mc->irq = platform_get_irq(pdev, 0);
- if (mc->irq < 0)
- return mc->irq;
+ if (mc->soc->handle_irq) {
+ unsigned int i;
WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n");
- if (mc->soc->num_channels)
- mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmask,
- MC_INTMASK);
- else
- mc_writel(mc, mc->soc->intmask, MC_INTMASK);
+ for (i = 0; i < mc->soc->num_interrupts; i++) {
+ int irq;
- err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 0,
- dev_name(&pdev->dev), mc);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq,
- err);
- return err;
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, mc->soc->handle_irq[i], 0,
+ dev_name(&pdev->dev), mc);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", irq, err);
+ return err;
+ }
+ }
+
+ for (i = 0; i < mc->soc->num_intmasks; i++) {
+ if (mc->soc->num_channels)
+ mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmasks[i].mask,
+ mc->soc->intmasks[i].reg);
+ else
+ mc_writel(mc, mc->soc->intmasks[i].mask, mc->soc->intmasks[i].reg);
}
}
@@ -971,8 +993,7 @@ static int tegra_mc_probe(struct platform_device *pdev)
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) {
mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc);
if (IS_ERR(mc->smmu)) {
- dev_err(&pdev->dev, "failed to probe SMMU: %ld\n",
- PTR_ERR(mc->smmu));
+ dev_err(&pdev->dev, "failed to probe SMMU: %pe\n", mc->smmu);
mc->smmu = NULL;
}
}
diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h
index 1d97cf4d3a94..649b54369263 100644
--- a/drivers/memory/tegra/mc.h
+++ b/drivers/memory/tegra/mc.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) 2014-2025 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved.
*/
#ifndef MEMORY_TEGRA_MC_H
@@ -13,15 +13,36 @@
#include <soc/tegra/mc.h>
#define MC_INTSTATUS 0x00
+/* Bit field of MC_INTSTATUS register */
+#define MC_INT_DECERR_EMEM BIT(6)
+#define MC_INT_INVALID_GART_PAGE BIT(7)
+#define MC_INT_SECURITY_VIOLATION BIT(8)
+#define MC_INT_ARBITRATION_EMEM BIT(9)
+#define MC_INT_INVALID_SMMU_PAGE BIT(10)
+#define MC_INT_INVALID_APB_ASID_UPDATE BIT(11)
+#define MC_INT_DECERR_VPR BIT(12)
+#define MC_INT_SECERR_SEC BIT(13)
+#define MC_INT_DECERR_MTS BIT(16)
+#define MC_INT_DECERR_GENERALIZED_CARVEOUT BIT(17)
+#define MC_INT_DECERR_ROUTE_SANITY BIT(20)
+#define MC_INT_DECERR_ROUTE_SANITY_GIC_MSI BIT(21)
+
#define MC_INTMASK 0x04
-#define MC_ERR_STATUS 0x08
-#define MC_ERR_ADR 0x0c
#define MC_GART_ERROR_REQ 0x30
#define MC_EMEM_ADR_CFG 0x54
+#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
+
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
#define MC_SECURITY_VIOLATION_STATUS 0x74
#define MC_EMEM_ARB_CFG 0x90
+#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) ((x) & 0x1ff)
+#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
+
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
+#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE BIT(30)
+#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE BIT(31)
+#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK 0x1ff
+
#define MC_EMEM_ARB_TIMING_RCD 0x98
#define MC_EMEM_ARB_TIMING_RP 0x9c
#define MC_EMEM_ARB_TIMING_RC 0xa0
@@ -41,60 +62,97 @@
#define MC_EMEM_ARB_MISC1 0xdc
#define MC_EMEM_ARB_RING1_THROTTLE 0xe0
#define MC_EMEM_ARB_OVERRIDE 0xe8
+#define MC_EMEM_ARB_OVERRIDE_EACK_MASK 0x3
+
#define MC_TIMING_CONTROL_DBG 0xf8
#define MC_TIMING_CONTROL 0xfc
-#define MC_ERR_VPR_STATUS 0x654
-#define MC_ERR_VPR_ADR 0x658
-#define MC_ERR_SEC_STATUS 0x67c
-#define MC_ERR_SEC_ADR 0x680
-#define MC_ERR_MTS_STATUS 0x9b0
-#define MC_ERR_MTS_ADR 0x9b4
-#define MC_ERR_ROUTE_SANITY_STATUS 0x9c0
-#define MC_ERR_ROUTE_SANITY_ADR 0x9c4
-#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00
-#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04
-#define MC_EMEM_ADR_CFG_CHANNEL_ENABLE 0xdf8
-#define MC_GLOBAL_INTSTATUS 0xf24
-#define MC_ERR_ADR_HI 0x11fc
+#define MC_TIMING_UPDATE BIT(0)
-#define MC_INT_DECERR_ROUTE_SANITY BIT(20)
-#define MC_INT_DECERR_GENERALIZED_CARVEOUT BIT(17)
-#define MC_INT_DECERR_MTS BIT(16)
-#define MC_INT_SECERR_SEC BIT(13)
-#define MC_INT_DECERR_VPR BIT(12)
-#define MC_INT_INVALID_APB_ASID_UPDATE BIT(11)
-#define MC_INT_INVALID_SMMU_PAGE BIT(10)
-#define MC_INT_ARBITRATION_EMEM BIT(9)
-#define MC_INT_SECURITY_VIOLATION BIT(8)
-#define MC_INT_INVALID_GART_PAGE BIT(7)
-#define MC_INT_DECERR_EMEM BIT(6)
+#define MC_GLOBAL_INTSTATUS 0xf24
-#define MC_ERR_STATUS_TYPE_SHIFT 28
-#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (0x6 << 28)
-#define MC_ERR_STATUS_TYPE_MASK (0x7 << 28)
-#define MC_ERR_STATUS_READABLE BIT(27)
-#define MC_ERR_STATUS_WRITABLE BIT(26)
-#define MC_ERR_STATUS_NONSECURE BIT(25)
-#define MC_ERR_STATUS_ADR_HI_SHIFT 20
-#define MC_ERR_STATUS_ADR_HI_MASK 0x3
-#define MC_ERR_STATUS_SECURITY BIT(17)
+/* Bit field of MC_ERR_STATUS_0 register */
#define MC_ERR_STATUS_RW BIT(16)
+#define MC_ERR_STATUS_SECURITY BIT(17)
+#define MC_ERR_STATUS_NONSECURE BIT(25)
+#define MC_ERR_STATUS_WRITABLE BIT(26)
+#define MC_ERR_STATUS_READABLE BIT(27)
-#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
-
-#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) ((x) & 0x1ff)
-#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
-
-#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK 0x1ff
-#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE BIT(30)
-#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE BIT(31)
+#define MC_ERR_STATUS_GSC_ADR_HI_MASK 0xffff
+#define MC_ERR_STATUS_GSC_ADR_HI_SHIFT 16
+#define MC_ERR_STATUS_RT_ADR_HI_SHIFT 15
-#define MC_EMEM_ARB_OVERRIDE_EACK_MASK 0x3
+#define MC_ERR_STATUS_TYPE_SHIFT 28
+#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (0x6 << 28)
+#define MC_ERR_STATUS_RT_TYPE_MASK (0xf << 28)
+#define MC_ERR_STATUS_RT_TYPE_SHIFT 28
-#define MC_TIMING_UPDATE BIT(0)
+#define MC_ERR_STATUS_ADR_HI_SHIFT 20
#define MC_BROADCAST_CHANNEL ~0
+/* Tegra264 specific registers */
+
+/* Registers for MSS HUB */
+#define MSS_HUB_GLOBAL_INTSTATUS_0 0x6000
+#define MSS_HUBC_INTR BIT(0)
+#define MSS_HUB_GLOBAL_MASK 0x7F00
+#define MSS_HUB_GLOBAL_SHIFT 8
+
+#define MSS_HUB_HUBC_INTSTATUS_0 0x6008
+#define MSS_HUB_INTRSTATUS_0 0x600c
+#define MSS_HUB_HUBC_INTMASK_0 0x6010
+#define MSS_HUB_HUBC_SCRUB_DONE_INTMASK BIT(0)
+
+#define MSS_HUB_HUBC_INTPRIORITY_0 0x6014
+#define MSS_HUB_INTRMASK_0 0x6018
+#define MSS_HUB_COALESCER_ERR_INTMASK BIT(0)
+#define MSS_HUB_SMMU_BYPASS_ALLOW_ERR_INTMASK BIT(1)
+#define MSS_HUB_ILLEGAL_TBUGRP_ID_INTMASK BIT(2)
+#define MSS_HUB_MSI_ERR_INTMASK BIT(3)
+#define MSS_HUB_POISON_RSP_INTMASK BIT(4)
+#define MSS_HUB_RESTRICTED_ACCESS_ERR_INTMASK BIT(5)
+#define MSS_HUB_RESERVED_PA_ERR_INTMASK BIT(6)
+
+#define MSS_HUB_INTRPRIORITY_0 0x601c
+#define MSS_HUB_SMMU_BYPASS_ALLOW_ERR_STATUS_0 0x6020
+#define MSS_HUB_MSI_ERR_STATUS_0 0x6024
+#define MSS_HUB_POISON_RSP_STATUS_0 0x6028
+#define MSS_HUB_COALESCE_ERR_STATUS_0 0x60e0
+#define MSS_HUB_COALESCE_ERR_ADR_HI_0 0x60e4
+#define MSS_HUB_COALESCE_ERR_ADR_0 0x60e8
+#define MSS_HUB_RESTRICTED_ACCESS_ERR_STATUS_0 0x638c
+#define MSS_HUB_RESERVED_PA_ERR_STATUS_0 0x6390
+#define MSS_HUB_ILLEGAL_TBUGRP_ID_ERR_STATUS_0 0x63b0
+
+/* Registers for channels */
+#define MC_CH_INTSTATUS_0 0x82d4
+#define MC_CH_INTMASK_0 0x82d8
+#define WCAM_ERR_INTMASK BIT(19)
+
+#define MC_ERR_GENERALIZED_CARVEOUT_STATUS_1_0 0xbc74
+
+/* Registers for MCF */
+#define MCF_COMMON_INTSTATUS0_0_0 0xce04
+#define MCF_INTSTATUS_0 0xce2c
+#define MCF_INTMASK_0 0xce30
+#define MCF_INTPRIORITY_0 0xce34
+
+/* Registers for SBS */
+#define MSS_SBS_INTSTATUS_0 0xec08
+#define MSS_SBS_INTMASK_0 0xec0c
+#define MSS_SBS_FILL_FIFO_ISO_OVERFLOW_INTMASK BIT(0)
+#define MSS_SBS_FILL_FIFO_SISO_OVERFLOW_INTMASK BIT(1)
+#define MSS_SBS_FILL_FIFO_NISO_OVERFLOW_INTMASK BIT(2)
+
+/* Bit field of MC_ERR_ROUTE_SANITY_STATUS_0 register */
+#define MC_ERR_ROUTE_SANITY_RW BIT(12)
+#define MC_ERR_ROUTE_SANITY_SEC BIT(13)
+
+#define ERR_GENERALIZED_APERTURE_ID_SHIFT 0
+#define ERR_GENERALIZED_APERTURE_ID_MASK 0x1F
+#define ERR_GENERALIZED_CARVEOUT_APERTURE_ID_SHIFT 5
+#define ERR_GENERALIZED_CARVEOUT_APERTURE_ID_MASK 0x1F
+
static inline u32 tegra_mc_scale_percents(u64 val, unsigned int percents)
{
val = val * percents;
@@ -203,8 +261,9 @@ extern const struct tegra_mc_ops tegra186_mc_ops;
#endif
irqreturn_t tegra30_mc_handle_irq(int irq, void *data);
+extern const irq_handler_t tegra30_mc_irq_handlers[1];
extern const char * const tegra_mc_status_names[32];
-extern const char * const tegra_mc_error_names[8];
+extern const char * const tegra20_mc_error_names[8];
/*
* These IDs are for internal use of Tegra ICC drivers. The ID numbers are
diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c
index 41350570c815..02dd4e26288a 100644
--- a/drivers/memory/tegra/tegra114.c
+++ b/drivers/memory/tegra/tegra114.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/of.h>
@@ -1101,6 +1101,14 @@ static const struct tegra_mc_reset tegra114_mc_resets[] = {
TEGRA114_MC_RESET(VI, 0x200, 0x204, 17),
};
+static const struct tegra_mc_intmask tegra114_mc_intmasks[] = {
+ {
+ .reg = MC_INTMASK,
+ .mask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
+ MC_INT_DECERR_EMEM,
+ },
+};
+
const struct tegra_mc_soc tegra114_mc_soc = {
.clients = tegra114_mc_clients,
.num_clients = ARRAY_SIZE(tegra114_mc_clients),
@@ -1108,10 +1116,14 @@ const struct tegra_mc_soc tegra114_mc_soc = {
.atom_size = 32,
.client_id_mask = 0x7f,
.smmu = &tegra114_smmu_soc,
- .intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
- MC_INT_DECERR_EMEM,
+ .intmasks = tegra114_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra114_mc_intmasks),
.reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra114_mc_resets,
.num_resets = ARRAY_SIZE(tegra114_mc_resets),
.ops = &tegra30_mc_ops,
+ .regs = &tegra20_mc_regs,
+ .handle_irq = tegra30_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
+ .mc_err_status_type_mask = (0x7 << 28),
};
diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c
index ff26815e51f1..5cfbc169c5f9 100644
--- a/drivers/memory/tegra/tegra124-emc.c
+++ b/drivers/memory/tegra/tegra124-emc.c
@@ -608,7 +608,7 @@ static int tegra124_emc_prepare_timing_change(struct tegra_emc *emc,
if ((last->emc_mode_1 & 0x1) == (timing->emc_mode_1 & 0x1))
dll_change = DLL_CHANGE_NONE;
- else if (timing->emc_mode_1 & 0x1)
+ else if (!(timing->emc_mode_1 & 0x1))
dll_change = DLL_CHANGE_ON;
else
dll_change = DLL_CHANGE_OFF;
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c
index 991d4f7bc070..df87c5038625 100644
--- a/drivers/memory/tegra/tegra124.c
+++ b/drivers/memory/tegra/tegra124.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/of.h>
@@ -1258,6 +1258,15 @@ static const struct tegra_smmu_soc tegra124_smmu_soc = {
.num_asids = 128,
};
+static const struct tegra_mc_intmask tegra124_mc_intmasks[] = {
+ {
+ .reg = MC_INTMASK,
+ .mask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
+ MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE |
+ MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ },
+};
+
const struct tegra_mc_soc tegra124_mc_soc = {
.clients = tegra124_mc_clients,
.num_clients = ARRAY_SIZE(tegra124_mc_clients),
@@ -1267,14 +1276,18 @@ const struct tegra_mc_soc tegra124_mc_soc = {
.smmu = &tegra124_smmu_soc,
.emem_regs = tegra124_mc_emem_regs,
.num_emem_regs = ARRAY_SIZE(tegra124_mc_emem_regs),
- .intmask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
- MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE |
- MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ .intmasks = tegra124_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra124_mc_intmasks),
.reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra124_mc_resets,
.num_resets = ARRAY_SIZE(tegra124_mc_resets),
.icc_ops = &tegra124_mc_icc_ops,
.ops = &tegra30_mc_ops,
+ .regs = &tegra20_mc_regs,
+ .handle_irq = tegra30_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
+ .mc_addr_hi_mask = 0x3,
+ .mc_err_status_type_mask = (0x7 << 28),
};
#endif /* CONFIG_ARCH_TEGRA_124_SOC */
@@ -1292,6 +1305,15 @@ static const struct tegra_smmu_soc tegra132_smmu_soc = {
.num_asids = 128,
};
+static const struct tegra_mc_intmask tegra132_mc_intmasks[] = {
+ {
+ .reg = MC_INTMASK,
+ .mask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
+ MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE |
+ MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ },
+};
+
const struct tegra_mc_soc tegra132_mc_soc = {
.clients = tegra124_mc_clients,
.num_clients = ARRAY_SIZE(tegra124_mc_clients),
@@ -1299,13 +1321,17 @@ const struct tegra_mc_soc tegra132_mc_soc = {
.atom_size = 32,
.client_id_mask = 0x7f,
.smmu = &tegra132_smmu_soc,
- .intmask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
- MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE |
- MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ .intmasks = tegra132_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra132_mc_intmasks),
.reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra124_mc_resets,
.num_resets = ARRAY_SIZE(tegra124_mc_resets),
.icc_ops = &tegra124_mc_icc_ops,
.ops = &tegra30_mc_ops,
+ .regs = &tegra20_mc_regs,
+ .handle_irq = tegra30_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
+ .mc_addr_hi_mask = 0x3,
+ .mc_err_status_type_mask = (0x7 << 28),
};
#endif /* CONFIG_ARCH_TEGRA_132_SOC */
diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c
index dfddceecdd1a..03ebab6fbe68 100644
--- a/drivers/memory/tegra/tegra186-emc.c
+++ b/drivers/memory/tegra/tegra186-emc.c
@@ -22,6 +22,7 @@ struct tegra186_emc {
struct tegra_bpmp *bpmp;
struct device *dev;
struct clk *clk;
+ struct clk *clk_dbb;
struct tegra186_emc_dvfs *dvfs;
unsigned int num_dvfs;
@@ -328,6 +329,13 @@ static int tegra186_emc_probe(struct platform_device *pdev)
goto put_bpmp;
}
+ emc->clk_dbb = devm_clk_get_optional_enabled(&pdev->dev, "dbb");
+ if (IS_ERR(emc->clk_dbb)) {
+ err = dev_err_probe(&pdev->dev, PTR_ERR(emc->clk_dbb),
+ "failed to get DBB clock\n");
+ goto put_bpmp;
+ }
+
platform_set_drvdata(pdev, emc);
emc->dev = &pdev->dev;
diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c
index aee11457bf8e..91d56165605f 100644
--- a/drivers/memory/tegra/tegra186.c
+++ b/drivers/memory/tegra/tegra186.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2017-2025 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2017-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/io.h>
@@ -174,7 +174,6 @@ const struct tegra_mc_ops tegra186_mc_ops = {
.remove = tegra186_mc_remove,
.resume = tegra186_mc_resume,
.probe_device = tegra186_mc_probe_device,
- .handle_irq = tegra30_mc_handle_irq,
};
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
@@ -902,17 +901,30 @@ static const struct tegra_mc_client tegra186_mc_clients[] = {
},
};
+static const struct tegra_mc_intmask tegra186_mc_intmasks[] = {
+ {
+ .reg = MC_INTMASK,
+ .mask = MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
+ MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
+ MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ },
+};
+
const struct tegra_mc_soc tegra186_mc_soc = {
.num_clients = ARRAY_SIZE(tegra186_mc_clients),
.clients = tegra186_mc_clients,
.num_address_bits = 40,
.num_channels = 4,
.client_id_mask = 0xff,
- .intmask = MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
- MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
- MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ .intmasks = tegra186_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra186_mc_intmasks),
.ops = &tegra186_mc_ops,
.ch_intmask = 0x0000000f,
.global_intstatus_channel_shift = 0,
+ .regs = &tegra20_mc_regs,
+ .handle_irq = tegra30_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
+ .mc_addr_hi_mask = 0x3,
+ .mc_err_status_type_mask = (0x7 << 28),
};
#endif
diff --git a/drivers/memory/tegra/tegra194.c b/drivers/memory/tegra/tegra194.c
index 26035ac3a1eb..a8cc57690696 100644
--- a/drivers/memory/tegra/tegra194.c
+++ b/drivers/memory/tegra/tegra194.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2017-2021 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2017-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <soc/tegra/mc.h>
@@ -1343,19 +1343,31 @@ static const struct tegra_mc_client tegra194_mc_clients[] = {
},
};
+static const struct tegra_mc_intmask tegra194_mc_intmasks[] = {
+ {
+ .reg = MC_INTMASK,
+ .mask = MC_INT_DECERR_ROUTE_SANITY | MC_INT_DECERR_GENERALIZED_CARVEOUT |
+ MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
+ MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ },
+};
+
const struct tegra_mc_soc tegra194_mc_soc = {
.num_clients = ARRAY_SIZE(tegra194_mc_clients),
.clients = tegra194_mc_clients,
.num_address_bits = 40,
.num_channels = 16,
.client_id_mask = 0xff,
- .intmask = MC_INT_DECERR_ROUTE_SANITY |
- MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
- MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
- MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ .intmasks = tegra194_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra194_mc_intmasks),
.has_addr_hi_reg = true,
.ops = &tegra186_mc_ops,
.icc_ops = &tegra_mc_icc_ops,
.ch_intmask = 0x00000f00,
.global_intstatus_channel_shift = 8,
+ .regs = &tegra20_mc_regs,
+ .handle_irq = tegra30_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
+ .mc_addr_hi_mask = 0x3,
+ .mc_err_status_type_mask = (0x7 << 28),
};
diff --git a/drivers/memory/tegra/tegra20.c b/drivers/memory/tegra/tegra20.c
index 4748113bfe9d..27dd6886f86e 100644
--- a/drivers/memory/tegra/tegra20.c
+++ b/drivers/memory/tegra/tegra20.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2012-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/bitfield.h>
@@ -695,7 +695,7 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data)
unsigned int bit;
/* mask all interrupts to avoid flooding */
- status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
+ status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmasks[0].mask;
if (!status)
return IRQ_NONE;
@@ -713,7 +713,7 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data)
value = mc_readl(mc, reg);
id = value & mc->soc->client_id_mask;
- desc = tegra_mc_error_names[2];
+ desc = tegra20_mc_error_names[2];
if (value & BIT(31))
direction = "write";
@@ -724,7 +724,7 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data)
value = mc_readl(mc, reg);
id = (value >> 1) & mc->soc->client_id_mask;
- desc = tegra_mc_error_names[2];
+ desc = tegra20_mc_error_names[2];
if (value & BIT(0))
direction = "write";
@@ -736,7 +736,7 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data)
id = value & mc->soc->client_id_mask;
type = (value & BIT(30)) ? 4 : 3;
- desc = tegra_mc_error_names[type];
+ desc = tegra20_mc_error_names[type];
secure = "secure ";
if (value & BIT(31))
@@ -761,9 +761,20 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data)
return IRQ_HANDLED;
}
+static const irq_handler_t tegra20_mc_irq_handlers[] = {
+ tegra20_mc_handle_irq
+};
+
static const struct tegra_mc_ops tegra20_mc_ops = {
.probe = tegra20_mc_probe,
- .handle_irq = tegra20_mc_handle_irq,
+};
+
+static const struct tegra_mc_intmask tegra20_mc_intmasks[] = {
+ {
+ .reg = MC_INTMASK,
+ .mask = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE |
+ MC_INT_DECERR_EMEM,
+ },
};
const struct tegra_mc_soc tegra20_mc_soc = {
@@ -771,11 +782,15 @@ const struct tegra_mc_soc tegra20_mc_soc = {
.num_clients = ARRAY_SIZE(tegra20_mc_clients),
.num_address_bits = 32,
.client_id_mask = 0x3f,
- .intmask = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE |
- MC_INT_DECERR_EMEM,
+ .intmasks = tegra20_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra20_mc_intmasks),
.reset_ops = &tegra20_mc_reset_ops,
.resets = tegra20_mc_resets,
.num_resets = ARRAY_SIZE(tegra20_mc_resets),
.icc_ops = &tegra20_mc_icc_ops,
.ops = &tegra20_mc_ops,
+ .regs = &tegra20_mc_regs,
+ .handle_irq = tegra20_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra20_mc_irq_handlers),
+ .mc_err_status_type_mask = (0x7 << 28),
};
diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c
index 3c2949c16fde..f58f3ef6f681 100644
--- a/drivers/memory/tegra/tegra210.c
+++ b/drivers/memory/tegra/tegra210.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2015 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2015-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <dt-bindings/memory/tegra210-mc.h>
@@ -1273,6 +1273,15 @@ static const struct tegra_mc_reset tegra210_mc_resets[] = {
TEGRA210_MC_RESET(TSECB, 0x970, 0x974, 13),
};
+static const struct tegra_mc_intmask tegra210_mc_intmasks[] = {
+ {
+ .reg = MC_INTMASK,
+ .mask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
+ MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE |
+ MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ },
+};
+
const struct tegra_mc_soc tegra210_mc_soc = {
.clients = tegra210_mc_clients,
.num_clients = ARRAY_SIZE(tegra210_mc_clients),
@@ -1280,11 +1289,15 @@ const struct tegra_mc_soc tegra210_mc_soc = {
.atom_size = 64,
.client_id_mask = 0xff,
.smmu = &tegra210_smmu_soc,
- .intmask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
- MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE |
- MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ .intmasks = tegra210_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra210_mc_intmasks),
.reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra210_mc_resets,
.num_resets = ARRAY_SIZE(tegra210_mc_resets),
.ops = &tegra30_mc_ops,
+ .regs = &tegra20_mc_regs,
+ .handle_irq = tegra30_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
+ .mc_addr_hi_mask = 0x3,
+ .mc_err_status_type_mask = (0x7 << 28),
};
diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c
index 5f57cea48b62..87b22038a5fb 100644
--- a/drivers/memory/tegra/tegra234.c
+++ b/drivers/memory/tegra/tegra234.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2022-2023, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2022-2026, NVIDIA CORPORATION. All rights reserved.
*/
#include <soc/tegra/mc.h>
@@ -1132,16 +1132,23 @@ static const struct tegra_mc_icc_ops tegra234_mc_icc_ops = {
.set = tegra234_mc_icc_set,
};
+static const struct tegra_mc_intmask tegra234_mc_intmasks[] = {
+ {
+ .reg = MC_INTMASK,
+ .mask = MC_INT_DECERR_ROUTE_SANITY | MC_INT_DECERR_GENERALIZED_CARVEOUT |
+ MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
+ MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ },
+};
+
const struct tegra_mc_soc tegra234_mc_soc = {
.num_clients = ARRAY_SIZE(tegra234_mc_clients),
.clients = tegra234_mc_clients,
.num_address_bits = 40,
.num_channels = 16,
.client_id_mask = 0x1ff,
- .intmask = MC_INT_DECERR_ROUTE_SANITY |
- MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
- MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
- MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ .intmasks = tegra234_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra234_mc_intmasks),
.has_addr_hi_reg = true,
.ops = &tegra186_mc_ops,
.icc_ops = &tegra234_mc_icc_ops,
@@ -1152,4 +1159,9 @@ const struct tegra_mc_soc tegra234_mc_soc = {
* supported.
*/
.num_carveouts = 32,
+ .regs = &tegra20_mc_regs,
+ .handle_irq = tegra30_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
+ .mc_addr_hi_mask = 0x3,
+ .mc_err_status_type_mask = (0x7 << 28),
};
diff --git a/drivers/memory/tegra/tegra264.c b/drivers/memory/tegra/tegra264.c
index 5203e6c11372..e43ef14da1ee 100644
--- a/drivers/memory/tegra/tegra264.c
+++ b/drivers/memory/tegra/tegra264.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2025, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2025-2026, NVIDIA CORPORATION. All rights reserved.
*/
#include <dt-bindings/memory/nvidia,tegra264.h>
@@ -188,6 +188,41 @@ static const struct tegra_mc_client tegra264_mc_clients[] = {
},
};
+static const char *const tegra264_hub_error_names[32] = {
+ [0] = "coalescer error",
+ [1] = "SMMU BYPASS ALLOW error",
+ [2] = "Illegal tbugrp_id error",
+ [3] = "Malformed MSI request error",
+ [4] = "Read response with poison bit error",
+ [5] = "Restricted access violation error",
+ [6] = "Reserved PA error",
+};
+
+static const char *const tegra264_mc_error_names[4] = {
+ [1] = "EMEM decode error",
+ [2] = "TrustZone violation",
+ [3] = "Carveout violation",
+};
+
+static const char *const tegra264_rt_error_names[16] = {
+ [1] = "DECERR_PARTIAL_POPULATED",
+ [2] = "DECERR_SMMU_BYPASS",
+ [3] = "DECERR_INVALID_MMIO",
+ [4] = "DECERR_INVALID_GIC_MSI",
+ [5] = "DECERR_ATOMIC_SYSRAM",
+ [9] = "DECERR_REMOTE_REQ_PRE_BOOT",
+ [10] = "DECERR_ISO_OVER_C2C",
+ [11] = "DECERR_UNSUPPORTED_SBS_OPCODE",
+ [12] = "DECERR_SBS_REQ_OVER_SISO_LL",
+};
+
+/*
+ * MC instance aperture mapping for hubc registers
+ */
+static const int mc_hubc_aperture_number[5] = {
+ 7, 8, 9, 10, 11
+};
+
/*
* tegra264_mc_icc_set() - Pass MC client info to the BPMP-FW
* @src: ICC node for Memory Controller's (MC) Client
@@ -283,6 +318,312 @@ static int tegra264_mc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *pea
return 0;
}
+static void mcf_log_fault(struct tegra_mc *mc, u32 channel, unsigned long mcf_ch_intstatus)
+{
+ unsigned int bit;
+
+ for_each_set_bit(bit, &mcf_ch_intstatus, 32) {
+ const char *client = "unknown", *desc = "NA";
+ u32 status_reg, status1_reg = 0, addr_reg, addr_hi_reg = 0, err_type_mask = 0;
+ u32 value, client_id, i, addr_hi_shift = 0, addr_hi_mask = 0, status1;
+ u32 mc_rw_bit = MC_ERR_STATUS_RW, mc_sec_bit = MC_ERR_STATUS_SECURITY;
+ phys_addr_t addr = 0;
+ u8 type;
+
+ switch (BIT(bit)) {
+ case MC_INT_DECERR_EMEM:
+ case MC_INT_SECURITY_VIOLATION:
+ status_reg = mc->soc->regs->err_status;
+ addr_reg = mc->soc->regs->err_add;
+ addr_hi_reg = mc->soc->regs->err_add_hi;
+ err_type_mask = mc->soc->mc_err_status_type_mask;
+ break;
+
+ case MC_INT_DECERR_VPR:
+ status_reg = mc->soc->regs->err_vpr_status;
+ addr_reg = mc->soc->regs->err_vpr_add;
+ addr_hi_shift = MC_ERR_STATUS_ADR_HI_SHIFT;
+ addr_hi_mask = mc->soc->mc_addr_hi_mask;
+ break;
+
+ case MC_INT_SECERR_SEC:
+ status_reg = mc->soc->regs->err_sec_status;
+ addr_reg = mc->soc->regs->err_sec_add;
+ addr_hi_shift = MC_ERR_STATUS_ADR_HI_SHIFT;
+ addr_hi_mask = mc->soc->mc_addr_hi_mask;
+ break;
+
+ case MC_INT_DECERR_MTS:
+ status_reg = mc->soc->regs->err_mts_status;
+ addr_reg = mc->soc->regs->err_mts_add;
+ addr_hi_shift = MC_ERR_STATUS_ADR_HI_SHIFT;
+ addr_hi_mask = mc->soc->mc_addr_hi_mask;
+ break;
+
+ case MC_INT_DECERR_GENERALIZED_CARVEOUT:
+ status_reg = mc->soc->regs->err_gen_co_status;
+ status1_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS_1_0;
+ addr_reg = mc->soc->regs->err_gen_co_add;
+ addr_hi_shift = MC_ERR_STATUS_GSC_ADR_HI_SHIFT;
+ addr_hi_mask = MC_ERR_STATUS_GSC_ADR_HI_MASK;
+ break;
+
+ case MC_INT_DECERR_ROUTE_SANITY:
+ case MC_INT_DECERR_ROUTE_SANITY_GIC_MSI:
+ status_reg = mc->soc->regs->err_route_status;
+ addr_reg = mc->soc->regs->err_route_add;
+ addr_hi_shift = MC_ERR_STATUS_RT_ADR_HI_SHIFT;
+ addr_hi_mask = mc->soc->mc_addr_hi_mask;
+ mc_sec_bit = MC_ERR_ROUTE_SANITY_SEC;
+ mc_rw_bit = MC_ERR_ROUTE_SANITY_RW;
+ err_type_mask = MC_ERR_STATUS_RT_TYPE_MASK;
+ break;
+
+ default:
+ dev_err_ratelimited(mc->dev, "Incorrect MC interrupt mask\n");
+ return;
+ }
+
+ value = mc_ch_readl(mc, channel, status_reg);
+ if (addr_hi_reg) {
+ addr = mc_ch_readl(mc, channel, addr_hi_reg);
+ } else {
+ if (!status1_reg) {
+ addr = ((value >> addr_hi_shift) & addr_hi_mask);
+ } else {
+ status1 = mc_ch_readl(mc, channel, status1_reg);
+ addr = ((status1 >> addr_hi_shift) & addr_hi_mask);
+ }
+ }
+
+ addr <<= 32;
+ addr |= mc_ch_readl(mc, channel, addr_reg);
+
+ client_id = value & mc->soc->client_id_mask;
+ for (i = 0; i < mc->soc->num_clients; i++) {
+ if (mc->soc->clients[i].id == client_id) {
+ client = mc->soc->clients[i].name;
+ break;
+ }
+ }
+
+ if (err_type_mask == MC_ERR_STATUS_RT_TYPE_MASK) {
+ type = (value & err_type_mask) >>
+ MC_ERR_STATUS_RT_TYPE_SHIFT;
+ desc = tegra264_rt_error_names[type];
+ } else if (err_type_mask) {
+ type = (value & err_type_mask) >>
+ MC_ERR_STATUS_TYPE_SHIFT;
+ desc = tegra264_mc_error_names[type];
+ }
+
+ dev_err_ratelimited(mc->dev, "%s: %s %s @%pa: %s (%s)\n",
+ client, value & mc_sec_bit ? "secure" : "non-secure",
+ value & mc_rw_bit ? "write" : "read", &addr,
+ tegra_mc_status_names[bit] ?: "unknown", desc);
+ if (status1_reg)
+ dev_err_ratelimited(mc->dev, "gsc_apr_id=%u gsc_co_apr_id=%u\n",
+ ((status1 >> ERR_GENERALIZED_APERTURE_ID_SHIFT)
+ & ERR_GENERALIZED_APERTURE_ID_MASK),
+ ((status1 >> ERR_GENERALIZED_CARVEOUT_APERTURE_ID_SHIFT)
+ & ERR_GENERALIZED_CARVEOUT_APERTURE_ID_MASK));
+ }
+
+ /* clear interrupts */
+ mc_ch_writel(mc, channel, mcf_ch_intstatus, MCF_INTSTATUS_0);
+}
+
+static irqreturn_t handle_mcf_irq(int irq, void *data)
+{
+ struct tegra_mc *mc = data;
+ unsigned long common_intstat, intstatus;
+ u32 slice;
+
+ /* Read MCF_COMMON_INTSTATUS0_0_0 from MCB block */
+ common_intstat = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, MCF_COMMON_INTSTATUS0_0_0);
+ if (common_intstat == 0) {
+ dev_warn(mc->dev, "No interrupt in MCF\n");
+ return IRQ_NONE;
+ }
+
+ for_each_set_bit(slice, &common_intstat, 32) {
+ /* Find out the slice number on which interrupt occurred */
+ if (slice > 4) {
+ dev_err(mc->dev, "Slice index out of bounds: %u\n", slice);
+ return IRQ_NONE;
+ }
+
+ intstatus = mc_ch_readl(mc, slice, MCF_INTSTATUS_0);
+ if (intstatus != 0)
+ mcf_log_fault(mc, slice, intstatus);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hub_log_fault(struct tegra_mc *mc, u32 hub, unsigned long hub_intstat)
+{
+ unsigned int bit;
+
+ for_each_set_bit(bit, &hub_intstat, 32) {
+ const char *client = "unknown";
+ u32 client_id, status_reg, value, i;
+ phys_addr_t addr = 0;
+
+ switch (BIT(bit)) {
+ case MSS_HUB_COALESCER_ERR_INTMASK:
+ status_reg = MSS_HUB_COALESCE_ERR_STATUS_0;
+ addr = mc_ch_readl(mc, hub, MSS_HUB_COALESCE_ERR_ADR_HI_0);
+ addr <<= 32;
+ addr |= mc_ch_readl(mc, hub, MSS_HUB_COALESCE_ERR_ADR_0);
+ break;
+
+ case MSS_HUB_SMMU_BYPASS_ALLOW_ERR_INTMASK:
+ status_reg = MSS_HUB_SMMU_BYPASS_ALLOW_ERR_STATUS_0;
+ break;
+
+ case MSS_HUB_ILLEGAL_TBUGRP_ID_INTMASK:
+ status_reg = MSS_HUB_ILLEGAL_TBUGRP_ID_ERR_STATUS_0;
+ break;
+
+ case MSS_HUB_MSI_ERR_INTMASK:
+ status_reg = MSS_HUB_MSI_ERR_STATUS_0;
+ break;
+
+ case MSS_HUB_POISON_RSP_INTMASK:
+ status_reg = MSS_HUB_POISON_RSP_STATUS_0;
+ break;
+
+ case MSS_HUB_RESTRICTED_ACCESS_ERR_INTMASK:
+ status_reg = MSS_HUB_RESTRICTED_ACCESS_ERR_STATUS_0;
+ break;
+
+ case MSS_HUB_RESERVED_PA_ERR_INTMASK:
+ status_reg = MSS_HUB_RESERVED_PA_ERR_STATUS_0;
+ break;
+
+ default:
+ dev_err_ratelimited(mc->dev, "Incorrect HUB interrupt mask\n");
+ return;
+ }
+
+ value = mc_ch_readl(mc, hub, status_reg);
+
+ client_id = value & mc->soc->client_id_mask;
+ for (i = 0; i < mc->soc->num_clients; i++) {
+ if (mc->soc->clients[i].id == client_id) {
+ client = mc->soc->clients[i].name;
+ break;
+ }
+ }
+
+ dev_err_ratelimited(mc->dev, "%s: @%pa: %s status: 0x%x\n",
+ client, &addr, tegra264_hub_error_names[bit] ?: "unknown",
+ value);
+ }
+
+ /* clear interrupts */
+ mc_ch_writel(mc, hub, hub_intstat, MSS_HUB_INTRSTATUS_0);
+}
+
+static irqreturn_t handle_hub_irq(int irq, void *data, int mc_hubc_aperture_number)
+{
+ struct tegra_mc *mc = data;
+ u32 global_intstat;
+ unsigned long hub_interrupt, intstat, hub;
+
+ /* Read MSS_HUB_GLOBAL_INTSTATUS_0 from mc_hubc_aperture_number block */
+ global_intstat = mc_ch_readl(mc, mc_hubc_aperture_number, MSS_HUB_GLOBAL_INTSTATUS_0);
+ if (global_intstat == 0) {
+ dev_warn(mc->dev, "No interrupt in HUB/HUBC\n");
+ return IRQ_NONE;
+ }
+
+ /* Handle interrupt from hubc */
+ if (global_intstat & MSS_HUBC_INTR) {
+ /* Read MSS_HUB_HUBC_INTSTATUS_0 from block mc_hubc_aperture_number */
+ intstat = mc_ch_readl(mc, mc_hubc_aperture_number, MSS_HUB_HUBC_INTSTATUS_0);
+ if (intstat != 0) {
+ dev_err_ratelimited(mc->dev, "Scrubber operation status: 0x%lx\n",
+ intstat);
+ /* Clear hubc interrupt */
+ mc_ch_writel(mc, mc_hubc_aperture_number, intstat,
+ MSS_HUB_HUBC_INTSTATUS_0);
+ }
+ }
+
+ hub_interrupt = (global_intstat & MSS_HUB_GLOBAL_MASK) >> MSS_HUB_GLOBAL_SHIFT;
+ /* Handle interrupt from hub */
+ for_each_set_bit(hub, &hub_interrupt, 32) {
+ /* Read MSS_HUB_INTRSTATUS_0 from block MCi */
+ intstat = mc_ch_readl(mc, hub, MSS_HUB_INTRSTATUS_0);
+ if (intstat != 0)
+ hub_log_fault(mc, hub, intstat);
+ }
+
+ /* Clear global interrupt status register */
+ mc_ch_writel(mc, mc_hubc_aperture_number, global_intstat, MSS_HUB_GLOBAL_INTSTATUS_0);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_disp_hub_irq(int irq, void *data)
+{
+ return handle_hub_irq(irq, data, mc_hubc_aperture_number[0]);
+}
+
+static irqreturn_t handle_system_hub_irq(int irq, void *data)
+{
+ return handle_hub_irq(irq, data, mc_hubc_aperture_number[1]);
+}
+
+static irqreturn_t handle_vision_hub_irq(int irq, void *data)
+{
+ return handle_hub_irq(irq, data, mc_hubc_aperture_number[2]);
+}
+
+static irqreturn_t handle_uphy_hub_irq(int irq, void *data)
+{
+ return handle_hub_irq(irq, data, mc_hubc_aperture_number[3]);
+}
+
+static irqreturn_t handle_top_hub_irq(int irq, void *data)
+{
+ return handle_hub_irq(irq, data, mc_hubc_aperture_number[4]);
+}
+
+static irqreturn_t handle_generic_irq(struct tegra_mc *mc, unsigned long intstat_reg)
+{
+ u32 intstat, i;
+
+ /* Iterate over all MC blocks to read INTSTATUS */
+ for (i = 0; i < mc->num_channels; i++) {
+ intstat = mc_ch_readl(mc, i, intstat_reg);
+ if (intstat) {
+ dev_err_ratelimited(mc->dev, "channel: %i status: 0x%x\n", i, intstat);
+ /* Clear interrupt */
+ mc_ch_writel(mc, i, intstat, intstat_reg);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_sbs_irq(int irq, void *data)
+{
+ return handle_generic_irq((struct tegra_mc *)data, MSS_SBS_INTSTATUS_0);
+}
+
+static irqreturn_t handle_channel_irq(int irq, void *data)
+{
+ return handle_generic_irq((struct tegra_mc *)data, MC_CH_INTSTATUS_0);
+}
+
+static const irq_handler_t tegra264_mc_irq_handlers[8] = {
+ handle_mcf_irq, handle_disp_hub_irq, handle_vision_hub_irq,
+ handle_system_hub_irq, handle_uphy_hub_irq, handle_top_hub_irq,
+ handle_sbs_irq, handle_channel_irq
+};
+
static const struct tegra_mc_icc_ops tegra264_mc_icc_ops = {
.xlate = tegra_mc_icc_xlate,
.aggregate = tegra264_mc_icc_aggregate,
@@ -290,16 +631,80 @@ static const struct tegra_mc_icc_ops tegra264_mc_icc_ops = {
.set = tegra264_mc_icc_set,
};
+static const struct tegra_mc_regs tegra264_mc_regs = {
+ .cfg_channel_enable = 0x8870,
+ .err_status = 0xbc00,
+ .err_add = 0xbc04,
+ .err_add_hi = 0xbc08,
+ .err_vpr_status = 0xbc20,
+ .err_vpr_add = 0xbc24,
+ .err_sec_status = 0xbc3c,
+ .err_sec_add = 0xbc40,
+ .err_mts_status = 0xbc5c,
+ .err_mts_add = 0xbc60,
+ .err_gen_co_status = 0xbc78,
+ .err_gen_co_add = 0xbc7c,
+ .err_route_status = 0xbc64,
+ .err_route_add = 0xbc68,
+};
+
+static const struct tegra_mc_intmask tegra264_mc_intmasks[] = {
+ {
+ .reg = MCF_INTMASK_0,
+ .mask = MC_INT_DECERR_ROUTE_SANITY_GIC_MSI | MC_INT_DECERR_ROUTE_SANITY |
+ MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
+ MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | MC_INT_SECURITY_VIOLATION |
+ MC_INT_DECERR_EMEM,
+ },
+ {
+ .reg = MCF_INTPRIORITY_0,
+ .mask = MC_INT_DECERR_ROUTE_SANITY_GIC_MSI | MC_INT_DECERR_ROUTE_SANITY |
+ MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
+ MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | MC_INT_SECURITY_VIOLATION |
+ MC_INT_DECERR_EMEM,
+ },
+ {
+ .reg = MSS_HUB_INTRMASK_0,
+ .mask = MSS_HUB_COALESCER_ERR_INTMASK | MSS_HUB_SMMU_BYPASS_ALLOW_ERR_INTMASK |
+ MSS_HUB_ILLEGAL_TBUGRP_ID_INTMASK | MSS_HUB_MSI_ERR_INTMASK |
+ MSS_HUB_POISON_RSP_INTMASK | MSS_HUB_RESTRICTED_ACCESS_ERR_INTMASK |
+ MSS_HUB_RESERVED_PA_ERR_INTMASK,
+ },
+ {
+ .reg = MSS_HUB_INTRPRIORITY_0,
+ .mask = MSS_HUB_COALESCER_ERR_INTMASK | MSS_HUB_SMMU_BYPASS_ALLOW_ERR_INTMASK |
+ MSS_HUB_ILLEGAL_TBUGRP_ID_INTMASK | MSS_HUB_MSI_ERR_INTMASK |
+ MSS_HUB_POISON_RSP_INTMASK | MSS_HUB_RESTRICTED_ACCESS_ERR_INTMASK |
+ MSS_HUB_RESERVED_PA_ERR_INTMASK,
+ },
+ {
+ .reg = MSS_HUB_HUBC_INTMASK_0,
+ .mask = MSS_HUB_HUBC_SCRUB_DONE_INTMASK,
+ },
+ {
+ .reg = MSS_HUB_HUBC_INTPRIORITY_0,
+ .mask = MSS_HUB_HUBC_SCRUB_DONE_INTMASK,
+ },
+ {
+ .reg = MSS_SBS_INTMASK_0,
+ .mask = MSS_SBS_FILL_FIFO_ISO_OVERFLOW_INTMASK |
+ MSS_SBS_FILL_FIFO_SISO_OVERFLOW_INTMASK |
+ MSS_SBS_FILL_FIFO_NISO_OVERFLOW_INTMASK,
+ },
+ {
+ .reg = MC_CH_INTMASK_0,
+ .mask = WCAM_ERR_INTMASK,
+ }
+};
+
const struct tegra_mc_soc tegra264_mc_soc = {
.num_clients = ARRAY_SIZE(tegra264_mc_clients),
.clients = tegra264_mc_clients,
.num_address_bits = 40,
.num_channels = 16,
.client_id_mask = 0x1ff,
- .intmask = MC_INT_DECERR_ROUTE_SANITY |
- MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
- MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
- MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
+ .intmasks = tegra264_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra264_mc_intmasks),
.has_addr_hi_reg = true,
.ops = &tegra186_mc_ops,
.icc_ops = &tegra264_mc_icc_ops,
@@ -310,4 +715,9 @@ const struct tegra_mc_soc tegra264_mc_soc = {
* supported.
*/
.num_carveouts = 32,
+ .mc_addr_hi_mask = 0xff,
+ .mc_err_status_type_mask = (0x3 << 28),
+ .regs = &tegra264_mc_regs,
+ .handle_irq = tegra264_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra264_mc_irq_handlers),
};
diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c
index 606106dd2b32..5812c8cd6ce4 100644
--- a/drivers/memory/tegra/tegra30-emc.c
+++ b/drivers/memory/tegra/tegra30-emc.c
@@ -554,14 +554,14 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
emc->emc_cfg = readl_relaxed(emc->regs + EMC_CFG);
emc_dbg = readl_relaxed(emc->regs + EMC_DBG);
- if (emc->dll_on == !!(timing->emc_mode_1 & 0x1))
+ if (emc->dll_on == !(timing->emc_mode_1 & 0x1))
dll_change = DLL_CHANGE_NONE;
- else if (timing->emc_mode_1 & 0x1)
+ else if (!(timing->emc_mode_1 & 0x1))
dll_change = DLL_CHANGE_ON;
else
dll_change = DLL_CHANGE_OFF;
- emc->dll_on = !!(timing->emc_mode_1 & 0x1);
+ emc->dll_on = !(timing->emc_mode_1 & 0x1);
if (timing->data[80] && !readl_relaxed(emc->regs + EMC_ZCAL_INTERVAL))
emc->zcal_long = true;
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
index a6bcde4b92c0..8389e3af0121 100644
--- a/drivers/memory/tegra/tegra30.c
+++ b/drivers/memory/tegra/tegra30.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/device.h>
@@ -1384,6 +1384,14 @@ static const struct tegra_mc_icc_ops tegra30_mc_icc_ops = {
.set = tegra30_mc_icc_set,
};
+static const struct tegra_mc_intmask tegra30_mc_intmasks[] = {
+ {
+ .reg = MC_INTMASK,
+ .mask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
+ MC_INT_DECERR_EMEM,
+ },
+};
+
const struct tegra_mc_soc tegra30_mc_soc = {
.clients = tegra30_mc_clients,
.num_clients = ARRAY_SIZE(tegra30_mc_clients),
@@ -1393,11 +1401,15 @@ const struct tegra_mc_soc tegra30_mc_soc = {
.smmu = &tegra30_smmu_soc,
.emem_regs = tegra30_mc_emem_regs,
.num_emem_regs = ARRAY_SIZE(tegra30_mc_emem_regs),
- .intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
- MC_INT_DECERR_EMEM,
+ .intmasks = tegra30_mc_intmasks,
+ .num_intmasks = ARRAY_SIZE(tegra30_mc_intmasks),
.reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra30_mc_resets,
.num_resets = ARRAY_SIZE(tegra30_mc_resets),
.icc_ops = &tegra30_mc_icc_ops,
.ops = &tegra30_mc_ops,
+ .regs = &tegra20_mc_regs,
+ .handle_irq = tegra30_mc_irq_handlers,
+ .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
+ .mc_err_status_type_mask = (0x7 << 28),
};
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
index 6ee4c59db620..e6da035d1306 100644
--- a/include/soc/tegra/mc.h
+++ b/include/soc/tegra/mc.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (C) 2014-2026 NVIDIA Corporation
*/
#ifndef __SOC_TEGRA_MC_H__
@@ -10,10 +10,11 @@
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/interconnect-provider.h>
+#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/reset-controller.h>
-#include <linux/types.h>
#include <linux/tegra-icc.h>
+#include <linux/types.h>
struct clk;
struct device;
@@ -164,10 +165,31 @@ struct tegra_mc_ops {
int (*probe)(struct tegra_mc *mc);
void (*remove)(struct tegra_mc *mc);
int (*resume)(struct tegra_mc *mc);
- irqreturn_t (*handle_irq)(int irq, void *data);
int (*probe_device)(struct tegra_mc *mc, struct device *dev);
};
+struct tegra_mc_regs {
+ unsigned int cfg_channel_enable;
+ unsigned int err_status;
+ unsigned int err_add;
+ unsigned int err_add_hi;
+ unsigned int err_vpr_status;
+ unsigned int err_vpr_add;
+ unsigned int err_sec_status;
+ unsigned int err_sec_add;
+ unsigned int err_mts_status;
+ unsigned int err_mts_add;
+ unsigned int err_gen_co_status;
+ unsigned int err_gen_co_add;
+ unsigned int err_route_status;
+ unsigned int err_route_add;
+};
+
+struct tegra_mc_intmask {
+ u32 reg;
+ u32 mask;
+};
+
struct tegra_mc_soc {
const struct tegra_mc_client *clients;
unsigned int num_clients;
@@ -185,7 +207,6 @@ struct tegra_mc_soc {
const struct tegra_smmu_soc *smmu;
- u32 intmask;
u32 ch_intmask;
u32 global_intstatus_channel_shift;
bool has_addr_hi_reg;
@@ -196,6 +217,14 @@ struct tegra_mc_soc {
const struct tegra_mc_icc_ops *icc_ops;
const struct tegra_mc_ops *ops;
+ const struct tegra_mc_regs *regs;
+
+ const irq_handler_t *handle_irq;
+ unsigned int num_interrupts;
+ unsigned int mc_addr_hi_mask;
+ unsigned int mc_err_status_type_mask;
+ const struct tegra_mc_intmask *intmasks;
+ unsigned int num_intmasks;
};
struct tegra_mc {
@@ -206,7 +235,6 @@ struct tegra_mc {
void __iomem *bcast_ch_regs;
void __iomem **ch_regs;
struct clk *clk;
- int irq;
const struct tegra_mc_soc *soc;
unsigned long tick;
@@ -256,4 +284,6 @@ tegra_mc_get_carveout_info(struct tegra_mc *mc, unsigned int id,
}
#endif
+extern const struct tegra_mc_regs tegra20_mc_regs;
+
#endif /* __SOC_TEGRA_MC_H__ */