1 /*
2 * Copyright 2022 NXP
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <stdbool.h>
8
9 #include "mflash_drv.h"
10
11 #include "fsl_flexspi.h"
12 #include "fsl_cache.h"
13 #include "pin_mux.h"
14
15 #define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL 2
16 #define NOR_CMD_LUT_SEQ_IDX_READ_FAST 1
17 #define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD 0 // set it to index0 to align with xip settings
18 #define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE 4
19 #define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5
20 #define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE 6
21 #define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD 7
22 #define NOR_CMD_LUT_SEQ_IDX_READID 8
23 #define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG 9
24 #define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG 12
25
26 #define CUSTOM_LUT_LENGTH 60
27 #define FLASH_BUSY_STATUS_POL 1
28 #define FLASH_BUSY_STATUS_OFFSET 0
29
30 #define FLASH_SIZE 0x10000
31
32 #ifndef XIP_EXTERNAL_FLASH
33 flexspi_device_config_t deviceconfig = {
34 .flexspiRootClk = 100000000,
35 .flashSize = FLASH_SIZE,
36 .CSIntervalUnit = kFLEXSPI_CsIntervalUnit1SckCycle,
37 .CSInterval = 2,
38 .CSHoldTime = 3,
39 .CSSetupTime = 3,
40 .dataValidTime = 0,
41 .columnspace = 0,
42 .enableWordAddress = 0,
43 .AWRSeqIndex = 0,
44 .AWRSeqNumber = 0,
45 .ARDSeqIndex = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD,
46 .ARDSeqNumber = 1,
47 .AHBWriteWaitUnit = kFLEXSPI_AhbWriteWaitUnit2AhbCycle,
48 .AHBWriteWaitInterval = 0,
49 };
50 #endif
51
52 static uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
53 /* Normal read mode -SDR */
54 [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] =
55 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x13, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x20),
56 [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] =
57 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
58
59 /* Fast read mode - SDR */
60 [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST] =
61 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x0C, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x20),
62 [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = FLEXSPI_LUT_SEQ(
63 kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x08, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
64
65 /* Fast read quad mode - SDR */
66 [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] =
67 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEC, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x20),
68 [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
69 kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),
70
71 /* Write Enable */
72 [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
73 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
74
75 /* Erase Sector */
76 [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] =
77 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x21, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x20),
78
79 /* Page Program - single mode */
80 [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] =
81 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x12, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x20),
82 [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] =
83 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
84
85 /* Page Program - quad mode */
86 [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
87 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x34, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x20),
88 [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] =
89 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
90
91 /* Read ID */
92 [4 * NOR_CMD_LUT_SEQ_IDX_READID] =
93 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xAB, kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x18),
94 [4 * NOR_CMD_LUT_SEQ_IDX_READID + 1] =
95 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
96
97 /* Enable Quad mode */
98 [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
99 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x01, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),
100
101 /* Read status register */
102 [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =
103 FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
104 };
105
flexspi_nor_wait_bus_busy(FLEXSPI_Type * base)106 static status_t flexspi_nor_wait_bus_busy(FLEXSPI_Type *base)
107 {
108 /* Wait status ready. */
109 bool isBusy;
110 uint32_t readValue;
111 status_t status;
112 flexspi_transfer_t flashXfer;
113
114 flashXfer.deviceAddress = 0;
115 flashXfer.port = kFLEXSPI_PortA1;
116 flashXfer.cmdType = kFLEXSPI_Read;
117 flashXfer.SeqNumber = 1;
118 flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READSTATUSREG;
119 flashXfer.data = &readValue;
120 flashXfer.dataSize = 1;
121
122 do
123 {
124 status = FLEXSPI_TransferBlocking(base, &flashXfer);
125
126 if (status != kStatus_Success)
127 {
128 return status;
129 }
130 if (FLASH_BUSY_STATUS_POL)
131 {
132 if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET))
133 {
134 isBusy = true;
135 }
136 else
137 {
138 isBusy = false;
139 }
140 }
141 else
142 {
143 if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET))
144 {
145 isBusy = false;
146 }
147 else
148 {
149 isBusy = true;
150 }
151 }
152
153 } while (isBusy);
154
155 return status;
156 }
157
flexspi_nor_write_enable(FLEXSPI_Type * base,uint32_t baseAddr)158 static status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr)
159 {
160 flexspi_transfer_t flashXfer;
161 status_t status;
162
163 /* Write neable */
164 flashXfer.deviceAddress = baseAddr;
165 flashXfer.port = kFLEXSPI_PortA1;
166 flashXfer.cmdType = kFLEXSPI_Command;
167 flashXfer.SeqNumber = 1;
168 flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE;
169
170 status = FLEXSPI_TransferBlocking(base, &flashXfer);
171
172 return status;
173 }
174
flexspi_nor_enable_quad_mode(FLEXSPI_Type * base)175 status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base)
176 {
177 flexspi_transfer_t flashXfer;
178 status_t status;
179 uint32_t writeValue = 0x40;
180
181 /* Write neable */
182 status = flexspi_nor_write_enable(base, 0);
183
184 if (status != kStatus_Success)
185 {
186 return status;
187 }
188
189 /* Enable quad mode. */
190 flashXfer.deviceAddress = 0;
191 flashXfer.port = kFLEXSPI_PortA1;
192 flashXfer.cmdType = kFLEXSPI_Write;
193 flashXfer.SeqNumber = 1;
194 flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG;
195 flashXfer.data = &writeValue;
196 flashXfer.dataSize = 1;
197
198 status = FLEXSPI_TransferBlocking(base, &flashXfer);
199 if (status != kStatus_Success)
200 {
201 return status;
202 }
203
204 status = flexspi_nor_wait_bus_busy(base);
205
206 return status;
207 }
208
flexspi_nor_flash_sector_erase(FLEXSPI_Type * base,uint32_t address)209 static status_t flexspi_nor_flash_sector_erase(FLEXSPI_Type *base, uint32_t address)
210 {
211 status_t status;
212 flexspi_transfer_t flashXfer;
213
214 /* Write enable */
215 status = flexspi_nor_write_enable(base, 0);
216
217 if (status != kStatus_Success)
218 {
219 return status;
220 }
221
222 flashXfer.deviceAddress = address;
223 flashXfer.port = kFLEXSPI_PortA1;
224 flashXfer.cmdType = kFLEXSPI_Command;
225 flashXfer.SeqNumber = 1;
226 flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASESECTOR;
227 status = FLEXSPI_TransferBlocking(base, &flashXfer);
228
229 if (status != kStatus_Success)
230 {
231 return status;
232 }
233
234 status = flexspi_nor_wait_bus_busy(base);
235
236 return status;
237 }
238
flexspi_nor_flash_page_program(FLEXSPI_Type * base,uint32_t address,const uint32_t * src)239 static status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src)
240 {
241 status_t status;
242 flexspi_transfer_t flashXfer;
243
244 /* Write enable */
245 status = flexspi_nor_write_enable(base, address);
246
247 if (status != kStatus_Success)
248 {
249 return status;
250 }
251
252 /* Prepare page program command */
253 flashXfer.deviceAddress = address;
254 flashXfer.port = kFLEXSPI_PortA1;
255 flashXfer.cmdType = kFLEXSPI_Write;
256 flashXfer.SeqNumber = 1;
257 flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD;
258 flashXfer.data = (uint32_t *)src;
259 flashXfer.dataSize = MFLASH_PAGE_SIZE;
260 status = FLEXSPI_TransferBlocking(base, &flashXfer);
261
262 if (status != kStatus_Success)
263 {
264 return status;
265 }
266
267 status = flexspi_nor_wait_bus_busy(base);
268
269 return status;
270 }
271
flexspi_nor_read_data(FLEXSPI_Type * base,uint32_t startAddress,uint32_t * buffer,uint32_t length)272 static status_t flexspi_nor_read_data(FLEXSPI_Type *base, uint32_t startAddress, uint32_t *buffer, uint32_t length)
273 {
274 status_t status;
275 flexspi_transfer_t flashXfer;
276 uint32_t readAddress = startAddress;
277
278 /* Read page. */
279 flashXfer.deviceAddress = readAddress;
280 flashXfer.port = kFLEXSPI_PortA1;
281 flashXfer.cmdType = kFLEXSPI_Read;
282 flashXfer.SeqNumber = 1;
283 flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD;
284 flashXfer.data = buffer;
285 flashXfer.dataSize = length;
286
287 status = FLEXSPI_TransferBlocking(base, &flashXfer);
288
289 return status;
290 }
291
292 /* Initialize flash peripheral,
293 * cannot be invoked directly, requires calling wrapper in non XIP memory */
mflash_drv_init_internal(void)294 static int32_t mflash_drv_init_internal(void)
295 {
296 status_t status = kStatus_Success;
297
298 /* NOTE: Multithread access is not supported for SRAM target.
299 * XIP target MUST be protected by disabling global interrupts
300 * since all ISR (and API that is used inside) is placed at XIP.
301 * It is necessary to place at least "mflash_drv.o", "fsl_flexspi.o" to SRAM */
302 /* disable interrupts when running from XIP */
303 uint32_t primask = __get_PRIMASK();
304
305 __asm("cpsid i");
306
307 #ifndef XIP_EXTERNAL_FLASH
308 flexspi_config_t config;
309
310 /* Get FLEXSPI default settings and configure the flexspi. */
311 FLEXSPI_GetDefaultConfig(&config);
312
313 /* Set AHB buffer size for reading data through AHB bus. */
314 config.ahbConfig.enableAHBPrefetch = true;
315 config.ahbConfig.enableAHBBufferable = true;
316 config.ahbConfig.enableAHBCachable = true;
317 config.rxSampleClock = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad;
318 FLEXSPI_Init(MFLASH_FLEXSPI, &config);
319
320 /* AHB Read Address option bit. This option bit is intend to remove AHB burst start address alignment limitation */
321 MFLASH_FLEXSPI->AHBCR |= FLEXSPI_AHBCR_READADDROPT_MASK;
322
323 /* Configure flash settings according to serial flash feature. */
324 FLEXSPI_SetFlashConfig(MFLASH_FLEXSPI, &deviceconfig, kFLEXSPI_PortA1);
325 #endif
326
327 /* Update LUT table. */
328 FLEXSPI_UpdateLUT(MFLASH_FLEXSPI, 0, customLUT, CUSTOM_LUT_LENGTH);
329
330 #ifndef XIP_EXTERNAL_FLASH
331 /* Enter quad mode. */
332 status = flexspi_nor_enable_quad_mode(MFLASH_FLEXSPI);
333 #endif
334
335 /* Do software reset. */
336 FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
337
338 if (primask == 0)
339 {
340 __asm("cpsie i");
341 }
342
343 return status;
344 }
345
346 /* API - initialize 'mflash' */
mflash_drv_init(void)347 int32_t mflash_drv_init(void)
348 {
349 /* Necessary to have double wrapper call in non_xip memory */
350 return mflash_drv_init_internal();
351 }
352
353 /* Internal - erase single sector */
mflash_drv_sector_erase_internal(uint32_t sector_addr)354 static int32_t mflash_drv_sector_erase_internal(uint32_t sector_addr)
355 {
356 status_t status;
357 uint32_t primask = __get_PRIMASK();
358
359 __asm("cpsid i");
360
361 status = flexspi_nor_flash_sector_erase(MFLASH_FLEXSPI, sector_addr);
362
363 /* Do software reset. */
364 FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
365
366 DCACHE_InvalidateByRange(MFLASH_BASE_ADDRESS + sector_addr, MFLASH_SECTOR_SIZE);
367
368 if (primask == 0)
369 {
370 __asm("cpsie i");
371 }
372
373 /* Flush pipeline to allow pending interrupts take place
374 * before starting next loop */
375 __ISB();
376
377 return status;
378 }
379
380 /* Calling wrapper for 'mflash_drv_sector_erase_internal'.
381 * Erase one sector starting at 'sector_addr' - must be sector aligned.
382 */
mflash_drv_sector_erase(uint32_t sector_addr)383 int32_t mflash_drv_sector_erase(uint32_t sector_addr)
384 {
385 if (0 == mflash_drv_is_sector_aligned(sector_addr))
386 return kStatus_InvalidArgument;
387
388 return mflash_drv_sector_erase_internal(sector_addr);
389 }
390
391 /* Internal - write single page */
mflash_drv_page_program_internal(uint32_t page_addr,uint32_t * data)392 static int32_t mflash_drv_page_program_internal(uint32_t page_addr, uint32_t *data)
393 {
394 uint32_t primask = __get_PRIMASK();
395
396 __asm("cpsid i");
397
398 status_t status;
399 status = flexspi_nor_flash_page_program(MFLASH_FLEXSPI, page_addr, data);
400
401 /* Do software reset. */
402 FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
403
404 DCACHE_InvalidateByRange(MFLASH_BASE_ADDRESS + page_addr, MFLASH_PAGE_SIZE);
405
406 if (primask == 0)
407 {
408 __asm("cpsie i");
409 }
410
411 /* Flush pipeline to allow pending interrupts take place
412 * before starting next loop */
413 __ISB();
414
415 return status;
416 }
417
418 /* Calling wrapper for 'mflash_drv_page_program_internal'.
419 * Write 'data' to 'page_addr' - must be page aligned.
420 * NOTE: Don't try to store constant data that are located in XIP !!
421 */
mflash_drv_page_program(uint32_t page_addr,uint32_t * data)422 int32_t mflash_drv_page_program(uint32_t page_addr, uint32_t *data)
423 {
424 if (0 == mflash_drv_is_page_aligned(page_addr))
425 return kStatus_InvalidArgument;
426
427 return mflash_drv_page_program_internal(page_addr, data);
428 }
429
430 /* Internal - read data */
mflash_drv_read_internal(uint32_t addr,uint32_t * buffer,uint32_t len)431 static int32_t mflash_drv_read_internal(uint32_t addr, uint32_t *buffer, uint32_t len)
432 {
433 uint32_t primask = __get_PRIMASK();
434
435 __asm("cpsid i");
436
437 status_t status;
438 status = flexspi_nor_read_data(MFLASH_FLEXSPI, addr, buffer, len);
439
440 /* Do software reset. */
441 FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
442
443 if (primask == 0)
444 {
445 __asm("cpsie i");
446 }
447
448 /* Flush pipeline to allow pending interrupts take place
449 * before starting next loop */
450 __ISB();
451
452 return status;
453 }
454
455 /* API - Read data */
mflash_drv_read(uint32_t addr,uint32_t * buffer,uint32_t len)456 int32_t mflash_drv_read(uint32_t addr, uint32_t *buffer, uint32_t len)
457 {
458 /* Check alignment */
459 if (((uint32_t)buffer % 4) || (len % 4))
460 return kStatus_InvalidArgument;
461
462 return mflash_drv_read_internal(addr, buffer, len);
463 }
464
465 /* API - Get pointer to FLASH region */
mflash_drv_phys2log(uint32_t addr,uint32_t len)466 void *mflash_drv_phys2log(uint32_t addr, uint32_t len)
467 {
468 return (void *)(addr + MFLASH_BASE_ADDRESS);
469 }
470
471 /* API - Get pointer to FLASH region */
mflash_drv_log2phys(void * ptr,uint32_t len)472 uint32_t mflash_drv_log2phys(void *ptr, uint32_t len)
473 {
474 if ((uint32_t)ptr < MFLASH_BASE_ADDRESS)
475 return kStatus_InvalidArgument;
476
477 return ((uint32_t)ptr - MFLASH_BASE_ADDRESS);
478 }
479