| /* Copyright 2020 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "cache.h" |
| #include "console.h" |
| #include "csr.h" |
| |
| extern struct mpu_entry mpu_entries[]; |
| |
| #define CONFIGURE_MPU_ENTRY(index) \ |
| do { \ |
| if (mpu_entries[index].end_addr - \ |
| mpu_entries[index].start_addr) { \ |
| write_csr(CSR_MPU_L(index), \ |
| mpu_entries[index].start_addr | \ |
| mpu_entries[index].attribute); \ |
| write_csr(CSR_MPU_H(index), \ |
| mpu_entries[index].end_addr); \ |
| mpu_en |= BIT(index); \ |
| } \ |
| } while (0) |
| |
| void cache_init(void) |
| { |
| uint32_t mpu_en = 0; |
| |
| /* disable mpu */ |
| clear_csr(CSR_MCTREN, CSR_MCTREN_MPU); |
| |
| /* enable i$, d$ */ |
| set_csr(CSR_MCTREN, CSR_MCTREN_ICACHE); |
| set_csr(CSR_MCTREN, CSR_MCTREN_DCACHE); |
| |
| #ifdef CHIP_FAMILY_RV55 |
| set_csr(CSR_MCTREN, CSR_MCTREN_BTB); |
| set_csr(CSR_MCTREN, CSR_MCTREN_TLP); |
| #endif |
| |
| /* invalidate icache and dcache */ |
| cache_invalidate_icache(); |
| cache_invalidate_dcache(); |
| |
| /* set mpu entries |
| * |
| * b/172886808: The loop to configure mpu entries must be enrolled. |
| */ |
| BUILD_ASSERT(NR_MPU_ENTRIES == 16); |
| CONFIGURE_MPU_ENTRY(0); |
| CONFIGURE_MPU_ENTRY(1); |
| CONFIGURE_MPU_ENTRY(2); |
| CONFIGURE_MPU_ENTRY(3); |
| CONFIGURE_MPU_ENTRY(4); |
| CONFIGURE_MPU_ENTRY(5); |
| CONFIGURE_MPU_ENTRY(6); |
| CONFIGURE_MPU_ENTRY(7); |
| CONFIGURE_MPU_ENTRY(8); |
| CONFIGURE_MPU_ENTRY(9); |
| CONFIGURE_MPU_ENTRY(10); |
| CONFIGURE_MPU_ENTRY(11); |
| CONFIGURE_MPU_ENTRY(12); |
| CONFIGURE_MPU_ENTRY(13); |
| CONFIGURE_MPU_ENTRY(14); |
| CONFIGURE_MPU_ENTRY(15); |
| |
| /* enable mpu entries */ |
| write_csr(CSR_MPU_ENTRY_EN, mpu_en); |
| |
| /* enable mpu */ |
| set_csr(CSR_MCTREN, CSR_MCTREN_MPU); |
| |
| /* fence */ |
| asm volatile("fence.i" ::: "memory"); |
| } |
| |
| #ifdef DEBUG |
| /* |
| * I for I-cache |
| * D for D-cache |
| * C for control transfer instructions (branch, jump, ret, interrupt, ...) |
| */ |
| static enum { PMU_SELECT_I = 0, PMU_SELECT_D, PMU_SELECT_C } pmu_select; |
| |
| static int command_enable_pmu(int argc, const char **argv) |
| { |
| static const char *const selectors[] = { |
| [PMU_SELECT_I] = "I", |
| [PMU_SELECT_D] = "D", |
| [PMU_SELECT_C] = "C", |
| }; |
| int i; |
| |
| if (argc != 2) |
| return EC_ERROR_PARAM1; |
| |
| for (i = 0; i < ARRAY_SIZE(selectors); ++i) { |
| if (strcasecmp(argv[1], selectors[i]) == 0) { |
| pmu_select = i; |
| break; |
| } |
| } |
| if (i >= ARRAY_SIZE(selectors)) |
| return EC_ERROR_PARAM1; |
| |
| ccprintf("select \"%s\"\n", selectors[pmu_select]); |
| |
| /* disable all PMU */ |
| clear_csr(CSR_PMU_MPMUCTR, |
| CSR_PMU_MPMUCTR_C | CSR_PMU_MPMUCTR_I | CSR_PMU_MPMUCTR_H3 | |
| CSR_PMU_MPMUCTR_H4 | CSR_PMU_MPMUCTR_H5); |
| |
| /* reset cycle count */ |
| write_csr(CSR_PMU_MCYCLE, 0); |
| write_csr(CSR_PMU_MCYCLEH, 0); |
| /* reset retired-instruction count */ |
| write_csr(CSR_PMU_MINSTRET, 0); |
| write_csr(CSR_PMU_MINSTRETH, 0); |
| /* reset counter{3,4,5} */ |
| write_csr(CSR_PMU_MHPMCOUNTER3, 0); |
| write_csr(CSR_PMU_MHPMCOUNTER3H, 0); |
| write_csr(CSR_PMU_MHPMCOUNTER4, 0); |
| write_csr(CSR_PMU_MHPMCOUNTER4H, 0); |
| write_csr(CSR_PMU_MHPMCOUNTER5, 0); |
| write_csr(CSR_PMU_MHPMCOUNTER5H, 0); |
| |
| /* select different event IDs for counter{3,4,5} */ |
| switch (pmu_select) { |
| case PMU_SELECT_I: |
| /* I-cache access count */ |
| write_csr(CSR_PMU_MHPMEVENT3, 1); |
| /* I-cache miss count */ |
| write_csr(CSR_PMU_MHPMEVENT4, 3); |
| /* noncacheable I-AXI access count */ |
| write_csr(CSR_PMU_MHPMEVENT5, 5); |
| break; |
| case PMU_SELECT_D: |
| /* D-cache access count */ |
| write_csr(CSR_PMU_MHPMEVENT3, 11); |
| /* D-cache miss count */ |
| write_csr(CSR_PMU_MHPMEVENT4, 12); |
| /* noncacheable D-AXI access count */ |
| write_csr(CSR_PMU_MHPMEVENT5, 14); |
| break; |
| case PMU_SELECT_C: |
| /* control transfer instruction count */ |
| write_csr(CSR_PMU_MHPMEVENT3, 27); |
| /* control transfer miss-predict count */ |
| write_csr(CSR_PMU_MHPMEVENT4, 28); |
| /* interrupt count */ |
| write_csr(CSR_PMU_MHPMEVENT5, 29); |
| break; |
| } |
| |
| cache_invalidate_icache(); |
| cache_flush_dcache(); |
| |
| /* enable all PMU */ |
| set_csr(CSR_PMU_MPMUCTR, |
| CSR_PMU_MPMUCTR_C | CSR_PMU_MPMUCTR_I | CSR_PMU_MPMUCTR_H3 | |
| CSR_PMU_MPMUCTR_H4 | CSR_PMU_MPMUCTR_H5); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_SAFE_CONSOLE_COMMAND(enable_pmu, command_enable_pmu, "[I | D | C]", |
| "Enable PMU"); |
| |
| static int command_disable_pmu(int argc, const char **argv) |
| { |
| clear_csr(CSR_PMU_MPMUCTR, |
| CSR_PMU_MPMUCTR_C | CSR_PMU_MPMUCTR_I | CSR_PMU_MPMUCTR_H3 | |
| CSR_PMU_MPMUCTR_H4 | CSR_PMU_MPMUCTR_H5); |
| return EC_SUCCESS; |
| } |
| DECLARE_SAFE_CONSOLE_COMMAND(disable_pmu, command_disable_pmu, NULL, |
| "Disable PMU"); |
| |
| static int command_show_pmu(int argc, const char **argv) |
| { |
| uint64_t val3, val4, val5; |
| uint32_t p; |
| |
| val3 = ((uint64_t)read_csr(CSR_PMU_MCYCLEH) << 32) | |
| read_csr(CSR_PMU_MCYCLE); |
| ccprintf("cycles: %lld\n", val3); |
| |
| val3 = ((uint64_t)read_csr(CSR_PMU_MINSTRETH) << 32) | |
| read_csr(CSR_PMU_MINSTRET); |
| ccprintf("retired instructions: %lld\n", val3); |
| |
| val3 = ((uint64_t)read_csr(CSR_PMU_MHPMCOUNTER3H) << 32) | |
| read_csr(CSR_PMU_MHPMCOUNTER3); |
| val4 = ((uint64_t)read_csr(CSR_PMU_MHPMCOUNTER4H) << 32) | |
| read_csr(CSR_PMU_MHPMCOUNTER4); |
| val5 = ((uint64_t)read_csr(CSR_PMU_MHPMCOUNTER5H) << 32) | |
| read_csr(CSR_PMU_MHPMCOUNTER5); |
| |
| if (val3) |
| p = val4 * 10000 / val3; |
| else |
| p = 0; |
| |
| switch (pmu_select) { |
| case PMU_SELECT_I: |
| ccprintf("I-cache:\n"); |
| ccprintf(" access: %lld\n", val3); |
| ccprintf(" miss: %lld (%d.%d%%)\n", val4, p / 100, p % 100); |
| ccprintf("non-cacheable I: %lld\n", val5); |
| break; |
| case PMU_SELECT_D: |
| ccprintf("D-cache:\n"); |
| ccprintf(" access: %lld\n", val3); |
| ccprintf(" miss: %lld (%d.%d%%)\n", val4, p / 100, p % 100); |
| ccprintf("non-cacheable D: %lld\n", val5); |
| break; |
| case PMU_SELECT_C: |
| ccprintf("control transfer instruction:\n"); |
| ccprintf(" total: %lld\n", val3); |
| ccprintf(" miss-predict: %lld (%d.%d%%)\n", val4, p / 100, |
| p % 100); |
| ccprintf("interrupts: %lld\n", val5); |
| break; |
| } |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_SAFE_CONSOLE_COMMAND(show_pmu, command_show_pmu, NULL, "Show PMU"); |
| #endif |