summaryrefslogtreecommitdiff
path: root/drivers/memory/tegra/tegra210-emc-table.c
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2020-04-03 21:03:15 +0300
committerThierry Reding <treding@nvidia.com>2020-06-22 14:54:57 +0300
commit0553d7b204ef48091e76753175d21d0c30b7ae2a (patch)
treeb2ad4e7053be824ff1d5346d3eb4ee2865bf46ce /drivers/memory/tegra/tegra210-emc-table.c
parent9b9d8632f51f3609dfdfe8efc3c1e4e773c6c385 (diff)
downloadlinux-0553d7b204ef48091e76753175d21d0c30b7ae2a.tar.xz
memory: tegra: Support derated timings on Tegra210
Derated timings are used to ensure that the memory chips keep operating correctly at high temperatures. This adds code to support polling of the chip operating state when high temperatures are measured on the chip and change the refresh mode accordingly. Under very high temperatures, the driver will switch to the derated tables to ensure proper operation of the memory chips. Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/memory/tegra/tegra210-emc-table.c')
-rw-r--r--drivers/memory/tegra/tegra210-emc-table.c45
1 files changed, 38 insertions, 7 deletions
diff --git a/drivers/memory/tegra/tegra210-emc-table.c b/drivers/memory/tegra/tegra210-emc-table.c
index a5ab6e9e743a..3e0598363b87 100644
--- a/drivers/memory/tegra/tegra210-emc-table.c
+++ b/drivers/memory/tegra/tegra210-emc-table.c
@@ -13,32 +13,63 @@ static int tegra210_emc_table_device_init(struct reserved_mem *rmem,
struct device *dev)
{
struct tegra210_emc *emc = dev_get_drvdata(dev);
- unsigned int i;
+ struct tegra210_emc_timing *timings;
+ unsigned int i, count = 0;
- emc->timings = memremap(rmem->base, rmem->size, MEMREMAP_WB);
- if (!emc->timings) {
+ timings = memremap(rmem->base, rmem->size, MEMREMAP_WB);
+ if (!timings) {
dev_err(dev, "failed to map EMC table\n");
return -ENOMEM;
}
- emc->num_timings = 0;
+ count = 0;
for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) {
- if (emc->timings[i].revision == 0)
+ if (timings[i].revision == 0)
break;
- emc->num_timings++;
+ 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);
- memunmap(emc->timings);
+ 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 = {