1 /*
2  * Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include "pico/btstack_flash_bank.h"
8 #include "pico/flash.h"
9 #include "hardware/sync.h"
10 #include "hardware/flash.h"
11 #include <string.h>
12 
13 // Check sizes
14 static_assert(PICO_FLASH_BANK_TOTAL_SIZE % (FLASH_SECTOR_SIZE * 2) == 0, "PICO_FLASH_BANK_TOTAL_SIZE invalid");
15 static_assert(PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
16 
17 // Size of one bank
18 #define PICO_FLASH_BANK_SIZE (PICO_FLASH_BANK_TOTAL_SIZE / 2)
19 
20 #if 0
21 #define DEBUG_PRINT(format,args...) printf(format, ## args)
22 #else
23 #define DEBUG_PRINT(...)
24 #endif
25 
pico_flash_bank_get_size(void * context)26 static uint32_t pico_flash_bank_get_size(void * context) {
27     (void)(context);
28     return PICO_FLASH_BANK_SIZE;
29 }
30 
pico_flash_bank_get_alignment(void * context)31 static uint32_t pico_flash_bank_get_alignment(void * context) {
32     (void)(context);
33     return 1;
34 }
35 
36 typedef struct {
37     bool op_is_erase;
38     uintptr_t p0;
39     uintptr_t p1;
40 } mutation_operation_t;
41 
pico_flash_bank_perform_flash_mutation_operation(void * param)42 static void pico_flash_bank_perform_flash_mutation_operation(void *param) {
43     const mutation_operation_t *mop = (const mutation_operation_t *)param;
44     if (mop->op_is_erase) {
45         flash_range_erase(mop->p0, PICO_FLASH_BANK_SIZE);
46     } else {
47         flash_range_program(mop->p0, (const uint8_t *)mop->p1, FLASH_PAGE_SIZE);
48     }
49 }
50 
51 #ifndef pico_flash_bank_get_storage_offset_func
pico_flash_bank_get_fixed_storage_offset(void)52 static inline uint32_t pico_flash_bank_get_fixed_storage_offset(void) {
53     static_assert(PICO_FLASH_BANK_STORAGE_OFFSET + PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
54 #ifndef NDEBUG
55     // Check we're not overlapping the binary in flash
56     extern char __flash_binary_end;
57     assert(((uintptr_t)&__flash_binary_end - XIP_BASE <= PICO_FLASH_BANK_STORAGE_OFFSET));
58 #endif
59     return PICO_FLASH_BANK_STORAGE_OFFSET;
60 }
61 #define pico_flash_bank_get_storage_offset_func pico_flash_bank_get_fixed_storage_offset
62 #else
63 extern uint32_t pico_flash_bank_get_storage_offset_func(void);
64 #endif
65 
pico_flash_bank_erase(void * context,int bank)66 static void pico_flash_bank_erase(void * context, int bank) {
67     (void)(context);
68     DEBUG_PRINT("erase: bank %d\n", bank);
69     mutation_operation_t mop = {
70             .op_is_erase = true,
71             .p0 = pico_flash_bank_get_storage_offset_func() + (PICO_FLASH_BANK_SIZE * bank),
72     };
73     // todo choice of timeout and check return code... currently we have no way to return an error
74     //      to the caller anyway. flash_safe_execute asserts by default on problem other than timeout,
75     //      so that's fine for now, and UINT32_MAX is a timeout of 49 days which seems long enough
76     flash_safe_execute(pico_flash_bank_perform_flash_mutation_operation, &mop, UINT32_MAX);
77 }
78 
pico_flash_bank_read(void * context,int bank,uint32_t offset,uint8_t * buffer,uint32_t size)79 static void pico_flash_bank_read(void *context, int bank, uint32_t offset, uint8_t *buffer, uint32_t size) {
80     (void)(context);
81     DEBUG_PRINT("read: bank %d offset %u size %u\n", bank, offset, size);
82 
83     assert(bank <= 1);
84     if (bank > 1) return;
85 
86     assert(offset < PICO_FLASH_BANK_SIZE);
87     if (offset >= PICO_FLASH_BANK_SIZE) return;
88 
89     assert((offset + size) <= PICO_FLASH_BANK_SIZE);
90     if ((offset + size) > PICO_FLASH_BANK_SIZE) return;
91 
92     // Flash is xip
93     memcpy(buffer, (void *)(XIP_BASE + pico_flash_bank_get_storage_offset_func() + (PICO_FLASH_BANK_SIZE * bank) + offset), size);
94 }
95 
pico_flash_bank_write(void * context,int bank,uint32_t offset,const uint8_t * data,uint32_t size)96 static void pico_flash_bank_write(void * context, int bank, uint32_t offset, const uint8_t *data, uint32_t size) {
97     (void)(context);
98     DEBUG_PRINT("write: bank %d offset %u size %u\n", bank, offset, size);
99 
100     assert(bank <= 1);
101     if (bank > 1) return;
102 
103     assert(offset < PICO_FLASH_BANK_SIZE);
104     if (offset >= PICO_FLASH_BANK_SIZE) return;
105 
106     assert((offset + size) <= PICO_FLASH_BANK_SIZE);
107     if ((offset + size) > PICO_FLASH_BANK_SIZE) return;
108 
109     if (size == 0) return;
110 
111     // calc bank start position
112     const uint32_t bank_start_pos = pico_flash_bank_get_storage_offset_func() + (PICO_FLASH_BANK_SIZE * bank);
113 
114     // Calculate first and last page in the bank
115     const uint32_t first_page = offset / FLASH_PAGE_SIZE;
116     const uint32_t last_page = (offset + size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
117 
118     // Now we only care about the offset in the first page
119     offset %= FLASH_PAGE_SIZE;
120 
121     // Amount of data we've copied
122     uint32_t data_pos = 0;
123     uint32_t size_left = size;
124 
125     // Write all the pages required
126     for(uint32_t page = first_page; page < last_page; page++) {
127         uint8_t page_data[FLASH_PAGE_SIZE];
128 
129         assert(data_pos < size && size_left <= size);
130 
131         // Copy data we're not going to overwrite in the first page
132         if (page == first_page && offset > 0) {
133             memcpy(page_data,
134                 (void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE)),
135                 offset);
136         }
137 
138         // Copy the data we're not going to overwrite in the last page
139         if (page == last_page - 1 && (offset + size_left) < FLASH_PAGE_SIZE) {
140             memcpy(page_data + offset + size_left,
141                 (void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE) + offset + size_left),
142                 FLASH_PAGE_SIZE - offset - size_left);
143         }
144 
145         // Now copy the new data into the page
146         const uint32_t size_to_copy = MIN(size_left, FLASH_PAGE_SIZE - offset);
147         memcpy(page_data + offset, data + data_pos, size_to_copy);
148 
149         data_pos += size_to_copy;
150         size_left -= size_to_copy;
151 
152         // zero offset for the following pages
153         offset = 0;
154 
155         // Now program the entire page
156         mutation_operation_t mop = {
157                 .op_is_erase = false,
158                 .p0 = bank_start_pos + (page * FLASH_PAGE_SIZE),
159                 .p1 = (uintptr_t)page_data
160         };
161         // todo choice of timeout and check return code... currently we have no way to return an error
162         //      to the caller anyway. flash_safe_execute asserts by default on problem other than timeout,
163         //      so that's fine for now, and UINT32_MAX is a timeout of 49 days which seems long enough
164         flash_safe_execute(pico_flash_bank_perform_flash_mutation_operation, &mop, UINT32_MAX);
165     }
166 }
167 
168 static const hal_flash_bank_t pico_flash_bank_instance_obj = {
169     /* uint32_t (*get_size)(..) */ 		 &pico_flash_bank_get_size,
170     /* uint32_t (*get_alignment)(..); */ &pico_flash_bank_get_alignment,
171     /* void (*erase)(..);    */ 		 &pico_flash_bank_erase,
172     /* void (*read)(..);      */ 		 &pico_flash_bank_read,
173     /* void (*write)(..);     */ 		 &pico_flash_bank_write,
174 };
175 
pico_flash_bank_instance(void)176 const hal_flash_bank_t *pico_flash_bank_instance(void) {
177     return &pico_flash_bank_instance_obj;
178 }
179