Commit 06f07981 authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Krzysztof Kozlowski

memory: tegra-mc: Add interconnect framework

Add common SoC-agnostic ICC framework which turns Tegra Memory Controller
into a memory interconnection provider. This allows us to use interconnect
API for tuning of memory configurations.
Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
Tested-by: default avatarPeter Geis <pgwipeout@gmail.com>
Tested-by: default avatarNicolas Chauvet <kwizart@gmail.com>
Link: https://lore.kernel.org/r/20201104164923.21238-33-digetx@gmail.comSigned-off-by: default avatarKrzysztof Kozlowski <krzk@kernel.org>
parent d5ecac0a
...@@ -3,6 +3,7 @@ config TEGRA_MC ...@@ -3,6 +3,7 @@ config TEGRA_MC
bool "NVIDIA Tegra Memory Controller support" bool "NVIDIA Tegra Memory Controller support"
default y default y
depends on ARCH_TEGRA depends on ARCH_TEGRA
select INTERCONNECT
help help
This driver supports the Memory Controller (MC) hardware found on This driver supports the Memory Controller (MC) hardware found on
NVIDIA Tegra SoCs. NVIDIA Tegra SoCs.
......
...@@ -639,6 +639,101 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data) ...@@ -639,6 +639,101 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/*
* Memory Controller (MC) has few Memory Clients that are issuing memory
* bandwidth allocation requests to the MC interconnect provider. The MC
* provider aggregates the requests and then sends the aggregated request
* up to the External Memory Controller (EMC) interconnect provider which
* re-configures hardware interface to External Memory (EMEM) in accordance
* to the required bandwidth. Each MC interconnect node represents an
* individual Memory Client.
*
* Memory interconnect topology:
*
* +----+
* +--------+ | |
* | TEXSRD +--->+ |
* +--------+ | |
* | | +-----+ +------+
* ... | MC +--->+ EMC +--->+ EMEM |
* | | +-----+ +------+
* +--------+ | |
* | DISP.. +--->+ |
* +--------+ | |
* +----+
*/
static int tegra_mc_interconnect_setup(struct tegra_mc *mc)
{
struct icc_node *node;
unsigned int i;
int err;
/* older device-trees don't have interconnect properties */
if (!device_property_present(mc->dev, "#interconnect-cells") ||
!mc->soc->icc_ops)
return 0;
mc->provider.dev = mc->dev;
mc->provider.data = &mc->provider;
mc->provider.set = mc->soc->icc_ops->set;
mc->provider.aggregate = mc->soc->icc_ops->aggregate;
mc->provider.xlate_extended = mc->soc->icc_ops->xlate_extended;
err = icc_provider_add(&mc->provider);
if (err)
return err;
/* create Memory Controller node */
node = icc_node_create(TEGRA_ICC_MC);
if (IS_ERR(node)) {
err = PTR_ERR(node);
goto del_provider;
}
node->name = "Memory Controller";
icc_node_add(node, &mc->provider);
/* link Memory Controller to External Memory Controller */
err = icc_link_create(node, 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);
if (IS_ERR(node)) {
err = PTR_ERR(node);
goto remove_nodes;
}
node->name = 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);
if (err)
goto remove_nodes;
}
/*
* MC driver is registered too early, so early that generic driver
* syncing doesn't work for the MC. But it doesn't really matter
* since syncing works for the EMC drivers, hence we can sync the
* MC driver by ourselves and then EMC will complete syncing of
* the whole ICC state.
*/
icc_sync_state(mc->dev);
return 0;
remove_nodes:
icc_nodes_remove(&mc->provider);
del_provider:
icc_provider_del(&mc->provider);
return err;
}
static int tegra_mc_probe(struct platform_device *pdev) static int tegra_mc_probe(struct platform_device *pdev)
{ {
struct resource *res; struct resource *res;
...@@ -727,6 +822,11 @@ static int tegra_mc_probe(struct platform_device *pdev) ...@@ -727,6 +822,11 @@ static int tegra_mc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to register reset controller: %d\n", dev_err(&pdev->dev, "failed to register reset controller: %d\n",
err); err);
err = tegra_mc_interconnect_setup(mc);
if (err < 0)
dev_err(&pdev->dev, "failed to initialize interconnect: %d\n",
err);
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) { if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) {
mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc);
if (IS_ERR(mc->smmu)) { if (IS_ERR(mc->smmu)) {
......
...@@ -78,6 +78,20 @@ ...@@ -78,6 +78,20 @@
#define MC_TIMING_UPDATE BIT(0) #define MC_TIMING_UPDATE BIT(0)
static inline u32 tegra_mc_scale_percents(u64 val, unsigned int percents)
{
val = val * percents;
do_div(val, 100);
return min_t(u64, val, U32_MAX);
}
static inline struct tegra_mc *
icc_provider_to_tegra_mc(struct icc_provider *provider)
{
return container_of(provider, struct tegra_mc, provider);
}
static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
{ {
return readl_relaxed(mc->regs + offset); return readl_relaxed(mc->regs + offset);
...@@ -115,4 +129,12 @@ extern const struct tegra_mc_soc tegra132_mc_soc; ...@@ -115,4 +129,12 @@ extern const struct tegra_mc_soc tegra132_mc_soc;
extern const struct tegra_mc_soc tegra210_mc_soc; extern const struct tegra_mc_soc tegra210_mc_soc;
#endif #endif
/*
* These IDs are for internal use of Tegra ICC drivers. The ID numbers are
* chosen such that they don't conflict with the device-tree ICC node IDs.
*/
#define TEGRA_ICC_MC 1000
#define TEGRA_ICC_EMC 1001
#define TEGRA_ICC_EMEM 1002
#endif /* MEMORY_TEGRA_MC_H */ #endif /* MEMORY_TEGRA_MC_H */
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
#ifndef __SOC_TEGRA_MC_H__ #ifndef __SOC_TEGRA_MC_H__
#define __SOC_TEGRA_MC_H__ #define __SOC_TEGRA_MC_H__
#include <linux/bits.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/interconnect-provider.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -141,6 +143,17 @@ struct tegra_mc_reset_ops { ...@@ -141,6 +143,17 @@ struct tegra_mc_reset_ops {
const struct tegra_mc_reset *rst); const struct tegra_mc_reset *rst);
}; };
#define TEGRA_MC_ICC_TAG_DEFAULT 0
#define TEGRA_MC_ICC_TAG_ISO BIT(0)
struct tegra_mc_icc_ops {
int (*set)(struct icc_node *src, struct icc_node *dst);
int (*aggregate)(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak);
struct icc_node_data *(*xlate_extended)(struct of_phandle_args *spec,
void *data);
};
struct tegra_mc_soc { struct tegra_mc_soc {
const struct tegra_mc_client *clients; const struct tegra_mc_client *clients;
unsigned int num_clients; unsigned int num_clients;
...@@ -160,6 +173,8 @@ struct tegra_mc_soc { ...@@ -160,6 +173,8 @@ struct tegra_mc_soc {
const struct tegra_mc_reset_ops *reset_ops; const struct tegra_mc_reset_ops *reset_ops;
const struct tegra_mc_reset *resets; const struct tegra_mc_reset *resets;
unsigned int num_resets; unsigned int num_resets;
const struct tegra_mc_icc_ops *icc_ops;
}; };
struct tegra_mc { struct tegra_mc {
...@@ -178,6 +193,8 @@ struct tegra_mc { ...@@ -178,6 +193,8 @@ struct tegra_mc {
struct reset_controller_dev reset; struct reset_controller_dev reset;
struct icc_provider provider;
spinlock_t lock; spinlock_t lock;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment