mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 14:44:10 +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"
|
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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
|
|
||||||
#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;
|
|||||||
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 @@
|
|||||||
#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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
struct reset_controller_dev reset;
|
struct reset_controller_dev reset;
|
||||||
|
|
||||||
|
struct icc_provider provider;
|
||||||
|
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user