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