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 #if (CONFIG_SOC_SERIES_APOLLO4X)
22 #define MIN_WRITE_SIZE 16
23 #else
24 #define MIN_WRITE_SIZE 4
25 #endif /* CONFIG_SOC_SERIES_APOLLO4X */
26 #define FLASH_WRITE_BLOCK_SIZE MAX(DT_PROP(SOC_NV_FLASH_NODE, write_block_size), MIN_WRITE_SIZE)
27 #define FLASH_ERASE_BLOCK_SIZE DT_PROP(SOC_NV_FLASH_NODE, erase_block_size)
28 
29 BUILD_ASSERT((FLASH_WRITE_BLOCK_SIZE & (MIN_WRITE_SIZE - 1)) == 0,
30 	     "The flash write block size must be a multiple of MIN_WRITE_SIZE!");
31 
32 #define FLASH_ERASE_BYTE 0xFF
33 #define FLASH_ERASE_WORD                                                                           \
34 	(((uint32_t)(FLASH_ERASE_BYTE << 24)) | ((uint32_t)(FLASH_ERASE_BYTE << 16)) |             \
35 	 ((uint32_t)(FLASH_ERASE_BYTE << 8)) | ((uint32_t)FLASH_ERASE_BYTE))
36 
37 #if defined(CONFIG_MULTITHREADING)
38 static struct k_sem flash_ambiq_sem;
39 #define FLASH_SEM_INIT() k_sem_init(&flash_ambiq_sem, 1, 1)
40 #define FLASH_SEM_TAKE() k_sem_take(&flash_ambiq_sem, K_FOREVER)
41 #define FLASH_SEM_GIVE() k_sem_give(&flash_ambiq_sem)
42 #else
43 #define FLASH_SEM_INIT()
44 #define FLASH_SEM_TAKE()
45 #define FLASH_SEM_GIVE()
46 #endif /* CONFIG_MULTITHREADING */
47 
48 static const struct flash_parameters flash_ambiq_parameters = {
49 	.write_block_size = FLASH_WRITE_BLOCK_SIZE,
50 	.erase_value = FLASH_ERASE_BYTE,
51 #if defined(CONFIG_SOC_SERIES_APOLLO4X)
52 	.caps = {
53 		.no_explicit_erase = true,
54 	},
55 #endif
56 };
57 
flash_ambiq_valid_range(off_t offset,size_t len)58 static bool flash_ambiq_valid_range(off_t offset, size_t len)
59 {
60 	if ((offset < 0) || offset >= SOC_NV_FLASH_SIZE || (SOC_NV_FLASH_SIZE - offset) < len) {
61 		return false;
62 	}
63 
64 	return true;
65 }
66 
flash_ambiq_read(const struct device * dev,off_t offset,void * data,size_t len)67 static int flash_ambiq_read(const struct device *dev, off_t offset, void *data, size_t len)
68 {
69 	ARG_UNUSED(dev);
70 
71 	if (!flash_ambiq_valid_range(offset, len)) {
72 		return -EINVAL;
73 	}
74 
75 	if (len == 0) {
76 		return 0;
77 	}
78 
79 	memcpy(data, (uint8_t *)(SOC_NV_FLASH_ADDR + offset), len);
80 
81 	return 0;
82 }
83 
flash_ambiq_write(const struct device * dev,off_t offset,const void * data,size_t len)84 static int flash_ambiq_write(const struct device *dev, off_t offset, const void *data, size_t len)
85 {
86 	ARG_UNUSED(dev);
87 
88 	int ret = 0;
89 	unsigned int key = 0;
90 	uint32_t aligned[FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t)] = {0};
91 	uint32_t *src = (uint32_t *)data;
92 
93 	/* write address must be block size aligned and the write length must be multiple of block
94 	 * size.
95 	 */
96 	if (!flash_ambiq_valid_range(offset, len) ||
97 	    ((uint32_t)offset & (FLASH_WRITE_BLOCK_SIZE - 1)) ||
98 	    (len & (FLASH_WRITE_BLOCK_SIZE - 1))) {
99 		return -EINVAL;
100 	}
101 
102 	if (len == 0) {
103 		return 0;
104 	}
105 
106 	FLASH_SEM_TAKE();
107 
108 	key = irq_lock();
109 
110 	for (int i = 0; i < len / FLASH_WRITE_BLOCK_SIZE; i++) {
111 		for (int j = 0; j < FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t); j++) {
112 			/* Make sure the source data is 4-byte aligned. */
113 			aligned[j] = UNALIGNED_GET((uint32_t *)src);
114 			src++;
115 		}
116 #if (CONFIG_SOC_SERIES_APOLLO4X)
117 		ret = am_hal_mram_main_program(
118 			AM_HAL_MRAM_PROGRAM_KEY, aligned,
119 			(uint32_t *)(SOC_NV_FLASH_ADDR + offset + i * FLASH_WRITE_BLOCK_SIZE),
120 			FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t));
121 #elif (CONFIG_SOC_SERIES_APOLLO3X)
122 		ret = am_hal_flash_program_main(
123 			AM_HAL_FLASH_PROGRAM_KEY, aligned,
124 			(uint32_t *)(SOC_NV_FLASH_ADDR + offset + i * FLASH_WRITE_BLOCK_SIZE),
125 			FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t));
126 #endif /* CONFIG_SOC_SERIES_APOLLO4X */
127 		if (ret) {
128 			break;
129 		}
130 	}
131 
132 	irq_unlock(key);
133 
134 	FLASH_SEM_GIVE();
135 
136 	return ret;
137 }
138 
flash_ambiq_erase(const struct device * dev,off_t offset,size_t len)139 static int flash_ambiq_erase(const struct device *dev, off_t offset, size_t len)
140 {
141 	ARG_UNUSED(dev);
142 
143 	int ret = 0;
144 
145 	if (!flash_ambiq_valid_range(offset, len)) {
146 		return -EINVAL;
147 	}
148 
149 	if (len == 0) {
150 		return 0;
151 	}
152 
153 #if (CONFIG_SOC_SERIES_APOLLO4X)
154 	/* The erase address and length alignment check will be done in HAL.*/
155 #elif (CONFIG_SOC_SERIES_APOLLO3X)
156 	if ((offset % FLASH_ERASE_BLOCK_SIZE) != 0) {
157 		LOG_ERR("offset 0x%lx is not on a page boundary", (long)offset);
158 		return -EINVAL;
159 	}
160 
161 	if ((len % FLASH_ERASE_BLOCK_SIZE) != 0) {
162 		LOG_ERR("len %zu is not multiple of a page size", len);
163 		return -EINVAL;
164 	}
165 #endif /* CONFIG_SOC_SERIES_APOLLO4X */
166 
167 	FLASH_SEM_TAKE();
168 
169 #if (CONFIG_SOC_SERIES_APOLLO4X)
170 	ret = am_hal_mram_main_fill(AM_HAL_MRAM_PROGRAM_KEY, FLASH_ERASE_WORD,
171 				    (uint32_t *)(SOC_NV_FLASH_ADDR + offset),
172 				    (len / sizeof(uint32_t)));
173 #elif (CONFIG_SOC_SERIES_APOLLO3X)
174 	unsigned int key = 0;
175 
176 	key = irq_lock();
177 
178 	ret = am_hal_flash_page_erase(
179 		AM_HAL_FLASH_PROGRAM_KEY,
180 		AM_HAL_FLASH_ADDR2INST(((uint32_t)SOC_NV_FLASH_ADDR + offset)),
181 		AM_HAL_FLASH_ADDR2PAGE(((uint32_t)SOC_NV_FLASH_ADDR + offset)));
182 
183 	irq_unlock(key);
184 #endif /* CONFIG_SOC_SERIES_APOLLO4X */
185 
186 	FLASH_SEM_GIVE();
187 
188 	return ret;
189 }
190 
flash_ambiq_get_parameters(const struct device * dev)191 static const struct flash_parameters *flash_ambiq_get_parameters(const struct device *dev)
192 {
193 	ARG_UNUSED(dev);
194 
195 	return &flash_ambiq_parameters;
196 }
197 
198 #if CONFIG_FLASH_PAGE_LAYOUT
199 static const struct flash_pages_layout pages_layout = {
200 	.pages_count = SOC_NV_FLASH_SIZE / FLASH_ERASE_BLOCK_SIZE,
201 	.pages_size = FLASH_ERASE_BLOCK_SIZE,
202 };
203 
flash_ambiq_pages_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)204 static void flash_ambiq_pages_layout(const struct device *dev,
205 				     const struct flash_pages_layout **layout, size_t *layout_size)
206 {
207 	ARG_UNUSED(dev);
208 
209 	*layout = &pages_layout;
210 	*layout_size = 1;
211 }
212 #endif /* CONFIG_FLASH_PAGE_LAYOUT */
213 
214 static DEVICE_API(flash, flash_ambiq_driver_api) = {
215 	.read = flash_ambiq_read,
216 	.write = flash_ambiq_write,
217 	.erase = flash_ambiq_erase,
218 	.get_parameters = flash_ambiq_get_parameters,
219 #ifdef CONFIG_FLASH_PAGE_LAYOUT
220 	.page_layout = flash_ambiq_pages_layout,
221 #endif
222 };
223 
flash_ambiq_init(const struct device * dev)224 static int flash_ambiq_init(const struct device *dev)
225 {
226 	ARG_UNUSED(dev);
227 
228 	FLASH_SEM_INIT();
229 
230 	return 0;
231 }
232 
233 DEVICE_DT_INST_DEFINE(0, flash_ambiq_init, NULL, NULL, NULL, POST_KERNEL,
234 		      CONFIG_FLASH_INIT_PRIORITY, &flash_ambiq_driver_api);
235