/******************************************************************************
* @file     mpu_armv7.h
* @brief    CMSIS MPU API for Armv7-M MPU
* @version  V5.1.1
* @date     10. February 2020
******************************************************************************/

/*
 * Copyright (c) 2017-2020 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#if   defined( __ICCARM__ )
    #pragma system_include      /* treat file as system include file for MISRA check */
#elif defined( __clang__ )
    #pragma clang system_header /* treat file as system include file */
#endif

#ifndef ARM_MPU_ARMV7_H
    #define ARM_MPU_ARMV7_H

    #define ARM_MPU_REGION_SIZE_32B      ( ( uint8_t ) 0x04U ) /*/!< MPU Region Size 32 Bytes */
    #define ARM_MPU_REGION_SIZE_64B      ( ( uint8_t ) 0x05U ) /*/!< MPU Region Size 64 Bytes */
    #define ARM_MPU_REGION_SIZE_128B     ( ( uint8_t ) 0x06U ) /*/!< MPU Region Size 128 Bytes */
    #define ARM_MPU_REGION_SIZE_256B     ( ( uint8_t ) 0x07U ) /*/!< MPU Region Size 256 Bytes */
    #define ARM_MPU_REGION_SIZE_512B     ( ( uint8_t ) 0x08U ) /*/!< MPU Region Size 512 Bytes */
    #define ARM_MPU_REGION_SIZE_1KB      ( ( uint8_t ) 0x09U ) /*/!< MPU Region Size 1 KByte */
    #define ARM_MPU_REGION_SIZE_2KB      ( ( uint8_t ) 0x0AU ) /*/!< MPU Region Size 2 KBytes */
    #define ARM_MPU_REGION_SIZE_4KB      ( ( uint8_t ) 0x0BU ) /*/!< MPU Region Size 4 KBytes */
    #define ARM_MPU_REGION_SIZE_8KB      ( ( uint8_t ) 0x0CU ) /*/!< MPU Region Size 8 KBytes */
    #define ARM_MPU_REGION_SIZE_16KB     ( ( uint8_t ) 0x0DU ) /*/!< MPU Region Size 16 KBytes */
    #define ARM_MPU_REGION_SIZE_32KB     ( ( uint8_t ) 0x0EU ) /*/!< MPU Region Size 32 KBytes */
    #define ARM_MPU_REGION_SIZE_64KB     ( ( uint8_t ) 0x0FU ) /*/!< MPU Region Size 64 KBytes */
    #define ARM_MPU_REGION_SIZE_128KB    ( ( uint8_t ) 0x10U ) /*/!< MPU Region Size 128 KBytes */
    #define ARM_MPU_REGION_SIZE_256KB    ( ( uint8_t ) 0x11U ) /*/!< MPU Region Size 256 KBytes */
    #define ARM_MPU_REGION_SIZE_512KB    ( ( uint8_t ) 0x12U ) /*/!< MPU Region Size 512 KBytes */
    #define ARM_MPU_REGION_SIZE_1MB      ( ( uint8_t ) 0x13U ) /*/!< MPU Region Size 1 MByte */
    #define ARM_MPU_REGION_SIZE_2MB      ( ( uint8_t ) 0x14U ) /*/!< MPU Region Size 2 MBytes */
    #define ARM_MPU_REGION_SIZE_4MB      ( ( uint8_t ) 0x15U ) /*/!< MPU Region Size 4 MBytes */
    #define ARM_MPU_REGION_SIZE_8MB      ( ( uint8_t ) 0x16U ) /*/!< MPU Region Size 8 MBytes */
    #define ARM_MPU_REGION_SIZE_16MB     ( ( uint8_t ) 0x17U ) /*/!< MPU Region Size 16 MBytes */
    #define ARM_MPU_REGION_SIZE_32MB     ( ( uint8_t ) 0x18U ) /*/!< MPU Region Size 32 MBytes */
    #define ARM_MPU_REGION_SIZE_64MB     ( ( uint8_t ) 0x19U ) /*/!< MPU Region Size 64 MBytes */
    #define ARM_MPU_REGION_SIZE_128MB    ( ( uint8_t ) 0x1AU ) /*/!< MPU Region Size 128 MBytes */
    #define ARM_MPU_REGION_SIZE_256MB    ( ( uint8_t ) 0x1BU ) /*/!< MPU Region Size 256 MBytes */
    #define ARM_MPU_REGION_SIZE_512MB    ( ( uint8_t ) 0x1CU ) /*/!< MPU Region Size 512 MBytes */
    #define ARM_MPU_REGION_SIZE_1GB      ( ( uint8_t ) 0x1DU ) /*/!< MPU Region Size 1 GByte */
    #define ARM_MPU_REGION_SIZE_2GB      ( ( uint8_t ) 0x1EU ) /*/!< MPU Region Size 2 GBytes */
    #define ARM_MPU_REGION_SIZE_4GB      ( ( uint8_t ) 0x1FU ) /*/!< MPU Region Size 4 GBytes */

    #define ARM_MPU_AP_NONE              0U                    /*/!< MPU Access Permission no access */
    #define ARM_MPU_AP_PRIV              1U                    /*/!< MPU Access Permission privileged access only */
    #define ARM_MPU_AP_URO               2U                    /*/!< MPU Access Permission unprivileged access read-only */
    #define ARM_MPU_AP_FULL              3U                    /*/!< MPU Access Permission full access */
    #define ARM_MPU_AP_PRO               5U                    /*/!< MPU Access Permission privileged access read-only */
    #define ARM_MPU_AP_RO                6U                    /*/!< MPU Access Permission read-only access */

/** MPU Region Base Address Register Value
 *
 * \param Region The region to be configured, number 0 to 15.
 * \param BaseAddress The base address for the region.
 */
    #define ARM_MPU_RBAR( Region, BaseAddress ) \
    ( ( ( BaseAddress ) & MPU_RBAR_ADDR_Msk ) | \
      ( ( Region ) & MPU_RBAR_REGION_Msk ) |    \
      ( MPU_RBAR_VALID_Msk ) )

/**
 * MPU Memory Access Attributes
 *
 * \param TypeExtField      Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.
 * \param IsShareable       Region is shareable between multiple bus masters.
 * \param IsCacheable       Region is cacheable, i.e. its value may be kept in cache.
 * \param IsBufferable      Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.
 */
    #define ARM_MPU_ACCESS_( TypeExtField, IsShareable, IsCacheable, IsBufferable ) \
    ( ( ( ( TypeExtField ) << MPU_RASR_TEX_Pos ) & MPU_RASR_TEX_Msk ) |             \
      ( ( ( IsShareable ) << MPU_RASR_S_Pos ) & MPU_RASR_S_Msk ) |                  \
      ( ( ( IsCacheable ) << MPU_RASR_C_Pos ) & MPU_RASR_C_Msk ) |                  \
      ( ( ( IsBufferable ) << MPU_RASR_B_Pos ) & MPU_RASR_B_Msk ) )

/**
 * MPU Region Attribute and Size Register Value
 *
 * \param DisableExec       Instruction access disable bit, 1= disable instruction fetches.
 * \param AccessPermission  Data access permissions, allows you to configure read/write access for User and Privileged mode.
 * \param AccessAttributes  Memory access attribution, see \ref ARM_MPU_ACCESS_.
 * \param SubRegionDisable  Sub-region disable field.
 * \param Size              Region size of the region to be configured, for example 4K, 8K.
 */
    #define ARM_MPU_RASR_EX( DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size )         \
    ( ( ( ( DisableExec ) << MPU_RASR_XN_Pos ) & MPU_RASR_XN_Msk ) |                                           \
      ( ( ( AccessPermission ) << MPU_RASR_AP_Pos ) & MPU_RASR_AP_Msk ) |                                      \
      ( ( ( AccessAttributes ) & ( MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk ) ) ) | \
      ( ( ( SubRegionDisable ) << MPU_RASR_SRD_Pos ) & MPU_RASR_SRD_Msk ) |                                    \
      ( ( ( Size ) << MPU_RASR_SIZE_Pos ) & MPU_RASR_SIZE_Msk ) |                                              \
      ( ( ( MPU_RASR_ENABLE_Msk ) ) ) )

/**
 * MPU Region Attribute and Size Register Value
 *
 * \param DisableExec       Instruction access disable bit, 1= disable instruction fetches.
 * \param AccessPermission  Data access permissions, allows you to configure read/write access for User and Privileged mode.
 * \param TypeExtField      Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.
 * \param IsShareable       Region is shareable between multiple bus masters.
 * \param IsCacheable       Region is cacheable, i.e. its value may be kept in cache.
 * \param IsBufferable      Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.
 * \param SubRegionDisable  Sub-region disable field.
 * \param Size              Region size of the region to be configured, for example 4K, 8K.
 */
    #define ARM_MPU_RASR( DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size ) \
    ARM_MPU_RASR_EX( DisableExec, AccessPermission, ARM_MPU_ACCESS_( TypeExtField, IsShareable, IsCacheable, IsBufferable ), SubRegionDisable, Size )

/**
 * MPU Memory Access Attribute for strongly ordered memory.
 *  - TEX: 000b
 *  - Shareable
 *  - Non-cacheable
 *  - Non-bufferable
 */
    #define ARM_MPU_ACCESS_ORDERED    ARM_MPU_ACCESS_( 0U, 1U, 0U, 0U )

/**
 * MPU Memory Access Attribute for device memory.
 *  - TEX: 000b (if shareable) or 010b (if non-shareable)
 *  - Shareable or non-shareable
 *  - Non-cacheable
 *  - Bufferable (if shareable) or non-bufferable (if non-shareable)
 *
 * \param IsShareable Configures the device memory as shareable or non-shareable.
 */
    #define ARM_MPU_ACCESS_DEVICE( IsShareable )                      ( ( IsShareable ) ? ARM_MPU_ACCESS_( 0U, 1U, 0U, 1U ) : ARM_MPU_ACCESS_( 2U, 0U, 0U, 0U ) )

/**
 * MPU Memory Access Attribute for normal memory.
 *  - TEX: 1BBb (reflecting outer cacheability rules)
 *  - Shareable or non-shareable
 *  - Cacheable or non-cacheable (reflecting inner cacheability rules)
 *  - Bufferable or non-bufferable (reflecting inner cacheability rules)
 *
 * \param OuterCp Configures the outer cache policy.
 * \param InnerCp Configures the inner cache policy.
 * \param IsShareable Configures the memory as shareable or non-shareable.
 */
    #define ARM_MPU_ACCESS_NORMAL( OuterCp, InnerCp, IsShareable )    ARM_MPU_ACCESS_( ( 4U | ( OuterCp ) ), IsShareable, ( ( InnerCp ) >> 1U ), ( ( InnerCp ) & 1U ) )

/**
 * MPU Memory Access Attribute non-cacheable policy.
 */
    #define ARM_MPU_CACHEP_NOCACHE    0U

/**
 * MPU Memory Access Attribute write-back, write and read allocate policy.
 */
    #define ARM_MPU_CACHEP_WB_WRA     1U

/**
 * MPU Memory Access Attribute write-through, no write allocate policy.
 */
    #define ARM_MPU_CACHEP_WT_NWA     2U

/**
 * MPU Memory Access Attribute write-back, no write allocate policy.
 */
    #define ARM_MPU_CACHEP_WB_NWA     3U


/**
 * Struct for a single MPU Region
 */
    typedef struct
    {
        uint32_t RBAR; /*!< The region base address register value (RBAR) */
        uint32_t RASR; /*!< The region attribute and size register value (RASR) \ref MPU_RASR */
    } ARM_MPU_Region_t;

/** Enable the MPU.
 * \param MPU_Control Default access permissions for unconfigured regions.
 */
    __STATIC_INLINE void ARM_MPU_Enable( uint32_t MPU_Control )
    {
        __DMB();
        MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk;
        #ifdef SCB_SHCSR_MEMFAULTENA_Msk
            SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
        #endif
        __DSB();
        __ISB();
    }

/** Disable the MPU.
 */
    __STATIC_INLINE void ARM_MPU_Disable( void )
    {
        __DMB();
        #ifdef SCB_SHCSR_MEMFAULTENA_Msk
            SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
        #endif
        MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;
        __DSB();
        __ISB();
    }

/** Clear and disable the given MPU region.
 * \param rnr Region number to be cleared.
 */
    __STATIC_INLINE void ARM_MPU_ClrRegion( uint32_t rnr )
    {
        MPU->RNR = rnr;
        MPU->RASR = 0U;
    }

/** Configure an MPU region.
 * \param rbar Value for RBAR register.
 * \param rsar Value for RSAR register.
 */
    __STATIC_INLINE void ARM_MPU_SetRegion( uint32_t rbar,
                                            uint32_t rasr )
    {
        MPU->RBAR = rbar;
        MPU->RASR = rasr;
    }

/** Configure the given MPU region.
 * \param rnr Region number to be configured.
 * \param rbar Value for RBAR register.
 * \param rsar Value for RSAR register.
 */
    __STATIC_INLINE void ARM_MPU_SetRegionEx( uint32_t rnr,
                                              uint32_t rbar,
                                              uint32_t rasr )
    {
        MPU->RNR = rnr;
        MPU->RBAR = rbar;
        MPU->RASR = rasr;
    }

/** Memcopy with strictly ordered memory access, e.g. for register targets.
 * \param dst Destination data is copied to.
 * \param src Source data is copied from.
 * \param len Amount of data words to be copied.
 */
    __STATIC_INLINE void ARM_MPU_OrderedMemcpy( volatile uint32_t * dst,
                                                const uint32_t * __RESTRICT src,
                                                uint32_t len )
    {
        uint32_t i;

        for( i = 0U; i < len; ++i )
        {
            dst[ i ] = src[ i ];
        }
    }

/** Load the given number of MPU regions from a table.
 * \param table Pointer to the MPU configuration table.
 * \param cnt Amount of regions to be configured.
 */
    __STATIC_INLINE void ARM_MPU_Load( ARM_MPU_Region_t const * table,
                                       uint32_t cnt )
    {
        const uint32_t rowWordSize = sizeof( ARM_MPU_Region_t ) / 4U;

        while( cnt > MPU_TYPE_RALIASES )
        {
            ARM_MPU_OrderedMemcpy( &( MPU->RBAR ), &( table->RBAR ), MPU_TYPE_RALIASES * rowWordSize );
            table += MPU_TYPE_RALIASES;
            cnt -= MPU_TYPE_RALIASES;
        }

        ARM_MPU_OrderedMemcpy( &( MPU->RBAR ), &( table->RBAR ), cnt * rowWordSize );
    }

#endif /* ifndef ARM_MPU_ARMV7_H */
