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 <dig...@gmail.com>
> ---
>  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

Reply via email to