Merge branch 'for-linus' of git://neil.brown.name/md
[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 // TracePoint support for realtime-debugging
101 #ifdef _DBG_TRACE_POINTS_
102 void TgtDbgSignalTracePoint(u8 bTracePointNumber_p);
103 void TgtDbgPostTraceValue(u32 dwTraceValue_p);
104 #define TGT_DBG_SIGNAL_TRACE_POINT(p)   TgtDbgSignalTracePoint(p)
105 #define TGT_DBG_POST_TRACE_VALUE(v)     TgtDbgPostTraceValue(v)
106 #else
107 #define TGT_DBG_SIGNAL_TRACE_POINT(p)
108 #define TGT_DBG_POST_TRACE_VALUE(v)
109 #endif
110 #define HRT_DBG_POST_TRACE_VALUE(Event_p, uiNodeId_p, wErrorCode_p) \
111     TGT_DBG_POST_TRACE_VALUE((0xE << 28) | (Event_p << 24) \
112                              | (uiNodeId_p << 16) | wErrorCode_p)
113
114 #define TIMERHDL_MASK         0x0FFFFFFF
115 #define TIMERHDL_SHIFT        28
116 #define HDL_TO_IDX(Hdl)       ((Hdl >> TIMERHDL_SHIFT) - 1)
117 #define HDL_INIT(Idx)         ((Idx + 1) << TIMERHDL_SHIFT)
118 #define HDL_INC(Hdl)          (((Hdl + 1) & TIMERHDL_MASK) \
119                                | (Hdl & ~TIMERHDL_MASK))
120
121 //---------------------------------------------------------------------------
122 // modul global types
123 //---------------------------------------------------------------------------
124
125 typedef struct {
126         tEplTimerEventArg m_EventArg;
127         tEplTimerkCallback m_pfnCallback;
128         struct hrtimer m_Timer;
129         BOOL m_fContinuously;
130         unsigned long long m_ullPeriod;
131
132 } tEplTimerHighReskTimerInfo;
133
134 typedef struct {
135         tEplTimerHighReskTimerInfo m_aTimerInfo[TIMER_COUNT];
136
137 } tEplTimerHighReskInstance;
138
139 //---------------------------------------------------------------------------
140 // local vars
141 //---------------------------------------------------------------------------
142
143 static tEplTimerHighReskInstance EplTimerHighReskInstance_l;
144
145 //---------------------------------------------------------------------------
146 // local function prototypes
147 //---------------------------------------------------------------------------
148
149 enum hrtimer_restart EplTimerHighReskCallback(struct hrtimer *pTimer_p);
150
151 //=========================================================================//
152 //                                                                         //
153 //          P U B L I C   F U N C T I O N S                                //
154 //                                                                         //
155 //=========================================================================//
156
157 //---------------------------------------------------------------------------
158 //
159 // Function:    EplTimerHighReskInit()
160 //
161 // Description: initializes the high resolution timer module.
162 //
163 // Parameters:  void
164 //
165 // Return:      tEplKernel      = error code
166 //
167 // State:       not tested
168 //
169 //---------------------------------------------------------------------------
170
171 tEplKernel EplTimerHighReskInit(void)
172 {
173         tEplKernel Ret;
174
175         Ret = EplTimerHighReskAddInstance();
176
177         return Ret;
178
179 }
180
181 //---------------------------------------------------------------------------
182 //
183 // Function:    EplTimerHighReskAddInstance()
184 //
185 // Description: initializes the high resolution timer module.
186 //
187 // Parameters:  void
188 //
189 // Return:      tEplKernel      = error code
190 //
191 // State:       not tested
192 //
193 //---------------------------------------------------------------------------
194
195 tEplKernel EplTimerHighReskAddInstance(void)
196 {
197         tEplKernel Ret;
198         unsigned int uiIndex;
199
200         Ret = kEplSuccessful;
201
202         EPL_MEMSET(&EplTimerHighReskInstance_l, 0,
203                    sizeof(EplTimerHighReskInstance_l));
204
205         /*
206          * Initialize hrtimer structures for all usable timers.
207          */
208         for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++) {
209                 tEplTimerHighReskTimerInfo *pTimerInfo;
210                 struct hrtimer *pTimer;
211
212                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
213                 pTimer = &pTimerInfo->m_Timer;
214                 hrtimer_init(pTimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
215
216                 pTimer->function = EplTimerHighReskCallback;
217         }
218
219         return Ret;
220 }
221
222 //---------------------------------------------------------------------------
223 //
224 // Function:    EplTimerHighReskDelInstance()
225 //
226 // Description: shuts down the high resolution timer module.
227 //
228 // Parameters:  void
229 //
230 // Return:      tEplKernel      = error code
231 //
232 // State:       not tested
233 //
234 //---------------------------------------------------------------------------
235
236 tEplKernel EplTimerHighReskDelInstance(void)
237 {
238         tEplTimerHighReskTimerInfo *pTimerInfo;
239         tEplKernel Ret;
240         unsigned int uiIndex;
241
242         Ret = kEplSuccessful;
243
244         for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++) {
245                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[0];
246                 pTimerInfo->m_pfnCallback = NULL;
247                 pTimerInfo->m_EventArg.m_TimerHdl = 0;
248                 /*
249                  * In this case we can not just try to cancel the timer.
250                  * We actually have to wait until its callback function
251                  * has returned.
252                  */
253                 hrtimer_cancel(&pTimerInfo->m_Timer);
254         }
255
256         return Ret;
257
258 }
259
260 //---------------------------------------------------------------------------
261 //
262 // Function:    EplTimerHighReskModifyTimerNs()
263 //
264 // Description: modifies the timeout of the timer with the specified handle.
265 //              If the handle the pointer points to is zero, the timer must
266 //              be created first.
267 //              If it is not possible to stop the old timer,
268 //              this function always assures that the old timer does not
269 //              trigger the callback function with the same handle as the new
270 //              timer. That means the callback function must check the passed
271 //              handle with the one returned by this function. If these are
272 //              unequal, the call can be discarded.
273 //
274 // Parameters:  pTimerHdl_p     = pointer to timer handle
275 //              ullTimeNs_p     = relative timeout in [ns]
276 //              pfnCallback_p   = callback function, which is called mutual
277 //                                exclusive with the Edrv callback functions
278 //                                (Rx and Tx).
279 //              ulArgument_p    = user-specific argument
280 //              fContinuously_p = if TRUE, callback function will be called
281 //                                continuously;
282 //                                otherwise, it is a oneshot timer.
283 //
284 // Return:      tEplKernel      = error code
285 //
286 // State:       not tested
287 //
288 //---------------------------------------------------------------------------
289
290 tEplKernel EplTimerHighReskModifyTimerNs(tEplTimerHdl *pTimerHdl_p,
291                                          unsigned long long ullTimeNs_p,
292                                          tEplTimerkCallback pfnCallback_p,
293                                          unsigned long ulArgument_p,
294                                          BOOL fContinuously_p)
295 {
296         tEplKernel Ret;
297         unsigned int uiIndex;
298         tEplTimerHighReskTimerInfo *pTimerInfo;
299         ktime_t RelTime;
300
301         Ret = kEplSuccessful;
302
303         // check pointer to handle
304         if (pTimerHdl_p == NULL) {
305                 Ret = kEplTimerInvalidHandle;
306                 goto Exit;
307         }
308
309         if (*pTimerHdl_p == 0) {        // no timer created yet
310
311                 // search free timer info structure
312                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[0];
313                 for (uiIndex = 0; uiIndex < TIMER_COUNT;
314                      uiIndex++, pTimerInfo++) {
315                         if (pTimerInfo->m_EventArg.m_TimerHdl == 0) {   // free structure found
316                                 break;
317                         }
318                 }
319                 if (uiIndex >= TIMER_COUNT) {   // no free structure found
320                         Ret = kEplTimerNoTimerCreated;
321                         goto Exit;
322                 }
323
324                 pTimerInfo->m_EventArg.m_TimerHdl = HDL_INIT(uiIndex);
325         } else {
326                 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
327                 if (uiIndex >= TIMER_COUNT) {   // invalid handle
328                         Ret = kEplTimerInvalidHandle;
329                         goto Exit;
330                 }
331
332                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
333         }
334
335         /*
336          * increment timer handle
337          * (if timer expires right after this statement, the user
338          * would detect an unknown timer handle and discard it)
339          */
340         pTimerInfo->m_EventArg.m_TimerHdl =
341             HDL_INC(pTimerInfo->m_EventArg.m_TimerHdl);
342         *pTimerHdl_p = pTimerInfo->m_EventArg.m_TimerHdl;
343
344         // reject too small time values
345         if ((fContinuously_p && (ullTimeNs_p < TIMER_MIN_VAL_CYCLE))
346             || (!fContinuously_p && (ullTimeNs_p < TIMER_MIN_VAL_SINGLE))) {
347                 Ret = kEplTimerNoTimerCreated;
348                 goto Exit;
349         }
350
351         pTimerInfo->m_EventArg.m_ulArg = ulArgument_p;
352         pTimerInfo->m_pfnCallback = pfnCallback_p;
353         pTimerInfo->m_fContinuously = fContinuously_p;
354         pTimerInfo->m_ullPeriod = ullTimeNs_p;
355
356         /*
357          * HRTIMER_MODE_REL does not influence general handling of this timer.
358          * It only sets relative mode for this start operation.
359          * -> Expire time is calculated by: Now + RelTime
360          * hrtimer_start also skips pending timer events.
361          * The state HRTIMER_STATE_CALLBACK is ignored.
362          * We have to cope with that in our callback function.
363          */
364         RelTime = ktime_add_ns(ktime_set(0, 0), ullTimeNs_p);
365         hrtimer_start(&pTimerInfo->m_Timer, RelTime, HRTIMER_MODE_REL);
366
367       Exit:
368         return Ret;
369
370 }
371
372 //---------------------------------------------------------------------------
373 //
374 // Function:    EplTimerHighReskDeleteTimer()
375 //
376 // Description: deletes the timer with the specified handle. Afterward the
377 //              handle is set to zero.
378 //
379 // Parameters:  pTimerHdl_p     = pointer to timer handle
380 //
381 // Return:      tEplKernel      = error code
382 //
383 // State:       not tested
384 //
385 //---------------------------------------------------------------------------
386
387 tEplKernel EplTimerHighReskDeleteTimer(tEplTimerHdl *pTimerHdl_p)
388 {
389         tEplKernel Ret = kEplSuccessful;
390         unsigned int uiIndex;
391         tEplTimerHighReskTimerInfo *pTimerInfo;
392
393         // check pointer to handle
394         if (pTimerHdl_p == NULL) {
395                 Ret = kEplTimerInvalidHandle;
396                 goto Exit;
397         }
398
399         if (*pTimerHdl_p == 0) {        // no timer created yet
400                 goto Exit;
401         } else {
402                 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
403                 if (uiIndex >= TIMER_COUNT) {   // invalid handle
404                         Ret = kEplTimerInvalidHandle;
405                         goto Exit;
406                 }
407                 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
408                 if (pTimerInfo->m_EventArg.m_TimerHdl != *pTimerHdl_p) {        // invalid handle
409                         goto Exit;
410                 }
411         }
412
413         *pTimerHdl_p = 0;
414         pTimerInfo->m_EventArg.m_TimerHdl = 0;
415         pTimerInfo->m_pfnCallback = NULL;
416
417         /*
418          * Three return cases of hrtimer_try_to_cancel have to be tracked:
419          *  1 - timer has been removed
420          *  0 - timer was not active
421          *      We need not do anything. hrtimer timers just consist of
422          *      a hrtimer struct, which we might enqueue in the hrtimers
423          *      event list by calling hrtimer_start().
424          *      If a timer is not enqueued, it is not present in hrtimers.
425          * -1 - callback function is running
426          *      In this case we have to ensure that the timer is not
427          *      continuously restarted. This has been done by clearing
428          *      its handle.
429          */
430         hrtimer_try_to_cancel(&pTimerInfo->m_Timer);
431
432       Exit:
433         return Ret;
434
435 }
436
437 //---------------------------------------------------------------------------
438 //
439 // Function:    EplTimerHighReskCallback()
440 //
441 // Description: Callback function commonly used for all timers.
442 //
443 // Parameters:  pTimer_p = pointer to hrtimer
444 //
445 // Return:
446 //
447 // State:       not tested
448 //
449 //---------------------------------------------------------------------------
450
451 enum hrtimer_restart EplTimerHighReskCallback(struct hrtimer *pTimer_p)
452 {
453         unsigned int uiIndex;
454         tEplTimerHighReskTimerInfo *pTimerInfo;
455         tEplTimerHdl OrgTimerHdl;
456         enum hrtimer_restart Ret;
457
458         BENCHMARK_MOD_24_SET(4);
459
460         Ret = HRTIMER_NORESTART;
461         pTimerInfo =
462             container_of(pTimer_p, tEplTimerHighReskTimerInfo, m_Timer);
463         uiIndex = HDL_TO_IDX(pTimerInfo->m_EventArg.m_TimerHdl);
464         if (uiIndex >= TIMER_COUNT) {   // invalid handle
465                 goto Exit;
466         }
467
468         /*
469          * We store the timer handle before calling the callback function
470          * as the timer can be modified inside it.
471          */
472         OrgTimerHdl = pTimerInfo->m_EventArg.m_TimerHdl;
473
474         if (pTimerInfo->m_pfnCallback != NULL) {
475                 pTimerInfo->m_pfnCallback(&pTimerInfo->m_EventArg);
476         }
477
478         if (pTimerInfo->m_fContinuously) {
479                 ktime_t Interval;
480 #ifdef PROVE_OVERRUN
481                 ktime_t Now;
482                 unsigned long Overruns;
483 #endif
484
485                 if (OrgTimerHdl != pTimerInfo->m_EventArg.m_TimerHdl) {
486                         /* modified timer has already been restarted */
487                         goto Exit;
488                 }
489 #ifdef PROVE_OVERRUN
490                 Now = ktime_get();
491                 Interval =
492                     ktime_add_ns(ktime_set(0, 0), pTimerInfo->m_ullPeriod);
493                 Overruns = hrtimer_forward(pTimer_p, Now, Interval);
494                 if (Overruns > 1) {
495                         printk
496                             ("EplTimerHighResk: Continuous timer (handle 0x%lX) had to skip %lu interval(s)!\n",
497                              pTimerInfo->m_EventArg.m_TimerHdl, Overruns - 1);
498                 }
499 #else
500                 pTimer_p->expires = ktime_add_ns(pTimer_p->expires,
501                                                  pTimerInfo->m_ullPeriod);
502 #endif
503
504                 Ret = HRTIMER_RESTART;
505         }
506
507       Exit:
508         BENCHMARK_MOD_24_RESET(4);
509         return Ret;
510 }