summaryrefslogtreecommitdiff
path: root/drivers/memory
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/memory')
-rw-r--r--drivers/memory/Kconfig23
-rw-r--r--drivers/memory/Makefile1
-rw-r--r--drivers/memory/brcmstb_memc.c56
-rw-r--r--drivers/memory/bt1-l2-ctl.c2
-rw-r--r--drivers/memory/emif.c1
-rw-r--r--drivers/memory/mtk-smi.c85
-rw-r--r--drivers/memory/omap-gpmc.c19
-rw-r--r--drivers/memory/stm32_omm.c470
-rw-r--r--drivers/memory/tegra/Kconfig8
-rw-r--r--drivers/memory/tegra/Makefile2
-rw-r--r--drivers/memory/tegra/mc.c5
-rw-r--r--drivers/memory/tegra/mc.h9
-rw-r--r--drivers/memory/tegra/tegra186-emc.c5
-rw-r--r--drivers/memory/tegra/tegra186.c17
-rw-r--r--drivers/memory/tegra/tegra210-emc-core.c5
-rw-r--r--drivers/memory/tegra/tegra264-bwmgr.h50
-rw-r--r--drivers/memory/tegra/tegra264.c313
17 files changed, 985 insertions, 86 deletions
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index c82d8d8a16ea..79df0d22e218 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -32,7 +32,7 @@ config ARM_PL172_MPMC
config ATMEL_EBI
bool "Atmel EBI driver"
- default y if ARCH_AT91
+ default ARCH_AT91
depends on ARCH_AT91 || COMPILE_TEST
depends on OF
select MFD_SYSCON
@@ -147,7 +147,7 @@ config FPGA_DFL_EMIF
config MVEBU_DEVBUS
bool "Marvell EBU Device Bus Controller"
- default y if PLAT_ORION
+ default PLAT_ORION
depends on PLAT_ORION || COMPILE_TEST
depends on OF
help
@@ -198,7 +198,7 @@ config DA8XX_DDRCTL
config PL353_SMC
tristate "ARM PL35X Static Memory Controller(SMC) driver"
- default y if ARM
+ default ARM
depends on ARM || COMPILE_TEST
depends on ARM_AMBA
help
@@ -225,6 +225,23 @@ config STM32_FMC2_EBI
devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
SOCs containing the FMC2 External Bus Interface.
+config STM32_OMM
+ tristate "STM32 Octo Memory Manager"
+ depends on SPI_STM32_OSPI || COMPILE_TEST
+ help
+ This driver manages the muxing between the 2 OSPI busses and
+ the 2 output ports. There are 4 possible muxing configurations:
+ - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2
+ output is on port 2
+ - OSPI1 and OSPI2 are multiplexed over the same output port 1
+ - swapped mode (no multiplexing), OSPI1 output is on port 2,
+ OSPI2 output is on port 1
+ - OSPI1 and OSPI2 are multiplexed over the same output port 2
+ It also manages :
+ - the split of the memory area shared between the 2 OSPI instances.
+ - chip select selection override.
+ - the time between 2 transactions in multiplexed mode.
+
source "drivers/memory/samsung/Kconfig"
source "drivers/memory/tegra/Kconfig"
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index d2e6ca9abbe0..c1959661bf63 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o
obj-$(CONFIG_PL353_SMC) += pl353-smc.o
obj-$(CONFIG_RENESAS_RPCIF) += renesas-rpc-if.o
obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o
+obj-$(CONFIG_STM32_OMM) += stm32_omm.o
obj-$(CONFIG_SAMSUNG_MC) += samsung/
obj-$(CONFIG_TEGRA_MC) += tegra/
diff --git a/drivers/memory/brcmstb_memc.c b/drivers/memory/brcmstb_memc.c
index c87b37e2c1f0..ba73470b1b13 100644
--- a/drivers/memory/brcmstb_memc.c
+++ b/drivers/memory/brcmstb_memc.c
@@ -184,62 +184,10 @@ static const struct of_device_id brcmstb_memc_of_match[] = {
.compatible = "brcm,brcmstb-memc-ddr-rev-b.2.1",
.data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
},
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.2",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.3",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.5",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.6",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.7",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.8",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.0",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.1",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.0",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.2",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.3",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- {
- .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.4",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
- },
- /* default to the original offset */
+ /* default to the V21 offset */
{
.compatible = "brcm,brcmstb-memc-ddr",
- .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X]
+ .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
},
{}
};
diff --git a/drivers/memory/bt1-l2-ctl.c b/drivers/memory/bt1-l2-ctl.c
index 78bd71b203f2..0fd96abc172a 100644
--- a/drivers/memory/bt1-l2-ctl.c
+++ b/drivers/memory/bt1-l2-ctl.c
@@ -222,7 +222,7 @@ static ssize_t l2_ctl_latency_show(struct device *dev,
if (ret)
return ret;
- return scnprintf(buf, PAGE_SIZE, "%u\n", data);
+ return sysfs_emit(buf, "%u\n", data);
}
static ssize_t l2_ctl_latency_store(struct device *dev,
diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
index 2e1ecae9e959..2fadad0666b1 100644
--- a/drivers/memory/emif.c
+++ b/drivers/memory/emif.c
@@ -39,6 +39,7 @@
* are two devices attached to this EMIF, this
* value is the maximum of the two temperature
* levels.
+ * @lpmode: Chosen low power mode
* @node: node in the device list
* @base: base address of memory-mapped IO registers.
* @dev: device pointer.
diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c
index a8f5467d6b31..733e22f695ab 100644
--- a/drivers/memory/mtk-smi.c
+++ b/drivers/memory/mtk-smi.c
@@ -283,6 +283,75 @@ static int mtk_smi_larb_config_port_gen2_general(struct device *dev)
return 0;
}
+static const u8 mtk_smi_larb_mt6893_ostd[][SMI_LARB_PORT_NR_MAX] = {
+ [0] = {0x2, 0x6, 0x2, 0x2, 0x2, 0x28, 0x18, 0x18, 0x1, 0x1, 0x1, 0x8,
+ 0x8, 0x1, 0x3f},
+ [1] = {0x2, 0x6, 0x2, 0x2, 0x2, 0x28, 0x18, 0x18, 0x1, 0x1, 0x1, 0x8,
+ 0x8, 0x1, 0x3f},
+ [2] = {0x5, 0x5, 0x5, 0x5, 0x1, 0x3f},
+ [3] = {0x5, 0x5, 0x5, 0x5, 0x1, 0x3f},
+ [4] = {0x28, 0x19, 0xb, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1},
+ [5] = {0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x16},
+ [6] = {},
+ [7] = {0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x4, 0x1,
+ 0x4, 0x1, 0xa, 0x6, 0x1, 0xa, 0x6, 0x1, 0x1, 0x1, 0x1, 0x5,
+ 0x3, 0x3, 0x4},
+ [8] = {0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x4, 0x1,
+ 0x4, 0x1, 0xa, 0x6, 0x1, 0xa, 0x6, 0x1, 0x1, 0x1, 0x1, 0x5,
+ 0x3, 0x3, 0x4},
+ [9] = {0x9, 0x7, 0xf, 0x8, 0x1, 0x8, 0x9, 0x3, 0x3, 0x6, 0x7, 0x4,
+ 0x9, 0x3, 0x4, 0xe, 0x1, 0x7, 0x8, 0x7, 0x7, 0x1, 0x6, 0x2,
+ 0xf, 0x8, 0x1, 0x1, 0x1},
+ [10] = {},
+ [11] = {0x9, 0x7, 0xf, 0x8, 0x1, 0x8, 0x9, 0x3, 0x3, 0x6, 0x7, 0x4,
+ 0x9, 0x3, 0x4, 0xe, 0x1, 0x7, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x1, 0x1, 0x1, 0x1, 0x1},
+ [12] = {},
+ [13] = {0x2, 0xc, 0xc, 0xe, 0x6, 0x6, 0x6, 0x6, 0x6, 0x12, 0x6, 0x1},
+ [14] = {0x2, 0xc, 0xc, 0x28, 0x12, 0x6},
+ [15] = {0x28, 0x1, 0x2, 0x28, 0x1},
+ [16] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x2, 0x14, 0x14, 0x4, 0x4, 0x4, 0x2,
+ 0x4, 0x2, 0x8, 0x4, 0x4},
+ [17] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x2, 0x14, 0x14, 0x4, 0x4, 0x4, 0x2,
+ 0x4, 0x2, 0x8, 0x4, 0x4},
+ [18] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x2, 0x14, 0x14, 0x4, 0x4, 0x4, 0x2,
+ 0x4, 0x2, 0x8, 0x4, 0x4},
+ [19] = {0x2, 0x2, 0x4, 0x2},
+ [20] = {0x9, 0x9, 0x5, 0x5, 0x1, 0x1},
+};
+
+static const u8 mtk_smi_larb_mt8186_ostd[][SMI_LARB_PORT_NR_MAX] = {
+ [0] = {0x2, 0x1, 0x8, 0x1,},
+ [1] = {0x1, 0x3, 0x1, 0x1,},
+ [2] = {0x6, 0x1, 0x4, 0x1,},
+ [3] = {},
+ [4] = {0xf, 0x1, 0x5, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x1, 0x1, 0x1,},
+ [5] = {},
+ [6] = {},
+ [7] = {0x1, 0x3, 0x1, 0x1, 0x1, 0x3, 0x2, 0xd, 0x7, 0x5, 0x3,
+ 0x1, 0x5,},
+ [8] = {0x1, 0x2, 0x2,},
+ [9] = {0x9, 0x7, 0xf, 0x8, 0x1, 0x8, 0x9, 0x3, 0x3, 0xb, 0x7, 0x4,
+ 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x1, 0x1, 0x1, 0x1, 0x1,},
+ [10] = {},
+ [11] = {0x9, 0x7, 0xf, 0x8, 0x1, 0x8, 0x9, 0x3, 0x3, 0xb, 0x7, 0x4,
+ 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x7, 0x7, 0x1, 0x6, 0x2,
+ 0xf, 0x8, 0x1, 0x1, 0x1,},
+ [12] = {},
+ [13] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x6, 0x6, 0x6, 0x1, 0x1, 0x1,},
+ [14] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1,},
+ [15] = {},
+ [16] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x1, 0x14, 0x1, 0x4, 0x4, 0x4,
+ 0x2, 0x4, 0x2, 0x8, 0x4, 0x4,},
+ [17] = {0x28, 0x14, 0x2, 0xc, 0x18, 0x1, 0x14, 0x1, 0x4, 0x4, 0x4,
+ 0x2, 0x4, 0x2, 0x8, 0x4, 0x4,},
+ [18] = {},
+ [19] = {0x1, 0x1, 0x1, 0x1,},
+ [20] = {0x2, 0x2, 0x2, 0x2, 0x1, 0x1,},
+};
+
static const u8 mtk_smi_larb_mt8188_ostd[][SMI_LARB_PORT_NR_MAX] = {
[0] = {0x02, 0x18, 0x22, 0x22, 0x01, 0x02, 0x0a,},
[1] = {0x12, 0x02, 0x14, 0x14, 0x01, 0x18, 0x0a,},
@@ -429,6 +498,12 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt6779 = {
/* DUMMY | IPU0 | IPU1 | CCU | MDLA */
};
+static const struct mtk_smi_larb_gen mtk_smi_larb_mt6893 = {
+ .config_port = mtk_smi_larb_config_port_gen2_general,
+ .flags_general = MTK_SMI_FLAG_THRT_UPDATE | MTK_SMI_FLAG_SW_FLAG,
+ .ostd = mtk_smi_larb_mt6893_ostd,
+};
+
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8167 = {
/* mt8167 do not need the port in larb */
.config_port = mtk_smi_larb_config_port_mt8167,
@@ -448,6 +523,7 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt8183 = {
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8186 = {
.config_port = mtk_smi_larb_config_port_gen2_general,
.flags_general = MTK_SMI_FLAG_SLEEP_CTL,
+ .ostd = mtk_smi_larb_mt8186_ostd,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8188 = {
@@ -474,6 +550,7 @@ static const struct of_device_id mtk_smi_larb_of_ids[] = {
{.compatible = "mediatek,mt2712-smi-larb", .data = &mtk_smi_larb_mt2712},
{.compatible = "mediatek,mt6779-smi-larb", .data = &mtk_smi_larb_mt6779},
{.compatible = "mediatek,mt6795-smi-larb", .data = &mtk_smi_larb_mt8173},
+ {.compatible = "mediatek,mt6893-smi-larb", .data = &mtk_smi_larb_mt6893},
{.compatible = "mediatek,mt8167-smi-larb", .data = &mtk_smi_larb_mt8167},
{.compatible = "mediatek,mt8173-smi-larb", .data = &mtk_smi_larb_mt8173},
{.compatible = "mediatek,mt8183-smi-larb", .data = &mtk_smi_larb_mt8183},
@@ -694,6 +771,13 @@ static const struct mtk_smi_common_plat mtk_smi_common_mt6795 = {
.init = mtk_smi_common_mt6795_init,
};
+static const struct mtk_smi_common_plat mtk_smi_common_mt6893 = {
+ .type = MTK_SMI_GEN2,
+ .has_gals = true,
+ .bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(4) |
+ F_MMU1_LARB(5) | F_MMU1_LARB(7),
+};
+
static const struct mtk_smi_common_plat mtk_smi_common_mt8183 = {
.type = MTK_SMI_GEN2,
.has_gals = true,
@@ -756,6 +840,7 @@ static const struct of_device_id mtk_smi_common_of_ids[] = {
{.compatible = "mediatek,mt2712-smi-common", .data = &mtk_smi_common_gen2},
{.compatible = "mediatek,mt6779-smi-common", .data = &mtk_smi_common_mt6779},
{.compatible = "mediatek,mt6795-smi-common", .data = &mtk_smi_common_mt6795},
+ {.compatible = "mediatek,mt6893-smi-common", .data = &mtk_smi_common_mt6893},
{.compatible = "mediatek,mt8167-smi-common", .data = &mtk_smi_common_gen2},
{.compatible = "mediatek,mt8173-smi-common", .data = &mtk_smi_common_gen2},
{.compatible = "mediatek,mt8183-smi-common", .data = &mtk_smi_common_mt8183},
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
index d5bf3243fe78..d9e13c1f9b13 100644
--- a/drivers/memory/omap-gpmc.c
+++ b/drivers/memory/omap-gpmc.c
@@ -1455,8 +1455,8 @@ static int gpmc_setup_irq(struct gpmc_device *gpmc)
gpmc->irq_chip.irq_unmask = gpmc_irq_unmask;
gpmc->irq_chip.irq_set_type = gpmc_irq_set_type;
- gpmc_irq_domain = irq_domain_create_linear(of_fwnode_handle(gpmc->dev->of_node),
- gpmc->nirqs, &gpmc_irq_domain_ops, gpmc);
+ gpmc_irq_domain = irq_domain_create_linear(dev_fwnode(gpmc->dev), gpmc->nirqs,
+ &gpmc_irq_domain_ops, gpmc);
if (!gpmc_irq_domain) {
dev_err(gpmc->dev, "IRQ domain add failed\n");
return -ENODEV;
@@ -2374,7 +2374,7 @@ static void gpmc_probe_dt_children(struct platform_device *pdev)
static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
- return 1; /* we're input only */
+ return GPIO_LINE_DIRECTION_IN; /* we're input only */
}
static int gpmc_gpio_direction_input(struct gpio_chip *chip,
@@ -2383,17 +2383,6 @@ static int gpmc_gpio_direction_input(struct gpio_chip *chip,
return 0; /* we're input only */
}
-static int gpmc_gpio_direction_output(struct gpio_chip *chip,
- unsigned int offset, int value)
-{
- return -EINVAL; /* we're input only */
-}
-
-static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
-{
-}
-
static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
u32 reg;
@@ -2415,8 +2404,6 @@ static int gpmc_gpio_init(struct gpmc_device *gpmc)
gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
- gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
- gpmc->gpio_chip.set = gpmc_gpio_set;
gpmc->gpio_chip.get = gpmc_gpio_get;
gpmc->gpio_chip.base = -1;
diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c
new file mode 100644
index 000000000000..bee2ecc8c2b9
--- /dev/null
+++ b/drivers/memory/stm32_omm.c
@@ -0,0 +1,470 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) STMicroelectronics 2025 - All Rights Reserved
+ * Author(s): Patrice Chotard <patrice.chotard@foss.st.com> for STMicroelectronics.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bus/stm32_firewall_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define OMM_CR 0
+#define CR_MUXEN BIT(0)
+#define CR_MUXENMODE_MASK GENMASK(1, 0)
+#define CR_CSSEL_OVR_EN BIT(4)
+#define CR_CSSEL_OVR_MASK GENMASK(6, 5)
+#define CR_REQ2ACK_MASK GENMASK(23, 16)
+
+#define OMM_CHILD_NB 2
+#define OMM_CLK_NB 3
+
+struct stm32_omm {
+ struct resource *mm_res;
+ struct clk_bulk_data clk_bulk[OMM_CLK_NB];
+ struct reset_control *child_reset[OMM_CHILD_NB];
+ void __iomem *io_base;
+ u32 cr;
+ u8 nb_child;
+ bool restore_omm;
+};
+
+static int stm32_omm_set_amcr(struct device *dev, bool set)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ resource_size_t mm_ospi2_size = 0;
+ static const char * const mm_name[] = { "ospi1", "ospi2" };
+ struct regmap *syscfg_regmap;
+ struct device_node *node;
+ struct resource res, res1;
+ unsigned int syscon_args[2];
+ int ret, idx;
+ unsigned int i, amcr, read_amcr;
+
+ for (i = 0; i < omm->nb_child; i++) {
+ idx = of_property_match_string(dev->of_node,
+ "memory-region-names",
+ mm_name[i]);
+ if (idx < 0)
+ continue;
+
+ /* res1 only used on second loop iteration */
+ res1.start = res.start;
+ res1.end = res.end;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", idx);
+ if (!node)
+ continue;
+
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret) {
+ of_node_put(node);
+ dev_err(dev, "unable to resolve memory region\n");
+ return ret;
+ }
+
+ /* check that memory region fits inside OMM memory map area */
+ if (!resource_contains(omm->mm_res, &res)) {
+ dev_err(dev, "%s doesn't fit inside OMM memory map area\n",
+ mm_name[i]);
+ dev_err(dev, "%pR doesn't fit inside %pR\n", &res, omm->mm_res);
+ of_node_put(node);
+
+ return -EFAULT;
+ }
+
+ if (i == 1) {
+ mm_ospi2_size = resource_size(&res);
+
+ /* check that OMM memory region 1 doesn't overlap memory region 2 */
+ if (resource_overlaps(&res, &res1)) {
+ dev_err(dev, "OMM memory-region %s overlaps memory region %s\n",
+ mm_name[0], mm_name[1]);
+ dev_err(dev, "%pR overlaps %pR\n", &res1, &res);
+ of_node_put(node);
+
+ return -EFAULT;
+ }
+ }
+ of_node_put(node);
+ }
+
+ syscfg_regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, "st,syscfg-amcr",
+ 2, syscon_args);
+ if (IS_ERR(syscfg_regmap))
+ return dev_err_probe(dev, PTR_ERR(syscfg_regmap),
+ "Failed to get st,syscfg-amcr property\n");
+
+ amcr = mm_ospi2_size / SZ_64M;
+
+ if (set)
+ regmap_update_bits(syscfg_regmap, syscon_args[0], syscon_args[1], amcr);
+
+ /* read AMCR and check coherency with memory-map areas defined in DT */
+ regmap_read(syscfg_regmap, syscon_args[0], &read_amcr);
+ read_amcr = read_amcr >> (ffs(syscon_args[1]) - 1);
+
+ if (amcr != read_amcr) {
+ dev_err(dev, "AMCR value not coherent with DT memory-map areas\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int stm32_omm_toggle_child_clock(struct device *dev, bool enable)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ int i, ret;
+
+ for (i = 0; i < omm->nb_child; i++) {
+ if (enable) {
+ ret = clk_prepare_enable(omm->clk_bulk[i + 1].clk);
+ if (ret) {
+ dev_err(dev, "Can not enable clock\n");
+ goto clk_error;
+ }
+ } else {
+ clk_disable_unprepare(omm->clk_bulk[i + 1].clk);
+ }
+ }
+
+ return 0;
+
+clk_error:
+ while (i--)
+ clk_disable_unprepare(omm->clk_bulk[i + 1].clk);
+
+ return ret;
+}
+
+static int stm32_omm_disable_child(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ struct reset_control *reset;
+ int ret;
+ u8 i;
+
+ ret = stm32_omm_toggle_child_clock(dev, true);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < omm->nb_child; i++) {
+ /* reset OSPI to ensure CR_EN bit is set to 0 */
+ reset = omm->child_reset[i];
+ ret = reset_control_acquire(reset);
+ if (ret) {
+ stm32_omm_toggle_child_clock(dev, false);
+ dev_err(dev, "Can not acquire reset %d\n", ret);
+ return ret;
+ }
+
+ reset_control_assert(reset);
+ udelay(2);
+ reset_control_deassert(reset);
+
+ reset_control_release(reset);
+ }
+
+ return stm32_omm_toggle_child_clock(dev, false);
+}
+
+static int stm32_omm_configure(struct device *dev)
+{
+ static const char * const clocks_name[] = {"omm", "ospi1", "ospi2"};
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ unsigned long clk_rate_max = 0;
+ u32 mux = 0;
+ u32 cssel_ovr = 0;
+ u32 req2ack = 0;
+ struct reset_control *rstc;
+ unsigned long clk_rate;
+ int ret;
+ u8 i;
+
+ for (i = 0; i < OMM_CLK_NB; i++)
+ omm->clk_bulk[i].id = clocks_name[i];
+
+ /* retrieve OMM, OSPI1 and OSPI2 clocks */
+ ret = devm_clk_bulk_get(dev, OMM_CLK_NB, omm->clk_bulk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get OMM/OSPI's clocks\n");
+
+ /* Ensure both OSPI instance are disabled before configuring OMM */
+ ret = stm32_omm_disable_child(dev);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ /* parse children's clock */
+ for (i = 1; i <= omm->nb_child; i++) {
+ clk_rate = clk_get_rate(omm->clk_bulk[i].clk);
+ if (!clk_rate) {
+ dev_err(dev, "Invalid clock rate\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (clk_rate > clk_rate_max)
+ clk_rate_max = clk_rate;
+ }
+
+ rstc = devm_reset_control_get_exclusive(dev, "omm");
+ if (IS_ERR(rstc)) {
+ ret = dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n");
+ goto error;
+ }
+
+ reset_control_assert(rstc);
+ udelay(2);
+ reset_control_deassert(rstc);
+
+ omm->cr = readl_relaxed(omm->io_base + OMM_CR);
+ /* optional */
+ ret = of_property_read_u32(dev->of_node, "st,omm-mux", &mux);
+ if (!ret) {
+ if (mux & CR_MUXEN) {
+ ret = of_property_read_u32(dev->of_node, "st,omm-req2ack-ns",
+ &req2ack);
+ if (!ret && !req2ack) {
+ req2ack = DIV_ROUND_UP(req2ack, NSEC_PER_SEC / clk_rate_max) - 1;
+
+ if (req2ack > 256)
+ req2ack = 256;
+ }
+
+ req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
+
+ omm->cr &= ~CR_REQ2ACK_MASK;
+ omm->cr |= FIELD_PREP(CR_REQ2ACK_MASK, req2ack);
+
+ /*
+ * If the mux is enabled, the 2 OSPI clocks have to be
+ * always enabled
+ */
+ ret = stm32_omm_toggle_child_clock(dev, true);
+ if (ret)
+ goto error;
+ }
+
+ omm->cr &= ~CR_MUXENMODE_MASK;
+ omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux);
+ }
+
+ /* optional */
+ ret = of_property_read_u32(dev->of_node, "st,omm-cssel-ovr", &cssel_ovr);
+ if (!ret) {
+ omm->cr &= ~CR_CSSEL_OVR_MASK;
+ omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr);
+ omm->cr |= CR_CSSEL_OVR_EN;
+ }
+
+ omm->restore_omm = true;
+ writel_relaxed(omm->cr, omm->io_base + OMM_CR);
+
+ ret = stm32_omm_set_amcr(dev, true);
+
+error:
+ pm_runtime_put_sync_suspend(dev);
+
+ return ret;
+}
+
+static int stm32_omm_check_access(struct device_node *np)
+{
+ struct stm32_firewall firewall;
+ int ret;
+
+ ret = stm32_firewall_get_firewall(np, &firewall, 1);
+ if (ret)
+ return ret;
+
+ return stm32_firewall_grant_access(&firewall);
+}
+
+static int stm32_omm_probe(struct platform_device *pdev)
+{
+ static const char * const resets_name[] = {"ospi1", "ospi2"};
+ struct device *dev = &pdev->dev;
+ u8 child_access_granted = 0;
+ struct stm32_omm *omm;
+ int i, ret;
+
+ omm = devm_kzalloc(dev, sizeof(*omm), GFP_KERNEL);
+ if (!omm)
+ return -ENOMEM;
+
+ omm->io_base = devm_platform_ioremap_resource_byname(pdev, "regs");
+ if (IS_ERR(omm->io_base))
+ return PTR_ERR(omm->io_base);
+
+ omm->mm_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory_map");
+ if (!omm->mm_res)
+ return -ENODEV;
+
+ /* check child's access */
+ for_each_child_of_node_scoped(dev->of_node, child) {
+ if (omm->nb_child >= OMM_CHILD_NB) {
+ dev_err(dev, "Bad DT, found too much children\n");
+ return -E2BIG;
+ }
+
+ ret = stm32_omm_check_access(child);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ if (!ret)
+ child_access_granted++;
+
+ omm->nb_child++;
+ }
+
+ if (omm->nb_child != OMM_CHILD_NB)
+ return -EINVAL;
+
+ platform_set_drvdata(pdev, omm);
+
+ devm_pm_runtime_enable(dev);
+
+ /* check if OMM's resource access is granted */
+ ret = stm32_omm_check_access(dev->of_node);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ for (i = 0; i < omm->nb_child; i++) {
+ omm->child_reset[i] = devm_reset_control_get_exclusive_released(dev,
+ resets_name[i]);
+
+ if (IS_ERR(omm->child_reset[i]))
+ return dev_err_probe(dev, PTR_ERR(omm->child_reset[i]),
+ "Can't get %s reset\n", resets_name[i]);
+ }
+
+ if (!ret && child_access_granted == OMM_CHILD_NB) {
+ ret = stm32_omm_configure(dev);
+ if (ret)
+ return ret;
+ } else {
+ dev_dbg(dev, "Octo Memory Manager resource's access not granted\n");
+ /*
+ * AMCR can't be set, so check if current value is coherent
+ * with memory-map areas defined in DT
+ */
+ ret = stm32_omm_set_amcr(dev, false);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_of_platform_populate(dev);
+ if (ret) {
+ if (omm->cr & CR_MUXEN)
+ stm32_omm_toggle_child_clock(&pdev->dev, false);
+
+ return dev_err_probe(dev, ret, "Failed to create Octo Memory Manager child\n");
+ }
+
+ return 0;
+}
+
+static void stm32_omm_remove(struct platform_device *pdev)
+{
+ struct stm32_omm *omm = platform_get_drvdata(pdev);
+
+ if (omm->cr & CR_MUXEN)
+ stm32_omm_toggle_child_clock(&pdev->dev, false);
+}
+
+static const struct of_device_id stm32_omm_of_match[] = {
+ { .compatible = "st,stm32mp25-omm", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, stm32_omm_of_match);
+
+static int __maybe_unused stm32_omm_runtime_suspend(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(omm->clk_bulk[0].clk);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_omm_runtime_resume(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(omm->clk_bulk[0].clk);
+}
+
+static int __maybe_unused stm32_omm_suspend(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+
+ if (omm->restore_omm && omm->cr & CR_MUXEN)
+ stm32_omm_toggle_child_clock(dev, false);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int __maybe_unused stm32_omm_resume(struct device *dev)
+{
+ struct stm32_omm *omm = dev_get_drvdata(dev);
+ int ret;
+
+ pinctrl_pm_select_default_state(dev);
+
+ if (!omm->restore_omm)
+ return 0;
+
+ /* Ensure both OSPI instance are disabled before configuring OMM */
+ ret = stm32_omm_disable_child(dev);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ writel_relaxed(omm->cr, omm->io_base + OMM_CR);
+ ret = stm32_omm_set_amcr(dev, true);
+ pm_runtime_put_sync_suspend(dev);
+ if (ret)
+ return ret;
+
+ if (omm->cr & CR_MUXEN)
+ ret = stm32_omm_toggle_child_clock(dev, true);
+
+ return ret;
+}
+
+static const struct dev_pm_ops stm32_omm_pm_ops = {
+ SET_RUNTIME_PM_OPS(stm32_omm_runtime_suspend,
+ stm32_omm_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_omm_suspend, stm32_omm_resume)
+};
+
+static struct platform_driver stm32_omm_driver = {
+ .probe = stm32_omm_probe,
+ .remove = stm32_omm_remove,
+ .driver = {
+ .name = "stm32-omm",
+ .of_match_table = stm32_omm_of_match,
+ .pm = &stm32_omm_pm_ops,
+ },
+};
+module_platform_driver(stm32_omm_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics Octo Memory Manager driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig
index 3fe83d7c2bf8..fc5a27791826 100644
--- a/drivers/memory/tegra/Kconfig
+++ b/drivers/memory/tegra/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config TEGRA_MC
bool "NVIDIA Tegra Memory Controller support"
- default y
+ default ARCH_TEGRA
depends on ARCH_TEGRA || (COMPILE_TEST && COMMON_CLK)
select INTERCONNECT
help
@@ -12,7 +12,7 @@ if TEGRA_MC
config TEGRA20_EMC
tristate "NVIDIA Tegra20 External Memory Controller driver"
- default y
+ default ARCH_TEGRA_2x_SOC
depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select PM_DEVFREQ
@@ -25,7 +25,7 @@ config TEGRA20_EMC
config TEGRA30_EMC
tristate "NVIDIA Tegra30 External Memory Controller driver"
- default y
+ default ARCH_TEGRA_3x_SOC
depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST
select PM_OPP
select DDR
@@ -37,7 +37,7 @@ config TEGRA30_EMC
config TEGRA124_EMC
tristate "NVIDIA Tegra124 External Memory Controller driver"
- default y
+ default ARCH_TEGRA_124_SOC
depends on ARCH_TEGRA_124_SOC || COMPILE_TEST
select TEGRA124_CLK_EMC if ARCH_TEGRA
select PM_OPP
diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile
index 0750847dac3c..6334601e6120 100644
--- a/drivers/memory/tegra/Makefile
+++ b/drivers/memory/tegra/Makefile
@@ -10,6 +10,7 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
tegra-mc-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
tegra-mc-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra194.o
tegra-mc-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186.o tegra234.o
+tegra-mc-$(CONFIG_ARCH_TEGRA_264_SOC) += tegra186.o tegra264.o
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
@@ -21,5 +22,6 @@ obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-emc.o
obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186-emc.o
obj-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186-emc.o
+obj-$(CONFIG_ARCH_TEGRA_264_SOC) += tegra186-emc.o
tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index bd5b58f1fd42..6edb210287dc 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 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2014-2025 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/clk.h>
@@ -49,6 +49,9 @@ static const struct of_device_id tegra_mc_of_match[] = {
#ifdef CONFIG_ARCH_TEGRA_234_SOC
{ .compatible = "nvidia,tegra234-mc", .data = &tegra234_mc_soc },
#endif
+#ifdef CONFIG_ARCH_TEGRA_264_SOC
+ { .compatible = "nvidia,tegra264-mc", .data = &tegra264_mc_soc },
+#endif
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h
index c3f6655bec60..1d97cf4d3a94 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 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2014-2025 NVIDIA CORPORATION. All rights reserved.
*/
#ifndef MEMORY_TEGRA_MC_H
@@ -182,6 +182,10 @@ extern const struct tegra_mc_soc tegra194_mc_soc;
extern const struct tegra_mc_soc tegra234_mc_soc;
#endif
+#ifdef CONFIG_ARCH_TEGRA_264_SOC
+extern const struct tegra_mc_soc tegra264_mc_soc;
+#endif
+
#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \
defined(CONFIG_ARCH_TEGRA_114_SOC) || \
defined(CONFIG_ARCH_TEGRA_124_SOC) || \
@@ -193,7 +197,8 @@ extern const struct tegra_mc_ops tegra30_mc_ops;
#if defined(CONFIG_ARCH_TEGRA_186_SOC) || \
defined(CONFIG_ARCH_TEGRA_194_SOC) || \
- defined(CONFIG_ARCH_TEGRA_234_SOC)
+ defined(CONFIG_ARCH_TEGRA_234_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_264_SOC)
extern const struct tegra_mc_ops tegra186_mc_ops;
#endif
diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c
index bc807d7fcd4e..d6cd90c7ad53 100644
--- a/drivers/memory/tegra/tegra186-emc.c
+++ b/drivers/memory/tegra/tegra186-emc.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2019 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2019-2025 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/clk.h>
@@ -394,6 +394,9 @@ static const struct of_device_id tegra186_emc_of_match[] = {
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
{ .compatible = "nvidia,tegra234-emc" },
#endif
+#if defined(CONFIG_ARCH_TEGRA_264_SOC)
+ { .compatible = "nvidia,tegra264-emc" },
+#endif
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra186_emc_of_match);
diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c
index 1b3183951bfe..aee11457bf8e 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-2021 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2017-2025 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/io.h>
@@ -26,11 +26,24 @@
static int tegra186_mc_probe(struct tegra_mc *mc)
{
struct platform_device *pdev = to_platform_device(mc->dev);
+ struct resource *res;
unsigned int i;
char name[8];
int err;
- mc->bcast_ch_regs = devm_platform_ioremap_resource_byname(pdev, "broadcast");
+ /*
+ * From Tegra264, the SID region is not present in MC node and BROADCAST is first.
+ * The common function 'tegra_mc_probe()' already maps first region entry from DT.
+ * Check if the SID region is present in DT then map BROADCAST. Otherwise, consider
+ * the first entry mapped in mc probe as the BROADCAST region. This is done to avoid
+ * mapping the region twice when SID is not present and keep backward compatibility.
+ */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sid");
+ if (res)
+ mc->bcast_ch_regs = devm_platform_ioremap_resource_byname(pdev, "broadcast");
+ else
+ mc->bcast_ch_regs = mc->regs;
+
if (IS_ERR(mc->bcast_ch_regs)) {
if (PTR_ERR(mc->bcast_ch_regs) == -EINVAL) {
dev_warn(&pdev->dev,
diff --git a/drivers/memory/tegra/tegra210-emc-core.c b/drivers/memory/tegra/tegra210-emc-core.c
index e63f62690571..e96ca4157d48 100644
--- a/drivers/memory/tegra/tegra210-emc-core.c
+++ b/drivers/memory/tegra/tegra210-emc-core.c
@@ -558,7 +558,7 @@ tegra210_emc_table_register_offsets = {
static void tegra210_emc_train(struct timer_list *timer)
{
- struct tegra210_emc *emc = from_timer(emc, timer, training);
+ struct tegra210_emc *emc = timer_container_of(emc, timer, training);
unsigned long flags;
if (!emc->last)
@@ -614,7 +614,8 @@ static unsigned int tegra210_emc_get_temperature(struct tegra210_emc *emc)
static void tegra210_emc_poll_refresh(struct timer_list *timer)
{
- struct tegra210_emc *emc = from_timer(emc, timer, refresh_timer);
+ struct tegra210_emc *emc = timer_container_of(emc, timer,
+ refresh_timer);
unsigned int temperature;
if (!emc->debugfs.temperature)
diff --git a/drivers/memory/tegra/tegra264-bwmgr.h b/drivers/memory/tegra/tegra264-bwmgr.h
new file mode 100644
index 000000000000..93bfceaac9c8
--- /dev/null
+++ b/drivers/memory/tegra/tegra264-bwmgr.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2025 NVIDIA CORPORATION. All rights reserved. */
+
+#ifndef MEMORY_TEGRA_TEGRA264_BWMGR_H
+#define MEMORY_TEGRA_TEGRA264_BWMGR_H
+
+#define TEGRA264_BWMGR_ICC_PRIMARY 1
+#define TEGRA264_BWMGR_DEBUG 2
+#define TEGRA264_BWMGR_CPU_CLUSTER0 3
+#define TEGRA264_BWMGR_CPU_CLUSTER1 4
+#define TEGRA264_BWMGR_CPU_CLUSTER2 5
+#define TEGRA264_BWMGR_CPU_CLUSTER3 6
+#define TEGRA264_BWMGR_CPU_CLUSTER4 7
+#define TEGRA264_BWMGR_CPU_CLUSTER5 8
+#define TEGRA264_BWMGR_CPU_CLUSTER6 9
+#define TEGRA264_BWMGR_CACTMON 10
+#define TEGRA264_BWMGR_DISPLAY 11
+#define TEGRA264_BWMGR_VI 12
+#define TEGRA264_BWMGR_APE 13
+#define TEGRA264_BWMGR_VIFAL 14
+#define TEGRA264_BWMGR_GPU 15
+#define TEGRA264_BWMGR_EQOS 16
+#define TEGRA264_BWMGR_PCIE_0 17
+#define TEGRA264_BWMGR_PCIE_1 18
+#define TEGRA264_BWMGR_PCIE_2 19
+#define TEGRA264_BWMGR_PCIE_3 20
+#define TEGRA264_BWMGR_PCIE_4 21
+#define TEGRA264_BWMGR_PCIE_5 22
+#define TEGRA264_BWMGR_SDMMC_1 23
+#define TEGRA264_BWMGR_SDMMC_2 24
+#define TEGRA264_BWMGR_NVDEC 25
+#define TEGRA264_BWMGR_NVENC 26
+#define TEGRA264_BWMGR_NVJPG_0 27
+#define TEGRA264_BWMGR_NVJPG_1 28
+#define TEGRA264_BWMGR_OFAA 29
+#define TEGRA264_BWMGR_XUSB_HOST 30
+#define TEGRA264_BWMGR_XUSB_DEV 31
+#define TEGRA264_BWMGR_TSEC 32
+#define TEGRA264_BWMGR_VIC 33
+#define TEGRA264_BWMGR_APEDMA 34
+#define TEGRA264_BWMGR_SE 35
+#define TEGRA264_BWMGR_ISP 36
+#define TEGRA264_BWMGR_HDA 37
+#define TEGRA264_BWMGR_VI2FAL 38
+#define TEGRA264_BWMGR_VI2 39
+#define TEGRA264_BWMGR_RCE 40
+#define TEGRA264_BWMGR_PVA 41
+#define TEGRA264_BWMGR_NVPMODEL 42
+
+#endif
diff --git a/drivers/memory/tegra/tegra264.c b/drivers/memory/tegra/tegra264.c
new file mode 100644
index 000000000000..5203e6c11372
--- /dev/null
+++ b/drivers/memory/tegra/tegra264.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <dt-bindings/memory/nvidia,tegra264.h>
+
+#include <linux/interconnect.h>
+#include <linux/of_device.h>
+#include <linux/tegra-icc.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/mc.h>
+
+#include "mc.h"
+#include "tegra264-bwmgr.h"
+
+/*
+ * MC Client entries are sorted in the increasing order of the
+ * override and security register offsets.
+ */
+static const struct tegra_mc_client tegra264_mc_clients[] = {
+ {
+ .id = TEGRA264_MEMORY_CLIENT_HDAR,
+ .name = "hdar",
+ .bpmp_id = TEGRA264_BWMGR_HDA,
+ .type = TEGRA_ICC_ISO_AUDIO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_HDAW,
+ .name = "hdaw",
+ .bpmp_id = TEGRA264_BWMGR_HDA,
+ .type = TEGRA_ICC_ISO_AUDIO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_MGBE0R,
+ .name = "mgbe0r",
+ .bpmp_id = TEGRA264_BWMGR_EQOS,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_MGBE0W,
+ .name = "mgbe0w",
+ .bpmp_id = TEGRA264_BWMGR_EQOS,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_MGBE1R,
+ .name = "mgbe1r",
+ .bpmp_id = TEGRA264_BWMGR_EQOS,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_MGBE1W,
+ .name = "mgbe1w",
+ .bpmp_id = TEGRA264_BWMGR_EQOS,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_SDMMC0R,
+ .name = "sdmmc0r",
+ .bpmp_id = TEGRA264_BWMGR_SDMMC_1,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_SDMMC0W,
+ .name = "sdmmc0w",
+ .bpmp_id = TEGRA264_BWMGR_SDMMC_1,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_VICR,
+ .name = "vicr",
+ .bpmp_id = TEGRA264_BWMGR_VIC,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_VICW,
+ .name = "vicw",
+ .bpmp_id = TEGRA264_BWMGR_VIC,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_APER,
+ .name = "aper",
+ .bpmp_id = TEGRA264_BWMGR_APE,
+ .type = TEGRA_ICC_ISO_AUDIO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_APEW,
+ .name = "apew",
+ .bpmp_id = TEGRA264_BWMGR_APE,
+ .type = TEGRA_ICC_ISO_AUDIO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_APEDMAR,
+ .name = "apedmar",
+ .bpmp_id = TEGRA264_BWMGR_APEDMA,
+ .type = TEGRA_ICC_ISO_AUDIO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_APEDMAW,
+ .name = "apedmaw",
+ .bpmp_id = TEGRA264_BWMGR_APEDMA,
+ .type = TEGRA_ICC_ISO_AUDIO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_VIFALCONR,
+ .name = "vifalconr",
+ .bpmp_id = TEGRA264_BWMGR_VIFAL,
+ .type = TEGRA_ICC_ISO_VIFAL,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_VIFALCONW,
+ .name = "vifalconw",
+ .bpmp_id = TEGRA264_BWMGR_VIFAL,
+ .type = TEGRA_ICC_ISO_VIFAL,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_RCER,
+ .name = "rcer",
+ .bpmp_id = TEGRA264_BWMGR_RCE,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_RCEW,
+ .name = "rcew",
+ .bpmp_id = TEGRA264_BWMGR_RCE,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE0W,
+ .name = "pcie0w",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_0,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE1R,
+ .name = "pcie1r",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_1,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE1W,
+ .name = "pcie1w",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_1,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE2AR,
+ .name = "pcie2ar",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_2,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE2AW,
+ .name = "pcie2aw",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_2,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE3R,
+ .name = "pcie3r",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_3,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE3W,
+ .name = "pcie3w",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_3,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE4R,
+ .name = "pcie4r",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_4,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE4W,
+ .name = "pcie4w",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_4,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE5R,
+ .name = "pcie5r",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_5,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_PCIE5W,
+ .name = "pcie5w",
+ .bpmp_id = TEGRA264_BWMGR_PCIE_5,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_GPUR02MC,
+ .name = "gpur02mc",
+ .bpmp_id = TEGRA264_BWMGR_GPU,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_GPUW02MC,
+ .name = "gpuw02mc",
+ .bpmp_id = TEGRA264_BWMGR_GPU,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_NVDECSRD2MC,
+ .name = "nvdecsrd2mc",
+ .bpmp_id = TEGRA264_BWMGR_NVDEC,
+ .type = TEGRA_ICC_NISO,
+ }, {
+ .id = TEGRA264_MEMORY_CLIENT_NVDECSWR2MC,
+ .name = "nvdecswr2mc",
+ .bpmp_id = TEGRA264_BWMGR_NVDEC,
+ .type = TEGRA_ICC_NISO,
+ },
+};
+
+/*
+ * tegra264_mc_icc_set() - Pass MC client info to the BPMP-FW
+ * @src: ICC node for Memory Controller's (MC) Client
+ * @dst: ICC node for Memory Controller (MC)
+ *
+ * Passing the current request info from the MC to the BPMP-FW where
+ * LA and PTSA registers are accessed and the final EMC freq is set
+ * based on client_id, type, latency and bandwidth.
+ * icc_set_bw() makes set_bw calls for both MC and EMC providers in
+ * sequence. Both the calls are protected by 'mutex_lock(&icc_lock)'.
+ * So, the data passed won't be updated by concurrent set calls from
+ * other clients.
+ */
+static int tegra264_mc_icc_set(struct icc_node *src, struct icc_node *dst)
+{
+ struct tegra_mc *mc = icc_provider_to_tegra_mc(dst->provider);
+ struct mrq_bwmgr_int_request bwmgr_req = { 0 };
+ struct mrq_bwmgr_int_response bwmgr_resp = { 0 };
+ const struct tegra_mc_client *pclient = src->data;
+ struct tegra_bpmp_message msg;
+ int ret;
+
+ /*
+ * Same Src and Dst node will happen during boot from icc_node_add().
+ * This can be used to pre-initialize and set bandwidth for all clients
+ * before their drivers are loaded. We are skipping this case as for us,
+ * the pre-initialization already happened in Bootloader(MB2) and BPMP-FW.
+ */
+ if (src->id == dst->id)
+ return 0;
+
+ if (!mc->bwmgr_mrq_supported)
+ return 0;
+
+ if (!mc->bpmp) {
+ dev_err(mc->dev, "BPMP reference NULL\n");
+ return -ENOENT;
+ }
+
+ if (pclient->type == TEGRA_ICC_NISO)
+ bwmgr_req.bwmgr_calc_set_req.niso_bw = src->avg_bw;
+ else
+ bwmgr_req.bwmgr_calc_set_req.iso_bw = src->avg_bw;
+
+ bwmgr_req.bwmgr_calc_set_req.client_id = pclient->bpmp_id;
+
+ bwmgr_req.cmd = CMD_BWMGR_INT_CALC_AND_SET;
+ bwmgr_req.bwmgr_calc_set_req.mc_floor = src->peak_bw;
+ bwmgr_req.bwmgr_calc_set_req.floor_unit = BWMGR_INT_UNIT_KBPS;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_BWMGR_INT;
+ msg.tx.data = &bwmgr_req;
+ msg.tx.size = sizeof(bwmgr_req);
+ msg.rx.data = &bwmgr_resp;
+ msg.rx.size = sizeof(bwmgr_resp);
+
+ ret = tegra_bpmp_transfer(mc->bpmp, &msg);
+ if (ret < 0) {
+ dev_err(mc->dev, "BPMP transfer failed: %d\n", ret);
+ goto error;
+ }
+ if (msg.rx.ret < 0) {
+ pr_err("failed to set bandwidth for %u: %d\n",
+ bwmgr_req.bwmgr_calc_set_req.client_id, msg.rx.ret);
+ ret = -EINVAL;
+ }
+
+error:
+ return ret;
+}
+
+static int tegra264_mc_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
+ u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
+{
+ struct icc_provider *p = node->provider;
+ struct tegra_mc *mc = icc_provider_to_tegra_mc(p);
+
+ if (!mc->bwmgr_mrq_supported)
+ return 0;
+
+ *agg_avg += avg_bw;
+ *agg_peak = max(*agg_peak, peak_bw);
+
+ return 0;
+}
+
+static int tegra264_mc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak)
+{
+ *avg = 0;
+ *peak = 0;
+
+ return 0;
+}
+
+static const struct tegra_mc_icc_ops tegra264_mc_icc_ops = {
+ .xlate = tegra_mc_icc_xlate,
+ .aggregate = tegra264_mc_icc_aggregate,
+ .get_bw = tegra264_mc_icc_get_init_bw,
+ .set = tegra264_mc_icc_set,
+};
+
+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,
+ .has_addr_hi_reg = true,
+ .ops = &tegra186_mc_ops,
+ .icc_ops = &tegra264_mc_icc_ops,
+ .ch_intmask = 0x0000ff00,
+ .global_intstatus_channel_shift = 8,
+ /*
+ * Additionally, there are lite carveouts but those are not currently
+ * supported.
+ */
+ .num_carveouts = 32,
+};