arch: arc: add support of mpu v6

Add support of ARC mpu v6
* minimal region size down to 32 bytes
* maximal region number up to 32
* not support uncacheable region and volatile uncached region
* clean up mpu code for better readablity

Signed-off-by: Yuguo Zou <yuguo.zou@synopsys.com>
diff --git a/arch/arc/core/mpu/Kconfig b/arch/arc/core/mpu/Kconfig
index 70a9850..38b9c58 100644
--- a/arch/arc/core/mpu/Kconfig
+++ b/arch/arc/core/mpu/Kconfig
@@ -5,11 +5,12 @@
 
 config ARC_MPU_VER
 	int "ARC MPU version"
-	range 2 4
+	range 2 6
 	default 2
 	help
 	  ARC MPU has several versions. For MPU v2, the minimum region is 2048 bytes;
-	  For MPU v3 and v4, the minimum region is 32 bytes
+	  For other versions, the minimum region is 32 bytes; v4 has secure features,
+	  v6 supports up to 32 regions.
 
 config ARC_CORE_MPU
 	bool "ARC Core MPU functionalities"
@@ -31,8 +32,8 @@
 	select SRAM_REGION_PERMISSIONS
 	select ARC_CORE_MPU
 	select THREAD_STACK_INFO
-	select GEN_PRIV_STACKS if ARC_MPU_VER = 2
-	select MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT if ARC_MPU_VER = 2
+	select GEN_PRIV_STACKS if ARC_MPU_VER != 4
+	select MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT if ARC_MPU_VER !=4
 	select MPU_REQUIRES_NON_OVERLAPPING_REGIONS if ARC_MPU_VER = 4
 	help
 	  Target has ARC MPU (currently only works for EMSK 2.2/2.3 ARCEM7D)
diff --git a/arch/arc/core/mpu/arc_mpu.c b/arch/arc/core/mpu/arc_mpu.c
index 9fbe49d..111bc0f 100644
--- a/arch/arc/core/mpu/arc_mpu.c
+++ b/arch/arc/core/mpu/arc_mpu.c
@@ -52,8 +52,8 @@
 	}
 }
 
-#if CONFIG_ARC_MPU_VER == 2
-#include "arc_mpu_v2_internal.h"
-#elif CONFIG_ARC_MPU_VER == 4
+#if CONFIG_ARC_MPU_VER == 4
 #include "arc_mpu_v4_internal.h"
+#else
+#include "arc_mpu_common_internal.h"
 #endif
diff --git a/arch/arc/core/mpu/arc_mpu_common_internal.h b/arch/arc/core/mpu/arc_mpu_common_internal.h
new file mode 100644
index 0000000..e6e2174
--- /dev/null
+++ b/arch/arc/core/mpu/arc_mpu_common_internal.h
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2021 Synopsys.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_
+#define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_
+
+#if CONFIG_ARC_MPU_VER == 2 || CONFIG_ARC_MPU_VER == 3
+#include "arc_mpu_v2_internal.h"
+#elif CONFIG_ARC_MPU_VER == 6
+#include "arc_mpu_v6_internal.h"
+#else
+#error "Unsupported MPU version"
+#endif
+
+/**
+ * @brief configure the base address and size for an MPU region
+ *
+ * @param type MPU region type
+ * @param base base address in RAM
+ * @param size size of the region
+ */
+static inline int _mpu_configure(uint8_t type, uint32_t base, uint32_t size)
+{
+	int32_t region_index =  get_region_index_by_type(type);
+	uint32_t region_attr = get_region_attr_by_type(type);
+
+	LOG_DBG("Region info: 0x%x 0x%x", base, size);
+
+	if (region_attr == 0U || region_index < 0) {
+		return -EINVAL;
+	}
+
+	/*
+	 * For ARC MPU, MPU regions can be overlapped, smaller
+	 * region index has higher priority.
+	 */
+	_region_init(region_index, base, size, region_attr);
+
+	return 0;
+}
+
+/* ARC Core MPU Driver API Implementation for ARC MP */
+
+/**
+ * @brief enable the MPU
+ */
+void arc_core_mpu_enable(void)
+{
+	/* Enable MPU */
+	z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN,
+		z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) | AUX_MPU_EN_ENABLE);
+}
+
+/**
+ * @brief disable the MPU
+ */
+void arc_core_mpu_disable(void)
+{
+    /* Disable MPU */
+	z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN,
+		z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & AUX_MPU_EN_DISABLE);
+}
+
+/**
+ * @brief configure the thread's MPU regions
+ *
+ * @param thread the target thread
+ */
+void arc_core_mpu_configure_thread(struct k_thread *thread)
+{
+#if defined(CONFIG_USERSPACE)
+	/* configure stack region of user thread */
+	if (thread->base.user_options & K_USER) {
+		LOG_DBG("configure user thread %p's stack", thread);
+		if (_mpu_configure(THREAD_STACK_USER_REGION,
+					 (uint32_t)thread->stack_info.start,
+					 thread->stack_info.size) < 0) {
+			LOG_ERR("user thread %p's stack failed", thread);
+			return;
+		}
+	}
+
+	LOG_DBG("configure thread %p's domain", thread);
+	arc_core_mpu_configure_mem_domain(thread);
+#endif
+}
+
+
+/**
+ * @brief configure the default region
+ *
+ * @param region_attr region attribute of default region
+ */
+void arc_core_mpu_default(uint32_t region_attr)
+{
+	uint32_t val =  z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & (~AUX_MPU_RDP_ATTR_MASK);
+
+	region_attr &= AUX_MPU_RDP_ATTR_MASK;
+	z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, region_attr | val);
+}
+
+/**
+ * @brief configure the MPU region
+ *
+ * @param index   MPU region index
+ * @param base    base address
+ * @param region_attr region attribute
+ */
+int arc_core_mpu_region(uint32_t index, uint32_t base, uint32_t size, uint32_t region_attr)
+{
+	if (index >= get_num_regions()) {
+		return -EINVAL;
+	}
+
+	region_attr &= AUX_MPU_RDP_ATTR_MASK;
+
+	_region_init(index, base, size, region_attr);
+
+	return 0;
+}
+
+#if defined(CONFIG_USERSPACE)
+
+/**
+ * @brief configure MPU regions for the memory partitions of the memory domain
+ *
+ * @param thread the thread which has memory domain
+ */
+void arc_core_mpu_configure_mem_domain(struct k_thread *thread)
+{
+	int region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
+	uint32_t num_partitions;
+	struct k_mem_partition *pparts;
+	struct k_mem_domain *mem_domain = NULL;
+
+	if (thread) {
+		mem_domain = thread->mem_domain_info.mem_domain;
+	}
+
+	if (mem_domain) {
+		LOG_DBG("configure domain: %p", mem_domain);
+		num_partitions = mem_domain->num_partitions;
+		pparts = mem_domain->partitions;
+	} else {
+		LOG_DBG("disable domain partition regions");
+		num_partitions = 0U;
+		pparts = NULL;
+	}
+
+	for (; region_index >= 0; region_index--) {
+		if (num_partitions) {
+			LOG_DBG("set region 0x%x 0x%lx 0x%x",
+				 region_index, pparts->start, pparts->size);
+			_region_init(region_index, pparts->start, pparts->size, pparts->attr);
+			num_partitions--;
+		} else {
+			/* clear the left mpu entries */
+			_region_init(region_index, 0, 0, 0);
+		}
+		pparts++;
+	}
+}
+
+/**
+ * @brief remove MPU regions for the memory partitions of the memory domain
+ *
+ * @param mem_domain the target memory domain
+ */
+void arc_core_mpu_remove_mem_domain(struct k_mem_domain *mem_domain)
+{
+	ARG_UNUSED(mem_domain);
+
+	int region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
+
+	for (; region_index >= 0; region_index--) {
+		_region_init(region_index, 0, 0, 0);
+	}
+}
+
+/**
+ * @brief reset MPU region for a single memory partition
+ *
+ * @param domain  the target memory domain
+ * @param partition_id  memory partition id
+ */
+void arc_core_mpu_remove_mem_partition(struct k_mem_domain *domain, uint32_t part_id)
+{
+	ARG_UNUSED(domain);
+
+	int region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
+
+	LOG_DBG("disable region 0x%x", region_index + part_id);
+	/* Disable region */
+	_region_init(region_index + part_id, 0, 0, 0);
+}
+
+/**
+ * @brief get the maximum number of free regions for memory domain partitions
+ */
+int arc_core_mpu_get_max_domain_partition_regions(void)
+{
+	return get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) + 1;
+}
+
+/**
+ * @brief validate the given buffer is user accessible or not
+ */
+int arc_core_mpu_buffer_validate(void *addr, size_t size, int write)
+{
+	/*
+	 * For ARC MPU, smaller region number takes priority.
+	 * we can stop the iteration immediately once we find the
+	 * matched region that grants permission or denies access.
+	 *
+	 */
+	for (int r_index = 0; r_index < get_num_regions(); r_index++) {
+		if (!_is_enabled_region(r_index) || !_is_in_region(r_index, (uint32_t)addr, size)) {
+			continue;
+		}
+
+		if (_is_user_accessible_region(r_index, write)) {
+			return 0;
+		} else {
+			return -EPERM;
+		}
+	}
+
+	return -EPERM;
+}
+#endif /* CONFIG_USERSPACE */
+
+/* ARC MPU Driver Initial Setup */
+/*
+ * @brief MPU default initialization and configuration
+ *
+ * This function provides the default configuration mechanism for the Memory
+ * Protection Unit (MPU).
+ */
+static int arc_mpu_init(const struct device *arg)
+{
+	ARG_UNUSED(arg);
+
+	uint32_t num_regions = get_num_regions();
+
+	if (mpu_config.num_regions > num_regions) {
+		__ASSERT(0, "Request to configure: %u regions (supported: %u)\n",
+			 mpu_config.num_regions, num_regions);
+		return -EINVAL;
+	}
+
+	/* Disable MPU */
+	arc_core_mpu_disable();
+
+	/*
+	 * the MPU regions are filled in the reverse order.
+	 * According to ARCv2 ISA, the MPU region with smaller
+	 * index has higher priority. The static background MPU
+	 * regions in mpu_config will be in the bottom. Then
+	 * the special type regions will be above.
+	 */
+	int r_index = num_regions - mpu_config.num_regions;
+
+	/* clear all the regions first */
+	for (uint32_t i = 0U; i < r_index; i++) {
+		_region_init(i, 0, 0, 0);
+	}
+
+	/* configure the static regions */
+	for (uint32_t i = 0U; i < mpu_config.num_regions; i++) {
+		_region_init(r_index, mpu_config.mpu_regions[i].base,
+			 mpu_config.mpu_regions[i].size, mpu_config.mpu_regions[i].attr);
+		r_index++;
+	}
+
+	/* default region: no read, write and execute */
+	arc_core_mpu_default(0);
+
+	/* Enable MPU */
+	arc_core_mpu_enable();
+
+	return 0;
+}
+
+SYS_INIT(arc_mpu_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
+
+#endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_COMMON_INTERNAL_H_ */
diff --git a/arch/arc/core/mpu/arc_mpu_v2_internal.h b/arch/arc/core/mpu/arc_mpu_v2_internal.h
index 12b2c64..f59b1e9 100644
--- a/arch/arc/core/mpu/arc_mpu_v2_internal.h
+++ b/arch/arc/core/mpu/arc_mpu_v2_internal.h
@@ -6,15 +6,36 @@
 #ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V2_INTERNAL_H_
 #define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V2_INTERNAL_H_
 
-#define AUX_MPU_RDB_VALID_MASK (0x1)
-#define AUX_MPU_EN_ENABLE   (0x40000000)
-#define AUX_MPU_EN_DISABLE  (0xBFFFFFFF)
+#define AUX_MPU_EN_ENABLE   BIT(30)
+#define AUX_MPU_EN_DISABLE  ~BIT(30)
 
-#define AUX_MPU_RDP_REGION_SIZE(bits) \
-	(((bits - 1) & 0x3) | (((bits - 1) & 0x1C) << 7))
+/*
+ * The size of the region is a 5-bit field, the three MSB bits are
+ * represented in [11:9] and the two LSB bits are represented in [1:0].
+ * Together these fields specify the size of the region in bytes:
+ * 00000-00011	Reserved
+ * 0x4  32		0x5  64		0x6  128	0x7 256
+ * 0x8  512		0x9  1k		0xA  2K		0xB 4K
+ * 0xC  8K		0xD  16K	0xE  32K	0xF 64K
+ * 0x10 128K	0x11 256K	0x12 512K	0x13 1M
+ * 0x14 2M		0x15 4M		0x16 8M		0x17 16M
+ * 0x18 32M		0x19 64M	0x1A 128M	0x1B 256M
+ * 0x1C 512M	0x1D 1G		0x1E 2G		0x1F 4G
+ *
+ * Bit ... 12 11   10    9 8    3  2  1         0
+ *     ------+------------+------+---+-----------+
+ *     ...   | SIZE[11:9] | ATTR | R | SIZE[1:0] |
+ *     ------+------------+------+---+-----------+
+ */
+/* arrange size into proper bit field in RDP aux reg*/
+#define AUX_MPU_RDP_REGION_SIZE(size)  (((size - 1) & BIT_MASK(2)) | \
+					(((size - 1) & (BIT_MASK(3) << 2)) << 7))
+/* recover size from bit fields in RDP aux reg*/
+#define AUX_MPU_RDP_SIZE_SHIFT(rdp)     ((rdp & BIT_MASK(2)) | (((rdp >> 9) & BIT_MASK(3)) << 2))
 
-#define AUX_MPU_RDP_ATTR_MASK (0x1FC)
-#define AUX_MPU_RDP_SIZE_MASK (0xE03)
+#define AUX_MPU_RDB_VALID_MASK BIT(0)
+#define AUX_MPU_RDP_ATTR_MASK  (BIT_MASK(6) << 3)
+#define AUX_MPU_RDP_SIZE_MASK  ((BIT_MASK(3) << 9) | BIT_MASK(2))
 
 /* For MPU version 2, the minimum protection region size is 2048 bytes */
 #if CONFIG_ARC_MPU_VER == 2
@@ -39,7 +60,7 @@
 			bits = ARC_FEATURE_MPU_ALIGNMENT_BITS;
 		}
 
-		if ((1 << bits) < size) {
+		if (BIT(bits) < size) {
 			bits++;
 		}
 
@@ -72,8 +93,7 @@
 	 */
 	switch (type) {
 	case THREAD_STACK_USER_REGION:
-		return get_num_regions() - mpu_config.num_regions
-		       - THREAD_STACK_REGION;
+		return get_num_regions() - mpu_config.num_regions - THREAD_STACK_REGION;
 	case THREAD_STACK_REGION:
 	case THREAD_APP_DATA_REGION:
 	case THREAD_DOMAIN_PARTITION_REGION:
@@ -110,14 +130,14 @@
 		       & (~AUX_MPU_RDB_VALID_MASK);
 	r_size_lshift = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + r_index * 2U)
 			& AUX_MPU_RDP_SIZE_MASK;
-	r_size_lshift = (r_size_lshift & 0x3) | ((r_size_lshift >> 7) & 0x1C);
+	r_size_lshift = AUX_MPU_RDP_SIZE_SHIFT(r_size_lshift);
 	r_addr_end = r_addr_start  + (1 << (r_size_lshift + 1));
 
 	if (start >= r_addr_start && (start + size) <= r_addr_end) {
-		return 1;
+		return true;
 	}
 
-	return 0;
+	return false;
 }
 
 /**
@@ -140,296 +160,4 @@
 		(AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR));
 }
 
-/**
- * @brief configure the base address and size for an MPU region
- *
- * @param type MPU region type
- * @param base base address in RAM
- * @param size size of the region
- */
-static inline int _mpu_configure(uint8_t type, uint32_t base, uint32_t size)
-{
-	int32_t region_index =  get_region_index_by_type(type);
-	uint32_t region_attr = get_region_attr_by_type(type);
-
-	LOG_DBG("Region info: 0x%x 0x%x", base, size);
-
-	if (region_attr == 0U || region_index < 0) {
-		return -EINVAL;
-	}
-
-	/*
-	 * For ARC MPU v2, MPU regions can be overlapped, smaller
-	 * region index has higher priority.
-	 */
-	_region_init(region_index, base, size, region_attr);
-
-	return 0;
-}
-
-/* ARC Core MPU Driver API Implementation for ARC MPUv2 */
-
-/**
- * @brief enable the MPU
- */
-void arc_core_mpu_enable(void)
-{
-	/* Enable MPU */
-	z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN,
-		z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) | AUX_MPU_EN_ENABLE);
-}
-
-/**
- * @brief disable the MPU
- */
-void arc_core_mpu_disable(void)
-{
-	/* Disable MPU */
-	z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN,
-		z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & AUX_MPU_EN_DISABLE);
-}
-
-/**
- * @brief configure the thread's MPU regions
- *
- * @param thread the target thread
- */
-void arc_core_mpu_configure_thread(struct k_thread *thread)
-{
-#if defined(CONFIG_USERSPACE)
-	/* configure stack region of user thread */
-	if (thread->base.user_options & K_USER) {
-		LOG_DBG("configure user thread %p's stack", thread);
-		if (_mpu_configure(THREAD_STACK_USER_REGION,
-				   (uint32_t)thread->stack_info.start,
-				   thread->stack_info.size) < 0) {
-			LOG_ERR("user thread %p's stack failed", thread);
-			return;
-		}
-	}
-
-	LOG_DBG("configure thread %p's domain", thread);
-	arc_core_mpu_configure_mem_domain(thread);
-#endif
-}
-
-
-/**
- * @brief configure the default region
- *
- * @param region_attr region attribute of default region
- */
-void arc_core_mpu_default(uint32_t region_attr)
-{
-	uint32_t val =  z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) &
-		    (~AUX_MPU_RDP_ATTR_MASK);
-
-	region_attr &= AUX_MPU_RDP_ATTR_MASK;
-
-	z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, region_attr | val);
-}
-
-/**
- * @brief configure the MPU region
- *
- * @param index   MPU region index
- * @param base    base address
- * @param region_attr region attribute
- */
-int arc_core_mpu_region(uint32_t index, uint32_t base, uint32_t size,
-			 uint32_t region_attr)
-{
-	if (index >= get_num_regions()) {
-		return -EINVAL;
-	}
-
-	region_attr &= AUX_MPU_RDP_ATTR_MASK;
-
-	_region_init(index, base, size, region_attr);
-
-	return 0;
-}
-
-#if defined(CONFIG_USERSPACE)
-
-/**
- * @brief configure MPU regions for the memory partitions of the memory domain
- *
- * @param thread the thread which has memory domain
- */
-void arc_core_mpu_configure_mem_domain(struct k_thread *thread)
-{
-	int region_index =
-		get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
-	uint32_t num_partitions;
-	struct k_mem_partition *pparts;
-	struct k_mem_domain *mem_domain = NULL;
-
-	if (thread) {
-		mem_domain = thread->mem_domain_info.mem_domain;
-	}
-
-	if (mem_domain) {
-		LOG_DBG("configure domain: %p", mem_domain);
-		num_partitions = mem_domain->num_partitions;
-		pparts = mem_domain->partitions;
-	} else {
-		LOG_DBG("disable domain partition regions");
-		num_partitions = 0U;
-		pparts = NULL;
-	}
-
-	for (; region_index >= 0; region_index--) {
-		if (num_partitions) {
-			LOG_DBG("set region 0x%x 0x%lx 0x%x",
-				region_index, pparts->start, pparts->size);
-			_region_init(region_index, pparts->start,
-			 pparts->size, pparts->attr);
-			num_partitions--;
-		} else {
-			/* clear the left mpu entries */
-			_region_init(region_index, 0, 0, 0);
-		}
-		pparts++;
-	}
-}
-
-/**
- * @brief remove MPU regions for the memory partitions of the memory domain
- *
- * @param mem_domain the target memory domain
- */
-void arc_core_mpu_remove_mem_domain(struct k_mem_domain *mem_domain)
-{
-	ARG_UNUSED(mem_domain);
-
-	int region_index =
-		get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
-
-	for (; region_index >= 0; region_index--) {
-		_region_init(region_index, 0, 0, 0);
-	}
-}
-
-/**
- * @brief reset MPU region for a single memory partition
- *
- * @param domain  the target memory domain
- * @param partition_id  memory partition id
- */
-void arc_core_mpu_remove_mem_partition(struct k_mem_domain *domain,
-			uint32_t part_id)
-{
-	ARG_UNUSED(domain);
-
-	int region_index =
-		get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);
-
-	LOG_DBG("disable region 0x%x", region_index + part_id);
-	/* Disable region */
-	_region_init(region_index + part_id, 0, 0, 0);
-}
-
-/**
- * @brief get the maximum number of free regions for memory domain partitions
- */
-int arc_core_mpu_get_max_domain_partition_regions(void)
-{
-	return get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) + 1;
-}
-
-/**
- * @brief validate the given buffer is user accessible or not
- */
-int arc_core_mpu_buffer_validate(void *addr, size_t size, int write)
-{
-	int r_index;
-
-	/*
-	 * For ARC MPU v2, smaller region number takes priority.
-	 * we can stop the iteration immediately once we find the
-	 * matched region that grants permission or denies access.
-	 *
-	 */
-	for (r_index = 0; r_index < get_num_regions(); r_index++) {
-		if (!_is_enabled_region(r_index) ||
-		    !_is_in_region(r_index, (uint32_t)addr, size)) {
-			continue;
-		}
-
-		if (_is_user_accessible_region(r_index, write)) {
-			return 0;
-		} else {
-			return -EPERM;
-		}
-	}
-
-	return -EPERM;
-}
-#endif /* CONFIG_USERSPACE */
-
-/* ARC MPU Driver Initial Setup */
-/*
- * @brief MPU default initialization and configuration
- *
- * This function provides the default configuration mechanism for the Memory
- * Protection Unit (MPU).
- */
-static int arc_mpu_init(const struct device *arg)
-{
-	ARG_UNUSED(arg);
-
-	uint32_t num_regions;
-	uint32_t i;
-
-	num_regions = get_num_regions();
-
-	/* ARC MPU supports up to 16 Regions */
-	if (mpu_config.num_regions > num_regions) {
-		__ASSERT(0,
-		"Request to configure: %u regions (supported: %u)\n",
-		mpu_config.num_regions, num_regions);
-		return -EINVAL;
-	}
-
-	/* Disable MPU */
-	arc_core_mpu_disable();
-
-	int r_index;
-	/*
-	 * the MPU regions are filled in the reverse order.
-	 * According to ARCv2 ISA, the MPU region with smaller
-	 * index has higher priority. The static background MPU
-	 * regions in mpu_config will be in the bottom. Then
-	 * the special type regions will be above.
-	 *
-	 */
-	r_index = num_regions - mpu_config.num_regions;
-
-	/* clear all the regions first */
-	for (i = 0U; i < r_index; i++) {
-		_region_init(i, 0, 0, 0);
-	}
-
-	/* configure the static regions */
-	for (i = 0U; i < mpu_config.num_regions; i++) {
-		_region_init(r_index,
-			     mpu_config.mpu_regions[i].base,
-			     mpu_config.mpu_regions[i].size,
-			     mpu_config.mpu_regions[i].attr);
-		r_index++;
-	}
-
-	/* default region: no read, write and execute */
-	arc_core_mpu_default(0);
-
-	/* Enable MPU */
-	arc_core_mpu_enable();
-
-	return 0;
-}
-
-SYS_INIT(arc_mpu_init, PRE_KERNEL_1,
-	 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
-
 #endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V2_INTERNAL_H_ */
diff --git a/arch/arc/core/mpu/arc_mpu_v6_internal.h b/arch/arc/core/mpu/arc_mpu_v6_internal.h
new file mode 100644
index 0000000..1dbd50b
--- /dev/null
+++ b/arch/arc/core/mpu/arc_mpu_v6_internal.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2021 Synopsys.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V6_INTERNAL_H_
+#define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V6_INTERNAL_H_
+
+#define AUX_MPU_EN_BANK_MASK BIT(0)
+#define AUX_MPU_EN_IC		BIT(12)
+#define AUX_MPU_EN_DC		BIT(13)
+#define AUX_MPU_EN_ENABLE   BIT(30)
+#define AUX_MPU_EN_DISABLE  ~BIT(30)
+
+/*
+ * The size of the region is a 5-bit field, the three MSB bits are
+ * represented in [11:9] and the two LSB bits are represented in [1:0].
+ * Together these fields specify the size of the region in bytes:
+ * 00000-00011	Reserved
+ * 0x4  32		0x5  64		0x6  128	0x7 256
+ * 0x8  512		0x9  1k		0xA  2K		0xB 4K
+ * 0xC  8K		0xD  16K	0xE  32K	0xF 64K
+ * 0x10 128K	0x11 256K	0x12 512K	0x13 1M
+ * 0x14 2M		0x15 4M		0x16 8M		0x17 16M
+ * 0x18 32M		0x19 64M	0x1A 128M	0x1B 256M
+ * 0x1C 512M	0x1D 1G		0x1E 2G		0x1F 4G
+ *
+ * Bit ... 12 11   10    9 8    3  2  1         0
+ *     ------+------------+------+---+-----------+
+ *     ...   | SIZE[11:9] | ATTR | R | SIZE[1:0] |
+ *     ------+------------+------+---+-----------+
+ */
+/* arrange size into proper bit field in RDP aux reg*/
+#define AUX_MPU_RDP_REGION_SIZE(size)  (((size - 1) & BIT_MASK(2)) | \
+					(((size - 1) & (BIT_MASK(3) << 2)) << 7))
+/* recover size from bit fields in RDP aux reg*/
+#define AUX_MPU_RDP_SIZE_SHIFT(rdp)     ((rdp & BIT_MASK(2)) | (((rdp >> 9) & BIT_MASK(3)) << 2))
+
+#define AUX_MPU_RDB_VALID_MASK BIT(0)
+#define AUX_MPU_RDP_ATTR_MASK  (BIT_MASK(6) << 3)
+#define AUX_MPU_RDP_SIZE_MASK  ((BIT_MASK(3) << 9) | BIT_MASK(2))
+/* Global code cacheability that applies to a region
+ * 0x0: (Default) Code is cacheable in all levels of the cache hierarchy
+ * 0x1: Code is not cacheable in any level of the cache hierarchy
+ */
+#define AUX_MPU_RDB_IC		BIT(12)
+/* Global data cacheability that applies to a region
+ * 0x0: (Default) Data is cacheable in all levels of the cache hierarchy
+ * 0x1: Data is not cacheable in any level of the cache hierarchy
+ */
+#define AUX_MPU_RDB_DC		BIT(13)
+/* Define a MPU region as non-volatile
+ * 0x0: (Default) The memory space for this MPU region is treated as a volatile uncached space.
+ * 0x1: The memory space for this MPU region is non-volatile
+ */
+#define AUX_MPU_RDB_NV		BIT(14)
+
+/* For MPU version 6, the minimum protection region size is 32 bytes */
+#define ARC_FEATURE_MPU_ALIGNMENT_BITS 5
+#define ARC_FEATURE_MPU_BANK_SIZE      16
+
+/**
+ * This internal function select a MPU bank
+ */
+static inline void _bank_select(uint32_t bank)
+{
+	uint32_t val;
+
+	val = z_arc_v2_aux_reg_read(_ARC_V2_MPU_EN) & (~AUX_MPU_EN_BANK_MASK);
+	z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, val | bank);
+}
+/**
+ * This internal function initializes a MPU region
+ */
+static inline void _region_init(uint32_t index, uint32_t region_addr,
+				uint32_t size, uint32_t region_attr)
+{
+	uint32_t bank = index / ARC_FEATURE_MPU_BANK_SIZE;
+
+	index = (index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
+
+	if (size > 0) {
+		uint8_t bits = find_msb_set(size) - 1;
+
+		if (bits < ARC_FEATURE_MPU_ALIGNMENT_BITS) {
+			bits = ARC_FEATURE_MPU_ALIGNMENT_BITS;
+		}
+
+		if (BIT(bits) < size) {
+			bits++;
+		}
+
+		/* Clear size bits and IC, DC bits, and set NV bit
+		 * The default value of NV bit is 0 which means the region is volatile and uncached.
+		 * Setting the NV bit here has no effect on mpu v6 but is for the
+		 * forward compatibility to mpu v7. Currently we do not allow to toggle these bits
+		 * until we implement the control of these region properties
+		 * TODO: support uncacheable regions and volatile uncached regions
+		 */
+		region_attr &= ~(AUX_MPU_RDP_SIZE_MASK | AUX_MPU_RDB_IC | AUX_MPU_RDB_DC);
+		region_attr |= AUX_MPU_RDP_REGION_SIZE(bits) | AUX_MPU_RDB_NV;
+		region_addr |= AUX_MPU_RDB_VALID_MASK;
+	} else {
+		region_addr = 0U;
+	}
+
+	_bank_select(bank);
+	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RDP0 + index, region_attr);
+	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RDB0 + index, region_addr);
+}
+
+/**
+ * This internal function is utilized by the MPU driver to parse the intent
+ * type (i.e. THREAD_STACK_REGION) and return the correct region index.
+ */
+static inline int get_region_index_by_type(uint32_t type)
+{
+	/*
+	 * The new MPU regions are allocated per type after the statically
+	 * configured regions. The type is one-indexed rather than
+	 * zero-indexed.
+	 *
+	 * For ARC MPU v6, the smaller index has higher priority, so the
+	 * index is allocated in reverse order. Static regions start from
+	 * the biggest index, then thread related regions.
+	 *
+	 */
+	switch (type) {
+	case THREAD_STACK_USER_REGION:
+		return get_num_regions() - mpu_config.num_regions - THREAD_STACK_REGION;
+	case THREAD_STACK_REGION:
+	case THREAD_APP_DATA_REGION:
+	case THREAD_DOMAIN_PARTITION_REGION:
+		/*
+		 * Start domain partition region from stack guard region
+		 * since stack guard is not supported.
+		 */
+		return get_num_regions() - mpu_config.num_regions - type + 1;
+	default:
+		__ASSERT(0, "Unsupported type");
+		return -EINVAL;
+	}
+}
+
+/**
+ * This internal function checks if region is enabled or not
+ */
+static inline bool _is_enabled_region(uint32_t r_index)
+{
+	uint32_t bank = r_index / ARC_FEATURE_MPU_BANK_SIZE;
+	uint32_t index = (r_index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
+
+	_bank_select(bank);
+	return ((z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + index)
+		 & AUX_MPU_RDB_VALID_MASK) == AUX_MPU_RDB_VALID_MASK);
+}
+
+/**
+ * This internal function check if the given buffer in in the region
+ */
+static inline bool _is_in_region(uint32_t r_index, uint32_t start, uint32_t size)
+{
+	uint32_t r_addr_start;
+	uint32_t r_addr_end;
+	uint32_t r_size_lshift;
+	uint32_t bank = r_index / ARC_FEATURE_MPU_BANK_SIZE;
+	uint32_t index = (r_index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
+
+	_bank_select(bank);
+	r_addr_start = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + index) & (~AUX_MPU_RDB_VALID_MASK);
+	r_size_lshift = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + index) & AUX_MPU_RDP_SIZE_MASK;
+	r_size_lshift = AUX_MPU_RDP_SIZE_SHIFT(r_size_lshift);
+	r_addr_end = r_addr_start  + (1 << (r_size_lshift + 1));
+
+	if (start >= r_addr_start && (start + size) <= r_addr_end) {
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * This internal function check if the region is user accessible or not
+ */
+static inline bool _is_user_accessible_region(uint32_t r_index, int write)
+{
+	uint32_t r_ap;
+	uint32_t bank = r_index / ARC_FEATURE_MPU_BANK_SIZE;
+	uint32_t index = (r_index % ARC_FEATURE_MPU_BANK_SIZE) * 2U;
+
+	_bank_select(bank);
+	r_ap = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + index);
+
+	r_ap &= AUX_MPU_RDP_ATTR_MASK;
+
+	if (write) {
+		return ((r_ap & (AUX_MPU_ATTR_UW | AUX_MPU_ATTR_KW)) ==
+			(AUX_MPU_ATTR_UW | AUX_MPU_ATTR_KW));
+	}
+
+	return ((r_ap & (AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR)) ==
+		(AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR));
+}
+
+#endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V6_INTERNAL_H_ */
diff --git a/include/arch/arc/arch.h b/include/arch/arc/arch.h
index f576513..a7441f8 100644
--- a/include/arch/arc/arch.h
+++ b/include/arch/arc/arch.h
@@ -66,7 +66,7 @@
 #ifdef CONFIG_ARC_CORE_MPU
 #if CONFIG_ARC_MPU_VER == 2
 #define Z_ARC_MPU_ALIGN	2048
-#elif (CONFIG_ARC_MPU_VER == 3) || (CONFIG_ARC_MPU_VER == 4)
+#elif (CONFIG_ARC_MPU_VER == 3) || (CONFIG_ARC_MPU_VER == 4) || (CONFIG_ARC_MPU_VER == 6)
 #define Z_ARC_MPU_ALIGN	32
 #else
 #error "Unsupported MPU version"
diff --git a/include/arch/arc/v2/linker.ld b/include/arch/arc/v2/linker.ld
index 13245c7..89f3c96 100644
--- a/include/arch/arc/v2/linker.ld
+++ b/include/arch/arc/v2/linker.ld
@@ -30,7 +30,7 @@
 #ifdef CONFIG_ARC_MPU_ENABLE
 	#if CONFIG_ARC_MPU_VER == 2
 		#define MPU_MIN_SIZE 2048
-	#elif (CONFIG_ARC_MPU_VER == 3) || (CONFIG_ARC_MPU_VER == 4)
+	#elif (CONFIG_ARC_MPU_VER == 3) ||  (CONFIG_ARC_MPU_VER == 4) || (CONFIG_ARC_MPU_VER == 6)
 		#define MPU_MIN_SIZE 32
 	#endif
 	#define MPU_MIN_SIZE_ALIGN . = ALIGN(MPU_MIN_SIZE);