]> rtime.felk.cvut.cz Git - arc.git/blob - arch/ppc/mpc55xx/drivers/Pwm.c
Updated installation of ISRs in mpc55xx drivers
[arc.git] / arch / ppc / mpc55xx / drivers / Pwm.c
1 /* -------------------------------- Arctic Core ------------------------------\r
2  * Arctic Core - the open source AUTOSAR platform http://arccore.com\r
3  *\r
4  * Copyright (C) 2009  ArcCore AB <contact@arccore.com>\r
5  *\r
6  * This source code is free software; you can redistribute it and/or modify it\r
7  * under the terms of the GNU General Public License version 2 as published by the\r
8  * Free Software Foundation; See <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>.\r
9  *\r
10  * This program is distributed in the hope that it will be useful, but\r
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r
13  * for more details.\r
14  * -------------------------------- Arctic Core ------------------------------*/\r
15 \r
16 \r
17 \r
18 \r
19 \r
20 \r
21 \r
22 \r
23 /*\r
24  * Pwm.c\r
25  *\r
26  * TODO: Implement DMA support for PWM\r
27  * TODO: Test PWM for MPC5567 and MPC5554\r
28  *\r
29  *  Created on: 2009-jul-09\r
30  *      Author: nian\r
31  */\r
32 \r
33 //#define USE_KERNEL\r
34 \r
35 #include <assert.h>\r
36 #include <string.h>\r
37 \r
38 #include "Pwm.h"\r
39 #include "MemMap.h"\r
40 //#include "SchM_Pwm.h"\r
41 #include "Det.h"\r
42 #ifdef CFG_MPC5516\r
43 #include "mpc5516.h"\r
44 #elif defined(CFG_MPC5567)\r
45 #include "mpc5567.h"\r
46 #endif\r
47 #if defined(USE_KERNEL)\r
48 #include "Os.h"\r
49 #endif\r
50 #include "Mcu.h"\r
51 #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
52 #include "irq.h"\r
53 #include "arc.h"\r
54 #endif\r
55 \r
56 \r
57 #if PWM_DEV_EROR_DETECT==STD_ON\r
58         #define PWM_VALIDATE(_exp, _errid) \\r
59                 if (!(_exp)) { \\r
60                         Pwm_ReportError(_errid); \\r
61                         return; \\r
62                 }\r
63         #define Pwm_VALIDATE_CHANNEL(_ch) PWM_VALIDATE(_ch <= 15, PWM_E_PARAM_CHANNEL)\r
64         #define Pwm_VALIDATE_INITIALIZED() PWM_VALIDATE(Pwm_ModuleState == PWM_STATE_INITIALIZED, PWM_E_UNINIT)\r
65         #define Pwm_VALIDATE_UNINITIALIZED() PWM_VALIDATE(Pwm_ModuleState != PWM_STATE_INITIALIZED, PWM_E_ALREADY_INITIALIZED)\r
66 #else\r
67         #define Pwm_VALIDATE_CHANNEL(ch)\r
68         #define Pwm_VALIDATE_INITIALIZED()\r
69         #define Pwm_VALIDATE_UNINITIALIZED()\r
70 #endif\r
71 \r
72 \r
73 typedef enum {\r
74         PWM_STATE_UNINITIALIZED, PWM_STATE_INITIALIZED\r
75 } Pwm_ModuleStateType;\r
76 \r
77 static Pwm_ModuleStateType Pwm_ModuleState = PWM_STATE_UNINITIALIZED;\r
78 \r
79 // Run-time variables\r
80 typedef struct {\r
81         Pwm_ChannelClassType Class;\r
82 \r
83         #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
84                 Pwm_NotificationHandlerType NotificationRoutine;\r
85                 Pwm_EdgeNotificationType NotificationState;\r
86         #endif\r
87 } Pwm_ChannelStructType;\r
88 \r
89 // We use Pwm_ChannelType as index here\r
90 Pwm_ChannelStructType ChannelRuntimeStruct[16];\r
91 \r
92 /* Local functions */\r
93 void inline Pwm_InitChannel(Pwm_ChannelType Channel);\r
94 void inline Pwm_DeInitChannel(Pwm_ChannelType Channel);\r
95 \r
96 #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
97 static void Pwm_Isr(void);\r
98 #endif\r
99 \r
100 void Pwm_Init(const Pwm_ConfigType* ConfigPtr) {\r
101     Pwm_ChannelType channel_iterator;\r
102 \r
103     Pwm_VALIDATE_UNINITIALIZED();\r
104     #if PWM_DEV_EROR_DETECT==STD_ON\r
105         /*\r
106          * PWM046: If development error detection is enabled for the Pwm module,\r
107          * the function Pwm_Init shall raise development error PWM_E_PARAM_CONFIG\r
108          * if ConfigPtr is a null pointer.\r
109          *\r
110          * PWM120: For pre-compile and link-time configuration variants, a NULL\r
111          * pointer shall be passed to the initialization routine. In this case the\r
112          * check for this NULL pointer has to be omitted.\r
113          */\r
114         #if PWM_STATICALLY_CONFIGURED==OFF\r
115             if (ConfigPtr == NULL) {\r
116                 Pwm_ReportError(PWM_E_PARAM_CONFIG);\r
117                 return;\r
118             }\r
119         #endif\r
120     #endif\r
121 \r
122     /* Clock scaler uses system clock (~64MHz) as source, so prescaler 64 => 1MHz. */\r
123     EMIOS.MCR.B.GPRE = PWM_PRESCALER - 1;\r
124 \r
125     /* Enable eMIOS clock */\r
126     EMIOS.MCR.B.GPREN = 1;\r
127 \r
128     /* Stop channels when in debug mode */\r
129     EMIOS.MCR.B.FRZ = PWM_FREEZE_ENABLE;\r
130 \r
131     /* Use global time base */\r
132     EMIOS.MCR.B.GTBE = 1;\r
133 \r
134     Pwm_ModuleState = PWM_STATE_INITIALIZED;\r
135 \r
136     for (channel_iterator = 0; channel_iterator < PWM_NUMBER_OF_CHANNELS; channel_iterator++) {\r
137         Pwm_ChannelType channel = ConfigPtr->Channels[channel_iterator].channel;\r
138 \r
139         // Set up the registers in hw\r
140         memcpy((void*) &EMIOS.CH[channel],\r
141                 (void*) &ConfigPtr->Channels[channel_iterator].r,\r
142                 sizeof(Pwm_ChannelRegisterType));\r
143 \r
144         #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
145                 /*\r
146                  * PWM052: The function Pwm_Init shall disable all notifications.\r
147                  *\r
148                  * This is now implemented in the configuration macro.\r
149                  */\r
150                 // Pwm_DisableNotification(channel);\r
151 \r
152                 // Install ISR\r
153                 switch (channel) {\r
154                                         case 0x0: ISR_INSTALL_ISR2( "Pwm0",  Pwm_Isr, EMISOS200_FLAG_F0,  PWM_ISR_PRIORITY, 0 ); break;\r
155                                         case 0x1: ISR_INSTALL_ISR2( "Pwm1",  Pwm_Isr, EMISOS200_FLAG_F1,  PWM_ISR_PRIORITY, 0 ); break;\r
156                                         case 0x2: ISR_INSTALL_ISR2( "Pwm2",  Pwm_Isr, EMISOS200_FLAG_F2,  PWM_ISR_PRIORITY, 0 ); break;\r
157                                         case 0x3: ISR_INSTALL_ISR2( "Pwm3",  Pwm_Isr, EMISOS200_FLAG_F3,  PWM_ISR_PRIORITY, 0 ); break;\r
158                                         case 0x4: ISR_INSTALL_ISR2( "Pwm4",  Pwm_Isr, EMISOS200_FLAG_F4,  PWM_ISR_PRIORITY, 0 ); break;\r
159                                         case 0x5: ISR_INSTALL_ISR2( "Pwm5",  Pwm_Isr, EMISOS200_FLAG_F5,  PWM_ISR_PRIORITY, 0 ); break;\r
160                                         case 0x6: ISR_INSTALL_ISR2( "Pwm6",  Pwm_Isr, EMISOS200_FLAG_F6,  PWM_ISR_PRIORITY, 0 ); break;\r
161                                         case 0x7: ISR_INSTALL_ISR2( "Pwm7",  Pwm_Isr, EMISOS200_FLAG_F7,  PWM_ISR_PRIORITY, 0 ); break;\r
162                                         case 0x8: ISR_INSTALL_ISR2( "Pwm8",  Pwm_Isr, EMISOS200_FLAG_F8,  PWM_ISR_PRIORITY, 0 ); break;\r
163                                         case 0x9: ISR_INSTALL_ISR2( "Pwm9",  Pwm_Isr, EMISOS200_FLAG_F9,  PWM_ISR_PRIORITY, 0 ); break;\r
164                                         case 0xA: ISR_INSTALL_ISR2( "Pwm10", Pwm_Isr, EMISOS200_FLAG_F10, PWM_ISR_PRIORITY, 0 ); break;\r
165                                         case 0xB: ISR_INSTALL_ISR2( "Pwm11", Pwm_Isr, EMISOS200_FLAG_F11, PWM_ISR_PRIORITY, 0 ); break;\r
166                                         case 0xC: ISR_INSTALL_ISR2( "Pwm12", Pwm_Isr, EMISOS200_FLAG_F12, PWM_ISR_PRIORITY, 0 ); break;\r
167                                         case 0xD: ISR_INSTALL_ISR2( "Pwm13", Pwm_Isr, EMISOS200_FLAG_F13, PWM_ISR_PRIORITY, 0 ); break;\r
168                                         case 0xE: ISR_INSTALL_ISR2( "Pwm14", Pwm_Isr, EMISOS200_FLAG_F14, PWM_ISR_PRIORITY, 0 ); break;\r
169                                         case 0xF: ISR_INSTALL_ISR2( "Pwm15", Pwm_Isr, EMISOS200_FLAG_F15, PWM_ISR_PRIORITY, 0 ); break;\r
170                                         default: assert(0); break;\r
171                                 }\r
172 \r
173                 ChannelRuntimeStruct[channel].NotificationRoutine\r
174                         = ConfigPtr->NotificationHandlers[channel_iterator];\r
175         #endif\r
176     }\r
177 }\r
178 \r
179 #if PWM_DEINIT_API==STD_ON\r
180 \r
181 // TODO: Test that this function in fact turns the channel off.\r
182 void inline Pwm_DeInitChannel(Pwm_ChannelType Channel) {\r
183         Pwm_VALIDATE_CHANNEL(Channel);\r
184         Pwm_VALIDATE_INITIALIZED();\r
185 \r
186     Pwm_SetOutputToIdle(Channel);\r
187 \r
188     #ifdef CFG_MPC5516\r
189         // Set the disable bit for this channel\r
190         EMIOS.UCDIS.R |= (1 << (31 - Channel));\r
191     #endif\r
192 \r
193     /*\r
194      * PWM052: The function Pwm_DeInit shall disable all notifications.\r
195      */\r
196     #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
197         Pwm_DisableNotification(Channel);\r
198     #endif\r
199 }\r
200 \r
201 void Pwm_DeInit() {\r
202         /* TODO: Implement Pwm_DeInit() */\r
203         Pwm_ChannelType channel_iterator;\r
204 \r
205         Pwm_VALIDATE_INITIALIZED();\r
206 \r
207         for (channel_iterator = 0; channel_iterator < PWM_NUMBER_OF_CHANNELS; channel_iterator++) {\r
208                 Pwm_DeInitChannel(channel_iterator);\r
209 \r
210         }\r
211 \r
212         // Disable module\r
213         EMIOS.MCR.B.MDIS = 1;\r
214 \r
215         Pwm_ModuleState = PWM_STATE_UNINITIALIZED;\r
216 }\r
217 #endif\r
218 \r
219 void Pwm_GetVersionInfo(Std_VersionInfoType* VersionInfo) {\r
220         /* TODO: Implement Pwm_GetVersionInfo */\r
221 }\r
222 \r
223 /*\r
224  * PWM083: The function Pwm_SetPeriodAndDuty shall be pre compile time\r
225  * changeable ON/OFF by the configuration parameter PwmSetPeriodAndDuty.\r
226  */\r
227 #if PWM_SET_PERIOD_AND_DUTY==STD_ON\r
228         void Pwm_SetPeriodAndDuty(Pwm_ChannelType Channel, Pwm_PeriodType Period,\r
229                         Pwm_DutyCycleType DutyCycle) {\r
230 \r
231                 Pwm_VALIDATE_INITIALIZED();\r
232                 Pwm_VALIDATE_CHANNEL(Channel);\r
233                 PWM_VALIDATE(ChannelRuntimeStruct[Channel].Class == PWM_VARIABLE_PERIOD, PWM_E_PERIOD_UNCHANGEABLE);\r
234 \r
235                 uint16 leading_edge_position = (uint16) (((uint32) Period\r
236                                 * (uint32) DutyCycle) >> 15);\r
237 \r
238 \r
239 \r
240                 /* Timer instant for leading edge */\r
241                 EMIOS.CH[Channel].CADR.R = leading_edge_position;\r
242 \r
243                 /* Timer instant for the period to restart */\r
244                 EMIOS.CH[Channel].CBDR.R = Period;\r
245 \r
246         }\r
247 #endif\r
248 \r
249 \r
250 /**\r
251  * PWM013: The function Pwm_SetDutyCycle shall set the duty cycle of the PWM\r
252  * channel.\r
253  *\r
254  * @TODO: How to conform with PWM018: "The driver shall forbid the spike on the PWM output signal"?\r
255  *\r
256  * @param Channel PWM channel to use. 0 <= Channel < PWM_NUMBER_OF_CHANNELS <= 16\r
257  * @param DutyCycle 0 <= DutyCycle <= 0x8000\r
258  */\r
259 void Pwm_SetDutyCycle(Pwm_ChannelType Channel, Pwm_DutyCycleType DutyCycle) {\r
260 \r
261         uint16 leading_edge_position = (uint16) ((EMIOS.CH[Channel].CBDR.R\r
262                                 * (uint32) DutyCycle) >> 15);\r
263 \r
264 \r
265         Pwm_VALIDATE_INITIALIZED();\r
266         Pwm_VALIDATE_CHANNEL(Channel);\r
267 \r
268         /* Timer instant for leading edge */\r
269 \r
270         /*\r
271          * PWM017: The function Pwm_SetDutyCycle shall update the duty cycle at\r
272          * the end of the period if supported by the implementation and configured\r
273          * with PwmDutycycleUpdatedEndperiod. [ This is achieved in hardware since\r
274          * the A and B registers are double buffered ]\r
275          *\r
276          * PWM014: The function Pwm_SetDutyCycle shall set the output state according\r
277          * to the configured polarity parameter [which is already set from\r
278          * Pwm_InitChannel], when the duty parameter is 0% [=0] or 100% [=0x8000].\r
279          */\r
280         if (DutyCycle == Pwm_100_Procent || DutyCycle == Pwm_0_Procent) {\r
281                 EMIOS.CH[Channel].CADR.R = 0;\r
282 \r
283         } else {\r
284                 EMIOS.CH[Channel].CADR.R = leading_edge_position;\r
285 \r
286         }\r
287 }\r
288 \r
289 void Pwm_SetOutputToIdle(Pwm_ChannelType Channel) {\r
290         Pwm_VALIDATE_CHANNEL(Channel);\r
291         Pwm_VALIDATE_INITIALIZED();\r
292 \r
293         /* TODO: Make Pwm_SetOutputToIdle sensitive to PwmIdleState (currently uses PwmPolarity) */\r
294         EMIOS.CH[Channel].CADR.R = 0;\r
295 }\r
296 \r
297 /*\r
298  * PWM085: The function Pwm_GetOutputState shall be pre compile configurable\r
299  * ON/OFF by the configuration parameter PwmGetOutputState\r
300  */\r
301 #if PWM_GET_OUTPUT_STATE==STD_ON\r
302         /*\r
303          * PWM022: The function Pwm_GetOutputState shall read the internal state\r
304          * of the PWM output signal and return it.\r
305          */\r
306         Pwm_OutputStateType Pwm_GetOutputState(Pwm_ChannelType Channel) {\r
307 \r
308 \r
309 \r
310                 // We need to return something, even in presence of errors\r
311                 if (Channel >= 16) {\r
312                         Pwm_ReportError(PWM_E_PARAM_CHANNEL);\r
313 \r
314                         /*\r
315                          * Accordingly to PWM025, we should return PWM_LOW on failure.\r
316                          */\r
317                         return PWM_LOW;\r
318 \r
319                 } else if (Pwm_ModuleState != PWM_STATE_INITIALIZED) {\r
320                         Pwm_ReportError(PWM_E_UNINIT);\r
321 \r
322                         /*\r
323                          * Accordingly to PWM025, we should return PWM_LOW on failure.\r
324                          */\r
325                         return PWM_LOW;\r
326 \r
327                 }\r
328 \r
329                 return EMIOS.CH[Channel].CSR.B.UCOUT;\r
330 \r
331         }\r
332 #endif\r
333 \r
334 #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
335         void Pwm_DisableNotification(Pwm_ChannelType Channel) {\r
336                 Pwm_VALIDATE_CHANNEL(Channel);\r
337                 Pwm_VALIDATE_INITIALIZED();\r
338 \r
339                 // Disable flags on this channel\r
340                 EMIOS.CH[Channel].CCR.B.FEN = 0;\r
341         }\r
342 \r
343         void Pwm_EnableNotification(Pwm_ChannelType Channel,\r
344                         Pwm_EdgeNotificationType Notification) {\r
345                 Pwm_VALIDATE_CHANNEL(Channel);\r
346                 Pwm_VALIDATE_INITIALIZED();\r
347 \r
348                 ChannelRuntimeStruct[Channel].NotificationState = Notification;\r
349 \r
350                 // Enable flags on this channel\r
351                 EMIOS.CH[Channel].CCR.B.FEN = 1;\r
352         }\r
353 \r
354         static void Pwm_Isr(void) {\r
355                 // Find out which channel that triggered the interrupt\r
356 #ifdef CFG_MPC5516\r
357                 uint32_t flagmask = EMIOS.GFLAG.R;\r
358 #elif defined(CFG_MPC5567)\r
359                 uint32_t flagmask = EMIOS.GFR.R;\r
360 #endif\r
361 \r
362                 // There are 24 channels specified in the global flag register, but\r
363                 // we only listen to the first 16 as only these support OPWM\r
364                 for (Pwm_ChannelType emios_ch = 0; emios_ch < 16; emios_ch++) {\r
365                         if (flagmask & (1 << emios_ch)) {\r
366 \r
367                                 if (ChannelRuntimeStruct[emios_ch].NotificationRoutine != NULL && EMIOS.CH[emios_ch].CCR.B.FEN) {\r
368 \r
369                                         Pwm_EdgeNotificationType notification = ChannelRuntimeStruct[emios_ch].NotificationState;\r
370                                         if (notification == PWM_BOTH_EDGES ||\r
371                                                         notification == EMIOS.CH[emios_ch].CSR.B.UCOUT) {\r
372                                                         ChannelRuntimeStruct[emios_ch].NotificationRoutine();\r
373                                         }\r
374                                 }\r
375 \r
376                                 // Clear interrupt\r
377                                 EMIOS.CH[emios_ch].CSR.B.FLAG = 1;\r
378                         }\r
379                 }\r
380         }\r
381 \r
382 #endif /* PWM_NOTIFICATION_SUPPORED == STD_ON */\r