]> git.sur5r.net Git - freertos/blob - Demo/Common/drivers/Atmel/at91lib/peripherals/efc/efc.c
Atmel provided hardware specifics.
[freertos] / Demo / Common / drivers / Atmel / at91lib / peripherals / efc / efc.c
1 /* ----------------------------------------------------------------------------\r
2  *         ATMEL Microcontroller Software Support\r
3  * ----------------------------------------------------------------------------\r
4  * Copyright (c) 2008, Atmel Corporation\r
5  *\r
6  * All rights reserved.\r
7  *\r
8  * Redistribution and use in source and binary forms, with or without\r
9  * modification, are permitted provided that the following conditions are met:\r
10  *\r
11  * - Redistributions of source code must retain the above copyright notice,\r
12  * this list of conditions and the disclaimer below.\r
13  *\r
14  * Atmel's name may not be used to endorse or promote products derived from\r
15  * this software without specific prior written permission.\r
16  *\r
17  * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
20  * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27  * ----------------------------------------------------------------------------\r
28  */\r
29 \r
30 #ifndef trace_LEVEL\r
31     #define trace_LEVEL trace_INFO\r
32 #endif\r
33 \r
34 //------------------------------------------------------------------------------\r
35 //         Headers\r
36 //------------------------------------------------------------------------------\r
37 \r
38 #include "efc.h"\r
39 \r
40 #ifdef BOARD_FLASH_EFC\r
41 \r
42 #include <utility/assert.h>\r
43 #include <utility/trace.h>\r
44 \r
45 //------------------------------------------------------------------------------\r
46 //         Local definitions\r
47 //------------------------------------------------------------------------------\r
48 \r
49 // Round a number to the nearest integral value (number must have been\r
50 // multiplied by 10, e.g. to round 10.3 enter 103).\r
51 #define ROUND(n)    ((((n) % 10) >= 5) ? (((n) / 10) + 1) : ((n) / 10))\r
52 \r
53 // Returns the FMCN field value when manipulating lock bits, given MCK.\r
54 #if defined(at91sam7a3)\r
55     #define FMCN_BITS(mck)      (ROUND((mck) / 100000) << 16) // <- Not correct according to the datasheet but it works\r
56 #else\r
57     #define FMCN_BITS(mck)      (ROUND((mck) / 100000) << 16)\r
58 #endif\r
59 \r
60 // Returns the FMCN field value when manipulating the rest of the flash.\r
61 #define FMCN_FLASH(mck)         ((((mck) / 2000000) * 3) << 16)\r
62 \r
63 //------------------------------------------------------------------------------\r
64 //         Local functions\r
65 //------------------------------------------------------------------------------\r
66 \r
67 \r
68 /// Master clock frequency, used to infer the value of the FMCN field.\r
69 static unsigned int lMck;\r
70 /// Calculated value of the FMCN field base on Master clock frequency.\r
71 static unsigned int lMckFMCN;\r
72 \r
73 //------------------------------------------------------------------------------\r
74 //         Global functions\r
75 //------------------------------------------------------------------------------\r
76 \r
77 //------------------------------------------------------------------------------\r
78 /// Sets the system master clock so the FMCN field of the EFC(s) can be\r
79 /// programmed properly.\r
80 /// \param mck  Master clock frequency in Hz.\r
81 //------------------------------------------------------------------------------\r
82 void EFC_SetMasterClock(unsigned int mck)\r
83 {\r
84     lMck = mck;\r
85     lMckFMCN = FMCN_BITS(lMck);\r
86 }\r
87 \r
88 //------------------------------------------------------------------------------\r
89 /// Enables the given interrupt sources on an EFC peripheral.\r
90 /// \param pEfc  Pointer to an AT91S_EFC structure.\r
91 /// \param sources  Interrupt sources to enable.\r
92 //------------------------------------------------------------------------------\r
93 void EFC_EnableIt(AT91S_EFC *pEfc, unsigned int sources)\r
94 {\r
95     SANITY_CHECK(pEfc);\r
96     SANITY_CHECK((sources & ~0x0000000D) == 0);\r
97 \r
98     pEfc->EFC_FMR |= sources;\r
99 }\r
100 \r
101 //------------------------------------------------------------------------------\r
102 /// Disables the given interrupt sources on an EFC peripheral.\r
103 /// \param pEfc  Pointer to an AT91S_EFC structure.\r
104 /// \param sources  Interrupt sources to disable.\r
105 //------------------------------------------------------------------------------\r
106 void EFC_DisableIt(AT91S_EFC *pEfc, unsigned int sources)\r
107 {\r
108     SANITY_CHECK(pEfc);\r
109     SANITY_CHECK((sources & ~(AT91C_MC_FRDY | AT91C_MC_LOCKE | AT91C_MC_PROGE)) == 0);\r
110 \r
111     pEfc->EFC_FMR &= ~sources;\r
112 }\r
113 \r
114 //------------------------------------------------------------------------------\r
115 /// Enables or disable the "Erase before programming" feature of an EFC.\r
116 /// \param pEfc  Pointer to an AT91S_EFC structure.\r
117 /// \param enable  If 1, the feature is enabled; otherwise it is disabled.\r
118 //------------------------------------------------------------------------------\r
119 void EFC_SetEraseBeforeProgramming(AT91S_EFC *pEfc, unsigned char enable)\r
120 {\r
121     SANITY_CHECK(pEfc);\r
122 \r
123     if (enable) {\r
124 \r
125         pEfc->EFC_FMR &= ~AT91C_MC_NEBP;\r
126     }\r
127     else {\r
128 \r
129         pEfc->EFC_FMR |= AT91C_MC_NEBP;\r
130     }\r
131 }\r
132 \r
133 //------------------------------------------------------------------------------\r
134 /// Translates the given address into EFC, page and offset values. The resulting\r
135 /// values are stored in the provided variables if they are not null.\r
136 /// \param address  Address to translate.\r
137 /// \param ppEfc  Pointer to target EFC peripheral.\r
138 /// \param pPage  First page accessed.\r
139 /// \param pOffset  Byte offset in first page.\r
140 //------------------------------------------------------------------------------\r
141 void EFC_TranslateAddress(\r
142     unsigned int address,\r
143     AT91S_EFC **ppEfc,\r
144     unsigned short *pPage,\r
145     unsigned short *pOffset)\r
146 {\r
147     AT91S_EFC *pEfc;\r
148     unsigned short page;\r
149     unsigned short offset;\r
150 \r
151     SANITY_CHECK(address >= AT91C_IFLASH);\r
152     SANITY_CHECK(address <= (AT91C_IFLASH + AT91C_IFLASH_SIZE));\r
153 \r
154 #if defined(AT91C_BASE_EFC0)\r
155     if (address >= (AT91C_IFLASH + AT91C_IFLASH_SIZE / 2)) {\r
156 \r
157         pEfc = AT91C_BASE_EFC1;\r
158         page = (address - AT91C_IFLASH - AT91C_IFLASH_SIZE / 2) / AT91C_IFLASH_PAGE_SIZE;\r
159         offset = (address - AT91C_IFLASH - AT91C_IFLASH_SIZE / 2) % AT91C_IFLASH_PAGE_SIZE;\r
160     }\r
161     else {\r
162 \r
163         pEfc = AT91C_BASE_EFC0;\r
164         page = (address - AT91C_IFLASH) / AT91C_IFLASH_PAGE_SIZE;\r
165         offset = (address - AT91C_IFLASH) % AT91C_IFLASH_PAGE_SIZE;\r
166     }\r
167 #else\r
168     pEfc = AT91C_BASE_EFC;\r
169     page = (address - AT91C_IFLASH) / AT91C_IFLASH_PAGE_SIZE;\r
170     offset = (address - AT91C_IFLASH) % AT91C_IFLASH_PAGE_SIZE;\r
171 #endif\r
172     trace_LOG(trace_DEBUG,\r
173               "-D- Translated 0x%08X to EFC=0x%08X, page=%d and offset=%d\n\r",\r
174               address, (unsigned int) pEfc, page, offset);\r
175 \r
176     // Store values\r
177     if (ppEfc) {\r
178 \r
179         *ppEfc = pEfc;\r
180     }\r
181     if (pPage) {\r
182 \r
183         *pPage = page;\r
184     }\r
185     if (pOffset) {\r
186 \r
187         *pOffset = offset;\r
188     }\r
189 }\r
190 \r
191 //------------------------------------------------------------------------------\r
192 /// Computes the address of a flash access given the EFC, page and offset.\r
193 /// \param pEfc  Pointer to an AT91S_EFC structure.\r
194 /// \param page  Page number.\r
195 /// \param offset  Byte offset inside page.\r
196 /// \param pAddress  Computed address (optional).\r
197 //------------------------------------------------------------------------------\r
198 void EFC_ComputeAddress(\r
199     AT91S_EFC *pEfc,\r
200     unsigned short page,\r
201     unsigned short offset,\r
202     unsigned int *pAddress)\r
203 {\r
204     unsigned int address;\r
205 \r
206     SANITY_CHECK(pEfc);\r
207 #if defined(AT91C_BASE_EFC1)\r
208     SANITY_CHECK(page <= (AT91C_IFLASH_NB_OF_PAGES / 2));\r
209 #else\r
210     SANITY_CHECK(page <= AT91C_IFLASH_NB_OF_PAGES);\r
211 #endif\r
212     SANITY_CHECK(offset < AT91C_IFLASH_PAGE_SIZE);\r
213 \r
214     // Compute address\r
215     address = AT91C_IFLASH + page * AT91C_IFLASH_PAGE_SIZE + offset;\r
216 #if defined(AT91C_BASE_EFC1)\r
217     if (pEfc == AT91C_BASE_EFC1) {\r
218 \r
219         address += AT91C_IFLASH_SIZE / 2;\r
220     }\r
221 #endif\r
222 \r
223     // Store result\r
224     if (pAddress) {\r
225 \r
226         *pAddress = address;\r
227     }\r
228 }\r
229 \r
230 //------------------------------------------------------------------------------\r
231 /// Starts the executing the given command on an EFC. This function returns\r
232 /// as soon as the command is started. It does NOT set the FMCN field automatically.\r
233 /// \param pEfc  Pointer to an AT91S_EFC structure.\r
234 /// \param command  Command to execute.\r
235 /// \param argument  Command argument (should be 0 if not used).\r
236 //------------------------------------------------------------------------------\r
237 void EFC_StartCommand(\r
238     AT91S_EFC *pEfc,\r
239     unsigned char command,\r
240     unsigned short argument)\r
241 {\r
242     SANITY_CHECK(pEfc);\r
243     ASSERT(lMck != 0, "-F- Master clock not set.\n\r");\r
244 \r
245     // Check command & argument\r
246     switch (command) {\r
247 \r
248         case AT91C_MC_FCMD_PROG_AND_LOCK:\r
249             ASSERT(0, "-F- Write and lock command cannot be carried out.\n\r");\r
250             break;\r
251 \r
252         case AT91C_MC_FCMD_START_PROG:\r
253         case AT91C_MC_FCMD_LOCK:\r
254         case AT91C_MC_FCMD_UNLOCK:\r
255             ASSERT(argument < AT91C_IFLASH_NB_OF_PAGES,\r
256                    "-F- Maximum number of pages is %d (argument was %d)\n\r",\r
257                    AT91C_IFLASH_NB_OF_PAGES,\r
258                    argument);\r
259             break;\r
260 \r
261 #if (EFC_NUM_GPNVMS > 0)\r
262         case AT91C_MC_FCMD_SET_GP_NVM:\r
263         case AT91C_MC_FCMD_CLR_GP_NVM:\r
264             ASSERT(argument < EFC_NUM_GPNVMS, "-F- A maximum of %d GPNVMs are available on the chip.\n\r", EFC_NUM_GPNVMS);\r
265             break;\r
266 #endif\r
267 \r
268         case AT91C_MC_FCMD_ERASE_ALL:\r
269 \r
270 #if !defined(EFC_NO_SECURITY_BIT)\r
271         case AT91C_MC_FCMD_SET_SECURITY:\r
272 #endif\r
273             ASSERT(argument == 0, "-F- Argument is meaningless for the given command\n\r");\r
274             break;\r
275 \r
276         default: ASSERT(0, "-F- Unknown command %d\n\r", command);\r
277     }\r
278 \r
279     // Set FMCN\r
280     switch (command) {\r
281 \r
282         case AT91C_MC_FCMD_LOCK:\r
283         case AT91C_MC_FCMD_UNLOCK:\r
284 #if (EFC_NUM_GPNVMS > 0)\r
285         case AT91C_MC_FCMD_SET_GP_NVM:\r
286         case AT91C_MC_FCMD_CLR_GP_NVM:\r
287 #endif\r
288 #if !defined(EFC_NO_SECURITY_BIT)\r
289         case AT91C_MC_FCMD_SET_SECURITY:\r
290 #endif\r
291             pEfc->EFC_FMR = (pEfc->EFC_FMR & ~AT91C_MC_FMCN) | lMckFMCN;\r
292             break;\r
293 \r
294         case AT91C_MC_FCMD_START_PROG:\r
295         case AT91C_MC_FCMD_ERASE_ALL:\r
296             pEfc->EFC_FMR = (pEfc->EFC_FMR & ~AT91C_MC_FMCN) | lMckFMCN;\r
297             break;\r
298     }\r
299 \r
300     // Start command\r
301     ASSERT((pEfc->EFC_FSR & AT91C_MC_FRDY) != 0, "-F- Efc is not ready\n\r");\r
302     pEfc->EFC_FCR = (0x5A << 24) | (argument << 8) | command;\r
303 }\r
304 \r
305 //------------------------------------------------------------------------------\r
306 /// Performs the given command and wait until its completion (or an error).\r
307 /// Returns 0 if successful; otherwise returns an error code.\r
308 /// \param pEfc  Pointer to an AT91S_EFC structure.\r
309 /// \param command  Command to perform.\r
310 /// \param argument  Optional command argument.\r
311 //------------------------------------------------------------------------------\r
312 #ifdef __ICCARM__\r
313 __ramfunc\r
314 #else\r
315 __attribute__ ((section (".ramfunc")))\r
316 #endif\r
317 unsigned char EFC_PerformCommand(\r
318     AT91S_EFC *pEfc,\r
319     unsigned char command,\r
320     unsigned short argument)\r
321 {\r
322     unsigned int status;\r
323 \r
324     // Set FMCN\r
325     switch (command) {\r
326 \r
327         case AT91C_MC_FCMD_LOCK:\r
328         case AT91C_MC_FCMD_UNLOCK:\r
329 #if (EFC_NUM_GPNVMS > 0)\r
330         case AT91C_MC_FCMD_SET_GP_NVM:\r
331         case AT91C_MC_FCMD_CLR_GP_NVM:\r
332 #endif\r
333 #if !defined(EFC_NO_SECURITY_BIT)\r
334         case AT91C_MC_FCMD_SET_SECURITY:\r
335 #endif\r
336             pEfc->EFC_FMR = (pEfc->EFC_FMR & ~AT91C_MC_FMCN) | lMckFMCN;\r
337             break;\r
338 \r
339         case AT91C_MC_FCMD_START_PROG:\r
340         case AT91C_MC_FCMD_ERASE_ALL:\r
341             pEfc->EFC_FMR = (pEfc->EFC_FMR & ~AT91C_MC_FMCN) | lMckFMCN;\r
342             break;\r
343     }\r
344 \r
345 #ifdef BOARD_FLASH_IAP_ADDRESS\r
346     // Pointer on IAP function in ROM\r
347     static void (*IAP_PerformCommand)(unsigned int, unsigned int);\r
348     unsigned int index = 0;\r
349 #ifdef AT91C_BASE_EFC1\r
350     if (pEfc == AT91C_BASE_EFC1) {\r
351 \r
352         index = 1;\r
353     }\r
354 #endif\r
355     IAP_PerformCommand = (void (*)(unsigned int, unsigned int)) *((unsigned int *) BOARD_FLASH_IAP_ADDRESS);\r
356 \r
357     // Check if IAP function is implemented (opcode in SWI != 'b' or 'ldr') */\r
358     if ((((((unsigned long) IAP_PerformCommand >> 24) & 0xFF) != 0xEA) &&\r
359         (((unsigned long) IAP_PerformCommand >> 24) & 0xFF) != 0xE5)) {\r
360 \r
361         IAP_PerformCommand(index, (0x5A << 24) | (argument << 8) | command);\r
362         return (pEfc->EFC_FSR & (AT91C_MC_LOCKE | AT91C_MC_PROGE));\r
363     }\r
364 #endif\r
365 \r
366     pEfc->EFC_FCR = (0x5A << 24) | (argument << 8) | command;\r
367     do {\r
368 \r
369         status = pEfc->EFC_FSR;\r
370     }\r
371     while ((status & AT91C_MC_FRDY) == 0);\r
372 \r
373     return (status & (AT91C_MC_PROGE | AT91C_MC_LOCKE));\r
374 }\r
375 \r
376 //------------------------------------------------------------------------------\r
377 /// Returns the current status of an EFC. Keep in mind that this function clears\r
378 /// the value of some status bits (LOCKE, PROGE).\r
379 /// \param pEfc  Pointer to an AT91S_EFC structure.\r
380 //------------------------------------------------------------------------------\r
381 unsigned int EFC_GetStatus(AT91S_EFC *pEfc)\r
382 {\r
383     return pEfc->EFC_FSR;\r
384 }\r
385 \r
386 #endif //#ifdef BOARD_FLASH_EFC\r
387 \r