1 /*
2  * Copyright 2019-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 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