2019-05-29 16:21:36 +08:00
|
|
|
// 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);
|
2020-04-04 02:03:15 +08:00
|
|
|
struct tegra210_emc_timing *timings;
|
|
|
|
unsigned int i, count = 0;
|
2019-05-29 16:21:36 +08:00
|
|
|
|
2020-04-04 02:03:15 +08:00
|
|
|
timings = memremap(rmem->base, rmem->size, MEMREMAP_WB);
|
|
|
|
if (!timings) {
|
2019-05-29 16:21:36 +08:00
|
|
|
dev_err(dev, "failed to map EMC table\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2020-04-04 02:03:15 +08:00
|
|
|
count = 0;
|
2019-05-29 16:21:36 +08:00
|
|
|
|
|
|
|
for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) {
|
2020-04-04 02:03:15 +08:00
|
|
|
if (timings[i].revision == 0)
|
2019-05-29 16:21:36 +08:00
|
|
|
break;
|
|
|
|
|
2020-04-04 02:03:15 +08:00
|
|
|
count++;
|
2019-05-29 16:21:36 +08:00
|
|
|
}
|
|
|
|
|
2020-04-04 02:03:15 +08:00
|
|
|
/* 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;
|
|
|
|
|
2019-05-29 16:21:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tegra210_emc_table_device_release(struct reserved_mem *rmem,
|
|
|
|
struct device *dev)
|
|
|
|
{
|
2020-04-04 02:03:15 +08:00
|
|
|
struct tegra210_emc_timing *timings = rmem->priv;
|
2019-05-29 16:21:36 +08:00
|
|
|
struct tegra210_emc *emc = dev_get_drvdata(dev);
|
|
|
|
|
2020-04-04 02:03:15 +08:00
|
|
|
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);
|
2019-05-29 16:21:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|