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 // 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)
107 #define TGT_DBG_SIGNAL_TRACE_POINT(p)
108 #define TGT_DBG_POST_TRACE_VALUE(v)
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)
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))
121 //---------------------------------------------------------------------------
122 // modul global types
123 //---------------------------------------------------------------------------
126 tEplTimerEventArg m_EventArg;
127 tEplTimerkCallback m_pfnCallback;
128 struct hrtimer m_Timer;
129 BOOL m_fContinuously;
130 unsigned long long m_ullPeriod;
132 } tEplTimerHighReskTimerInfo;
135 tEplTimerHighReskTimerInfo m_aTimerInfo[TIMER_COUNT];
137 } tEplTimerHighReskInstance;
139 //---------------------------------------------------------------------------
141 //---------------------------------------------------------------------------
143 static tEplTimerHighReskInstance EplTimerHighReskInstance_l;
145 //---------------------------------------------------------------------------
146 // local function prototypes
147 //---------------------------------------------------------------------------
149 enum hrtimer_restart EplTimerHighReskCallback(struct hrtimer *pTimer_p);
151 //=========================================================================//
153 // P U B L I C F U N C T I O N S //
155 //=========================================================================//
157 //---------------------------------------------------------------------------
159 // Function: EplTimerHighReskInit()
161 // Description: initializes the high resolution timer module.
165 // Return: tEplKernel = error code
169 //---------------------------------------------------------------------------
171 tEplKernel EplTimerHighReskInit(void)
175 Ret = EplTimerHighReskAddInstance();
181 //---------------------------------------------------------------------------
183 // Function: EplTimerHighReskAddInstance()
185 // Description: initializes the high resolution timer module.
189 // Return: tEplKernel = error code
193 //---------------------------------------------------------------------------
195 tEplKernel EplTimerHighReskAddInstance(void)
198 unsigned int uiIndex;
200 Ret = kEplSuccessful;
202 EPL_MEMSET(&EplTimerHighReskInstance_l, 0,
203 sizeof(EplTimerHighReskInstance_l));
206 * Initialize hrtimer structures for all usable timers.
208 for (uiIndex = 0; uiIndex < TIMER_COUNT; uiIndex++) {
209 tEplTimerHighReskTimerInfo *pTimerInfo;
210 struct hrtimer *pTimer;
212 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
213 pTimer = &pTimerInfo->m_Timer;
214 hrtimer_init(pTimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
216 pTimer->function = EplTimerHighReskCallback;
222 //---------------------------------------------------------------------------
224 // Function: EplTimerHighReskDelInstance()
226 // Description: shuts down the high resolution timer module.
230 // Return: tEplKernel = error code
234 //---------------------------------------------------------------------------
236 tEplKernel EplTimerHighReskDelInstance(void)
238 tEplTimerHighReskTimerInfo *pTimerInfo;
240 unsigned int uiIndex;
242 Ret = kEplSuccessful;
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;
249 * In this case we can not just try to cancel the timer.
250 * We actually have to wait until its callback function
253 hrtimer_cancel(&pTimerInfo->m_Timer);
260 //---------------------------------------------------------------------------
262 // Function: EplTimerHighReskModifyTimerNs()
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
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.
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
279 // ulArgument_p = user-specific argument
280 // fContinuously_p = if TRUE, callback function will be called
282 // otherwise, it is a oneshot timer.
284 // Return: tEplKernel = error code
288 //---------------------------------------------------------------------------
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)
297 unsigned int uiIndex;
298 tEplTimerHighReskTimerInfo *pTimerInfo;
301 Ret = kEplSuccessful;
303 // check pointer to handle
304 if (pTimerHdl_p == NULL) {
305 Ret = kEplTimerInvalidHandle;
309 if (*pTimerHdl_p == 0) { // no timer created yet
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
319 if (uiIndex >= TIMER_COUNT) { // no free structure found
320 Ret = kEplTimerNoTimerCreated;
324 pTimerInfo->m_EventArg.m_TimerHdl = HDL_INIT(uiIndex);
326 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
327 if (uiIndex >= TIMER_COUNT) { // invalid handle
328 Ret = kEplTimerInvalidHandle;
332 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
336 * increment timer handle
337 * (if timer expires right after this statement, the user
338 * would detect an unknown timer handle and discard it)
340 pTimerInfo->m_EventArg.m_TimerHdl =
341 HDL_INC(pTimerInfo->m_EventArg.m_TimerHdl);
342 *pTimerHdl_p = pTimerInfo->m_EventArg.m_TimerHdl;
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;
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;
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.
364 RelTime = ktime_add_ns(ktime_set(0, 0), ullTimeNs_p);
365 hrtimer_start(&pTimerInfo->m_Timer, RelTime, HRTIMER_MODE_REL);
372 //---------------------------------------------------------------------------
374 // Function: EplTimerHighReskDeleteTimer()
376 // Description: deletes the timer with the specified handle. Afterward the
377 // handle is set to zero.
379 // Parameters: pTimerHdl_p = pointer to timer handle
381 // Return: tEplKernel = error code
385 //---------------------------------------------------------------------------
387 tEplKernel EplTimerHighReskDeleteTimer(tEplTimerHdl *pTimerHdl_p)
389 tEplKernel Ret = kEplSuccessful;
390 unsigned int uiIndex;
391 tEplTimerHighReskTimerInfo *pTimerInfo;
393 // check pointer to handle
394 if (pTimerHdl_p == NULL) {
395 Ret = kEplTimerInvalidHandle;
399 if (*pTimerHdl_p == 0) { // no timer created yet
402 uiIndex = HDL_TO_IDX(*pTimerHdl_p);
403 if (uiIndex >= TIMER_COUNT) { // invalid handle
404 Ret = kEplTimerInvalidHandle;
407 pTimerInfo = &EplTimerHighReskInstance_l.m_aTimerInfo[uiIndex];
408 if (pTimerInfo->m_EventArg.m_TimerHdl != *pTimerHdl_p) { // invalid handle
414 pTimerInfo->m_EventArg.m_TimerHdl = 0;
415 pTimerInfo->m_pfnCallback = NULL;
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
430 hrtimer_try_to_cancel(&pTimerInfo->m_Timer);
437 //---------------------------------------------------------------------------
439 // Function: EplTimerHighReskCallback()
441 // Description: Callback function commonly used for all timers.
443 // Parameters: pTimer_p = pointer to hrtimer
449 //---------------------------------------------------------------------------
451 enum hrtimer_restart EplTimerHighReskCallback(struct hrtimer *pTimer_p)
453 unsigned int uiIndex;
454 tEplTimerHighReskTimerInfo *pTimerInfo;
455 tEplTimerHdl OrgTimerHdl;
456 enum hrtimer_restart Ret;
458 BENCHMARK_MOD_24_SET(4);
460 Ret = HRTIMER_NORESTART;
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
469 * We store the timer handle before calling the callback function
470 * as the timer can be modified inside it.
472 OrgTimerHdl = pTimerInfo->m_EventArg.m_TimerHdl;
474 if (pTimerInfo->m_pfnCallback != NULL) {
475 pTimerInfo->m_pfnCallback(&pTimerInfo->m_EventArg);
478 if (pTimerInfo->m_fContinuously) {
482 unsigned long Overruns;
485 if (OrgTimerHdl != pTimerInfo->m_EventArg.m_TimerHdl) {
486 /* modified timer has already been restarted */
492 ktime_add_ns(ktime_set(0, 0), pTimerInfo->m_ullPeriod);
493 Overruns = hrtimer_forward(pTimer_p, Now, Interval);
496 ("EplTimerHighResk: Continuous timer (handle 0x%lX) had to skip %lu interval(s)!\n",
497 pTimerInfo->m_EventArg.m_TimerHdl, Overruns - 1);
500 pTimer_p->expires = ktime_add_ns(pTimer_p->expires,
501 pTimerInfo->m_ullPeriod);
504 Ret = HRTIMER_RESTART;
508 BENCHMARK_MOD_24_RESET(4);