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 */
100 #ifndef CONFIG_HIGH_RES_TIMERS
101 #error "Kernel symbol CONFIG_HIGH_RES_TIMERS is required."
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)
111 #define TGT_DBG_SIGNAL_TRACE_POINT(p)
112 #define TGT_DBG_POST_TRACE_VALUE(v)
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)
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))
125 //---------------------------------------------------------------------------
126 // modul global types
127 //---------------------------------------------------------------------------
130 tEplTimerEventArg m_EventArg;
131 tEplTimerkCallback m_pfnCallback;
132 struct hrtimer m_Timer;
133 BOOL m_fContinuously;
134 unsigned long long m_ullPeriod;
136 } tEplTimerHighReskTimerInfo;
139 tEplTimerHighReskTimerInfo m_aTimerInfo[TIMER_COUNT];
141 } tEplTimerHighReskInstance;
143 //---------------------------------------------------------------------------
145 //---------------------------------------------------------------------------
147 static tEplTimerHighReskInstance EplTimerHighReskInstance_l;
149 //---------------------------------------------------------------------------
150 // local function prototypes
151 //---------------------------------------------------------------------------
153 enum hrtimer_restart EplTimerHighReskCallback(struct hrtimer *pTimer_p);
155 //=========================================================================//
157 // P U B L I C F U N C T I O N S //
159 //=========================================================================//
161 //---------------------------------------------------------------------------
163 // Function: EplTimerHighReskInit()
165 // Description: initializes the high resolution timer module.
169 // Return: tEplKernel = error code
173 //---------------------------------------------------------------------------
175 tEplKernel PUBLIC EplTimerHighReskInit(void)
179 Ret = EplTimerHighReskAddInstance();
185 //---------------------------------------------------------------------------
187 // Function: EplTimerHighReskAddInstance()
189 // Description: initializes the high resolution timer module.
193 // Return: tEplKernel = error code
197 //---------------------------------------------------------------------------
199 tEplKernel PUBLIC EplTimerHighReskAddInstance(void)
202 unsigned int uiIndex;
204 Ret = kEplSuccessful;
206 EPL_MEMSET(&EplTimerHighReskInstance_l, 0,
207 sizeof(EplTimerHighReskInstance_l));
209 #ifndef CONFIG_HIGH_RES_TIMERS
211 ("EplTimerHighResk: Kernel symbol CONFIG_HIGH_RES_TIMERS is required.\n");
212 Ret = kEplNoResource;
217 * Initialize hrtimer structures for all usable timers.
219 for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++) {
220 tEplTimerHighReskTimerInfo *pTimerInfo;
221 struct hrtimer *pTimer;
223 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
224 pTimer = &pTimerInfo->m_Timer;
225 hrtimer_init(pTimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
227 pTimer->function = EplTimerHighReskCallback;
230 * We use HRTIMER_CB_SOFTIRQ here.
231 * HRTIMER_CB_IRQSAFE is critical as the callback function
232 * would be called with IRQs disabled.
234 pTimer->cb_mode = HRTIMER_CB_SOFTIRQ;
240 //---------------------------------------------------------------------------
242 // Function: EplTimerHighReskDelInstance()
244 // Description: shuts down the high resolution timer module.
248 // Return: tEplKernel = error code
252 //---------------------------------------------------------------------------
254 tEplKernel PUBLIC EplTimerHighReskDelInstance(void)
256 tEplTimerHighReskTimerInfo *pTimerInfo;
258 unsigned int uiIndex;
260 Ret = kEplSuccessful;
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;
267 * In this case we can not just try to cancel the timer.
268 * We actually have to wait until its callback function
271 hrtimer_cancel(&pTimerInfo->m_Timer);
278 //---------------------------------------------------------------------------
280 // Function: EplTimerHighReskModifyTimerNs()
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
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.
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
297 // ulArgument_p = user-specific argument
298 // fContinuously_p = if TRUE, callback function will be called
300 // otherwise, it is a oneshot timer.
302 // Return: tEplKernel = error code
306 //---------------------------------------------------------------------------
308 tEplKernel PUBLIC EplTimerHighReskModifyTimerNs(tEplTimerHdl * pTimerHdl_p,
309 unsigned long long ullTimeNs_p,
312 unsigned long ulArgument_p,
313 BOOL fContinuously_p)
316 unsigned int uiIndex;
317 tEplTimerHighReskTimerInfo *pTimerInfo;
320 Ret = kEplSuccessful;
322 // check pointer to handle
323 if (pTimerHdl_p == NULL) {
324 Ret = kEplTimerInvalidHandle;
328 if (*pTimerHdl_p == 0) { // no timer created yet
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
338 if (uiIndex >= TIMER_COUNT) { // no free structure found
339 Ret = kEplTimerNoTimerCreated;
343 pTimerInfo->m_EventArg.m_TimerHdl = HDL_INIT(uiIndex);
345 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
346 if (uiIndex >= TIMER_COUNT) { // invalid handle
347 Ret = kEplTimerInvalidHandle;
351 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
355 * increment timer handle
356 * (if timer expires right after this statement, the user
357 * would detect an unknown timer handle and discard it)
359 pTimerInfo->m_EventArg.m_TimerHdl =
360 HDL_INC(pTimerInfo->m_EventArg.m_TimerHdl);
361 *pTimerHdl_p = pTimerInfo->m_EventArg.m_TimerHdl;
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;
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;
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.
383 RelTime = ktime_add_ns(ktime_set(0, 0), ullTimeNs_p);
384 hrtimer_start(&pTimerInfo->m_Timer, RelTime, HRTIMER_MODE_REL);
391 //---------------------------------------------------------------------------
393 // Function: EplTimerHighReskDeleteTimer()
395 // Description: deletes the timer with the specified handle. Afterward the
396 // handle is set to zero.
398 // Parameters: pTimerHdl_p = pointer to timer handle
400 // Return: tEplKernel = error code
404 //---------------------------------------------------------------------------
406 tEplKernel PUBLIC EplTimerHighReskDeleteTimer(tEplTimerHdl * pTimerHdl_p)
408 tEplKernel Ret = kEplSuccessful;
409 unsigned int uiIndex;
410 tEplTimerHighReskTimerInfo *pTimerInfo;
412 // check pointer to handle
413 if (pTimerHdl_p == NULL) {
414 Ret = kEplTimerInvalidHandle;
418 if (*pTimerHdl_p == 0) { // no timer created yet
421 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
422 if (uiIndex >= TIMER_COUNT) { // invalid handle
423 Ret = kEplTimerInvalidHandle;
426 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
427 if (pTimerInfo->m_EventArg.m_TimerHdl != *pTimerHdl_p) { // invalid handle
433 pTimerInfo->m_EventArg.m_TimerHdl = 0;
434 pTimerInfo->m_pfnCallback = NULL;
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
449 hrtimer_try_to_cancel(&pTimerInfo->m_Timer);
456 //---------------------------------------------------------------------------
458 // Function: EplTimerHighReskCallback()
460 // Description: Callback function commonly used for all timers.
462 // Parameters: pTimer_p = pointer to hrtimer
468 //---------------------------------------------------------------------------
470 enum hrtimer_restart EplTimerHighReskCallback(struct hrtimer *pTimer_p)
472 unsigned int uiIndex;
473 tEplTimerHighReskTimerInfo *pTimerInfo;
474 tEplTimerHdl OrgTimerHdl;
475 enum hrtimer_restart Ret;
477 BENCHMARK_MOD_24_SET(4);
479 Ret = HRTIMER_NORESTART;
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
488 * We store the timer handle before calling the callback function
489 * as the timer can be modified inside it.
491 OrgTimerHdl = pTimerInfo->m_EventArg.m_TimerHdl;
493 if (pTimerInfo->m_pfnCallback != NULL) {
494 pTimerInfo->m_pfnCallback(&pTimerInfo->m_EventArg);
497 if (pTimerInfo->m_fContinuously) {
501 unsigned long Overruns;
504 if (OrgTimerHdl != pTimerInfo->m_EventArg.m_TimerHdl) {
505 /* modified timer has already been restarted */
511 ktime_add_ns(ktime_set(0, 0), pTimerInfo->m_ullPeriod);
512 Overruns = hrtimer_forward(pTimer_p, Now, Interval);
515 ("EplTimerHighResk: Continuous timer (handle 0x%lX) had to skip %lu interval(s)!\n",
516 pTimerInfo->m_EventArg.m_TimerHdl, Overruns - 1);
519 pTimer_p->expires = ktime_add_ns(pTimer_p->expires,
520 pTimerInfo->m_ullPeriod);
523 Ret = HRTIMER_RESTART;
527 BENCHMARK_MOD_24_RESET(4);