mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 06:34:12 +08:00
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: Dmitry Osipenko <digetx@gmail.com> Tested-by: Peter Geis <pgwipeout@gmail.com> Tested-by: Nicolas Chauvet <kwizart@gmail.com> Link: https://lore.kernel.org/r/20201104164923.21238-33-digetx@gmail.com Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
This commit is contained in:
parent
d5ecac0afa
commit
06f079816d
@ -3,6 +3,7 @@ config TEGRA_MC
|
||||
bool "NVIDIA Tegra Memory Controller support"
|
||||
default y
|
||||
depends on ARCH_TEGRA
|
||||
select INTERCONNECT
|
||||
help
|
||||
This driver supports the Memory Controller (MC) hardware found on
|
||||
NVIDIA Tegra SoCs.
|
||||
|
@ -639,6 +639,101 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data)
|
||||
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)
|
||||
{
|
||||
struct resource *res;
|
||||
@ -727,6 +822,11 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "failed to register reset controller: %d\n",
|
||||
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) {
|
||||
mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc);
|
||||
if (IS_ERR(mc->smmu)) {
|
||||
|
@ -78,6 +78,20 @@
|
||||
|
||||
#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)
|
||||
{
|
||||
return readl_relaxed(mc->regs + offset);
|
||||
@ -115,4 +129,12 @@ extern const struct tegra_mc_soc tegra132_mc_soc;
|
||||
extern const struct tegra_mc_soc tegra210_mc_soc;
|
||||
#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 */
|
||||
|
@ -6,7 +6,9 @@
|
||||
#ifndef __SOC_TEGRA_MC_H__
|
||||
#define __SOC_TEGRA_MC_H__
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interconnect-provider.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
@ -141,6 +143,17 @@ struct tegra_mc_reset_ops {
|
||||
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 {
|
||||
const struct tegra_mc_client *clients;
|
||||
unsigned int num_clients;
|
||||
@ -160,6 +173,8 @@ struct tegra_mc_soc {
|
||||
const struct tegra_mc_reset_ops *reset_ops;
|
||||
const struct tegra_mc_reset *resets;
|
||||
unsigned int num_resets;
|
||||
|
||||
const struct tegra_mc_icc_ops *icc_ops;
|
||||
};
|
||||
|
||||
struct tegra_mc {
|
||||
@ -178,6 +193,8 @@ struct tegra_mc {
|
||||
|
||||
struct reset_controller_dev reset;
|
||||
|
||||
struct icc_provider provider;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user