// 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_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <soc/tegra/fuse.h>
#include "mc.h"
static const struct of_device_id tegra_mc_of_match[] = {
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
{ .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
{ .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_114_SOC
{ .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_124_SOC
{ .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_132_SOC
{ .compatible = "nvidia,tegra132-mc", .data = &tegra132_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_210_SOC
{ .compatible = "nvidia,tegra210-mc", .data = &tegra210_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_186_SOC
{ .compatible = "nvidia,tegra186-mc", .data = &tegra186_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_194_SOC
{ .compatible = "nvidia,tegra194-mc", .data = &tegra194_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_234_SOC
{ .compatible = "nvidia,tegra234-mc", .data = &tegra234_mc_soc },
#endif
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
static void tegra_mc_devm_action_put_device(void *data)
{
struct tegra_mc *mc = data;
put_device(mc->dev);
}
/**
* 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)
{
struct platform_device *pdev;
struct device_node *np;
struct tegra_mc *mc;
int err;
np = of_parse_phandle(dev->of_node, "nvidia,memory-controller", 0);
if (!np)
return ERR_PTR(-ENOENT);
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev)
return ERR_PTR(-ENODEV);
mc = platform_get_drvdata(pdev);
if (!mc) {
put_device(&pdev->dev);
return ERR_PTR(-EPROBE_DEFER);
}
err = devm_add_action_or_reset(dev, tegra_mc_devm_action_put_device, mc);
if (err)
return ERR_PTR(err);
return mc;
}
EXPORT_SYMBOL_GPL(devm_tegra_memory_controller_get);
int tegra_mc_probe_device(struct tegra_mc *mc, struct device *dev)
{
if (mc->soc->ops && mc->soc->ops->probe_device)
return mc->soc->ops->probe_device(mc, dev);
return 0;
}
EXPORT_SYMBOL_GPL(tegra_mc_probe_device);
int tegra_mc_get_carveout_info(struct tegra_mc *mc, unsigned int id,
phys_addr_t *base, u64 *size)
{
u32 offset;
if (id < 1 || id >= mc->soc->num_carveouts)
return -EINVAL;
if (id < 6)
offset = 0xc0c + 0x50 * (id - 1);
else
offset = 0x2004 + 0x50 * (id - 6);
*base = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, offset + 0x0);
#ifdef CONFIG_PHYS_ADDR_T_64BIT
*base |= (phys_addr_t)mc_ch_readl(mc, MC_BROADCAST_CHANNEL, offset + 0x4) << 32;
#endif
if (size)
*size = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, offset + 0x8) << 17;
return 0;
}
EXPORT_SYMBOL_GPL(tegra_mc_get_carveout_info);
static int tegra_mc_block_dma_common(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->control) | BIT(rst->bit);
mc_writel(mc, value, rst->control);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static bool tegra_mc_dma_idling_common(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
return (mc_readl(mc, rst->status) & BIT(rst->bit)) != 0;
}
static int tegra_mc_unblock_dma_common(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->control) & ~BIT(rst->bit);
mc_writel(mc, value, rst->control);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static int tegra_mc_reset_status_common(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
return (mc_readl(mc, rst->control) & BIT(rst->bit)) != 0;
}
const struct tegra_mc_reset_ops tegra_mc_reset_ops_common = {
.block_dma = tegra_mc_block_dma_common,
.dma_idling = tegra_mc_dma_idling_common,
.unblock_dma = tegra_mc_unblock_dma_common,
.