Skip to content

Commit 095ee35

Browse files
committed
Merge tag 'tegra-for-6.5-memory' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into soc/drivers
memory: tegra: Changes for v6.5-rc1 This introduces an interconnect provider for the memory controller and external memory controller found on Tegra234 chips that will eventually be used to dynamically scale the EMC frequency based on a device's bandwidth needs. * tag 'tegra-for-6.5-memory' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux: memory: tegra: Make CPU cluster BW request a multiple of MC channels memory: tegra: Add software memory clients in Tegra234 memory: tegra: Add memory clients for Tegra234 memory: tegra: Add interconnect support for DRAM scaling in Tegra234 dt-bindings: tegra: Add ICC IDs for dummy memory clients dt-bindings: tegra: Document compatible for IGX Link: https://lore.kernel.org/r/20230609193620.2275240-4-thierry.reding@gmail.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
2 parents b161ec3 + e852af7 commit 095ee35

8 files changed

Lines changed: 835 additions & 1 deletion

File tree

Documentation/devicetree/bindings/arm/tegra.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ properties:
167167
- const: nvidia,p3737-0000+p3701-0000
168168
- const: nvidia,p3701-0000
169169
- const: nvidia,tegra234
170+
- description: NVIDIA IGX Orin Development Kit
171+
items:
172+
- const: nvidia,p3740-0002+p3701-0008
173+
- const: nvidia,p3701-0008
174+
- const: nvidia,tegra234
170175
- description: Jetson Orin NX
171176
items:
172177
- const: nvidia,p3767-0000

drivers/memory/tegra/mc.c

Lines changed: 24 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);
@@ -838,6 +843,23 @@ static int tegra_mc_interconnect_setup(struct tegra_mc *mc)
838843
return err;
839844
}
840845

846+
static void tegra_mc_num_channel_enabled(struct tegra_mc *mc)
847+
{
848+
unsigned int i;
849+
u32 value;
850+
851+
value = mc_ch_readl(mc, 0, MC_EMEM_ADR_CFG_CHANNEL_ENABLE);
852+
if (value <= 0) {
853+
mc->num_channels = mc->soc->num_channels;
854+
return;
855+
}
856+
857+
for (i = 0; i < 32; i++) {
858+
if (value & BIT(i))
859+
mc->num_channels++;
860+
}
861+
}
862+
841863
static int tegra_mc_probe(struct platform_device *pdev)
842864
{
843865
struct tegra_mc *mc;
@@ -876,6 +898,8 @@ static int tegra_mc_probe(struct platform_device *pdev)
876898
return err;
877899
}
878900

901+
tegra_mc_num_channel_enabled(mc);
902+
879903
if (mc->soc->ops && mc->soc->ops->handle_irq) {
880904
mc->irq = platform_get_irq(pdev, 0);
881905
if (mc->irq < 0)

drivers/memory/tegra/mc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#define MC_ERR_ROUTE_SANITY_ADR 0x9c4
5454
#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00
5555
#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04
56+
#define MC_EMEM_ADR_CFG_CHANNEL_ENABLE 0xdf8
5657
#define MC_GLOBAL_INTSTATUS 0xf24
5758
#define MC_ERR_ADR_HI 0x11fc
5859

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)