1 /****************************************************************************
3 (c) SYSTEC electronic GmbH, D-07973 Greiz, August-Bebel-Str. 29
4 www.systec-electronic.com
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.
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
20 1. Redistributions of source code must retain the above copyright
21 notice, this list of conditions and the following disclaimer.
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.
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.
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.
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.
54 -------------------------------------------------------------------------
56 $RCSfile: TimerHighReskX86.c,v $
60 $Revision: 1.4 $ $Date: 2008/04/17 21:38:01 $
67 -------------------------------------------------------------------------
71 ****************************************************************************/
74 #include "kernel/EplTimerHighResk.h"
75 #include "Benchmark.h"
77 //#include <linux/config.h>
78 #include <linux/module.h>
79 #include <linux/kernel.h>
80 #include <linux/hrtimer.h>
82 /***************************************************************************/
85 /* G L O B A L D E F I N I T I O N S */
88 /***************************************************************************/
90 //---------------------------------------------------------------------------
92 //---------------------------------------------------------------------------
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 */
101 #ifndef CONFIG_HIGH_RES_TIMERS
102 #error "Kernel symbol CONFIG_HIGH_RES_TIMERS is required."
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)
113 #define TGT_DBG_SIGNAL_TRACE_POINT(p)
114 #define TGT_DBG_POST_TRACE_VALUE(v)
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)
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))
127 //---------------------------------------------------------------------------
128 // modul global types
129 //---------------------------------------------------------------------------
133 tEplTimerEventArg m_EventArg;
134 tEplTimerkCallback m_pfnCallback;
135 struct hrtimer m_Timer;
136 BOOL m_fContinuously;
137 unsigned long long m_ullPeriod;
139 } tEplTimerHighReskTimerInfo;
143 tEplTimerHighReskTimerInfo m_aTimerInfo[TIMER_COUNT];
145 } tEplTimerHighReskInstance;
147 //---------------------------------------------------------------------------
149 //---------------------------------------------------------------------------
151 static tEplTimerHighReskInstance EplTimerHighReskInstance_l;
153 //---------------------------------------------------------------------------
154 // local function prototypes
155 //---------------------------------------------------------------------------
157 enum hrtimer_restart EplTimerHighReskCallback (struct hrtimer* pTimer_p);
159 //=========================================================================//
161 // P U B L I C F U N C T I O N S //
163 //=========================================================================//
166 //---------------------------------------------------------------------------
168 // Function: EplTimerHighReskInit()
170 // Description: initializes the high resolution timer module.
174 // Return: tEplKernel = error code
178 //---------------------------------------------------------------------------
180 tEplKernel PUBLIC EplTimerHighReskInit(void)
184 Ret = EplTimerHighReskAddInstance();
191 //---------------------------------------------------------------------------
193 // Function: EplTimerHighReskAddInstance()
195 // Description: initializes the high resolution timer module.
199 // Return: tEplKernel = error code
203 //---------------------------------------------------------------------------
205 tEplKernel PUBLIC EplTimerHighReskAddInstance(void)
208 unsigned int uiIndex;
210 Ret = kEplSuccessful;
212 EPL_MEMSET(&EplTimerHighReskInstance_l, 0, sizeof (EplTimerHighReskInstance_l));
214 #ifndef CONFIG_HIGH_RES_TIMERS
215 printk("EplTimerHighResk: Kernel symbol CONFIG_HIGH_RES_TIMERS is required.\n");
216 Ret = kEplNoResource;
221 * Initialize hrtimer structures for all usable timers.
223 for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++)
225 tEplTimerHighReskTimerInfo* pTimerInfo;
226 struct hrtimer* pTimer;
228 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
229 pTimer = &pTimerInfo->m_Timer;
230 hrtimer_init(pTimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
232 pTimer->function = EplTimerHighReskCallback;
235 * We use HRTIMER_CB_SOFTIRQ here.
236 * HRTIMER_CB_IRQSAFE is critical as the callback function
237 * would be called with IRQs disabled.
239 pTimer->cb_mode = HRTIMER_CB_SOFTIRQ;
246 //---------------------------------------------------------------------------
248 // Function: EplTimerHighReskDelInstance()
250 // Description: shuts down the high resolution timer module.
254 // Return: tEplKernel = error code
258 //---------------------------------------------------------------------------
260 tEplKernel PUBLIC EplTimerHighReskDelInstance(void)
262 tEplTimerHighReskTimerInfo* pTimerInfo;
264 unsigned int uiIndex;
266 Ret = kEplSuccessful;
268 for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++)
270 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[0];
271 pTimerInfo->m_pfnCallback = NULL;
272 pTimerInfo->m_EventArg.m_TimerHdl = 0;
274 * In this case we can not just try to cancel the timer.
275 * We actually have to wait until its callback function
278 hrtimer_cancel(&pTimerInfo->m_Timer);
286 //---------------------------------------------------------------------------
288 // Function: EplTimerHighReskModifyTimerNs()
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
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.
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
305 // ulArgument_p = user-specific argument
306 // fContinuously_p = if TRUE, callback function will be called
308 // otherwise, it is a oneshot timer.
310 // Return: tEplKernel = error code
314 //---------------------------------------------------------------------------
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)
323 unsigned int uiIndex;
324 tEplTimerHighReskTimerInfo* pTimerInfo;
327 Ret = kEplSuccessful;
329 // check pointer to handle
330 if(pTimerHdl_p == NULL)
332 Ret = kEplTimerInvalidHandle;
336 if (*pTimerHdl_p == 0)
337 { // no timer created yet
339 // search free timer info structure
340 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[0];
341 for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++, pTimerInfo++)
343 if (pTimerInfo->m_EventArg.m_TimerHdl == 0)
344 { // free structure found
348 if (uiIndex >= TIMER_COUNT)
349 { // no free structure found
350 Ret = kEplTimerNoTimerCreated;
354 pTimerInfo->m_EventArg.m_TimerHdl = HDL_INIT(uiIndex);
358 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
359 if (uiIndex >= TIMER_COUNT)
361 Ret = kEplTimerInvalidHandle;
365 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
369 * increment timer handle
370 * (if timer expires right after this statement, the user
371 * would detect an unknown timer handle and discard it)
373 pTimerInfo->m_EventArg.m_TimerHdl = HDL_INC(pTimerInfo->m_EventArg.m_TimerHdl);
374 *pTimerHdl_p = pTimerInfo->m_EventArg.m_TimerHdl;
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)) )
380 Ret = kEplTimerNoTimerCreated;
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;
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.
397 RelTime = ktime_add_ns(ktime_set(0,0), ullTimeNs_p);
398 hrtimer_start(&pTimerInfo->m_Timer, RelTime, HRTIMER_MODE_REL);
406 //---------------------------------------------------------------------------
408 // Function: EplTimerHighReskDeleteTimer()
410 // Description: deletes the timer with the specified handle. Afterward the
411 // handle is set to zero.
413 // Parameters: pTimerHdl_p = pointer to timer handle
415 // Return: tEplKernel = error code
419 //---------------------------------------------------------------------------
421 tEplKernel PUBLIC EplTimerHighReskDeleteTimer(tEplTimerHdl* pTimerHdl_p)
423 tEplKernel Ret = kEplSuccessful;
424 unsigned int uiIndex;
425 tEplTimerHighReskTimerInfo* pTimerInfo;
427 // check pointer to handle
428 if(pTimerHdl_p == NULL)
430 Ret = kEplTimerInvalidHandle;
434 if (*pTimerHdl_p == 0)
435 { // no timer created yet
440 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
441 if (uiIndex >= TIMER_COUNT)
443 Ret = kEplTimerInvalidHandle;
446 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
447 if (pTimerInfo->m_EventArg.m_TimerHdl != *pTimerHdl_p)
454 pTimerInfo->m_EventArg.m_TimerHdl = 0;
455 pTimerInfo->m_pfnCallback = NULL;
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
470 hrtimer_try_to_cancel(&pTimerInfo->m_Timer);
479 //---------------------------------------------------------------------------
481 // Function: EplTimerHighReskCallback()
483 // Description: Callback function commonly used for all timers.
485 // Parameters: pTimer_p = pointer to hrtimer
491 //---------------------------------------------------------------------------
493 enum hrtimer_restart EplTimerHighReskCallback (struct hrtimer* pTimer_p)
495 unsigned int uiIndex;
496 tEplTimerHighReskTimerInfo* pTimerInfo;
497 tEplTimerHdl OrgTimerHdl;
498 enum hrtimer_restart Ret;
500 BENCHMARK_MOD_24_SET(4);
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)
511 * We store the timer handle before calling the callback function
512 * as the timer can be modified inside it.
514 OrgTimerHdl = pTimerInfo->m_EventArg.m_TimerHdl;
516 if (pTimerInfo->m_pfnCallback != NULL)
518 pTimerInfo->m_pfnCallback(&pTimerInfo->m_EventArg);
521 if (pTimerInfo->m_fContinuously)
526 unsigned long Overruns;
529 if (OrgTimerHdl != pTimerInfo->m_EventArg.m_TimerHdl)
531 /* modified timer has already been restarted */
537 Interval = ktime_add_ns(ktime_set(0,0), pTimerInfo->m_ullPeriod);
538 Overruns = hrtimer_forward(pTimer_p, Now, Interval);
541 printk("EplTimerHighResk: Continuous timer (handle 0x%lX) had to skip %lu interval(s)!\n", pTimerInfo->m_EventArg.m_TimerHdl, Overruns-1);
544 pTimer_p->expires = ktime_add_ns(pTimer_p->expires,
545 pTimerInfo->m_ullPeriod);
548 Ret = HRTIMER_RESTART;
552 BENCHMARK_MOD_24_RESET(4);