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 0x4000
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 if (primask == 0)
351 {
352 __asm("cpsie i");
353 }
354
355 return status;
356 }
357
358 /* API - initialize 'mflash' */
mflash_drv_init(void)359 int32_t mflash_drv_init(void)
360 {
361 /* Necessary to have double wrapper call in non_xip memory */
362 return mflash_drv_init_internal();
363 }
364
365 /* Internal - erase single sector */
mflash_drv_sector_erase_internal(uint32_t sector_addr)366 static int32_t mflash_drv_sector_erase_internal(uint32_t sector_addr)
367 {
368 status_t status;
369 uint32_t primask = __get_PRIMASK();
370
371 __asm("cpsid i");
372
373 status = flexspi_nor_flash_sector_erase(MFLASH_FLEXSPI, sector_addr);
374
375 /* Do software reset. */
376 FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
377
378 DCACHE_InvalidateByRange(MFLASH_BASE_ADDRESS + sector_addr, MFLASH_SECTOR_SIZE);
379
380 if (primask == 0)
381 {
382 __asm("cpsie i");
383 }
384
385 /* Flush pipeline to allow pending interrupts take place
386 * before starting next loop */
387 __ISB();
388
389 return status;
390 }
391
392 /* Calling wrapper for 'mflash_drv_sector_erase_internal'.
393 * Erase one sector starting at 'sector_addr' - must be sector aligned.
394 */
mflash_drv_sector_erase(uint32_t sector_addr)395 int32_t mflash_drv_sector_erase(uint32_t sector_addr)
396 {
397 if (0 == mflash_drv_is_sector_aligned(sector_addr))
398 return kStatus_InvalidArgument;
399
400 return mflash_drv_sector_erase_internal(sector_addr);
401 }
402
403 /* Internal - write single page */
mflash_drv_page_program_internal(uint32_t page_addr,uint32_t * data)404 static int32_t mflash_drv_page_program_internal(uint32_t page_addr, uint32_t *data)
405 {
406 uint32_t primask = __get_PRIMASK();
407
408 __asm("cpsid i");
409
410 status_t status;
411 status = flexspi_nor_flash_page_program(MFLASH_FLEXSPI, page_addr, data);
412
413 /* Do software reset. */
414 FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
415
416 DCACHE_InvalidateByRange(MFLASH_BASE_ADDRESS + page_addr, MFLASH_PAGE_SIZE);
417
418 if (primask == 0)
419 {
420 __asm("cpsie i");
421 }
422
423 /* Flush pipeline to allow pending interrupts take place
424 * before starting next loop */
425 __ISB();
426
427 return status;
428 }
429
430 /* Calling wrapper for 'mflash_drv_page_program_internal'.
431 * Write 'data' to 'page_addr' - must be page aligned.
432 * NOTE: Don't try to store constant data that are located in XIP !!
433 */
mflash_drv_page_program(uint32_t page_addr,uint32_t * data)434 int32_t mflash_drv_page_program(uint32_t page_addr, uint32_t *data)
435 {
436 if (0 == mflash_drv_is_page_aligned(page_addr))
437 return kStatus_InvalidArgument;
438
439 return mflash_drv_page_program_internal(page_addr, data);
440 }
441
442 /* Internal - read data */
mflash_drv_read_internal(uint32_t addr,uint32_t * buffer,uint32_t len)443 static int32_t mflash_drv_read_internal(uint32_t addr, uint32_t *buffer, uint32_t len)
444 {
445 uint32_t primask = __get_PRIMASK();
446
447 __asm("cpsid i");
448
449 status_t status;
450 status = flexspi_nor_read_data(MFLASH_FLEXSPI, addr, buffer, len);
451
452 /* Do software reset. */
453 FLEXSPI_SoftwareReset(MFLASH_FLEXSPI);
454
455 if (primask == 0)
456 {
457 __asm("cpsie i");
458 }
459
460 /* Flush pipeline to allow pending interrupts take place
461 * before starting next loop */
462 __ISB();
463
464 return status;
465 }
466
467 /* API - Read data */
mflash_drv_read(uint32_t addr,uint32_t * buffer,uint32_t len)468 int32_t mflash_drv_read(uint32_t addr, uint32_t *buffer, uint32_t len)
469 {
470 /* Check alignment */
471 if (((uint32_t)buffer % 4) || (len % 4))
472 return kStatus_InvalidArgument;
473
474 return mflash_drv_read_internal(addr, buffer, len);
475 }
476
477 /* API - Get pointer to FLASH region */
mflash_drv_phys2log(uint32_t addr,uint32_t len)478 void *mflash_drv_phys2log(uint32_t addr, uint32_t len)
479 {
480 return (void *)(addr + MFLASH_BASE_ADDRESS);
481 }
482
483 /* API - Get pointer to FLASH region */
mflash_drv_log2phys(void * ptr,uint32_t len)484 uint32_t mflash_drv_log2phys(void *ptr, uint32_t len)
485 {
486 if ((uint32_t)ptr < MFLASH_BASE_ADDRESS)
487 return kStatus_InvalidArgument;
488
489 return ((uint32_t)ptr - MFLASH_BASE_ADDRESS);
490 }
491