1 /*
2  * Copyright (c) 2023 Ambiq Micro Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ambiq_flash_controller
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/flash.h>
12 #include <zephyr/logging/log.h>
13 
14 #include <am_mcu_apollo.h>
15 
16 LOG_MODULE_REGISTER(flash_ambiq, CONFIG_FLASH_LOG_LEVEL);
17 
18 #define SOC_NV_FLASH_NODE      DT_INST(0, soc_nv_flash)
19 #define SOC_NV_FLASH_ADDR      DT_REG_ADDR(SOC_NV_FLASH_NODE)
20 #define SOC_NV_FLASH_SIZE      DT_REG_SIZE(SOC_NV_FLASH_NODE)
21 #define MIN_WRITE_SIZE         16
22 #define FLASH_WRITE_BLOCK_SIZE MAX(DT_PROP(SOC_NV_FLASH_NODE, write_block_size), MIN_WRITE_SIZE)
23 #define FLASH_ERASE_BLOCK_SIZE DT_PROP(SOC_NV_FLASH_NODE, erase_block_size)
24 
25 BUILD_ASSERT((FLASH_WRITE_BLOCK_SIZE & (MIN_WRITE_SIZE - 1)) == 0,
26 	     "The flash write block size must be a multiple of 16!");
27 
28 #define FLASH_ERASE_BYTE 0xFF
29 #define FLASH_ERASE_WORD                                                                           \
30 	(((uint32_t)(FLASH_ERASE_BYTE << 24)) | ((uint32_t)(FLASH_ERASE_BYTE << 16)) |             \
31 	 ((uint32_t)(FLASH_ERASE_BYTE << 8)) | ((uint32_t)FLASH_ERASE_BYTE))
32 
33 #if defined(CONFIG_MULTITHREADING)
34 static struct k_sem flash_ambiq_sem;
35 #define FLASH_SEM_INIT() k_sem_init(&flash_ambiq_sem, 1, 1)
36 #define FLASH_SEM_TAKE() k_sem_take(&flash_ambiq_sem, K_FOREVER)
37 #define FLASH_SEM_GIVE() k_sem_give(&flash_ambiq_sem)
38 #else
39 #define FLASH_SEM_INIT()
40 #define FLASH_SEM_TAKE()
41 #define FLASH_SEM_GIVE()
42 #endif /* CONFIG_MULTITHREADING */
43 
44 static const struct flash_parameters flash_ambiq_parameters = {
45 	.write_block_size = FLASH_WRITE_BLOCK_SIZE,
46 	.erase_value = FLASH_ERASE_BYTE,
47 };
48 
flash_ambiq_valid_range(off_t offset,size_t len)49 static bool flash_ambiq_valid_range(off_t offset, size_t len)
50 {
51 	if ((offset < 0) || offset >= SOC_NV_FLASH_SIZE || (SOC_NV_FLASH_SIZE - offset) < len) {
52 		return false;
53 	}
54 
55 	return true;
56 }
57 
flash_ambiq_read(const struct device * dev,off_t offset,void * data,size_t len)58 static int flash_ambiq_read(const struct device *dev, off_t offset, void *data, size_t len)
59 {
60 	ARG_UNUSED(dev);
61 
62 	if (!flash_ambiq_valid_range(offset, len)) {
63 		return -EINVAL;
64 	}
65 
66 	if (len == 0) {
67 		return 0;
68 	}
69 
70 	memcpy(data, (uint8_t *)(SOC_NV_FLASH_ADDR + offset), len);
71 
72 	return 0;
73 }
74 
flash_ambiq_write(const struct device * dev,off_t offset,const void * data,size_t len)75 static int flash_ambiq_write(const struct device *dev, off_t offset, const void *data, size_t len)
76 {
77 	ARG_UNUSED(dev);
78 
79 	int ret = 0;
80 	uint32_t critical = 0;
81 	uint32_t aligned[FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t)] = {0};
82 	uint32_t *src = (uint32_t *)data;
83 
84 	/* write address must be block size aligned and the write length must be multiple of block
85 	 * size.
86 	 */
87 	if (!flash_ambiq_valid_range(offset, len) ||
88 	    ((uint32_t)offset & (FLASH_WRITE_BLOCK_SIZE - 1)) ||
89 	    (len & (FLASH_WRITE_BLOCK_SIZE - 1))) {
90 		return -EINVAL;
91 	}
92 
93 	if (len == 0) {
94 		return 0;
95 	}
96 
97 	FLASH_SEM_TAKE();
98 
99 	critical = am_hal_interrupt_master_disable();
100 	for (int i = 0; i < len / FLASH_WRITE_BLOCK_SIZE; i++) {
101 		for (int j = 0; j < FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t); j++) {
102 			/* Make sure the source data is 4-byte aligned. */
103 			aligned[j] = UNALIGNED_GET((uint32_t *)src);
104 			src++;
105 		}
106 		ret = am_hal_mram_main_program(
107 			AM_HAL_MRAM_PROGRAM_KEY, aligned,
108 			(uint32_t *)(SOC_NV_FLASH_ADDR + offset + i * FLASH_WRITE_BLOCK_SIZE),
109 			FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t));
110 		if (ret) {
111 			break;
112 		}
113 	}
114 	am_hal_interrupt_master_set(critical);
115 
116 	FLASH_SEM_GIVE();
117 
118 	return ret;
119 }
120 
flash_ambiq_erase(const struct device * dev,off_t offset,size_t len)121 static int flash_ambiq_erase(const struct device *dev, off_t offset, size_t len)
122 {
123 	ARG_UNUSED(dev);
124 
125 	int ret = 0;
126 
127 	if (!flash_ambiq_valid_range(offset, len)) {
128 		return -EINVAL;
129 	}
130 
131 	/* The erase address and length alignment check will be done in HAL.*/
132 
133 	if (len == 0) {
134 		return 0;
135 	}
136 
137 	FLASH_SEM_TAKE();
138 
139 	ret = am_hal_mram_main_fill(AM_HAL_MRAM_PROGRAM_KEY, FLASH_ERASE_WORD,
140 				    (uint32_t *)(SOC_NV_FLASH_ADDR + offset),
141 				    (len / sizeof(uint32_t)));
142 
143 	FLASH_SEM_GIVE();
144 
145 	return ret;
146 }
147 
flash_ambiq_get_parameters(const struct device * dev)148 static const struct flash_parameters *flash_ambiq_get_parameters(const struct device *dev)
149 {
150 	ARG_UNUSED(dev);
151 
152 	return &flash_ambiq_parameters;
153 }
154 
155 #if CONFIG_FLASH_PAGE_LAYOUT
156 static const struct flash_pages_layout pages_layout = {
157 	.pages_count = SOC_NV_FLASH_SIZE / FLASH_ERASE_BLOCK_SIZE,
158 	.pages_size = FLASH_ERASE_BLOCK_SIZE,
159 };
160 
flash_ambiq_pages_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)161 static void flash_ambiq_pages_layout(const struct device *dev,
162 				     const struct flash_pages_layout **layout, size_t *layout_size)
163 {
164 	ARG_UNUSED(dev);
165 
166 	*layout = &pages_layout;
167 	*layout_size = 1;
168 }
169 #endif /* CONFIG_FLASH_PAGE_LAYOUT */
170 
171 static const struct flash_driver_api flash_ambiq_driver_api = {
172 	.read = flash_ambiq_read,
173 	.write = flash_ambiq_write,
174 	.erase = flash_ambiq_erase,
175 	.get_parameters = flash_ambiq_get_parameters,
176 #ifdef CONFIG_FLASH_PAGE_LAYOUT
177 	.page_layout = flash_ambiq_pages_layout,
178 #endif
179 };
180 
flash_ambiq_init(const struct device * dev)181 static int flash_ambiq_init(const struct device *dev)
182 {
183 	ARG_UNUSED(dev);
184 
185 	FLASH_SEM_INIT();
186 
187 	return 0;
188 }
189 
190 DEVICE_DT_INST_DEFINE(0, flash_ambiq_init, NULL, NULL, NULL, POST_KERNEL,
191 		      CONFIG_FLASH_INIT_PRIORITY, &flash_ambiq_driver_api);
192