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