/* Copyright (c) 2010, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ /* perf-v7.c DESCRIPTION Manipulation, initialization of the ARMV7 Performance counter register. EXTERNALIZED FUNCTIONS INITIALIZATION AND SEQUENCING REQUIREMENTS */ /* INCLUDE FILES FOR MODULE */ #include #include #include #include #include #include #include #include "cp15_registers.h" /* DEFINITIONS AND DECLARATIONS FOR MODULE This section contains definitions for constants, macros, types, variables and other items needed by this module. */ /* Constant / Define Declarations */ #define PM_NUM_COUNTERS 4 #define PM_V7_ERR -1 /*------------------------------------------------------------------------ * Global control bits ------------------------------------------------------------------------*/ #define PM_GLOBAL_ENABLE (1<<0) #define PM_EVENT_RESET (1<<1) #define PM_CYCLE_RESET (1<<2) #define PM_CLKDIV (1<<3) #define PM_GLOBAL_TRACE (1<<4) #define PM_DISABLE_PROHIBIT (1<<5) /*--------------------------------------------------------------------------- * Enable and clear bits for each event/trigger ----------------------------------------------------------------------------*/ #define PM_EV0_ENABLE (1<<0) #define PM_EV1_ENABLE (1<<1) #define PM_EV2_ENABLE (1<<2) #define PM_EV3_ENABLE (1<<3) #define PM_COUNT_ENABLE (1<<31) #define PM_ALL_ENABLE (0x8000000F) /*----------------------------------------------------------------------------- * Overflow actions ------------------------------------------------------------------------------*/ #define PM_OVERFLOW_NOACTION (0) #define PM_OVERFLOW_HALT (1) #define PM_OVERFLOW_STOP (2) #define PM_OVERFLOW_SKIP (3) /* * Shifts for each trigger type */ #define PM_STOP_SHIFT 24 #define PM_RELOAD_SHIFT 22 #define PM_RESUME_SHIFT 20 #define PM_SUSPEND_SHIFT 18 #define PM_START_SHIFT 16 #define PM_STOPALL_SHIFT 15 #define PM_STOPCOND_SHIFT 12 #define PM_RELOADCOND_SHIFT 9 #define PM_RESUMECOND_SHIFT 6 #define PM_SUSPENDCOND_SHIFT 3 #define PM_STARTCOND_SHIFT 0 /*--------------------------------------------------------------------------- External control register. What todo when various events happen. Triggering events, etc. ----------------------------------------------------------------------------*/ #define PM_EXTTR0 0 #define PM_EXTTR1 1 #define PM_EXTTR2 2 #define PM_EXTTR3 3 #define PM_COND_NO_STOP 0 #define PM_COND_STOP_CNTOVRFLW 1 #define PM_COND_STOP_EXTERNAL 4 #define PM_COND_STOP_TRACE 5 #define PM_COND_STOP_EVOVRFLW 6 #define PM_COND_STOP_EVTYPER 7 /*-------------------------------------------------------------------------- Protect against concurrent access. There is an index register that is used to select the appropriate bank of registers. If multiple processes are writting this at different times we could have a mess... ---------------------------------------------------------------------------*/ #define PM_LOCK() #define PM_UNLOCK() #define PRINT printk /*-------------------------------------------------------------------------- The Event definitions --------------------------------------------------------------------------*/ #define PM_EVT_SW_INCREMENT 0 #define PM_EVT_L1_I_MISS 1 #define PM_EVT_ITLB_MISS 2 #define PM_EVT_L1_D_MISS 3 #define PM_EVT_L1_D_ACCESS 4 #define PM_EVT_DTLB_MISS 5 #define PM_EVT_DATA_READ 6 #define PM_EVT_DATA_WRITE 7 #define PM_EVT_INSTRUCTION 8 #define PM_EVT_EXCEPTIONS 9 #define PM_EVT_EXCEPTION_RET 10 #define PM_EVT_CTX_CHANGE 11 #define PM_EVT_PC_CHANGE 12 #define PM_EVT_BRANCH 13 #define PM_EVT_RETURN 14 #define PM_EVT_UNALIGNED 15 #define PM_EVT_BRANCH_MISS 16 #define PM_EVT_EXTERNAL0 0x40 #define PM_EVT_EXTERNAL1 0x41 #define PM_EVT_EXTERNAL2 0x42 #define PM_EVT_EXTERNAL3 0x43 #define PM_EVT_TRACE0 0x44 #define PM_EVT_TRACE1 0x45 #define PM_EVT_TRACE2 0x46 #define PM_EVT_TRACE3 0x47 #define PM_EVT_PM0 0x48 #define PM_EVT_PM1 0x49 #define PM_EVT_PM2 0x4a #define PM_EVT_PM3 0x4b #define PM_EVT_LPM0_EVT0 0x4c #define PM_EVT_LPM0_EVT1 0x4d #define PM_EVT_LPM0_EVT2 0x4e #define PM_EVT_LPM0_EVT3 0x4f #define PM_EVT_LPM1_EVT0 0x50 #define PM_EVT_LPM1_EVT1 0x51 #define PM_EVT_LPM1_EVT2 0x52 #define PM_EVT_LPM1_EVT3 0x53 #define PM_EVT_LPM2_EVT0 0x54 #define PM_EVT_LPM2_EVT1 0x55 #define PM_EVT_LPM2_EVT2 0x56 #define PM_EVT_LPM2_EVT3 0x57 #define PM_EVT_L2_EVT0 0x58 #define PM_EVT_L2_EVT1 0x59 #define PM_EVT_L2_EVT2 0x5a #define PM_EVT_L2_EVT3 0x5b #define PM_EVT_VLP_EVT0 0x5c #define PM_EVT_VLP_EVT1 0x5d #define PM_EVT_VLP_EVT2 0x5e #define PM_EVT_VLP_EVT3 0x5f /* Type Declarations */ /*-------------------------------------------------------------------------- A performance monitor trigger setup/initialization structure. Contains all of the fields necessary to setup a complex trigger with the internal performance monitor. ---------------------------------------------------------------------------*/ struct pm_trigger_s { int index; int event_type; bool interrupt; bool overflow_enable; bool event_export; unsigned char overflow_action; unsigned char stop_index; unsigned char reload_index; unsigned char resume_index; unsigned char suspend_index; unsigned char start_index; bool overflow_stop; unsigned char stop_condition; unsigned char reload_condition; unsigned char resume_condition; unsigned char suspend_condition; unsigned char start_condition; }; /* * Name and index place holder so we can display the event */ struct pm_name_s { unsigned long index; char *name; }; /* Local Object Definitions */ unsigned long pm_cycle_overflow_count; unsigned long pm_overflow_count[PM_NUM_COUNTERS]; /*--------------------------------------------------------------------------- Max number of events read from the config registers ---------------------------------------------------------------------------*/ static int pm_max_events; /*-------------------------------------------------------------------------- Storage area for each of the triggers *---------------------------------------------------------------------------*/ static struct pm_trigger_s pm_triggers[4]; /*-------------------------------------------------------------------------- Names and indexes of the events --------------------------------------------------------------------------*/ static struct pm_name_s pm_names[] = { { PM_EVT_SW_INCREMENT, "SW Increment"}, { PM_EVT_L1_I_MISS, "L1 I MISS"}, { PM_EVT_ITLB_MISS, "L1 ITLB MISS"}, { PM_EVT_L1_D_MISS, "L1 D MISS"}, { PM_EVT_L1_D_ACCESS, "L1 D ACCESS"}, { PM_EVT_DTLB_MISS, "DTLB MISS"}, { PM_EVT_DATA_READ, "DATA READ"}, { PM_EVT_DATA_WRITE, "DATA WRITE"}, { PM_EVT_INSTRUCTION, "INSTRUCTIONS"}, { PM_EVT_EXCEPTIONS, "EXCEPTIONS"}, { PM_EVT_EXCEPTION_RET, "EXCEPTION RETURN"}, { PM_EVT_CTX_CHANGE, "CTX CHANGE"}, { PM_EVT_PC_CHANGE, "PC CHANGE"}, { PM_EVT_BRANCH, "BRANCH"}, { PM_EVT_RETURN, "RETURN"}, { PM_EVT_UNALIGNED, "UNALIGNED"}, { PM_EVT_BRANCH_MISS, "BRANCH MISS"}, { PM_EVT_EXTERNAL0, "EXTERNAL 0"}, { PM_EVT_EXTERNAL1, "EXTERNAL 1"}, { PM_EVT_EXTERNAL2, "EXTERNAL 2"}, { PM_EVT_EXTERNAL3, "EXTERNAL 3"}, { PM_EVT_TRACE0, "TRACE 0"}, { PM_EVT_TRACE1, "TRACE 1"}, { PM_EVT_TRACE2, "TRACE 2"}, { PM_EVT_TRACE3, "TRACE 3"}, { PM_EVT_PM0, "PM0"}, { PM_EVT_PM1, "PM1"}, { PM_EVT_PM2, "PM2"}, { PM_EVT_PM3, "PM3"}, { PM_EVT_LPM0_EVT0, "LPM0 E0"}, { PM_EVT_LPM0_EVT1, "LPM0 E1"}, { PM_EVT_LPM0_EVT2 , "LPM0 E2"}, { PM_EVT_LPM0_EVT3, "LPM0 E3"}, { PM_EVT_LPM1_EVT0, "LPM1 E0"}, { PM_EVT_LPM1_EVT1, "LPM1 E1"}, { PM_EVT_LPM1_EVT2, "LPM1 E2"}, { PM_EVT_LPM1_EVT3, "LPM1 E3"}, { PM_EVT_LPM2_EVT0, "LPM2 E0"}, { PM_EVT_LPM2_EVT1 , "LPM2 E1"}, { PM_EVT_LPM2_EVT2, "LPM2 E2"}, { PM_EVT_LPM2_EVT3, "LPM2 E3"}, { PM_EVT_L2_EVT0 , "L2 E0"}, { PM_EVT_L2_EVT1, "L2 E1"}, { PM_EVT_L2_EVT2, "L2 E2"}, { PM_EVT_L2_EVT3 , "L2 E3"}, { PM_EVT_VLP_EVT0 , "VLP E0"}, { PM_EVT_VLP_EVT1, "VLP E1"}, { PM_EVT_VLP_EVT2, "VLP E2"}, { PM_EVT_VLP_EVT3, "VLP E3"}, }; static int irqid; /* Function Definitions */ /* FUNCTION pm_find_event_name DESCRIPTION Find the name associated with the event index passed and return the pointer. DEPENDENCIES RETURN VALUE Pointer to text string containing the name of the event or pointer to an error string. Either way access to the returned string will not cause an access error. SIDE EFFECTS */ char *pm_find_event_name(unsigned long index) { unsigned long i = 0; while (pm_names[i].index != -1) { if (pm_names[i].index == index) return pm_names[i].name; i++; } return "BAD INDEX"; } /* FUNCTION pm_group_stop DESCRIPTION Stop a group of the performance monitors. Event monitor 0 is bit 0, event monitor 1 bit 1, etc. The cycle count can also be disabled with bit 31. Macros are provided for all of the indexes including an ALL. DEPENDENCIES RETURN VALUE None SIDE EFFECTS Stops the performance monitoring for the index passed. */ void pm_group_stop(unsigned long mask) { WCP15_PMCNTENCLR(mask); } /* FUNCTION pm_group_start DESCRIPTION Start a group of the performance monitors. Event monitor 0 is bit 0, event monitor 1 bit 1, etc. The cycle count can also be enabled with bit 31. Macros are provided for all of the indexes including an ALL. DEPENDENCIES RETURN VALUE None SIDE EFFECTS Starts the performance monitoring for the index passed. */ void pm_group_start(unsigned long mask) { WCP15_PMCNTENSET(mask); } /* FUNCTION pm_cycle_overflow_action DESCRIPTION Action to take for an overflow of the cycle counter. DEPENDENCIES RETURN VALUE None SIDE EFFECTS Modify the state actions for overflow */ void pm_cycle_overflow_action(int action) { unsigned long reg = 0; if ((action > PM_OVERFLOW_SKIP) || (action < 0)) return; RCP15_PMACTLR(reg); reg &= ~(1<<30); /*clear it*/ WCP15_PMACTLR(reg | (action<<30)); } /* FUNCTION pm_get_overflow DESCRIPTION Return the overflow condition for the index passed. DEPENDENCIES RETURN VALUE 0 no overflow !0 (anything else) overflow; SIDE EFFECTS */ unsigned long pm_get_overflow(int index) { unsigned long overflow = 0; /* * Range check */ if (index > pm_max_events) return PM_V7_ERR; RCP15_PMOVSR(overflow); return overflow & (1< pm_max_events) return; WCP15_PMCNTENCLR(1< pm_max_events) return; WCP15_PMCNTENSET(1< pm_max_events) return PM_V7_ERR; /* * Lock, select the index and read the count...unlock */ PM_LOCK(); WCP15_PMSELR(index); WCP15_PMXEVCNTR(new_value); PM_UNLOCK(); return reg; } int pm_reset_count(int index) { return pm_set_count(index, 0); } /* FUNCTION pm_get_count DESCRIPTION Return the number of events that have happened for the index passed. DEPENDENCIES RETURN VALUE -1 if the index is out of range The number of events if inrange SIDE EFFECTS */ unsigned long pm_get_count(int index) { unsigned long reg = 0; /* * Range check */ if (index > pm_max_events) return PM_V7_ERR; /* * Lock, select the index and read the count...unlock */ PM_LOCK(); WCP15_PMSELR(index); RCP15_PMXEVCNTR(reg); PM_UNLOCK(); return reg; } /* FUNCTION pm_show_event_info DESCRIPTION Display (print) the information about the event at the index passed. Shows the index, name and count if a valid index is passed. If the index is not valid, then nothing is displayed. DEPENDENCIES RETURN VALUE None SIDE EFFECTS */ void pm_show_event_info(unsigned long index) { unsigned long count; unsigned long event_type; if (index > pm_max_events) return; if (pm_triggers[index].index > pm_max_events) return; count = pm_get_count(index); event_type = pm_triggers[index].event_type; PRINT("Event %ld Trigger %s(%ld) count:%ld\n", index, pm_find_event_name(event_type), event_type, count); } /* FUNCTION pm_event_init DESCRIPTION Given the struct pm_trigger_s info passed, configure the event. This can be a complex trigger or a simple trigger. Any old values in the event are lost. DEPENDENCIES RETURN VALUE status SIDE EFFECTS stops and clears the event at the index passed. */ int pm_event_init(struct pm_trigger_s *data) { unsigned long trigger; unsigned long actlr = 0; if (0 == data) return PM_V7_ERR; if (data->index > pm_max_events) return PM_V7_ERR; /* * Setup the trigger based ont he passed values */ trigger = ((data->overflow_enable&1)<<31) | ((data->event_export&1)<<30) | ((data->stop_index&3)<reload_index&3)<resume_index&3)<suspend_index&3)<start_index&3)<overflow_stop&1)<stop_condition&7)<reload_condition&7)<resume_condition&7)<suspend_condition&7)<start_condition&7)<index); /* * Lock, select the bank, set the trigger event and the event type * then unlock. */ PM_LOCK(); RCP15_PMACTLR(actlr); actlr &= ~(3<<(data->index<<1)); WCP15_PMACTLR(actlr | ((data->overflow_action&3) << (data->index<<1))); WCP15_PMSELR(data->index); WCP15_PMXEVTYPER(data->event_type); WCP15_PMXEVCNTCR(trigger); PM_UNLOCK(); /* * Make a copy of the trigger so we know what it is when/if it triggers. */ memcpy(&pm_triggers[data->index], data, sizeof(*data)); /* * We do not re-enable this here so events can be started together with * pm_group_start() that way an accurate measure can be taken... */ return 0; } int pm_set_event(int index, unsigned long event) { unsigned long reg = 0; /* * Range check */ if (index > pm_max_events) return PM_V7_ERR; /* * Lock, select the index and read the count...unlock */ PM_LOCK(); WCP15_PMSELR(index); WCP15_PMXEVTYPER(event); PM_UNLOCK(); return reg; } /* FUNCTION pm_set_local_iu DESCRIPTION Set the local IU triggers. Note that the MSB determines if these are enabled or not. DEPENDENCIES RETURN VALUE NONE SIDE EFFECTS */ void pm_set_local_iu(unsigned long value) { WCP15_LPM0EVTYPER(value); } /* FUNCTION pm_set_local_iu DESCRIPTION Set the local IU triggers. Note that the MSB determines if these are enabled or not. DEPENDENCIES RETURN VALUE NONE SIDE EFFECTS */ void pm_set_local_xu(unsigned long value) { WCP15_LPM1EVTYPER(value); } /* FUNCTION pm_set_local_su DESCRIPTION Set the local SU triggers. Note that the MSB determines if these are enabled or not. DEPENDENCIES RETURN VALUE NONE SIDE EFFECTS */ void pm_set_local_su(unsigned long value) { WCP15_LPM2EVTYPER(value); } /* FUNCTION pm_set_local_l2 DESCRIPTION Set the local L2 triggers. Note that the MSB determines if these are enabled or not. DEPENDENCIES RETURN VALUE NONE SIDE EFFECTS */ void pm_set_local_l2(unsigned long value) { WCP15_L2LPMEVTYPER(value); } /* FUNCTION pm_set_local_vu DESCRIPTION Set the local VU triggers. Note that the MSB determines if these are enabled or not. DEPENDENCIES RETURN VALUE NONE SIDE EFFECTS */ void pm_set_local_vu(unsigned long value) { WCP15_VLPMEVTYPER(value); } /* FUNCTION pm_isr DESCRIPTION: Performance Monitor interrupt service routine to capture overflows DEPENDENCIES RETURN VALUE SIDE EFFECTS */ static irqreturn_t pm_isr(int irq, void *d) { int i; for (i = 0; i < PM_NUM_COUNTERS; i++) { if (pm_get_overflow(i)) { pm_overflow_count[i]++; pm_reset_overflow(i); } } if (pm_get_cycle_overflow()) { pm_cycle_overflow_count++; pm_reset_cycle_overflow(); } return IRQ_HANDLED; } void pm_stop_all(void) { WCP15_PMCNTENCLR(0xFFFFFFFF); } void pm_reset_all(void) { WCP15_PMCR(0xF); WCP15_PMOVSR(PM_ALL_ENABLE); /* overflow clear */ } void pm_start_all(void) { WCP15_PMCNTENSET(PM_ALL_ENABLE); } /* FUNCTION pm_initialize DESCRIPTION Initialize the performanca monitoring for the v7 processor. Ensures the cycle count is running and the event counters are enabled. DEPENDENCIES RETURN VALUE NONE SIDE EFFECTS */ void pm_initialize(void) { unsigned long reg = 0; unsigned char imp; unsigned char id; unsigned char num; unsigned long enables = 0; static int initialized; if (initialized) return; initialized = 1; irqid = INT_ARMQC_PERFMON; RCP15_PMCR(reg); imp = (reg>>24) & 0xFF; id = (reg>>16) & 0xFF; pm_max_events = num = (reg>>11) & 0xFF; PRINT("V7Performance Monitor Capabilities\n"); PRINT(" Implementor %c(%d)\n", imp, imp); PRINT(" Id %d %x\n", id, id); PRINT(" Num Events %d %x\n", num, num); PRINT("\nCycle counter enabled by default...\n"); /* * Global enable, ensure the global enable is set so all * subsequent actions take effect. Also resets the counts */ RCP15_PMCR(enables); WCP15_PMCR(enables | PM_GLOBAL_ENABLE | PM_EVENT_RESET | PM_CYCLE_RESET | PM_CLKDIV); /* * Enable access from user space */ WCP15_PMUSERENR(1); WCP15_PMACTLR(1); /* * Install interrupt handler and the enable the interrupts */ pm_reset_cycle_overflow(); pm_reset_overflow(0); pm_reset_overflow(1); pm_reset_overflow(2); pm_reset_overflow(3); if (0 != request_irq(irqid, pm_isr, 0, "perfmon", 0)) printk(KERN_ERR "%s:%d request_irq returned error\n", __FILE__, __LINE__); WCP15_PMINTENSET(PM_ALL_ENABLE); /* * Enable the cycle counter. Default, count 1:1 no divisor. */ pm_enable_cycle_counter(); } void pm_free_irq(void) { free_irq(irqid, 0); } void pm_deinitialize(void) { unsigned long enables = 0; RCP15_PMCR(enables); WCP15_PMCR(enables & ~PM_GLOBAL_ENABLE); }