1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2019 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 #include "fsl_eeprom.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 
14 /* Component ID definition, used by tools. */
15 #ifndef FSL_COMPONENT_ID
16 #define FSL_COMPONENT_ID "platform.drivers.eeprom"
17 #endif
18 
19 /*******************************************************************************
20  * Prototypes
21  ******************************************************************************/
22 
23 /*!
24  * @brief Get the EEPROM instance from peripheral base address.
25  *
26  * @param base EEPROM peripheral base address.
27  * @return EEPROM instance.
28  */
29 static uint32_t EEPROM_GetInstance(EEPROM_Type *base);
30 
31 /*******************************************************************************
32  * Variables
33  ******************************************************************************/
34 
35 /* Array of EEPROM peripheral base address. */
36 static EEPROM_Type *const s_eepromBases[] = EEPROM_BASE_PTRS;
37 
38 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
39 /* Array of EEPROM clock name. */
40 static const clock_ip_name_t s_eepromClock[] = EEPROM_CLOCKS;
41 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
42 
43 /*******************************************************************************
44  * Code
45  ******************************************************************************/
EEPROM_GetInstance(EEPROM_Type * base)46 static uint32_t EEPROM_GetInstance(EEPROM_Type *base)
47 {
48     uint32_t instance;
49 
50     /* Find the instance index from base address mappings. */
51     for (instance = 0; instance < ARRAY_SIZE(s_eepromBases); instance++)
52     {
53         if (s_eepromBases[instance] == base)
54         {
55             break;
56         }
57     }
58 
59     assert(instance < ARRAY_SIZE(s_eepromBases));
60 
61     return instance;
62 }
63 
64 /*!
65  * brief Get EEPROM default configure settings.
66  *
67  * param config  EEPROM config structure pointer.
68  */
EEPROM_GetDefaultConfig(eeprom_config_t * config)69 void EEPROM_GetDefaultConfig(eeprom_config_t *config)
70 {
71     /* Initializes the configure structure to zero. */
72     (void)memset(config, 0, sizeof(*config));
73 
74     config->autoProgram     = kEEPROM_AutoProgramWriteWord;
75     config->writeWaitPhase1 = 0x5U;
76     config->writeWaitPhase2 = 0x9U;
77     config->writeWaitPhase3 = 0x3U;
78     config->readWaitPhase1  = 0xFU;
79     config->readWaitPhase2  = 0x8U;
80     config->lockTimingParam = false;
81 }
82 
EEPROM_Flush(EEPROM_Type * base)83 static void EEPROM_Flush(EEPROM_Type *base)
84 {
85     /* Write all prepared words */
86     EEPROM_ClearInterruptFlag(base, (uint32_t)kEEPROM_ProgramFinishInterruptEnable);
87     base->CMD = FSL_FEATURE_EEPROM_PROGRAM_CMD;
88 
89     /* Waiting for operation finished */
90     while ((EEPROM_GetInterruptStatus(base) & (uint32_t)kEEPROM_ProgramFinishInterruptEnable) == 0UL)
91     {
92     }
93 }
94 
95 /*!
96  * brief Initializes the EEPROM with the user configuration structure.
97  *
98  * This function configures the EEPROM module with the user-defined configuration. This function also sets the
99  * internal clock frequency to about 155kHz according to the source clock frequency.
100  *
101  * param base     EEPROM peripheral base address.
102  * param config   The pointer to the configuration structure.
103  * param sourceClock_Hz EEPROM source clock frequency in Hz.
104  */
EEPROM_Init(EEPROM_Type * base,const eeprom_config_t * config,uint32_t sourceClock_Hz)105 void EEPROM_Init(EEPROM_Type *base, const eeprom_config_t *config, uint32_t sourceClock_Hz)
106 {
107     assert(config != NULL);
108 
109     uint32_t clockDiv = 0;
110 
111 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
112     /* Enable the SAI clock */
113     CLOCK_EnableClock(s_eepromClock[EEPROM_GetInstance(base)]);
114 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
115 
116 #if !(defined(FSL_FEATURE_EEPROM_HAS_NO_RESET) && FSL_FEATURE_EEPROM_HAS_NO_RESET)
117     /* Reset the EEPROM module */
118     RESET_PeripheralReset(kEEPROM_RST_SHIFT_RSTn);
119 #endif /* FSL_FEATURE_EEPROM_HAS_NO_RESET */
120 
121 #if (defined(FSL_SDK_ENABLE_DRIVER_POWER_CONTROL) && (FSL_SDK_ENABLE_DRIVER_POWER_CONTROL))
122     POWER_DisablePD(kPDRUNCFG_PD_EEPROM);
123 
124     /* Delay larger than 100us. */
125     uint32_t count = SystemCoreClock / 1000;
126     while (count--)
127     {
128         __NOP();
129     }
130 #endif /* FSL_SDK_ENABLE_DRIVER_POWER_CONTROL */
131 
132     /* Set the clock divider */
133     clockDiv = sourceClock_Hz / (uint32_t)FSL_FEATURE_EEPROM_INTERNAL_FREQ;
134     if ((sourceClock_Hz % (uint32_t)FSL_FEATURE_EEPROM_INTERNAL_FREQ) >
135         ((uint32_t)FSL_FEATURE_EEPROM_INTERNAL_FREQ / 2UL))
136     {
137         clockDiv += 1UL;
138     }
139 
140     if (sourceClock_Hz / clockDiv > (uint32_t)FSL_FEATURE_EEPROM_INTERNAL_FREQ)
141     {
142         clockDiv += 1UL;
143     }
144 
145     base->CLKDIV = clockDiv - 1UL;
146 
147     /* Set the auto program feature */
148     EEPROM_SetAutoProgram(base, config->autoProgram);
149 
150     /* Set time delay parameter */
151     base->RWSTATE =
152         EEPROM_RWSTATE_RPHASE1(config->readWaitPhase1 - 1UL) | EEPROM_RWSTATE_RPHASE2(config->readWaitPhase2 - 1UL);
153     base->WSTATE = EEPROM_WSTATE_PHASE1(config->writeWaitPhase1 - 1UL) |
154                    EEPROM_WSTATE_PHASE2(config->writeWaitPhase2 - 1UL) |
155                    EEPROM_WSTATE_PHASE3(config->writeWaitPhase3 - 1UL);
156     base->WSTATE |= EEPROM_WSTATE_LCK_PARWEP(config->lockTimingParam);
157 
158     /* Clear the remaining write operation  */
159     base->CMD = FSL_FEATURE_EEPROM_PROGRAM_CMD;
160     while ((EEPROM_GetInterruptStatus(base) & (uint32_t)kEEPROM_ProgramFinishInterruptEnable) == 0UL)
161     {
162     }
163 }
164 
165 /*!
166  * brief Deinitializes the EEPROM regions.
167  *
168  * param base     EEPROM peripheral base address.
169  */
EEPROM_Deinit(EEPROM_Type * base)170 void EEPROM_Deinit(EEPROM_Type *base)
171 {
172 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
173     /* Enable the SAI clock */
174     CLOCK_DisableClock(s_eepromClock[EEPROM_GetInstance(base)]);
175 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
176 }
177 
178 /*!
179  * brief Write a word data in address of EEPROM.
180  *
181  * Users can write a page or at least a word data into EEPROM address.
182  *
183  * param base     EEPROM peripheral base address.
184  * param offset   Offset from the begining address of EEPROM. This value shall be 4-byte aligned.
185  * param data     Data need be write.
186  */
EEPROM_WriteWord(EEPROM_Type * base,uint32_t offset,uint32_t data)187 status_t EEPROM_WriteWord(EEPROM_Type *base, uint32_t offset, uint32_t data)
188 {
189     uint32_t *addr  = NULL;
190     status_t status = kStatus_Success;
191 
192     if ((offset % 4UL != 0UL) || (offset > (uint32_t)FSL_FEATURE_EEPROM_SIZE))
193     {
194         status = kStatus_InvalidArgument;
195     }
196     else
197     {
198         /* Set auto program settings */
199         if (base->AUTOPROG != (uint32_t)kEEPROM_AutoProgramDisable)
200         {
201             EEPROM_SetAutoProgram(base, kEEPROM_AutoProgramWriteWord);
202         }
203 
204         EEPROM_ClearInterruptFlag(base, (uint32_t)kEEPROM_ProgramFinishInterruptEnable);
205 
206         /* Compute the page */
207         addr  = (uint32_t *)((uint32_t)FSL_FEATURE_EEPROM_BASE_ADDRESS + offset);
208         *addr = data;
209 
210         /* Check if manual program erase is needed. */
211         if (base->AUTOPROG != (uint32_t)kEEPROM_AutoProgramWriteWord)
212         {
213             base->CMD = FSL_FEATURE_EEPROM_PROGRAM_CMD;
214         }
215 
216         /* Waiting for operation to finish */
217         while ((EEPROM_GetInterruptStatus(base) & (uint32_t)kEEPROM_ProgramFinishInterruptEnable) == 0UL)
218         {
219         }
220     }
221 
222     return status;
223 }
224 
225 /*!
226  * brief Write data from a user allocated buffer in address of EEPROM.
227  *
228  * Users can write any bytes data into EEPROM address by wBuf.
229  *
230  * param base     EEPROM peripheral base address.
231  * param offset   Offset from the begining address of EEPROM.
232  * param wBuf     Data need be write.
233  * param size     Number of bytes to write.
234  */
EEPROM_Write(EEPROM_Type * base,uint32_t offset,void * wBuf,uint32_t size)235 void EEPROM_Write(EEPROM_Type *base, uint32_t offset, void *wBuf, uint32_t size)
236 {
237     uint8_t *src;
238     bool unalignedStart;
239     uint32_t memUnit   = 0;
240     uint32_t alignSize = 0;
241 
242 #if (defined(FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED) && FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED)
243     uint16_t *dst;
244     alignSize = 2;
245     memUnit   = FSL_FEATURE_EEPROM_ROW_SIZE;
246 #else
247     uint32_t i = 0;
248     uint32_t *dst;
249     uint32_t data32_Align  = 0;
250     uint32_t unalignedSize = 0;
251     alignSize              = 4;
252     memUnit                = FSL_FEATURE_EEPROM_SIZE / FSL_FEATURE_EEPROM_PAGE_COUNT;
253 #endif /* FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED */
254 
255     /* Offset and size must be positive */
256     assert(size > 0UL);
257     /* All bytes must be written to a valid EEPROM address */
258     assert((offset + size) <= (uint32_t)FSL_FEATURE_EEPROM_SIZE);
259 
260     src = wBuf;
261 
262 #if (defined(FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED) && FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED)
263     dst            = &((uint16_t *)FSL_FEATURE_EEPROM_BASE_ADDRESS)[offset / alignSize];
264     unalignedStart = (offset % alignSize != 0UL);
265 #else
266     dst                    = &((uint32_t *)FSL_FEATURE_EEPROM_BASE_ADDRESS)[offset / alignSize];
267     unalignedStart         = (offset % alignSize) != 0UL;
268     unalignedSize          = alignSize - (offset % alignSize);
269 #endif /* FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED */
270 
271     while (size > 0UL)
272     {
273         /* If first byte is to be copied to non aligned EEPROM byte */
274         if (unalignedStart)
275         {
276 /* The first byte from the buffer is not 16-bits aligned.
277  * Read the LSB from EEPROM to complete it.
278  */
279 #if (defined(FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED) && FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED)
280             *dst = (uint16_t)(((uint16_t)src[0] << 8UL) | (*dst & 0x00ffUL));
281 #else
282             /* The first byte from the buffer is not 32-bits aligned.
283              * Read the rest of data after position offset and realign them.
284              */
285             for (i = 0; i < unalignedSize; i++)
286             {
287                 data32_Align |= (uint32_t)((uint32_t)src[i] << (8UL * ((alignSize - unalignedSize) + i)));
288             }
289             *dst = (*dst & (0xffffffffUL >> (8UL * unalignedSize))) | data32_Align;
290 #endif                                                 /* FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED */
291             src += (alignSize - (offset % alignSize)); /* Operate src to let wBuf pointer offset correct*/
292             size -= (alignSize - (offset % alignSize));
293             unalignedStart = false;
294         }
295         else if (size >= alignSize)
296         {
297 #if (defined(FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED) && FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED)
298             /* Combine two bytes from the buffer to a 16-bits word */
299             *dst = (uint16_t)(((uint16_t)src[1] << 8UL) | (uint16_t)src[0]);
300 #else
301             /* Combine four bytes from the buffer to a 32-bits word */
302             *dst = (uint32_t)(((uint32_t)src[3] << 24UL) | ((uint32_t)src[2] << 16UL) | ((uint32_t)src[1] << 8UL) |
303                               src[0]);
304 #endif                        /* FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED */
305             src += alignSize; /* Normal operate src to offset wBuf pointer by 4 bytes*/
306             size -= alignSize;
307         }
308         else
309         {
310 /* The last several bytes from the buffer is not 32-bit aligned.
311  * Read the rest of bytes of non 32-bit aligned data
312  * and realign them by 32-bit aligned */
313 #if (defined(FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED) && FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED)
314             *dst = (uint16_t)((*dst & 0xff00UL) | src[0]);
315 #else
316             data32_Align = 0; /* Clear data32_Align */
317             for (i = 0; i < (size % alignSize); i++)
318             {
319                 data32_Align |= (uint32_t)((uint32_t)src[i] << (8UL * i));
320             }
321             *dst = (*dst & (0xffffffffUL << (8UL * (size % alignSize)))) | data32_Align;
322 #endif /* FSL_FEATURE_EEPROM_TWOBYTES_ALIGNED */
323             size -= (size % alignSize);
324         }
325 
326         dst++; /* EEPROM mempory pointer go ahead */
327 
328         /* When memory unit size reached, have to flush. */
329         if ((((uint32_t)dst % memUnit) == 0UL) && (size > 0UL))
330         {
331             EEPROM_Flush(base);
332         }
333     }
334 
335     /* Normal need to flush after write data into eeprom */
336     EEPROM_Flush(base);
337 }
338 #if !(defined(FSL_FEATURE_EEPROM_PAGE_COUNT) && FSL_FEATURE_EEPROM_PAGE_COUNT)
EEPROM_WriteRow(EEPROM_Type * base,uint32_t rowNum,uint32_t * data)339 status_t EEPROM_WriteRow(EEPROM_Type *base, uint32_t rowNum, uint32_t *data)
340 {
341     uint32_t i      = 0;
342     uint32_t *addr  = NULL;
343     status_t status = kStatus_Success;
344 
345     if ((rowNum > FSL_FEATURE_EEPROM_ROW_COUNT) || (!data))
346     {
347         status = kStatus_InvalidArgument;
348     }
349     else
350     {
351         /* Set auto program settings */
352         if (base->AUTOPROG != (uint32_t)kEEPROM_AutoProgramDisable)
353         {
354             EEPROM_SetAutoProgram(base, kEEPROM_AutoProgramLastWord);
355         }
356 
357         EEPROM_ClearInterruptFlag(base, (uint32_t)kEEPROM_ProgramFinishInterruptEnable);
358 
359         addr = (uint32_t *)((uint32_t)FSL_FEATURE_EEPROM_BASE_ADDRESS +
360                             rowNum * ((uint32_t)FSL_FEATURE_EEPROM_SIZE / (uint32_t)FSL_FEATURE_EEPROM_ROW_COUNT));
361         for (i = 0; i < ((uint32_t)FSL_FEATURE_EEPROM_SIZE / (uint32_t)FSL_FEATURE_EEPROM_ROW_COUNT) / 4UL; i++)
362         {
363             addr[i] = data[i];
364         }
365 
366         if (base->AUTOPROG == kEEPROM_AutoProgramDisable)
367         {
368             base->CMD = FSL_FEATURE_EEPROM_PROGRAM_CMD;
369         }
370 
371         /* Waiting for operation to finish */
372         while ((EEPROM_GetInterruptStatus(base) & (uint32_t)kEEPROM_ProgramFinishInterruptEnable) == 0UL)
373         {
374         }
375     }
376 
377     return status;
378 }
379 #else
380 /*!
381  * brief Write a page data into EEPROM.
382  *
383  * Users can write a page or at least a word data into EEPROM address.
384  *
385  * param base     EEPROM peripheral base address.
386  * param pageNum  Page number to be written.
387  * param data     Data need be write. This array data size shall equals to the page size.
388  */
EEPROM_WritePage(EEPROM_Type * base,uint32_t pageNum,uint32_t * data)389 status_t EEPROM_WritePage(EEPROM_Type *base, uint32_t pageNum, uint32_t *data)
390 {
391     uint32_t i = 0;
392     uint32_t *addr = NULL;
393     status_t status = kStatus_Success;
394 
395     if ((pageNum > (uint32_t)FSL_FEATURE_EEPROM_PAGE_COUNT) || (data == NULL))
396     {
397         status = kStatus_InvalidArgument;
398     }
399     else
400     {
401         /* Set auto program settings */
402         if (base->AUTOPROG != (uint32_t)kEEPROM_AutoProgramDisable)
403         {
404             EEPROM_SetAutoProgram(base, kEEPROM_AutoProgramLastWord);
405         }
406 
407         EEPROM_ClearInterruptFlag(base, (uint32_t)kEEPROM_ProgramFinishInterruptEnable);
408 
409         addr = (uint32_t *)((uint32_t)FSL_FEATURE_EEPROM_BASE_ADDRESS +
410                             pageNum * ((uint32_t)FSL_FEATURE_EEPROM_SIZE / (uint32_t)FSL_FEATURE_EEPROM_PAGE_COUNT));
411         for (i = 0; i < ((uint32_t)FSL_FEATURE_EEPROM_SIZE / (uint32_t)FSL_FEATURE_EEPROM_PAGE_COUNT) / 4UL; i++)
412         {
413             addr[i] = data[i];
414         }
415 
416         if (base->AUTOPROG == (uint32_t)kEEPROM_AutoProgramDisable)
417         {
418             base->CMD = FSL_FEATURE_EEPROM_PROGRAM_CMD;
419         }
420 
421         /* Waiting for operation to finish */
422         while ((EEPROM_GetInterruptStatus(base) & (uint32_t)kEEPROM_ProgramFinishInterruptEnable) == 0UL)
423         {
424         }
425     }
426 
427     return status;
428 }
429 #endif /* FSL_FEATURE_EEPROM_PAGE_COUNT */
430