mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 03:35:32 -04:00
Merge tag 'memory-controller-drv-7.3' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into soc/drivers
Memory controller drivers for v7.3 1. Few improvements: STM32 OMM, Tegra MC and JZ4780-nemc. 2. Refactor Tegra MC interconnect code to better represent actual hardware in multi-socket NUMA cases. * tag 'memory-controller-drv-7.3' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl: memory: jz4780-nemc: Use dev_err_probe() for clock error memory: tegra: Guard against NULL mc_regs in IRQ handler memory: tegra: add multi-socket support to the memory interconnect memory: stm32_omm: initialize ret in stm32_omm_set_amcr Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
@@ -313,10 +313,9 @@ static int jz4780_nemc_probe(struct platform_device *pdev)
|
||||
writel(0, nemc->base + NEMC_NFCSR);
|
||||
|
||||
nemc->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(nemc->clk)) {
|
||||
dev_err(dev, "failed to get clock\n");
|
||||
return PTR_ERR(nemc->clk);
|
||||
}
|
||||
if (IS_ERR(nemc->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(nemc->clk),
|
||||
"failed to get clock\n");
|
||||
|
||||
ret = clk_prepare_enable(nemc->clk);
|
||||
if (ret) {
|
||||
|
||||
@@ -46,7 +46,7 @@ static int stm32_omm_set_amcr(struct device *dev, bool set)
|
||||
struct device_node *node;
|
||||
struct resource res, res1;
|
||||
unsigned int syscon_args[2];
|
||||
int ret, idx;
|
||||
int ret = 0, idx;
|
||||
unsigned int i, amcr, read_amcr;
|
||||
|
||||
for (i = 0; i < omm->nb_child; i++) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
@@ -22,6 +24,9 @@
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
static DEFINE_MUTEX(tegra_mc_debugfs_root_lock);
|
||||
static struct dentry *tegra_mc_debugfs_root;
|
||||
|
||||
static const struct of_device_id tegra_mc_of_match[] = {
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
{ .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc },
|
||||
@@ -598,6 +603,13 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (!mc->soc->regs) {
|
||||
dev_err_ratelimited(mc->dev,
|
||||
"MC error interrupt 0x%08lx with no error register map, Clearing.\n",
|
||||
status);
|
||||
goto clear;
|
||||
}
|
||||
|
||||
for_each_set_bit(bit, &status, 32) {
|
||||
const char *error = tegra_mc_status_names[bit] ?: "unknown";
|
||||
const char *client = "unknown", *desc;
|
||||
@@ -736,6 +748,7 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
|
||||
desc, perm);
|
||||
}
|
||||
|
||||
clear:
|
||||
/* clear interrupts */
|
||||
if (mc->soc->num_channels) {
|
||||
mc_ch_writel(mc, channel, status, MC_INTSTATUS);
|
||||
@@ -778,7 +791,7 @@ struct icc_node *tegra_mc_icc_xlate(const struct of_phandle_args *spec, void *da
|
||||
struct icc_node *node;
|
||||
|
||||
list_for_each_entry(node, &mc->provider.nodes, node_list) {
|
||||
if (node->id == spec->args[0])
|
||||
if (tegra_mc_client_id_from_node(node) == spec->args[0])
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -834,6 +847,7 @@ const struct tegra_mc_icc_ops tegra_mc_icc_ops = {
|
||||
*/
|
||||
static int tegra_mc_interconnect_setup(struct tegra_mc *mc)
|
||||
{
|
||||
int node_id = dev_to_node(mc->dev);
|
||||
struct icc_node *node;
|
||||
unsigned int i;
|
||||
int err;
|
||||
@@ -854,31 +868,40 @@ static int tegra_mc_interconnect_setup(struct tegra_mc *mc)
|
||||
icc_provider_init(&mc->provider);
|
||||
|
||||
/* create Memory Controller node */
|
||||
node = icc_node_create(TEGRA_ICC_MC);
|
||||
node = tegra_mc_icc_node_create(node_id, TEGRA_ICC_MC);
|
||||
if (IS_ERR(node))
|
||||
return PTR_ERR(node);
|
||||
|
||||
node->name = "Memory Controller";
|
||||
if (node_id == NUMA_NO_NODE)
|
||||
node->name = "Memory Controller";
|
||||
else
|
||||
node->name = dev_name(mc->dev);
|
||||
|
||||
icc_node_add(node, &mc->provider);
|
||||
|
||||
/* link Memory Controller to External Memory Controller */
|
||||
err = icc_link_create(node, TEGRA_ICC_EMC);
|
||||
err = tegra_mc_icc_link_create(node, node_id, TEGRA_ICC_EMC);
|
||||
if (err)
|
||||
goto remove_nodes;
|
||||
|
||||
for (i = 0; i < mc->soc->num_clients; i++) {
|
||||
/* create MC client node */
|
||||
node = icc_node_create(mc->soc->clients[i].id);
|
||||
node = tegra_mc_icc_node_create(node_id, mc->soc->clients[i].id);
|
||||
if (IS_ERR(node)) {
|
||||
err = PTR_ERR(node);
|
||||
goto remove_nodes;
|
||||
}
|
||||
|
||||
node->name = mc->soc->clients[i].name;
|
||||
if (node_id == NUMA_NO_NODE)
|
||||
node->name = mc->soc->clients[i].name;
|
||||
else
|
||||
node->name = devm_kasprintf(mc->dev, GFP_KERNEL, "%d-%s",
|
||||
node_id, mc->soc->clients[i].name);
|
||||
|
||||
icc_node_add(node, &mc->provider);
|
||||
|
||||
/* link Memory Client to Memory Controller */
|
||||
err = icc_link_create(node, TEGRA_ICC_MC);
|
||||
err = tegra_mc_icc_link_create(node, node_id, TEGRA_ICC_MC);
|
||||
if (err)
|
||||
goto remove_nodes;
|
||||
|
||||
@@ -957,7 +980,16 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(mc->regs))
|
||||
return PTR_ERR(mc->regs);
|
||||
|
||||
mc->debugfs.root = debugfs_create_dir("mc", NULL);
|
||||
scoped_guard(mutex, &tegra_mc_debugfs_root_lock) {
|
||||
if (!tegra_mc_debugfs_root)
|
||||
tegra_mc_debugfs_root = debugfs_create_dir("mc", NULL);
|
||||
|
||||
if (dev_to_node(mc->dev) == NUMA_NO_NODE)
|
||||
mc->debugfs.root = tegra_mc_debugfs_root;
|
||||
else
|
||||
mc->debugfs.root = debugfs_create_dir(dev_name(mc->dev),
|
||||
tegra_mc_debugfs_root);
|
||||
}
|
||||
|
||||
if (mc->soc->ops && mc->soc->ops->probe) {
|
||||
err = mc->soc->ops->probe(mc);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/numa.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <soc/tegra/mc.h>
|
||||
@@ -167,6 +168,42 @@ icc_provider_to_tegra_mc(struct icc_provider *provider)
|
||||
return container_of(provider, struct tegra_mc, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compose a globally-unique ICC node ID. On single-socket
|
||||
* systems (NUMA_NO_NODE), the SoC client ID is returned unchanged.
|
||||
* On multi-socket systems, the NUMA node ID is encoded in the
|
||||
* upper bits of the returned ID.
|
||||
*
|
||||
* The client ID field is sized to keep composed IDs below
|
||||
* ICC_DYN_ID_START (the start of the ICC core's dynamic-ID range).
|
||||
*/
|
||||
#define TEGRA_MC_CLIENT_ID_BITS 12
|
||||
#define TEGRA_MC_CLIENT_ID_MASK ((1U << TEGRA_MC_CLIENT_ID_BITS) - 1)
|
||||
|
||||
static inline u32 tegra_mc_get_client_id(int node_id, int id)
|
||||
{
|
||||
if (node_id == NUMA_NO_NODE)
|
||||
return id;
|
||||
|
||||
return ((node_id + 1) << TEGRA_MC_CLIENT_ID_BITS) | id;
|
||||
}
|
||||
|
||||
static inline struct icc_node *tegra_mc_icc_node_create(int node_id, int id)
|
||||
{
|
||||
return icc_node_create(tegra_mc_get_client_id(node_id, id));
|
||||
}
|
||||
|
||||
static inline int tegra_mc_icc_link_create(struct icc_node *node, int node_id, int id)
|
||||
{
|
||||
return icc_link_create(node, tegra_mc_get_client_id(node_id, id));
|
||||
}
|
||||
|
||||
/* Return the SoC client ID encoded in an ICC node ID. */
|
||||
static inline u32 tegra_mc_client_id_from_node(const struct icc_node *node)
|
||||
{
|
||||
return node->id & TEGRA_MC_CLIENT_ID_MASK;
|
||||
}
|
||||
|
||||
static inline u32 mc_ch_readl(const struct tegra_mc *mc, int ch,
|
||||
unsigned long offset)
|
||||
{
|
||||
|
||||
@@ -3,15 +3,21 @@
|
||||
* Copyright (C) 2019-2025 NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <soc/tegra/bpmp.h>
|
||||
#include "mc.h"
|
||||
|
||||
static DEFINE_MUTEX(tegra_emc_debugfs_root_lock);
|
||||
static struct dentry *tegra_emc_debugfs_root;
|
||||
|
||||
struct tegra186_emc_dvfs {
|
||||
unsigned long latency;
|
||||
unsigned long rate;
|
||||
@@ -206,7 +212,17 @@ static int tegra186_emc_get_emc_dvfs_latency(struct tegra186_emc *emc)
|
||||
return err;
|
||||
}
|
||||
|
||||
emc->debugfs.root = debugfs_create_dir("emc", NULL);
|
||||
scoped_guard(mutex, &tegra_emc_debugfs_root_lock) {
|
||||
if (!tegra_emc_debugfs_root)
|
||||
tegra_emc_debugfs_root = debugfs_create_dir("emc", NULL);
|
||||
|
||||
if (dev_to_node(emc->dev) == NUMA_NO_NODE)
|
||||
emc->debugfs.root = tegra_emc_debugfs_root;
|
||||
else
|
||||
emc->debugfs.root = debugfs_create_dir(dev_name(emc->dev),
|
||||
tegra_emc_debugfs_root);
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc,
|
||||
&tegra186_emc_debug_available_rates_fops);
|
||||
debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc,
|
||||
@@ -238,7 +254,7 @@ tegra186_emc_of_icc_xlate(const struct of_phandle_args *spec, void *data)
|
||||
|
||||
/* External Memory is the only possible ICC route */
|
||||
list_for_each_entry(node, &provider->nodes, node_list) {
|
||||
if (node->id != TEGRA_ICC_EMEM)
|
||||
if (tegra_mc_client_id_from_node(node) != TEGRA_ICC_EMEM)
|
||||
continue;
|
||||
|
||||
return node;
|
||||
@@ -257,6 +273,7 @@ static int tegra186_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *pe
|
||||
|
||||
static int tegra186_emc_interconnect_init(struct tegra186_emc *emc)
|
||||
{
|
||||
int node_id = dev_to_node(emc->dev->parent);
|
||||
struct icc_node *node;
|
||||
int err;
|
||||
|
||||
@@ -270,26 +287,36 @@ static int tegra186_emc_interconnect_init(struct tegra186_emc *emc)
|
||||
icc_provider_init(&emc->provider);
|
||||
|
||||
/* create External Memory Controller node */
|
||||
node = icc_node_create(TEGRA_ICC_EMC);
|
||||
if (IS_ERR(node))
|
||||
return PTR_ERR(node);
|
||||
|
||||
node->name = "External Memory Controller";
|
||||
icc_node_add(node, &emc->provider);
|
||||
|
||||
/* link External Memory Controller to External Memory (DRAM) */
|
||||
err = icc_link_create(node, TEGRA_ICC_EMEM);
|
||||
if (err)
|
||||
goto remove_nodes;
|
||||
|
||||
/* create External Memory node */
|
||||
node = icc_node_create(TEGRA_ICC_EMEM);
|
||||
node = tegra_mc_icc_node_create(node_id, TEGRA_ICC_EMC);
|
||||
if (IS_ERR(node)) {
|
||||
err = PTR_ERR(node);
|
||||
goto remove_nodes;
|
||||
}
|
||||
|
||||
node->name = "External Memory (DRAM)";
|
||||
if (node_id == NUMA_NO_NODE)
|
||||
node->name = "External Memory Controller";
|
||||
else
|
||||
node->name = dev_name(emc->dev);
|
||||
|
||||
icc_node_add(node, &emc->provider);
|
||||
|
||||
/* link External Memory Controller to External Memory (DRAM) */
|
||||
err = tegra_mc_icc_link_create(node, node_id, TEGRA_ICC_EMEM);
|
||||
if (err)
|
||||
goto remove_nodes;
|
||||
|
||||
/* create External Memory node */
|
||||
node = tegra_mc_icc_node_create(node_id, TEGRA_ICC_EMEM);
|
||||
if (IS_ERR(node)) {
|
||||
err = PTR_ERR(node);
|
||||
goto remove_nodes;
|
||||
}
|
||||
|
||||
if (node_id == NUMA_NO_NODE)
|
||||
node->name = "External Memory (DRAM)";
|
||||
else
|
||||
node->name = devm_kasprintf(emc->dev, GFP_KERNEL, "%d-dram", node_id);
|
||||
|
||||
icc_node_add(node, &emc->provider);
|
||||
|
||||
err = icc_provider_register(&emc->provider);
|
||||
@@ -382,6 +409,16 @@ static void tegra186_emc_remove(struct platform_device *pdev)
|
||||
|
||||
debugfs_remove_recursive(emc->debugfs.root);
|
||||
|
||||
scoped_guard(mutex, &tegra_emc_debugfs_root_lock) {
|
||||
if (emc->debugfs.root == tegra_emc_debugfs_root) {
|
||||
tegra_emc_debugfs_root = NULL;
|
||||
} else if (tegra_emc_debugfs_root &&
|
||||
simple_empty(tegra_emc_debugfs_root)) {
|
||||
debugfs_remove(tegra_emc_debugfs_root);
|
||||
tegra_emc_debugfs_root = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
mc->bpmp = NULL;
|
||||
tegra_bpmp_put(emc->bpmp);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user