File Index
Files and directories
Sign in
FILE

tegra186-emc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/memory/tegra/tegra186-emc.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2019-2025 NVIDIA CORPORATION.  All rights reserved.
  4 */
  5
  6#include <linux/clk.h>
  7#include <linux/debugfs.h>
  8#include <linux/module.h>
  9#include <linux/mod_devicetable.h>
 10#include <linux/of_platform.h>
 11#include <linux/platform_device.h>
 12
 13#include <soc/tegra/bpmp.h>
 14#include "mc.h"
 15
 16struct tegra186_emc_dvfs {
 17	unsigned long latency;
 18	unsigned long rate;
 19};
 20
 21struct tegra186_emc {
 22	struct tegra_bpmp *bpmp;
 23	struct device *dev;
 24	struct clk *clk;
 25
 26	struct tegra186_emc_dvfs *dvfs;
 27	unsigned int num_dvfs;
 28
 29	struct {
 30		struct dentry *root;
 31		unsigned long min_rate;
 32		unsigned long max_rate;
 33	} debugfs;
 34
 35	struct icc_provider provider;
 36};
 37
 38/*
 39 * debugfs interface
 40 *
 41 * The memory controller driver exposes some files in debugfs that can be used
 42 * to control the EMC frequency. The top-level directory can be found here:
 43 *
 44 *   /sys/kernel/debug/emc
 45 *
 46 * It contains the following files:
 47 *
 48 *   - available_rates: This file contains a list of valid, space-separated
 49 *     EMC frequencies.
 50 *
 51 *   - min_rate: Writing a value to this file sets the given frequency as the
 52 *       floor of the permitted range. If this is higher than the currently
 53 *       configured EMC frequency, this will cause the frequency to be
 54 *       increased so that it stays within the valid range.
 55 *
 56 *   - max_rate: Similarily to the min_rate file, writing a value to this file
 57 *       sets the given frequency as the ceiling of the permitted range. If
 58 *       the value is lower than the currently configured EMC frequency, this
 59 *       will cause the frequency to be decreased so that it stays within the
 60 *       valid range.
 61 */
 62
 63static bool tegra186_emc_validate_rate(struct tegra186_emc *emc,
 64				       unsigned long rate)
 65{
 66	unsigned int i;
 67
 68	for (i = 0; i < emc->num_dvfs; i++)
 69		if (rate == emc->dvfs[i].rate)
 70			return true;
 71
 72	return false;
 73}
 74
 75static int tegra186_emc_debug_available_rates_show(struct seq_file *s,
 76						   void *data)
 77{
 78	struct tegra186_emc *emc = s->private;
 79	const char *prefix = "";
 80	unsigned int i;
 81
 82	for (i = 0; i < emc->num_dvfs; i++) {
 83		seq_printf(s, "%s%lu", prefix, emc->dvfs[i].rate);
 84		prefix = " ";
 85	}
 86
 87	seq_puts(s, "\n");
 88
 89	return 0;
 90}
 91DEFINE_SHOW_ATTRIBUTE(tegra186_emc_debug_available_rates);
 92
 93static int tegra186_emc_debug_min_rate_get(void *data, u64 *rate)
 94{
 95	struct tegra186_emc *emc = data;
 96
 97	*rate = emc->debugfs.min_rate;
 98
 99	return 0;
100}
101
102static int tegra186_emc_debug_min_rate_set(void *data, u64 rate)
103{
104	struct tegra186_emc *emc = data;
105	int err;
106
107	if (!tegra186_emc_validate_rate(emc, rate))
108		return -EINVAL;
109
110	err = clk_set_min_rate(emc->clk, rate);
111	if (err < 0)
112		return err;
113
114	emc->debugfs.min_rate = rate;
115
116	return 0;
117}
118
119DEFINE_DEBUGFS_ATTRIBUTE(tegra186_emc_debug_min_rate_fops,
120			  tegra186_emc_debug_min_rate_get,
121			  tegra186_emc_debug_min_rate_set, "%llu\n");
122
123static int tegra186_emc_debug_max_rate_get(void *data, u64 *rate)
124{
125	struct tegra186_emc *emc = data;
126
127	*rate = emc->debugfs.max_rate;
128
129	return 0;
130}
131
132static int tegra186_emc_debug_max_rate_set(void *data, u64 rate)
133{
134	struct tegra186_emc *emc = data;
135	int err;
136
137	if (!tegra186_emc_validate_rate(emc, rate))
138		return -EINVAL;
139
140	err = clk_set_max_rate(emc->clk, rate);
141	if (err < 0)
142		return err;
143
144	emc->debugfs.max_rate = rate;
145
146	return 0;
147}
148
149DEFINE_DEBUGFS_ATTRIBUTE(tegra186_emc_debug_max_rate_fops,
150			  tegra186_emc_debug_max_rate_get,
151			  tegra186_emc_debug_max_rate_set, "%llu\n");
152
153static int tegra186_emc_get_emc_dvfs_latency(struct tegra186_emc *emc)
154{
155	struct mrq_emc_dvfs_latency_response response;
156	struct tegra_bpmp_message msg;
157	unsigned int i;
158	int err;
159
160	memset(&msg, 0, sizeof(msg));
161	msg.mrq = MRQ_EMC_DVFS_LATENCY;
162	msg.tx.data = NULL;
163	msg.tx.size = 0;
164	msg.rx.data = &response;
165	msg.rx.size = sizeof(response);
166
167	err = tegra_bpmp_transfer(emc->bpmp, &msg);
168	if (err < 0) {
169		dev_err(emc->dev, "failed to EMC DVFS pairs: %d\n", err);
170		return err;
171	}
172	if (msg.rx.ret < 0) {
173		dev_err(emc->dev, "EMC DVFS MRQ failed: %d (BPMP error code)\n", msg.rx.ret);
174		return -EINVAL;
175	}
176
177	emc->debugfs.min_rate = ULONG_MAX;
178	emc->debugfs.max_rate = 0;
179
180	emc->num_dvfs = response.num_pairs;
181
182	emc->dvfs = devm_kmalloc_array(emc->dev, emc->num_dvfs, sizeof(*emc->dvfs), GFP_KERNEL);
183	if (!emc->dvfs)
184		return -ENOMEM;
185
186	dev_dbg(emc->dev, "%u DVFS pairs:\n", emc->num_dvfs);
187
188	for (i = 0; i < emc->num_dvfs; i++) {
189		emc->dvfs[i].rate = response.pairs[i].freq * 1000;
190		emc->dvfs[i].latency = response.pairs[i].latency;
191
192		if (emc->dvfs[i].rate < emc->debugfs.min_rate)
193			emc->debugfs.min_rate = emc->dvfs[i].rate;
194
195		if (emc->dvfs[i].rate > emc->debugfs.max_rate)
196			emc->debugfs.max_rate = emc->dvfs[i].rate;
197
198		dev_dbg(emc->dev, "  %2u: %lu Hz -> %lu us\n", i,
199			emc->dvfs[i].rate, emc->dvfs[i].latency);
200	}
201
202	err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate, emc->debugfs.max_rate);
203	if (err < 0) {
204		dev_err(emc->dev, "failed to set rate range [%lu-%lu] for %pC\n",
205			emc->debugfs.min_rate, emc->debugfs.max_rate, emc->clk);
206		return err;
207	}
208
209	emc->debugfs.root = debugfs_create_dir("emc", NULL);
210	debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc,
211			    &tegra186_emc_debug_available_rates_fops);
212	debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc,
213			    &tegra186_emc_debug_min_rate_fops);
214	debugfs_create_file("max_rate", 0644, emc->debugfs.root, emc,
215			    &tegra186_emc_debug_max_rate_fops);
216
217	return 0;
218}
219
220/*
221 * tegra186_emc_icc_set_bw() - Set BW api for EMC provider
222 * @src: ICC node for External Memory Controller (EMC)
223 * @dst: ICC node for External Memory (DRAM)
224 *
225 * Do nothing here as info to BPMP-FW is now passed in the BW set function
226 * of the MC driver. BPMP-FW sets the final Freq based on the passed values.
227 */
228static int tegra186_emc_icc_set_bw(struct icc_node *src, struct icc_node *dst)
229{
230	return 0;
231}
232
233static struct icc_node *
234tegra186_emc_of_icc_xlate(const struct of_phandle_args *spec, void *data)
235{
236	struct icc_provider *provider = data;
237	struct icc_node *node;
238
239	/* External Memory is the only possible ICC route */
240	list_for_each_entry(node, &provider->nodes, node_list) {
241		if (node->id != TEGRA_ICC_EMEM)
242			continue;
243
244		return node;
245	}
246
247	return ERR_PTR(-EPROBE_DEFER);
248}
249
250static int tegra186_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak)
251{
252	*avg = 0;
253	*peak = 0;
254
255	return 0;
256}
257
258static int tegra186_emc_interconnect_init(struct tegra186_emc *emc)
259{
260	struct tegra_mc *mc = dev_get_drvdata(emc->dev->parent);
261	const struct tegra_mc_soc *soc = mc->soc;
262	struct icc_node *node;
263	int err;
264
265	emc->provider.dev = emc->dev;
266	emc->provider.set = tegra186_emc_icc_set_bw;
267	emc->provider.data = &emc->provider;
268	emc->provider.aggregate = soc->icc_ops->aggregate;
269	emc->provider.xlate = tegra186_emc_of_icc_xlate;
270	emc->provider.get_bw = tegra186_emc_icc_get_init_bw;
271
272	icc_provider_init(&emc->provider);
273
274	/* create External Memory Controller node */
275	node = icc_node_create(TEGRA_ICC_EMC);
276	if (IS_ERR(node))
277		return PTR_ERR(node);
278
279	node->name = "External Memory Controller";
280	icc_node_add(node, &emc->provider);
281
282	/* link External Memory Controller to External Memory (DRAM) */
283	err = icc_link_create(node, TEGRA_ICC_EMEM);
284	if (err)
285		goto remove_nodes;
286
287	/* create External Memory node */
288	node = icc_node_create(TEGRA_ICC_EMEM);
289	if (IS_ERR(node)) {
290		err = PTR_ERR(node);
291		goto remove_nodes;
292	}
293
294	node->name = "External Memory (DRAM)";
295	icc_node_add(node, &emc->provider);
296
297	err = icc_provider_register(&emc->provider);
298	if (err)
299		goto remove_nodes;
300
301	return 0;
302
303remove_nodes:
304	icc_nodes_remove(&emc->provider);
305
306	return dev_err_probe(emc->dev, err, "failed to initialize ICC\n");
307}
308
309static int tegra186_emc_probe(struct platform_device *pdev)
310{
311	struct tegra_mc *mc = dev_get_drvdata(pdev->dev.parent);
312	struct tegra186_emc *emc;
313	int err;
314
315	emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL);
316	if (!emc)
317		return -ENOMEM;
318
319	emc->bpmp = tegra_bpmp_get(&pdev->dev);
320	if (IS_ERR(emc->bpmp))
321		return dev_err_probe(&pdev->dev, PTR_ERR(emc->bpmp),
322				     "failed to get BPMP\n");
323
324	emc->clk = devm_clk_get(&pdev->dev, "emc");
325	if (IS_ERR(emc->clk)) {
326		err = dev_err_probe(&pdev->dev, PTR_ERR(emc->clk),
327				    "failed to get EMC clock\n");
328		goto put_bpmp;
329	}
330
331	platform_set_drvdata(pdev, emc);
332	emc->dev = &pdev->dev;
333
334	if (tegra_bpmp_mrq_is_supported(emc->bpmp, MRQ_EMC_DVFS_LATENCY)) {
335		err = tegra186_emc_get_emc_dvfs_latency(emc);
336		if (err)
337			goto put_bpmp;
338	}
339
340	if (mc && mc->soc->icc_ops) {
341		if (tegra_bpmp_mrq_is_supported(emc->bpmp, MRQ_BWMGR_INT)) {
342			mc->bwmgr_mrq_supported = true;
343
344			/*
345			 * MC driver probe can't get BPMP reference as it gets probed
346			 * earlier than BPMP. So, save the BPMP ref got from the EMC
347			 * DT node in the mc->bpmp and use it in MC's icc_set hook.
348			 */
349			mc->bpmp = emc->bpmp;
350			barrier();
351		}
352
353		/*
354		 * Initialize the ICC even if BPMP-FW doesn't support 'MRQ_BWMGR_INT'.
355		 * Use the flag 'mc->bwmgr_mrq_supported' within MC driver and return
356		 * EINVAL instead of passing the request to BPMP-FW later when the BW
357		 * request is made by client with 'icc_set_bw()' call.
358		 */
359		err = tegra186_emc_interconnect_init(emc);
360		if (err) {
361			mc->bpmp = NULL;
362			goto put_bpmp;
363		}
364	}
365
366	return 0;
367
368put_bpmp:
369	tegra_bpmp_put(emc->bpmp);
370	return err;
371}
372
373static void tegra186_emc_remove(struct platform_device *pdev)
374{
375	struct tegra_mc *mc = dev_get_drvdata(pdev->dev.parent);
376	struct tegra186_emc *emc = platform_get_drvdata(pdev);
377
378	debugfs_remove_recursive(emc->debugfs.root);
379
380	mc->bpmp = NULL;
381	tegra_bpmp_put(emc->bpmp);
382}
383
384static const struct of_device_id tegra186_emc_of_match[] = {
385#if defined(CONFIG_ARCH_TEGRA_186_SOC)
386	{ .compatible = "nvidia,tegra186-emc" },
387#endif
388#if defined(CONFIG_ARCH_TEGRA_194_SOC)
389	{ .compatible = "nvidia,tegra194-emc" },
390#endif
391#if defined(CONFIG_ARCH_TEGRA_234_SOC)
392	{ .compatible = "nvidia,tegra234-emc" },
393#endif
394#if defined(CONFIG_ARCH_TEGRA_264_SOC)
395	{ .compatible = "nvidia,tegra264-emc" },
396#endif
397	{ /* sentinel */ }
398};
399MODULE_DEVICE_TABLE(of, tegra186_emc_of_match);
400
401static struct platform_driver tegra186_emc_driver = {
402	.driver = {
403		.name = "tegra186-emc",
404		.of_match_table = tegra186_emc_of_match,
405		.suppress_bind_attrs = true,
406		.sync_state = icc_sync_state,
407	},
408	.probe = tegra186_emc_probe,
409	.remove = tegra186_emc_remove,
410};
411module_platform_driver(tegra186_emc_driver);
412
413MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
414MODULE_DESCRIPTION("NVIDIA Tegra186 External Memory Controller driver");
~/ Read-only file preview.