diff options
author | Hiro Sugawara <hsugawara@nvidia.com> | 2011-01-19 13:57:30 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:42:13 -0800 |
commit | c00e431f2dcc108084381b9afb37567cd734ccaa (patch) | |
tree | e8cad89623c0077e8bc8c166cc8acafaf7f65e01 /arch/arm/mach-tegra/common-t3.c | |
parent | b043bd2cc95f3e61c89a6084ed48a03df425b1a5 (diff) |
[arm:tegra] Adding MC_DECERR interrupt handler
Adding MC_DECERR interrupt handler ported from Froyo.
This addition will not gracefully terminate a failing DMA transfer.
The handler does noting but simply reporting the error status with prink,
and the clinet software will likely hang forever waiting for a non-
completing DMA transfer. But it is still useful for debugging.
Reviewed-on: http://git-master/r/16289
(cherry picked from commit 4c66e8b978f054b332c21a97a53d89f588d24889)
Original-Change-Id: I7b19c70d8cbb62be9ab3f955bf19c707c1e5045d
Reviewed-on: http://git-master/r/16590
Tested-by: Hiro Sugawara <hsugawara@nvidia.com>
Reviewed-by: Scott Williams <scwilliams@nvidia.com>
Tested-by: Scott Williams <scwilliams@nvidia.com>
Original-Change-Id: Ibdcfe63d56d22e39d8c5398ff50eb663bd0d82f3
Rebase-Id: R5a24a2ae2ab4585c3d48c76761beef815a665649
Diffstat (limited to 'arch/arm/mach-tegra/common-t3.c')
-rw-r--r-- | arch/arm/mach-tegra/common-t3.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/common-t3.c b/arch/arm/mach-tegra/common-t3.c new file mode 100644 index 000000000000..b2b9a0b1d9e7 --- /dev/null +++ b/arch/arm/mach-tegra/common-t3.c @@ -0,0 +1,197 @@ +/* + * arch/arm/mach-tegra/common-t3.c + * + * Tegra 3 SoC-specific initialization (memory controller, etc.) + * + * Copyright (c) 2010-2011, NVIDIA Corporation. + * + * 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/kernel.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> + +#include <mach/iomap.h> +#include <mach/irqs.h> + +#define MC_INT_STATUS 0x0 +#define MC_INT_MASK 0x4 +#define MC_INT_DECERR_EMEM (1<<6) +#define MC_INT_SECURITY_VIOLATION (1<<8) +#define MC_INT_ARBITRATION_EMEM (1<<9) +#define MC_INT_INVALID_SMMU_PAGE (1<<10) + +#define MC_ERROR_STATUS 0x8 +#define MC_ERROR_ADDRESS 0xC + +struct mc_client { + const char *name; +}; + +#define client(_name) \ + { \ + .name = _name, \ + } + +static const struct mc_client mc_clients[] = { + client("ptc"), + client("display0_wina"), client("display1_wina"), + client("display0_winb"), client("display1_winb"), + client("display0_winc"), client("display1_winc"), + client("display0_winb_vfilter"), + client("display1_winb_vfilter"), + client("epp"), client("gr2d_pat"), + client("gr2d_src"), client("mpe_unified"), + client("vi_chroma_filter"), client("pcie"), + client("avp"), + client("display0_cursor"), client("display1_cursor"), + client("gr3d0_fdc"), client("gr3d1_fdc"), + client("gr2d_dst"), client("hda"), + client("host1x_dma"), client("host1x_generic"), + client("gr3d0_idx"), client("gr3d1_idx"), + client("mpe_intrapred"), client("mpe_mpea"), + client("mpe_mpec"), client("ahb_dma"), + client("ahb_slave"), client("sata"), + client("gr3d0_tex"), client("gr3d1_tex"), + client("vde_bsev"), client("vde_mbe"), + client("vde_mce"), client("vde_tpe"), + client("cpu_lp"), client("cpu"), + client("epp_u"), client("epp_v"), + client("epp_y"), client("mpe_unified"), + client("vi_sb"), client("vi_u"), + client("vi_v"), client("vi_y"), + client("gr2d_dst"), client("pcie"), + client("avp"), client("gr3d0_fdc"), + client("gr3d1_fdc"), client("hda"), + client("host1x"), client("isp"), + client("cpu_lp"), client("cpu"), + client("mpe_mpec"), client("ahb_dma"), + client("ahb_slave"), client("sata"), + client("vde_bsev"), client("vde_dbg"), + client("vde_mbe"), client("vde_tpm"), +}; + +static const char *smmu_page_attrib[] = { + "SMMU: nr-nw-s", + "SMMU: nr-nw-ns", + "SMMU: nr-wr-s", + "SMMU: nr-wr-ns", + "SMMU: rd-nw-s", + "SMMU: rd-nw-ns", + "SMMU: rd-wr-s", + "SMMU: rd-wr-ns" +}; + +static DEFINE_SPINLOCK(mc_lock); +static unsigned long error_count = 0; +#define MAX_PRINTS 5 + +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); +} + +static DECLARE_DELAYED_WORK(unthrottle_prints_work, unthrottle_prints); + +static irqreturn_t tegra_mc_error_isr(int irq, void *data) +{ + void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE); + const struct mc_client *client = NULL; + const char *mc_err; + const char *mc_err_info; + unsigned long count; + u32 stat; + u32 addr; + u32 err; + u32 type; + u32 is_write; + u32 is_secure; + u32 client_id; + + stat = readl(mc + MC_INT_STATUS); + stat &= (MC_INT_DECERR_EMEM | + MC_INT_SECURITY_VIOLATION | + MC_INT_INVALID_SMMU_PAGE); + + cancel_delayed_work(&unthrottle_prints_work); + + spin_lock(&mc_lock); + count = ++error_count; + spin_unlock(&mc_lock); + + if (count >= MAX_PRINTS) { + if (count == MAX_PRINTS) + pr_err("Too many MC errors; throttling prints\n"); + schedule_delayed_work(&unthrottle_prints_work, HZ/2); + goto out; + } + + err = readl(mc + MC_ERROR_STATUS); + addr = readl(mc + MC_ERROR_ADDRESS); + is_write = err & (1<<16); + is_secure = err & (1<<17); + type = (err >> 28) & 7; + client_id = err & 0x7f; + if (client_id < ARRAY_SIZE(mc_clients)) + client = &mc_clients[client_id]; + + if (stat & MC_INT_DECERR_EMEM) + mc_err = "MC_DECERR"; + else if (stat & MC_INT_SECURITY_VIOLATION) + mc_err = "MC_SECURITY_ERR"; + else if (stat & MC_INT_INVALID_SMMU_PAGE) + mc_err = "MC_SMMU_ERR"; + else + mc_err = "unknown"; + + mc_err_info = ""; + if (type == 3) { + mc_err_info = "SECURITY_TRUSTZONE"; + } else if (type == 4) { + mc_err_info = "SECURITY_CARVEOUT"; + } else if (type == 6) { + u32 attrib = (err >> 25) & 7; + mc_err_info = smmu_page_attrib[attrib]; + } + + pr_err("%s (0x%08X): %p %s (%s %s %s)\n", mc_err, err, (void*)addr, + (client) ? client->name : "unknown", + (is_secure)? "secure" : "non-secure", + (is_write) ? "write" : "read", + mc_err_info); + +out: + writel(stat, mc + MC_INT_STATUS); + return IRQ_HANDLED; +} + +void __init tegra_mc_init(void) +{ + 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__); + } else { + void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE); + u32 reg = MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION | + MC_INT_INVALID_SMMU_PAGE; + writel(reg, mc + MC_INT_MASK); + } +} |