linux/drivers/memory/tegra/mc.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2014 NVIDIA CORPORATION.  All rights reserved.
 */

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/tegra-icc.h>

#include <soc/tegra/fuse.h>

#include "mc.h"

static const struct of_device_id tegra_mc_of_match[] =;
MODULE_DEVICE_TABLE(of, tegra_mc_of_match);

static void tegra_mc_devm_action_put_device(void *data)
{}

/**
 * devm_tegra_memory_controller_get() - get Tegra Memory Controller handle
 * @dev: device pointer for the consumer device
 *
 * This function will search for the Memory Controller node in a device-tree
 * and retrieve the Memory Controller handle.
 *
 * Return: ERR_PTR() on error or a valid pointer to a struct tegra_mc.
 */
struct tegra_mc *devm_tegra_memory_controller_get(struct device *dev)
{}
EXPORT_SYMBOL_GPL();

int tegra_mc_probe_device(struct tegra_mc *mc, struct device *dev)
{}
EXPORT_SYMBOL_GPL();

int tegra_mc_get_carveout_info(struct tegra_mc *mc, unsigned int id,
                               phys_addr_t *base, u64 *size)
{}
EXPORT_SYMBOL_GPL();

static int tegra_mc_block_dma_common(struct tegra_mc *mc,
				     const struct tegra_mc_reset *rst)
{}

static bool tegra_mc_dma_idling_common(struct tegra_mc *mc,
				       const struct tegra_mc_reset *rst)
{}

static int tegra_mc_unblock_dma_common(struct tegra_mc *mc,
				       const struct tegra_mc_reset *rst)
{}

static int tegra_mc_reset_status_common(struct tegra_mc *mc,
					const struct tegra_mc_reset *rst)
{}

const struct tegra_mc_reset_ops tegra_mc_reset_ops_common =;

static inline struct tegra_mc *reset_to_mc(struct reset_controller_dev *rcdev)
{}

static const struct tegra_mc_reset *tegra_mc_reset_find(struct tegra_mc *mc,
							unsigned long id)
{}

static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev,
				    unsigned long id)
{}

static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev,
				      unsigned long id)
{}

static int tegra_mc_hotreset_status(struct reset_controller_dev *rcdev,
				    unsigned long id)
{}

static const struct reset_control_ops tegra_mc_reset_ops =;

static int tegra_mc_reset_setup(struct tegra_mc *mc)
{}

int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
{}
EXPORT_SYMBOL_GPL();

unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
{}
EXPORT_SYMBOL_GPL();

#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \
    defined(CONFIG_ARCH_TEGRA_114_SOC) || \
    defined(CONFIG_ARCH_TEGRA_124_SOC) || \
    defined(CONFIG_ARCH_TEGRA_132_SOC) || \
    defined(CONFIG_ARCH_TEGRA_210_SOC)
static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
{
	unsigned long long tick;
	unsigned int i;
	u32 value;

	/* compute the number of MC clock cycles per tick */
	tick = (unsigned long long)mc->tick * clk_get_rate(mc->clk);
	do_div(tick, NSEC_PER_SEC);

	value = mc_readl(mc, MC_EMEM_ARB_CFG);
	value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK;
	value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick);
	mc_writel(mc, value, MC_EMEM_ARB_CFG);

	/* write latency allowance defaults */
	for (i = 0; i < mc->soc->num_clients; i++) {
		const struct tegra_mc_client *client = &mc->soc->clients[i];
		u32 value;

		value = mc_readl(mc, client->regs.la.reg);
		value &= ~(client->regs.la.mask << client->regs.la.shift);
		value |= (client->regs.la.def & client->regs.la.mask) << client->regs.la.shift;
		mc_writel(mc, value, client->regs.la.reg);
	}

	/* latch new values */
	mc_writel(mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL);

	return 0;
}

static int load_one_timing(struct tegra_mc *mc,
			   struct tegra_mc_timing *timing,
			   struct device_node *node)
{
	int err;
	u32 tmp;

	err = of_property_read_u32(node, "clock-frequency", &tmp);
	if (err) {
		dev_err(mc->dev,
			"timing %pOFn: failed to read rate\n", node);
		return err;
	}

	timing->rate = tmp;
	timing->emem_data = devm_kcalloc(mc->dev, mc->soc->num_emem_regs,
					 sizeof(u32), GFP_KERNEL);
	if (!timing->emem_data)
		return -ENOMEM;

	err = of_property_read_u32_array(node, "nvidia,emem-configuration",
					 timing->emem_data,
					 mc->soc->num_emem_regs);
	if (err) {
		dev_err(mc->dev,
			"timing %pOFn: failed to read EMEM configuration\n",
			node);
		return err;
	}

	return 0;
}

static int load_timings(struct tegra_mc *mc, struct device_node *node)
{
	struct device_node *child;
	struct tegra_mc_timing *timing;
	int child_count = of_get_child_count(node);
	int i = 0, err;

	mc->timings = devm_kcalloc(mc->dev, child_count, sizeof(*timing),
				   GFP_KERNEL);
	if (!mc->timings)
		return -ENOMEM;

	mc->num_timings = child_count;

	for_each_child_of_node(node, child) {
		timing = &mc->timings[i++];

		err = load_one_timing(mc, timing, child);
		if (err) {
			of_node_put(child);
			return err;
		}
	}

	return 0;
}

static int tegra_mc_setup_timings(struct tegra_mc *mc)
{
	struct device_node *node;
	u32 ram_code, node_ram_code;
	int err;

	ram_code = tegra_read_ram_code();

	mc->num_timings = 0;

	for_each_child_of_node(mc->dev->of_node, node) {
		err = of_property_read_u32(node, "nvidia,ram-code",
					   &node_ram_code);
		if (err || (node_ram_code != ram_code))
			continue;

		err = load_timings(mc, node);
		of_node_put(node);
		if (err)
			return err;
		break;
	}

	if (mc->num_timings == 0)
		dev_warn(mc->dev,
			 "no memory timings for RAM code %u registered\n",
			 ram_code);

	return 0;
}

int tegra30_mc_probe(struct tegra_mc *mc)
{
	int err;

	mc->clk = devm_clk_get_optional(mc->dev, "mc");
	if (IS_ERR(mc->clk)) {
		dev_err(mc->dev, "failed to get MC clock: %ld\n", PTR_ERR(mc->clk));
		return PTR_ERR(mc->clk);
	}

	/* ensure that debug features are disabled */
	mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG);

	err = tegra_mc_setup_latency_allowance(mc);
	if (err < 0) {
		dev_err(mc->dev, "failed to setup latency allowance: %d\n", err);
		return err;
	}

	err = tegra_mc_setup_timings(mc);
	if (err < 0) {
		dev_err(mc->dev, "failed to setup timings: %d\n", err);
		return err;
	}

	return 0;
}

const struct tegra_mc_ops tegra30_mc_ops = {
	.probe = tegra30_mc_probe,
	.handle_irq = tegra30_mc_handle_irq,
};
#endif

static int mc_global_intstatus_to_channel(const struct tegra_mc *mc, u32 status,
					  unsigned int *mc_channel)
{}

static u32 mc_channel_to_global_intstatus(const struct tegra_mc *mc,
					  unsigned int channel)
{}

irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
{}

const char *const tegra_mc_status_names[32] =;

const char *const tegra_mc_error_names[8] =;

struct icc_node *tegra_mc_icc_xlate(const struct of_phandle_args *spec, void *data)
{}

static int tegra_mc_icc_get(struct icc_node *node, u32 *average, u32 *peak)
{}

static int tegra_mc_icc_set(struct icc_node *src, struct icc_node *dst)
{}

const struct tegra_mc_icc_ops tegra_mc_icc_ops =;

/*
 * 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)
{}

static void tegra_mc_num_channel_enabled(struct tegra_mc *mc)
{}

static int tegra_mc_probe(struct platform_device *pdev)
{}

static void tegra_mc_sync_state(struct device *dev)
{}

static struct platform_driver tegra_mc_driver =;

static int tegra_mc_init(void)
{}
arch_initcall(tegra_mc_init);

MODULE_AUTHOR();
MODULE_DESCRIPTION();