| /* |
| * linux/drivers/marvell/mmp-debug.c |
| * |
| * Author: Neil Zhang <zhangwm@marvell.com> |
| * Copyright: (C) 2013 Marvell International Ltd. |
| * |
| * 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. |
| */ |
| |
| #include <linux/io.h> |
| #include <linux/of.h> |
| #include <linux/of_address.h> |
| #include <linux/init.h> |
| #include <linux/reboot.h> |
| #include <linux/kexec.h> |
| #include <linux/signal.h> |
| #include <linux/features.h> |
| #include <soc/asr/regs-addr.h> |
| |
| #include <asm/cacheflush.h> |
| #include <asm/system_misc.h> |
| |
| #ifdef CONFIG_CPU_ASR1901 |
| #define FAB_TIMEOUT_CTRL 0x230 |
| #define FAB_TIMEOUT_STATUS0 0x264 |
| #define FAB_TIMEOUT_STATUS1 0x268 |
| #define FAB_TIMEOUT_ID 0x260 |
| #else |
| #define FAB_TIMEOUT_CTRL 0x60 |
| #define FAB_TIMEOUT_STATUS0 0x68 |
| #define FAB_TIMEOUT_STATUS1 0x70 |
| #define FAB_TIMEOUT_ID 0x78 |
| #endif |
| |
| static void __iomem *squ_base, *apmu_base; |
| static u32 fab_timeout_write_addr, fab_timeout_read_addr; |
| |
| static int mmp_axi_timeout(unsigned long addr, unsigned int fsr, |
| struct pt_regs *regs) |
| { |
| u32 tmp; |
| (void) addr; |
| (void) fsr; |
| (void) regs; |
| |
| tmp = readl_relaxed(squ_base + FAB_TIMEOUT_STATUS0); |
| fab_timeout_write_addr = tmp & 0xfffffffc; |
| tmp = readl_relaxed(squ_base + FAB_TIMEOUT_STATUS1); |
| fab_timeout_read_addr = tmp & 0xfffffffc; |
| |
| /* Return For those not caused by AXI timeout */ |
| if (!fab_timeout_write_addr && !fab_timeout_read_addr) |
| return 1; |
| |
| pr_info("********** mmp_axi_timeout **********\n"); |
| pr_info("write_addr=0x%x read_addr=0x%x", |
| fab_timeout_write_addr, fab_timeout_read_addr); |
| pr_info("FAB_TIMEOUT_ID=0x%x\n", |
| readl_relaxed(squ_base + FAB_TIMEOUT_ID)); |
| pr_info("FAB_TIMEOUT_STATUS0=0x%x\n", |
| readl_relaxed(squ_base + FAB_TIMEOUT_STATUS0)); |
| pr_info("FAB_TIMEOUT_STATUS1=0x%x\n", |
| readl_relaxed(squ_base + FAB_TIMEOUT_STATUS1)); |
| |
| pr_info("PMU_SDH0_CLK_RES_CTRL=0x%x\n", |
| readl_relaxed(apmu_base + 0x54)); |
| pr_info("PMU_SDH1_CLK_RES_CTRL=0x%x\n", |
| readl_relaxed(apmu_base + 0x58)); |
| pr_info("PMU_QSPI_CLK_RES_CTRL=0x%x\n", |
| readl_relaxed(apmu_base + 0x60)); |
| #ifdef CONFIG_CPU_ASR1901 |
| pr_info("XGMAC_CLK_RST_CTRL=0x%x\n", |
| readl_relaxed(apmu_base + 0x3d4)); |
| #else |
| pr_info("EMAC_CLK_RST_CTRL=0x%x\n", |
| readl_relaxed(apmu_base + 0x160)); |
| #endif |
| pr_info("*************************************\n"); |
| |
| /* return 1 to trigger ramdump later */ |
| return 1; |
| } |
| |
| static int __init mmp_debug_init(void) |
| { |
| struct device_node *node; |
| u32 tmp; |
| |
| node = of_find_compatible_node(NULL, NULL, "mrvl,mmp-debug"); |
| if (!node) { |
| pr_info("This platform doesn't support debug feature!\n"); |
| return 0; |
| } |
| |
| squ_base = of_iomap(node, 0); |
| apmu_base = regs_addr_get_va(REGS_ADDR_APMU); |
| |
| pr_info("axi facbric monitor last save info:\n"); |
| pr_info("FAB_TIMEOUT_ID=0x%x\n", |
| readl_relaxed(squ_base + FAB_TIMEOUT_ID)); |
| pr_info("FAB_TIMEOUT_STATUS0=0x%x\n", |
| readl_relaxed(squ_base + FAB_TIMEOUT_STATUS0)); |
| pr_info("FAB_TIMEOUT_STATUS1=0x%x\n", |
| readl_relaxed(squ_base + FAB_TIMEOUT_STATUS1)); |
| |
| /* clear last saved information */ |
| tmp = readl_relaxed(squ_base + FAB_TIMEOUT_CTRL); |
| tmp |= 1 << 27; |
| writel_relaxed(tmp, squ_base + FAB_TIMEOUT_CTRL); |
| |
| /* reset axi fabric monitor */ |
| tmp = readl_relaxed(squ_base + FAB_TIMEOUT_CTRL); |
| tmp &= ~(1 << 28); |
| writel_relaxed(tmp, squ_base + FAB_TIMEOUT_CTRL); |
| |
| /* configure to data abort mode */ |
| tmp = readl_relaxed(squ_base + FAB_TIMEOUT_CTRL); |
| tmp &= ~(1 << 31); |
| tmp |= 1 << 30; |
| tmp |= 1 << 29; /* mask timeout interrupt */ |
| tmp |= 1 << 28; |
| tmp &= ~(1 << 27); |
| #ifdef CONFIG_CPU_ASR1901 |
| tmp &= ~(1 << 24); |
| #endif |
| writel_relaxed(tmp, squ_base + FAB_TIMEOUT_CTRL); |
| pr_info("enable mmp axi facbric monitor, FAB_TIMEOUT_CTRL=0x%x\n", |
| readl_relaxed(squ_base + FAB_TIMEOUT_CTRL)); |
| |
| /* Register debug fault handler. */ |
| #ifdef CONFIG_ARM |
| hook_fault_code(0x8, mmp_axi_timeout, SIGBUS, |
| 0, "external abort on non-linefetch"); |
| hook_fault_code(0xc, mmp_axi_timeout, SIGBUS, |
| 0, "external abort on translation"); |
| hook_fault_code(0xe, mmp_axi_timeout, SIGBUS, |
| 0, "external abort on translation"); |
| hook_fault_code(0x16, mmp_axi_timeout, SIGBUS, |
| BUS_OBJERR, "imprecise external abort"); |
| #endif |
| |
| #ifdef CONFIG_ARM64 |
| hook_fault_code(0x10, mmp_axi_timeout, SIGBUS, |
| 0, "synchronous external abort"); |
| hook_fault_code(0x11, mmp_axi_timeout, SIGBUS, |
| 0, "asynchronous external abort"); |
| #endif |
| |
| return 0; |
| } |
| |
| arch_initcall(mmp_debug_init); |