/* * Tegra 18x SoC-specific mcerr code. * * Copyright (c) 2015-2018, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define pr_fmt(fmt) "mc-err: " fmt #include #include #include #include #include #include /*** Auto generated by `mcp.pl'. Do not modify! ***/ static struct mc_client mc_clients[] = { client("ptc", "csr_ptcr", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, client("afi", "csr_afir", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, client("hda", "csr_hdar", INVALID), client("hc", "csr_host1xdmar", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, client("nvenc", "csr_nvencsrd", INVALID), dummy_client, dummy_client, client("sata", "csr_satar", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, client("mpcore", "csr_mpcorer", INVALID), dummy_client, dummy_client, dummy_client, client("nvenc", "csw_nvencswr", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, client("afi", "csw_afiw", INVALID), dummy_client, dummy_client, dummy_client, client("hda", "csw_hdaw", INVALID), dummy_client, dummy_client, dummy_client, client("mpcore", "csw_mpcorew", INVALID), dummy_client, dummy_client, dummy_client, client("sata", "csw_sataw", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, client("isp2", "csr_ispra", INVALID), dummy_client, client("isp2", "csw_ispwa", INVALID), client("isp2", "csw_ispwb", INVALID), dummy_client, dummy_client, client("xusb_host", "csr_xusb_hostr", INVALID), client("xusb_host", "csw_xusb_hostw", INVALID), client("xusb_dev", "csr_xusb_devr", INVALID), client("xusb_dev", "csw_xusb_devw", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, client("tsec", "csr_tsecsrd", INVALID), client("tsec", "csw_tsecswr", INVALID), dummy_client, dummy_client, client("gpu", "csr_gpusrd", INVALID), client("gpu", "csw_gpuswr", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, client("sdmmc1a", "csr_sdmmcra", INVALID), client("sdmmc2a", "csr_sdmmcraa", INVALID), client("sdmmc3a", "csr_sdmmcr", INVALID), client("sdmmc4a", "csr_sdmmcrab", INVALID), client("sdmmc1a", "csw_sdmmcwa", INVALID), client("sdmmc2a", "csw_sdmmcwaa", INVALID), client("sdmmc3a", "csw_sdmmcw", INVALID), client("sdmmc4a", "csw_sdmmcwab", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, client("vic", "csr_vicsrd", INVALID), client("vic", "csw_vicswr", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, client("vi", "csw_viw", INVALID), dummy_client, dummy_client, dummy_client, dummy_client, dummy_client, client("nvdec", "csr_nvdecsrd", INVALID), client("nvdec", "csw_nvdecswr", INVALID), client("ape", "csr_aper", INVALID), client("ape", "csw_apew", INVALID), dummy_client, dummy_client, client("nvjpg", "csr_nvjpgsrd", INVALID), client("nvjpg", "csw_nvjpgswr", INVALID), client("se", "csr_sesrd", INVALID), client("se", "csw_seswr", INVALID), dummy_client, dummy_client, client("etr", "csr_etrr", INVALID), client("etr", "csw_etrw", INVALID), client("tsecb", "csr_tsecsrdb", INVALID), client("tsecb", "csw_tsecswrb", INVALID), client("gpu", "csr_gpusrd2", INVALID), client("gpu", "csw_gpuswr2", INVALID), dummy_client, dummy_client, client("axis", "csr_axisr", INVALID), client("axis", "csw_axisw", INVALID), client("eqos", "csr_eqosr", INVALID), client("eqos", "csw_eqosw", INVALID), client("ufshc", "csr_ufshcr", INVALID), client("ufshc", "csw_ufshcw", INVALID), client("nvdisplay", "csr_nvdisplayr", INVALID), client("bpmp", "csr_bpmpr", INVALID), client("bpmp", "csw_bpmpw", INVALID), client("bpmp", "csr_bpmpdmar", INVALID), client("bpmp", "csw_bpmpdmaw", INVALID), client("aon", "csr_aonr", INVALID), client("aon", "csw_aonw", INVALID), client("aon", "csr_aondmar", INVALID), client("aon", "csw_aondmaw", INVALID), client("sce", "csr_scer", INVALID), client("sce", "csw_scew", INVALID), client("sce", "csr_scedmar", INVALID), client("sce", "csw_scedmaw", INVALID), client("ape", "csr_apedmar", INVALID), client("ape", "csw_apedmaw", INVALID), client("nvdisplay", "csr_nvdisplayr1", INVALID), client("vic", "csr_vicsrd1", INVALID), client("nvdec", "csr_nvdecsrd1", INVALID), }; static int mc_client_last = ARRAY_SIZE(mc_clients) - 1; /*** Done. ***/ static const char *t186_intr_info[] = { NULL, /* Bit 0 */ NULL, NULL, NULL, NULL, /* Bit 4 */ NULL, "decerr-emem", NULL, "secerr", /* Bit 8 */ "arb-emem", NULL, NULL, "decerr-vpr", /* Bit 12 */ "decerr-sec", NULL, NULL, "decerr-mts", /* Bit 16 */ "decerr-gsc", "scrub-ecc", "wcam-err", NULL, /* Bit 20 */ NULL, NULL, NULL, NULL, /* Bit 24 */ NULL, NULL, NULL, NULL, /* Bit 28 */ NULL, NULL, NULL, }; #define MC_INT_DECERR_MTS (1<<16) #define MC_INT_WCAM_ERR (1<<19) /* hub common int status */ #define MC_HUBC_INT_SCRUB_ECC_WR_ACK (1 << 0) /* reported in MC_INTSTATUS_0 */ static const struct mc_error hub_mc_errors[] = { MC_ERR(MC_INT_DECERR_EMEM, "EMEM address decode error", 0, MC_ERR_STATUS, MC_ERR_ADR), MC_ERR(MC_INT_SECURITY_VIOLATION, "non secure access to secure region", 0, MC_ERR_STATUS, MC_ERR_ADR), MC_ERR(MC_INT_DECERR_VPR, "MC request violates VPR requirements", E_VPR, MC_ERR_VPR_STATUS, MC_ERR_VPR_ADR), MC_ERR(MC_INT_SECERR_SEC, "MC request violated SEC carveout requirements", 0, MC_ERR_SEC_STATUS, MC_ERR_SEC_ADR), MC_ERR(MC_INT_DECERR_MTS, "MTS carveout access violation", 0, MC_ERR_MTS_STATUS, MC_ERR_MTS_ADR), MC_ERR(MC_INT_DECERR_GENERALIZED_CARVEOUT, "GSC access violation", 0, MC_ERR_GENERALIZED_CARVEOUT_STATUS, MC_ERR_GENERALIZED_CARVEOUT_ADR), /* combination interrupts */ MC_ERR(MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION, "non secure access to secure region", 0, MC_ERR_STATUS, MC_ERR_ADR), MC_ERR(MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_EMEM, "EMEM GSC access violation", 0, MC_ERR_GENERALIZED_CARVEOUT_STATUS, MC_ERR_GENERALIZED_CARVEOUT_ADR), /* NULL terminate. */ MC_ERR(0, NULL, 0, 0, 0), }; /* reported in MC_CH_INTSTATUS_0 */ static const struct mc_error ch_mc_errors[] = { MC_ERR(MC_INT_WCAM_ERR, "WCAM error", E_TWO_STATUS, MC_WCAM_IRQ_P0_STATUS0, MC_WCAM_IRQ_P1_STATUS0), /* NULL terminate. */ MC_ERR(0, NULL, 0, 0, 0), }; /* reported in MC_HUBC_INTSTATUS_0 */ static const struct mc_error hubc_mc_errors[] = { MC_ERR(MC_HUBC_INT_SCRUB_ECC_WR_ACK, "ECC scrub complete", E_NO_STATUS, 0, 0), /* NULL terminate. */ MC_ERR(0, NULL, 0, 0, 0), }; enum { INTSTATUS_CH0 = 0, INTSTATUS_CH1 = 1, INTSTATUS_CH2 = 2, INTSTATUS_CH3 = 3, INTSTATUS_HUB0 = 16, INTSTATUS_HUB1 = 17, INTSTATUS_HUBC = 24, }; #define MC_INTSTATUS_CLEAR 0x00033340 #define MC_CH_INTSTATUS_CLEAR 0x00080200 #define MC_HUBC_INTSTATUS_CLEAR 0x00000001 #define MC_GLOBAL_INTSTATUS_CLEAR 0x0103000F static void clear_interrupt(unsigned int irq) { mc_writel(MC_INTSTATUS_CLEAR, MC_INTSTATUS); mc_writel(MC_CH_INTSTATUS_CLEAR, MC_CH_INTSTATUS); mc_writel(MC_HUBC_INTSTATUS_CLEAR, MC_HUBC_INTSTATUS); mc_writel(MC_GLOBAL_INTSTATUS_CLEAR, MC_GLOBAL_INTSTATUS); } static void log_fault(int src_chan, const struct mc_error *fault) { phys_addr_t addr; struct mc_client *client; u32 status, write, secure, client_id; if (fault->flags & E_VPR) mcerr_pr("vpr base=%x:%x, size=%x, ctrl=%x, override:(%x, %x, %x, %x)\n", mc_readl(MC_VIDEO_PROTECT_BOM_ADR_HI), mc_readl(MC_VIDEO_PROTECT_BOM), mc_readl(MC_VIDEO_PROTECT_SIZE_MB), mc_readl(MC_VIDEO_PROTECT_REG_CTRL), mc_readl(MC_VIDEO_PROTECT_VPR_OVERRIDE), mc_readl(MC_VIDEO_PROTECT_VPR_OVERRIDE1), mc_readl(MC_VIDEO_PROTECT_GPU_OVERRIDE_0), mc_readl(MC_VIDEO_PROTECT_GPU_OVERRIDE_1)); if (fault->flags & E_NO_STATUS) { mcerr_pr("MC fault - no status: %s\n", fault->msg); return; } status = __mc_readl(src_chan, fault->stat_reg); addr = __mc_readl(src_chan, fault->addr_reg); if (fault->flags & E_TWO_STATUS) { mcerr_pr("MC fault - %s\n", fault->msg); mcerr_pr("status: 0x%08x status2: 0x%08llx\n", status, addr); return; } secure = !!(status & MC_ERR_STATUS_SECURE); write = !!(status & MC_ERR_STATUS_WRITE); client_id = status & 0xff; client = &mc_clients[client_id <= mc_client_last ? client_id : mc_client_last]; /* * LPAE: make sure we get the extra 2 physical address bits available * and pass them down to the printing function. */ addr |= (((phys_addr_t)(status & MC_ERR_STATUS_ADR_HI)) << 12); mcerr_pr("(%d) %s: %s\n", client->swgid, client->name, fault->msg); mcerr_pr(" status = 0x%08x; addr = 0x%08llx\n", status, (long long unsigned int)addr); mcerr_pr(" secure: %s, access-type: %s\n", secure ? "yes" : "no", write ? "write" : "read"); } static void log_mcerr_fault(unsigned int irq) { int faults_handled = 0; const struct mc_error *err; int mc_channel = MC_BROADCAST_CHANNEL; u32 int_status, ch_int_status, hubc_int_status; u32 g_intstatus = mc_readl(MC_GLOBAL_INTSTATUS); /* * If multiple interrupts come in just handle the first one we see. The * HW only keeps track of 1 interrupt's data and we don't know which * particular fault is actually being kept... */ if (g_intstatus & (BIT(INTSTATUS_CH0)) || g_intstatus & (BIT(INTSTATUS_HUB0))) { mc_channel = 0; } else if (g_intstatus & (BIT(INTSTATUS_CH1)) || g_intstatus & (BIT(INTSTATUS_HUB1))) { mc_channel = 1; } else if (g_intstatus & (BIT(INTSTATUS_CH2))) { mc_channel = 2; } else if (g_intstatus & (BIT(INTSTATUS_CH3))) { mc_channel = 3; } else if (g_intstatus & (BIT(INTSTATUS_HUBC))) { mc_channel = MC_BROADCAST_CHANNEL; } else { #ifdef CONFIG_TEGRA_MC_TRACE_PRINTK trace_printk("mcerr: unknown source (intstatus = 0x%08x)\n", g_intstatus); #endif return; } int_status = __mc_readl(mc_channel, MC_INTSTATUS); ch_int_status = __mc_readl(mc_channel, MC_CH_INTSTATUS); hubc_int_status = __mc_readl(mc_channel, MC_HUBC_INTSTATUS); for (err = hub_mc_errors; err->sig && err->msg; err++) { if ((int_status & mc_int_mask) != err->sig) continue; log_fault(mc_channel, err); __mc_writel(mc_channel, int_status, MC_INTSTATUS); faults_handled++; break; } for (err = ch_mc_errors; err->sig && err->msg; err++) { if ((ch_int_status) != err->sig) continue; log_fault(mc_channel, err); __mc_writel(mc_channel, ch_int_status, MC_CH_INTSTATUS); faults_handled++; break; } for (err = hubc_mc_errors; err->sig && err->msg; err++) { if ((hubc_int_status) != err->sig) continue; log_fault(mc_channel, err); __mc_writel(mc_channel, hubc_int_status, MC_HUBC_INTSTATUS); faults_handled++; break; } if (!faults_handled) pr_err("unknown mcerr fault, int_status=0x%08x, " "ch_int_status=0x%08x, hubc_int_status=0x%08x\n", int_status, ch_int_status, hubc_int_status); } static struct mcerr_ops mcerr_ops = { .nr_clients = ARRAY_SIZE(mc_clients), .intr_descriptions = t186_intr_info, .clear_interrupt = clear_interrupt, .log_mcerr_fault = log_mcerr_fault, .mc_clients = mc_clients, }; static struct mcerr_ops *t18x_mcerr_of_setup(struct device_node *np) { pr_info("mcerr ops are set to t18x\n"); return &mcerr_ops; } MCERR_OF_DECLARE(mcerr_of, "nvidia,tegra-t18x-mc", t18x_mcerr_of_setup);