summaryrefslogblamecommitdiffstats
path: root/cpukit/score/cpu/arm/include/libcpu/arm-cp15.h
blob: c239eaccc896172d32a0e94c0208155f6b569b9f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019

                                           


        
                                




                                         
                                      
                                                         
  



















                                                                              










                                







                                                                           






                                                
                                                             
  
                            








                                             



                                                                           


     
                                  
                                                                  








                                                              


                                    



                                                                    

                                

                                  





                                                                            





















































                                                                                  


                                                   


                                                 








                                                                 
                                                                              





                                                                 








                                                                              

                                  
                                            




                                         
                                             



                                          
                        
                                            






                                         
         
 




                                 
 





                                     
                                   




                                     



                                   

                                   


                                 
                                       


                                 
 














                                                              


















                                                            
   













                                                             










                                                      

                                            



                       
                    
                     
                                     






                                                    

                                            



                       
                    








                                                    

                                            



                       
                    








                                                    

                                        


                       
                    










                                     





                      









                                                                             

                                            


































                                              

                                            



                       
                    








                                                      

                                                   


                       
                    







                                      

                                                                               
                                                          



























                                                                     

                                            



                       
                    








                                                    

                                                


                       
                    







                                     

                                            



                       
                    








                                                    

                                            


                       
                    







                                     

                                            



                       
                    








                                                    

                                                   


                       
                    







                                     

                                        



                       
                    








                                                    

                                           


                       
                    







                                     

                                        



                       
                    
                     
                                     


                       
    






                                                                             

 

                                              




                                      
                    







                                     
                                        















                                                        















                                                                        
                                         



                       
                    
                     
                                     


                       


    

                                                          


                       
                                      
 
                    
                     
                                     


                       


    

                                        

                       
                   
 
                    
                     
                                     

                       
                     


    

                                                   


                       
                                      
 
                    
                     
                                     





                       

                                            
 
                          
 
                    
                     









                                                 
                   

                                                               


    







                        
                                  

                                            

                       
               
 
                    








                                                    






                                                
                                                                                                 

                                            


                                          
                                                       
 
                                                   
                      
                                  
                                                          
                      









                                      
                                            

                                            


                                          
                                                       
 
                                                   

                                              
                                                          








                                                   

                                              


                                          
                                                       
 
                                                   

                                      
                                                          







                                   

                                    

                                            





                       
                                     






                                                    

                                            



                            

                                             



                                     

                                            



                                       

                                    

                                            





                       
                                     






                                                    

                                                



                             

                                                        
 
                                      

 

                                           

                                            





                       
                                     






                                                    

                                               












                                     














                                                                 

                                        


                       
 
                    
                     
                                     

                       
                     



              

                                                                         

                                                               















                                                                

                                                              















                                             

                                              













                                     
















                                                             

                                           

                       
                   
 
                    
                     
                                     

                       
                     



              

                                                           




                                        
                    
                     
                                     






                       

                                                                               


                       
                    
                     
                                             






                                     

                                                         


                       

                                        
                    
                     
                                      

                       
                     


    

                                        

                       
                   
 
                    
                     
                                     

                       
                     



              

                                                    


                       

                                        
                    
                     
                                     

                       
                     



              

                                                                        


                       
                    
                     
                                             

                       
                                     



              
                                        




























                                                                             
                                               







                                                            

                                                               
                                                



     

                                               

                       

                                        
 
                    
                     
                                      

                       
                     



              

                                                                   

                       
 
                    
                     
                                              

                       
                                     



              
                                        




























                                                             










                                                               
                                             




                                        
                                        

                       
 
                    
                     


                                   

                       

              


    

                                              


                       





                                           









                                      















                                                                              

 

                                                              


                       
                                        
 
                    
                     
                                      


                       
              


    

                                                                                  

                       
 
                    
                     
                                              

                       

                                     


    

                                                       


                       
                    
                     


                                   

                       

              


    

         

                                        



                       
                    
                     
                                      


                       
              


    

                                        

                       
                   
 
                    
                     
                                     

                       

                     


    

                                            













                                                        

                                             












                                                                  

                                            













                                                    

                                            











                                     

                                           

                                            















                                                             

                                        













                                                      

                                            











                                       





























                                                      







































































































































                                                                  

                                 


















































































































































































































































































































                                                                        

























































































































































                                                             
                                      






































































































































































                                                             































                                                    










                                                                             





                                                               
                                        
                       

  
         





                                     
/* SPDX-License-Identifier: BSD-2-Clause */

/**
 * @file
 *
 * @ingroup RTEMSScoreCPUARMCP15
 *
 * @brief ARM co-processor 15 (CP15) API.
 */

/*
 * Copyright (c) 2013 Hesham AL-Matary
 * Copyright (C) 2009, 2017 embedded brains GmbH & Co. KG
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef LIBCPU_SHARED_ARM_CP15_H
#define LIBCPU_SHARED_ARM_CP15_H

#include <rtems.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/*
 * Allow users of this header file to optionally place the inline functions
 * into a non-standard section.
 */
#ifndef ARM_CP15_TEXT_SECTION
  #define ARM_CP15_TEXT_SECTION
#endif

#define ARM_CP15_CACHE_PREPARE_MVA(mva) \
  ((const void *) (((uint32_t) (mva)) & ~0x1fU))

#define ARM_CP15_TLB_PREPARE_MVA(mva) \
  ((const void *) (((uint32_t) (mva)) & ~0x3fU))

/**
 * @defgroup RTEMSScoreCPUARMCP15 ARM Co-Processor 15 Support
 *
 * @ingroup RTEMSScoreCPUARM
 *
 * @brief ARM co-processor 15 (CP15) support.
 *
 * @{
 */

/**
 * @name MMU Defines
 *
 * See section B3.8.2, "Short-descriptor format memory region attributes,
 * without TEX remap" in the "ARM Architecture Reference Manual ARMv7-A and
 * ARMv7-R edition".
 *
 * @{
 */

#define ARM_MMU_SECT_BASE_SHIFT 20
#define ARM_MMU_SECT_BASE_MASK (0xfffU << ARM_MMU_SECT_BASE_SHIFT)
#define ARM_MMU_SECT_NS (1U << 19)
#define ARM_MMU_SECT_NG (1U << 17)
#define ARM_MMU_SECT_S (1U << 16)
#define ARM_MMU_SECT_AP_2 (1U << 15)
#define ARM_MMU_SECT_TEX_2 (1U << 14)
#define ARM_MMU_SECT_TEX_1 (1U << 13)
#define ARM_MMU_SECT_TEX_0 (1U << 12)
#define ARM_MMU_SECT_TEX_SHIFT 12
#define ARM_MMU_SECT_TEX_MASK (0x3U << ARM_MMU_SECT_TEX_SHIFT)
#define ARM_MMU_SECT_AP_1 (1U << 11)
#define ARM_MMU_SECT_AP_0 (1U << 10)
#define ARM_MMU_SECT_AP_SHIFT 10
#define ARM_MMU_SECT_AP_MASK (0x23U << ARM_MMU_SECT_AP_SHIFT)
#define ARM_MMU_SECT_DOMAIN_SHIFT 5
#define ARM_MMU_SECT_DOMAIN_MASK (0xfU << ARM_MMU_SECT_DOMAIN_SHIFT)
#define ARM_MMU_SECT_XN (1U << 4)
#define ARM_MMU_SECT_C (1U << 3)
#define ARM_MMU_SECT_B (1U << 2)
#define ARM_MMU_SECT_PXN (1U << 0)
#define ARM_MMU_SECT_DEFAULT 0x2U
#define ARM_MMU_SECT_GET_INDEX(mva) \
  (((uint32_t) (mva)) >> ARM_MMU_SECT_BASE_SHIFT)
#define ARM_MMU_SECT_MVA_ALIGN_UP(mva) \
  ((1U << ARM_MMU_SECT_BASE_SHIFT) \
    + ((((uint32_t) (mva) - 1U)) & ~((1U << ARM_MMU_SECT_BASE_SHIFT) - 1U)))

#define ARM_MMU_PAGE_TABLE_BASE_SHIFT 10
#define ARM_MMU_PAGE_TABLE_BASE_MASK (0x3fffffU << ARM_MMU_PAGE_TABLE_BASE_SHIFT)
#define ARM_MMU_PAGE_TABLE_DOMAIN_SHIFT 5
#define ARM_MMU_PAGE_TABLE_DOMAIN_MASK (0xfU << ARM_MMU_PAGE_TABLE_DOMAIN_SHIFT)
#define ARM_MMU_PAGE_TABLE_NS (1U << 3)
#define ARM_MMU_PAGE_TABLE_PXN (1U << 2)
#define ARM_MMU_PAGE_TABLE_DEFAULT 0x1U

#define ARM_MMU_SMALL_PAGE_BASE_SHIFT 12
#define ARM_MMU_SMALL_PAGE_BASE_MASK (0xfffffU << ARM_MMU_SMALL_PAGE_BASE_SHIFT)
#define ARM_MMU_SMALL_PAGE_NG (1U << 11)
#define ARM_MMU_SMALL_PAGE_S (1U << 10)
#define ARM_MMU_SMALL_PAGE_AP_2 (1U << 9)
#define ARM_MMU_SMALL_PAGE_TEX_2 (1U << 8)
#define ARM_MMU_SMALL_PAGE_TEX_1 (1U << 7)
#define ARM_MMU_SMALL_PAGE_TEX_0 (1U << 6)
#define ARM_MMU_SMALL_PAGE_TEX_SHIFT 6
#define ARM_MMU_SMALL_PAGE_TEX_MASK (0x3U << ARM_MMU_SMALL_PAGE_TEX_SHIFT)
#define ARM_MMU_SMALL_PAGE_AP_1 (1U << 5)
#define ARM_MMU_SMALL_PAGE_AP_0 (1U << 4)
#define ARM_MMU_SMALL_PAGE_AP_SHIFT 4
#define ARM_MMU_SMALL_PAGE_AP_MASK (0x23U << ARM_MMU_SMALL_PAGE_AP_SHIFT)
#define ARM_MMU_SMALL_PAGE_C (1U << 3)
#define ARM_MMU_SMALL_PAGE_B (1U << 2)
#define ARM_MMU_SMALL_PAGE_XN (1U << 0)
#define ARM_MMU_SMALL_PAGE_DEFAULT 0x2U
#define ARM_MMU_SMALL_PAGE_GET_INDEX(mva) \
  (((uint32_t) (mva)) >> ARM_MMU_SMALL_PAGE_BASE_SHIFT)
#define ARM_MMU_SMALL_PAGE_MVA_ALIGN_UP(mva) \
  ((1U << ARM_MMU_SMALL_PAGE_BASE_SHIFT) \
    + ((((uint32_t) (mva) - 1U)) & ~((1U << ARM_MMU_SMALL_PAGE_BASE_SHIFT) - 1U)))

#define ARM_MMU_SECT_FLAGS_TO_PAGE_TABLE(flags) \
  (ARM_MMU_PAGE_TABLE_DEFAULT \
    | ((flags) & ARM_MMU_SECT_DOMAIN_MASK) \
    | (((flags) & ARM_MMU_SECT_NS) >> 16) \
    | (((flags) & ARM_MMU_SECT_PXN) << 2))

#define ARM_MMU_PAGE_TABLE_FLAGS_TO_SECT(flags) \
  (ARM_MMU_SECT_DEFAULT \
    | ((flags) & ARM_MMU_PAGE_TABLE_DOMAIN_MASK) \
    | (((flags) & ARM_MMU_PAGE_TABLE_NS) << 16) \
    | (((flags) & ARM_MMU_PAGE_TABLE_PXN) >> 2))

#define ARM_MMU_SECT_FLAGS_TO_SMALL_PAGE(flags) \
  ((((flags) & 0x3fc00) >> 6) \
    | ((flags) & (ARM_MMU_SECT_C | ARM_MMU_SECT_B | 0x2)) \
    | (((flags) & ARM_MMU_SECT_XN) >> 4))

#define ARM_MMU_SMALL_PAGE_FLAGS_TO_SECT(flags) \
  ((((flags) & 0xff0) << 6) \
    | ((flags) & (ARM_MMU_SMALL_PAGE_C | ARM_MMU_SMALL_PAGE_B | 0x2)) \
    | (((flags) & ARM_MMU_SMALL_PAGE_XN) << 4))

#define ARM_MMU_TRANSLATION_TABLE_ENTRY_SIZE 4U
#define ARM_MMU_TRANSLATION_TABLE_ENTRY_COUNT 4096U

#define ARM_MMU_SMALL_PAGE_TABLE_ENTRY_SIZE 4U
#define ARM_MMU_SMALL_PAGE_TABLE_ENTRY_COUNT 256U

#define ARM_MMU_DEFAULT_CLIENT_DOMAIN 15U

#define ARMV7_MMU_READ_ONLY \
  ((ARM_MMU_DEFAULT_CLIENT_DOMAIN << ARM_MMU_SECT_DOMAIN_SHIFT) \
    | ARM_MMU_SECT_AP_0 \
    | ARM_MMU_SECT_AP_2 \
    | ARM_MMU_SECT_DEFAULT)

#define ARMV7_MMU_READ_ONLY_CACHED \
  (ARMV7_MMU_READ_ONLY | ARM_MMU_SECT_TEX_0 | ARM_MMU_SECT_C | ARM_MMU_SECT_B)

#define ARMV7_MMU_READ_WRITE \
  ((ARM_MMU_DEFAULT_CLIENT_DOMAIN << ARM_MMU_SECT_DOMAIN_SHIFT) \
    | ARM_MMU_SECT_AP_0 \
    | ARM_MMU_SECT_DEFAULT)

#ifdef RTEMS_SMP
  #define ARMV7_MMU_READ_WRITE_CACHED \
    (ARMV7_MMU_READ_WRITE \
      | ARM_MMU_SECT_TEX_0 | ARM_MMU_SECT_C | ARM_MMU_SECT_B | ARM_MMU_SECT_S)
#else
  #define ARMV7_MMU_READ_WRITE_CACHED \
    (ARMV7_MMU_READ_WRITE \
      | ARM_MMU_SECT_TEX_0 | ARM_MMU_SECT_C | ARM_MMU_SECT_B)
#endif

#define ARMV7_MMU_DATA_READ_ONLY \
  (ARMV7_MMU_READ_ONLY | ARM_MMU_SECT_TEX_0)

#define ARMV7_MMU_DATA_READ_ONLY_CACHED \
  ARMV7_MMU_READ_ONLY_CACHED

#define ARMV7_MMU_DATA_READ_WRITE \
  (ARMV7_MMU_READ_WRITE | ARM_MMU_SECT_TEX_0)

#define ARMV7_MMU_DATA_READ_WRITE_CACHED \
  ARMV7_MMU_READ_WRITE_CACHED

#define ARMV7_MMU_CODE \
  (ARMV7_MMU_READ_ONLY | ARM_MMU_SECT_TEX_0)

#define ARMV7_MMU_CODE_CACHED \
  ARMV7_MMU_READ_ONLY_CACHED

#define ARMV7_MMU_DEVICE \
  (ARMV7_MMU_READ_WRITE | ARM_MMU_SECT_B)

/** @} */

/**
 * @name Control Register Defines
 *
 * @{
 */

#define ARM_CP15_CTRL_TE (1U << 30)
#define ARM_CP15_CTRL_AFE (1U << 29)
#define ARM_CP15_CTRL_TRE (1U << 28)
#define ARM_CP15_CTRL_NMFI (1U << 27)
#define ARM_CP15_CTRL_EE (1U << 25)
#define ARM_CP15_CTRL_VE (1U << 24)
#define ARM_CP15_CTRL_XP (1U << 23)
#define ARM_CP15_CTRL_U (1U << 22)
#define ARM_CP15_CTRL_FI (1U << 21)
#define ARM_CP15_CTRL_UWXN (1U << 20)
#define ARM_CP15_CTRL_WXN (1U << 19)
#define ARM_CP15_CTRL_HA (1U << 17)
#define ARM_CP15_CTRL_L4 (1U << 15)
#define ARM_CP15_CTRL_RR (1U << 14)
#define ARM_CP15_CTRL_V (1U << 13)
#define ARM_CP15_CTRL_I (1U << 12)
#define ARM_CP15_CTRL_Z (1U << 11)
#define ARM_CP15_CTRL_SW (1U << 10)
#define ARM_CP15_CTRL_R (1U << 9)
#define ARM_CP15_CTRL_S (1U << 8)
#define ARM_CP15_CTRL_B (1U << 7)
#define ARM_CP15_CTRL_CP15BEN (1U << 5)
#define ARM_CP15_CTRL_C (1U << 2)
#define ARM_CP15_CTRL_A (1U << 1)
#define ARM_CP15_CTRL_M (1U << 0)

/** @} */

/**
 * @name Domain Access Control Defines
 *
 * @{
 */

#define ARM_CP15_DAC_NO_ACCESS 0x0U
#define ARM_CP15_DAC_CLIENT 0x1U
#define ARM_CP15_DAC_MANAGER 0x3U
#define ARM_CP15_DAC_DOMAIN(index, val) ((val) << (2 * index))

/** @} */

/**
 * @name Fault Status Register Defines
 *
 * @{
 */

#define ARM_CP15_FAULT_STATUS_MASK 0x040F

#define ARM_CP15_FSR_ALIGNMENT_FAULT   0x00000001
#define ARM_CP15_FSR_BACKGROUND_FAULT  0x0000
#define ARM_CP15_FSR_ACCESS_PERMISSION_FAULT 0x000D
#define ARM_CP15_FSR_PRECISE_EXTERNAL_ABORT_FAULT 0x0008
#define ARM_CP15_FSR_IMPRECISE_EXTERNAL_ABORT_FAULT 0x0406
#define ARM_CP15_FSR_PRECISE_PARITY_ERROR_EXCEPTION 0x0006
#define ARM_CP15_FSR_IMPRECISE_PARITY_ERROR_EXCEPTION 0x0408
#define ARM_CP15_FSR_DEBUG_EVENT 0x0002

/** @} */

/**
 * @name CTR, Cache Type Register Defines
 *
 * The format can be obtained from CP15 by call
 * arm_cp15_cache_type_get_format(arm_cp15_get_cache_type());
 *
 * @{
 */

#define ARM_CP15_CACHE_TYPE_FORMAT_ARMV6 0
#define ARM_CP15_CACHE_TYPE_FORMAT_ARMV7 4

/** @} */

/**
 * @name CCSIDR, Cache Size ID Register Defines
 *
 * @{
 */

#define ARM_CP15_CACHE_CSS_ID_DATA 0
#define ARM_CP15_CACHE_CSS_ID_INSTRUCTION 1
#define ARM_CP15_CACHE_CSS_LEVEL(level) ((level) << 1)

/** @} */

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_id_code(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c0, c0, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_tcm_status(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c0, c0, 2\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_control(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c1, c0, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_control(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c1, c0, 0\n"
    "nop\n"
    "nop\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
    : "memory"
  );
}

/**
 * @name MMU Functions
 *
 * @{
 */

/**
 * @brief Disable the MMU.
 *
 * This function will clean and invalidate eight cache lines before and after
 * the current stack pointer.
 *
 * @param[in] cls The data cache line size.
 *
 * @return The current control register value.
 */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_mmu_disable(uint32_t cls)
{
  ARM_SWITCH_REGISTERS;
  uint32_t ctrl;
  uint32_t tmp_0;
  uint32_t tmp_1;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[ctrl], c1, c0, 0\n"
    "bic %[tmp_0], %[ctrl], #1\n"
    "mcr p15, 0, %[tmp_0], c1, c0, 0\n"
    "nop\n"
    "nop\n"
    "mov %[tmp_1], sp\n"
    "rsb %[tmp_0], %[cls], #0\n"
    "and %[tmp_0], %[tmp_0], %[tmp_1]\n"
    "sub %[tmp_0], %[tmp_0], %[cls], asl #3\n"
    "add %[tmp_1], %[tmp_0], %[cls], asl #4\n"
    "1:\n"
    "mcr p15, 0, %[tmp_0], c7, c14, 1\n"
    "add %[tmp_0], %[tmp_0], %[cls]\n"
    "cmp %[tmp_1], %[tmp_0]\n"
    "bne 1b\n"
    ARM_SWITCH_BACK
    : [ctrl] "=&r" (ctrl),
      [tmp_0] "=&r" (tmp_0),
      [tmp_1] "=&r" (tmp_1)
      ARM_SWITCH_ADDITIONAL_OUTPUT
    : [cls] "r" (cls)
    : "memory", "cc"
  );

  return ctrl;
}

ARM_CP15_TEXT_SECTION static inline uint32_t
*arm_cp15_get_translation_table_base(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t *base;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[base], c2, c0, 0\n"
    ARM_SWITCH_BACK
    : [base] "=&r" (base) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return base;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_translation_table_base(uint32_t *base)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[base], c2, c0, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [base] "r" (base)
  );
}

/* Translation Table Base Control Register - DDI0301H arm1176jzfs TRM 3.2.15 */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_translation_table_base_control_register(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t ttb_cr;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[ttb_cr], c2, c0, 2\n"
    ARM_SWITCH_BACK
    : [ttb_cr] "=&r" (ttb_cr) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return ttb_cr;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_translation_table_base_control_register(uint32_t ttb_cr)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[ttb_cr], c2, c0, 2\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [ttb_cr] "r" (ttb_cr)
  );
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_domain_access_control(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c3, c0, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_domain_access_control(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c3, c0, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_data_fault_status(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c5, c0, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_data_fault_status(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c5, c0, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_instruction_fault_status(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c5, c0, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_instruction_fault_status(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c5, c0, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

ARM_CP15_TEXT_SECTION static inline void
*arm_cp15_get_fault_address(void)
{
  ARM_SWITCH_REGISTERS;
  void *mva;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[mva], c6, c0, 0\n"
    ARM_SWITCH_BACK
    : [mva] "=&r" (mva) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return mva;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_fault_address(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c6, c0, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_tlb_invalidate(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c8, c7, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
  );

  /*
   * ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition, Issue C,
   * B3.10.1 General TLB maintenance requirements.
   */
  _ARM_Data_synchronization_barrier();
  _ARM_Instruction_synchronization_barrier();
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_tlb_invalidate_entry(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_TLB_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c8, c7, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_tlb_invalidate_entry_all_asids(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_TLB_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c8, c7, 3\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_tlb_invalidate_entry_all_asids_inner_shareable(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_TLB_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c8, c3, 3\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_tlb_instruction_invalidate(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c8, c5, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_tlb_instruction_invalidate_entry(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_TLB_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c8, c5, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_tlb_data_invalidate(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c8, c6, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_tlb_data_invalidate_entry(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_TLB_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c8, c6, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_tlb_lockdown_entry(const void *mva)
{
  uint32_t arm_switch_reg;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "add %[arm_switch_reg], pc, #16\n"
    "mcr p15, 0, %[arm_switch_reg], c7, c13, 1\n"
    "mcr p15, 0, %[mva], c8, c7, 1\n"
    "mrc p15, 0, %[arm_switch_reg], c10, c0, 0\n"
    "orr %[arm_switch_reg], #0x1\n"
    "mcr p15, 0, %[arm_switch_reg], c10, c0, 0\n"
    "ldr %[mva], [%[mva]]\n"
    "mrc p15, 0, %[arm_switch_reg], c10, c0, 0\n"
    "bic %[arm_switch_reg], #0x1\n"
    "mcr p15, 0, %[arm_switch_reg], c10, c0, 0\n"
    ARM_SWITCH_BACK
    : [mva] "=r" (mva), [arm_switch_reg] "=&r" (arm_switch_reg)
    : "[mva]" (mva)
  );
}

/** @} */

/**
 * @name Cache Functions
 *
 * @{
 */

/* Read cache type register CTR */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_cache_type(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c0, c0, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* Extract format version from cache type CTR */
ARM_CP15_TEXT_SECTION static inline int
arm_cp15_cache_type_get_format(uint32_t ct)
{
  return (ct >> 29) & 0x7U;
}

/* Read size of smallest cache line of all instruction/data caches controlled by the processor */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_min_cache_line_size(void)
{
  uint32_t mcls = 0;
  uint32_t ct = arm_cp15_get_cache_type();
  uint32_t format = arm_cp15_cache_type_get_format(ct);

  if (format == ARM_CP15_CACHE_TYPE_FORMAT_ARMV7) {
    /* ARMv7 format */
    mcls = (1U << (ct & 0xf)) * 4;
  } else if (format == ARM_CP15_CACHE_TYPE_FORMAT_ARMV6) {
    /* ARMv6 format */
    uint32_t mask = (1U << 12) - 1;
    uint32_t dcls = (ct >> 12) & mask;
    uint32_t icls = ct & mask;

    mcls = dcls <= icls ? dcls : icls;
  }

  return mcls;
}

/* Read size of smallest data cache lines */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_data_cache_line_size(void)
{
  uint32_t mcls = 0;
  uint32_t ct = arm_cp15_get_cache_type();
  uint32_t format = arm_cp15_cache_type_get_format(ct);

  if (format == ARM_CP15_CACHE_TYPE_FORMAT_ARMV7) {
    /* ARMv7 format */
    mcls = (1U << ((ct & 0xf0000) >> 16)) * 4;
  } else if (format == ARM_CP15_CACHE_TYPE_FORMAT_ARMV6) {
    /* ARMv6 format */
    uint32_t mask = (1U << 12) - 1;
    mcls = (ct >> 12) & mask;
  }

  return mcls;
}

/* Read size of smallest instruction cache lines */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_instruction_cache_line_size(void)
{
  uint32_t mcls = 0;
  uint32_t ct = arm_cp15_get_cache_type();
  uint32_t format = arm_cp15_cache_type_get_format(ct);

  if (format == ARM_CP15_CACHE_TYPE_FORMAT_ARMV7) {
    /* ARMv7 format */
    mcls = (1U << (ct & 0x0000f)) * 4;
  } else if (format == ARM_CP15_CACHE_TYPE_FORMAT_ARMV6) {
    /* ARMv6 format */
    uint32_t mask = (1U << 12) - 1;
    mcls = ct & mask;;
  }

  return mcls;
}

/* CCSIDR, Cache Size ID Register */

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_cache_size_id(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 1, %[val], c0, c0, 0\n"
     ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_ccsidr_get_line_power(uint32_t ccsidr)
{
  return (ccsidr & 0x7) + 4;
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_ccsidr_get_associativity(uint32_t ccsidr)
{
  return ((ccsidr >> 3) & 0x3ff) + 1;
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_ccsidr_get_num_sets(uint32_t ccsidr)
{
  return ((ccsidr >> 13) & 0x7fff) + 1;
}

/* CLIDR, Cache Level ID Register */

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_cache_level_id(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 1, %[val], c0, c0, 1\n"
     ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_clidr_get_level_of_coherency(uint32_t clidr)
{
  return (clidr >> 24) & 0x7;
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_clidr_get_cache_type(uint32_t clidr, uint32_t level)
{
  return (clidr >> (3 * level)) & 0x7;
}

/* CSSELR, Cache Size Selection Register */

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_cache_size_selection(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 2, %[val], c0, c0, 0\n"
     ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_cache_size_selection(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 2, %[val], c0, c0, 0\n"
     ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_cache_size_id_for_level(uint32_t level_and_inst_dat)
{
  rtems_interrupt_level irq_level;
  uint32_t ccsidr;

  rtems_interrupt_local_disable(irq_level);
  arm_cp15_set_cache_size_selection(level_and_inst_dat);
  _ARM_Instruction_synchronization_barrier();
  ccsidr = arm_cp15_get_cache_size_id();
  rtems_interrupt_local_enable(irq_level);

  return ccsidr;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_cache_invalidate(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c7, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
}

/* ICIALLUIS, Instruction Cache Invalidate All to PoU, Inner Shareable */

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_instruction_cache_inner_shareable_invalidate_all(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c1, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
}

/* BPIALLIS, Branch Predictor Invalidate All, Inner Shareable */

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_branch_predictor_inner_shareable_invalidate_all(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c1, 6\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
}

/* BPIALL, Branch Predictor Invalidate All */

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_branch_predictor_invalidate_all(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c5, 6\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
}

/* Flush Prefetch Buffer - DDI0301H arm1176jzfs TRM 3.2.22 */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_flush_prefetch_buffer(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c5, 4\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_instruction_cache_invalidate(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c5, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_instruction_cache_invalidate_line(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_CACHE_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c7, c5, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_instruction_cache_invalidate_line_by_set_and_way(uint32_t set_and_way)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[set_and_way], c7, c5, 2\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [set_and_way] "r" (set_and_way)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_instruction_cache_prefetch_line(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_CACHE_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c7, c13, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_invalidate(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c6, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_invalidate_line(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_CACHE_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c7, c6, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_invalidate_line_by_set_and_way(uint32_t set_and_way)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[set_and_way], c7, c6, 2\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [set_and_way] "r" (set_and_way)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_cache_invalidate_level(uint32_t level, uint32_t inst_data_fl)
{
  uint32_t ccsidr;
  uint32_t line_power;
  uint32_t associativity;
  uint32_t way;
  uint32_t way_shift;

  ccsidr = arm_cp15_get_cache_size_id_for_level((level << 1) | inst_data_fl);

  line_power = arm_ccsidr_get_line_power(ccsidr);
  associativity = arm_ccsidr_get_associativity(ccsidr);
  way_shift = __builtin_clz(associativity - 1);

  for (way = 0; way < associativity; ++way) {
    uint32_t num_sets = arm_ccsidr_get_num_sets(ccsidr);
    uint32_t set;

    for (set = 0; set < num_sets; ++set) {
      uint32_t set_way = (way << way_shift)
        | (set << line_power)
        | (level << 1);

      arm_cp15_data_cache_invalidate_line_by_set_and_way(set_way);
    }
  }
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_invalidate_all_levels(void)
{
  uint32_t clidr = arm_cp15_get_cache_level_id();
  uint32_t loc = arm_clidr_get_level_of_coherency(clidr);
  uint32_t level = 0;

  for (level = 0; level < loc; ++level) {
    uint32_t ctype = arm_clidr_get_cache_type(clidr, level);

    /* Check if this level has a data cache or unified cache */
    if (((ctype & (0x6)) == 2) || (ctype == 4)) {
      arm_cp15_cache_invalidate_level(level, 0);
    }
  }
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_clean_line(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_CACHE_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c7, c10, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_clean_line_by_set_and_way(uint32_t set_and_way)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[set_and_way], c7, c10, 2\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [set_and_way] "r" (set_and_way)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_clean_level(uint32_t level)
{
  uint32_t ccsidr;
  uint32_t line_power;
  uint32_t associativity;
  uint32_t way;
  uint32_t way_shift;

  ccsidr = arm_cp15_get_cache_size_id_for_level(level << 1);

  line_power = arm_ccsidr_get_line_power(ccsidr);
  associativity = arm_ccsidr_get_associativity(ccsidr);
  way_shift = __builtin_clz(associativity - 1);

  for (way = 0; way < associativity; ++way) {
    uint32_t num_sets = arm_ccsidr_get_num_sets(ccsidr);
    uint32_t set;

    for (set = 0; set < num_sets; ++set) {
      uint32_t set_way = (way << way_shift)
        | (set << line_power)
        | (level << 1);

      arm_cp15_data_cache_clean_line_by_set_and_way(set_way);
    }
  }
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_clean_all_levels(void)
{
  uint32_t clidr = arm_cp15_get_cache_level_id();
  uint32_t loc = arm_clidr_get_level_of_coherency(clidr);
  uint32_t level = 0;

  for (level = 0; level < loc; ++level) {
    uint32_t ctype = arm_clidr_get_cache_type(clidr, level);

    /* Check if this level has a data cache or unified cache */
    if (((ctype & (0x6)) == 2) || (ctype == 4)) {
      arm_cp15_data_cache_clean_level(level);
    }
  }
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_test_and_clean(void)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "1:\n"
    "mrc p15, 0, r15, c7, c10, 3\n"
    "bne 1b\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    :
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_clean_and_invalidate(void)
{
  ARM_SWITCH_REGISTERS;

#if __ARM_ARCH >= 6
  /*
   * In DDI0301H_arm1176jzfs_r0p7_trm
   * 'MCR p15, 0, <Rd>, c7, c14, 0' means
   * Clean and Invalidate Entire Data Cache
   */
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c14, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
#else
  /*
   * Assume this is an ARM926EJ-S.  Use the test, clean, and invalidate DCache
   * operation.
   */
  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "1:\n"
    "mrc p15, 0, r15, c7, c14, 3\n"
    "bne 1b\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    :
    : "memory"
  );
#endif
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_clean_and_invalidate_line(const void *mva)
{
  ARM_SWITCH_REGISTERS;

  mva = ARM_CP15_CACHE_PREPARE_MVA(mva);

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[mva], c7, c14, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [mva] "r" (mva)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_clean_and_invalidate_line_by_set_and_way(uint32_t set_and_way)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[set_and_way], c7, c14, 2\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [set_and_way] "r" (set_and_way)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_data_cache_test_and_clean_and_invalidate(void)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "1:\n"
    "mrc p15, 0, r15, c7, c14, 3\n"
    "bne 1b\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    :
    : "memory"
  );
}

/** @} */

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_drain_write_buffer(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c10, 4\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_wait_for_interrupt(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t sbz = 0;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[sbz], c7, c0, 4\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [sbz] "r" (sbz)
    : "memory"
  );
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_multiprocessor_affinity(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t mpidr;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
	  "mrc p15, 0, %[mpidr], c0, c0, 5\n"
    ARM_SWITCH_BACK
    : [mpidr] "=&r" (mpidr) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return mpidr & 0xff;
}

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cortex_a9_get_multiprocessor_cpu_id(void)
{
  return arm_cp15_get_multiprocessor_affinity() & 0xff;
}

#define ARM_CORTEX_A9_ACTL_FW (1U << 0)
#define ARM_CORTEX_A9_ACTL_L2_PREFETCH_HINT_ENABLE (1U << 1)
#define ARM_CORTEX_A9_ACTL_L1_PREFETCH_ENABLE (1U << 2)
#define ARM_CORTEX_A9_ACTL_WRITE_FULL_LINE_OF_ZEROS_MODE (1U << 3)
#define ARM_CORTEX_A9_ACTL_SMP (1U << 6)
#define ARM_CORTEX_A9_ACTL_EXCL (1U << 7)
#define ARM_CORTEX_A9_ACTL_ALLOC_IN_ONE_WAY (1U << 8)
#define ARM_CORTEX_A9_ACTL_PARITY_ON (1U << 9)

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_auxiliary_control(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c1, c0, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_auxiliary_control(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c1, c0, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* ID_PFR1, Processor Feature Register 1 */

ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_processor_feature_1(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c0, c1, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* VBAR, Vector Base Address Register, Security Extensions */

ARM_CP15_TEXT_SECTION static inline void
*arm_cp15_get_vector_base_address(void)
{
  ARM_SWITCH_REGISTERS;
  void *base;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[base], c12, c0, 0\n"
    ARM_SWITCH_BACK
    : [base] "=&r" (base) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return base;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_vector_base_address(void *base)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[base], c12, c0, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [base] "r" (base)
  );
}

ARM_CP15_TEXT_SECTION static inline void
*arm_cp15_get_hyp_vector_base_address(void)
{
  ARM_SWITCH_REGISTERS;
  void *base;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 4, %[base], c12, c0, 0\n"
    ARM_SWITCH_BACK
    : [base] "=&r" (base) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return base;
}

ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_hyp_vector_base_address(void *base)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 4, %[base], c12, c0, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [base] "r" (base)
  );
}

/* PMCCNTR */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_cycle_count(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c13, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMCCNTR */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_cycle_count(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c13, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMCEID0 */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_common_event_id_0(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c12, 6\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMCEID1 */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_common_event_id_1(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c12, 7\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

#define ARM_CP15_PMCLRSET_CYCLE_COUNTER 0x80000000

/* PMCCNTENCLR */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_count_enable_clear(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c12, 2\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMCCNTENCLR */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_count_enable_clear(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c12, 2\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMCCNTENSET */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_count_enable_set(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c12, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMCCNTENSET */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_count_enable_set(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c12, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

#define ARM_CP15_PMCR_IMP(x) ((x) << 24)
#define ARM_CP15_PMCR_IDCODE(x) ((x) << 16)
#define ARM_CP15_PMCR_N(x) ((x) << 11)
#define ARM_CP15_PMCR_DP (1U << 5)
#define ARM_CP15_PMCR_X (1U << 4)
#define ARM_CP15_PMCR_D (1U << 3)
#define ARM_CP15_PMCR_C (1U << 2)
#define ARM_CP15_PMCR_P (1U << 1)
#define ARM_CP15_PMCR_E (1U << 0)

/* PMCR */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_control(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c12, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMCR */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_control(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c12, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMINTENCLR */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_interrupt_enable_clear(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c14, 2\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMINTENCLR */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_interrupt_enable_clear(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c14, 2\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMINTENSET */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_interrupt_enable_set(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c14, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMINTENSET */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_interrupt_enable_set(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c14, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMOVSR */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_overflow_flag_status(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c12, 3\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMOVSR */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_overflow_flag_status(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c12, 3\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMOVSSET */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_overflow_flag_status_set(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c14, 3\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMOVSSET */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_overflow_flag_status_set(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c14, 3\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMSELR */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_event_counter_selection(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c12, 5\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMSELR */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_event_counter_selection(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c12, 5\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMSWINC */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_software_increment(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c12, 4\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMUSERENR */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_user_enable(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c14, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMUSERENR */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_user_enable(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c14, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMXEVCNTR */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_event_count(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c13, 2\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMXEVCNTR */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_event_count(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c13, 2\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* PMXEVTYPER */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_performance_monitors_event_type_select(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c9, c13, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* PMXEVTYPER */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_performance_monitors_event_type_select(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c9, c13, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* CNTFRQ */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_counter_frequency(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c14, c0, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTFRQ */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_counter_frequency(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c14, c0, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* CNTPCT */
ARM_CP15_TEXT_SECTION static inline uint64_t
arm_cp15_get_counter_physical_count(void)
{
  ARM_SWITCH_REGISTERS;
  uint64_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrrc p15, 0, %Q[val], %R[val], c14\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTKCTL */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_counter_non_secure_pl1_control(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c14, c1, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTKCTL */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_counter_non_secure_pl1_control(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c14, c1, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* CNTP_TVAL */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_counter_pl1_physical_timer_value(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c14, c2, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTP_TVAL */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_counter_pl1_physical_timer_value(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c14, c2, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* CNTP_CTL */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_counter_pl1_physical_timer_control(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c14, c2, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTP_CTL */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_counter_pl1_physical_timer_control(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c14, c2, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* CNTV_TVAL */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_counter_pl1_virtual_timer_value(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c14, c3, 0\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTV_TVAL */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_counter_pl1_virtual_timer_value(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c14, c3, 0\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* CNTV_CTL */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_counter_pl1_virtual_timer_control(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c14, c3, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTV_CTL */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_counter_pl1_virtual_timer_control(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c14, c3, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* CNTVCT */
ARM_CP15_TEXT_SECTION static inline uint64_t
arm_cp15_get_counter_virtual_count(void)
{
  ARM_SWITCH_REGISTERS;
  uint64_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrrc p15, 1, %Q[val], %R[val], c14\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTP_CVAL */
ARM_CP15_TEXT_SECTION static inline uint64_t
arm_cp15_get_counter_pl1_physical_compare_value(void)
{
  ARM_SWITCH_REGISTERS;
  uint64_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrrc p15, 2, %Q[val], %R[val], c14\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTP_CVAL */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_counter_pl1_physical_compare_value(uint64_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcrr p15, 2, %Q[val], %R[val], c14\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* CNTV_CVAL */
ARM_CP15_TEXT_SECTION static inline uint64_t
arm_cp15_get_counter_pl1_virtual_compare_value(void)
{
  ARM_SWITCH_REGISTERS;
  uint64_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrrc p15, 3, %Q[val], %R[val], c14\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTV_CVAL */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_counter_pl1_virtual_compare_value(uint64_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcrr p15, 3, %Q[val], %R[val], c14\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* CNTVOFF */
ARM_CP15_TEXT_SECTION static inline uint64_t
arm_cp15_get_counter_virtual_offset(void)
{
  ARM_SWITCH_REGISTERS;
  uint64_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrrc p15, 4, %Q[val], %R[val], c14\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* CNTVOFF */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_counter_virtual_offset(uint64_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcrr p15, 4, %Q[val], %R[val], c14\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/* Diagnostic Control Register */
ARM_CP15_TEXT_SECTION static inline uint32_t
arm_cp15_get_diagnostic_control(void)
{
  ARM_SWITCH_REGISTERS;
  uint32_t val;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mrc p15, 0, %[val], c15, c0, 1\n"
    ARM_SWITCH_BACK
    : [val] "=&r" (val) ARM_SWITCH_ADDITIONAL_OUTPUT
  );

  return val;
}

/* Diagnostic Control Register */
ARM_CP15_TEXT_SECTION static inline void
arm_cp15_set_diagnostic_control(uint32_t val)
{
  ARM_SWITCH_REGISTERS;

  __asm__ volatile (
    ARM_SWITCH_TO_ARM
    "mcr p15, 0, %[val], c15, c0, 1\n"
    ARM_SWITCH_BACK
    : ARM_SWITCH_OUTPUT
    : [val] "r" (val)
  );
}

/**
 * @brief Sets the @a section_flags for the address range [@a begin, @a end).
 *
 * @return Previous section flags of the first modified entry.
 */
uint32_t arm_cp15_set_translation_table_entries(
  const void *begin,
  const void *end,
  uint32_t section_flags
);

/**
 * @brief Sets the @a exception @a handler in the vector table.
 *
 * @return Previous handler.
 */
void* arm_cp15_set_exception_handler(
  Arm_symbolic_exception_name exception,
  void (*handler)(void)
);

/** @} */

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* LIBCPU_SHARED_ARM_CP15_H */