1 #include "hardware/xip_cache.h"
2 #include "hardware/structs/xip.h"
3 // For barrier macros:
4 #include "hardware/sync.h"
5 
6 // Implementation-private constants (exporting these would create a compatibility headache as they
7 // don't exist on all platforms; all of these operations are exposed through APIs anyways)
8 
9 #if !PICO_RP2040
10 typedef enum {
11     XIP_CACHE_INVALIDATE_BY_SET_WAY = 0,
12     XIP_CACHE_CLEAN_BY_SET_WAY = 1,
13     XIP_CACHE_INVALIDATE_BY_ADDRESS = 2,
14     XIP_CACHE_CLEAN_BY_ADDRESS = 3,
15     XIP_CACHE_PIN_AT_ADDRESS = 7,
16     XIP_CACHE_OP_MAX = 7
17 } cache_op_t;
18 #endif
19 
20 // Used to ensure subsequent accesses observe the new state of the maintained cache lines
21 #define __post_maintenance_barrier() do {__dsb(); __isb();} while (0)
22 
23 // All functions in this file are marked non-flash, even though they themselves may be executed
24 // safely from flash, because they are likely to be called during a flash programming operation
25 // (which makes flash execution momentarily unsafe)
26 
check_xip_offset_range(uintptr_t start_offset,uintptr_t size_bytes)27 __force_inline static void check_xip_offset_range(uintptr_t start_offset, uintptr_t size_bytes) {
28     // We use offsets, not addresses, for consistency with the flash API. This means the range of
29     // valid inputs starts at 0.
30     (void)start_offset;
31     (void)size_bytes;
32     valid_params_if(HARDWARE_XIP_CACHE, start_offset <= XIP_CACHE_ADDRESS_SPACE_SIZE);
33     valid_params_if(HARDWARE_XIP_CACHE, start_offset + size_bytes <= XIP_CACHE_ADDRESS_SPACE_SIZE);
34     // Check for unsigned wrapping too:
35     valid_params_if(HARDWARE_XIP_CACHE, start_offset + size_bytes >= start_offset);
36 }
37 
38 #if !PICO_RP2040
39 // Generic code for RP2350-style caches: apply a maintenance operation to a range of offsets
__no_inline_not_in_flash_func(xip_cache_maintain)40 static void __no_inline_not_in_flash_func(xip_cache_maintain)(uintptr_t start_offset, uintptr_t size_bytes, cache_op_t op) {
41     check_xip_offset_range(start_offset, size_bytes);
42     valid_params_if(HARDWARE_XIP_CACHE, (start_offset & (XIP_CACHE_LINE_SIZE - 1u)) == 0);
43     valid_params_if(HARDWARE_XIP_CACHE, (size_bytes & (XIP_CACHE_LINE_SIZE - 1u)) == 0);
44     valid_params_if(HARDWARE_XIP_CACHE, (uint)op <= (uint)XIP_CACHE_OP_MAX);
45 
46     uintptr_t end = start_offset + size_bytes;
47     for (uintptr_t offset = start_offset; offset < end; offset += XIP_CACHE_LINE_SIZE) {
48         *(io_wo_8 *) (XIP_MAINTENANCE_BASE + offset + (uintptr_t)op) = 0;
49     }
50     __post_maintenance_barrier();
51 }
52 #endif
53 
__no_inline_not_in_flash_func(xip_cache_invalidate_all)54 void __no_inline_not_in_flash_func(xip_cache_invalidate_all)(void) {
55 #if PICO_RP2040
56     xip_ctrl_hw->flush = 1;
57     // Read back to wait for completion
58     (void)xip_ctrl_hw->flush;
59     __post_maintenance_barrier();
60 #else
61     xip_cache_maintain(XIP_CACHE_ADDRESS_SPACE_SIZE - XIP_CACHE_SIZE, XIP_CACHE_SIZE, XIP_CACHE_INVALIDATE_BY_SET_WAY);
62 #endif
63 }
64 
__no_inline_not_in_flash_func(xip_cache_invalidate_range)65 void __no_inline_not_in_flash_func(xip_cache_invalidate_range)(uintptr_t start_offset, uintptr_t size_bytes) {
66 #if PICO_RP2040
67     // Accsses are at intervals of one half cache line (so 4 bytes) because RP2040's cache has two
68     // valid flags per cache line, and we need to clear both.
69     check_xip_offset_range(start_offset, size_bytes);
70     valid_params_if(HARDWARE_XIP_CACHE, (start_offset & 3u) == 0);
71     valid_params_if(HARDWARE_XIP_CACHE, (size_bytes & 3u) == 0);
72 
73     uintptr_t end = start_offset + size_bytes;
74     // On RP2040 you can invalidate a sector (half-line) by writing to its normal cached+allocating address
75     for (uintptr_t offset = start_offset; offset < end; offset += 4u) {
76         *(io_wo_32 *)(offset + XIP_BASE) = 0;
77     }
78     __post_maintenance_barrier();
79 
80 #else
81 
82     xip_cache_maintain(start_offset, size_bytes, XIP_CACHE_INVALIDATE_BY_ADDRESS);
83 
84 #endif
85 }
86 
87 #if !XIP_CACHE_IS_READ_ONLY
__no_inline_not_in_flash_func(xip_cache_clean_all)88 void __no_inline_not_in_flash_func(xip_cache_clean_all)(void) {
89     // Use addresses outside of the downstream QMI address range to work around RP2350-E11; this
90     // effectively performs a clean+invalidate (except being a no-op on pinned lines) due to the
91     // erroneous update of the tag. Consequently you will take a miss on the next access to the
92     // cleaned address.
93     xip_cache_maintain(XIP_END - XIP_BASE - XIP_CACHE_SIZE, XIP_CACHE_SIZE, XIP_CACHE_CLEAN_BY_SET_WAY);
94 }
95 #endif
96 
97 #if !XIP_CACHE_IS_READ_ONLY
__no_inline_not_in_flash_func(xip_cache_clean_range)98 void __no_inline_not_in_flash_func(xip_cache_clean_range)(uintptr_t start_offset, uintptr_t size_bytes) {
99     xip_cache_maintain(start_offset, size_bytes, XIP_CACHE_CLEAN_BY_ADDRESS);
100 }
101 #endif
102 
103 #if !PICO_RP2040
__no_inline_not_in_flash_func(xip_cache_pin_range)104 void __no_inline_not_in_flash_func(xip_cache_pin_range)(uintptr_t start_offset, uintptr_t size_bytes) {
105     valid_params_if(HARDWARE_XIP_CACHE, size_bytes <= XIP_CACHE_SIZE);
106     xip_cache_maintain(start_offset, size_bytes, XIP_CACHE_PIN_AT_ADDRESS);
107 }
108 #endif
109 
110