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