OpenCloudOS-Kernel/drivers/memory/tegra/mc.c

923 lines
21 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
/*
* 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>
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <soc/tegra/fuse.h>
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
#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
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
#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 },
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
#endif
{ }
};
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(dev, tegra_mc_devm_action_put_device, mc);
if (err) {
put_device(mc->dev);
return ERR_PTR(err);
}
return mc;
}
EXPORT_SYMBOL_GPL(devm_tegra_memory_controller_get);
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,
.reset_status = tegra_mc_reset_status_common,
};
static inline struct tegra_mc *reset_to_mc(struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct tegra_mc, reset);
}
static const struct tegra_mc_reset *tegra_mc_reset_find(struct tegra_mc *mc,
unsigned long id)
{
unsigned int i;
for (i = 0; i < mc->soc->num_resets; i++)
if (mc->soc->resets[i].id == id)
return &mc->soc->resets[i];
return NULL;
}
static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct tegra_mc *mc = reset_to_mc(rcdev);
const struct tegra_mc_reset_ops *rst_ops;
const struct tegra_mc_reset *rst;
int retries = 500;
int err;
rst = tegra_mc_reset_find(mc, id);
if (!rst)
return -ENODEV;
rst_ops = mc->soc->reset_ops;
if (!rst_ops)
return -ENODEV;
/* DMA flushing will fail if reset is already asserted */
if (rst_ops->reset_status) {
/* check whether reset is asserted */
if (rst_ops->reset_status(mc, rst))
return 0;
}
if (rst_ops->block_dma) {
/* block clients DMA requests */
err = rst_ops->block_dma(mc, rst);
if (err) {
dev_err(mc->dev, "failed to block %s DMA: %d\n",
rst->name, err);
return err;
}
}
if (rst_ops->dma_idling) {
/* wait for completion of the outstanding DMA requests */
while (!rst_ops->dma_idling(mc, rst)) {
if (!retries--) {
dev_err(mc->dev, "failed to flush %s DMA\n",
rst->name);
return -EBUSY;
}
usleep_range(10, 100);
}
}
if (rst_ops->hotreset_assert) {
/* clear clients DMA requests sitting before arbitration */
err = rst_ops->hotreset_assert(mc, rst);
if (err) {
dev_err(mc->dev, "failed to hot reset %s: %d\n",
rst->name, err);
return err;
}
}
return 0;
}
static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct tegra_mc *mc = reset_to_mc(rcdev);
const struct tegra_mc_reset_ops *rst_ops;
const struct tegra_mc_reset *rst;
int err;
rst = tegra_mc_reset_find(mc, id);
if (!rst)
return -ENODEV;
rst_ops = mc->soc->reset_ops;
if (!rst_ops)
return -ENODEV;
if (rst_ops->hotreset_deassert) {
/* take out client from hot reset */
err = rst_ops->hotreset_deassert(mc, rst);
if (err) {
dev_err(mc->dev, "failed to deassert hot reset %s: %d\n",
rst->name, err);
return err;
}
}
if (rst_ops->unblock_dma) {
/* allow new DMA requests to proceed to arbitration */
err = rst_ops->unblock_dma(mc, rst);
if (err) {
dev_err(mc->dev, "failed to unblock %s DMA : %d\n",
rst->name, err);
return err;
}
}
return 0;
}
static int tegra_mc_hotreset_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct tegra_mc *mc = reset_to_mc(rcdev);
const struct tegra_mc_reset_ops *rst_ops;
const struct tegra_mc_reset *rst;
rst = tegra_mc_reset_find(mc, id);
if (!rst)
return -ENODEV;
rst_ops = mc->soc->reset_ops;
if (!rst_ops)
return -ENODEV;
return rst_ops->reset_status(mc, rst);
}
static const struct reset_control_ops tegra_mc_reset_ops = {
.assert = tegra_mc_hotreset_assert,
.deassert = tegra_mc_hotreset_deassert,
.status = tegra_mc_hotreset_status,
};
static int tegra_mc_reset_setup(struct tegra_mc *mc)
{
int err;
mc->reset.ops = &tegra_mc_reset_ops;
mc->reset.owner = THIS_MODULE;
mc->reset.of_node = mc->dev->of_node;
mc->reset.of_reset_n_cells = 1;
mc->reset.nr_resets = mc->soc->num_resets;
err = reset_controller_register(&mc->reset);
if (err < 0)
return err;
return 0;
}
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
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);
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
do_div(tick, NSEC_PER_SEC);
value = mc_readl(mc, MC_EMEM_ARB_CFG);
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
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);
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
/* write latency allowance defaults */
for (i = 0; i < mc->soc->num_clients; i++) {
const struct tegra_mc_la *la = &mc->soc->clients[i].la;
u32 value;
value = mc_readl(mc, la->reg);
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
value &= ~(la->mask << la->shift);
value |= (la->def & la->mask) << la->shift;
mc_writel(mc, value, la->reg);
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
}
/* latch new values */
mc_writel(mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL);
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
return 0;
}
int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
{
unsigned int i;
struct tegra_mc_timing *timing = NULL;
for (i = 0; i < mc->num_timings; i++) {
if (mc->timings[i].rate == rate) {
timing = &mc->timings[i];
break;
}
}
if (!timing) {
dev_err(mc->dev, "no memory timing registered for rate %lu\n",
rate);
return -EINVAL;
}
for (i = 0; i < mc->soc->num_emem_regs; ++i)
mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]);
return 0;
}
EXPORT_SYMBOL_GPL(tegra_mc_write_emem_configuration);
unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
{
u8 dram_count;
dram_count = mc_readl(mc, MC_EMEM_ADR_CFG);
dram_count &= MC_EMEM_ADR_CFG_EMEM_NUMDEV;
dram_count++;
return dram_count;
}
EXPORT_SYMBOL_GPL(tegra_mc_get_emem_device_count);
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;
}
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
static const char *const status_names[32] = {
[ 1] = "External interrupt",
[ 6] = "EMEM address decode error",
[ 7] = "GART page fault",
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
[ 8] = "Security violation",
[ 9] = "EMEM arbitration error",
[10] = "Page fault",
[11] = "Invalid APB ASID update",
[12] = "VPR violation",
[13] = "Secure carveout violation",
[16] = "MTS carveout violation",
};
static const char *const error_names[8] = {
[2] = "EMEM decode error",
[3] = "TrustZone violation",
[4] = "Carveout violation",
[6] = "SMMU translation error",
};
static irqreturn_t tegra_mc_irq(int irq, void *data)
{
struct tegra_mc *mc = data;
unsigned long status;
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
unsigned int bit;
/* mask all interrupts to avoid flooding */
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
if (!status)
return IRQ_NONE;
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
for_each_set_bit(bit, &status, 32) {
const char *error = status_names[bit] ?: "unknown";
const char *client = "unknown", *desc;
const char *direction, *secure;
phys_addr_t addr = 0;
unsigned int i;
char perm[7];
u8 id, type;
u32 value;
value = mc_readl(mc, MC_ERR_STATUS);
#ifdef CONFIG_PHYS_ADDR_T_64BIT
if (mc->soc->num_address_bits > 32) {
addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
MC_ERR_STATUS_ADR_HI_MASK);
addr <<= 32;
}
#endif
if (value & MC_ERR_STATUS_RW)
direction = "write";
else
direction = "read";
if (value & MC_ERR_STATUS_SECURITY)
secure = "secure ";
else
secure = "";
id = value & mc->soc->client_id_mask;
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
for (i = 0; i < mc->soc->num_clients; i++) {
if (mc->soc->clients[i].id == id) {
client = mc->soc->clients[i].name;
break;
}
}
type = (value & MC_ERR_STATUS_TYPE_MASK) >>
MC_ERR_STATUS_TYPE_SHIFT;
desc = error_names[type];
switch (value & MC_ERR_STATUS_TYPE_MASK) {
case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE:
perm[0] = ' ';
perm[1] = '[';
if (value & MC_ERR_STATUS_READABLE)
perm[2] = 'R';
else
perm[2] = '-';
if (value & MC_ERR_STATUS_WRITABLE)
perm[3] = 'W';
else
perm[3] = '-';
if (value & MC_ERR_STATUS_NONSECURE)
perm[4] = '-';
else
perm[4] = 'S';
perm[5] = ']';
perm[6] = '\0';
break;
default:
perm[0] = '\0';
break;
}
value = mc_readl(mc, MC_ERR_ADR);
addr |= value;
dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n",
client, secure, direction, &addr, error,
desc, perm);
}
/* clear interrupts */
mc_writel(mc, status, MC_INTSTATUS);
return IRQ_HANDLED;
}
static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data)
{
struct tegra_mc *mc = data;
unsigned long status;
unsigned int bit;
/* mask all interrupts to avoid flooding */
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
if (!status)
return IRQ_NONE;
for_each_set_bit(bit, &status, 32) {
const char *direction = "read", *secure = "";
const char *error = status_names[bit];
const char *client, *desc;
phys_addr_t addr;
u32 value, reg;
u8 id, type;
switch (BIT(bit)) {
case MC_INT_DECERR_EMEM:
reg = MC_DECERR_EMEM_OTHERS_STATUS;
value = mc_readl(mc, reg);
id = value & mc->soc->client_id_mask;
desc = error_names[2];
if (value & BIT(31))
direction = "write";
break;
case MC_INT_INVALID_GART_PAGE:
reg = MC_GART_ERROR_REQ;
value = mc_readl(mc, reg);
id = (value >> 1) & mc->soc->client_id_mask;
desc = error_names[2];
if (value & BIT(0))
direction = "write";
break;
case MC_INT_SECURITY_VIOLATION:
reg = MC_SECURITY_VIOLATION_STATUS;
value = mc_readl(mc, reg);
id = value & mc->soc->client_id_mask;
type = (value & BIT(30)) ? 4 : 3;
desc = error_names[type];
secure = "secure ";
if (value & BIT(31))
direction = "write";
break;
default:
continue;
}
client = mc->soc->clients[id].name;
addr = mc_readl(mc, reg + sizeof(u32));
dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s)\n",
client, secure, direction, &addr, error,
desc);
}
/* clear interrupts */
mc_writel(mc, status, MC_INTSTATUS);
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;
}
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
static int tegra_mc_probe(struct platform_device *pdev)
{
struct resource *res;
struct tegra_mc *mc;
void *isr;
u64 mask;
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
int err;
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
if (!mc)
return -ENOMEM;
platform_set_drvdata(pdev, mc);
spin_lock_init(&mc->lock);
mc->soc = of_device_get_match_data(&pdev->dev);
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
mc->dev = &pdev->dev;
mask = DMA_BIT_MASK(mc->soc->num_address_bits);
err = dma_coerce_mask_and_coherent(&pdev->dev, mask);
if (err < 0) {
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
return err;
}
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
/* length of MC tick in nanoseconds */
mc->tick = 30;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mc->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mc->regs))
return PTR_ERR(mc->regs);
mc->clk = devm_clk_get(&pdev->dev, "mc");
if (IS_ERR(mc->clk)) {
dev_err(&pdev->dev, "failed to get MC clock: %ld\n",
PTR_ERR(mc->clk));
return PTR_ERR(mc->clk);
}
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
if (mc->soc == &tegra20_mc_soc) {
isr = tegra20_mc_irq;
} else
#endif
{
/* 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(&pdev->dev,
"failed to setup latency allowance: %d\n",
err);
return err;
}
isr = tegra_mc_irq;
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
err = tegra_mc_setup_timings(mc);
if (err < 0) {
dev_err(&pdev->dev, "failed to setup timings: %d\n",
err);
return err;
}
}
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
mc->irq = platform_get_irq(pdev, 0);
if (mc->irq < 0)
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
return mc->irq;
WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n");
mc_writel(mc, mc->soc->intmask, MC_INTMASK);
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
err = devm_request_irq(&pdev->dev, mc->irq, isr, 0,
dev_name(&pdev->dev), mc);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq,
err);
return err;
}
mc->debugfs.root = debugfs_create_dir("mc", NULL);
if (mc->soc->init) {
err = mc->soc->init(mc);
if (err < 0)
dev_err(&pdev->dev, "failed to initialize SoC driver: %d\n",
err);
}
err = tegra_mc_reset_setup(mc);
if (err < 0)
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)) {
dev_err(&pdev->dev, "failed to probe SMMU: %ld\n",
PTR_ERR(mc->smmu));
mc->smmu = NULL;
}
}
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && !mc->soc->smmu) {
mc->gart = tegra_gart_probe(&pdev->dev, mc);
if (IS_ERR(mc->gart)) {
dev_err(&pdev->dev, "failed to probe GART: %ld\n",
PTR_ERR(mc->gart));
mc->gart = NULL;
}
}
return 0;
}
static int tegra_mc_suspend(struct device *dev)
{
struct tegra_mc *mc = dev_get_drvdata(dev);
int err;
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) {
err = tegra_gart_suspend(mc->gart);
if (err)
return err;
}
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
return 0;
}
static int tegra_mc_resume(struct device *dev)
{
struct tegra_mc *mc = dev_get_drvdata(dev);
int err;
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) {
err = tegra_gart_resume(mc->gart);
if (err)
return err;
}
return 0;
}
static const struct dev_pm_ops tegra_mc_pm_ops = {
.suspend = tegra_mc_suspend,
.resume = tegra_mc_resume,
};
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
static struct platform_driver tegra_mc_driver = {
.driver = {
.name = "tegra-mc",
.of_match_table = tegra_mc_of_match,
.pm = &tegra_mc_pm_ops,
memory: Add NVIDIA Tegra memory controller support The memory controller on NVIDIA Tegra exposes various knobs that can be used to tune the behaviour of the clients attached to it. Currently this driver sets up the latency allowance registers to the HW defaults. Eventually an API should be exported by this driver (via a custom API or a generic subsystem) to allow clients to register latency requirements. This driver also registers an IOMMU (SMMU) that's implemented by the memory controller. It is supported on Tegra30, Tegra114 and Tegra124 currently. Tegra20 has a GART instead. The Tegra SMMU operates on memory clients and SWGROUPs. A memory client is a unidirectional, special-purpose DMA master. A SWGROUP represents a set of memory clients that form a logical functional unit corresponding to a single device. Typically a device has two clients: one client for read transactions and one client for write transactions, but there are also devices that have only read clients, but many of them (such as the display controllers). Because there is no 1:1 relationship between memory clients and devices the driver keeps a table of memory clients and the SWGROUPs that they belong to per SoC. Note that this is an exception and due to the fact that the SMMU is tightly integrated with the rest of the Tegra SoC. The use of these tables is discouraged in drivers for generic IOMMU devices such as the ARM SMMU because the same IOMMU could be used in any number of SoCs and keeping such tables for each SoC would not scale. Acked-by: Joerg Roedel <jroedel@suse.de> Signed-off-by: Thierry Reding <treding@nvidia.com>
2014-04-16 15:24:44 +08:00
.suppress_bind_attrs = true,
},
.prevent_deferred_probe = true,
.probe = tegra_mc_probe,
};
static int tegra_mc_init(void)
{
return platform_driver_register(&tegra_mc_driver);
}
arch_initcall(tegra_mc_init);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra Memory Controller driver");
MODULE_LICENSE("GPL v2");