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