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