Skip to content

Commit 54a85e0

Browse files
ashishmhetre8krzk
authored andcommitted
memory: tegra: Add MC error logging on Tegra186 onward
Add support for logging memory controller errors on Tegra186, Tegra194 and Tegra234. On these SoCs, interrupts can occur on multiple channels. Add support required to read the status of interrupts across multiple channels, log and clear them. Also add new interrupts supported on these SoCs. Reviewed-by: Dmitry Osipenko <[email protected]> Signed-off-by: Ashish Mhetre <[email protected]> Signed-off-by: Thierry Reding <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Krzysztof Kozlowski <[email protected]>
1 parent a7cffa1 commit 54a85e0

File tree

6 files changed

+189
-18
lines changed

6 files changed

+189
-18
lines changed

drivers/memory/tegra/mc.c

Lines changed: 118 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -508,33 +508,125 @@ int tegra30_mc_probe(struct tegra_mc *mc)
508508
return 0;
509509
}
510510

511-
static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
511+
const struct tegra_mc_ops tegra30_mc_ops = {
512+
.probe = tegra30_mc_probe,
513+
.handle_irq = tegra30_mc_handle_irq,
514+
};
515+
#endif
516+
517+
static int mc_global_intstatus_to_channel(const struct tegra_mc *mc, u32 status,
518+
unsigned int *mc_channel)
519+
{
520+
if ((status & mc->soc->ch_intmask) == 0)
521+
return -EINVAL;
522+
523+
*mc_channel = __ffs((status & mc->soc->ch_intmask) >>
524+
mc->soc->global_intstatus_channel_shift);
525+
526+
return 0;
527+
}
528+
529+
static u32 mc_channel_to_global_intstatus(const struct tegra_mc *mc,
530+
unsigned int channel)
531+
{
532+
return BIT(channel) << mc->soc->global_intstatus_channel_shift;
533+
}
534+
535+
irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
512536
{
513537
struct tegra_mc *mc = data;
538+
unsigned int bit, channel;
514539
unsigned long status;
515-
unsigned int bit;
516540

517-
/* mask all interrupts to avoid flooding */
518-
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
541+
if (mc->soc->num_channels) {
542+
u32 global_status;
543+
int err;
544+
545+
global_status = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, MC_GLOBAL_INTSTATUS);
546+
err = mc_global_intstatus_to_channel(mc, global_status, &channel);
547+
if (err < 0) {
548+
dev_err_ratelimited(mc->dev, "unknown interrupt channel 0x%08x\n",
549+
global_status);
550+
return IRQ_NONE;
551+
}
552+
553+
/* mask all interrupts to avoid flooding */
554+
status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmask;
555+
} else {
556+
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
557+
}
558+
519559
if (!status)
520560
return IRQ_NONE;
521561

522562
for_each_set_bit(bit, &status, 32) {
523563
const char *error = tegra_mc_status_names[bit] ?: "unknown";
524564
const char *client = "unknown", *desc;
525565
const char *direction, *secure;
566+
u32 status_reg, addr_reg;
567+
u32 intmask = BIT(bit);
526568
phys_addr_t addr = 0;
569+
#ifdef CONFIG_PHYS_ADDR_T_64BIT
570+
u32 addr_hi_reg = 0;
571+
#endif
527572
unsigned int i;
528573
char perm[7];
529574
u8 id, type;
530575
u32 value;
531576

532-
value = mc_readl(mc, MC_ERR_STATUS);
577+
switch (intmask) {
578+
case MC_INT_DECERR_VPR:
579+
status_reg = MC_ERR_VPR_STATUS;
580+
addr_reg = MC_ERR_VPR_ADR;
581+
break;
582+
583+
case MC_INT_SECERR_SEC:
584+
status_reg = MC_ERR_SEC_STATUS;
585+
addr_reg = MC_ERR_SEC_ADR;
586+
break;
587+
588+
case MC_INT_DECERR_MTS:
589+
status_reg = MC_ERR_MTS_STATUS;
590+
addr_reg = MC_ERR_MTS_ADR;
591+
break;
592+
593+
case MC_INT_DECERR_GENERALIZED_CARVEOUT:
594+
status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS;
595+
addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR;
596+
break;
597+
598+
case MC_INT_DECERR_ROUTE_SANITY:
599+
status_reg = MC_ERR_ROUTE_SANITY_STATUS;
600+
addr_reg = MC_ERR_ROUTE_SANITY_ADR;
601+
break;
602+
603+
default:
604+
status_reg = MC_ERR_STATUS;
605+
addr_reg = MC_ERR_ADR;
606+
607+
#ifdef CONFIG_PHYS_ADDR_T_64BIT
608+
if (mc->soc->has_addr_hi_reg)
609+
addr_hi_reg = MC_ERR_ADR_HI;
610+
#endif
611+
break;
612+
}
613+
614+
if (mc->soc->num_channels)
615+
value = mc_ch_readl(mc, channel, status_reg);
616+
else
617+
value = mc_readl(mc, status_reg);
533618

534619
#ifdef CONFIG_PHYS_ADDR_T_64BIT
535620
if (mc->soc->num_address_bits > 32) {
536-
addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
537-
MC_ERR_STATUS_ADR_HI_MASK);
621+
if (addr_hi_reg) {
622+
if (mc->soc->num_channels)
623+
addr = mc_ch_readl(mc, channel, addr_hi_reg);
624+
else
625+
addr = mc_readl(mc, addr_hi_reg);
626+
} else {
627+
addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
628+
MC_ERR_STATUS_ADR_HI_MASK);
629+
}
538630
addr <<= 32;
539631
}
540632
#endif
@@ -591,7 +683,10 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
591683
break;
592684
}
593685

594-
value = mc_readl(mc, MC_ERR_ADR);
686+
if (mc->soc->num_channels)
687+
value = mc_ch_readl(mc, channel, addr_reg);
688+
else
689+
value = mc_readl(mc, addr_reg);
595690
addr |= value;
596691

597692
dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n",
@@ -600,17 +695,18 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
600695
}
601696

602697
/* clear interrupts */
603-
mc_writel(mc, status, MC_INTSTATUS);
698+
if (mc->soc->num_channels) {
699+
mc_ch_writel(mc, channel, status, MC_INTSTATUS);
700+
mc_ch_writel(mc, MC_BROADCAST_CHANNEL,
701+
mc_channel_to_global_intstatus(mc, channel),
702+
MC_GLOBAL_INTSTATUS);
703+
} else {
704+
mc_writel(mc, status, MC_INTSTATUS);
705+
}
604706

605707
return IRQ_HANDLED;
606708
}
607709

608-
const struct tegra_mc_ops tegra30_mc_ops = {
609-
.probe = tegra30_mc_probe,
610-
.handle_irq = tegra30_mc_handle_irq,
611-
};
612-
#endif
613-
614710
const char *const tegra_mc_status_names[32] = {
615711
[ 1] = "External interrupt",
616712
[ 6] = "EMEM address decode error",
@@ -622,6 +718,8 @@ const char *const tegra_mc_status_names[32] = {
622718
[12] = "VPR violation",
623719
[13] = "Secure carveout violation",
624720
[16] = "MTS carveout violation",
721+
[17] = "Generalized carveout violation",
722+
[20] = "Route Sanity error",
625723
};
626724

627725
const char *const tegra_mc_error_names[8] = {
@@ -764,7 +862,11 @@ static int tegra_mc_probe(struct platform_device *pdev)
764862

765863
WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n");
766864

767-
mc_writel(mc, mc->soc->intmask, MC_INTMASK);
865+
if (mc->soc->num_channels)
866+
mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmask,
867+
MC_INTMASK);
868+
else
869+
mc_writel(mc, mc->soc->intmask, MC_INTMASK);
768870

769871
err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 0,
770872
dev_name(&pdev->dev), mc);

drivers/memory/tegra/mc.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,21 @@
4343
#define MC_EMEM_ARB_OVERRIDE 0xe8
4444
#define MC_TIMING_CONTROL_DBG 0xf8
4545
#define MC_TIMING_CONTROL 0xfc
46-
46+
#define MC_ERR_VPR_STATUS 0x654
47+
#define MC_ERR_VPR_ADR 0x658
48+
#define MC_ERR_SEC_STATUS 0x67c
49+
#define MC_ERR_SEC_ADR 0x680
50+
#define MC_ERR_MTS_STATUS 0x9b0
51+
#define MC_ERR_MTS_ADR 0x9b4
52+
#define MC_ERR_ROUTE_SANITY_STATUS 0x9c0
53+
#define MC_ERR_ROUTE_SANITY_ADR 0x9c4
54+
#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00
55+
#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04
56+
#define MC_GLOBAL_INTSTATUS 0xf24
57+
#define MC_ERR_ADR_HI 0x11fc
58+
59+
#define MC_INT_DECERR_ROUTE_SANITY BIT(20)
60+
#define MC_INT_DECERR_GENERALIZED_CARVEOUT BIT(17)
4761
#define MC_INT_DECERR_MTS BIT(16)
4862
#define MC_INT_SECERR_SEC BIT(13)
4963
#define MC_INT_DECERR_VPR BIT(12)
@@ -78,6 +92,8 @@
7892

7993
#define MC_TIMING_UPDATE BIT(0)
8094

95+
#define MC_BROADCAST_CHANNEL ~0
96+
8197
static inline u32 tegra_mc_scale_percents(u64 val, unsigned int percents)
8298
{
8399
val = val * percents;
@@ -92,6 +108,30 @@ icc_provider_to_tegra_mc(struct icc_provider *provider)
92108
return container_of(provider, struct tegra_mc, provider);
93109
}
94110

111+
static inline u32 mc_ch_readl(const struct tegra_mc *mc, int ch,
112+
unsigned long offset)
113+
{
114+
if (!mc->bcast_ch_regs)
115+
return 0;
116+
117+
if (ch == MC_BROADCAST_CHANNEL)
118+
return readl_relaxed(mc->bcast_ch_regs + offset);
119+
120+
return readl_relaxed(mc->ch_regs[ch] + offset);
121+
}
122+
123+
static inline void mc_ch_writel(const struct tegra_mc *mc, int ch,
124+
u32 value, unsigned long offset)
125+
{
126+
if (!mc->bcast_ch_regs)
127+
return;
128+
129+
if (ch == MC_BROADCAST_CHANNEL)
130+
writel_relaxed(value, mc->bcast_ch_regs + offset);
131+
else
132+
writel_relaxed(value, mc->ch_regs[ch] + offset);
133+
}
134+
95135
static inline u32 mc_readl(const struct tegra_mc *mc, unsigned long offset)
96136
{
97137
return readl_relaxed(mc->regs + offset);
@@ -156,6 +196,7 @@ extern const struct tegra_mc_ops tegra30_mc_ops;
156196
extern const struct tegra_mc_ops tegra186_mc_ops;
157197
#endif
158198

199+
irqreturn_t tegra30_mc_handle_irq(int irq, void *data);
159200
extern const char * const tegra_mc_status_names[32];
160201
extern const char * const tegra_mc_error_names[8];
161202

drivers/memory/tegra/tegra186.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <dt-bindings/memory/tegra186-mc.h>
1717
#endif
1818

19+
#include "mc.h"
20+
1921
#define MC_SID_STREAMID_OVERRIDE_MASK GENMASK(7, 0)
2022
#define MC_SID_STREAMID_SECURITY_WRITE_ACCESS_DISABLED BIT(16)
2123
#define MC_SID_STREAMID_SECURITY_OVERRIDE BIT(8)
@@ -173,6 +175,7 @@ const struct tegra_mc_ops tegra186_mc_ops = {
173175
.remove = tegra186_mc_remove,
174176
.resume = tegra186_mc_resume,
175177
.probe_device = tegra186_mc_probe_device,
178+
.handle_irq = tegra30_mc_handle_irq,
176179
};
177180

178181
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
@@ -905,6 +908,12 @@ const struct tegra_mc_soc tegra186_mc_soc = {
905908
.clients = tegra186_mc_clients,
906909
.num_address_bits = 40,
907910
.num_channels = 4,
911+
.client_id_mask = 0xff,
912+
.intmask = MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
913+
MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
914+
MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
908915
.ops = &tegra186_mc_ops,
916+
.ch_intmask = 0x0000000f,
917+
.global_intstatus_channel_shift = 0,
909918
};
910919
#endif

drivers/memory/tegra/tegra194.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,5 +1348,13 @@ const struct tegra_mc_soc tegra194_mc_soc = {
13481348
.clients = tegra194_mc_clients,
13491349
.num_address_bits = 40,
13501350
.num_channels = 16,
1351+
.client_id_mask = 0xff,
1352+
.intmask = MC_INT_DECERR_ROUTE_SANITY |
1353+
MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
1354+
MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
1355+
MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
1356+
.has_addr_hi_reg = true,
13511357
.ops = &tegra186_mc_ops,
1358+
.ch_intmask = 0x00000f00,
1359+
.global_intstatus_channel_shift = 8,
13521360
};

drivers/memory/tegra/tegra234.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,5 +98,13 @@ const struct tegra_mc_soc tegra234_mc_soc = {
9898
.clients = tegra234_mc_clients,
9999
.num_address_bits = 40,
100100
.num_channels = 16,
101+
.client_id_mask = 0x1ff,
102+
.intmask = MC_INT_DECERR_ROUTE_SANITY |
103+
MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
104+
MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
105+
MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
106+
.has_addr_hi_reg = true,
101107
.ops = &tegra186_mc_ops,
108+
.ch_intmask = 0x0000ff00,
109+
.global_intstatus_channel_shift = 8,
102110
};

include/soc/tegra/mc.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,15 @@ struct tegra_mc_soc {
193193
unsigned int num_address_bits;
194194
unsigned int atom_size;
195195

196-
u8 client_id_mask;
196+
u16 client_id_mask;
197197
u8 num_channels;
198198

199199
const struct tegra_smmu_soc *smmu;
200200

201201
u32 intmask;
202+
u32 ch_intmask;
203+
u32 global_intstatus_channel_shift;
204+
bool has_addr_hi_reg;
202205

203206
const struct tegra_mc_reset_ops *reset_ops;
204207
const struct tegra_mc_reset *resets;

0 commit comments

Comments
 (0)