// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. */ #include #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; } 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);