memory: tegra: Changes for v5.9-rc1
This contains the Tegra210 EMC frequency scaling support that didn't make it into v5.8. In addition there are a couple of cleanups and minor fixes. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEiOrDCAFJzPfAjcif3SOs138+s6EFAl8RzAwTHHRyZWRpbmdA bnZpZGlhLmNvbQAKCRDdI6zXfz6zoWAbEACQrlQXrjY7FnmzWULjtz282MqXiG/6 WXEGBFIo8ozBjJes7/CAAKLIzkr6Py9qKEzFmCUeX6jAQtRM6QMUSCeG9XWeRr/2 +gMJIehqTalpeqVXurW7OgTUkzluH0Er9ro0/vMPqfuB9eR4Fi+ROjaivWv3vw2z CJzkMnt/5y0f/WUMiFbG1JRAMCy4QaTlgru104n4uMt8e8q9qs8JfWFQcX4ByFif 5llkgFYhN/CgMlH+9SSc5gO4jUSGaexDNfj2bOpAV0Vb4a990CSjiuJd9Z6ToOuR 3w2lr6/WB0S+AEkI44TPMa/YxOKMNd7LE4bBLjBt8VdV6e+Lz11jGou4TbZSWsui QAhGmItS9Ohiozixxin5Qzp179Mmu9MuyHUocWCK8S4oBZhvkIsjz1Mzn8TEYHxG UadYUQSBolTFtYqJZIJaWPhFveMmecWaFiXZWv8OOOUlduxWdLeD1/rABZX1a+5f nklPaabTBqmtSU22Mh/xxhXrmxkm805IZr2u42fSthjUuoNagFawYG6kzDdXP84j id9JM6BDCAiO8sn/q6W34+CZmqzmZvVFUNNd/CwGiT74DRw1uLjhnNHh0Z/EEgFa 7gh5JHZmb8Vj6HCDGqewqHpPKhSksdv2Rh+ycIkEy478sJBmN3U9N/HCH5qtipZE oHm6S/yqan/fag== =Mqww -----END PGP SIGNATURE----- Merge tag 'tegra-for-5.9-memory' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into arm/drivers memory: tegra: Changes for v5.9-rc1 This contains the Tegra210 EMC frequency scaling support that didn't make it into v5.8. In addition there are a couple of cleanups and minor fixes. * tag 'tegra-for-5.9-memory' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux: memory: tegra: Add Tegra132 compatible string match memory: tegra: Fix KCONFIG variables for Tegra186 and Tegra194 memory: tegra: Delete some dead code memory: tegra: Avoid unused function warnings memory: tegra: Drop <linux/clk-provider.h> memory: tegra: Fix an error handling path in tegra186_emc_probe() memory: tegra30-emc: Poll EMC-CaR handshake instead of waiting for interrupt memory: tegra20-emc: Poll EMC-CaR handshake instead of waiting for interrupt memory: tegra: Support derated timings on Tegra210 memory: tegra: Add EMC scaling sequence code for Tegra210 memory: tegra: Add EMC scaling support code for Tegra210 memory: tegra: Make debugfs permissions human-readable Link: https://lore.kernel.org/r/20200717161300.1661002-3-thierry.reding@gmail.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
c7d0ff9323
|
@ -36,3 +36,17 @@ config TEGRA124_EMC
|
|||
Tegra124 chips. The EMC controls the external DRAM on the board.
|
||||
This driver is required to change memory timings / clock rate for
|
||||
external memory.
|
||||
|
||||
config TEGRA210_EMC_TABLE
|
||||
bool
|
||||
depends on ARCH_TEGRA_210_SOC
|
||||
|
||||
config TEGRA210_EMC
|
||||
tristate "NVIDIA Tegra210 External Memory Controller driver"
|
||||
depends on TEGRA_MC && ARCH_TEGRA_210_SOC
|
||||
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.
|
||||
|
|
|
@ -13,5 +13,9 @@ obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
|
|||
obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
|
||||
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
|
||||
|
||||
tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#define MC_EMEM_ARB_TIMING_W2W 0xbc
|
||||
#define MC_EMEM_ARB_TIMING_R2W 0xc0
|
||||
#define MC_EMEM_ARB_TIMING_W2R 0xc4
|
||||
#define MC_EMEM_ARB_MISC2 0xc8
|
||||
#define MC_EMEM_ARB_DA_TURNS 0xd0
|
||||
#define MC_EMEM_ARB_DA_COVERS 0xd4
|
||||
#define MC_EMEM_ARB_MISC0 0xd8
|
||||
|
|
|
@ -984,6 +984,7 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
|
|||
|
||||
static const struct of_device_id tegra_emc_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-emc" },
|
||||
{ .compatible = "nvidia,tegra132-emc" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -1178,11 +1179,11 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
|
|||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root, emc,
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc,
|
||||
&tegra_emc_debug_available_rates_fops);
|
||||
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("min_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_min_rate_fops);
|
||||
debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("max_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_max_rate_fops);
|
||||
}
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(emc->clk)) {
|
||||
err = PTR_ERR(emc->clk);
|
||||
dev_err(&pdev->dev, "failed to get EMC clock: %d\n", err);
|
||||
return err;
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, emc);
|
||||
|
@ -201,7 +201,7 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
|||
err = tegra_bpmp_transfer(emc->bpmp, &msg);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to EMC DVFS pairs: %d\n", err);
|
||||
return err;
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
emc->debugfs.min_rate = ULONG_MAX;
|
||||
|
@ -211,8 +211,10 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
|||
|
||||
emc->dvfs = devm_kmalloc_array(&pdev->dev, emc->num_dvfs,
|
||||
sizeof(*emc->dvfs), GFP_KERNEL);
|
||||
if (!emc->dvfs)
|
||||
return -ENOMEM;
|
||||
if (!emc->dvfs) {
|
||||
err = -ENOMEM;
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "%u DVFS pairs:\n", emc->num_dvfs);
|
||||
|
||||
|
@ -237,15 +239,10 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
|||
"failed to set rate range [%lu-%lu] for %pC\n",
|
||||
emc->debugfs.min_rate, emc->debugfs.max_rate,
|
||||
emc->clk);
|
||||
return err;
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
emc->debugfs.root = debugfs_create_dir("emc", NULL);
|
||||
if (!emc->debugfs.root) {
|
||||
dev_err(&pdev->dev, "failed to create debugfs directory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root,
|
||||
emc, &tegra186_emc_debug_available_rates_fops);
|
||||
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
|
@ -254,6 +251,10 @@ static int tegra186_emc_probe(struct platform_device *pdev)
|
|||
emc, &tegra186_emc_debug_max_rate_fops);
|
||||
|
||||
return 0;
|
||||
|
||||
put_bpmp:
|
||||
tegra_bpmp_put(emc->bpmp);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra186_emc_remove(struct platform_device *pdev)
|
||||
|
@ -267,10 +268,10 @@ static int tegra186_emc_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct of_device_id tegra186_emc_of_match[] = {
|
||||
#if defined(CONFIG_ARCH_TEGRA186_SOC)
|
||||
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
|
||||
{ .compatible = "nvidia,tegra186-emc" },
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA194_SOC)
|
||||
#if defined(CONFIG_ARCH_TEGRA_194_SOC)
|
||||
{ .compatible = "nvidia,tegra194-emc" },
|
||||
#endif
|
||||
{ /* sentinel */ }
|
||||
|
|
|
@ -1570,12 +1570,12 @@ static const struct of_device_id tegra186_mc_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra186_mc_of_match);
|
||||
|
||||
static int tegra186_mc_suspend(struct device *dev)
|
||||
static int __maybe_unused tegra186_mc_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_mc_resume(struct device *dev)
|
||||
static int __maybe_unused tegra186_mc_resume(struct device *dev)
|
||||
{
|
||||
struct tegra186_mc *mc = dev_get_drvdata(dev);
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -144,7 +144,6 @@ struct emc_timing {
|
|||
|
||||
struct tegra_emc {
|
||||
struct device *dev;
|
||||
struct completion clk_handshake_complete;
|
||||
struct notifier_block clk_nb;
|
||||
struct clk *clk;
|
||||
void __iomem *regs;
|
||||
|
@ -162,17 +161,13 @@ struct tegra_emc {
|
|||
static irqreturn_t tegra_emc_isr(int irq, void *data)
|
||||
{
|
||||
struct tegra_emc *emc = data;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
||||
u32 status;
|
||||
|
||||
status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask;
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* notify about EMC-CAR handshake completion */
|
||||
if (status & EMC_CLKCHANGE_COMPLETE_INT)
|
||||
complete(&emc->clk_handshake_complete);
|
||||
|
||||
/* notify about HW problem */
|
||||
if (status & EMC_REFRESH_OVERFLOW_INT)
|
||||
dev_err_ratelimited(emc->dev,
|
||||
|
@ -224,14 +219,13 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
|
|||
/* wait until programming has settled */
|
||||
readl_relaxed(emc->regs + emc_timing_registers[i - 1]);
|
||||
|
||||
reinit_completion(&emc->clk_handshake_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int err;
|
||||
u32 v;
|
||||
|
||||
dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush);
|
||||
|
||||
|
@ -242,11 +236,12 @@ static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
|
|||
return 0;
|
||||
}
|
||||
|
||||
timeout = wait_for_completion_timeout(&emc->clk_handshake_complete,
|
||||
msecs_to_jiffies(100));
|
||||
if (timeout == 0) {
|
||||
dev_err(emc->dev, "EMC-CAR handshake failed\n");
|
||||
return -EIO;
|
||||
err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, v,
|
||||
v & EMC_CLKCHANGE_COMPLETE_INT,
|
||||
1, 100);
|
||||
if (err) {
|
||||
dev_err(emc->dev, "emc-car handshake timeout: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -412,7 +407,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
|
|||
|
||||
static int emc_setup_hw(struct tegra_emc *emc)
|
||||
{
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
||||
u32 emc_cfg, emc_dbg;
|
||||
|
||||
emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
|
||||
|
@ -647,11 +642,11 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc)
|
|||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root,
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_available_rates_fops);
|
||||
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("min_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_min_rate_fops);
|
||||
debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("max_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_max_rate_fops);
|
||||
}
|
||||
|
||||
|
@ -686,7 +681,6 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
init_completion(&emc->clk_handshake_complete);
|
||||
emc->clk_nb.notifier_call = tegra_emc_clk_change_notify;
|
||||
emc->dev = &pdev->dev;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,90 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/of_reserved_mem.h>
|
||||
|
||||
#include "tegra210-emc.h"
|
||||
|
||||
#define TEGRA_EMC_MAX_FREQS 16
|
||||
|
||||
static int tegra210_emc_table_device_init(struct reserved_mem *rmem,
|
||||
struct device *dev)
|
||||
{
|
||||
struct tegra210_emc *emc = dev_get_drvdata(dev);
|
||||
struct tegra210_emc_timing *timings;
|
||||
unsigned int i, count = 0;
|
||||
|
||||
timings = memremap(rmem->base, rmem->size, MEMREMAP_WB);
|
||||
if (!timings) {
|
||||
dev_err(dev, "failed to map EMC table\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) {
|
||||
if (timings[i].revision == 0)
|
||||
break;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
/* only the nominal and derated tables are expected */
|
||||
if (emc->derated) {
|
||||
dev_warn(dev, "excess EMC table '%s'\n", rmem->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (emc->nominal) {
|
||||
if (count != emc->num_timings) {
|
||||
dev_warn(dev, "%u derated vs. %u nominal entries\n",
|
||||
count, emc->num_timings);
|
||||
memunmap(timings);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
emc->derated = timings;
|
||||
} else {
|
||||
emc->num_timings = count;
|
||||
emc->nominal = timings;
|
||||
}
|
||||
|
||||
out:
|
||||
/* keep track of which table this is */
|
||||
rmem->priv = timings;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra210_emc_table_device_release(struct reserved_mem *rmem,
|
||||
struct device *dev)
|
||||
{
|
||||
struct tegra210_emc_timing *timings = rmem->priv;
|
||||
struct tegra210_emc *emc = dev_get_drvdata(dev);
|
||||
|
||||
if ((emc->nominal && timings != emc->nominal) &&
|
||||
(emc->derated && timings != emc->derated))
|
||||
dev_warn(dev, "trying to release unassigned EMC table '%s'\n",
|
||||
rmem->name);
|
||||
|
||||
memunmap(timings);
|
||||
}
|
||||
|
||||
static const struct reserved_mem_ops tegra210_emc_table_ops = {
|
||||
.device_init = tegra210_emc_table_device_init,
|
||||
.device_release = tegra210_emc_table_device_release,
|
||||
};
|
||||
|
||||
static int tegra210_emc_table_init(struct reserved_mem *rmem)
|
||||
{
|
||||
pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n", &rmem->base,
|
||||
(unsigned long)rmem->size);
|
||||
|
||||
rmem->ops = &tegra210_emc_table_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
RESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table",
|
||||
tegra210_emc_table_init);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,50 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef TEGRA210_MC_H
|
||||
#define TEGRA210_MC_H
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
/* register definitions */
|
||||
#define MC_LATENCY_ALLOWANCE_AVPC_0 0x2e4
|
||||
#define MC_LATENCY_ALLOWANCE_HC_0 0x310
|
||||
#define MC_LATENCY_ALLOWANCE_HC_1 0x314
|
||||
#define MC_LATENCY_ALLOWANCE_MPCORE_0 0x320
|
||||
#define MC_LATENCY_ALLOWANCE_NVENC_0 0x328
|
||||
#define MC_LATENCY_ALLOWANCE_PPCS_0 0x344
|
||||
#define MC_LATENCY_ALLOWANCE_PPCS_1 0x348
|
||||
#define MC_LATENCY_ALLOWANCE_ISP2_0 0x370
|
||||
#define MC_LATENCY_ALLOWANCE_ISP2_1 0x374
|
||||
#define MC_LATENCY_ALLOWANCE_XUSB_0 0x37c
|
||||
#define MC_LATENCY_ALLOWANCE_XUSB_1 0x380
|
||||
#define MC_LATENCY_ALLOWANCE_TSEC_0 0x390
|
||||
#define MC_LATENCY_ALLOWANCE_VIC_0 0x394
|
||||
#define MC_LATENCY_ALLOWANCE_VI2_0 0x398
|
||||
#define MC_LATENCY_ALLOWANCE_GPU_0 0x3ac
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMCA_0 0x3b8
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 0x3bc
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMC_0 0x3c0
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 0x3c4
|
||||
#define MC_LATENCY_ALLOWANCE_GPU2_0 0x3e8
|
||||
#define MC_LATENCY_ALLOWANCE_NVDEC_0 0x3d8
|
||||
#define MC_MLL_MPCORER_PTSA_RATE 0x44c
|
||||
#define MC_FTOP_PTSA_RATE 0x50c
|
||||
#define MC_EMEM_ARB_TIMING_RFCPB 0x6c0
|
||||
#define MC_EMEM_ARB_TIMING_CCDMW 0x6c4
|
||||
#define MC_EMEM_ARB_REFPB_HP_CTRL 0x6f0
|
||||
#define MC_EMEM_ARB_REFPB_BANK_CTRL 0x6f4
|
||||
#define MC_PTSA_GRANT_DECREMENT 0x960
|
||||
#define MC_EMEM_ARB_DHYST_CTRL 0xbcc
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 0xbd0
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 0xbd4
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 0xbd8
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 0xbdc
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 0xbe0
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 0xbe4
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 0xbe8
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xbec
|
||||
|
||||
#endif
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
|
@ -327,7 +326,6 @@ struct emc_timing {
|
|||
struct tegra_emc {
|
||||
struct device *dev;
|
||||
struct tegra_mc *mc;
|
||||
struct completion clk_handshake_complete;
|
||||
struct notifier_block clk_nb;
|
||||
struct clk *clk;
|
||||
void __iomem *regs;
|
||||
|
@ -374,52 +372,10 @@ static int emc_seq_update_timing(struct tegra_emc *emc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void emc_complete_clk_change(struct tegra_emc *emc)
|
||||
{
|
||||
struct emc_timing *timing = emc->new_timing;
|
||||
unsigned int dram_num;
|
||||
bool failed = false;
|
||||
int err;
|
||||
|
||||
/* re-enable auto-refresh */
|
||||
dram_num = tegra_mc_get_emem_device_count(emc->mc);
|
||||
writel_relaxed(EMC_REFCTRL_ENABLE_ALL(dram_num),
|
||||
emc->regs + EMC_REFCTRL);
|
||||
|
||||
/* restore auto-calibration */
|
||||
if (emc->vref_cal_toggle)
|
||||
writel_relaxed(timing->emc_auto_cal_interval,
|
||||
emc->regs + EMC_AUTO_CAL_INTERVAL);
|
||||
|
||||
/* restore dynamic self-refresh */
|
||||
if (timing->emc_cfg_dyn_self_ref) {
|
||||
emc->emc_cfg |= EMC_CFG_DYN_SREF_ENABLE;
|
||||
writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
|
||||
}
|
||||
|
||||
/* set number of clocks to wait after each ZQ command */
|
||||
if (emc->zcal_long)
|
||||
writel_relaxed(timing->emc_zcal_cnt_long,
|
||||
emc->regs + EMC_ZCAL_WAIT_CNT);
|
||||
|
||||
/* wait for writes to settle */
|
||||
udelay(2);
|
||||
|
||||
/* update restored timing */
|
||||
err = emc_seq_update_timing(emc);
|
||||
if (err)
|
||||
failed = true;
|
||||
|
||||
/* restore early ACK */
|
||||
mc_writel(emc->mc, emc->mc_override, MC_EMEM_ARB_OVERRIDE);
|
||||
|
||||
WRITE_ONCE(emc->bad_state, failed);
|
||||
}
|
||||
|
||||
static irqreturn_t tegra_emc_isr(int irq, void *data)
|
||||
{
|
||||
struct tegra_emc *emc = data;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
||||
u32 status;
|
||||
|
||||
status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask;
|
||||
|
@ -434,18 +390,6 @@ static irqreturn_t tegra_emc_isr(int irq, void *data)
|
|||
/* clear interrupts */
|
||||
writel_relaxed(status, emc->regs + EMC_INTSTATUS);
|
||||
|
||||
/* notify about EMC-CAR handshake completion */
|
||||
if (status & EMC_CLKCHANGE_COMPLETE_INT) {
|
||||
if (completion_done(&emc->clk_handshake_complete)) {
|
||||
dev_err_ratelimited(emc->dev,
|
||||
"bogus handshake interrupt\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
emc_complete_clk_change(emc);
|
||||
complete(&emc->clk_handshake_complete);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -801,29 +745,58 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
|
|||
*/
|
||||
mc_readl(emc->mc, MC_EMEM_ARB_OVERRIDE);
|
||||
|
||||
reinit_completion(&emc->clk_handshake_complete);
|
||||
|
||||
emc->new_timing = timing;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_complete_timing_change(struct tegra_emc *emc,
|
||||
unsigned long rate)
|
||||
{
|
||||
unsigned long timeout;
|
||||
struct emc_timing *timing = emc_find_timing(emc, rate);
|
||||
unsigned int dram_num;
|
||||
int err;
|
||||
u32 v;
|
||||
|
||||
timeout = wait_for_completion_timeout(&emc->clk_handshake_complete,
|
||||
msecs_to_jiffies(100));
|
||||
if (timeout == 0) {
|
||||
dev_err(emc->dev, "emc-car handshake failed\n");
|
||||
return -EIO;
|
||||
err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, v,
|
||||
v & EMC_CLKCHANGE_COMPLETE_INT,
|
||||
1, 100);
|
||||
if (err) {
|
||||
dev_err(emc->dev, "emc-car handshake timeout: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (READ_ONCE(emc->bad_state))
|
||||
return -EIO;
|
||||
/* re-enable auto-refresh */
|
||||
dram_num = tegra_mc_get_emem_device_count(emc->mc);
|
||||
writel_relaxed(EMC_REFCTRL_ENABLE_ALL(dram_num),
|
||||
emc->regs + EMC_REFCTRL);
|
||||
|
||||
return 0;
|
||||
/* restore auto-calibration */
|
||||
if (emc->vref_cal_toggle)
|
||||
writel_relaxed(timing->emc_auto_cal_interval,
|
||||
emc->regs + EMC_AUTO_CAL_INTERVAL);
|
||||
|
||||
/* restore dynamic self-refresh */
|
||||
if (timing->emc_cfg_dyn_self_ref) {
|
||||
emc->emc_cfg |= EMC_CFG_DYN_SREF_ENABLE;
|
||||
writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
|
||||
}
|
||||
|
||||
/* set number of clocks to wait after each ZQ command */
|
||||
if (emc->zcal_long)
|
||||
writel_relaxed(timing->emc_zcal_cnt_long,
|
||||
emc->regs + EMC_ZCAL_WAIT_CNT);
|
||||
|
||||
/* wait for writes to settle */
|
||||
udelay(2);
|
||||
|
||||
/* update restored timing */
|
||||
err = emc_seq_update_timing(emc);
|
||||
if (!err)
|
||||
emc->bad_state = false;
|
||||
|
||||
/* restore early ACK */
|
||||
mc_writel(emc->mc, emc->mc_override, MC_EMEM_ARB_OVERRIDE);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int emc_unprepare_timing_change(struct tegra_emc *emc,
|
||||
|
@ -1033,7 +1006,7 @@ static struct device_node *emc_find_node_by_ram_code(struct device *dev)
|
|||
|
||||
static int emc_setup_hw(struct tegra_emc *emc)
|
||||
{
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
|
||||
u32 fbio_cfg5, emc_cfg, emc_dbg;
|
||||
enum emc_dram_type dram_type;
|
||||
|
||||
|
@ -1275,11 +1248,11 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc)
|
|||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root,
|
||||
debugfs_create_file("available_rates", 0444, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_available_rates_fops);
|
||||
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("min_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_min_rate_fops);
|
||||
debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
|
||||
debugfs_create_file("max_rate", 0644, emc->debugfs.root,
|
||||
emc, &tegra_emc_debug_max_rate_fops);
|
||||
}
|
||||
|
||||
|
@ -1321,7 +1294,6 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
|||
if (!emc->mc)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
init_completion(&emc->clk_handshake_complete);
|
||||
emc->clk_nb.notifier_call = emc_clk_change_notify;
|
||||
emc->dev = &pdev->dev;
|
||||
|
||||
|
|
Loading…
Reference in New Issue