1 /*
2 * Copyright (c) 2018, Piotr Mienkowski
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT silabs_gecko_flash_controller
8 #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
9
10 #include <stddef.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/device.h>
15 #include <em_msc.h>
16 #include <zephyr/drivers/flash.h>
17 #include <soc.h>
18
19 #define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(flash_gecko);
22
23 struct flash_gecko_data {
24 struct k_sem mutex;
25 };
26
27
28 static const struct flash_parameters flash_gecko_parameters = {
29 .write_block_size = DT_PROP(SOC_NV_FLASH_NODE, write_block_size),
30 .erase_value = 0xff,
31 };
32
33 static bool write_range_is_valid(off_t offset, uint32_t size);
34 static bool read_range_is_valid(off_t offset, uint32_t size);
35 static int erase_flash_block(off_t offset, size_t size);
36 static void flash_gecko_write_protection(bool enable);
37
flash_gecko_read(const struct device * dev,off_t offset,void * data,size_t size)38 static int flash_gecko_read(const struct device *dev, off_t offset,
39 void *data,
40 size_t size)
41 {
42 if (!read_range_is_valid(offset, size)) {
43 return -EINVAL;
44 }
45
46 if (!size) {
47 return 0;
48 }
49
50 memcpy(data, (uint8_t *)CONFIG_FLASH_BASE_ADDRESS + offset, size);
51
52 return 0;
53 }
54
flash_gecko_write(const struct device * dev,off_t offset,const void * data,size_t size)55 static int flash_gecko_write(const struct device *dev, off_t offset,
56 const void *data, size_t size)
57 {
58 struct flash_gecko_data *const dev_data = dev->data;
59 MSC_Status_TypeDef msc_ret;
60 void *address;
61 int ret = 0;
62
63 if (!write_range_is_valid(offset, size)) {
64 return -EINVAL;
65 }
66
67 if (!size) {
68 return 0;
69 }
70
71 k_sem_take(&dev_data->mutex, K_FOREVER);
72 flash_gecko_write_protection(false);
73
74 address = (uint8_t *)CONFIG_FLASH_BASE_ADDRESS + offset;
75 msc_ret = MSC_WriteWord(address, data, size);
76 if (msc_ret < 0) {
77 ret = -EIO;
78 }
79
80 flash_gecko_write_protection(true);
81 k_sem_give(&dev_data->mutex);
82
83 return ret;
84 }
85
flash_gecko_erase(const struct device * dev,off_t offset,size_t size)86 static int flash_gecko_erase(const struct device *dev, off_t offset,
87 size_t size)
88 {
89 struct flash_gecko_data *const dev_data = dev->data;
90 int ret;
91
92 if (!read_range_is_valid(offset, size)) {
93 return -EINVAL;
94 }
95
96 if ((offset % FLASH_PAGE_SIZE) != 0) {
97 LOG_ERR("offset 0x%lx: not on a page boundary", (long)offset);
98 return -EINVAL;
99 }
100
101 if ((size % FLASH_PAGE_SIZE) != 0) {
102 LOG_ERR("size %zu: not multiple of a page size", size);
103 return -EINVAL;
104 }
105
106 if (!size) {
107 return 0;
108 }
109
110 k_sem_take(&dev_data->mutex, K_FOREVER);
111 flash_gecko_write_protection(false);
112
113 ret = erase_flash_block(offset, size);
114
115 flash_gecko_write_protection(true);
116 k_sem_give(&dev_data->mutex);
117
118 return ret;
119 }
120
flash_gecko_write_protection(bool enable)121 static void flash_gecko_write_protection(bool enable)
122 {
123 if (enable) {
124 /* Lock the MSC module. */
125 MSC->LOCK = 0;
126 } else {
127 /* Unlock the MSC module. */
128 #if defined(MSC_LOCK_LOCKKEY_UNLOCK)
129 MSC->LOCK = MSC_LOCK_LOCKKEY_UNLOCK;
130 #else
131 MSC->LOCK = MSC_UNLOCK_CODE;
132 #endif
133 }
134 }
135
136 /* Note:
137 * - A flash address to write to must be aligned to words.
138 * - Number of bytes to write must be divisible by 4.
139 */
write_range_is_valid(off_t offset,uint32_t size)140 static bool write_range_is_valid(off_t offset, uint32_t size)
141 {
142 return read_range_is_valid(offset, size)
143 && (offset % sizeof(uint32_t) == 0)
144 && (size % 4 == 0U);
145 }
146
read_range_is_valid(off_t offset,uint32_t size)147 static bool read_range_is_valid(off_t offset, uint32_t size)
148 {
149 return (offset + size) <= (CONFIG_FLASH_SIZE * 1024);
150 }
151
erase_flash_block(off_t offset,size_t size)152 static int erase_flash_block(off_t offset, size_t size)
153 {
154 MSC_Status_TypeDef msc_ret;
155 void *address;
156 int ret = 0;
157
158 for (off_t tmp = offset; tmp < offset + size; tmp += FLASH_PAGE_SIZE) {
159 address = (uint8_t *)CONFIG_FLASH_BASE_ADDRESS + tmp;
160 msc_ret = MSC_ErasePage(address);
161 if (msc_ret < 0) {
162 ret = -EIO;
163 break;
164 }
165 }
166
167 return ret;
168 }
169
170 #if CONFIG_FLASH_PAGE_LAYOUT
171 static const struct flash_pages_layout flash_gecko_0_pages_layout = {
172 .pages_count = DT_REG_SIZE(SOC_NV_FLASH_NODE) /
173 DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
174 .pages_size = DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
175 };
176
flash_gecko_page_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)177 void flash_gecko_page_layout(const struct device *dev,
178 const struct flash_pages_layout **layout,
179 size_t *layout_size)
180 {
181 *layout = &flash_gecko_0_pages_layout;
182 *layout_size = 1;
183 }
184 #endif /* CONFIG_FLASH_PAGE_LAYOUT */
185
186 static const struct flash_parameters *
flash_gecko_get_parameters(const struct device * dev)187 flash_gecko_get_parameters(const struct device *dev)
188 {
189 ARG_UNUSED(dev);
190
191 return &flash_gecko_parameters;
192 }
193
flash_gecko_init(const struct device * dev)194 static int flash_gecko_init(const struct device *dev)
195 {
196 struct flash_gecko_data *const dev_data = dev->data;
197
198 k_sem_init(&dev_data->mutex, 1, 1);
199
200 MSC_Init();
201
202 /* Lock the MSC module. */
203 MSC->LOCK = 0;
204
205 LOG_INF("Device %s initialized", dev->name);
206
207 return 0;
208 }
209
210 static const struct flash_driver_api flash_gecko_driver_api = {
211 .read = flash_gecko_read,
212 .write = flash_gecko_write,
213 .erase = flash_gecko_erase,
214 .get_parameters = flash_gecko_get_parameters,
215 #ifdef CONFIG_FLASH_PAGE_LAYOUT
216 .page_layout = flash_gecko_page_layout,
217 #endif
218 };
219
220 static struct flash_gecko_data flash_gecko_0_data;
221
222 DEVICE_DT_INST_DEFINE(0, flash_gecko_init, NULL,
223 &flash_gecko_0_data, NULL, POST_KERNEL,
224 CONFIG_FLASH_INIT_PRIORITY, &flash_gecko_driver_api);
225