1 /*
2 * Copyright (c) 2016 Linaro Limited
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <string.h>
10 #include <zephyr/drivers/flash.h>
11 #include <errno.h>
12 #include <zephyr/init.h>
13 #include <soc.h>
14 #include <zephyr/sys/barrier.h>
15 #include "flash_priv.h"
16
17 #include "fsl_common.h"
18
19 #define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(flash_mcux);
22
23
24 #if DT_NODE_HAS_STATUS(DT_INST(0, nxp_kinetis_ftfa), okay)
25 #define DT_DRV_COMPAT nxp_kinetis_ftfa
26 #elif DT_NODE_HAS_STATUS(DT_INST(0, nxp_kinetis_ftfe), okay)
27 #define DT_DRV_COMPAT nxp_kinetis_ftfe
28 #elif DT_NODE_HAS_STATUS(DT_INST(0, nxp_kinetis_ftfl), okay)
29 #define DT_DRV_COMPAT nxp_kinetis_ftfl
30 #elif DT_NODE_HAS_STATUS(DT_INST(0, nxp_iap_fmc55), okay)
31 #define DT_DRV_COMPAT nxp_iap_fmc55
32 #define SOC_HAS_IAP 1
33 #elif DT_NODE_HAS_STATUS(DT_INST(0, nxp_iap_fmc553), okay)
34 #define DT_DRV_COMPAT nxp_iap_fmc553
35 #define SOC_HAS_IAP 1
36 #else
37 #error No matching compatible for soc_flash_mcux.c
38 #endif
39
40 #if defined(SOC_HAS_IAP) && !defined(CONFIG_SOC_LPC55S36)
41 #include "fsl_iap.h"
42 #else
43 #include "fsl_flash.h"
44 #endif /* SOC_HAS_IAP && !CONFIG_SOC_LPC55S36*/
45
46
47 #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
48
49 #if defined(CONFIG_CHECK_BEFORE_READING) && !defined(CONFIG_SOC_LPC55S36)
50 #define FMC_STATUS_FAIL FLASH_INT_CLR_ENABLE_FAIL_MASK
51 #define FMC_STATUS_ERR FLASH_INT_CLR_ENABLE_ERR_MASK
52 #define FMC_STATUS_DONE FLASH_INT_CLR_ENABLE_DONE_MASK
53 #define FMC_STATUS_ECC FLASH_INT_CLR_ENABLE_ECC_ERR_MASK
54
55 #define FMC_STATUS_FAILURES \
56 (FMC_STATUS_FAIL | FMC_STATUS_ERR | FMC_STATUS_ECC)
57
58 #define FMC_CMD_BLANK_CHECK 5
59 #define FMC_CMD_MARGIN_CHECK 6
60
61 /* Issue single command that uses an a start and stop address. */
get_cmd_status(uint32_t cmd,uint32_t addr,size_t len)62 static uint32_t get_cmd_status(uint32_t cmd, uint32_t addr, size_t len)
63 {
64 FLASH_Type *p_fmc = (FLASH_Type *)DT_INST_REG_ADDR(0);
65 uint32_t status;
66
67 /* issue low level command */
68 p_fmc->INT_CLR_STATUS = 0xF;
69 p_fmc->STARTA = (addr>>4) & 0x3FFFF;
70 p_fmc->STOPA = ((addr+len-1)>>4) & 0x3FFFF;
71 p_fmc->CMD = cmd;
72 barrier_dsync_fence_full();
73 barrier_isync_fence_full();
74
75 /* wait for command to be done */
76 while (!(p_fmc->INT_STATUS & FMC_STATUS_DONE))
77 ;
78
79 /* get read status and then clear it */
80 status = p_fmc->INT_STATUS;
81 p_fmc->INT_CLR_STATUS = 0xF;
82
83 return status;
84 }
85
86 /* This function prevents erroneous reading. Some ECC enabled devices will
87 * crash when reading an erased or wrongly programmed area.
88 */
is_area_readable(uint32_t addr,size_t len)89 static status_t is_area_readable(uint32_t addr, size_t len)
90 {
91 uint32_t key;
92 status_t status;
93
94 key = irq_lock();
95
96 /* Check if the are is correctly programmed and can be read. */
97 status = get_cmd_status(FMC_CMD_MARGIN_CHECK, addr, len);
98 if (status & FMC_STATUS_FAILURES) {
99 /* If the area was erased, ECC errors are triggered on read. */
100 status = get_cmd_status(FMC_CMD_BLANK_CHECK, addr, len);
101 if (!(status & FMC_STATUS_FAIL)) {
102 LOG_DBG("read request on erased addr:0x%08x size:%d",
103 addr, len);
104 irq_unlock(key);
105 return -ENODATA;
106 }
107 LOG_DBG("read request error for addr:0x%08x size:%d",
108 addr, len);
109 irq_unlock(key);
110 return -EIO;
111 }
112
113 irq_unlock(key);
114
115 return 0;
116 }
117 #endif /* CONFIG_CHECK_BEFORE_READING && ! CONFIG_SOC_LPC55S36 */
118
119 struct flash_priv {
120 flash_config_t config;
121 /*
122 * HACK: flash write protection is managed in software.
123 */
124 struct k_sem write_lock;
125 uint32_t pflash_block_base;
126 };
127
128 static const struct flash_parameters flash_mcux_parameters = {
129 #if DT_NODE_HAS_PROP(SOC_NV_FLASH_NODE, write_block_size)
130 .write_block_size = DT_PROP(SOC_NV_FLASH_NODE, write_block_size),
131 #else
132 .write_block_size = FSL_FEATURE_FLASH_PFLASH_BLOCK_WRITE_UNIT_SIZE,
133 #endif
134 .erase_value = 0xff,
135 };
136
137 /*
138 * Interrupt vectors could be executed from flash hence the need for locking.
139 * The underlying MCUX driver takes care of copying the functions to SRAM.
140 *
141 * For more information, see the application note below on Read-While-Write
142 * http://cache.freescale.com/files/32bit/doc/app_note/AN4695.pdf
143 *
144 */
145
flash_mcux_erase(const struct device * dev,off_t offset,size_t len)146 static int flash_mcux_erase(const struct device *dev, off_t offset,
147 size_t len)
148 {
149 struct flash_priv *priv = dev->data;
150 uint32_t addr;
151 status_t rc;
152 unsigned int key;
153
154 if (k_sem_take(&priv->write_lock, K_FOREVER)) {
155 return -EACCES;
156 }
157
158 addr = offset + priv->pflash_block_base;
159
160 key = irq_lock();
161 rc = FLASH_Erase(&priv->config, addr, len, kFLASH_ApiEraseKey);
162 irq_unlock(key);
163
164 k_sem_give(&priv->write_lock);
165
166 return (rc == kStatus_Success) ? 0 : -EINVAL;
167 }
168
169
170 /*
171 * @brief Read a flash memory area.
172 *
173 * @param dev Device struct
174 * @param offset The address's offset
175 * @param data The buffer to store or read the value
176 * @param length The size of the buffer
177 * @return 0 on success,
178 * -EIO for erroneous area
179 */
flash_mcux_read(const struct device * dev,off_t offset,void * data,size_t len)180 static int flash_mcux_read(const struct device *dev, off_t offset,
181 void *data, size_t len)
182 {
183 struct flash_priv *priv = dev->data;
184 uint32_t addr;
185 status_t rc = 0;
186
187 /*
188 * The MCUX supports different flash chips whose valid ranges are
189 * hidden below the API: until the API export these ranges, we can not
190 * do any generic validation
191 */
192 addr = offset + priv->pflash_block_base;
193
194 #ifdef CONFIG_CHECK_BEFORE_READING
195 #ifdef CONFIG_SOC_LPC55S36
196 /* Validates the given address range is loaded in the flash hiding region. */
197 rc = FLASH_IsFlashAreaReadable(&priv->config, addr, len);
198 if (rc != kStatus_FLASH_Success) {
199 rc = -EIO;
200 } else {
201 /* Check whether the flash is erased ("len" and "addr" must be word-aligned). */
202 rc = FLASH_VerifyErase(&priv->config, ((addr + 0x3) & ~0x3), ((len + 0x3) & ~0x3));
203 if (rc == kStatus_FLASH_Success) {
204 rc = -ENODATA;
205 } else {
206 rc = 0;
207 }
208 }
209 #else
210 rc = is_area_readable(addr, len);
211 #endif /* CONFIG_SOC_LPC55S36 */
212 #endif /* CONFIG_CHECK_BEFORE_READING */
213
214 if (!rc) {
215 memcpy(data, (void *) addr, len);
216 }
217 #ifdef CONFIG_CHECK_BEFORE_READING
218 else if (rc == -ENODATA) {
219 /* Erased area, return dummy data as an erased page. */
220 memset(data, 0xFF, len);
221 rc = 0;
222 }
223 #endif
224 return rc;
225 }
226
flash_mcux_write(const struct device * dev,off_t offset,const void * data,size_t len)227 static int flash_mcux_write(const struct device *dev, off_t offset,
228 const void *data, size_t len)
229 {
230 struct flash_priv *priv = dev->data;
231 uint32_t addr;
232 status_t rc;
233 unsigned int key;
234
235 if (k_sem_take(&priv->write_lock, K_FOREVER)) {
236 return -EACCES;
237 }
238
239 addr = offset + priv->pflash_block_base;
240
241 key = irq_lock();
242 rc = FLASH_Program(&priv->config, addr, (uint8_t *) data, len);
243 irq_unlock(key);
244
245 k_sem_give(&priv->write_lock);
246
247 return (rc == kStatus_Success) ? 0 : -EINVAL;
248 }
249
250 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
251 static const struct flash_pages_layout dev_layout = {
252 .pages_count = DT_REG_SIZE(SOC_NV_FLASH_NODE) /
253 DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
254 .pages_size = DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
255 };
256
flash_mcux_pages_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)257 static void flash_mcux_pages_layout(const struct device *dev,
258 const struct flash_pages_layout **layout,
259 size_t *layout_size)
260 {
261 *layout = &dev_layout;
262 *layout_size = 1;
263 }
264 #endif /* CONFIG_FLASH_PAGE_LAYOUT */
265
266 static const struct flash_parameters *
flash_mcux_get_parameters(const struct device * dev)267 flash_mcux_get_parameters(const struct device *dev)
268 {
269 ARG_UNUSED(dev);
270
271 return &flash_mcux_parameters;
272 }
273
274 static struct flash_priv flash_data;
275
276 static const struct flash_driver_api flash_mcux_api = {
277 .erase = flash_mcux_erase,
278 .write = flash_mcux_write,
279 .read = flash_mcux_read,
280 .get_parameters = flash_mcux_get_parameters,
281 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
282 .page_layout = flash_mcux_pages_layout,
283 #endif
284 };
285
flash_mcux_init(const struct device * dev)286 static int flash_mcux_init(const struct device *dev)
287 {
288 struct flash_priv *priv = dev->data;
289 uint32_t pflash_block_base;
290 status_t rc;
291
292 k_sem_init(&priv->write_lock, 1, 1);
293
294 rc = FLASH_Init(&priv->config);
295
296 #ifdef SOC_HAS_IAP
297 FLASH_GetProperty(&priv->config, kFLASH_PropertyPflashBlockBaseAddr,
298 &pflash_block_base);
299 #else
300 FLASH_GetProperty(&priv->config, kFLASH_PropertyPflash0BlockBaseAddr,
301 &pflash_block_base);
302 #endif
303 priv->pflash_block_base = (uint32_t) pflash_block_base;
304
305 return (rc == kStatus_Success) ? 0 : -EIO;
306 }
307
308 DEVICE_DT_INST_DEFINE(0, flash_mcux_init, NULL,
309 &flash_data, NULL, POST_KERNEL,
310 CONFIG_FLASH_INIT_PRIORITY, &flash_mcux_api);
311