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


  ;PRESERVE8

  .text

  .state32

; ------------------------------------------------------------
; 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

