@@---------------------------------------------------------------
@@ Performance Monitoring Unit (PMU) Example Code for Cortex-A/R
@@
@@ Copyright (C) ARM Limited, 2010-2012. All rights reserved.
@@---------------------------------------------------------------


  @PRESERVE8

  .text

  .code 32

@ ------------------------------------------------------------
@ Performance Monitor Block
@ ------------------------------------------------------------

  .global _getPMN
  @ Returns the number of progammable counters
  @ uint32_t getPMN(void)
_getPMN:
  MRC     p15, #0, r0, c9, c12, #0 @ Read PMCR Register
  MOV     r0, r0, LSR #11        @ Shift N field down to bit 0
  AND     r0, r0, #0x1F          @ Mask to leave just the 5 N bits
  BX      lr
@end_getPMN


  .global _pmn_config
  @ Sets the event for a programmable counter to record
  @ void pmn_config(unsigned counter, uint32_t event)
  @ counter (in r0) = Which counter to program (e.g. 0 for PMN0, 1 for PMN1)
  @ event   (in r1) = The event code (from appropriate TRM or ARM Architecture Reference Manual)
_pmn_config:
  AND     r0, r0, #0x1F          @ Mask to leave only bits 4:0
  MCR     p15, #0, r0, c9, c12, #5 @ Write PMSELR Register
  ISB                            @ Synchronize context
  MCR     p15, #0, r1, c9, c13, #1 @ Write PMXEVTYPER Register
  BX      lr
@end_pmn_config


  .global _ccnt_divider
  @ Enables/disables the divider (1/64) on CCNT
  @ void ccnt_divider(int divider)
  @ divider (in r0) = If 0 disable divider, else enable divider
_ccnt_divider:
  MRC     p15, #0, r1, c9, c12, #0  @ Read PMCR

  CMP     r0, #0x0                @ IF (r0 == 0)
  BICEQ   r1, r1, #0x08           @ THEN: Clear the D bit (disables the divisor)
  ORRNE   r1, r1, #0x08           @ ELSE: Set the D bit (enables the divisor)

  MCR     p15, #0, r1, c9, c12, #0  @ Write PMCR
  BX      lr
@end_ccnt_divider

  @ ---------------------------------------------------------------
  @ Enable/Disable
  @ ---------------------------------------------------------------

  .global _enable_pmu
  @ Global PMU enable
  @ void enable_pmu(void)
_enable_pmu:
  MRC     p15, #0, r0, c9, c12, #0  @ Read PMCR
  ORR     r0, r0, #0x01           @ Set E bit
  MCR     p15, #0, r0, c9, c12, #0  @ Write PMCR
  BX      lr
@end_enable_pmu


  .global _disable_pmu
  @ Global PMU disable
  @ void disable_pmu(void)
_disable_pmu:
  MRC     p15, #0, r0, c9, c12, #0  @ Read PMCR
  BIC     r0, r0, #0x01           @ Clear E bit
  MCR     p15, #0, r0, c9, c12, #0  @ Write PMCR
  BX      lr
@end_disable_pmu


  .global _enable_ccnt
  @ Enable the CCNT
  @ void enable_ccnt(void)
_enable_ccnt:
  MOV     r0, #0x80000000         @ Set C bit
  MCR     p15, #0, r0, c9, c12, #1  @ Write PMCNTENSET Register
  BX      lr
@end_enable_ccnt


  .global _disable_ccnt
  @ Disable the CCNT
  @ void disable_ccnt(void)
_disable_ccnt:
  MOV     r0, #0x80000000         @ Set C bit
  MCR     p15, #0, r0, c9, c12, #2  @ Write PMCNTENCLR Register
  BX      lr
@end_disable_ccnt


  .global _enable_pmn
  @ Enable PMN{n}
  @ void enable_pmn(uint32_t counter)
  @ counter (in r0) = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
_enable_pmn:
  MOV     r1, #0x1
  MOV     r1, r1, LSL r0
  MCR     p15, #0, r1, c9, c12, #1  @ Write PMCNTENSET Register
  BX      lr
@end_enable_pmn


   .global _disable_pmn
  @ Disable PMN{n}
  @ void disable_pmn(uint32_t counter)
  @ counter (in r0) = The counter to disable (e.g. 0 for PMN0, 1 for PMN1)
_disable_pmn:
  MOV     r1, #0x1
  MOV     r1, r1, LSL r0
  MCR     p15, #0, r1, c9, c12, #2  @ Write PMCNTENCLR Register
  BX      lr
@end_disable_pmn


  .global  _enable_pmu_user_access
  @ Enables User mode access to the PMU (must be called in a privileged mode)
  @ void enable_pmu_user_access(void)
_enable_pmu_user_access:
  MRC     p15, #0, r0, c9, c14, #0  @ Read PMUSERENR Register
  ORR     r0, r0, #0x01           @ Set EN bit (bit 0)
  MCR     p15, #0, r0, c9, c14, #0  @ Write PMUSERENR Register
  ISB                             @ Synchronize context
  BX      lr
@end_enable_pmu_user_access


  .global  _disable_pmu_user_access
  @ Disables User mode access to the PMU (must be called in a privileged mode)
  @ void disable_pmu_user_access(void)
_disable_pmu_user_access:
  MRC     p15, #0, r0, c9, c14, #0  @ Read PMUSERENR Register
  BIC     r0, r0, #0x01           @ Clear EN bit (bit 0)
  MCR     p15, #0, r0, c9, c14, #0  @ Write PMUSERENR Register
  ISB                             @ Synchronize context
  BX      lr
@end_disable_pmu_user_access


  @ ---------------------------------------------------------------
  @ Counter read registers
  @ ---------------------------------------------------------------

  .global _read_ccnt
  @ Returns the value of CCNT
  @ uint32_t read_ccnt(void)
_read_ccnt:
  MRC     p15, #0, r0, c9, c13, #0 @ Read CCNT Register
  BX      lr
@end_read_ccnt


  .global  _read_pmn
  @ Returns the value of PMN{n}
  @ uint32_t read_pmn(uint32_t counter)
  @ counter (in r0) = The counter to read (e.g. 0 for PMN0, 1 for PMN1)
_read_pmn:
  AND     r0, r0, #0x1F          @ Mask to leave only bits 4:0
  MCR     p15, #0, r0, c9, c12, #5 @ Write PMSELR Register
  ISB                            @ Synchronize context
  MRC     p15, #0, r0, c9, c13, #2 @ Read current PMNx Register
  BX      lr
@end_read_pmn


  @ ---------------------------------------------------------------
  @ Software Increment
  @ ---------------------------------------------------------------

  .global _pmu_software_increment
  @ Writes to software increment register
  @ void pmu_software_increment(uint32_t counter)
  @ counter (in r0) = The counter to increment (e.g. 0 for PMN0, 1 for PMN1)
_pmu_software_increment:
  MOV     r1, #0x01
  MOV     r1, r1, LSL r0
  MCR     p15, #0, r1, c9, c12, #4 @ Write PMSWINCR Register
  BX      lr
@end_pmu_software_increment


  @ ---------------------------------------------------------------
  @ Overflow & Interrupt Generation
  @ ---------------------------------------------------------------

  .global _read_flags
  @ Returns the value of the overflow flags
  @ uint32_t read_flags(void)
_read_flags:
  MRC     p15, #0, r0, c9, c12, #3 @ Read PMOVSR Register
  BX      lr
@end_read_flags


  .global  _write_flags
  @ Writes the overflow flags
  @ void write_flags(uint32_t flags)
_write_flags:
  MCR     p15, #0, r0, c9, c12, #3 @ Write PMOVSR Register
  ISB                            @ Synchronize context
  BX      lr
@end_write_flags


  .global _enable_ccnt_irq
  @ Enables interrupt generation on overflow of the CCNT
  @ void enable_ccnt_irq(void)
_enable_ccnt_irq:
  MOV     r0, #0x80000000
  MCR     p15, #0, r0, c9, c14, #1  @ Write PMINTENSET Register
  BX      lr
@end_enable_ccnt_irq


  .global  _disable_ccnt_irq
  @ Disables interrupt generation on overflow of the CCNT
  @ void disable_ccnt_irq(void)
_disable_ccnt_irq:
  MOV     r0, #0x80000000
  MCR     p15, #0, r0, c9, c14, #2   @ Write PMINTENCLR Register
  BX      lr
@end_disable_ccnt_irq


  .global _enable_pmn_irq
  @ Enables interrupt generation on overflow of PMN{x}
  @ void enable_pmn_irq(uint32_t counter)
  @ counter (in r0) = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
_enable_pmn_irq:
  MOV     r1, #0x1
  MOV     r0, r1, LSL r0
  MCR     p15, #0, r0, c9, c14, #1   @ Write PMINTENSET Register
  BX      lr
@end_enable_pmn_irq


  .global _disable_pmn_irq
  @ Disables interrupt generation on overflow of PMN{x}
  @ void disable_pmn_irq(uint32_t counter)
  @ counter (in r0) = The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
_disable_pmn_irq:
  MOV     r1, #0x1
  MOV     r0, r1, LSL r0
  MCR     p15, #0, r0, c9, c14, #2  @ Write PMINTENCLR Register
  BX      lr
@end_disable_pmn_irq


  @ ---------------------------------------------------------------
  @ Reset Functions
  @ ---------------------------------------------------------------

  .global _reset_pmn
  @ Resets all programmable counters to zero
  @ void reset_pmn(void)
_reset_pmn:
  MRC     p15, #0, r0, c9, c12, #0  @ Read PMCR
  ORR     r0, r0, #0x2            @ Set P bit (Event counter reset)
  MCR     p15, #0, r0, c9, c12, #0  @ Write PMCR
  BX      lr
@end_reset_pmn


  .global  _reset_ccnt
  @ Resets the CCNT
  @ void reset_ccnt(void)
_reset_ccnt:
  MRC     p15, #0, r0, c9, c12, #0  @ Read PMCR
  ORR     r0, r0, #0x4            @ Set C bit (Clock counter reset)
  MCR     p15, #0, r0, c9, c12, #0  @ Write PMCR
  BX      lr
@end_reset_ccnt

