Hi Dmitry, Thank you for updating the patches! On 6/9/20 16:13, Dmitry Osipenko wrote: > Now memory controller is a memory interconnection provider. This allows us > to use interconnect API in order to change memory configuration. > > Signed-off-by: Dmitry Osipenko <digetx@xxxxxxxxx> > --- > drivers/memory/tegra/Kconfig | 1 + > drivers/memory/tegra/mc.c | 114 +++++++++++++++++++++++++++++++++++ > drivers/memory/tegra/mc.h | 8 +++ > include/soc/tegra/mc.h | 3 + > 4 files changed, 126 insertions(+) > > diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig > index 5bf75b316a2f..7055fdef2c32 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 772aa021b5f6..7ef7ac9e103e 100644 > --- a/drivers/memory/tegra/mc.c > +++ b/drivers/memory/tegra/mc.c > @@ -594,6 +594,118 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data) > return IRQ_HANDLED; > } > > +static int tegra_mc_icc_set(struct icc_node *src, struct icc_node *dst) > +{ > + return 0; > +} > + > +static int tegra_mc_icc_aggregate(struct icc_node *node, > + u32 tag, u32 avg_bw, u32 peak_bw, > + u32 *agg_avg, u32 *agg_peak) > +{ > + *agg_avg = min((u64)avg_bw + (*agg_avg), (u64)U32_MAX); > + *agg_peak = max(*agg_peak, peak_bw); > + > + return 0; > +} > + > +/* > + * 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_onecell_data *data; > + struct icc_node *node; > + unsigned int num_nodes; > + unsigned int i; > + int err; > + > + /* older device-trees don't have interconnect properties */ > + if (!of_find_property(mc->dev->of_node, "#interconnect-cells", NULL)) > + return 0; > + > + num_nodes = mc->soc->num_clients; > + > + data = devm_kzalloc(mc->dev, struct_size(data, nodes, num_nodes), > + GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + mc->provider.dev = mc->dev; > + mc->provider.set = tegra_mc_icc_set; Hmm, maybe the core should not require a set() implementation and we can just make it optional instead. Then the dummy function would not be needed. > + mc->provider.data = data; > + mc->provider.xlate = of_icc_xlate_onecell; > + mc->provider.aggregate = tegra_mc_icc_aggregate; > + > + err = icc_provider_add(&mc->provider); > + if (err) > + goto err_msg; Nit: I am planning to re-organize some of the existing drivers to call icc_provider_add() after the topology is populated. Could you please move this after the nodes are created and linked. > + > + /* create Memory Controller node */ > + node = icc_node_create(TEGRA_ICC_MC); > + err = PTR_ERR_OR_ZERO(node); > + if (err) > + 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 < num_nodes; i++) { > + /* create MC client node */ > + node = icc_node_create(mc->soc->clients[i].id); > + err = PTR_ERR_OR_ZERO(node); > + if (err) > + 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; > + > + data->nodes[i] = node; > + } > + data->num_nodes = num_nodes; > + > + return 0; > + > +remove_nodes: > + icc_nodes_remove(&mc->provider); > +del_provider: > + icc_provider_del(&mc->provider); > +err_msg: > + dev_err(mc->dev, "failed to initialize ICC: %d\n", err); > + > + return err; > +} > + > static int tegra_mc_probe(struct platform_device *pdev) > { > struct resource *res; > @@ -702,6 +814,8 @@ static int tegra_mc_probe(struct platform_device *pdev) > } > } > > + tegra_mc_interconnect_setup(mc); > + > return 0; > } > > diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h > index afa3ba45c9e6..abeb6a2cc36a 100644 > --- a/drivers/memory/tegra/mc.h > +++ b/drivers/memory/tegra/mc.h > @@ -115,4 +115,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's ICC, the values are chosen > + * such that they don't conflict with the device-tree ICC node IDs. > + */ > +#define TEGRA_ICC_EMC 1000 > +#define TEGRA_ICC_EMEM 2000 > +#define TEGRA_ICC_MC 3000 > + > #endif /* MEMORY_TEGRA_MC_H */ > diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h > index 1238e35653d1..71de023f9f47 100644 > --- a/include/soc/tegra/mc.h > +++ b/include/soc/tegra/mc.h > @@ -7,6 +7,7 @@ > #define __SOC_TEGRA_MC_H__ > > #include <linux/err.h> > +#include <linux/interconnect-provider.h> > #include <linux/reset-controller.h> > #include <linux/types.h> > > @@ -178,6 +179,8 @@ struct tegra_mc { > > struct reset_controller_dev reset; > > + struct icc_provider provider; > + > spinlock_t lock; > }; The rest looks good to me! Thanks, Georgi