mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-05-02 18:17:50 -04:00
Merge tag 'memory-controller-drv-tegra-5.14-2' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into arm/drivers
Memory controller drivers for v5.14 - Tegra SoC, part two
Second set of changes for Tegra SoC memory controller drivers,
containing patchset from Thierry Reding:
"The goal here is to avoid early identity mappings altogether and instead
postpone the need for the identity mappings to when devices are attached
to the SMMU. This works by making the SMMU driver coordinate with the
memory controller driver on when to start enforcing SMMU translations.
This makes Tegra behave in a more standard way and pushes the code to
deal with the Tegra-specific programming into the NVIDIA SMMU
implementation."
This pulls a dependency from Will Deacon (ARM SMMU driver) and contains
further ARM SMMU driver patches to resolve complex dependencies between
different patchsets. The pull from Will contains only one patch
("Implement ->probe_finalize()"). Further work in Will's tree might
depend on this patch, therefore patch was applied there.
On the other hand, this ("Implement ->probe_finalize()") patch is also a
dependency for ARM SMMU driver changes for Tegra. These changes,
bringing seamless transition from the firmware framebuffer to the OS
framebuffer, depend on earlier Tegra memory controller driver patches.
* tag 'memory-controller-drv-tegra-5.14-2' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl: (37 commits)
iommu/arm-smmu: Use Tegra implementation on Tegra186
iommu/arm-smmu: tegra: Implement SID override programming
iommu/arm-smmu: tegra: Detect number of instances at runtime
dt-bindings: arm-smmu: Add Tegra186 compatible string
memory: tegra: Delete dead debugfs checking code
iommu/arm-smmu: Implement ->probe_finalize()
memory: tegra: Implement SID override programming
memory: tegra: Split Tegra194 data into separate file
memory: tegra: Add memory client IDs to tables
memory: tegra: Unify drivers
memory: tegra: Only initialize reset controller if available
memory: tegra: Make IRQ support opitonal
memory: tegra: Parameterize interrupt handler
memory: tegra: Extract setup code into callback
memory: tegra: Make per-SoC setup more generic
memory: tegra: Push suspend/resume into SoC drivers
memory: tegra: Introduce struct tegra_mc_ops
memory: tegra: Unify struct tegra_mc across SoC generations
memory: tegra: Consolidate register fields
memory: tegra30-emc: Use devm_tegra_core_dev_init_opp_table()
...
Link: https://lore.kernel.org/r/20210614195200.21657-1-krzysztof.kozlowski@canonical.com
Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
@@ -2,16 +2,18 @@
|
||||
config TEGRA_MC
|
||||
bool "NVIDIA Tegra Memory Controller support"
|
||||
default y
|
||||
depends on ARCH_TEGRA
|
||||
depends on ARCH_TEGRA || (COMPILE_TEST && COMMON_CLK)
|
||||
select INTERCONNECT
|
||||
help
|
||||
This driver supports the Memory Controller (MC) hardware found on
|
||||
NVIDIA Tegra SoCs.
|
||||
|
||||
if TEGRA_MC
|
||||
|
||||
config TEGRA20_EMC
|
||||
tristate "NVIDIA Tegra20 External Memory Controller driver"
|
||||
default y
|
||||
depends on TEGRA_MC && ARCH_TEGRA_2x_SOC
|
||||
depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
|
||||
select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select PM_DEVFREQ
|
||||
help
|
||||
@@ -23,7 +25,7 @@ config TEGRA20_EMC
|
||||
config TEGRA30_EMC
|
||||
tristate "NVIDIA Tegra30 External Memory Controller driver"
|
||||
default y
|
||||
depends on TEGRA_MC && ARCH_TEGRA_3x_SOC
|
||||
depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST
|
||||
select PM_OPP
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
@@ -34,8 +36,8 @@ config TEGRA30_EMC
|
||||
config TEGRA124_EMC
|
||||
tristate "NVIDIA Tegra124 External Memory Controller driver"
|
||||
default y
|
||||
depends on TEGRA_MC && ARCH_TEGRA_124_SOC
|
||||
select TEGRA124_CLK_EMC
|
||||
depends on ARCH_TEGRA_124_SOC || COMPILE_TEST
|
||||
select TEGRA124_CLK_EMC if ARCH_TEGRA
|
||||
select PM_OPP
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
@@ -45,14 +47,16 @@ config TEGRA124_EMC
|
||||
|
||||
config TEGRA210_EMC_TABLE
|
||||
bool
|
||||
depends on ARCH_TEGRA_210_SOC
|
||||
depends on ARCH_TEGRA_210_SOC || COMPILE_TEST
|
||||
|
||||
config TEGRA210_EMC
|
||||
tristate "NVIDIA Tegra210 External Memory Controller driver"
|
||||
depends on TEGRA_MC && ARCH_TEGRA_210_SOC
|
||||
depends on ARCH_TEGRA_210_SOC || COMPILE_TEST
|
||||
select TEGRA210_EMC_TABLE
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
Tegra210 chips. The EMC controls the external DRAM on the board.
|
||||
This driver is required to change memory timings / clock rate for
|
||||
external memory.
|
||||
|
||||
endif
|
||||
|
||||
@@ -7,6 +7,8 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra124.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra194.o
|
||||
|
||||
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
|
||||
|
||||
@@ -15,7 +17,7 @@ obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o
|
||||
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
|
||||
obj-$(CONFIG_TEGRA210_EMC_TABLE) += tegra210-emc-table.o
|
||||
obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o tegra186-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra186-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186-emc.o
|
||||
|
||||
tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o
|
||||
|
||||
@@ -39,7 +39,13 @@ static const struct of_device_id tegra_mc_of_match[] = {
|
||||
#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
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
|
||||
|
||||
@@ -91,6 +97,15 @@ struct tegra_mc *devm_tegra_memory_controller_get(struct device *dev)
|
||||
}
|
||||
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);
|
||||
|
||||
static int tegra_mc_block_dma_common(struct tegra_mc *mc,
|
||||
const struct tegra_mc_reset *rst)
|
||||
{
|
||||
@@ -299,38 +314,6 @@ static int tegra_mc_reset_setup(struct tegra_mc *mc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_la *la = &mc->soc->clients[i].la;
|
||||
u32 value;
|
||||
|
||||
value = mc_readl(mc, la->reg);
|
||||
value &= ~(la->mask << la->shift);
|
||||
value |= (la->def & la->mask) << la->shift;
|
||||
mc_writel(mc, value, la->reg);
|
||||
}
|
||||
|
||||
/* latch new values */
|
||||
mc_writel(mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -368,6 +351,43 @@ unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_mc_get_emem_device_count);
|
||||
|
||||
#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)
|
||||
@@ -459,27 +479,35 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const status_names[32] = {
|
||||
[ 1] = "External interrupt",
|
||||
[ 6] = "EMEM address decode error",
|
||||
[ 7] = "GART page fault",
|
||||
[ 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",
|
||||
};
|
||||
int tegra30_mc_probe(struct tegra_mc *mc)
|
||||
{
|
||||
int err;
|
||||
|
||||
static const char *const error_names[8] = {
|
||||
[2] = "EMEM decode error",
|
||||
[3] = "TrustZone violation",
|
||||
[4] = "Carveout violation",
|
||||
[6] = "SMMU translation error",
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
static irqreturn_t tegra_mc_irq(int irq, void *data)
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
|
||||
{
|
||||
struct tegra_mc *mc = data;
|
||||
unsigned long status;
|
||||
@@ -491,7 +519,7 @@ static irqreturn_t tegra_mc_irq(int irq, void *data)
|
||||
return IRQ_NONE;
|
||||
|
||||
for_each_set_bit(bit, &status, 32) {
|
||||
const char *error = status_names[bit] ?: "unknown";
|
||||
const char *error = tegra_mc_status_names[bit] ?: "unknown";
|
||||
const char *client = "unknown", *desc;
|
||||
const char *direction, *secure;
|
||||
phys_addr_t addr = 0;
|
||||
@@ -531,7 +559,7 @@ static irqreturn_t tegra_mc_irq(int irq, void *data)
|
||||
|
||||
type = (value & MC_ERR_STATUS_TYPE_MASK) >>
|
||||
MC_ERR_STATUS_TYPE_SHIFT;
|
||||
desc = error_names[type];
|
||||
desc = tegra_mc_error_names[type];
|
||||
|
||||
switch (value & MC_ERR_STATUS_TYPE_MASK) {
|
||||
case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE:
|
||||
@@ -576,78 +604,31 @@ static irqreturn_t tegra_mc_irq(int irq, void *data)
|
||||
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;
|
||||
const struct tegra_mc_ops tegra30_mc_ops = {
|
||||
.probe = tegra30_mc_probe,
|
||||
.handle_irq = tegra30_mc_handle_irq,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* mask all interrupts to avoid flooding */
|
||||
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
const char *const tegra_mc_status_names[32] = {
|
||||
[ 1] = "External interrupt",
|
||||
[ 6] = "EMEM address decode error",
|
||||
[ 7] = "GART page fault",
|
||||
[ 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",
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
const char *const tegra_mc_error_names[8] = {
|
||||
[2] = "EMEM decode error",
|
||||
[3] = "TrustZone violation",
|
||||
[4] = "Carveout violation",
|
||||
[6] = "SMMU translation error",
|
||||
};
|
||||
|
||||
/*
|
||||
* Memory Controller (MC) has few Memory Clients that are issuing memory
|
||||
@@ -748,7 +729,6 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct tegra_mc *mc;
|
||||
void *isr;
|
||||
u64 mask;
|
||||
int err;
|
||||
|
||||
@@ -777,69 +757,37 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
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;
|
||||
|
||||
err = tegra_mc_setup_timings(mc);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to setup timings: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
mc->irq = platform_get_irq(pdev, 0);
|
||||
if (mc->irq < 0)
|
||||
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);
|
||||
|
||||
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 (mc->soc->ops && mc->soc->ops->probe) {
|
||||
err = mc->soc->ops->probe(mc);
|
||||
if (err < 0)
|
||||
dev_err(&pdev->dev, "failed to initialize SoC driver: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = tegra_mc_reset_setup(mc);
|
||||
if (err < 0)
|
||||
dev_err(&pdev->dev, "failed to register reset controller: %d\n",
|
||||
err);
|
||||
if (mc->soc->ops && mc->soc->ops->handle_irq) {
|
||||
mc->irq = platform_get_irq(pdev, 0);
|
||||
if (mc->irq < 0)
|
||||
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);
|
||||
|
||||
err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (mc->soc->reset_ops) {
|
||||
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)
|
||||
@@ -867,37 +815,28 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_mc_suspend(struct device *dev)
|
||||
static int __maybe_unused 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;
|
||||
}
|
||||
if (mc->soc->ops && mc->soc->ops->suspend)
|
||||
return mc->soc->ops->suspend(mc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_mc_resume(struct device *dev)
|
||||
static int __maybe_unused 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;
|
||||
}
|
||||
if (mc->soc->ops && mc->soc->ops->resume)
|
||||
return mc->soc->ops->resume(mc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tegra_mc_pm_ops = {
|
||||
.suspend = tegra_mc_suspend,
|
||||
.resume = tegra_mc_resume,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_mc_suspend, tegra_mc_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_mc_driver = {
|
||||
|
||||
@@ -129,6 +129,31 @@ extern const struct tegra_mc_soc tegra132_mc_soc;
|
||||
extern const struct tegra_mc_soc tegra210_mc_soc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_186_SOC
|
||||
extern const struct tegra_mc_soc tegra186_mc_soc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_194_SOC
|
||||
extern const struct tegra_mc_soc tegra194_mc_soc;
|
||||
#endif
|
||||
|
||||
#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)
|
||||
int tegra30_mc_probe(struct tegra_mc *mc);
|
||||
extern const struct tegra_mc_ops tegra30_mc_ops;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_TEGRA_186_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_194_SOC)
|
||||
extern const struct tegra_mc_ops tegra186_mc_ops;
|
||||
#endif
|
||||
|
||||
extern const char * const tegra_mc_status_names[32];
|
||||
extern const char * const tegra_mc_error_names[8];
|
||||
|
||||
/*
|
||||
* 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.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -272,8 +272,8 @@
|
||||
#define EMC_PUTERM_ADJ 0x574
|
||||
|
||||
#define DRAM_DEV_SEL_ALL 0
|
||||
#define DRAM_DEV_SEL_0 (2 << 30)
|
||||
#define DRAM_DEV_SEL_1 (1 << 30)
|
||||
#define DRAM_DEV_SEL_0 BIT(31)
|
||||
#define DRAM_DEV_SEL_1 BIT(30)
|
||||
|
||||
#define EMC_CFG_POWER_FEATURES_MASK \
|
||||
(EMC_CFG_DYN_SREF | EMC_CFG_DRAM_ACPD | EMC_CFG_DRAM_CLKSTOP_SR | \
|
||||
@@ -1269,10 +1269,6 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
|
||||
}
|
||||
|
||||
emc->debugfs.root = debugfs_create_dir("emc", NULL);
|
||||
if (!emc->debugfs.root) {
|
||||
dev_err(dev, "failed to create debugfs directory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc,
|
||||
&tegra_emc_debug_available_rates_fops);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1351
drivers/memory/tegra/tegra194.c
Normal file
1351
drivers/memory/tegra/tegra194.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -776,10 +776,6 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc)
|
||||
}
|
||||
|
||||
emc->debugfs.root = debugfs_create_dir("emc", NULL);
|
||||
if (!emc->debugfs.root) {
|
||||
dev_err(emc->dev, "failed to create debugfs directory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_available_rates_fops);
|
||||
@@ -908,49 +904,6 @@ err_msg:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_emc_opp_table_init(struct tegra_emc *emc)
|
||||
{
|
||||
u32 hw_version = BIT(tegra_sku_info.soc_process_id);
|
||||
struct opp_table *hw_opp_table;
|
||||
int err;
|
||||
|
||||
hw_opp_table = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);
|
||||
err = PTR_ERR_OR_ZERO(hw_opp_table);
|
||||
if (err) {
|
||||
dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = dev_pm_opp_of_add_table(emc->dev);
|
||||
if (err) {
|
||||
if (err == -ENODEV)
|
||||
dev_err(emc->dev, "OPP table not found, please update your device tree\n");
|
||||
else
|
||||
dev_err(emc->dev, "failed to add OPP table: %d\n", err);
|
||||
|
||||
goto put_hw_table;
|
||||
}
|
||||
|
||||
dev_info_once(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
|
||||
hw_version, clk_get_rate(emc->clk) / 1000000);
|
||||
|
||||
/* first dummy rate-set initializes voltage state */
|
||||
err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk));
|
||||
if (err) {
|
||||
dev_err(emc->dev, "failed to initialize OPP clock: %d\n", err);
|
||||
goto remove_table;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
remove_table:
|
||||
dev_pm_opp_of_remove_table(emc->dev);
|
||||
put_hw_table:
|
||||
dev_pm_opp_put_supported_hw(hw_opp_table);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void devm_tegra_emc_unset_callback(void *data)
|
||||
{
|
||||
tegra20_clk_set_emc_round_callback(NULL, NULL);
|
||||
@@ -1077,6 +1030,7 @@ static int tegra_emc_devfreq_init(struct tegra_emc *emc)
|
||||
|
||||
static int tegra_emc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_core_opp_params opp_params = {};
|
||||
struct device_node *np;
|
||||
struct tegra_emc *emc;
|
||||
int irq, err;
|
||||
@@ -1122,7 +1076,9 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tegra_emc_opp_table_init(emc);
|
||||
opp_params.init_state = true;
|
||||
|
||||
err = devm_tegra_core_dev_init_opp_table(&pdev->dev, &opp_params);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
@@ -679,7 +679,7 @@ static int tegra20_mc_stats_show(struct seq_file *s, void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_mc_init(struct tegra_mc *mc)
|
||||
static int tegra20_mc_probe(struct tegra_mc *mc)
|
||||
{
|
||||
debugfs_create_devm_seqfile(mc->dev, "stats", mc->debugfs.root,
|
||||
tegra20_mc_stats_show);
|
||||
@@ -687,6 +687,112 @@ static int tegra20_mc_init(struct tegra_mc *mc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_mc_suspend(struct tegra_mc *mc)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) {
|
||||
err = tegra_gart_suspend(mc->gart);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_mc_resume(struct tegra_mc *mc)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) {
|
||||
err = tegra_gart_resume(mc->gart);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t tegra20_mc_handle_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 *error = tegra_mc_status_names[bit];
|
||||
const char *direction = "read", *secure = "";
|
||||
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 = tegra_mc_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 = tegra_mc_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 = tegra_mc_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;
|
||||
}
|
||||
|
||||
static const struct tegra_mc_ops tegra20_mc_ops = {
|
||||
.probe = tegra20_mc_probe,
|
||||
.suspend = tegra20_mc_suspend,
|
||||
.resume = tegra20_mc_resume,
|
||||
.handle_irq = tegra20_mc_handle_irq,
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra20_mc_soc = {
|
||||
.clients = tegra20_mc_clients,
|
||||
.num_clients = ARRAY_SIZE(tegra20_mc_clients),
|
||||
@@ -698,5 +804,5 @@ const struct tegra_mc_soc tegra20_mc_soc = {
|
||||
.resets = tegra20_mc_resets,
|
||||
.num_resets = ARRAY_SIZE(tegra20_mc_resets),
|
||||
.icc_ops = &tegra20_mc_icc_ops,
|
||||
.init = tegra20_mc_init,
|
||||
.ops = &tegra20_mc_ops,
|
||||
};
|
||||
|
||||
@@ -1759,10 +1759,6 @@ static void tegra210_emc_debugfs_init(struct tegra210_emc *emc)
|
||||
}
|
||||
|
||||
emc->debugfs.root = debugfs_create_dir("emc", NULL);
|
||||
if (!emc->debugfs.root) {
|
||||
dev_err(dev, "failed to create debugfs directory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc,
|
||||
&tegra210_emc_debug_available_rates_fops);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -150,8 +150,8 @@
|
||||
#define EMC_SELF_REF_CMD_ENABLED BIT(0)
|
||||
|
||||
#define DRAM_DEV_SEL_ALL (0 << 30)
|
||||
#define DRAM_DEV_SEL_0 (2 << 30)
|
||||
#define DRAM_DEV_SEL_1 (1 << 30)
|
||||
#define DRAM_DEV_SEL_0 BIT(31)
|
||||
#define DRAM_DEV_SEL_1 BIT(30)
|
||||
#define DRAM_BROADCAST(num) \
|
||||
((num) > 1 ? DRAM_DEV_SEL_ALL : DRAM_DEV_SEL_0)
|
||||
|
||||
@@ -1354,10 +1354,6 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc)
|
||||
}
|
||||
|
||||
emc->debugfs.root = debugfs_create_dir("emc", NULL);
|
||||
if (!emc->debugfs.root) {
|
||||
dev_err(emc->dev, "failed to create debugfs directory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_available_rates_fops);
|
||||
@@ -1480,49 +1476,6 @@ err_msg:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_emc_opp_table_init(struct tegra_emc *emc)
|
||||
{
|
||||
u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
|
||||
struct opp_table *hw_opp_table;
|
||||
int err;
|
||||
|
||||
hw_opp_table = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);
|
||||
err = PTR_ERR_OR_ZERO(hw_opp_table);
|
||||
if (err) {
|
||||
dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = dev_pm_opp_of_add_table(emc->dev);
|
||||
if (err) {
|
||||
if (err == -ENODEV)
|
||||
dev_err(emc->dev, "OPP table not found, please update your device tree\n");
|
||||
else
|
||||
dev_err(emc->dev, "failed to add OPP table: %d\n", err);
|
||||
|
||||
goto put_hw_table;
|
||||
}
|
||||
|
||||
dev_info_once(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
|
||||
hw_version, clk_get_rate(emc->clk) / 1000000);
|
||||
|
||||
/* first dummy rate-set initializes voltage state */
|
||||
err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk));
|
||||
if (err) {
|
||||
dev_err(emc->dev, "failed to initialize OPP clock: %d\n", err);
|
||||
goto remove_table;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
remove_table:
|
||||
dev_pm_opp_of_remove_table(emc->dev);
|
||||
put_hw_table:
|
||||
dev_pm_opp_put_supported_hw(hw_opp_table);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void devm_tegra_emc_unset_callback(void *data)
|
||||
{
|
||||
tegra20_clk_set_emc_round_callback(NULL, NULL);
|
||||
@@ -1568,6 +1521,7 @@ static int tegra_emc_init_clk(struct tegra_emc *emc)
|
||||
|
||||
static int tegra_emc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_core_opp_params opp_params = {};
|
||||
struct device_node *np;
|
||||
struct tegra_emc *emc;
|
||||
int err;
|
||||
@@ -1617,7 +1571,9 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tegra_emc_opp_table_init(emc);
|
||||
opp_params.init_state = true;
|
||||
|
||||
err = devm_tegra_core_dev_init_opp_table(&pdev->dev, &opp_params);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user