diff options
Diffstat (limited to 'arch/arm/mach-ux500/prcmu-debug.c')
-rw-r--r-- | arch/arm/mach-ux500/prcmu-debug.c | 1041 |
1 files changed, 1041 insertions, 0 deletions
diff --git a/arch/arm/mach-ux500/prcmu-debug.c b/arch/arm/mach-ux500/prcmu-debug.c new file mode 100644 index 00000000000..6842e4b68fe --- /dev/null +++ b/arch/arm/mach-ux500/prcmu-debug.c @@ -0,0 +1,1041 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * + * Author: Martin Persson for ST-Ericsson + * Etienne Carriere <etienne.carriere@stericsson.com> for ST-Ericsson + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/mfd/dbx500-prcmu.h> + +#include <mach/hardware.h> + +#define MAX_STATES 5 +#define MAX_NAMELEN 16 +#define U8500_PRCMU_TCDM_SIZE 4096 + +struct state_history { + ktime_t start; + u32 state; + u32 counter[MAX_STATES]; + u8 opps[MAX_STATES]; + int max_states; + int req; + bool reqs[MAX_STATES]; + ktime_t time[MAX_STATES]; + int state_names[MAX_STATES]; + char prefix[MAX_NAMELEN]; + spinlock_t lock; +}; + +static struct state_history ape_sh = { + .prefix = "APE", + .req = PRCMU_QOS_APE_OPP, + .opps = {APE_50_OPP, APE_100_OPP}, + .state_names = {50, 100}, + .max_states = 2, +}; + +static struct state_history ddr_sh = { + .prefix = "DDR", + .req = PRCMU_QOS_DDR_OPP, + .opps = {DDR_25_OPP, DDR_50_OPP, DDR_100_OPP}, + .state_names = {25, 50, 100}, + .max_states = 3, +}; + +static struct state_history arm_sh = { + .prefix = "ARM", + .req = PRCMU_QOS_ARM_OPP, + .opps = {ARM_EXTCLK, ARM_50_OPP, ARM_100_OPP, ARM_MAX_OPP}, + .state_names = {25, 50, 100, 125}, + .max_states = 4, +}; + +static const u16 u8500_prcmu_dump_regs[] = { + /*ARMCLKFIX_MGT*/ 0x0, /*ACLK_MGT*/ 0x4, + /*SVAMMDSPCLK_MGT*/ 0x8, /*SIAMMDSPCLK_MGT*/ 0xc, + /*SGACLK_MGT*/ 0x14, /*UARTCLK_MGT*/ 0x18, + /*MSP02CLK_MGT*/ 0x1c, /*I2CCLK_MGT*/ 0x20, + /*SDMMCCLK_MGT*/ 0x24, /*SLIMCLK_MGT*/ 0x28, + /*PER1CLK_MGT*/ 0x2c, /*PER2CLK_MGT*/ 0x30, + /*PER3CLK_MGT*/ 0x34, /*PER5CLK_MGT*/ 0x38, + /*PER6CLK_MGT*/ 0x3c, /*PER7CLK_MGT*/ 0x40, + /*LCDCLK_MGT*/ 0x44, /*SPARE1CLK_MGT*/ 0x48, + /*BMLCLK_MGT*/ 0x4c, /*HSITXCLK_MGT*/ 0x50, + /*HSIRXCLK_MGT*/ 0x54, /*HDMICLK_MGT*/ 0x58, + /*APEATCLK_MGT*/ 0x5c, /*APETRACECLK_MGT*/ 0x60, + /*MCDECLK_MGT*/ 0x64, /*IPI2CCLK_MGT*/ 0x68, + /*DSIALTCLK_MGT*/ 0x6c, /*SPARE2CLK_MGT*/ 0x70, + /*DMACLK_MGT*/ 0x74, /*B2R2CLK_MGT*/ 0x78, + /*TVCLK_MGT*/ 0x7c, /*PLLSOC0_FREQ*/ 0x80, + /*PLLSOC1_FREQ*/ 0x84, /*PLLARM_FREQ*/ 0x88, + /*PLLDDR_FREQ*/ 0x8c, /*PLLSOC0_ENABLE*/ 0x90, + /*PLLSOC1_ENABLE*/ 0x94, /*PLLARM_ENABLE*/ 0x98, + /*PLLDDR_ENABLE*/ 0x9c, /*PLLSOC0_LOCKP*/ 0xa0, + /*PLLSOC1_LOCKP*/ 0xa4, /*PLLARM_LOCKP*/ 0xa8, + /*PLLDDR_LOCKP*/ 0xac, /*XP70CLK_MGT*/ 0xb0, + /*TIMER_0_REF*/ 0xb4, /*TIMER_0_DOWNCOUNT*/ 0xb8, + /*TIMER_0_MODE*/ 0xbc, /*TIMER_1_REF*/ 0xc0, + /*TIMER_1_DOWNCOUNT*/ 0xc4, /*TIMER_1_MODE*/ 0xc8, + /*TIMER_2_REF*/ 0xcc, /*TIMER_2_DOWNCOUNT*/ 0xd0, + /*TIMER_2_MODE*/ 0xd4, /*CLK009_MGT*/ 0xe4, + /*MODECLK*/ 0xe8, /*4500_CLK_REQ*/ 0xf8, + /*MBOX_CPU_VAL*/ 0xfc, /*PLL32K_ENABLE*/ 0x10c, + /*PLL32K_LOCKP*/ 0x110, /*ARM_CHGCLKREQ*/ 0x114, + /*ARM_PLLDIVPS*/ 0x118, /*ARMITMSK31TO0*/ 0x11c, + /*ARMITMSK63TO32*/ 0x120, /*ARMITMSK95TO64*/ 0x124, + /*ARMITMSK127TO96*/ 0x128, /*ARMSTANDBY_STATUS*/ 0x130, + /*CGATING_BYPASS*/ 0x134, /*GPIOCR*/ 0x138, + /*LEMI*/ 0x13c, /*COMPCR*/ 0x140, + /*COMPSTA*/ 0x144, /*ITSTATUS0*/ 0x148, + /*ITSTATUS1*/ 0x150, /*ITSTATUS2*/ 0x158, + /*ITSTATUS3*/ 0x160, /*ITSTATUS4*/ 0x168, + /*LINE_VALUE*/ 0x170, /*HOLD_EVT*/ 0x174, + /*EDGE_SENS_L*/ 0x178, /*EDGE_SENS_H*/ 0x17c, + /*DEBUG_CTRL_VAL*/ 0x190, /*DEBUG_NOPWRDOWN_VAL*/ 0x194, + /*DEBUG_CTRL_ACK*/ 0x198, /*A9PL_FORCE_CLKEN*/ 0x19c, + /*TPIU_FLUSHIN_REQ*/ 0x1a0, /*TPIU_FLUSHIN_ACK*/ 0x1a4, + /*STP_FLUSHIN_REQ*/ 0x1a8, /*STP_FLUSHIN_ACK*/ 0x1ac, + /*HWI2C_DIV*/ 0x1b0, /*HWI2C_CMD*/ 0x1b8, + /*HWI2C_DATA123*/ 0x1bc, /*HWI2C_SR*/ 0x1c0, + /*REMAPCR*/ 0x1c4, /*TCR*/ 0x1c8, + /*CLKOCR*/ 0x1cc, /*ITSTATUS_DBG*/ 0x1d0, + /*LINE_VALUE_DBG*/ 0x1d8, /*DBG_HOLD*/ 0x1dc, + /*EDGE_SENS_DBG*/ 0x1e0, /*APE_RESETN_VAL*/ 0x1ec, + /*A9_RESETN_SET*/ 0x1f0, /*A9_RESETN_VAL*/ 0x1f8, + /*MOD_RESETN_VAL*/ 0x204, /*GPIO_RESETN_VAL*/ 0x210, + /*4500_RESETN_VAL*/ 0x21c, /*SWD_RST_TEMPO*/ 0x238, + /*RST_4500_TEMPO*/ 0x23c, /*SVAMMDSP_IT*/ 0x240, + /*SIAMMDSP_IT*/ 0x248, /*POWER_STATE_VAL*/ 0x25c, + /*ARMITVALUE31TO0*/ 0x260, /*ARMITVALUE63TO32*/ 0x264, + /*ARMITVALUE95TO64*/ 0x268, /*ARMITVALUE127TO96*/ 0x26c, + /*REDUN_LOAD*/ 0x270, /*REDUN_STATUS*/ 0x274, + /*UNIPROCLK_MGT*/ 0x278, /*UICCCLK_MGT*/ 0x27c, + /*SSPCLK_MGT*/ 0x280, /*RNGCLK_MGT*/ 0x284, + /*MSP1CLK_MGT*/ 0x288, /*DAP_RESETN_SET*/ 0x2a0, + /*DAP_RESETN_VAL*/ 0x2a8, /*SRAM_DEDCSTOV*/ 0x300, + /*SRAM_LS_SLEEP*/ 0x304, /*SRAM_A9*/ 0x308, + /*ARM_LS_CLAMP*/ 0x30c, /*IOCR*/ 0x310, + /*MODEM_SYSCLKOK*/ 0x314, /*SYSCLKOK_DELAY*/ 0x318, + /*SYSCLKSTATUS*/ 0x31c, /*DSI_SW_RESET*/ 0x324, + /*A9_MASK_REQ*/ 0x328, /*A9_MASK_ACK*/ 0x32c, + /*HOSTACCESS_REQ*/ 0x334, /*TIMER_3_REF*/ 0x338, + /*TIMER_3_DOWNCOUNT*/ 0x33c, /*TIMER_3_MODE*/ 0x340, + /*PMB_SENS_CTRL*/ 0x344, /*PMB_REF_COUNTER*/ 0x348, + /*PMB_SENSOR_STATUS*/ 0x34c, /*APE_EPOD_CFG*/ 0x404, + /*DDR_EPOD_CFG*/ 0x408, /*EPOD_C_VAL*/ 0x418, + /*EPOD_VOK*/ 0x41c, /*MMIP_LS_CLAMP_VAL*/ 0x428, + /*VSAFE_LS_CLAMP_VAL*/ 0x434, /*DDRSUBSYS_APE_MINBW*/ 0x438, + /*DDRSUBSYS_STATUS*/ 0x43c, /*DDRSUBSYS_CONTROL*/ 0x440, + /*DDRSUBSYS_HIGH_LEAK_COND*/ 0x444, /*DDRSUBSYS_CONFIG*/ 0x448, + /*TIMER_4_REF*/ 0x450, /*TIMER_4_DOWNCOUNT*/ 0x454, + /*TIMER_4_MODE*/ 0x458, /*TIMER_5_REF*/ 0x45c, + /*TIMER_5_DOWNCOUNT*/ 0x460, /*TIMER_5_MODE*/ 0x464, + /*APE_MEM_REQ*/ 0x470, /*DBG_FRCS_APE_MEM_REQ*/ 0x474, + /*APE_MEM_WFX_EN*/ 0x478, /*APE_MEM_LATENCY*/ 0x47c, + /*APE_MEM_ACK*/ 0x480, /*ITSTATUS5*/ 0x484, + /*ARM_IT1_VAL*/ 0x494, /*MOD_PWR_OK*/ 0x498, + /*MOD_AUXCLKOK*/ 0x49c, /*MOD_AWAKE_STATUS*/ 0x4a0, + /*MOD_SWRESET_IRQ_ACK*/ 0x4a4, /*MOD_SWRESET_ACK*/ 0x4a8, + /*DBG_PWRCTL*/ 0x4ac, /*HWOBS_H*/ 0x4b0, + /*HWOBS_L*/ 0x4b4, /*PLLDSI_FREQ*/ 0x500, + /*PLLDSI_ENABLE*/ 0x504, /*PLLDSI_LOCKP*/ 0x508, + /*RNG_ENABLE*/ 0x50c, /*YYCLKEN0_MGT_SET*/ 0x510, + /*YYCLKEN0_MGT_VAL*/ 0x520, /*YYCLKEN1_MGT_VAL*/ 0x524, + /*XP70CLK_MGT2*/ 0x528, /*DSITVCLK_DIV*/ 0x52c, + /*DSI_PLLOUT_SEL*/ 0x530, /*DSI_GLITCHFREE_EN*/ 0x534, + /*CLKACTIV*/ 0x538, /*SIA_MMDSP_MEM_MGT*/ 0x53c, + /*SVA_MMDSP_MEM_MGT*/ 0x540, /*SXAMMDSP_FORCE_CLKEN*/ 0x544, + /*UICC_NANDTREE*/ 0x570, /*GPIOCR2*/ 0x574, + /*MDM_ACWAKE*/ 0x578, /*MOD_MEM_REQ*/ 0x5a4, + /*MOD_MEM_ACK*/ 0x5a8, /*ARM_PLLDIVPS_REQ*/ 0x5b0, + /*ARM_PLLDIVPS_ACK*/ 0x5b4, /*SRPTIMER_VAL*/ 0x5d0, +}; + +/* Offsets from secure base which is U8500_PRCMU_BASE + SZ_4K */ +static const u16 u8500_prcmu_dump_secure_regs[] = { + /*SECNONSEWM*/ 0x00, /*ESRAM0_INITN*/ 0x04, + /*ARMITMSKSEC_31TO0*/ 0x08, /*ARMITMSKSEC_63TO32*/ 0x0C, + /*ARMITMSKSEC_95TO64*/ 0x10, /*ARMITMSKSEC_127TO96*/ 0x14, + /*ARMIT_MASKXP70_IT*/ 0x18, /*ESRAM0_EPOD_CFG*/ 0x1C, + /*ESRAM0_EPOD_C_VAL*/ 0x20, /*ESRAM0_EPOD_VOK*/ 0x2C, + /*ESRAM0_LS_SLEEP*/ 0x30, /*SECURE_ONGOING*/ 0x34, + /*I2C_SECURE*/ 0x38, /*RESET_STATUS*/ 0x3C, + /*PERIPH4_RESETN_VAL*/ 0x48, /*SPAREOUT_SEC*/ 0x4C, + /*PIPELINEDCR*/ 0xD8, +}; + +static int ape_voltage_count; + +static void log_set(struct state_history *sh, u8 opp) +{ + ktime_t now; + ktime_t dtime; + unsigned long flags; + int state; + + now = ktime_get(); + spin_lock_irqsave(&sh->lock, flags); + + for (state = 0 ; sh->opps[state] != opp; state++) + ; + BUG_ON(state >= sh->max_states); + + dtime = ktime_sub(now, sh->start); + sh->time[sh->state] = ktime_add(sh->time[sh->state], dtime); + sh->start = now; + sh->counter[sh->state]++; + sh->state = state; + + spin_unlock_irqrestore(&sh->lock, flags); +} + +void prcmu_debug_ape_opp_log(u8 opp) +{ + if (opp == APE_50_PARTLY_25_OPP) + opp = APE_50_OPP; + + log_set(&ape_sh, opp); +} + +void prcmu_debug_ddr_opp_log(u8 opp) +{ + log_set(&ddr_sh, opp); +} + +void prcmu_debug_arm_opp_log(u8 opp) +{ + log_set(&arm_sh, opp); +} + +static void log_reset(struct state_history *sh) +{ + unsigned long flags; + int i; + + pr_info("reset\n"); + + spin_lock_irqsave(&sh->lock, flags); + for (i = 0; i < sh->max_states; i++) { + sh->counter[i] = 0; + sh->time[i] = ktime_set(0, 0); + } + + sh->start = ktime_get(); + spin_unlock_irqrestore(&sh->lock, flags); + +} + +static ssize_t ape_stats_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + log_reset(&ape_sh); + return count; +} + +static ssize_t ddr_stats_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + log_reset(&ddr_sh); + return count; +} + +static ssize_t arm_stats_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + log_reset(&arm_sh); + return count; +} + +static int log_print(struct seq_file *s, struct state_history *sh) +{ + int i; + unsigned long flags; + ktime_t total; + ktime_t dtime; + s64 t_ms; + s64 perc; + s64 total_ms; + + spin_lock_irqsave(&sh->lock, flags); + + dtime = ktime_sub(ktime_get(), sh->start); + + total = dtime; + + for (i = 0; i < sh->max_states; i++) + total = ktime_add(total, sh->time[i]); + total_ms = ktime_to_ms(total); + + for (i = 0; i < sh->max_states; i++) { + ktime_t t = sh->time[i]; + if (sh->state == i) + t = ktime_add(t, dtime); + + t_ms = ktime_to_ms(t); + perc = 100 * t_ms; + do_div(perc, total_ms); + + seq_printf(s, "%s OPP %d: # %u in %lld ms %d%%\n", + sh->prefix, sh->state_names[i], + sh->counter[i] + (int)(sh->state == i), + t_ms, (u32)perc); + + } + spin_unlock_irqrestore(&sh->lock, flags); + return 0; +} + +static int ape_stats_print(struct seq_file *s, void *p) +{ + log_print(s, &ape_sh); + return 0; +} + +static int ddr_stats_print(struct seq_file *s, void *p) +{ + log_print(s, &ddr_sh); + return 0; +} + +static int arm_stats_print(struct seq_file *s, void *p) +{ + log_print(s, &arm_sh); + return 0; +} + +static int opp_read(struct seq_file *s, void *p) +{ + int opp; + + struct state_history *sh = (struct state_history *)s->private; + + switch (sh->req) { + case PRCMU_QOS_DDR_OPP: + opp = prcmu_get_ddr_opp(); + seq_printf(s, "%s (%d)\n", + (opp == DDR_100_OPP) ? "100%" : + (opp == DDR_50_OPP) ? "50%" : + (opp == DDR_25_OPP) ? "25%" : + "unknown", opp); + break; + case PRCMU_QOS_APE_OPP: + opp = prcmu_get_ape_opp(); + seq_printf(s, "%s (%d)\n", + (opp == APE_100_OPP) ? "100%" : + (opp == APE_50_OPP) ? "50%" : + "unknown", opp); + break; + case PRCMU_QOS_ARM_OPP: + opp = prcmu_get_arm_opp(); + seq_printf(s, "%s (%d)\n", + (opp == ARM_MAX_OPP) ? "max" : + (opp == ARM_MAX_FREQ100OPP) ? "max-freq100" : + (opp == ARM_100_OPP) ? "100%" : + (opp == ARM_50_OPP) ? "50%" : + (opp == ARM_EXTCLK) ? "25% (extclk)" : + "unknown", opp); + break; + default: + break; + } + return 0; + +} + +static ssize_t opp_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + long unsigned i; + int err; + struct state_history *sh = (struct state_history *) + ((struct seq_file *)file->private_data)->private; + + err = kstrtoul_from_user(user_buf, count, 0, &i); + + if (err) + return err; + + prcmu_qos_force_opp(sh->req, i); + + pr_info("prcmu debug: forced OPP for %s to %d\n", sh->prefix, (int)i); + + return count; +} + +static int cpufreq_delay_read(struct seq_file *s, void *p) +{ + return seq_printf(s, "%lu\n", prcmu_qos_get_cpufreq_opp_delay()); +} + +static int ape_voltage_read(struct seq_file *s, void *p) +{ + return seq_printf(s, "This reference count only includes " + "requests via debugfs.\nCount: %d\n", + ape_voltage_count); +} + +static ssize_t ape_voltage_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + long unsigned i; + int err; + + err = kstrtoul_from_user(user_buf, count, 0, &i); + + if (err) + return err; + + switch (i) { + case 0: + if (ape_voltage_count == 0) + pr_info("prcmu debug: reference count is already 0\n"); + else { + err = prcmu_request_ape_opp_100_voltage(false); + if (err) + pr_err("prcmu debug: drop request failed\n"); + else + ape_voltage_count--; + } + break; + case 1: + err = prcmu_request_ape_opp_100_voltage(true); + if (err) + pr_err("prcmu debug: request failed\n"); + else + ape_voltage_count++; + break; + default: + pr_info("prcmu debug: value not equal to 0 or 1\n"); + } + return count; +} + +static ssize_t cpufreq_delay_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + long unsigned i; + + err = kstrtoul_from_user(user_buf, count, 0, &i); + + if (err) + return err; + + prcmu_qos_set_cpufreq_opp_delay(i); + + pr_info("prcmu debug: changed delay between cpufreq change and QoS " + "requirement to %lu.\n", i); + + return count; +} + +/* These are only for u8500 */ +#define PRCM_AVS_BASE 0x2FC +#define AVS_VBB_RET 0x0 +#define AVS_VBB_MAX_OPP 0x1 +#define AVS_VBB_100_OPP 0x2 +#define AVS_VBB_50_OPP 0x3 +#define AVS_VARM_MAX_OPP 0x4 +#define AVS_VARM_100_OPP 0x5 +#define AVS_VARM_50_OPP 0x6 +#define AVS_VARM_RET 0x7 +#define AVS_VAPE_100_OPP 0x8 +#define AVS_VAPE_50_OPP 0x9 +#define AVS_VMOD_100_OPP 0xA +#define AVS_VMOD_50_OPP 0xB +#define AVS_VSAFE 0xC +#define AVS_VSAFE_RET 0xD +#define AVS_SIZE 14 + +static int avs_read(struct seq_file *s, void *p) +{ + + u8 avs[AVS_SIZE]; + void __iomem *tcdm_base; + + if (cpu_is_u8500()) { + tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE); + + memcpy_fromio(avs, tcdm_base + PRCM_AVS_BASE, AVS_SIZE); + + seq_printf(s, "VBB_RET : 0x%02x\n", avs[AVS_VBB_RET]); + seq_printf(s, "VBB_MAX_OPP : 0x%02x\n", avs[AVS_VBB_MAX_OPP]); + seq_printf(s, "VBB_100_OPP : 0x%02x\n", avs[AVS_VBB_100_OPP]); + seq_printf(s, "VBB_50_OPP : 0x%02x\n", avs[AVS_VBB_50_OPP]); + seq_printf(s, "VARM_MAX_OPP : 0x%02x\n", avs[AVS_VARM_MAX_OPP]); + seq_printf(s, "VARM_100_OPP : 0x%02x\n", avs[AVS_VARM_100_OPP]); + seq_printf(s, "VARM_50_OPP : 0x%02x\n", avs[AVS_VARM_50_OPP]); + seq_printf(s, "VARM_RET : 0x%02x\n", avs[AVS_VARM_RET]); + seq_printf(s, "VAPE_100_OPP : 0x%02x\n", avs[AVS_VAPE_100_OPP]); + seq_printf(s, "VAPE_50_OPP : 0x%02x\n", avs[AVS_VAPE_50_OPP]); + seq_printf(s, "VMOD_100_OPP : 0x%02x\n", avs[AVS_VMOD_100_OPP]); + seq_printf(s, "VMOD_50_OPP : 0x%02x\n", avs[AVS_VMOD_50_OPP]); + seq_printf(s, "VSAFE : 0x%02x\n", avs[AVS_VSAFE]); + seq_printf(s, "VSAFE_RET : 0x%02x\n", avs[AVS_VSAFE_RET]); + } else { + seq_printf(s, "Only u8500 supported.\n"); + } + + return 0; +} + +static void prcmu_data_mem_print(struct seq_file *s) +{ + int i; + int err; + void __iomem *tcdm_base; + u32 dmem[4]; + + if (cpu_is_u8500()) { + tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE); + + for (i = 0; i < U8500_PRCMU_TCDM_SIZE; i += 16) { + dmem[0] = readl(tcdm_base + i + 0); + dmem[1] = readl(tcdm_base + i + 4); + dmem[2] = readl(tcdm_base + i + 8); + dmem[3] = readl(tcdm_base + i + 12); + + if (s) { + err = seq_printf(s, + "0x%x: 0x%08x 0x%08x 0x%08x 0x%08x\n", + ((int)tcdm_base) + i, dmem[0], + dmem[1], dmem[2], dmem[3]); + if (err < 0) { + pr_err("%s: seq_printf overflow, addr=%x\n", + __func__, ((int)tcdm_base) + i); + /* Can't do much here */ + return; + } + } else { + printk(KERN_INFO + "0x%x: 0x%08x 0x%08x 0x%08x 0x%08x\n", + ((int)tcdm_base) + i, dmem[0], + dmem[1], dmem[2], dmem[3]); + } + } + } +} + +void prcmu_debug_dump_data_mem(void) +{ + printk(KERN_INFO "PRCMU data memory dump:\n"); + prcmu_data_mem_print(NULL); +} + +static int prcmu_debugfs_data_mem_read(struct seq_file *s, void *p) +{ + seq_printf(s, "PRCMU data memory:\n"); + prcmu_data_mem_print(s); + + return 0; +} + +static void prcmu_regs_print(struct seq_file *s) +{ + int i; + int err; + void __iomem *prcmu_base; + u32 reg_val; + + if (cpu_is_u8500()) { + prcmu_base = __io_address(U8500_PRCMU_BASE); + + for (i = 0; i < ARRAY_SIZE(u8500_prcmu_dump_regs); i++) { + reg_val = readl(prcmu_base + + u8500_prcmu_dump_regs[i]); + + if (s) { + err = seq_printf(s, "0x%04x: 0x%08x\n", + u8500_prcmu_dump_regs[i], reg_val); + if (err < 0) { + pr_err("%s: seq_printf overflow," + "offset=%x\n", __func__, + u8500_prcmu_dump_regs[i]); + /* Can't do much here */ + return; + } + } else { + printk(KERN_INFO + "0x%04x: 0x%08x\n", + u8500_prcmu_dump_regs[i], reg_val); + } + } + } +} + +static void prcmu_secure_regs_print(struct seq_file *s) +{ + int i; + int err; + void __iomem *prcmu_sec_base; + u32 reg_val; + + if (cpu_is_u8500()) { + /* PRCMU secure base starts after SZ_4K */ + prcmu_sec_base = ioremap(U8500_PRCMU_BASE + SZ_4K, SZ_4K); + if (!prcmu_sec_base) { + pr_err("%s: ioremap faild\n", __func__); + return; + } + + for (i = 0; i < ARRAY_SIZE(u8500_prcmu_dump_secure_regs); i++) { + reg_val = readl(prcmu_sec_base + + u8500_prcmu_dump_secure_regs[i]); + + if (s) { + err = seq_printf(s, "0x%04x: 0x%08x\n", + u8500_prcmu_dump_secure_regs[i] + + SZ_4K, + reg_val); + if (err < 0) { + pr_err("%s: seq_printf overflow," + "offset=%x\n", __func__, + u8500_prcmu_dump_secure_regs[i] + + SZ_4K); + /* Can't do much here */ + break; + } + } else { + printk(KERN_INFO + "0x%04x: 0x%08x\n", + u8500_prcmu_dump_secure_regs[i] + + SZ_4K, + reg_val); + } + } + + iounmap(prcmu_sec_base); + } +} + +void prcmu_debug_dump_regs(void) +{ + printk(KERN_INFO "PRCMU registers dump:\n"); + prcmu_regs_print(NULL); + prcmu_secure_regs_print(NULL); +} + +static int prcmu_debugfs_regs_read(struct seq_file *s, void *p) +{ + seq_printf(s, "PRCMU registers:\n"); + prcmu_regs_print(s); + prcmu_secure_regs_print(s); + return 0; +} + +/* Interrupt debugging */ + +/* There are eight mailboxes */ +#define NUM_MAILBOXES 8 +#define NUM_MAILBOX0_EVENTS 32 +static u32 num_mailbox_interrupts[NUM_MAILBOXES]; +static u32 num_mailbox0_events[NUM_MAILBOX0_EVENTS]; +static u32 num_mailbox0_events_garbage[NUM_MAILBOX0_EVENTS]; + +void prcmu_debug_register_interrupt(u32 mailbox) +{ + if (mailbox < NUM_MAILBOXES) + num_mailbox_interrupts[mailbox]++; +} + +void prcmu_debug_register_mbox0_event(u32 ev, u32 mask) +{ + int i; + + for (i = 0 ; i < NUM_MAILBOX0_EVENTS; i++) + if (ev & (1 << i)) { + if (mask & (1 << i)) + num_mailbox0_events[i]++; + else + num_mailbox0_events_garbage[i]++; + } +} + +static int interrupt_read(struct seq_file *s, void *p) +{ + int i; + char **mbox0names; + + static char *mbox0names_u8500[] = { + "RTC", + "RTT0", + "RTT1", + "HSI0", + "HSI1", + "CA_WAKE", + "USB", + "ABB", + "ABB_FIFO", + "SYSCLK_OK", + "CA_SLEE", + "AC_WAKE_ACK", + "SIDE_TONE_OK", + "ANC_OK", + "SW_ERROR", + "AC_SLEEP_ACK", + NULL, + "ARM", + "HOTMON_LOW", + "HOTMON_HIGH", + "MODEM_SW_RESET_REQ", + NULL, + NULL, + "GPIO0", + "GPIO1", + "GPIO2", + "GPIO3", + "GPIO4", + "GPIO5", + "GPIO6", + "GPIO7", + "GPIO8"}; + static char *mbox0names_u5500[] = { + "RTC", + "RTT0", + "RTT1", + "CD_IRQ", + "SRP_TIM", + "APE_REQ", + "USB", + "ABB", + "LOW_POWER_AUDIO", + "TEMP_SENSOR_LOW", + "ARM", + "AC_WAKE_ACK", + NULL, + "TEMP_SENSOR_HIGH", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "MODEM_SW_RESET_REQ", + NULL, + NULL, + "GPIO0", + "GPIO1", + "GPIO2", + "GPIO3", + "GPIO4", + "GPIO5", + "GPIO6", + "GPIO7", + "AC_REL_ACK"}; + + if (cpu_is_u8500()) { + mbox0names = mbox0names_u8500; + } else if (cpu_is_u5500()) { + mbox0names = mbox0names_u5500; + } else { + seq_printf(s, "Unknown ASIC!\n"); + return 0; + } + + seq_printf(s, "mailbox0: %d\n", num_mailbox_interrupts[0]); + + for (i = 0; i < NUM_MAILBOX0_EVENTS; i++) + if (mbox0names[i]) { + seq_printf(s, " %20s %d ", mbox0names[i], + num_mailbox0_events[i] + ); + if (num_mailbox0_events_garbage[i]) + seq_printf(s, "unwanted: %d", + num_mailbox0_events_garbage[i]); + seq_printf(s, "\n"); + } else if (num_mailbox0_events[i]) { + seq_printf(s, " unknown (%d) %d\n", + i, num_mailbox0_events[i]); + } + + for (i = 1 ; i < NUM_MAILBOXES; i++) + seq_printf(s, "mailbox%d: %d\n", i, num_mailbox_interrupts[i]); + return 0; +} + +static int opp_open_file(struct inode *inode, struct file *file) +{ + return single_open(file, opp_read, inode->i_private); +} + +static int ape_stats_open_file(struct inode *inode, struct file *file) +{ + return single_open(file, ape_stats_print, inode->i_private); +} + +static int ddr_stats_open_file(struct inode *inode, struct file *file) +{ + return single_open(file, ddr_stats_print, inode->i_private); +} + +static int arm_stats_open_file(struct inode *inode, struct file *file) +{ + return single_open(file, arm_stats_print, inode->i_private); +} + +static int cpufreq_delay_open_file(struct inode *inode, struct file *file) +{ + return single_open(file, cpufreq_delay_read, inode->i_private); +} + +static int ape_voltage_open_file(struct inode *inode, struct file *file) +{ + return single_open(file, ape_voltage_read, inode->i_private); +} + +static int avs_open_file(struct inode *inode, struct file *file) +{ + return single_open(file, avs_read, inode->i_private); +} + +static int prcmu_data_mem_open_file(struct inode *inode, struct file *file) +{ + int err; + struct seq_file *s; + + err = single_open(file, prcmu_debugfs_data_mem_read, inode->i_private); + if (!err) { + /* Default buf size in seq_read is not enough */ + s = (struct seq_file *)file->private_data; + s->size = (PAGE_SIZE * 4); + s->buf = kmalloc(s->size, GFP_KERNEL); + if (!s->buf) { + single_release(inode, file); + err = -ENOMEM; + } + } + return err; +} + +static int prcmu_regs_open_file(struct inode *inode, struct file *file) +{ + int err; + struct seq_file *s; + + err = single_open(file, prcmu_debugfs_regs_read, inode->i_private); + if (!err) { + /* Default buf size in seq_read is not enough */ + s = (struct seq_file *)file->private_data; + s->size = (PAGE_SIZE * 2); + s->buf = kmalloc(s->size, GFP_KERNEL); + if (!s->buf) { + single_release(inode, file); + err = -ENOMEM; + } + } + return err; +} + +static int interrupt_open_file(struct inode *inode, struct file *file) +{ + return single_open(file, interrupt_read, inode->i_private); +} + +static const struct file_operations opp_fops = { + .open = opp_open_file, + .write = opp_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ape_stats_fops = { + .open = ape_stats_open_file, + .write = ape_stats_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ddr_stats_fops = { + .open = ddr_stats_open_file, + .write = ddr_stats_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations arm_stats_fops = { + .open = arm_stats_open_file, + .write = arm_stats_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations cpufreq_delay_fops = { + .open = cpufreq_delay_open_file, + .write = cpufreq_delay_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ape_voltage_fops = { + .open = ape_voltage_open_file, + .write = ape_voltage_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations avs_fops = { + .open = avs_open_file, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations prcmu_data_mem_fops = { + .open = prcmu_data_mem_open_file, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations prcmu_regs_fops = { + .open = prcmu_regs_open_file, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations interrupts_fops = { + .open = interrupt_open_file, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int setup_debugfs(void) +{ + struct dentry *dir; + struct dentry *file; + + dir = debugfs_create_dir("prcmu", NULL); + if (IS_ERR_OR_NULL(dir)) + goto fail; + + file = debugfs_create_file("ape_stats", (S_IRUGO | S_IWUGO), + dir, NULL, &ape_stats_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("ddr_stats", (S_IRUGO | S_IWUGO), + dir, NULL, &ddr_stats_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("arm_stats", (S_IRUGO | S_IWUGO), + dir, NULL, &arm_stats_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("ape_opp", (S_IRUGO), + dir, (void *)&ape_sh, + &opp_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("ddr_opp", (S_IRUGO), + dir, (void *)&ddr_sh, + &opp_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("arm_opp", (S_IRUGO), + dir, (void *)&arm_sh, + &opp_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("opp_cpufreq_delay", (S_IRUGO), + dir, NULL, &cpufreq_delay_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("ape_voltage", (S_IRUGO), + dir, NULL, &ape_voltage_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("avs", + (S_IRUGO), + dir, NULL, &avs_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("data_mem", (S_IRUGO), + dir, NULL, + &prcmu_data_mem_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("regs", (S_IRUGO), + dir, NULL, + &prcmu_regs_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + file = debugfs_create_file("interrupts", + (S_IRUGO), + dir, NULL, &interrupts_fops); + if (IS_ERR_OR_NULL(file)) + goto fail; + + return 0; +fail: + if (!IS_ERR_OR_NULL(dir)) + debugfs_remove_recursive(dir); + + pr_err("prcmu debug: debugfs entry failed\n"); + return -ENOMEM; +} + +static __init int prcmu_debug_init(void) +{ + spin_lock_init(&ape_sh.lock); + spin_lock_init(&ddr_sh.lock); + spin_lock_init(&arm_sh.lock); + ape_sh.start = ktime_get(); + ddr_sh.start = ktime_get(); + arm_sh.start = ktime_get(); + return 0; +} +arch_initcall(prcmu_debug_init); + +static __init int prcmu_debug_debugfs_init(void) +{ + setup_debugfs(); + return 0; +} +late_initcall(prcmu_debug_debugfs_init); |