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