Staging: epl: run Lindent on *.c files
[linux-2.6] / drivers / staging / epl / TimerHighReskX86.c
1 /****************************************************************************
2
3   (c) SYSTEC electronic GmbH, D-07973 Greiz, August-Bebel-Str. 29
4       www.systec-electronic.com
5
6   Project:      openPOWERLINK
7
8   Description:  target specific implementation of
9                 high resolution timer module for X86 under Linux
10                 The Linux kernel has to be compiled with high resolution
11                 timers enabled. This is done by configuring the kernel
12                 with CONFIG_HIGH_RES_TIMERS enabled.
13
14   License:
15
16     Redistribution and use in source and binary forms, with or without
17     modification, are permitted provided that the following conditions
18     are met:
19
20     1. Redistributions of source code must retain the above copyright
21        notice, this list of conditions and the following disclaimer.
22
23     2. Redistributions in binary form must reproduce the above copyright
24        notice, this list of conditions and the following disclaimer in the
25        documentation and/or other materials provided with the distribution.
26
27     3. Neither the name of SYSTEC electronic GmbH nor the names of its
28        contributors may be used to endorse or promote products derived
29        from this software without prior written permission. For written
30        permission, please contact info@systec-electronic.com.
31
32     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
35     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
36     COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
37     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
38     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
40     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
42     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43     POSSIBILITY OF SUCH DAMAGE.
44
45     Severability Clause:
46
47         If a provision of this License is or becomes illegal, invalid or
48         unenforceable in any jurisdiction, that shall not affect:
49         1. the validity or enforceability in that jurisdiction of any other
50            provision of this License; or
51         2. the validity or enforceability in other jurisdictions of that or
52            any other provision of this License.
53
54   -------------------------------------------------------------------------
55
56                 $RCSfile: TimerHighReskX86.c,v $
57
58                 $Author: D.Krueger $
59
60                 $Revision: 1.4 $  $Date: 2008/04/17 21:38:01 $
61
62                 $State: Exp $
63
64                 Build Environment:
65                     GNU
66
67   -------------------------------------------------------------------------
68
69   Revision History:
70
71 ****************************************************************************/
72
73 #include "EplInc.h"
74 #include "kernel/EplTimerHighResk.h"
75 #include "Benchmark.h"
76
77 //#include <linux/config.h>
78 #include <linux/module.h>
79 #include <linux/kernel.h>
80 #include <linux/hrtimer.h>
81
82 /***************************************************************************/
83 /*                                                                         */
84 /*                                                                         */
85 /*          G L O B A L   D E F I N I T I O N S                            */
86 /*                                                                         */
87 /*                                                                         */
88 /***************************************************************************/
89
90 //---------------------------------------------------------------------------
91 // const defines
92 //---------------------------------------------------------------------------
93
94 #define TIMER_COUNT           2 /* max 15 timers selectable */
95 #define TIMER_MIN_VAL_SINGLE  5000      /* min 5us */
96 #define TIMER_MIN_VAL_CYCLE   100000    /* min 100us */
97
98 #define PROVE_OVERRUN
99
100 #ifndef CONFIG_HIGH_RES_TIMERS
101 #error "Kernel symbol CONFIG_HIGH_RES_TIMERS is required."
102 #endif
103
104 // TracePoint support for realtime-debugging
105 #ifdef _DBG_TRACE_POINTS_
106 void PUBLIC TgtDbgSignalTracePoint(BYTE bTracePointNumber_p);
107 void PUBLIC TgtDbgPostTraceValue(DWORD dwTraceValue_p);
108 #define TGT_DBG_SIGNAL_TRACE_POINT(p)   TgtDbgSignalTracePoint(p)
109 #define TGT_DBG_POST_TRACE_VALUE(v)     TgtDbgPostTraceValue(v)
110 #else
111 #define TGT_DBG_SIGNAL_TRACE_POINT(p)
112 #define TGT_DBG_POST_TRACE_VALUE(v)
113 #endif
114 #define HRT_DBG_POST_TRACE_VALUE(Event_p, uiNodeId_p, wErrorCode_p) \
115     TGT_DBG_POST_TRACE_VALUE((0xE << 28) | (Event_p << 24) \
116                              | (uiNodeId_p << 16) | wErrorCode_p)
117
118 #define TIMERHDL_MASK         0x0FFFFFFF
119 #define TIMERHDL_SHIFT        28
120 #define HDL_TO_IDX(Hdl)       ((Hdl >> TIMERHDL_SHIFT) - 1)
121 #define HDL_INIT(Idx)         ((Idx + 1) << TIMERHDL_SHIFT)
122 #define HDL_INC(Hdl)          (((Hdl + 1) & TIMERHDL_MASK) \
123                                | (Hdl & ~TIMERHDL_MASK))
124
125 //---------------------------------------------------------------------------
126 // modul global types
127 //---------------------------------------------------------------------------
128
129 typedef struct {
130         tEplTimerEventArg m_EventArg;
131         tEplTimerkCallback m_pfnCallback;
132         struct hrtimer m_Timer;
133         BOOL m_fContinuously;
134         unsigned long long m_ullPeriod;
135
136 } tEplTimerHighReskTimerInfo;
137
138 typedef struct {
139         tEplTimerHighReskTimerInfo m_aTimerInfo[TIMER_COUNT];
140
141 } tEplTimerHighReskInstance;
142
143 //---------------------------------------------------------------------------
144 // local vars
145 //---------------------------------------------------------------------------
146
147 static tEplTimerHighReskInstance EplTimerHighReskInstance_l;
148
149 //---------------------------------------------------------------------------
150 // local function prototypes
151 //---------------------------------------------------------------------------
152
153 enum hrtimer_restart EplTimerHighReskCallback(struct hrtimer *pTimer_p);
154
155 //=========================================================================//
156 //                                                                         //
157 //          P U B L I C   F U N C T I O N S                                //
158 //                                                                         //
159 //=========================================================================//
160
161 //---------------------------------------------------------------------------
162 //
163 // Function:    EplTimerHighReskInit()
164 //
165 // Description: initializes the high resolution timer module.
166 //
167 // Parameters:  void
168 //
169 // Return:      tEplKernel      = error code
170 //
171 // State:       not tested
172 //
173 //---------------------------------------------------------------------------
174
175 tEplKernel PUBLIC EplTimerHighReskInit(void)
176 {
177         tEplKernel Ret;
178
179         Ret = EplTimerHighReskAddInstance();
180
181         return Ret;
182
183 }
184
185 //---------------------------------------------------------------------------
186 //
187 // Function:    EplTimerHighReskAddInstance()
188 //
189 // Description: initializes the high resolution timer module.
190 //
191 // Parameters:  void
192 //
193 // Return:      tEplKernel      = error code
194 //
195 // State:       not tested
196 //
197 //---------------------------------------------------------------------------
198
199 tEplKernel PUBLIC EplTimerHighReskAddInstance(void)
200 {
201         tEplKernel Ret;
202         unsigned int uiIndex;
203
204         Ret = kEplSuccessful;
205
206         EPL_MEMSET(&EplTimerHighReskInstance_l, 0,
207                    sizeof(EplTimerHighReskInstance_l));
208
209 #ifndef CONFIG_HIGH_RES_TIMERS
210         printk
211             ("EplTimerHighResk: Kernel symbol CONFIG_HIGH_RES_TIMERS is required.\n");
212         Ret = kEplNoResource;
213         return Ret;
214 #endif
215
216         /*
217          * Initialize hrtimer structures for all usable timers.
218          */
219         for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++) {
220                 tEplTimerHighReskTimerInfo *pTimerInfo;
221                 struct hrtimer *pTimer;
222
223                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
224                 pTimer = &pTimerInfo->m_Timer;
225                 hrtimer_init(pTimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
226
227                 pTimer->function = EplTimerHighReskCallback;
228
229                 /*
230                  * We use HRTIMER_CB_SOFTIRQ here.
231                  * HRTIMER_CB_IRQSAFE is critical as the callback function
232                  * would be called with IRQs disabled.
233                  */
234                 pTimer->cb_mode = HRTIMER_CB_SOFTIRQ;
235         }
236
237         return Ret;
238 }
239
240 //---------------------------------------------------------------------------
241 //
242 // Function:    EplTimerHighReskDelInstance()
243 //
244 // Description: shuts down the high resolution timer module.
245 //
246 // Parameters:  void
247 //
248 // Return:      tEplKernel      = error code
249 //
250 // State:       not tested
251 //
252 //---------------------------------------------------------------------------
253
254 tEplKernel PUBLIC EplTimerHighReskDelInstance(void)
255 {
256         tEplTimerHighReskTimerInfo *pTimerInfo;
257         tEplKernel Ret;
258         unsigned int uiIndex;
259
260         Ret = kEplSuccessful;
261
262         for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++) {
263                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[0];
264                 pTimerInfo->m_pfnCallback = NULL;
265                 pTimerInfo->m_EventArg.m_TimerHdl = 0;
266                 /*
267                  * In this case we can not just try to cancel the timer.
268                  * We actually have to wait until its callback function
269                  * has returned.
270                  */
271                 hrtimer_cancel(&pTimerInfo->m_Timer);
272         }
273
274         return Ret;
275
276 }
277
278 //---------------------------------------------------------------------------
279 //
280 // Function:    EplTimerHighReskModifyTimerNs()
281 //
282 // Description: modifies the timeout of the timer with the specified handle.
283 //              If the handle the pointer points to is zero, the timer must
284 //              be created first.
285 //              If it is not possible to stop the old timer,
286 //              this function always assures that the old timer does not
287 //              trigger the callback function with the same handle as the new
288 //              timer. That means the callback function must check the passed
289 //              handle with the one returned by this function. If these are
290 //              unequal, the call can be discarded.
291 //
292 // Parameters:  pTimerHdl_p     = pointer to timer handle
293 //              ullTimeNs_p     = relative timeout in [ns]
294 //              pfnCallback_p   = callback function, which is called mutual
295 //                                exclusive with the Edrv callback functions
296 //                                (Rx and Tx).
297 //              ulArgument_p    = user-specific argument
298 //              fContinuously_p = if TRUE, callback function will be called
299 //                                continuously;
300 //                                otherwise, it is a oneshot timer.
301 //
302 // Return:      tEplKernel      = error code
303 //
304 // State:       not tested
305 //
306 //---------------------------------------------------------------------------
307
308 tEplKernel PUBLIC EplTimerHighReskModifyTimerNs(tEplTimerHdl * pTimerHdl_p,
309                                                 unsigned long long ullTimeNs_p,
310                                                 tEplTimerkCallback
311                                                 pfnCallback_p,
312                                                 unsigned long ulArgument_p,
313                                                 BOOL fContinuously_p)
314 {
315         tEplKernel Ret;
316         unsigned int uiIndex;
317         tEplTimerHighReskTimerInfo *pTimerInfo;
318         ktime_t RelTime;
319
320         Ret = kEplSuccessful;
321
322         // check pointer to handle
323         if (pTimerHdl_p == NULL) {
324                 Ret = kEplTimerInvalidHandle;
325                 goto Exit;
326         }
327
328         if (*pTimerHdl_p == 0) {        // no timer created yet
329
330                 // search free timer info structure
331                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[0];
332                 for (uiIndex = 0; uiIndex < TIMER_COUNT;
333                      uiIndex++, pTimerInfo++) {
334                         if (pTimerInfo->m_EventArg.m_TimerHdl == 0) {   // free structure found
335                                 break;
336                         }
337                 }
338                 if (uiIndex >= TIMER_COUNT) {   // no free structure found
339                         Ret = kEplTimerNoTimerCreated;
340                         goto Exit;
341                 }
342
343                 pTimerInfo->m_EventArg.m_TimerHdl = HDL_INIT(uiIndex);
344         } else {
345                 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
346                 if (uiIndex >= TIMER_COUNT) {   // invalid handle
347                         Ret = kEplTimerInvalidHandle;
348                         goto Exit;
349                 }
350
351                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
352         }
353
354         /*
355          * increment timer handle
356          * (if timer expires right after this statement, the user
357          * would detect an unknown timer handle and discard it)
358          */
359         pTimerInfo->m_EventArg.m_TimerHdl =
360             HDL_INC(pTimerInfo->m_EventArg.m_TimerHdl);
361         *pTimerHdl_p = pTimerInfo->m_EventArg.m_TimerHdl;
362
363         // reject too small time values
364         if ((fContinuously_p && (ullTimeNs_p < TIMER_MIN_VAL_CYCLE))
365             || (!fContinuously_p && (ullTimeNs_p < TIMER_MIN_VAL_SINGLE))) {
366                 Ret = kEplTimerNoTimerCreated;
367                 goto Exit;
368         }
369
370         pTimerInfo->m_EventArg.m_ulArg = ulArgument_p;
371         pTimerInfo->m_pfnCallback = pfnCallback_p;
372         pTimerInfo->m_fContinuously = fContinuously_p;
373         pTimerInfo->m_ullPeriod = ullTimeNs_p;
374
375         /*
376          * HRTIMER_MODE_REL does not influence general handling of this timer.
377          * It only sets relative mode for this start operation.
378          * -> Expire time is calculated by: Now + RelTime
379          * hrtimer_start also skips pending timer events.
380          * The state HRTIMER_STATE_CALLBACK is ignored.
381          * We have to cope with that in our callback function.
382          */
383         RelTime = ktime_add_ns(ktime_set(0, 0), ullTimeNs_p);
384         hrtimer_start(&pTimerInfo->m_Timer, RelTime, HRTIMER_MODE_REL);
385
386       Exit:
387         return Ret;
388
389 }
390
391 //---------------------------------------------------------------------------
392 //
393 // Function:    EplTimerHighReskDeleteTimer()
394 //
395 // Description: deletes the timer with the specified handle. Afterward the
396 //              handle is set to zero.
397 //
398 // Parameters:  pTimerHdl_p     = pointer to timer handle
399 //
400 // Return:      tEplKernel      = error code
401 //
402 // State:       not tested
403 //
404 //---------------------------------------------------------------------------
405
406 tEplKernel PUBLIC EplTimerHighReskDeleteTimer(tEplTimerHdl * pTimerHdl_p)
407 {
408         tEplKernel Ret = kEplSuccessful;
409         unsigned int uiIndex;
410         tEplTimerHighReskTimerInfo *pTimerInfo;
411
412         // check pointer to handle
413         if (pTimerHdl_p == NULL) {
414                 Ret = kEplTimerInvalidHandle;
415                 goto Exit;
416         }
417
418         if (*pTimerHdl_p == 0) {        // no timer created yet
419                 goto Exit;
420         } else {
421                 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
422                 if (uiIndex >= TIMER_COUNT) {   // invalid handle
423                         Ret = kEplTimerInvalidHandle;
424                         goto Exit;
425                 }
426                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
427                 if (pTimerInfo->m_EventArg.m_TimerHdl != *pTimerHdl_p) {        // invalid handle
428                         goto Exit;
429                 }
430         }
431
432         *pTimerHdl_p = 0;
433         pTimerInfo->m_EventArg.m_TimerHdl = 0;
434         pTimerInfo->m_pfnCallback = NULL;
435
436         /*
437          * Three return cases of hrtimer_try_to_cancel have to be tracked:
438          *  1 - timer has been removed
439          *  0 - timer was not active
440          *      We need not do anything. hrtimer timers just consist of
441          *      a hrtimer struct, which we might enqueue in the hrtimers
442          *      event list by calling hrtimer_start().
443          *      If a timer is not enqueued, it is not present in hrtimers.
444          * -1 - callback function is running
445          *      In this case we have to ensure that the timer is not
446          *      continuously restarted. This has been done by clearing
447          *      its handle.
448          */
449         hrtimer_try_to_cancel(&pTimerInfo->m_Timer);
450
451       Exit:
452         return Ret;
453
454 }
455
456 //---------------------------------------------------------------------------
457 //
458 // Function:    EplTimerHighReskCallback()
459 //
460 // Description: Callback function commonly used for all timers.
461 //
462 // Parameters:  pTimer_p = pointer to hrtimer
463 //
464 // Return:
465 //
466 // State:       not tested
467 //
468 //---------------------------------------------------------------------------
469
470 enum hrtimer_restart EplTimerHighReskCallback(struct hrtimer *pTimer_p)
471 {
472         unsigned int uiIndex;
473         tEplTimerHighReskTimerInfo *pTimerInfo;
474         tEplTimerHdl OrgTimerHdl;
475         enum hrtimer_restart Ret;
476
477         BENCHMARK_MOD_24_SET(4);
478
479         Ret = HRTIMER_NORESTART;
480         pTimerInfo =
481             container_of(pTimer_p, tEplTimerHighReskTimerInfo, m_Timer);
482         uiIndex = HDL_TO_IDX(pTimerInfo->m_EventArg.m_TimerHdl);
483         if (uiIndex >= TIMER_COUNT) {   // invalid handle
484                 goto Exit;
485         }
486
487         /*
488          * We store the timer handle before calling the callback function
489          * as the timer can be modified inside it.
490          */
491         OrgTimerHdl = pTimerInfo->m_EventArg.m_TimerHdl;
492
493         if (pTimerInfo->m_pfnCallback != NULL) {
494                 pTimerInfo->m_pfnCallback(&pTimerInfo->m_EventArg);
495         }
496
497         if (pTimerInfo->m_fContinuously) {
498                 ktime_t Interval;
499 #ifdef PROVE_OVERRUN
500                 ktime_t Now;
501                 unsigned long Overruns;
502 #endif
503
504                 if (OrgTimerHdl != pTimerInfo->m_EventArg.m_TimerHdl) {
505                         /* modified timer has already been restarted */
506                         goto Exit;
507                 }
508 #ifdef PROVE_OVERRUN
509                 Now = ktime_get();
510                 Interval =
511                     ktime_add_ns(ktime_set(0, 0), pTimerInfo->m_ullPeriod);
512                 Overruns = hrtimer_forward(pTimer_p, Now, Interval);
513                 if (Overruns > 1) {
514                         printk
515                             ("EplTimerHighResk: Continuous timer (handle 0x%lX) had to skip %lu interval(s)!\n",
516                              pTimerInfo->m_EventArg.m_TimerHdl, Overruns - 1);
517                 }
518 #else
519                 pTimer_p->expires = ktime_add_ns(pTimer_p->expires,
520                                                  pTimerInfo->m_ullPeriod);
521 #endif
522
523                 Ret = HRTIMER_RESTART;
524         }
525
526       Exit:
527         BENCHMARK_MOD_24_RESET(4);
528         return Ret;
529 }