| /* |
| * Copyright (c) 2021 Andes Technology Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/init.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/arch/cpu.h> |
| #include <zephyr/linker/linker-defs.h> |
| |
| /* Programmable PMA mechanism is supported */ |
| #define MMSC_CFG_PPMA (1 << 30) |
| |
| /* |
| * PMA Configuration (PMACFG) bitfields |
| */ |
| |
| /* ETYPE: Entry address matching mode */ |
| #define PMACFG_ETYPE_MASK 3 |
| #define PMACFG_ETYPE_OFF 0 |
| #define PMACFG_ETYPE_TOR 1 |
| #define PMACFG_ETYPE_NA4 2 |
| #define PMACFG_ETYPE_NAPOT 3 |
| |
| /* MTYPE: Memory type attribute */ |
| #define PMACFG_MTYPE_MASK (0xF << 2) |
| /* non-cacheable attributes (bufferable or not) */ |
| #define PMACFG_MTYPE_MEMORY_NOCACHE_BUFFERABLE (3 << 2) |
| /* cacheable attributes (write-through/back, no/read/write/RW-allocate) */ |
| #define PMACFG_MTYPE_MEMORY_WBCACHE_RWALLOC (11 << 2) |
| |
| /* pmaaddr is 4-byte granularity in each mode */ |
| #define TO_PMA_ADDR(addr) ((addr) >> 2) |
| |
| /* The base address is aligned to size */ |
| #define NAPOT_BASE(start, size) TO_PMA_ADDR((start) & ~((size) - 1)) |
| /* The encoding of size is 0b01...1 |
| * (change the leading bit of bitmask to 0) |
| */ |
| #define NAPOT_SIZE(size) TO_PMA_ADDR(((size) - 1) >> 1) |
| |
| #define NA4_ENCODING(start) TO_PMA_ADDR(start) |
| #define NAPOT_ENCODING(start, size) (NAPOT_BASE(start, size) \ |
| | NAPOT_SIZE(size)) |
| |
| #ifdef CONFIG_64BIT |
| /* In riscv64, CSR pmacfg number are even number (0, 2, ...) */ |
| # define PMACFG_NUM(index) ((index / RV_REGSIZE) * 2) |
| #else |
| # define PMACFG_NUM(index) (index / RV_REGSIZE) |
| #endif |
| #define PMACFG_SHIFT(index) ((index % RV_REGSIZE) * 8) |
| |
| /* Wrappers of inline assembly */ |
| #define read_csr(var, csr) \ |
| __asm__ volatile ("csrr %0, %1" : "=r" (var) : "i" (csr)) |
| #define write_csr(csr, val) \ |
| __asm__ volatile ("csrw %0, %1" :: "i" (csr), "r" (val)) |
| |
| struct pma_region_attr { |
| /* Attributes belonging to pmacfg{i} */ |
| uint8_t pmacfg; |
| }; |
| |
| struct pma_region { |
| ulong_t start; |
| ulong_t size; |
| struct pma_region_attr attr; |
| }; |
| |
| /* |
| * Write value to CSRs pmaaddr{i} |
| */ |
| static void write_pmaaddr_csr(const uint32_t index, ulong_t value) |
| { |
| switch (index) { |
| case 0: |
| write_csr(NDS_PMAADDR0, value); break; |
| case 1: |
| write_csr(NDS_PMAADDR1, value); break; |
| case 2: |
| write_csr(NDS_PMAADDR2, value); break; |
| case 3: |
| write_csr(NDS_PMAADDR3, value); break; |
| case 4: |
| write_csr(NDS_PMAADDR4, value); break; |
| case 5: |
| write_csr(NDS_PMAADDR5, value); break; |
| case 6: |
| write_csr(NDS_PMAADDR6, value); break; |
| case 7: |
| write_csr(NDS_PMAADDR7, value); break; |
| case 8: |
| write_csr(NDS_PMAADDR8, value); break; |
| case 9: |
| write_csr(NDS_PMAADDR9, value); break; |
| case 10: |
| write_csr(NDS_PMAADDR10, value); break; |
| case 11: |
| write_csr(NDS_PMAADDR11, value); break; |
| case 12: |
| write_csr(NDS_PMAADDR12, value); break; |
| case 13: |
| write_csr(NDS_PMAADDR13, value); break; |
| case 14: |
| write_csr(NDS_PMAADDR14, value); break; |
| case 15: |
| write_csr(NDS_PMAADDR15, value); break; |
| } |
| } |
| |
| /* |
| * Write value to pma{i}cfg entry which are packed into CSRs pmacfg{j} |
| */ |
| static void write_pmacfg_entry(const uint32_t entry_index, |
| uint8_t entry_value) |
| { |
| /* 1-byte pma{i}cfg entries are packed into XLEN-byte CSRs pmacfg{j} */ |
| uint32_t index = PMACFG_NUM(entry_index); |
| uint8_t shift = PMACFG_SHIFT(entry_index); |
| ulong_t pmacfg = 0; |
| |
| switch (index) { |
| case 0: |
| read_csr(pmacfg, NDS_PMACFG0); break; |
| case 1: |
| read_csr(pmacfg, NDS_PMACFG1); break; |
| case 2: |
| read_csr(pmacfg, NDS_PMACFG2); break; |
| case 3: |
| read_csr(pmacfg, NDS_PMACFG3); break; |
| } |
| |
| /* clear old value in pmacfg entry */ |
| pmacfg &= ~(0xFF << shift); |
| /* set new value to pmacfg entry value */ |
| pmacfg |= entry_value << shift; |
| |
| switch (index) { |
| case 0: |
| write_csr(NDS_PMACFG0, pmacfg); break; |
| case 1: |
| write_csr(NDS_PMACFG1, pmacfg); break; |
| case 2: |
| write_csr(NDS_PMACFG2, pmacfg); break; |
| case 3: |
| write_csr(NDS_PMACFG3, pmacfg); break; |
| } |
| } |
| |
| /* |
| * This internal function performs PMA region initialization. |
| * |
| * Note: |
| * The caller must provide a valid region index. |
| */ |
| static void region_init(const uint32_t index, |
| const struct pma_region *region_conf) |
| { |
| ulong_t pmaaddr; |
| uint8_t pmacfg; |
| |
| if (region_conf->size == 4) { |
| pmaaddr = NA4_ENCODING(region_conf->start); |
| pmacfg = region_conf->attr.pmacfg | PMACFG_ETYPE_NA4; |
| } else { |
| pmaaddr = NAPOT_ENCODING(region_conf->start, region_conf->size); |
| pmacfg = region_conf->attr.pmacfg | PMACFG_ETYPE_NAPOT; |
| } |
| |
| write_pmaaddr_csr(index, pmaaddr); |
| write_pmacfg_entry(index, pmacfg); |
| } |
| |
| /* |
| * This internal function performs run-time sanity check for |
| * PMA region start address and size. |
| */ |
| static int pma_region_is_valid(const struct pma_region *region) |
| { |
| /* Region size must be power-of-two, |
| * and greater or equal to the minimum |
| * PMA region size. Start address of the |
| * region must align with size. |
| */ |
| int region_is_valid = |
| ((region->size & (region->size - 1)) == 0U) |
| && |
| (region->size >= CONFIG_SOC_ANDES_V5_PMA_REGION_MIN_ALIGN_AND_SIZE) |
| && |
| ((region->start & (region->size - 1)) == 0U); |
| |
| return region_is_valid; |
| } |
| |
| #ifdef CONFIG_NOCACHE_MEMORY |
| static void configure_nocache_region(void) |
| { |
| const struct pma_region nocache_region = { |
| .start = (ulong_t)&_nocache_ram_start, |
| .size = (ulong_t)&_nocache_ram_size, |
| .attr = {PMACFG_MTYPE_MEMORY_NOCACHE_BUFFERABLE}, |
| }; |
| |
| if (pma_region_is_valid(&nocache_region) == -EINVAL) { |
| __ASSERT(0, "Configuring PMA region of nocache region failed\n"); |
| } |
| |
| /* Initialize nocache region at PMA region 0 */ |
| region_init(0, &nocache_region); |
| } |
| #endif /* CONFIG_NOCACHE_MEMORY */ |
| |
| /* |
| * @brief Init PMA CSRs of each CPU core |
| * |
| * In SMP, each CPU has it's own PMA CSR and PMA CSR only affect one CPU. |
| * We should configure CSRs of all CPUs to make memory attribute |
| * (e.g. uncacheable) affects all CPUs. |
| */ |
| void pma_init_per_core(void) |
| { |
| #ifdef CONFIG_NOCACHE_MEMORY |
| configure_nocache_region(); |
| #endif /* CONFIG_NOCACHE_MEMORY */ |
| } |
| |
| static int pma_init(const struct device *arg) |
| { |
| ulong_t mmsc_cfg; |
| |
| __asm__ volatile ("csrr %0, %1" : "=r" (mmsc_cfg) : "i" (NDS_MMSC_CFG)); |
| |
| if (!(mmsc_cfg & MMSC_CFG_PPMA)) { |
| /* This CPU doesn't support PMA */ |
| |
| __ASSERT(0, "CPU doesn't support PMA. " |
| "Please disable CONFIG_SOC_ANDES_V5_PMA\n"); |
| return -1; |
| } |
| |
| pma_init_per_core(); |
| |
| return 0; |
| } |
| |
| SYS_INIT(pma_init, PRE_KERNEL_2, |
| CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); |