1 /*
2 * Copyright (c) 2018 Aurelien Jarno
3 * Copyright (c) 2023 Bjarki Arge Andreasen
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /*
9 * This driver defines a page as the erase_block_size.
10 * This driver defines a write page as defined by the flash controller
11 * This driver defines a section as a contiguous array of bytes
12 * This driver defines an area as the entire flash area
13 * This driver defines the write block size as the minimum write block size
14 */
15
16 #define DT_DRV_COMPAT atmel_sam_flash_controller
17
18 #include <zephyr/kernel.h>
19 #include <zephyr/devicetree.h>
20 #include <zephyr/drivers/flash.h>
21 #include <zephyr/sys/barrier.h>
22 #include <string.h>
23 #include <soc.h>
24
25 #include <zephyr/logging/log.h>
26 LOG_MODULE_REGISTER(flash_sam, CONFIG_FLASH_LOG_LEVEL);
27
28 #define SAM_FLASH_WRITE_PAGE_SIZE (512)
29
30 typedef void (*sam_flash_irq_init_fn_ptr)(void);
31
32 struct sam_flash_config {
33 Efc *regs;
34 sam_flash_irq_init_fn_ptr irq_init;
35 off_t area_address;
36 off_t area_size;
37 struct flash_parameters parameters;
38 struct flash_pages_layout *pages_layouts;
39 size_t pages_layouts_size;
40 };
41
42 struct sam_flash_erase_data {
43 off_t section_start;
44 size_t section_end;
45 bool succeeded;
46 };
47
48 struct sam_flash_data {
49 const struct device *dev;
50 struct k_spinlock lock;
51 struct sam_flash_erase_data erase_data;
52 struct k_sem ready_sem;
53 };
54
sam_flash_validate_offset_len(off_t offset,size_t len)55 static bool sam_flash_validate_offset_len(off_t offset, size_t len)
56 {
57 if (offset < 0) {
58 return false;
59 }
60
61 if ((offset + len) < len) {
62 return false;
63 }
64
65 return true;
66 }
67
sam_flash_aligned(size_t value,size_t alignment)68 static bool sam_flash_aligned(size_t value, size_t alignment)
69 {
70 return (value & (alignment - 1)) == 0;
71 }
72
sam_flash_offset_is_on_write_page_boundary(off_t offset)73 static bool sam_flash_offset_is_on_write_page_boundary(off_t offset)
74 {
75 return sam_flash_aligned(offset, SAM_FLASH_WRITE_PAGE_SIZE);
76 }
77
sam_flash_mask_ready_interrupt(const struct sam_flash_config * config)78 static inline void sam_flash_mask_ready_interrupt(const struct sam_flash_config *config)
79 {
80 Efc *regs = config->regs;
81
82 regs->EEFC_FMR &= ~EEFC_FMR_FRDY;
83 }
84
sam_flash_unmask_ready_interrupt(const struct sam_flash_config * config)85 static inline void sam_flash_unmask_ready_interrupt(const struct sam_flash_config *config)
86 {
87 Efc *regs = config->regs;
88
89 regs->EEFC_FMR |= EEFC_FMR_FRDY;
90 }
91
sam_flash_isr(const struct device * dev)92 static void sam_flash_isr(const struct device *dev)
93 {
94 struct sam_flash_data *data = dev->data;
95 const struct sam_flash_config *config = dev->config;
96
97 sam_flash_mask_ready_interrupt(config);
98 k_sem_give(&data->ready_sem);
99 }
100
sam_flash_section_wait_until_ready(const struct device * dev)101 static int sam_flash_section_wait_until_ready(const struct device *dev)
102 {
103 struct sam_flash_data *data = dev->data;
104 const struct sam_flash_config *config = dev->config;
105 Efc *regs = config->regs;
106 uint32_t eefc_fsr;
107
108 k_sem_reset(&data->ready_sem);
109 sam_flash_unmask_ready_interrupt(config);
110
111 if (k_sem_take(&data->ready_sem, K_MSEC(500)) < 0) {
112 LOG_ERR("Command did not execute in time");
113 return -EFAULT;
114 }
115
116 /* FSR register is cleared on read */
117 eefc_fsr = regs->EEFC_FSR;
118
119 if (eefc_fsr & EEFC_FSR_FCMDE) {
120 LOG_ERR("Invalid command requested");
121 return -EPERM;
122 }
123
124 if (eefc_fsr & EEFC_FSR_FLOCKE) {
125 LOG_ERR("Tried to modify locked region");
126 return -EPERM;
127 }
128
129 if (eefc_fsr & EEFC_FSR_FLERR) {
130 LOG_ERR("Programming failed");
131 return -EPERM;
132 }
133
134 return 0;
135 }
136
sam_flash_section_is_within_area(const struct device * dev,off_t offset,size_t len)137 static bool sam_flash_section_is_within_area(const struct device *dev, off_t offset, size_t len)
138 {
139 const struct sam_flash_config *config = dev->config;
140
141 if ((offset + ((off_t)len)) < offset) {
142 return false;
143 }
144
145 if ((offset >= 0) && ((offset + len) <= config->area_size)) {
146 return true;
147 }
148
149 LOG_WRN("Section from 0x%x to 0x%x is not within flash area (0x0 to %x)",
150 (size_t)offset, (size_t)(offset + len), (size_t)config->area_size);
151
152 return false;
153 }
154
sam_flash_section_is_aligned_with_write_block_size(const struct device * dev,off_t offset,size_t len)155 static bool sam_flash_section_is_aligned_with_write_block_size(const struct device *dev,
156 off_t offset, size_t len)
157 {
158 const struct sam_flash_config *config = dev->config;
159
160 if (sam_flash_aligned(offset, config->parameters.write_block_size) &&
161 sam_flash_aligned(len, config->parameters.write_block_size)) {
162 return true;
163 }
164
165 LOG_WRN("Section from 0x%x to 0x%x is not aligned with write block size (%u)",
166 (size_t)offset, (size_t)(offset + len), config->parameters.write_block_size);
167
168 return false;
169 }
170
sam_flash_section_is_aligned_with_pages(const struct device * dev,off_t offset,size_t len)171 static bool sam_flash_section_is_aligned_with_pages(const struct device *dev, off_t offset,
172 size_t len)
173 {
174 const struct sam_flash_config *config = dev->config;
175 struct flash_pages_info pages_info;
176
177 /* Get the page offset points to */
178 if (flash_get_page_info_by_offs(dev, offset, &pages_info) < 0) {
179 return false;
180 }
181
182 /* Validate offset points to start of page */
183 if (offset != pages_info.start_offset) {
184 return false;
185 }
186
187 /* Check if end of section is aligned with end of area */
188 if ((offset + len) == (config->area_size)) {
189 return true;
190 }
191
192 /* Get the page pointed to by end of section */
193 if (flash_get_page_info_by_offs(dev, offset + len, &pages_info) < 0) {
194 return false;
195 }
196
197 /* Validate offset points to start of page */
198 if ((offset + len) != pages_info.start_offset) {
199 return false;
200 }
201
202 return true;
203 }
204
sam_flash_read(const struct device * dev,off_t offset,void * data,size_t len)205 static int sam_flash_read(const struct device *dev, off_t offset, void *data, size_t len)
206 {
207 struct sam_flash_data *sam_data = dev->data;
208 const struct sam_flash_config *sam_config = dev->config;
209 k_spinlock_key_t key;
210
211 if (len == 0) {
212 return 0;
213 }
214
215 if (!sam_flash_validate_offset_len(offset, len)) {
216 return -EINVAL;
217 }
218
219 if (!sam_flash_section_is_within_area(dev, offset, len)) {
220 return -EINVAL;
221 }
222
223 key = k_spin_lock(&sam_data->lock);
224 memcpy(data, (uint8_t *)(sam_config->area_address + offset), len);
225 k_spin_unlock(&sam_data->lock, key);
226 return 0;
227 }
228
sam_flash_write_latch_buffer_to_page(const struct device * dev,off_t offset)229 static int sam_flash_write_latch_buffer_to_page(const struct device *dev, off_t offset)
230 {
231 const struct sam_flash_config *sam_config = dev->config;
232 Efc *regs = sam_config->regs;
233 uint32_t page = offset / SAM_FLASH_WRITE_PAGE_SIZE;
234
235 regs->EEFC_FCR = EEFC_FCR_FCMD_WP | EEFC_FCR_FARG(page) | EEFC_FCR_FKEY_PASSWD;
236 sam_flash_section_wait_until_ready(dev);
237 return 0;
238 }
239
sam_flash_write_latch_buffer_to_previous_page(const struct device * dev,off_t offset)240 static int sam_flash_write_latch_buffer_to_previous_page(const struct device *dev, off_t offset)
241 {
242 return sam_flash_write_latch_buffer_to_page(dev, offset - SAM_FLASH_WRITE_PAGE_SIZE);
243 }
244
sam_flash_write_dword_to_latch_buffer(off_t offset,uint32_t dword)245 static void sam_flash_write_dword_to_latch_buffer(off_t offset, uint32_t dword)
246 {
247 *((uint32_t *)offset) = dword;
248 barrier_dsync_fence_full();
249 }
250
sam_flash_write_dwords_to_flash(const struct device * dev,off_t offset,const uint32_t * dwords,size_t size)251 static int sam_flash_write_dwords_to_flash(const struct device *dev, off_t offset,
252 const uint32_t *dwords, size_t size)
253 {
254 for (size_t i = 0; i < size; i++) {
255 sam_flash_write_dword_to_latch_buffer(offset, dwords[i]);
256 offset += sizeof(uint32_t);
257 if (sam_flash_offset_is_on_write_page_boundary(offset)) {
258 sam_flash_write_latch_buffer_to_previous_page(dev, offset);
259 }
260 }
261
262 if (!sam_flash_offset_is_on_write_page_boundary(offset)) {
263 sam_flash_write_latch_buffer_to_page(dev, offset);
264 }
265
266 return 0;
267 }
268
sam_flash_write(const struct device * dev,off_t offset,const void * data,size_t len)269 static int sam_flash_write(const struct device *dev, off_t offset, const void *data, size_t len)
270 {
271 struct sam_flash_data *sam_data = dev->data;
272 k_spinlock_key_t key;
273
274 if (len == 0) {
275 return 0;
276 }
277
278 if (!sam_flash_validate_offset_len(offset, len)) {
279 return -EINVAL;
280 }
281
282 if (!sam_flash_section_is_aligned_with_write_block_size(dev, offset, len)) {
283 return -EINVAL;
284 }
285
286 LOG_DBG("Writing sector from 0x%x to 0x%x", (size_t)offset, (size_t)(offset + len));
287
288 key = k_spin_lock(&sam_data->lock);
289 if (sam_flash_write_dwords_to_flash(dev, offset, data, len / sizeof(uint32_t)) < 0) {
290 k_spin_unlock(&sam_data->lock, key);
291 return -EAGAIN;
292 }
293
294 k_spin_unlock(&sam_data->lock, key);
295 return 0;
296 }
297
sam_flash_unlock_write_page(const struct device * dev,uint16_t page_index)298 static int sam_flash_unlock_write_page(const struct device *dev, uint16_t page_index)
299 {
300 const struct sam_flash_config *sam_config = dev->config;
301 Efc *regs = sam_config->regs;
302
303 /* Perform unlock command of write page */
304 regs->EEFC_FCR = EEFC_FCR_FCMD_CLB
305 | EEFC_FCR_FARG(page_index)
306 | EEFC_FCR_FKEY_PASSWD;
307
308 return sam_flash_section_wait_until_ready(dev);
309 }
310
sam_flash_unlock_page(const struct device * dev,const struct flash_pages_info * info)311 static int sam_flash_unlock_page(const struct device *dev, const struct flash_pages_info *info)
312 {
313 uint16_t page_index_start;
314 uint16_t page_index_end;
315 int ret;
316
317 /* Convert from page offset and size to write page index and count */
318 page_index_start = info->start_offset / SAM_FLASH_WRITE_PAGE_SIZE;
319 page_index_end = page_index_start + (info->size / SAM_FLASH_WRITE_PAGE_SIZE);
320
321 for (uint16_t i = page_index_start; i < page_index_end; i++) {
322 ret = sam_flash_unlock_write_page(dev, i);
323 if (ret < 0) {
324 return ret;
325 }
326 }
327
328 return 0;
329 }
330
sam_flash_erase_page(const struct device * dev,const struct flash_pages_info * info)331 static int sam_flash_erase_page(const struct device *dev, const struct flash_pages_info *info)
332 {
333 const struct sam_flash_config *sam_config = dev->config;
334 Efc *regs = sam_config->regs;
335 uint32_t page_index;
336 int ret;
337
338 /* Convert from page offset to write page index */
339 page_index = info->start_offset / SAM_FLASH_WRITE_PAGE_SIZE;
340
341 LOG_DBG("Erasing page at 0x%x of size 0x%x", (size_t)info->start_offset, info->size);
342
343 /* Perform erase command of page */
344 switch (info->size) {
345 case 0x800:
346 regs->EEFC_FCR = EEFC_FCR_FCMD_EPA
347 | EEFC_FCR_FARG(page_index)
348 | EEFC_FCR_FKEY_PASSWD;
349 break;
350
351 case 0x1000:
352 regs->EEFC_FCR = EEFC_FCR_FCMD_EPA
353 | EEFC_FCR_FARG(page_index | 1)
354 | EEFC_FCR_FKEY_PASSWD;
355 break;
356
357 case 0x2000:
358 regs->EEFC_FCR = EEFC_FCR_FCMD_EPA
359 | EEFC_FCR_FARG(page_index | 2)
360 | EEFC_FCR_FKEY_PASSWD;
361 break;
362
363 case 0x4000:
364 regs->EEFC_FCR = EEFC_FCR_FCMD_EPA
365 | EEFC_FCR_FARG(page_index | 3)
366 | EEFC_FCR_FKEY_PASSWD;
367 break;
368
369 default:
370 return -EINVAL;
371 }
372
373 ret = sam_flash_section_wait_until_ready(dev);
374 if (ret == 0) {
375 return ret;
376 }
377
378 LOG_ERR("Failed to erase page at 0x%x of size 0x%x", (size_t)info->start_offset,
379 info->size);
380
381 return ret;
382 }
383
sam_flash_erase_foreach_page(const struct flash_pages_info * info,void * data)384 static bool sam_flash_erase_foreach_page(const struct flash_pages_info *info, void *data)
385 {
386 struct sam_flash_data *sam_data = data;
387 const struct device *dev = sam_data->dev;
388 struct sam_flash_erase_data *erase_data = &sam_data->erase_data;
389
390 /* Validate we reached first page to erase */
391 if (info->start_offset < erase_data->section_start) {
392 /* Next page */
393 return true;
394 }
395
396 /* Check if we've reached the end of pages to erase */
397 if (info->start_offset >= erase_data->section_end) {
398 /* Succeeded, stop iterating */
399 erase_data->succeeded = true;
400 return false;
401 }
402
403 if (sam_flash_unlock_page(dev, info) < 0) {
404 /* Failed to unlock page, stop iterating */
405 return false;
406 }
407
408 if (sam_flash_erase_page(dev, info) < 0) {
409 /* Failed to erase page, stop iterating */
410 return false;
411 }
412
413 /* Next page */
414 return true;
415 }
416
sam_flash_erase(const struct device * dev,off_t offset,size_t size)417 static int sam_flash_erase(const struct device *dev, off_t offset, size_t size)
418 {
419 struct sam_flash_data *sam_data = dev->data;
420 k_spinlock_key_t key;
421
422 if (size == 0) {
423 return 0;
424 }
425
426 if (!sam_flash_validate_offset_len(offset, size)) {
427 return -EINVAL;
428 }
429
430 if (!sam_flash_section_is_aligned_with_pages(dev, offset, size)) {
431 return -EINVAL;
432 }
433
434 LOG_DBG("Erasing sector from 0x%x to 0x%x", (size_t)offset, (size_t)(offset + size));
435
436 key = k_spin_lock(&sam_data->lock);
437 sam_data->erase_data.section_start = offset;
438 sam_data->erase_data.section_end = offset + size;
439 sam_data->erase_data.succeeded = false;
440 flash_page_foreach(dev, sam_flash_erase_foreach_page, sam_data);
441 if (!sam_data->erase_data.succeeded) {
442 k_spin_unlock(&sam_data->lock, key);
443 return -EFAULT;
444 }
445
446 k_spin_unlock(&sam_data->lock, key);
447 return 0;
448 }
449
sam_flash_get_parameters(const struct device * dev)450 static const struct flash_parameters *sam_flash_get_parameters(const struct device *dev)
451 {
452 const struct sam_flash_config *config = dev->config;
453
454 return &config->parameters;
455 }
456
sam_flash_api_pages_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)457 static void sam_flash_api_pages_layout(const struct device *dev,
458 const struct flash_pages_layout **layout,
459 size_t *layout_size)
460 {
461 const struct sam_flash_config *config = dev->config;
462
463 *layout = config->pages_layouts;
464 *layout_size = config->pages_layouts_size;
465 }
466
467 static struct flash_driver_api sam_flash_api = {
468 .read = sam_flash_read,
469 .write = sam_flash_write,
470 .erase = sam_flash_erase,
471 .get_parameters = sam_flash_get_parameters,
472 .page_layout = sam_flash_api_pages_layout,
473 };
474
sam_flash_init(const struct device * dev)475 static int sam_flash_init(const struct device *dev)
476 {
477 struct sam_flash_data *sam_data = dev->data;
478 const struct sam_flash_config *sam_config = dev->config;
479
480 sam_data->dev = dev;
481 k_sem_init(&sam_data->ready_sem, 0, 1);
482 sam_flash_mask_ready_interrupt(sam_config);
483 sam_config->irq_init();
484 return 0;
485 }
486
487 #define SAM_FLASH_DEVICE DT_INST(0, atmel_sam_flash)
488
489 #define SAM_FLASH_PAGES_LAYOUT(node_id, prop, idx) \
490 { \
491 .pages_count = DT_PHA_BY_IDX(node_id, prop, idx, pages_count), \
492 .pages_size = DT_PHA_BY_IDX(node_id, prop, idx, pages_size), \
493 }
494
495 #define SAM_FLASH_PAGES_LAYOUTS \
496 DT_FOREACH_PROP_ELEM_SEP(SAM_FLASH_DEVICE, erase_blocks, SAM_FLASH_PAGES_LAYOUT, (,))
497
498 #define SAM_FLASH_CONTROLLER(inst) \
499 struct flash_pages_layout sam_flash_pages_layouts##inst[] = { \
500 SAM_FLASH_PAGES_LAYOUTS \
501 }; \
502 \
503 static void sam_flash_irq_init_##inst(void) \
504 { \
505 IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \
506 sam_flash_isr, DEVICE_DT_INST_GET(inst), 0); \
507 irq_enable(DT_INST_IRQN(inst)); \
508 \
509 } \
510 \
511 static const struct sam_flash_config sam_flash_config##inst = { \
512 .regs = (Efc *)DT_INST_REG_ADDR(inst), \
513 .irq_init = sam_flash_irq_init_##inst, \
514 .area_address = DT_REG_ADDR(SAM_FLASH_DEVICE), \
515 .area_size = DT_REG_SIZE(SAM_FLASH_DEVICE), \
516 .parameters = { \
517 .write_block_size = DT_PROP(SAM_FLASH_DEVICE, write_block_size), \
518 .erase_value = 0xFF, \
519 }, \
520 .pages_layouts = sam_flash_pages_layouts##inst, \
521 .pages_layouts_size = ARRAY_SIZE(sam_flash_pages_layouts##inst), \
522 }; \
523 \
524 static struct sam_flash_data sam_flash_data##inst; \
525 \
526 DEVICE_DT_INST_DEFINE(inst, sam_flash_init, NULL, &sam_flash_data##inst, \
527 &sam_flash_config##inst, POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, \
528 &sam_flash_api);
529
530 SAM_FLASH_CONTROLLER(0)
531