diff options
author | Alex Waterman <alexw@nvidia.com> | 2013-10-23 12:29:52 -0700 |
---|---|---|
committer | Krishna Reddy <vdumpa@nvidia.com> | 2014-02-07 13:20:16 -0800 |
commit | 2219ca8ed25b79ce07b20e4af7cf558c8525ae98 (patch) | |
tree | bccbe808aadc05b55a61180084defb5fb3471780 /drivers/platform | |
parent | 31c364e9c2e0a95dcb39c49396da89316e7c15c8 (diff) |
ARM: tegra: mc: move MC code to drivers/platform/tegra/mc
Move the MC driver out of mach-tegra and prepare the driver
for using the DT.
Change-Id: I544259de9e899b29152d7e2f08bce5745e40fb30
Signed-off-by: Alex Waterman <alexw@nvidia.com>
Reviewed-on: http://git-master/r/302984
Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/tegra/Makefile | 5 | ||||
-rw-r--r-- | drivers/platform/tegra/mc-timing-t12x.c | 2 | ||||
-rw-r--r-- | drivers/platform/tegra/mc.c | 2 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/Makefile | 16 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mc-timing-t12x.c | 88 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mc.c | 297 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mcerr-t11.c | 198 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mcerr-t12.c | 218 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mcerr-t14.c | 194 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mcerr-t3.c | 104 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mcerr.c | 442 | ||||
-rw-r--r-- | drivers/platform/tegra/mcerr-t12.c | 2 | ||||
-rw-r--r-- | drivers/platform/tegra/mcerr.c | 2 |
13 files changed, 1559 insertions, 11 deletions
diff --git a/drivers/platform/tegra/Makefile b/drivers/platform/tegra/Makefile index da3bd255fd0b..07e495620696 100644 --- a/drivers/platform/tegra/Makefile +++ b/drivers/platform/tegra/Makefile @@ -12,6 +12,8 @@ obj-y += mipi-cal.o endif endif +obj-y += mc/ + ifneq ($(CONFIG_ARM64),) ccflags-y += -I$(srctree)/arch/arm/mach-tegra/include \ @@ -97,9 +99,6 @@ obj-$(CONFIG_ARCH_TEGRA_12x_SOC) += tegra12_edp.o obj-y += board-common.o -obj-y += mcerr.o -obj-$(CONFIG_ARCH_TEGRA_12x_SOC) += mcerr-t12.o - obj-y += board-touch-raydium_spi.o obj-y += board-panel.o diff --git a/drivers/platform/tegra/mc-timing-t12x.c b/drivers/platform/tegra/mc-timing-t12x.c deleted file mode 100644 index 9c9e3eebe397..000000000000 --- a/drivers/platform/tegra/mc-timing-t12x.c +++ /dev/null @@ -1,2 +0,0 @@ -/* Automatically generated file; DO NOT EDIT. */ -#include "../../../arch/arm/mach-tegra/mc-timing-t12x.c" diff --git a/drivers/platform/tegra/mc.c b/drivers/platform/tegra/mc.c deleted file mode 100644 index 191f3fdd2479..000000000000 --- a/drivers/platform/tegra/mc.c +++ /dev/null @@ -1,2 +0,0 @@ -/* Automatically generated file; DO NOT EDIT. */ -#include "../../../arch/arm/mach-tegra/mc.c" diff --git a/drivers/platform/tegra/mc/Makefile b/drivers/platform/tegra/mc/Makefile new file mode 100644 index 000000000000..31d86edca45d --- /dev/null +++ b/drivers/platform/tegra/mc/Makefile @@ -0,0 +1,16 @@ +# +# Memory controller code. +# + +obj-y += mc.o + +# MC error reporting. +obj-y += mcerr.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += mcerr-t3.o +obj-$(CONFIG_ARCH_TEGRA_11x_SOC) += mcerr-t11.o +obj-$(CONFIG_ARCH_TEGRA_12x_SOC) += mcerr-t12.o +obj-$(CONFIG_ARCH_TEGRA_14x_SOC) += mcerr-t14.o + +ifeq ($(CONFIG_PM_SLEEP),y) +obj-$(CONFIG_ARCH_TEGRA_12x_SOC) += mc-timing-t12x.o +endif diff --git a/drivers/platform/tegra/mc/mc-timing-t12x.c b/drivers/platform/tegra/mc/mc-timing-t12x.c new file mode 100644 index 000000000000..aeb87d908a05 --- /dev/null +++ b/drivers/platform/tegra/mc/mc-timing-t12x.c @@ -0,0 +1,88 @@ +/* + * arch/arm/mach-tegra/mc-timing-t12x.c + * + * Copyright (c) 2013-2014, 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. + */ + +#include <linux/io.h> + +#include <mach/mcerr.h> + +#include "../../../../arch/arm/mach-tegra/iomap.h" + +#define MC_LA_REG(mod) MC_LATENCY_ALLOWANCE_ ## mod + +static u32 mc_timing_reg_table[] = { + MC_LA_REG(AFI_0), + MC_LA_REG(AVPC_0), + MC_LA_REG(DC_0), + MC_LA_REG(DC_1), + MC_LA_REG(DC_2), + MC_LA_REG(DCB_0), + MC_LA_REG(DCB_1), + MC_LA_REG(DCB_2), + MC_LA_REG(HC_0), + MC_LA_REG(HC_1), + MC_LA_REG(HDA_0), + MC_LA_REG(MPCORE_0), + MC_LA_REG(MPCORELP_0), + MC_LA_REG(MSENC_0), + MC_LA_REG(PPCS_0), + MC_LA_REG(PPCS_1), + MC_LA_REG(PTC_0), + MC_LA_REG(SATA_0), + MC_LA_REG(VDE_0), + MC_LA_REG(VDE_1), + MC_LA_REG(VDE_2), + MC_LA_REG(VDE_3), + MC_LA_REG(ISP2_0), + MC_LA_REG(ISP2_1), + MC_LA_REG(XUSB_0), + MC_LA_REG(XUSB_1), + MC_LA_REG(ISP2B_0), + MC_LA_REG(ISP2B_1), + MC_LA_REG(TSEC_0), + MC_LA_REG(VIC_0), + MC_LA_REG(VI2_0), + MC_LA_REG(A9AVP_0), + MC_LA_REG(GPU_0), + MC_LA_REG(SDMMCA_0), + MC_LA_REG(SDMMCAA_0), + MC_LA_REG(SDMMC_0), + MC_LA_REG(SDMMCAB_0), + MC_LA_REG(DC_3) +}; + +#define NUM_LA_REGS ARRAY_SIZE(mc_timing_reg_table) + +void tegra12_mc_latency_allowance_save(u32 **pctx) +{ + u32 *ctx = *pctx; + u32 i; + for (i = 0; i < NUM_LA_REGS; ++i) + *ctx++ = readl(IOMEM(mc + + mc_timing_reg_table[i])); +} + +void tegra12_mc_latency_allowance_restore(u32 **pctx) +{ + u32 *ctx = *pctx; + u32 i; + for (i = 0; i < NUM_LA_REGS; ++i) + __raw_writel(*ctx++, IOMEM(mc + + mc_timing_reg_table[i])); +} diff --git a/drivers/platform/tegra/mc/mc.c b/drivers/platform/tegra/mc/mc.c new file mode 100644 index 000000000000..e1cdd7637d3e --- /dev/null +++ b/drivers/platform/tegra/mc/mc.c @@ -0,0 +1,297 @@ +/* + * arch/arm/mach-tegra/mc.c + * + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2011-2014, NVIDIA Corporation. All rights reserved. + * + * Author: + * Erik Gilling <konkers@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/debugfs.h> +#include <linux/tegra-soc.h> + +#include <mach/mc.h> +#include <mach/mcerr.h> + +#include "../../../../arch/arm/mach-tegra/iomap.h" + +#define MC_CLIENT_HOTRESET_CTRL 0x200 +#define MC_CLIENT_HOTRESET_STAT 0x204 +#define MC_CLIENT_HOTRESET_CTRL_1 0x970 +#define MC_CLIENT_HOTRESET_STAT_1 0x974 + +#define MC_TIMING_REG_NUM1 \ + ((MC_EMEM_ARB_TIMING_W2R - MC_EMEM_ARB_CFG) / 4 + 1) +#define MC_TIMING_REG_NUM2 \ + ((MC_EMEM_ARB_MISC1 - MC_EMEM_ARB_DA_TURNS) / 4 + 1) +#if defined(CONFIG_ARCH_TEGRA_12x_SOC) +#define MC_TIMING_REG_NUM3 T12X_MC_LATENCY_ALLOWANCE_NUM_REGS +#else +#define MC_TIMING_REG_NUM3 \ + ((MC_LATENCY_ALLOWANCE_VI_2 - MC_LATENCY_ALLOWANCE_BASE) / 4 + 1) +#endif + +static DEFINE_SPINLOCK(tegra_mc_lock); +void __iomem *mc = (void __iomem *)IO_ADDRESS(TEGRA_MC_BASE); +#ifdef MC_DUAL_CHANNEL +void __iomem *mc1 = (void __iomem *)IO_ADDRESS(TEGRA_MC1_BASE); +#endif + +#ifdef CONFIG_PM_SLEEP +static u32 mc_boot_timing[MC_TIMING_REG_NUM1 + MC_TIMING_REG_NUM2 + + MC_TIMING_REG_NUM3 + 4]; + +static void tegra_mc_timing_save(void) +{ + u32 off; + u32 *ctx = mc_boot_timing; + + for (off = MC_EMEM_ARB_CFG; off <= MC_EMEM_ARB_TIMING_W2R; off += 4) + *ctx++ = mc_readl(off); + + for (off = MC_EMEM_ARB_DA_TURNS; off <= MC_EMEM_ARB_MISC1; off += 4) + *ctx++ = mc_readl(off); + + *ctx++ = mc_readl(MC_EMEM_ARB_RING3_THROTTLE); + *ctx++ = mc_readl(MC_EMEM_ARB_OVERRIDE); + *ctx++ = mc_readl(MC_RESERVED_RSV); + +#if defined(CONFIG_ARCH_TEGRA_12x_SOC) + tegra12_mc_latency_allowance_save(&ctx); +#else + for (off = MC_LATENCY_ALLOWANCE_BASE; off <= MC_LATENCY_ALLOWANCE_VI_2; + off += 4) + *ctx++ = mc_readl(off); +#endif + + *ctx++ = mc_readl(MC_INT_MASK); +} + +void tegra_mc_timing_restore(void) +{ + u32 off; + u32 *ctx = mc_boot_timing; + + for (off = MC_EMEM_ARB_CFG; off <= MC_EMEM_ARB_TIMING_W2R; off += 4) + __mc_raw_writel(0, *ctx++, off); + + for (off = MC_EMEM_ARB_DA_TURNS; off <= MC_EMEM_ARB_MISC1; off += 4) + __mc_raw_writel(0, *ctx++, off); + + __mc_raw_writel(0, *ctx++, MC_EMEM_ARB_RING3_THROTTLE); + __mc_raw_writel(0, *ctx++, MC_EMEM_ARB_OVERRIDE); + __mc_raw_writel(0, *ctx++, MC_RESERVED_RSV); + +#if defined(CONFIG_ARCH_TEGRA_12x_SOC) + tegra12_mc_latency_allowance_restore(&ctx); +#else + for (off = MC_LATENCY_ALLOWANCE_BASE; off <= MC_LATENCY_ALLOWANCE_VI_2; + off += 4) + __mc_raw_writel(0, *ctx++, off); +#endif + + mc_writel(*ctx++, MC_INT_MASK); + off = mc_readl(MC_INT_MASK); + + mc_writel(0x1, MC_TIMING_CONTROL); + off = mc_readl(MC_TIMING_CONTROL); +#if defined(CONFIG_ARCH_TEGRA_3x_SOC) + /* Bug 1059264 + * Set extra snap level to avoid VI starving and dropping data. + */ + mc_writel(1, MC_VE_EXTRA_SNAP_LEVELS); +#endif +} +#else +#define tegra_mc_timing_save() +#endif + +/* + * If using T30/DDR3, the 2nd 16 bytes part of DDR3 atom is 2nd line and is + * discarded in tiling mode. + */ +int tegra_mc_get_tiled_memory_bandwidth_multiplier(void) +{ + int type; + + type = tegra_emc_get_dram_type(); + + if (type == DRAM_TYPE_DDR3) + return 2; + else + return 1; +} + +/* API to get EMC freq to be requested, for Bandwidth. + * bw_kbps: BandWidth passed is in KBps. + * returns freq in KHz + */ +unsigned int tegra_emc_bw_to_freq_req(unsigned int bw_kbps) +{ + unsigned int freq; + unsigned int bytes_per_emc_clk; + + bytes_per_emc_clk = tegra_mc_get_effective_bytes_width() * 2; + freq = (bw_kbps + bytes_per_emc_clk - 1) / bytes_per_emc_clk * + CONFIG_TEGRA_EMC_TO_DDR_CLOCK; + return freq; +} +EXPORT_SYMBOL_GPL(tegra_emc_bw_to_freq_req); + +/* API to get EMC bandwidth, for freq that can be requested. + * freq_khz: Frequency passed is in KHz. + * returns bandwidth in KBps + */ +unsigned int tegra_emc_freq_req_to_bw(unsigned int freq_khz) +{ + unsigned int bw; + unsigned int bytes_per_emc_clk; + + bytes_per_emc_clk = tegra_mc_get_effective_bytes_width() * 2; + bw = freq_khz * bytes_per_emc_clk / CONFIG_TEGRA_EMC_TO_DDR_CLOCK; + return bw; +} +EXPORT_SYMBOL_GPL(tegra_emc_freq_req_to_bw); + +#define HOTRESET_READ_COUNT 5 +static bool tegra_stable_hotreset_check(u32 stat_reg, u32 *stat) +{ + int i; + u32 cur_stat; + u32 prv_stat; + unsigned long flags; + + spin_lock_irqsave(&tegra_mc_lock, flags); + prv_stat = mc_readl(stat_reg); + for (i = 0; i < HOTRESET_READ_COUNT; i++) { + cur_stat = mc_readl(stat_reg); + if (cur_stat != prv_stat) { + spin_unlock_irqrestore(&tegra_mc_lock, flags); + return false; + } + } + *stat = cur_stat; + spin_unlock_irqrestore(&tegra_mc_lock, flags); + return true; +} + +int tegra_mc_flush(int id) +{ + u32 rst_ctrl, rst_stat; + u32 rst_ctrl_reg, rst_stat_reg; + unsigned long flags; + bool ret; + + if (id < 32) { + rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL; + rst_stat_reg = MC_CLIENT_HOTRESET_STAT; + } else { + id %= 32; + rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL_1; + rst_stat_reg = MC_CLIENT_HOTRESET_STAT_1; + } + + spin_lock_irqsave(&tegra_mc_lock, flags); + + rst_ctrl = mc_readl(rst_ctrl_reg); + rst_ctrl |= (1 << id); + mc_writel(rst_ctrl, rst_ctrl_reg); + + spin_unlock_irqrestore(&tegra_mc_lock, flags); + + do { + udelay(10); + rst_stat = 0; + ret = tegra_stable_hotreset_check(rst_stat_reg, &rst_stat); + if (!ret) + continue; + } while (!(rst_stat & (1 << id))); + + return 0; +} +EXPORT_SYMBOL(tegra_mc_flush); + +int tegra_mc_flush_done(int id) +{ + u32 rst_ctrl; + u32 rst_ctrl_reg, rst_stat_reg; + unsigned long flags; + + if (id < 32) { + rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL; + rst_stat_reg = MC_CLIENT_HOTRESET_STAT; + } else { + id %= 32; + rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL_1; + rst_stat_reg = MC_CLIENT_HOTRESET_STAT_1; + } + + spin_lock_irqsave(&tegra_mc_lock, flags); + + rst_ctrl = mc_readl(rst_ctrl_reg); + rst_ctrl &= ~(1 << id); + mc_writel(rst_ctrl, rst_ctrl_reg); + + spin_unlock_irqrestore(&tegra_mc_lock, flags); + + return 0; +} +EXPORT_SYMBOL(tegra_mc_flush_done); + +/* + * MC driver init. + */ +static int __init tegra_mc_init(void) +{ + +#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \ + defined(CONFIG_TEGRA_MC_EARLY_ACK) + u32 reg; +#endif + struct dentry *mc_debugfs_dir; + + tegra_mc_timing_save(); + +#if defined(CONFIG_ARCH_TEGRA_3x_SOC) + reg = 0x0f7f1010; + mc_writel(reg, MC_RESERVED_RSV); +#endif + +#if defined(CONFIG_TEGRA_MC_EARLY_ACK) + reg = mc_readl(MC_EMEM_ARB_OVERRIDE); + reg |= 3; +#if defined(CONFIG_TEGRA_ERRATA_1157520) + if (tegra_revision == TEGRA_REVISION_A01) + reg &= ~2; +#endif + mc_writel(reg, MC_EMEM_ARB_OVERRIDE); +#endif + + mc_debugfs_dir = debugfs_create_dir("mc", NULL); + if (mc_debugfs_dir == NULL) { + pr_err("Failed to make debugfs node: %ld\n", + PTR_ERR(mc_debugfs_dir)); + return PTR_ERR(mc_debugfs_dir); + } + + tegra_mcerr_init(mc_debugfs_dir); + + return 0; +} +arch_initcall(tegra_mc_init); diff --git a/drivers/platform/tegra/mc/mcerr-t11.c b/drivers/platform/tegra/mc/mcerr-t11.c new file mode 100644 index 000000000000..5cfc1b9b8e22 --- /dev/null +++ b/drivers/platform/tegra/mc/mcerr-t11.c @@ -0,0 +1,198 @@ +/* + * Tegra 11x SoC-specific mcerr code. + * + * Copyright (c) 2010-2014, 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. + */ + +#include <mach/mcerr.h> + +/*** Auto generated by `mcp.pl'. Do not modify! ***/ + +#define dummy_client client("dummy", "dummy") + +struct mc_client mc_clients[] = { + client("ptc", "csr_ptcr"), + client("dc", "csr_display0a"), + client("dcb", "csr_display0ab"), + client("dc", "csr_display0b"), + client("dcb", "csr_display0bb"), + client("dc", "csr_display0c"), + client("dcb", "csr_display0cb"), + dummy_client, + dummy_client, + client("epp", "cbr_eppup"), + client("g2", "cbr_g2pr"), + client("g2", "cbr_g2sr"), + dummy_client, + dummy_client, + dummy_client, + client("avpc", "csr_avpcarm7r"), + client("dc", "csr_displayhc"), + client("dcb", "csr_displayhcb"), + client("nv", "csr_fdcdrd"), + client("nv", "csr_fdcdrd2"), + client("g2", "csr_g2dr"), + client("hda", "csr_hdar"), + client("hc", "csr_host1xdmar"), + client("hc", "csr_host1xr"), + client("nv", "csr_idxsrd"), + dummy_client, + dummy_client, + dummy_client, + client("msenc", "csr_msencsrd"), + client("ppcs", "csr_ppcsahbdmar"), + client("ppcs", "csr_ppcsahbslvr"), + dummy_client, + client("nv", "csr_texl2srd"), + dummy_client, + client("vde", "csr_vdebsevr"), + client("vde", "csr_vdember"), + client("vde", "csr_vdemcer"), + client("vde", "csr_vdetper"), + client("mpcorelp", "csr_mpcorelpr"), + client("mpcore", "csr_mpcorer"), + client("epp", "cbw_eppu"), + client("epp", "cbw_eppv"), + client("epp", "cbw_eppy"), + client("msenc", "csw_msencswr"), + client("vi", "cbw_viwsb"), + client("vi", "cbw_viwu"), + client("vi", "cbw_viwv"), + client("vi", "cbw_viwy"), + client("g2", "ccw_g2dw"), + dummy_client, + client("avpc", "csw_avpcarm7w"), + client("nv", "csw_fdcdwr"), + client("nv", "csw_fdcdwr2"), + client("hda", "csw_hdaw"), + client("hc", "csw_host1xw"), + client("isp", "csw_ispw"), + client("mpcorelp", "csw_mpcorelpw"), + client("mpcore", "csw_mpcorew"), + dummy_client, + client("ppcs", "csw_ppcsahbdmaw"), + client("ppcs", "csw_ppcsahbslvw"), + dummy_client, + client("vde", "csw_vdebsevw"), + client("vde", "csw_vdedbgw"), + client("vde", "csw_vdembew"), + client("vde", "csw_vdetpmw"), + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + client("xusb_host", "csr_xusb_hostr"), + client("xusb_host", "csw_xusb_hostw"), + client("xusb_dev", "csr_xusb_devr"), + client("xusb_dev", "csw_xusb_devw"), + client("nv", "csw_fdcdwr3"), + client("nv", "csr_fdcdrd3"), + client("nv", "csw_fdcdwr4"), + client("nv", "csr_fdcdrd4"), + client("emucif", "csr_emucifr"), + client("emucif", "csw_emucifw"), + client("tsec", "csr_tsecsrd"), + client("tsec", "csw_tsecswr"), +}; +int mc_client_last = ARRAY_SIZE(mc_clients) - 1; +/*** Done. ***/ + +static void mcerr_t11x_info_update(struct mc_client *c, u32 stat) +{ + if (stat & MC_INT_DECERR_EMEM) + c->intr_counts[0]++; + if (stat & MC_INT_SECURITY_VIOLATION) + c->intr_counts[1]++; + if (stat & MC_INT_INVALID_SMMU_PAGE) + c->intr_counts[2]++; + if (stat & MC_INT_DECERR_VPR) + c->intr_counts[3]++; + if (stat & MC_INT_SECERR_SEC) + c->intr_counts[4]++; + + if (stat & ~MC_INT_EN_MASK) + c->intr_counts[5]++; +} + +/* + * T11x reports addresses in a 32 byte range thus we can only give an + * approximate location for the invalid memory request, not the exact address. + */ +static void mcerr_t11x_print(const struct mc_error *err, + const struct mc_client *client, + u32 status, phys_addr_t addr, + int secure, int rw, const char *smmu_info) +{ + pr_err("[mcerr] (%s) %s: %s\n", client->swgid, client->name, err->msg); + pr_err("[mcerr] status = 0x%08x; addr = [0x%08lx -> 0x%08lx]", + status, (ulong)(addr & ~0x1f), (ulong)(addr | 0x1f)); + pr_err("[mcerr] secure: %s, access-type: %s, SMMU fault: %s\n", + secure ? "yes" : "no", rw ? "write" : "read", + smmu_info ? smmu_info : "none"); +} + +#define fmt_hdr "%-18s %-18s %-9s %-9s %-9s %-10s %-10s %-9s\n" +#define fmt_cli "%-18s %-18s %-9u %-9u %-9u %-10u %-10u %-9u\n" +static int mcerr_t11x_debugfs_show(struct seq_file *s, void *v) +{ + int i, j; + int do_print; + + seq_printf(s, fmt_hdr, + "swgid", "client", "decerr", "secerr", "smmuerr", + "decerr-VPR", "secerr-SEC", "unknown"); + for (i = 0; i < ARRAY_SIZE(mc_clients); i++) { + do_print = 0; + if (strcmp(mc_clients[i].name, "dummy") == 0) + continue; + /* Only print clients who actually have errors. */ + for (j = 0; j < INTR_COUNT; j++) { + if (mc_clients[i].intr_counts[j]) { + do_print = 1; + break; + } + } + if (do_print) + seq_printf(s, fmt_cli, + mc_clients[i].swgid, + mc_clients[i].name, + mc_clients[i].intr_counts[0], + mc_clients[i].intr_counts[1], + mc_clients[i].intr_counts[2], + mc_clients[i].intr_counts[3], + mc_clients[i].intr_counts[4], + mc_clients[i].intr_counts[5]); + } + return 0; +} + +/* + * Set up chip specific functions and data for handling this particular chip's + * error decoding and logging. + */ +void mcerr_chip_specific_setup(struct mcerr_chip_specific *spec) +{ + spec->mcerr_print = mcerr_t11x_print; + spec->mcerr_info_update = mcerr_t11x_info_update; + spec->mcerr_debugfs_show = mcerr_t11x_debugfs_show; + spec->nr_clients = ARRAY_SIZE(mc_clients); + return; +} diff --git a/drivers/platform/tegra/mc/mcerr-t12.c b/drivers/platform/tegra/mc/mcerr-t12.c new file mode 100644 index 000000000000..0a2dde60e8aa --- /dev/null +++ b/drivers/platform/tegra/mc/mcerr-t12.c @@ -0,0 +1,218 @@ +/* + * Tegra 12x SoC-specific mcerr code. + * + * Copyright (c) 2012-2014, 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. + */ + +#include <mach/mcerr.h> + +/*** Auto generated by `mcp.pl'. Do not modify! ***/ + +#define dummy_client client("dummy", "dummy") + +struct mc_client mc_clients[] = { + client("ptc", "csr_ptcr"), + client("dc", "csr_display0a"), + client("dcb", "csr_display0ab"), + client("dc", "csr_display0b"), + client("dcb", "csr_display0bb"), + client("dc", "csr_display0c"), + client("dcb", "csr_display0cb"), + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + client("afi", "csr_afir"), + client("avpc", "csr_avpcarm7r"), + client("dc", "csr_displayhc"), + client("dcb", "csr_displayhcb"), + dummy_client, + dummy_client, + dummy_client, + client("hda", "csr_hdar"), + client("hc", "csr_host1xdmar"), + client("hc", "csr_host1xr"), + dummy_client, + dummy_client, + dummy_client, + dummy_client, + client("msenc", "csr_msencsrd"), + client("ppcs", "csr_ppcsahbdmar"), + client("ppcs", "csr_ppcsahbslvr"), + client("sata", "csr_satar"), + dummy_client, + dummy_client, + client("vde", "csr_vdebsevr"), + client("vde", "csr_vdember"), + client("vde", "csr_vdemcer"), + client("vde", "csr_vdetper"), + client("mpcorelp", "csr_mpcorelpr"), + client("mpcore", "csr_mpcorer"), + dummy_client, + dummy_client, + dummy_client, + client("msenc", "csw_msencswr"), + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + client("afi", "csw_afiw"), + client("avpc", "csw_avpcarm7w"), + dummy_client, + dummy_client, + client("hda", "csw_hdaw"), + client("hc", "csw_host1xw"), + dummy_client, + client("mpcorelp", "csw_mpcorelpw"), + client("mpcore", "csw_mpcorew"), + dummy_client, + client("ppcs", "csw_ppcsahbdmaw"), + client("ppcs", "csw_ppcsahbslvw"), + client("sata", "csw_sataw"), + client("vde", "csw_vdebsevw"), + client("vde", "csw_vdedbgw"), + client("vde", "csw_vdembew"), + client("vde", "csw_vdetpmw"), + dummy_client, + dummy_client, + client("isp2", "csr_ispra"), + dummy_client, + client("isp2", "csw_ispwa"), + client("isp2", "csw_ispwb"), + dummy_client, + dummy_client, + client("xusb_host", "csr_xusb_hostr"), + client("xusb_host", "csw_xusb_hostw"), + client("xusb_dev", "csr_xusb_devr"), + client("xusb_dev", "csw_xusb_devw"), + client("isp2b", "csr_isprab"), + dummy_client, + client("isp2b", "csw_ispwab"), + client("isp2b", "csw_ispwbb"), + dummy_client, + dummy_client, + client("tsec", "csr_tsecsrd"), + client("tsec", "csw_tsecswr"), + client("a9avp", "csr_a9avpscr"), + client("a9avp", "csw_a9avpscw"), + client("gpu", "csr_gpusrd"), + client("gpu", "csw_gpuswr"), + client("dc", "csr_displayt"), + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + client("sdmmc1a", "csr_sdmmcra"), + client("sdmmc2a", "csr_sdmmcraa"), + client("sdmmc3a", "csr_sdmmcr"), + client("sdmmc4a", "csr_sdmmcrab"), + client("sdmmc1a", "csw_sdmmcwa"), + client("sdmmc2a", "csw_sdmmcwaa"), + client("sdmmc3a", "csw_sdmmcw"), + client("sdmmc4a", "csw_sdmmcwab"), + dummy_client, + dummy_client, + dummy_client, + dummy_client, + client("vic", "csr_vicsrd"), + client("vic", "csw_vicswr"), + dummy_client, + dummy_client, + dummy_client, + dummy_client, + client("vi", "csw_viw"), + client("dc", "csr_displayd"), +}; +int mc_client_last = ARRAY_SIZE(mc_clients) - 1; +/*** Done. ***/ + +static void mcerr_t12x_info_update(struct mc_client *c, u32 stat) +{ + if (stat & MC_INT_DECERR_EMEM) + c->intr_counts[0]++; + if (stat & MC_INT_SECURITY_VIOLATION) + c->intr_counts[1]++; + if (stat & MC_INT_INVALID_SMMU_PAGE) + c->intr_counts[2]++; + if (stat & MC_INT_INVALID_APB_ASID_UPDATE) + c->intr_counts[3]++; + if (stat & MC_INT_DECERR_VPR) + c->intr_counts[4]++; + if (stat & MC_INT_SECERR_SEC) + c->intr_counts[5]++; + if (stat & MC_INT_DECERR_MTS) + c->intr_counts[6]++; + + if (stat & ~MC_INT_EN_MASK) + c->intr_counts[7]++; +} + +#define fmt_hdr "%-18s %-18s %-9s %-9s %-9s %-10s %-10s %-10s %-10s %-9s\n" +#define fmt_cli "%-18s %-18s %-9u %-9u %-9u %-10u %-10u %-10u %-10u %-9u\n" +static int mcerr_t12x_debugfs_show(struct seq_file *s, void *v) +{ + int i, j; + int do_print; + + seq_printf(s, fmt_hdr, + "swgid", "client", "decerr", "secerr", "smmuerr", + "apberr", "decerr-VPR", "secerr-SEC", + "decerr_MST", "unknown"); + for (i = 0; i < ARRAY_SIZE(mc_clients); i++) { + do_print = 0; + if (strcmp(mc_clients[i].name, "dummy") == 0) + continue; + /* Only print clients who actually have errors. */ + for (j = 0; j < INTR_COUNT; j++) { + if (mc_clients[i].intr_counts[j]) { + do_print = 1; + break; + } + } + if (do_print) + seq_printf(s, fmt_cli, + mc_clients[i].swgid, + mc_clients[i].name, + mc_clients[i].intr_counts[0], + mc_clients[i].intr_counts[1], + mc_clients[i].intr_counts[2], + mc_clients[i].intr_counts[3], + mc_clients[i].intr_counts[4], + mc_clients[i].intr_counts[5], + mc_clients[i].intr_counts[6], + mc_clients[i].intr_counts[7]); + } + return 0; +} + +/* + * Set up chip specific functions and data for handling this particular chip's + * error decoding and logging. + */ +void mcerr_chip_specific_setup(struct mcerr_chip_specific *spec) +{ + spec->mcerr_info_update = mcerr_t12x_info_update; + spec->mcerr_debugfs_show = mcerr_t12x_debugfs_show; + spec->nr_clients = ARRAY_SIZE(mc_clients); + return; +} + diff --git a/drivers/platform/tegra/mc/mcerr-t14.c b/drivers/platform/tegra/mc/mcerr-t14.c new file mode 100644 index 000000000000..4400f8a4e7af --- /dev/null +++ b/drivers/platform/tegra/mc/mcerr-t14.c @@ -0,0 +1,194 @@ +/* + * Tegra 14x SoC-specific mcerr code. + * + * Copyright (c) 2012-2014, 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. + */ + +#include <mach/mcerr.h> + +/*** Auto generated by `mcp.pl'. Do not modify! ***/ + +#define dummy_client client("dummy", "dummy") + +struct mc_client mc_clients[] = { + client("ptc", "csr_ptcr"), + client("dc", "csr_display0a"), + client("dcb", "cbr_display0ab"), + client("dc", "csr_display0b"), + client("dcb", "csr_display0bb"), + client("dc", "csr_display0c"), + client("dcb", "cbr_display0cb"), + dummy_client, + dummy_client, + client("epp", "cbr_eppup"), + client("g2", "cbr_g2pr"), + client("g2", "cbr_g2sr"), + dummy_client, + dummy_client, + dummy_client, + client("avpc", "csr_avpcarm7r"), + client("dc", "csr_displayhc"), + client("dcb", "csr_displayhcb"), + client("nv", "csr_fdcdrd"), + client("nv", "csr_fdcdrd2"), + client("g2", "csr_g2dr"), + client("hda", "csr_hdar"), + client("hc", "csr_host1xdmar"), + client("hc", "csr_host1xr"), + client("nv", "csr_idxsrd"), + dummy_client, + dummy_client, + dummy_client, + client("msenc", "csr_msencsrd"), + client("ppcs", "csr_ppcsahbdmar"), + client("ppcs", "csr_ppcsahbslvr"), + dummy_client, + client("nv", "csr_texl2srd"), + dummy_client, + client("vde", "csr_vdebsevr"), + client("vde", "csr_vdember"), + client("vde", "csr_vdemcer"), + client("vde", "csr_vdetper"), + client("mpcorelp", "csr_mpcorelpr"), + client("mpcore", "csr_mpcorer"), + client("epp", "cbw_eppu"), + client("epp", "cbw_eppv"), + client("epp", "cbw_eppy"), + client("msenc", "csw_msencswr"), + client("vi", "cbw_viwsb"), + client("vi", "cbw_viwu"), + client("vi", "cbw_viwv"), + client("vi", "cbw_viwy"), + client("g2", "ccw_g2dw"), + dummy_client, + client("avpc", "csw_avpcarm7w"), + client("nv", "csw_fdcdwr"), + client("nv", "csw_fdcdwr2"), + client("hda", "csw_hdaw"), + client("hc", "csw_host1xw"), + client("isp", "csw_ispw"), + client("mpcorelp", "csw_mpcorelpw"), + client("mpcore", "csw_mpcorew"), + dummy_client, + client("ppcs", "csw_ppcsahbdmaw"), + client("ppcs", "csw_ppcsahbslvw"), + dummy_client, + client("vde", "csw_vdebsevw"), + client("vde", "csw_vdedbgw"), + client("vde", "csw_vdembew"), + client("vde", "csw_vdetpmw"), + dummy_client, + dummy_client, + client("isp", "csr_ispra"), + dummy_client, + client("isp", "csw_ispwa"), + client("isp", "csw_ispwb"), + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + dummy_client, + client("emucif", "csr_emucifr"), + client("emucif", "csw_emucifw"), + client("tsec", "csr_tsecsrd"), + client("tsec", "csw_tsecswr"), + client("vi", "csw_viw"), + client("bbmci", "csr_bbcr"), + client("bbmci", "csw_bbcw"), + client("bbmcill", "csr_bbcllr"), + client("dc", "csr_displayt"), + dummy_client, + client("dc", "csr_displayd"), +}; +int mc_client_last = ARRAY_SIZE(mc_clients) - 1; +/*** Done. ***/ + +static void mcerr_t14x_info_update(struct mc_client *c, u32 stat) +{ + if (stat & MC_INT_DECERR_EMEM) + c->intr_counts[0]++; + if (stat & MC_INT_SECURITY_VIOLATION) + c->intr_counts[1]++; + if (stat & MC_INT_INVALID_SMMU_PAGE) + c->intr_counts[2]++; + if (stat & MC_INT_DECERR_VPR) + c->intr_counts[3]++; + if (stat & MC_INT_SECERR_SEC) + c->intr_counts[4]++; + if (stat & MC_INT_BBC_PRIVATE_MEM_VIOLATION) + c->intr_counts[5]++; + if (stat & MC_INT_DECERR_BBC) + c->intr_counts[6]++; + + if (stat & ~MC_INT_EN_MASK) + c->intr_counts[7]++; +} + +#define fmt_hdr "%-18s %-18s %-9s %-9s %-9s %-10s %-10s %-9s %-9s %-9s\n" +#define fmt_cli "%-18s %-18s %-9u %-9u %-9u %-10u %-10u %-9u %-9u %-9u\n"; +static int mcerr_t14x_debugfs_show(struct seq_file *s, void *v) +{ + int i, j; + int do_print; + + seq_printf(s, fmt_hdr, + "swgid", "client", "decerr", "secerr", "smmuerr", + "decerr-VPR", "secerr-SEC", "priv-bbc", "decerr-bbc", + "unknown"); + for (i = 0; i < ARRAY_SIZE(mc_clients); i++) { + do_print = 0; + if (strcmp(mc_clients[i].name, "dummy") == 0) + continue; + /* Only print clients who actually have errors. */ + for (j = 0; j < INTR_COUNT; j++) { + if (mc_clients[i].intr_counts[j]) { + do_print = 1; + break; + } + } + if (do_print) + seq_printf(s, fmt_cli, + mc_clients[i].swgid, + mc_clients[i].name, + mc_clients[i].intr_counts[0], + mc_clients[i].intr_counts[1], + mc_clients[i].intr_counts[2], + mc_clients[i].intr_counts[3], + mc_clients[i].intr_counts[4], + mc_clients[i].intr_counts[5], + mc_clients[i].intr_counts[6], + mc_clients[i].intr_counts[7]); + } + return 0; +} + +/* + * Set up chip specific functions and data for handling this particular chip's + * error decoding and logging. + */ +void mcerr_chip_specific_setup(struct mcerr_chip_specific *spec) +{ + spec->mcerr_info_update = mcerr_t14x_info_update; + spec->mcerr_debugfs_show = mcerr_t14x_debugfs_show; + spec->nr_clients = ARRAY_SIZE(mc_clients); + return; +} diff --git a/drivers/platform/tegra/mc/mcerr-t3.c b/drivers/platform/tegra/mc/mcerr-t3.c new file mode 100644 index 000000000000..50118f726879 --- /dev/null +++ b/drivers/platform/tegra/mc/mcerr-t3.c @@ -0,0 +1,104 @@ +/* + * Tegra 3 SoC-specific mcerr code. + * + * Copyright (c) 2010-2014, 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. + */ + +#include <mach/mcerr.h> + +/*** Auto generated by `mcp.pl'. Do not modify! ***/ + +#define dummy_client client("dummy", "dummy") + +struct mc_client mc_clients[] = { + client("ptc", "csr_ptcr"), + client("dc", "cbr_display0a"), + client("dcb", "cbr_display0ab"), + client("dc", "cbr_display0b"), + client("dcb", "cbr_display0bb"), + client("dc", "cbr_display0c"), + client("dcb", "cbr_display0cb"), + client("dc", "cbr_display1b"), + client("dcb", "cbr_display1bb"), + client("epp", "cbr_eppup"), + client("g2", "cbr_g2pr"), + client("g2", "cbr_g2sr"), + client("mpe", "cbr_mpeunifbr"), + client("vi", "cbr_viruv"), + client("afi", "csr_afir"), + client("avpc", "csr_avpcarm7r"), + client("dc", "csr_displayhc"), + client("dcb", "csr_displayhcb"), + client("nv", "csr_fdcdrd"), + client("nv2", "csr_fdcdrd2"), + client("g2", "csr_g2dr"), + client("hda", "csr_hdar"), + client("hc", "csr_host1xdmar"), + client("hc", "csr_host1xr"), + client("nv", "csr_idxsrd"), + client("nv2", "csr_idxsrd2"), + client("mpe", "csr_mpe_ipred"), + client("mpe", "csr_mpeamemrd"), + client("mpe", "csr_mpecsrd"), + client("ppcs", "csr_ppcsahbdmar"), + client("ppcs", "csr_ppcsahbslvr"), + client("sata", "csr_satar"), + client("nv", "csr_texsrd"), + client("nv2", "csr_texsrd2"), + client("vde", "csr_vdebsevr"), + client("vde", "csr_vdember"), + client("vde", "csr_vdemcer"), + client("vde", "csr_vdetper"), + client("mpcorelp", "csr_mpcorelpr"), + client("mpcore", "csr_mpcorer"), + client("epp", "cbw_eppu"), + client("epp", "cbw_eppv"), + client("epp", "cbw_eppy"), + client("mpe", "cbw_mpeunifbw"), + client("vi", "cbw_viwsb"), + client("vi", "cbw_viwu"), + client("vi", "cbw_viwv"), + client("vi", "cbw_viwy"), + client("g2", "ccw_g2dw"), + client("afi", "csw_afiw"), + client("avpc", "csw_avpcarm7w"), + client("nv", "csw_fdcdwr"), + client("nv2", "csw_fdcdwr2"), + client("hda", "csw_hdaw"), + client("hc", "csw_host1xw"), + client("isp", "csw_ispw"), + client("mpcorelp", "csw_mpcorelpw"), + client("mpcore", "csw_mpcorew"), + client("mpe", "csw_mpecswr"), + client("ppcs", "csw_ppcsahbdmaw"), + client("ppcs", "csw_ppcsahbslvw"), + client("sata", "csw_sataw"), + client("vde", "csw_vdebsevw"), + client("vde", "csw_vdedbgw"), + client("vde", "csw_vdembew"), + client("vde", "csw_vdetpmw"), +}; +int mc_client_last = ARRAY_SIZE(mc_clients) - 1; +/*** Done. ***/ + +/* + * Defaults work for T30. + */ +void mcerr_chip_specific_setup(struct mcerr_chip_specific *spec) +{ + return; +} diff --git a/drivers/platform/tegra/mc/mcerr.c b/drivers/platform/tegra/mc/mcerr.c new file mode 100644 index 000000000000..5aa8c4a5da5a --- /dev/null +++ b/drivers/platform/tegra/mc/mcerr.c @@ -0,0 +1,442 @@ +/* + * arch/arm/mach-tegra/mcerr.c + * + * MC error code common to T3x and T11x. T20 has been left alone. + * + * Copyright (c) 2010-2014, 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/stat.h> +#include <linux/sched.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/moduleparam.h> +#include <linux/spinlock_types.h> + +#include <mach/mc.h> +#include <mach/irqs.h> +#include <mach/mcerr.h> + +static bool mcerr_throttle_enabled = true; + +static int arb_intr_mma_set(const char *arg, const struct kernel_param *kp); +static int arb_intr_mma_get(char *buff, const struct kernel_param *kp); +static void unthrottle_prints(struct work_struct *work); + +static int spurious_intrs; + +static struct arb_emem_intr_info arb_intr_info = { + .lock = __SPIN_LOCK_UNLOCKED(arb_intr_info.lock), +}; +static int arb_intr_count; + +static struct kernel_param_ops arb_intr_mma_ops = { + .get = arb_intr_mma_get, + .set = arb_intr_mma_set, +}; + +module_param_cb(arb_intr_mma_in_ms, &arb_intr_mma_ops, + &arb_intr_info.arb_intr_mma, S_IRUGO | S_IWUSR); +module_param(arb_intr_count, int, S_IRUGO | S_IWUSR); +module_param(spurious_intrs, int, S_IRUGO | S_IWUSR); + +static const char *const smmu_page_attrib[] = { + "nr-nw-s", + "nr-nw-ns", + "nr-wr-s", + "nr-wr-ns", + "rd-nw-s", + "rd-nw-ns", + "rd-wr-s", + "rd-wr-ns" +}; + +/* + * Table of known errors and their interrupt signatures. + */ +static const struct mc_error mc_errors[] = { + MC_ERR(MC_INT_DECERR_EMEM, + "EMEM address decode error", + 0, MC_ERR_STATUS, MC_ERR_ADR), + MC_ERR(MC_INT_DECERR_VPR, + "MC request violates VPR requirments", + 0, MC_ERR_VPR_STATUS, MC_ERR_VPR_ADR), + MC_ERR(MC_INT_SECURITY_VIOLATION, + "non secure access to secure region", + 0, MC_ERR_STATUS, MC_ERR_ADR), + MC_ERR(MC_INT_SECERR_SEC, + "MC request violated SEC carveout requirements", + 0, MC_ERR_SEC_STATUS, MC_ERR_SEC_ADR), + + /* + * SMMU related faults. + */ + MC_ERR(MC_INT_INVALID_SMMU_PAGE, + "SMMU address translation fault", + E_SMMU, MC_ERR_STATUS, MC_ERR_ADR), + MC_ERR(MC_INT_INVALID_SMMU_PAGE | MC_INT_DECERR_EMEM, + "EMEM decode error on PDE or PTE entry", + E_SMMU, MC_ERR_STATUS, MC_ERR_ADR), + MC_ERR(MC_INT_INVALID_SMMU_PAGE | MC_INT_SECERR_SEC, + "secure SMMU address translation fault", + E_SMMU, MC_ERR_SEC_STATUS, MC_ERR_SEC_ADR), + MC_ERR(MC_INT_INVALID_SMMU_PAGE | MC_INT_DECERR_VPR, + "VPR SMMU address translation fault", + E_SMMU, MC_ERR_VPR_STATUS, MC_ERR_VPR_ADR), + + /* + * Baseband controller related faults. + */ + MC_ERR(MC_INT_BBC_PRIVATE_MEM_VIOLATION, + "client accessed BBC aperture", + 0, MC_ERR_BBC_STATUS, MC_ERR_BBC_ADR), + MC_ERR(MC_INT_DECERR_BBC, + "BBC accessed memory outside of its aperture", + E_NO_STATUS, 0, 0), + + /* + * MTS access violation. + */ + MC_ERR(MC_INT_DECERR_MTS, + "MTS carveout access violation", + 0, MC_ERR_STATUS, MC_ERR_ADR), + + /* NULL terminate. */ + MC_ERR(0, NULL, 0, 0, 0), +}; + +static DEFINE_SPINLOCK(mc_lock); +static unsigned long error_count; + +static DECLARE_DELAYED_WORK(unthrottle_prints_work, unthrottle_prints); + +static struct dentry *mcerr_debugfs_dir; + +/* + * Chip specific functions. + */ +static struct mcerr_chip_specific chip_specific; + +static int arb_intr_mma_set(const char *arg, const struct kernel_param *kp) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&arb_intr_info.lock, flags); + ret = param_set_int(arg, kp); + spin_unlock_irqrestore(&arb_intr_info.lock, flags); + return ret; +} + +static int arb_intr_mma_get(char *buff, const struct kernel_param *kp) +{ + return param_get_int(buff, kp); +} + +static void arb_intr(void) +{ + u64 time; + u32 time_diff_ms; + unsigned long flags; + + spin_lock_irqsave(&arb_intr_info.lock, flags); + arb_intr_count++; + time = sched_clock(); + time_diff_ms = (time - arb_intr_info.time) >> 20; + arb_intr_info.time = time; + arb_intr_info.arb_intr_mma = + ((MMA_HISTORY_SAMPLES - 1) * time_diff_ms + + arb_intr_info.arb_intr_mma) / MMA_HISTORY_SAMPLES; + spin_unlock_irqrestore(&arb_intr_info.lock, flags); +} + +static void unthrottle_prints(struct work_struct *work) +{ + unsigned long flags; + + spin_lock_irqsave(&mc_lock, flags); + error_count = 0; + spin_unlock_irqrestore(&mc_lock, flags); +} + +/* + * Common MC error handling code. + */ +static irqreturn_t tegra_mc_error_isr(int irq, void *data) +{ + int err_mc = 0; + struct mc_client *client = NULL; + const struct mc_error *fault; + const char *smmu_info; + unsigned long count; + phys_addr_t addr; + u32 status, intr; + u32 write, secure; + u32 client_id; + + intr = mc_readl(MC_INT_STATUS); + + cancel_delayed_work(&unthrottle_prints_work); + +#ifdef MC_DUAL_CHANNEL + /* + * Interrupts can come from either MC; handle the case in which the + * interrupt is generated by the second MC. + */ + if (intr & MC_INT_EXT_INTR_IN) { + err_mc = 1; + intr = __mc_readl(err_mc, MC_INT_STATUS); + } +#endif + + /* + * Sometimes the MC seems to generate spurious interrupts - that + * is interrupts with an interrupt status register equal to 0. + * Not much we can do other than keep a count of them. + */ + if (!intr) { + spurious_intrs++; + goto out; + } + + intr &= MC_INT_EN_MASK; + if (intr & MC_INT_ARBITRATION_EMEM) { + arb_intr(); + if (intr == MC_INT_ARBITRATION_EMEM) + goto out; + intr &= ~MC_INT_ARBITRATION_EMEM; + } + + spin_lock(&mc_lock); + count = ++error_count; + spin_unlock(&mc_lock); + + fault = chip_specific.mcerr_info(intr & MC_INT_EN_MASK); + if (WARN(!fault, "[mcerr] Unknown error! intr sig: 0x%08x\n", + intr & MC_INT_EN_MASK)) + goto out; + + if (fault->flags & E_NO_STATUS) { + pr_err("[mcerr] MC fault - no status: %s\n", fault->msg); + goto out; + } + + status = __mc_readl(err_mc, fault->stat_reg); + addr = __mc_readl(err_mc, fault->addr_reg); + secure = !!(status & MC_ERR_STATUS_SECURE); + write = !!(status & MC_ERR_STATUS_WRITE); + client_id = status & 0x7f; + client = &mc_clients[client_id <= mc_client_last + ? client_id : mc_client_last]; + +#ifdef MC_ERR_34BIT_PHYS_ADDR + /* + * 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); +#endif + + if (fault->flags & E_SMMU) + smmu_info = smmu_page_attrib[MC_ERR_SMMU_BITS(status)]; + else + smmu_info = NULL; + + chip_specific.mcerr_info_update(client, intr & MC_INT_EN_MASK); + + if (mcerr_throttle_enabled && count >= MAX_PRINTS) { + schedule_delayed_work(&unthrottle_prints_work, HZ/2); + if (count == MAX_PRINTS) + pr_err("Too many MC errors; throttling prints\n"); + goto out; + } + + chip_specific.mcerr_print(fault, client, status, addr, secure, write, + smmu_info); +out: + __mc_writel(err_mc, intr, MC_INT_STATUS); + if (err_mc != 0) { + __mc_readl(err_mc, MC_INT_STATUS); + mc_writel(MC_INT_EXT_INTR_IN, MC_INT_STATUS); + } + return IRQ_HANDLED; +} + +static const struct mc_error *mcerr_default_info(u32 intr) +{ + const struct mc_error *err; + + for (err = mc_errors; err->sig && err->msg; err++) { + if (intr != err->sig) + continue; + return err; + } + + return NULL; +} + +static void mcerr_default_info_update(struct mc_client *c, u32 stat) +{ + if (stat & MC_INT_DECERR_EMEM) + c->intr_counts[0]++; + if (stat & MC_INT_SECURITY_VIOLATION) + c->intr_counts[1]++; + if (stat & MC_INT_INVALID_SMMU_PAGE) + c->intr_counts[2]++; + + /* Unknown interrupts. */ + if (stat & ~MC_INT_EN_MASK) + c->intr_counts[3]++; +} + +/* + * This will print at least 8 hex digits for address. If the address is bigger + * then more digits will be printed but the full 16 hex digits for a 64 bit + * address will not get printed by the current code. + */ +static void mcerr_default_print(const struct mc_error *err, + const struct mc_client *client, + u32 status, phys_addr_t addr, + int secure, int rw, const char *smmu_info) +{ + pr_err("[mcerr] (%s) %s: %s\n", client->swgid, client->name, err->msg); + pr_err("[mcerr] status = 0x%08x; addr = 0x%08llx", status, + (long long unsigned int)addr); + pr_err("[mcerr] secure: %s, access-type: %s, SMMU fault: %s\n", + secure ? "yes" : "no", rw ? "write" : "read", + smmu_info ? smmu_info : "none"); +} + +/* + * Print the MC err stats for each client. + */ +static int mcerr_default_debugfs_show(struct seq_file *s, void *v) +{ + int i, j; + int do_print; + + seq_printf(s, "%-24s %-24s %-9s %-9s %-9s %-9s\n", "swgid", "client", + "decerr", "secerr", "smmuerr", "unknown"); + for (i = 0; i < chip_specific.nr_clients; i++) { + do_print = 0; + + /* Only print clients who actually have errors. */ + for (j = 0; j < INTR_COUNT; j++) { + if (mc_clients[i].intr_counts[j]) { + do_print = 1; + break; + } + } + + if (do_print) + seq_printf(s, "%-24s %-24s %-9u %-9u %-9u %-9u\n", + mc_clients[i].name, + mc_clients[i].swgid, + mc_clients[i].intr_counts[0], + mc_clients[i].intr_counts[1], + mc_clients[i].intr_counts[2], + mc_clients[i].intr_counts[3]); + } + return 0; +} + +static int mcerr_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, chip_specific.mcerr_debugfs_show, NULL); +} + +static const struct file_operations mcerr_debugfs_fops = { + .open = mcerr_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __get_throttle(void *data, u64 *val) +{ + *val = mcerr_throttle_enabled; + return 0; +} + +static int __set_throttle(void *data, u64 val) +{ + unsigned long flags; + + spin_lock_irqsave(&mc_lock, flags); + error_count = 0; + spin_unlock_irqrestore(&mc_lock, flags); + + mcerr_throttle_enabled = (bool) val; + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(mcerr_throttle_debugfs_fops, __get_throttle, + __set_throttle, "%llu\n"); + +/* + * This will always e successful. However, if something goes wrong in the + * init a message will be printed to the kernel log. Since this is a + * non-essential piece of the kernel no reason to fail the entire MC init + * if this fails. + */ +int __init tegra_mcerr_init(struct dentry *mc_parent) +{ + u32 reg; + + chip_specific.mcerr_info = mcerr_default_info; + chip_specific.mcerr_info_update = mcerr_default_info_update; + chip_specific.mcerr_print = mcerr_default_print; + chip_specific.mcerr_debugfs_show = mcerr_default_debugfs_show; + chip_specific.nr_clients = 0; + + /* + * mcerr_chip_specific_setup() can override any of the default + * functions as it wishes. + */ + mcerr_chip_specific_setup(&chip_specific); + + if (request_irq(INT_MC_GENERAL, tegra_mc_error_isr, 0, + "mc_status", NULL)) { + pr_err("%s: unable to register MC error interrupt\n", __func__); + goto done; + } else { + reg = MC_INT_EN_MASK; + mc_writel(reg, MC_INT_MASK); + } + + mcerr_debugfs_dir = debugfs_create_dir("err", mc_parent); + if (mcerr_debugfs_dir == NULL) { + pr_err("Failed to make debugfs node: %ld\n", + PTR_ERR(mcerr_debugfs_dir)); + goto done; + } + debugfs_create_file("mcerr", 0644, mcerr_debugfs_dir, NULL, + &mcerr_debugfs_fops); + debugfs_create_file("mcerr_throttle", S_IRUGO | S_IWUSR, + mcerr_debugfs_dir, NULL, + &mcerr_throttle_debugfs_fops); + + pr_info("Started MC error interface!\n"); + +done: + return 0; +} diff --git a/drivers/platform/tegra/mcerr-t12.c b/drivers/platform/tegra/mcerr-t12.c deleted file mode 100644 index 8339e4c9cc39..000000000000 --- a/drivers/platform/tegra/mcerr-t12.c +++ /dev/null @@ -1,2 +0,0 @@ -/* Automatically generated file; DO NOT EDIT. */ -#include "../../../arch/arm/mach-tegra/mcerr-t12.c" diff --git a/drivers/platform/tegra/mcerr.c b/drivers/platform/tegra/mcerr.c deleted file mode 100644 index e09cf476ea49..000000000000 --- a/drivers/platform/tegra/mcerr.c +++ /dev/null @@ -1,2 +0,0 @@ -/* Automatically generated file; DO NOT EDIT. */ -#include "../../../arch/arm/mach-tegra/mcerr.c" |