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