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 
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 
250553d7b2SThierry Reding 	count = 0;
2610de2114SJoseph Lo 
2710de2114SJoseph Lo 	for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) {
280553d7b2SThierry Reding 		if (timings[i].revision == 0)
2910de2114SJoseph Lo 			break;
3010de2114SJoseph Lo 
310553d7b2SThierry Reding 		count++;
3210de2114SJoseph Lo 	}
3310de2114SJoseph Lo 
340553d7b2SThierry Reding 	/* only the nominal and derated tables are expected */
350553d7b2SThierry Reding 	if (emc->derated) {
360553d7b2SThierry Reding 		dev_warn(dev, "excess EMC table '%s'\n", rmem->name);
370553d7b2SThierry Reding 		goto out;
380553d7b2SThierry Reding 	}
390553d7b2SThierry Reding 
400553d7b2SThierry Reding 	if (emc->nominal) {
410553d7b2SThierry Reding 		if (count != emc->num_timings) {
420553d7b2SThierry Reding 			dev_warn(dev, "%u derated vs. %u nominal entries\n",
430553d7b2SThierry Reding 				 count, emc->num_timings);
440553d7b2SThierry Reding 			memunmap(timings);
450553d7b2SThierry Reding 			return -EINVAL;
460553d7b2SThierry Reding 		}
470553d7b2SThierry Reding 
480553d7b2SThierry Reding 		emc->derated = timings;
490553d7b2SThierry Reding 	} else {
500553d7b2SThierry Reding 		emc->num_timings = count;
510553d7b2SThierry Reding 		emc->nominal = timings;
520553d7b2SThierry Reding 	}
530553d7b2SThierry Reding 
540553d7b2SThierry Reding out:
550553d7b2SThierry Reding 	/* keep track of which table this is */
560553d7b2SThierry Reding 	rmem->priv = timings;
570553d7b2SThierry Reding 
5810de2114SJoseph Lo 	return 0;
5910de2114SJoseph Lo }
6010de2114SJoseph Lo 
6110de2114SJoseph Lo static void tegra210_emc_table_device_release(struct reserved_mem *rmem,
6210de2114SJoseph Lo 					      struct device *dev)
6310de2114SJoseph Lo {
640553d7b2SThierry Reding 	struct tegra210_emc_timing *timings = rmem->priv;
6510de2114SJoseph Lo 	struct tegra210_emc *emc = dev_get_drvdata(dev);
6610de2114SJoseph Lo 
670553d7b2SThierry Reding 	if ((emc->nominal && timings != emc->nominal) &&
680553d7b2SThierry Reding 	    (emc->derated && timings != emc->derated))
690553d7b2SThierry Reding 		dev_warn(dev, "trying to release unassigned EMC table '%s'\n",
700553d7b2SThierry Reding 			 rmem->name);
710553d7b2SThierry Reding 
720553d7b2SThierry Reding 	memunmap(timings);
7310de2114SJoseph Lo }
7410de2114SJoseph Lo 
7510de2114SJoseph Lo static const struct reserved_mem_ops tegra210_emc_table_ops = {
7610de2114SJoseph Lo 	.device_init = tegra210_emc_table_device_init,
7710de2114SJoseph Lo 	.device_release = tegra210_emc_table_device_release,
7810de2114SJoseph Lo };
7910de2114SJoseph Lo 
8010de2114SJoseph Lo static int tegra210_emc_table_init(struct reserved_mem *rmem)
8110de2114SJoseph Lo {
8210de2114SJoseph Lo 	pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n", &rmem->base,
8310de2114SJoseph Lo 		 (unsigned long)rmem->size);
8410de2114SJoseph Lo 
8510de2114SJoseph Lo 	rmem->ops = &tegra210_emc_table_ops;
8610de2114SJoseph Lo 
8710de2114SJoseph Lo 	return 0;
8810de2114SJoseph Lo }
8910de2114SJoseph Lo RESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table",
9010de2114SJoseph Lo 		       tegra210_emc_table_init);
91