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_spifi.h"
12 #include "pin_mux.h"
13 
14 /* Command ID */
15 #define COMMAND_NUM    (6)
16 #define READ           (0)
17 #define PROGRAM_PAGE   (1)
18 #define GET_STATUS     (2)
19 #define ERASE_SECTOR   (3)
20 #define WRITE_ENABLE   (4)
21 #define WRITE_REGISTER (5)
22 
23 /* Commands definition, taken from SPIFI demo */
24 static spifi_command_t command[COMMAND_NUM] = {
25     /* read */
26     {MFLASH_PAGE_SIZE, false, kSPIFI_DataInput, 1, kSPIFI_CommandAllSerial, kSPIFI_CommandOpcodeAddrThreeBytes, 0x0B},
27     /* program */
28     {MFLASH_PAGE_SIZE, false, kSPIFI_DataOutput, 0, kSPIFI_CommandAllSerial, kSPIFI_CommandOpcodeAddrThreeBytes, 0x2},
29     /* status */
30     {1, false, kSPIFI_DataInput, 0, kSPIFI_CommandAllSerial, kSPIFI_CommandOpcodeOnly, 0x05},
31     /* erase */
32     {0, false, kSPIFI_DataOutput, 0, kSPIFI_CommandAllSerial, kSPIFI_CommandOpcodeAddrThreeBytes, 0x20},
33     /* write enable */
34     {0, false, kSPIFI_DataOutput, 0, kSPIFI_CommandAllSerial, kSPIFI_CommandOpcodeOnly, 0x06},
35     /* write register */
36     {4, false, kSPIFI_DataOutput, 0, kSPIFI_CommandAllSerial, kSPIFI_CommandOpcodeOnly, 0x01}};
37 
38 /* Wait until command finishes */
mflash_drv_check_if_finish(void)39 static inline void mflash_drv_check_if_finish(void)
40 {
41     uint8_t val = 0;
42     do
43     {
44         SPIFI_SetCommand(MFLASH_SPIFI, &command[GET_STATUS]);
45         while ((MFLASH_SPIFI->STAT & SPIFI_STAT_INTRQ_MASK) == 0U)
46         {
47         }
48         val = SPIFI_ReadDataByte(MFLASH_SPIFI);
49     } while (val & 0x1);
50 }
51 
mflash_drv_read_mode(void)52 static void mflash_drv_read_mode(void)
53 {
54     /* Switch back to read mode */
55     SPIFI_ResetCommand(MFLASH_SPIFI);
56     SPIFI_SetMemoryCommand(MFLASH_SPIFI, &command[READ]);
57 }
58 
59 /* Initialize SPIFI & flash peripheral,
60  * cannot be invoked directly, requires calling wrapper in non XIP memory */
mflash_drv_init_internal(void)61 static int32_t mflash_drv_init_internal(void)
62 {
63     /* NOTE: Multithread access is not supported for SRAM target.
64      *       XIP target MUST be protected by disabling global interrupts
65      *       since all ISR (and API that is used inside) is placed at XIP.
66      *       It is necessary to place at least "mflash_drv_drv.o", "fsl_spifi.o" to SRAM */
67     /* disable interrupts when running from XIP
68      * TODO: store/restore previous PRIMASK on stack to avoid
69      * failure in case of nested critical sections !! */
70     uint32_t primask = __get_PRIMASK();
71 
72     __asm("cpsid i");
73 
74     spifi_config_t config = {0};
75 
76 #ifndef XIP_IMAGE
77     uint32_t sourceClockFreq;
78 #if 0 /* Pinmuxing shall be initialized during board startup */
79     BOARD_InitSPIFI();
80 #endif
81     /* Reset peripheral */
82     RESET_PeripheralReset(kSPIFI_RST_SHIFT_RSTn);
83     /* Set SPIFI clock source */
84     CLOCK_AttachClk(kFRO_HF_to_SPIFI_CLK);
85     sourceClockFreq = CLOCK_GetSpifiClkFreq();
86     /* Set the clock divider */
87     CLOCK_SetClkDiv(kCLOCK_DivSpifiClk, sourceClockFreq / MFLASH_BAUDRATE, false);
88     /* Enable SPIFI clock */
89     CLOCK_EnableClock(kCLOCK_Spifi);
90 #endif
91 
92     SPIFI_GetDefaultConfig(&config);
93     config.dualMode = kSPIFI_DualMode;
94 #ifdef XIP_IMAGE
95     config.disablePrefetch     = false; // true;
96     config.disableCachePrefech = false; // true;
97 #else
98     config.disablePrefetch     = false; // true;
99     config.disableCachePrefech = false; // true;
100 #endif
101 
102     /* Reset the Command register */
103     SPIFI_ResetCommand(MFLASH_SPIFI);
104 
105     /* Set time delay parameter */
106     MFLASH_SPIFI->CTRL = SPIFI_CTRL_TIMEOUT(config.timeout) | SPIFI_CTRL_CSHIGH(config.csHighTime) |
107                          SPIFI_CTRL_D_PRFTCH_DIS(config.disablePrefetch) | SPIFI_CTRL_MODE3(config.spiMode) |
108                          SPIFI_CTRL_PRFTCH_DIS(config.disableCachePrefech) | SPIFI_CTRL_DUAL(config.dualMode) |
109                          SPIFI_CTRL_RFCLK(config.isReadFullClockCycle) | SPIFI_CTRL_FBCLK(config.isFeedbackClock);
110 
111     mflash_drv_read_mode();
112 
113     if (primask == 0)
114     {
115         __asm("cpsie i");
116     }
117 
118     return kStatus_Success;
119 }
120 
121 /* API - initialize 'mflash' - calling wrapper */
mflash_drv_init(void)122 int32_t mflash_drv_init(void)
123 {
124     return mflash_drv_init_internal();
125 }
126 
127 /* Erase single sector */
mflash_drv_sector_erase_internal(uint32_t sector_addr)128 static int32_t mflash_drv_sector_erase_internal(uint32_t sector_addr)
129 {
130     uint32_t primask = __get_PRIMASK();
131 
132     __asm("cpsid i");
133 
134     /* Reset the SPIFI to switch to command mode */
135     SPIFI_ResetCommand(MFLASH_SPIFI);
136 
137     /* Write enable */
138     SPIFI_SetCommand(MFLASH_SPIFI, &command[WRITE_ENABLE]);
139     /* Set address */
140     SPIFI_SetCommandAddress(MFLASH_SPIFI, sector_addr);
141     /* Erase sector */
142     SPIFI_SetCommand(MFLASH_SPIFI, &command[ERASE_SECTOR]);
143     /* Check if finished */
144     mflash_drv_check_if_finish();
145     /* Switch to read mode to enable interrupts as soon ass possible */
146     mflash_drv_read_mode();
147 
148     if (primask == 0)
149     {
150         __asm("cpsie i");
151     }
152 
153     /* Flush pipeline to allow pending interrupts take place */
154     __ISB();
155 
156     return kStatus_Success;
157 }
158 
159 /* API - Erase single sector - calling wrapper */
mflash_drv_sector_erase(uint32_t sector_addr)160 int32_t mflash_drv_sector_erase(uint32_t sector_addr)
161 {
162     return mflash_drv_sector_erase_internal(sector_addr);
163 }
164 
165 /* Page program */
mflash_drv_page_program_internal(uint32_t page_addr,const uint32_t * data)166 static int32_t mflash_drv_page_program_internal(uint32_t page_addr, const uint32_t *data)
167 {
168     uint32_t primask = __get_PRIMASK();
169 
170     __asm("cpsid i");
171 
172     /* Program page */
173     SPIFI_ResetCommand(MFLASH_SPIFI);
174     SPIFI_SetCommand(MFLASH_SPIFI, &command[WRITE_ENABLE]);
175     SPIFI_SetCommandAddress(MFLASH_SPIFI, page_addr);
176     SPIFI_SetCommand(MFLASH_SPIFI, &command[PROGRAM_PAGE]);
177 
178     /* Store 4B in each loop. Sector has always 4B alignment and size multiple of 4 */
179     for (uint32_t i = 0; i < MFLASH_PAGE_SIZE / sizeof(data[0]); i++)
180     {
181         SPIFI_WriteData(MFLASH_SPIFI, data[i]);
182     }
183 
184     mflash_drv_check_if_finish();
185     /* Switch to read mode to enable interrupts as soon ass possible */
186     mflash_drv_read_mode();
187 
188     if (primask == 0)
189     {
190         __asm("cpsie i");
191     }
192 
193     /* Flush pipeline to allow pending interrupts take place */
194     __ISB();
195 
196     return kStatus_Success;
197 }
198 
199 /* API - Page program - calling wrapper */
mflash_drv_page_program(uint32_t page_addr,uint32_t * data)200 int32_t mflash_drv_page_program(uint32_t page_addr, uint32_t *data)
201 {
202     return mflash_drv_page_program_internal(page_addr, data);
203 }
204 
205 /* API - Read data */
mflash_drv_read(uint32_t addr,uint32_t * buffer,uint32_t len)206 int32_t mflash_drv_read(uint32_t addr, uint32_t *buffer, uint32_t len)
207 {
208     void *ptr = mflash_drv_phys2log(addr, len);
209 
210     if (ptr == NULL)
211     {
212         return kStatus_InvalidArgument;
213     }
214 
215     memcpy(buffer, ptr, len);
216     return kStatus_Success;
217 }
218 
219 /* API - Get pointer to FLASH region */
mflash_drv_phys2log(uint32_t addr,uint32_t len)220 void *mflash_drv_phys2log(uint32_t addr, uint32_t len)
221 {
222     uint32_t last_addr = addr + (len != 0 ? len - 1 : 0);
223 
224     if (last_addr > (FSL_FEATURE_SPIFI_END_ADDR - FSL_FEATURE_SPIFI_START_ADDR))
225     {
226         return NULL;
227     }
228 
229     return (void *)(addr + FSL_FEATURE_SPIFI_START_ADDR);
230 }
231 
232 /* API - Get pointer to FLASH region */
mflash_drv_log2phys(void * ptr,uint32_t len)233 uint32_t mflash_drv_log2phys(void *ptr, uint32_t len)
234 {
235     uint32_t log_addr = (uint32_t)ptr;
236 
237     if (log_addr < FSL_FEATURE_SPIFI_START_ADDR || log_addr > FSL_FEATURE_SPIFI_END_ADDR)
238     {
239         return MFLASH_INVALID_ADDRESS;
240     }
241 
242     if (log_addr + len > FSL_FEATURE_SPIFI_END_ADDR + 1)
243     {
244         return MFLASH_INVALID_ADDRESS;
245     }
246 
247     return (log_addr - FSL_FEATURE_SPIFI_START_ADDR);
248 }
249