162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "tegra210-emc.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define TEGRA_EMC_MAX_FREQS 16 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic int tegra210_emc_table_device_init(struct reserved_mem *rmem, 1362306a36Sopenharmony_ci struct device *dev) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct tegra210_emc *emc = dev_get_drvdata(dev); 1662306a36Sopenharmony_ci struct tegra210_emc_timing *timings; 1762306a36Sopenharmony_ci unsigned int i, count = 0; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci timings = memremap(rmem->base, rmem->size, MEMREMAP_WB); 2062306a36Sopenharmony_ci if (!timings) { 2162306a36Sopenharmony_ci dev_err(dev, "failed to map EMC table\n"); 2262306a36Sopenharmony_ci return -ENOMEM; 2362306a36Sopenharmony_ci } 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) { 2662306a36Sopenharmony_ci if (timings[i].revision == 0) 2762306a36Sopenharmony_ci break; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci count++; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* only the nominal and derated tables are expected */ 3362306a36Sopenharmony_ci if (emc->derated) { 3462306a36Sopenharmony_ci dev_warn(dev, "excess EMC table '%s'\n", rmem->name); 3562306a36Sopenharmony_ci goto out; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (emc->nominal) { 3962306a36Sopenharmony_ci if (count != emc->num_timings) { 4062306a36Sopenharmony_ci dev_warn(dev, "%u derated vs. %u nominal entries\n", 4162306a36Sopenharmony_ci count, emc->num_timings); 4262306a36Sopenharmony_ci memunmap(timings); 4362306a36Sopenharmony_ci return -EINVAL; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci emc->derated = timings; 4762306a36Sopenharmony_ci } else { 4862306a36Sopenharmony_ci emc->num_timings = count; 4962306a36Sopenharmony_ci emc->nominal = timings; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ciout: 5362306a36Sopenharmony_ci /* keep track of which table this is */ 5462306a36Sopenharmony_ci rmem->priv = timings; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void tegra210_emc_table_device_release(struct reserved_mem *rmem, 6062306a36Sopenharmony_ci struct device *dev) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct tegra210_emc_timing *timings = rmem->priv; 6362306a36Sopenharmony_ci struct tegra210_emc *emc = dev_get_drvdata(dev); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if ((emc->nominal && timings != emc->nominal) && 6662306a36Sopenharmony_ci (emc->derated && timings != emc->derated)) 6762306a36Sopenharmony_ci dev_warn(dev, "trying to release unassigned EMC table '%s'\n", 6862306a36Sopenharmony_ci rmem->name); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci memunmap(timings); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct reserved_mem_ops tegra210_emc_table_ops = { 7462306a36Sopenharmony_ci .device_init = tegra210_emc_table_device_init, 7562306a36Sopenharmony_ci .device_release = tegra210_emc_table_device_release, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int tegra210_emc_table_init(struct reserved_mem *rmem) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n", &rmem->base, 8162306a36Sopenharmony_ci (unsigned long)rmem->size); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci rmem->ops = &tegra210_emc_table_ops; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ciRESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table", 8862306a36Sopenharmony_ci tegra210_emc_table_init); 89