From 6c6bd2075f01f8c8ae4bc803ba5cd23a3d3db533 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 4 Nov 2020 19:49:04 +0300 Subject: memory: tegra: Add and use devm_tegra_memory_controller_get() Multiple Tegra drivers need to retrieve Memory Controller and there is duplication of the retrieval code among the drivers. Add new devm_tegra_memory_controller_get() helper to remove the code's duplication and to fix put_device() which was missed in the duplicated code. Make EMC drivers to use the new helper. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20201104164923.21238-29-digetx@gmail.com Signed-off-by: Krzysztof Kozlowski --- include/soc/tegra/mc.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include/soc') diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index 1238e35653d1..d9395af98143 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -184,4 +184,14 @@ struct tegra_mc { int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate); unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc); +#ifdef CONFIG_TEGRA_MC +struct tegra_mc *devm_tegra_memory_controller_get(struct device *dev); +#else +static inline struct tegra_mc * +devm_tegra_memory_controller_get(struct device *dev) +{ + ERR_PTR(-ENODEV); +} +#endif + #endif /* __SOC_TEGRA_MC_H__ */ -- cgit v1.2.3 From 06f079816d4c4e43d4128f394ae249934a32dffd Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 4 Nov 2020 19:49:08 +0300 Subject: 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 Tested-by: Peter Geis Tested-by: Nicolas Chauvet Link: https://lore.kernel.org/r/20201104164923.21238-33-digetx@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/Kconfig | 1 + drivers/memory/tegra/mc.c | 100 +++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 22 ++++++++++ include/soc/tegra/mc.h | 17 ++++++++ 4 files changed, 140 insertions(+) (limited to 'include/soc') diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig index 9f0a96bf9ccc..b38e5255effe 100644 --- a/drivers/memory/tegra/Kconfig +++ b/drivers/memory/tegra/Kconfig @@ -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. diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 998f9148ecb8..a7e6a8e4c95a 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -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)) { diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index afa3ba45c9e6..33e40d600592 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -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 */ diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index d9395af98143..43876216de34 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -6,7 +6,9 @@ #ifndef __SOC_TEGRA_MC_H__ #define __SOC_TEGRA_MC_H__ +#include #include +#include #include #include @@ -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; }; -- cgit v1.2.3 From 3a0b6b5abfddd0baf0a053aec7e8be59cf33b659 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 11 Nov 2020 04:14:31 +0300 Subject: memory: tegra: Correct stub of devm_tegra_memory_controller_get() Correct typo in a stub of devm_tegra_memory_controller_get() to fix a non-ARM kernel compile-testing. Reported-by: Stephen Rothwell Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20201111011456.7875-2-digetx@gmail.com Signed-off-by: Krzysztof Kozlowski --- include/soc/tegra/mc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/soc') diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index 43876216de34..d731407e23bb 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -207,7 +207,7 @@ struct tegra_mc *devm_tegra_memory_controller_get(struct device *dev); static inline struct tegra_mc * devm_tegra_memory_controller_get(struct device *dev) { - ERR_PTR(-ENODEV); + return ERR_PTR(-ENODEV); } #endif -- cgit v1.2.3