Skip to content

Commit 9a38cb2

Browse files
Sumit Guptathierryreding
authored andcommitted
memory: tegra: Add interconnect support for DRAM scaling in Tegra234
Add Interconnect framework support to dynamically set the DRAM bandwidth from different clients. Both the MC and EMC drivers are added as ICC providers. The path for any request is: MC-Client[1-n] -> MC -> EMC -> EMEM/DRAM MC client's request for bandwidth will go to the MC driver which passes the client request info like BPMP Client ID, Client type and the Bandwidth to the BPMP-FW. The final DRAM freq to achieve the requested bandwidth is set by the BPMP-FW based on the passed parameters. Signed-off-by: Sumit Gupta <sumitg@nvidia.com> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Signed-off-by: Thierry Reding <treding@nvidia.com>
1 parent f382b1f commit 9a38cb2

5 files changed

Lines changed: 347 additions & 1 deletion

File tree

drivers/memory/tegra/mc.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/platform_device.h>
1616
#include <linux/slab.h>
1717
#include <linux/sort.h>
18+
#include <linux/tegra-icc.h>
1819

1920
#include <soc/tegra/fuse.h>
2021

@@ -792,6 +793,8 @@ static int tegra_mc_interconnect_setup(struct tegra_mc *mc)
792793
mc->provider.data = &mc->provider;
793794
mc->provider.set = mc->soc->icc_ops->set;
794795
mc->provider.aggregate = mc->soc->icc_ops->aggregate;
796+
mc->provider.get_bw = mc->soc->icc_ops->get_bw;
797+
mc->provider.xlate = mc->soc->icc_ops->xlate;
795798
mc->provider.xlate_extended = mc->soc->icc_ops->xlate_extended;
796799

797800
icc_provider_init(&mc->provider);
@@ -824,6 +827,8 @@ static int tegra_mc_interconnect_setup(struct tegra_mc *mc)
824827
err = icc_link_create(node, TEGRA_ICC_MC);
825828
if (err)
826829
goto remove_nodes;
830+
831+
node->data = (struct tegra_mc_client *)&(mc->soc->clients[i]);
827832
}
828833

829834
err = icc_provider_register(&mc->provider);

drivers/memory/tegra/tegra186-emc.c

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
#include <linux/debugfs.h>
88
#include <linux/module.h>
99
#include <linux/mod_devicetable.h>
10+
#include <linux/of_platform.h>
1011
#include <linux/platform_device.h>
1112

1213
#include <soc/tegra/bpmp.h>
14+
#include "mc.h"
1315

1416
struct tegra186_emc_dvfs {
1517
unsigned long latency;
@@ -29,8 +31,15 @@ struct tegra186_emc {
2931
unsigned long min_rate;
3032
unsigned long max_rate;
3133
} debugfs;
34+
35+
struct icc_provider provider;
3236
};
3337

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+
3443
/*
3544
* debugfs interface
3645
*
@@ -146,8 +155,102 @@ DEFINE_DEBUGFS_ATTRIBUTE(tegra186_emc_debug_max_rate_fops,
146155
tegra186_emc_debug_max_rate_get,
147156
tegra186_emc_debug_max_rate_set, "%llu\n");
148157

158+
/*
159+
* tegra_emc_icc_set_bw() - Set BW api for EMC provider
160+
* @src: ICC node for External Memory Controller (EMC)
161+
* @dst: ICC node for External Memory (DRAM)
162+
*
163+
* Do nothing here as info to BPMP-FW is now passed in the BW set function
164+
* of the MC driver. BPMP-FW sets the final Freq based on the passed values.
165+
*/
166+
static int tegra_emc_icc_set_bw(struct icc_node *src, struct icc_node *dst)
167+
{
168+
return 0;
169+
}
170+
171+
static struct icc_node *
172+
tegra_emc_of_icc_xlate(struct of_phandle_args *spec, void *data)
173+
{
174+
struct icc_provider *provider = data;
175+
struct icc_node *node;
176+
177+
/* External Memory is the only possible ICC route */
178+
list_for_each_entry(node, &provider->nodes, node_list) {
179+
if (node->id != TEGRA_ICC_EMEM)
180+
continue;
181+
182+
return node;
183+
}
184+
185+
return ERR_PTR(-EPROBE_DEFER);
186+
}
187+
188+
static int tegra_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak)
189+
{
190+
*avg = 0;
191+
*peak = 0;
192+
193+
return 0;
194+
}
195+
196+
static int tegra_emc_interconnect_init(struct tegra186_emc *emc)
197+
{
198+
struct tegra_mc *mc = dev_get_drvdata(emc->dev->parent);
199+
const struct tegra_mc_soc *soc = mc->soc;
200+
struct icc_node *node;
201+
int err;
202+
203+
emc->provider.dev = emc->dev;
204+
emc->provider.set = tegra_emc_icc_set_bw;
205+
emc->provider.data = &emc->provider;
206+
emc->provider.aggregate = soc->icc_ops->aggregate;
207+
emc->provider.xlate = tegra_emc_of_icc_xlate;
208+
emc->provider.get_bw = tegra_emc_icc_get_init_bw;
209+
210+
icc_provider_init(&emc->provider);
211+
212+
/* create External Memory Controller node */
213+
node = icc_node_create(TEGRA_ICC_EMC);
214+
if (IS_ERR(node)) {
215+
err = PTR_ERR(node);
216+
goto err_msg;
217+
}
218+
219+
node->name = "External Memory Controller";
220+
icc_node_add(node, &emc->provider);
221+
222+
/* link External Memory Controller to External Memory (DRAM) */
223+
err = icc_link_create(node, TEGRA_ICC_EMEM);
224+
if (err)
225+
goto remove_nodes;
226+
227+
/* create External Memory node */
228+
node = icc_node_create(TEGRA_ICC_EMEM);
229+
if (IS_ERR(node)) {
230+
err = PTR_ERR(node);
231+
goto remove_nodes;
232+
}
233+
234+
node->name = "External Memory (DRAM)";
235+
icc_node_add(node, &emc->provider);
236+
237+
err = icc_provider_register(&emc->provider);
238+
if (err)
239+
goto remove_nodes;
240+
241+
return 0;
242+
243+
remove_nodes:
244+
icc_nodes_remove(&emc->provider);
245+
err_msg:
246+
dev_err(emc->dev, "failed to initialize ICC: %d\n", err);
247+
248+
return err;
249+
}
250+
149251
static int tegra186_emc_probe(struct platform_device *pdev)
150252
{
253+
struct tegra_mc *mc = dev_get_drvdata(pdev->dev.parent);
151254
struct mrq_emc_dvfs_latency_response response;
152255
struct tegra_bpmp_message msg;
153256
struct tegra186_emc *emc;
@@ -236,6 +339,32 @@ static int tegra186_emc_probe(struct platform_device *pdev)
236339
debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
237340
emc, &tegra186_emc_debug_max_rate_fops);
238341

342+
if (mc && mc->soc->icc_ops) {
343+
if (tegra_bpmp_mrq_is_supported(emc->bpmp, MRQ_BWMGR_INT)) {
344+
mc->bwmgr_mrq_supported = true;
345+
346+
/*
347+
* MC driver probe can't get BPMP reference as it gets probed
348+
* earlier than BPMP. So, save the BPMP ref got from the EMC
349+
* DT node in the mc->bpmp and use it in MC's icc_set hook.
350+
*/
351+
mc->bpmp = emc->bpmp;
352+
barrier();
353+
}
354+
355+
/*
356+
* Initialize the ICC even if BPMP-FW doesn't support 'MRQ_BWMGR_INT'.
357+
* Use the flag 'mc->bwmgr_mrq_supported' within MC driver and return
358+
* EINVAL instead of passing the request to BPMP-FW later when the BW
359+
* request is made by client with 'icc_set_bw()' call.
360+
*/
361+
err = tegra_emc_interconnect_init(emc);
362+
if (err) {
363+
mc->bpmp = NULL;
364+
goto put_bpmp;
365+
}
366+
}
367+
239368
return 0;
240369

241370
put_bpmp:
@@ -245,9 +374,12 @@ static int tegra186_emc_probe(struct platform_device *pdev)
245374

246375
static int tegra186_emc_remove(struct platform_device *pdev)
247376
{
377+
struct tegra_mc *mc = dev_get_drvdata(pdev->dev.parent);
248378
struct tegra186_emc *emc = platform_get_drvdata(pdev);
249379

250380
debugfs_remove_recursive(emc->debugfs.root);
381+
382+
mc->bpmp = NULL;
251383
tegra_bpmp_put(emc->bpmp);
252384

253385
return 0;
@@ -272,6 +404,7 @@ static struct platform_driver tegra186_emc_driver = {
272404
.name = "tegra186-emc",
273405
.of_match_table = tegra186_emc_of_match,
274406
.suppress_bind_attrs = true,
407+
.sync_state = icc_sync_state,
275408
},
276409
.probe = tegra186_emc_probe,
277410
.remove = tegra186_emc_remove,

0 commit comments

Comments
 (0)