Commit 06f07981 authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Krzysztof Kozlowski
Browse files

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: default avatarDmitry Osipenko <digetx@gmail.com>
Tested-by: default avatarPeter Geis <pgwipeout@gmail.com>
Tested-by: default avatarNicolas Chauvet <kwizart@gmail.com>
Link: https://lore.kernel.org/r/20201104164923.21238-33-digetx@gmail.com


Signed-off-by: default avatarKrzysztof Kozlowski <krzk@kernel.org>
parent d5ecac0a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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.
+100 −0
Original line number Diff line number Diff line
@@ -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)) {
+22 −0
Original line number Diff line number Diff line
@@ -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 */
+17 −0
Original line number Diff line number Diff line
@@ -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;
};