Files
6CAR/arch/armv7-r/arm_mpu.c
2026-04-18 09:16:58 +08:00

248 lines
6.3 KiB
C

/*
* mpu.c
*
* Copyright (c) 2019 Semidrive Semiconductor.
* All rights reserved.
*
* Description: Cortex V7R MPU driver.
*
* Revision History:
* -----------------
*/
#if CONFIG_ARCH_WITH_MPU
#include <armv7-r/arm.h>
#include <armv7-r/mpu.h>
#include <armv7-r/register.h>
#include <math.h>
#include <bits.h>
#include <debug.h>
/* RBAR bits [31:5] */
#define MPU_RBAR_MASK (0xffffffe0)
/* RACR bits */
#define MPU_RACR_B_SHIFT (0) /* Bufferable */
#define MPU_RACR_C_SHIFT (1) /* Cacheable */
#define MPU_RACR_S_SHIFT (2) /* Sharable. For normal memory only. */
#define MPU_RACR_TEX_SHIFT (3) /* Type extension */
#define MPU_RACR_AP_SHIFT (8) /* Access permission */
#define MPU_RACR_XN_SHIFT (12) /* Execution Never */
/* RACR Access permissions */
#define RACR_AP_NO_NO (0) /* Privileged NO, Unprivileged NO */
#define RACR_AP_RW_NO (1) /* Privileged RW, Unprivileged NO */
#define RACR_AP_RW_RO (2) /* Privileged RW, Unprivileged RO */
#define RACR_AP_RW_RW (3) /* Privileged RW, Unprivileged RW */
#define RACR_AP_RO_NO (5) /* Privileged RO, Unprivileged NO */
#define RACR_AP_RO_RO (6) /* Privileged RO, Unprivileged RO */
#define MPU_RACR_CONFIG(TEX, C, B, S, AP, XN) \
((B << MPU_RACR_B_SHIFT) | \
(C << MPU_RACR_C_SHIFT) | \
(S << MPU_RACR_S_SHIFT) | \
(TEX << MPU_RACR_TEX_SHIFT) | \
(AP << MPU_RACR_AP_SHIFT) | \
(XN << MPU_RACR_XN_SHIFT))
/* Region Size and Enable Register */
#define RSR_EN (1 << 0) /* Enable region */
#define RSR_SIZE_SHIFT (1) /* Region Size = log2(size) - 1 */
static const uint32_t g_racr_config[] = {
[MPU_REGION_STRONGORDERED] = MPU_RACR_CONFIG(
0, 0, 0, /* See "TEX[2:0], C and B encodings" */
0, /* S bit is not used for strong ordered. */
RACR_AP_RW_RW,
1 /* XN */
),
[MPU_REGION_DEVICE] = MPU_RACR_CONFIG(
0, 0, 1,
0, /* S bit is not used for device memory */
RACR_AP_RW_RW,
1 /* XN */
),
[MPU_REGION_NORMAL] = MPU_RACR_CONFIG(
0, 1, 1, /* cacheable, WB, no WA */
0, /* Not shared. R5 L1 cache does not cache shared
normal regions */
RACR_AP_RW_RW,
0 /* non XN */
),
[MPU_REGION_NORMAL_NONCACHEABLE] = MPU_RACR_CONFIG(
1, 0, 0, /* non-cacheable */
0,
RACR_AP_RW_RW,
0 /* non XN */
),
[MPU_REGION_NORMAL_RO] = MPU_RACR_CONFIG(
0, 1, 1, /* cacheable, WB, no WA */
0, /* Not shared. R5 L1 cache does not cache shared
normal regions */
RACR_AP_RO_RO,
0 /* XN */
),
[MPU_REGION_NO_ACCESS] = MPU_RACR_CONFIG(
0,
0,
0,
0,
RACR_AP_NO_NO,
0
),
};
uint32_t all_region_index = 0;
static int mpu_region_nr(void)
{
uint32_t val = arm_read_mpuir();
return (int)BITS_SHIFT(val, 15, 8); /* 0, 12 or 16 */
}
/*
* Clear MPU region.
*/
void mpu_clear_region(void)
{
int region_nr = mpu_region_nr();
for (int cnt = 0; cnt < region_nr; cnt++) {
arm_write_rgnr(cnt);
arm_write_rsr(0);
}
}
/*
* Add MPU region.
*
* @region Region index from 0 to mpu_region_nr - 1.
* @base Base address of the region. Must align to region size.
* @size Region size. Must be power of 2, from 32 bytes to 4GB.
* @type Region type.
*/
#if CONFIG_ARMV7R_MPU_CACHE_ALIGN_CHECK
struct region_info {
uint32_t base;
uint32_t end;
//mpu_region_type type;
};
struct region_info region_array[16] = {0};
int region_index = 0;
static void add_region(uint32_t base, uint32_t size)
{
ASSERT(region_index >= 0 && region_index < 16);
region_array[region_index].base = base;
region_array[region_index].end = base + size;
region_index++;
}
bool mpu_is_belong_uncache_region(uint32_t base, uint32_t size)
{
int i;
for (i = 0; i < region_index; i++) {
uint32_t end = base + size;
if (base >= region_array[i].base && end <= region_array[i].end) {
return true;
}
}
return false;
}
#endif
void mpu_add_region(int region, uint32_t base, uint64_t size,
mpu_region_type_e type)
{
ASSERT(region >= 0 && region < mpu_region_nr());
ASSERT((size & (size - 1)) == 0 &&
size >= 32ull &&
size <= 4ull * 1024 * 1024 * 1024);
ASSERT(base % size == 0);
ASSERT(type < MPU_REGION_MAX);
/* Calculate size field of RSR register.
* pow(2, sz + 1) = size
*/
uint32_t sz = (uint32_t)log2(size) - 1;
arm_write_rgnr(region);
arm_write_rbar(base & MPU_RBAR_MASK);
arm_write_racr(g_racr_config[type]);
/* TODO - support sub regions? */
arm_write_rsr((sz << RSR_SIZE_SHIFT) | RSR_EN);
#if CONFIG_ARMV7R_MPU_CACHE_ALIGN_CHECK
//add region in nocache list
if (type != MPU_REGION_NORMAL) {
add_region(base, size);
}
#endif
all_region_index++;
}
uint32_t mpu_region_index(void)
{
return all_region_index;
}
/*
* Enable or disable the MPU.
*
* MPU regions must have been configured before enabling the MPU.
*/
void mpu_enable(bool enable)
{
uint32_t val = arm_read_sctlr();
if (enable)
/* Enable MPU. The default background region is always
* enabled as well.
*/
{
val |= SCTLR_BR | SCTLR_M;
}
else {
val &= ~SCTLR_M;
}
arm_write_sctlr(val);
}
void mpu_region_set_type(uint32_t base, uint64_t size,
mpu_region_type_e type)
{
ASSERT((size & (size - 1)) == 0 &&
size >= 32ull &&
size <= 4ull * 1024 * 1024 * 1024);
ASSERT(base % size == 0);
ASSERT(type < MPU_REGION_MAX);
/* Calculate size field of RSR register.
* pow(2, sz + 1) = size
*/
uint32_t sz = (uint32_t)log2(size) - 1;
int region_nr = mpu_region_nr();
for (int cnt = 0; cnt < region_nr; cnt++) {
uint32_t rbar, rsr;
arm_write_rgnr(cnt);
rbar = arm_read_rbar();
rsr = arm_read_rsr();
if (((rbar & MPU_RBAR_MASK) == base)
&& (rsr >> RSR_SIZE_SHIFT) == sz) {
arm_write_rsr(rsr & ~RSR_EN);
arm_write_racr(g_racr_config[type]);
arm_write_rsr(rsr);
}
}
}
#endif