110de2114SJoseph Lo // SPDX-License-Identifier: GPL-2.0
210de2114SJoseph Lo /*
310de2114SJoseph Lo  * Copyright (c) 2020, NVIDIA CORPORATION.  All rights reserved.
410de2114SJoseph Lo  */
510de2114SJoseph Lo 
610de2114SJoseph Lo #include <linux/of_reserved_mem.h>
710de2114SJoseph Lo 
810de2114SJoseph Lo #include "tegra210-emc.h"
910de2114SJoseph Lo 
1010de2114SJoseph Lo #define TEGRA_EMC_MAX_FREQS		16
1110de2114SJoseph Lo 
tegra210_emc_table_device_init(struct reserved_mem * rmem,struct device * dev)1210de2114SJoseph Lo static int tegra210_emc_table_device_init(struct reserved_mem *rmem,
1310de2114SJoseph Lo 					  struct device *dev)
1410de2114SJoseph Lo {
1510de2114SJoseph Lo 	struct tegra210_emc *emc = dev_get_drvdata(dev);
160553d7b2SThierry Reding 	struct tegra210_emc_timing *timings;
170553d7b2SThierry Reding 	unsigned int i, count = 0;
1810de2114SJoseph Lo 
190553d7b2SThierry Reding 	timings = memremap(rmem->base, rmem->size, MEMREMAP_WB);
200553d7b2SThierry Reding 	if (!timings) {
2110de2114SJoseph Lo 		dev_err(dev, "failed to map EMC table\n");
2210de2114SJoseph Lo 		return -ENOMEM;
2310de2114SJoseph Lo 	}
2410de2114SJoseph Lo 
2510de2114SJoseph Lo 	for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) {
260553d7b2SThierry Reding 		if (timings[i].revision == 0)
2710de2114SJoseph Lo 			break;
2810de2114SJoseph Lo 
290553d7b2SThierry Reding 		count++;
3010de2114SJoseph Lo 	}
3110de2114SJoseph Lo 
320553d7b2SThierry Reding 	/* only the nominal and derated tables are expected */
330553d7b2SThierry Reding 	if (emc->derated) {
340553d7b2SThierry Reding 		dev_warn(dev, "excess EMC table '%s'\n", rmem->name);
350553d7b2SThierry Reding 		goto out;
360553d7b2SThierry Reding 	}
370553d7b2SThierry Reding 
380553d7b2SThierry Reding 	if (emc->nominal) {
390553d7b2SThierry Reding 		if (count != emc->num_timings) {
400553d7b2SThierry Reding 			dev_warn(dev, "%u derated vs. %u nominal entries\n",
410553d7b2SThierry Reding 				 count, emc->num_timings);
420553d7b2SThierry Reding 			memunmap(timings);
430553d7b2SThierry Reding 			return -EINVAL;
440553d7b2SThierry Reding 		}
450553d7b2SThierry Reding 
460553d7b2SThierry Reding 		emc->derated = timings;
470553d7b2SThierry Reding 	} else {
480553d7b2SThierry Reding 		emc->num_timings = count;
490553d7b2SThierry Reding 		emc->nominal = timings;
500553d7b2SThierry Reding 	}
510553d7b2SThierry Reding 
520553d7b2SThierry Reding out:
530553d7b2SThierry Reding 	/* keep track of which table this is */
540553d7b2SThierry Reding 	rmem->priv = timings;
550553d7b2SThierry Reding 
5610de2114SJoseph Lo 	return 0;
5710de2114SJoseph Lo }
5810de2114SJoseph Lo 
tegra210_emc_table_device_release(struct reserved_mem * rmem,struct device * dev)5910de2114SJoseph Lo static void tegra210_emc_table_device_release(struct reserved_mem *rmem,
6010de2114SJoseph Lo 					      struct device *dev)
6110de2114SJoseph Lo {
620553d7b2SThierry Reding 	struct tegra210_emc_timing *timings = rmem->priv;
6310de2114SJoseph Lo 	struct tegra210_emc *emc = dev_get_drvdata(dev);
6410de2114SJoseph Lo 
650553d7b2SThierry Reding 	if ((emc->nominal && timings != emc->nominal) &&
660553d7b2SThierry Reding 	    (emc->derated && timings != emc->derated))
670553d7b2SThierry Reding 		dev_warn(dev, "trying to release unassigned EMC table '%s'\n",
680553d7b2SThierry Reding 			 rmem->name);
690553d7b2SThierry Reding 
700553d7b2SThierry Reding 	memunmap(timings);
7110de2114SJoseph Lo }
7210de2114SJoseph Lo 
7310de2114SJoseph Lo static const struct reserved_mem_ops tegra210_emc_table_ops = {
7410de2114SJoseph Lo 	.device_init = tegra210_emc_table_device_init,
7510de2114SJoseph Lo 	.device_release = tegra210_emc_table_device_release,
7610de2114SJoseph Lo };
7710de2114SJoseph Lo 
tegra210_emc_table_init(struct reserved_mem * rmem)7810de2114SJoseph Lo static int tegra210_emc_table_init(struct reserved_mem *rmem)
7910de2114SJoseph Lo {
8010de2114SJoseph Lo 	pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n", &rmem->base,
8110de2114SJoseph Lo 		 (unsigned long)rmem->size);
8210de2114SJoseph Lo 
8310de2114SJoseph Lo 	rmem->ops = &tegra210_emc_table_ops;
8410de2114SJoseph Lo 
8510de2114SJoseph Lo 	return 0;
8610de2114SJoseph Lo }
8710de2114SJoseph Lo RESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table",
8810de2114SJoseph Lo 		       tegra210_emc_table_init);
89