1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/of_reserved_mem.h> |
7 | |
8 | #include "tegra210-emc.h" |
9 | |
10 | #define TEGRA_EMC_MAX_FREQS 16 |
11 | |
12 | static int tegra210_emc_table_device_init(struct reserved_mem *rmem, |
13 | struct device *dev) |
14 | { |
15 | struct tegra210_emc *emc = dev_get_drvdata(dev); |
16 | struct tegra210_emc_timing *timings; |
17 | unsigned int i, count = 0; |
18 | |
19 | timings = memremap(offset: rmem->base, size: rmem->size, flags: MEMREMAP_WB); |
20 | if (!timings) { |
21 | dev_err(dev, "failed to map EMC table\n" ); |
22 | return -ENOMEM; |
23 | } |
24 | |
25 | for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) { |
26 | if (timings[i].revision == 0) |
27 | break; |
28 | |
29 | count++; |
30 | } |
31 | |
32 | /* only the nominal and derated tables are expected */ |
33 | if (emc->derated) { |
34 | dev_warn(dev, "excess EMC table '%s'\n" , rmem->name); |
35 | goto out; |
36 | } |
37 | |
38 | if (emc->nominal) { |
39 | if (count != emc->num_timings) { |
40 | dev_warn(dev, "%u derated vs. %u nominal entries\n" , |
41 | count, emc->num_timings); |
42 | memunmap(addr: timings); |
43 | return -EINVAL; |
44 | } |
45 | |
46 | emc->derated = timings; |
47 | } else { |
48 | emc->num_timings = count; |
49 | emc->nominal = timings; |
50 | } |
51 | |
52 | out: |
53 | /* keep track of which table this is */ |
54 | rmem->priv = timings; |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static void tegra210_emc_table_device_release(struct reserved_mem *rmem, |
60 | struct device *dev) |
61 | { |
62 | struct tegra210_emc_timing *timings = rmem->priv; |
63 | struct tegra210_emc *emc = dev_get_drvdata(dev); |
64 | |
65 | if ((emc->nominal && timings != emc->nominal) && |
66 | (emc->derated && timings != emc->derated)) |
67 | dev_warn(dev, "trying to release unassigned EMC table '%s'\n" , |
68 | rmem->name); |
69 | |
70 | memunmap(addr: timings); |
71 | } |
72 | |
73 | static const struct reserved_mem_ops tegra210_emc_table_ops = { |
74 | .device_init = tegra210_emc_table_device_init, |
75 | .device_release = tegra210_emc_table_device_release, |
76 | }; |
77 | |
78 | static int tegra210_emc_table_init(struct reserved_mem *rmem) |
79 | { |
80 | pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n" , &rmem->base, |
81 | (unsigned long)rmem->size); |
82 | |
83 | rmem->ops = &tegra210_emc_table_ops; |
84 | |
85 | return 0; |
86 | } |
87 | RESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table" , |
88 | tegra210_emc_table_init); |
89 | |