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