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