1 /*
2  * Copyright (c) 2017-2019, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <stdint.h>
34 #include <stddef.h>
35 #include <stdbool.h>
36 
37 #include <ti/drivers/dpl/HwiP.h>
38 #include <ti/drivers/dpl/SemaphoreP.h>
39 #include <ti/drivers/dpl/ClockP.h>
40 
41 #include <ti/drivers/Timer.h>
42 #include <ti/drivers/power/PowerCC32XX.h>
43 #include <ti/drivers/timer/TimerCC32XX.h>
44 
45 #include <ti/devices/cc32xx/inc/hw_types.h>
46 #include <ti/devices/cc32xx/inc/hw_memmap.h>
47 #include <ti/devices/cc32xx/inc/hw_timer.h>
48 #include <ti/devices/cc32xx/driverlib/timer.h>
49 
50 /*
51  * This macro is used to determine a logical shift value for the
52  * timerState.bitMask. Each timer peripheral occupies two bits in
53  * timerState.bitMask.
54  *
55  * The timer peripherals' base addresses have an offset of 0x1000 starting at
56  * 0x40030000. That byte is masked using 0xF000 which can result in a value
57  * ranging from 0x0000 to 0x3000 for this particular hardware instance. This
58  * value is then shifted right by 12 into the LSB. Lastly, the value is
59  * multiplied by two because there are two bits in the timerState.bitMask for
60  * each timer. The value returned is used for the logical shift.
61  */
62 #define timerMaskShift(baseAddress) ((((baseAddress) & 0XF000) >> 12) * 2)
63 
64 void TimerCC32XX_close(Timer_Handle handle);
65 int_fast16_t TimerCC32XX_control(Timer_Handle handle,
66      uint_fast16_t cmd, void *arg);
67 uint32_t TimerCC32XX_getCount(Timer_Handle handle);
68 void TimerCC32XX_init(Timer_Handle handle);
69 Timer_Handle TimerCC32XX_open(Timer_Handle handle, Timer_Params *params);
70 int32_t TimerCC32XX_setPeriod(Timer_Handle handle, Timer_PeriodUnits periodUnits, uint32_t period);
71 int32_t TimerCC32XX_start(Timer_Handle handle);
72 void TimerCC32XX_stop(Timer_Handle handle);
73 
74 /* Internal static Functions */
75 static void initHw(Timer_Handle handle);
76 static void getPrescaler(Timer_Handle handle);
77 static uint32_t getPowerMgrId(uint32_t baseAddress);
78 static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
79     uintptr_t clientArg);
80 static void TimerCC32XX_hwiIntFunction(uintptr_t arg);
81 
82 /* Function table for TimerCC32XX implementation */
83 const Timer_FxnTable TimerCC32XX_fxnTable = {
84     .closeFxn     = TimerCC32XX_close,
85     .openFxn      = TimerCC32XX_open,
86     .startFxn     = TimerCC32XX_start,
87     .stopFxn      = TimerCC32XX_stop,
88     .initFxn      = TimerCC32XX_init,
89     .getCountFxn  = TimerCC32XX_getCount,
90     .controlFxn   = TimerCC32XX_control,
91     .setPeriodFxn = TimerCC32XX_setPeriod
92 };
93 
94 /*
95  * Internal Timer status structure
96  *
97  * bitMask: Each timer peripheral occupies two bits in the bitMask. The least
98  * significant bit represents the first half width timer, TimerCC32XX_timer16A
99  * and the most significant bit represents the second half width timer,
100  * TimerCC32XX_timer16B. If the full width timer, TimerCC32XX_timer32, is used,
101  * both bits are set to 1.
102 
103  *    31 - 8     7 - 6    5 - 4    3 - 2    1 - 0
104  *  ------------------------------------------------
105  *  | Reserved | Timer3 | Timer2 | Timer1 | Timer0 |
106  *  ------------------------------------------------
107  */
108 static struct {
109     uint32_t bitMask;
110 } timerState;
111 
112 /*
113  *  ======== initHw ========
114  */
initHw(Timer_Handle handle)115 static void initHw(Timer_Handle handle)
116 {
117     TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
118     TimerCC32XX_Object  const *object = handle->object;
119 
120     /* Ensure the timer is disabled */
121     TimerDisable(hwAttrs->baseAddress, object->timer);
122 
123     if (object->timer == TIMER_A) {
124 
125         HWREG(hwAttrs->baseAddress + TIMER_O_TAMR) = TIMER_TAMR_TAMR_PERIOD;
126     }
127     else {
128 
129         HWREG(hwAttrs->baseAddress + TIMER_O_TBMR) = TIMER_TBMR_TBMR_PERIOD;
130     }
131 
132     if (hwAttrs->subTimer == TimerCC32XX_timer32) {
133 
134         HWREG(hwAttrs->baseAddress + TIMER_O_CFG) = TIMER_CFG_32_BIT_TIMER;
135     }
136     else {
137 
138         HWREG(hwAttrs->baseAddress + TIMER_O_CFG) = TIMER_CFG_16_BIT;
139     }
140 
141     /* Disable all interrupts */
142     HWREG(hwAttrs->baseAddress + TIMER_O_IMR) = ~object->timer;
143 
144     /* Writing the PSR Register has no effect for full width 32-bit mode */
145     TimerPrescaleSet(hwAttrs->baseAddress, object->timer, object->prescaler);
146     TimerLoadSet(hwAttrs->baseAddress, object->timer, object->period);
147 
148     /* This function controls the stall response for the timer. When true,
149      * the timer stops counting if the processor enters debug mode. The
150      * default setting for the hardware is false.
151      */
152     TimerControlStall(hwAttrs->baseAddress, object->timer, true);
153 }
154 
155 /*
156  * ========= getPrescaler =========
157  * This function calculates the prescaler and timer interval load register
158  * for a half timer. The handle is assumed to contain a object->period which
159  * represents the number of clock cycles in the desired period. The calling
160  * function, TimerCC32XX_open() checks for overflow before calling this function.
161  * Therefore, this function is guaranteed to never fail.
162  */
getPrescaler(Timer_Handle handle)163 static void getPrescaler(Timer_Handle handle)
164 {
165     TimerCC32XX_Object        *object = handle->object;
166     uint32_t                   bestDiff = ~0, bestPsr = 0, bestIload = 0;
167     uint32_t                   diff, intervalLoad, prescaler;
168 
169     /* Loop over the 8-bit prescaler */
170     for (prescaler = 1; prescaler < 256; prescaler++) {
171 
172         /* Calculate timer interval load */
173         intervalLoad = object->period / (prescaler + 1);
174 
175         /* Will this fit in 16-bits? */
176         if (intervalLoad > (uint16_t) ~0) {
177             continue;
178         }
179 
180         /* How close is the intervalLoad to what we actually want? */
181         diff = object->period - intervalLoad * (prescaler + 1);
182 
183         /* If it is closer to what we want */
184         if (diff <= bestDiff) {
185 
186             /* If its a perfect match */
187             if (diff == 0) {
188                 object->period = intervalLoad;
189                 object->prescaler = prescaler;
190 
191                 return;
192             }
193 
194             /* Snapshot in case we don't find something better */
195             bestDiff = diff;
196             bestPsr = prescaler;
197             bestIload = intervalLoad;
198         }
199     }
200 
201     /* Never found a perfect match, settle for the best */
202     object->period = bestIload;
203     object->prescaler = bestPsr;
204 }
205 
206 /*
207  *  ======== getPowerMgrId ========
208  */
getPowerMgrId(uint32_t baseAddress)209 static uint32_t getPowerMgrId(uint32_t baseAddress)
210 {
211     switch (baseAddress) {
212 
213         case TIMERA0_BASE:
214 
215             return (PowerCC32XX_PERIPH_TIMERA0);
216 
217         case TIMERA1_BASE:
218 
219             return (PowerCC32XX_PERIPH_TIMERA1);
220 
221         case TIMERA2_BASE:
222 
223             return (PowerCC32XX_PERIPH_TIMERA2);
224 
225         case TIMERA3_BASE:
226 
227             return (PowerCC32XX_PERIPH_TIMERA3);
228 
229         default:
230 
231             return ((uint32_t) -1);
232     }
233 }
234 
235 /*
236  *  ======== postNotifyFxn ========
237  *  This functions is called when a transition from LPDS mode is made.
238  *  clientArg should be a handle of a previously opened Timer instance.
239  */
postNotifyFxn(unsigned int eventType,uintptr_t eventArg,uintptr_t clientArg)240 static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
241     uintptr_t clientArg)
242 {
243     initHw((Timer_Handle) clientArg);
244 
245     return (Power_NOTIFYDONE);
246 }
247 
248 /*
249  *  ======== TimerCC32XX_allocateTimerResource ========
250  */
TimerCC32XX_allocateTimerResource(uint32_t baseAddress,TimerCC32XX_SubTimer subTimer)251 bool TimerCC32XX_allocateTimerResource(uint32_t baseAddress,
252     TimerCC32XX_SubTimer subTimer)
253 {
254     uintptr_t key;
255     uint32_t  mask;
256     uint32_t  powerMgrId;
257     bool      status;
258 
259     powerMgrId = getPowerMgrId(baseAddress);
260 
261     if (powerMgrId == (uint32_t) -1) {
262 
263         return (false);
264     }
265 
266     mask = subTimer << timerMaskShift(baseAddress);
267 
268     key = HwiP_disable();
269 
270     if (timerState.bitMask & mask) {
271 
272         status = false;
273     }
274     else {
275 
276         Power_setDependency(powerMgrId);
277         timerState.bitMask = timerState.bitMask | mask;
278         status = true;
279     }
280 
281     HwiP_restore(key);
282 
283     return (status);
284 }
285 
286 /*
287  *  ======== TimerCC32XX_close ========
288  */
TimerCC32XX_close(Timer_Handle handle)289 void TimerCC32XX_close(Timer_Handle handle)
290 {
291     TimerCC32XX_Object        *object = handle->object;
292     TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
293 
294     /* Stopping the Timer before closing it */
295     TimerCC32XX_stop(handle);
296 
297     Power_unregisterNotify(&(object->notifyObj));
298 
299     if (object->hwiHandle) {
300 
301         HwiP_clearInterrupt(hwAttrs->intNum);
302         HwiP_delete(object->hwiHandle);
303         object->hwiHandle = NULL;
304     }
305 
306     if (object->timerSem) {
307 
308         SemaphoreP_delete(object->timerSem);
309         object->timerSem = NULL;
310     }
311 
312     TimerCC32XX_freeTimerResource(hwAttrs->baseAddress, hwAttrs->subTimer);
313 }
314 
315 /*
316  *  ======== TimerCC32XX_control ========
317  */
TimerCC32XX_control(Timer_Handle handle,uint_fast16_t cmd,void * arg)318 int_fast16_t TimerCC32XX_control(Timer_Handle handle,
319         uint_fast16_t cmd, void *arg)
320 {
321     return (Timer_STATUS_UNDEFINEDCMD);
322 }
323 
324 /*
325  *  ======== TimerCC32XX_freeTimerResource ========
326  */
TimerCC32XX_freeTimerResource(uint32_t baseAddress,TimerCC32XX_SubTimer subTimer)327 void TimerCC32XX_freeTimerResource(uint32_t baseAddress,
328     TimerCC32XX_SubTimer subTimer)
329 {
330     uintptr_t key;
331     uint32_t  mask;
332 
333     mask = subTimer << timerMaskShift(baseAddress);
334 
335     key = HwiP_disable();
336 
337     timerState.bitMask = (timerState.bitMask & ~mask);
338 
339     Power_releaseDependency(getPowerMgrId(baseAddress));
340 
341     HwiP_restore(key);
342 }
343 
344 /*
345  *  ======== TimerCC32XX_getCount ========
346  */
TimerCC32XX_getCount(Timer_Handle handle)347 uint32_t TimerCC32XX_getCount(Timer_Handle handle)
348 {
349     TimerCC32XX_HWAttrs const *hWAttrs = handle->hwAttrs;
350     TimerCC32XX_Object  const *object = handle->object;
351     uint32_t                   count;
352 
353     if (object->timer == TIMER_A) {
354         count = HWREG(hWAttrs->baseAddress + TIMER_O_TAR);
355     }
356     else {
357         count = HWREG(hWAttrs->baseAddress + TIMER_O_TBR);
358     }
359 
360     /* Virtual up counter */
361     count = object->period - count;
362 
363     return (count);
364 }
365 
366 /*
367  *  ======== TimerCC32XX_hwiIntFunction ========
368  */
TimerCC32XX_hwiIntFunction(uintptr_t arg)369 void TimerCC32XX_hwiIntFunction(uintptr_t arg)
370 {
371     Timer_Handle handle = (Timer_Handle) arg;
372     TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
373     TimerCC32XX_Object  const *object  = handle->object;
374     uint32_t                   interruptMask;
375 
376     /* Only clear the interrupt for this->object->timer */
377     interruptMask = object->timer & (TIMER_TIMA_TIMEOUT | TIMER_TIMB_TIMEOUT);
378     TimerIntClear(hwAttrs->baseAddress, interruptMask);
379 
380     /* Hwi is not created when using Timer_FREE_RUNNING */
381     if (object->mode != Timer_CONTINUOUS_CALLBACK) {
382         TimerCC32XX_stop(handle);
383     }
384 
385     if (object-> mode != Timer_ONESHOT_BLOCKING) {
386         object->callBack(handle, Timer_STATUS_SUCCESS);
387     }
388 }
389 
390 /*
391  *  ======== TimerCC32XX_init ========
392  */
TimerCC32XX_init(Timer_Handle handle)393 void TimerCC32XX_init(Timer_Handle handle)
394 {
395     return;
396 }
397 
398 /*
399  *  ======== TimerCC32XX_open ========
400  */
TimerCC32XX_open(Timer_Handle handle,Timer_Params * params)401 Timer_Handle TimerCC32XX_open(Timer_Handle handle, Timer_Params *params)
402 {
403     TimerCC32XX_Object        *object = handle->object;
404     TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
405     SemaphoreP_Params          semParams;
406     HwiP_Params                hwiParams;
407     ClockP_FreqHz              clockFreq;
408 
409     /* Check for valid parameters */
410     if (((params->timerMode == Timer_ONESHOT_CALLBACK ||
411           params->timerMode == Timer_CONTINUOUS_CALLBACK) &&
412           params->timerCallback == NULL) ||
413           params->period == 0) {
414 
415         return (NULL);
416     }
417 
418     if (!TimerCC32XX_allocateTimerResource(hwAttrs->baseAddress,
419         hwAttrs->subTimer)) {
420 
421         return (NULL);
422     }
423 
424     Power_registerNotify(&(object->notifyObj), PowerCC32XX_AWAKE_LPDS,
425             postNotifyFxn, (uintptr_t) handle);
426 
427     object->mode = params->timerMode;
428     object->isRunning = false;
429     object->callBack = params->timerCallback;
430     object->period = params->period;
431     object->prescaler = 0;
432 
433     if (hwAttrs->subTimer == TimerCC32XX_timer16B) {
434 
435         object->timer = TIMER_B;
436     }
437     else {
438 
439         object->timer = TIMER_A;
440     }
441 
442     if (object->mode != Timer_FREE_RUNNING) {
443 
444         HwiP_Params_init(&hwiParams);
445         hwiParams.arg = (uintptr_t) handle;
446         hwiParams.priority = hwAttrs->intPriority;
447         object->hwiHandle = HwiP_create(hwAttrs->intNum,
448             TimerCC32XX_hwiIntFunction, &hwiParams);
449 
450         if (object->hwiHandle == NULL) {
451 
452             TimerCC32XX_close(handle);
453 
454             return (NULL);
455         }
456 
457     }
458 
459     /* Creating the semaphore if mode is blocking */
460     if (params->timerMode == Timer_ONESHOT_BLOCKING) {
461 
462         SemaphoreP_Params_init(&semParams);
463         semParams.mode = SemaphoreP_Mode_BINARY;
464         object->timerSem = SemaphoreP_create(0, &semParams);
465 
466         if (object->timerSem == NULL) {
467 
468             TimerCC32XX_close(handle);
469 
470             return (NULL);
471         }
472     }
473 
474     /* Formality; CC32XX System Clock fixed to 80.0 MHz */
475     ClockP_getCpuFreq(&clockFreq);
476 
477     if (params->periodUnits == Timer_PERIOD_US) {
478 
479         /* Checks if the calculated period will fit in 32-bits */
480         if (object->period >= ((uint32_t) ~0) / (clockFreq.lo / 1000000)) {
481 
482             TimerCC32XX_close(handle);
483 
484             return (NULL);
485         }
486 
487         object->period = object->period * (clockFreq.lo / 1000000);
488     }
489     else if (params->periodUnits == Timer_PERIOD_HZ) {
490 
491         /* If (object->period) > clockFreq */
492         if ((object->period = clockFreq.lo / object->period) == 0) {
493 
494             TimerCC32XX_close(handle);
495 
496             return (NULL);
497         }
498     }
499 
500     /* If using a half timer */
501     if (hwAttrs->subTimer != TimerCC32XX_timer32) {
502 
503         if (object->period > 0xFFFF) {
504 
505             /* 24-bit resolution for the half timer */
506             if (object->period >= (1 << 24)) {
507 
508                 TimerCC32XX_close(handle);
509 
510                 return (NULL);
511             }
512 
513             getPrescaler(handle);
514         }
515     }
516 
517     initHw(handle);
518 
519     return (handle);
520 }
521 
522 /*
523  *  ======== TimerCC32XX_setPeriod =======
524   */
TimerCC32XX_setPeriod(Timer_Handle handle,Timer_PeriodUnits periodUnits,uint32_t period)525 int32_t TimerCC32XX_setPeriod(Timer_Handle handle, Timer_PeriodUnits periodUnits, uint32_t period)
526 {
527     TimerCC32XX_HWAttrs const *hwAttrs= handle->hwAttrs;
528     TimerCC32XX_Object        *object = handle->object;
529     ClockP_FreqHz              clockFreq;
530 
531     /* Formality; CC32XX System Clock fixed to 80.0 MHz */
532     ClockP_getCpuFreq(&clockFreq);
533 
534     if (periodUnits == Timer_PERIOD_US) {
535 
536         /* Checks if the calculated period will fit in 32-bits */
537         if (period >= ((uint32_t) ~0) / (clockFreq.lo / 1000000)) {
538 
539             return (Timer_STATUS_ERROR);
540         }
541 
542         period = period * (clockFreq.lo / 1000000);
543     }
544     else if (periodUnits == Timer_PERIOD_HZ) {
545 
546         /* If period > clockFreq */
547         if ((period = clockFreq.lo / period) == 0) {
548 
549             return (Timer_STATUS_ERROR);
550         }
551     }
552 
553     /* If using a half timer */
554     if (hwAttrs->subTimer != TimerCC32XX_timer32) {
555 
556         if (period > 0xFFFF) {
557 
558             /* 24-bit resolution for the half timer */
559             if (period >= (1 << 24)) {
560 
561                 return (Timer_STATUS_ERROR);
562             }
563         }
564     }
565 
566     object->period = period;
567 
568     if (hwAttrs->subTimer != TimerCC32XX_timer32) {
569         getPrescaler(handle);
570     }
571 
572     /* Writing the PSR Register has no effect for full width 32-bit mode */
573     TimerPrescaleSet(hwAttrs->baseAddress, object->timer, object->prescaler);
574     TimerLoadSet(hwAttrs->baseAddress, object->timer, object->period);
575 
576     return (Timer_STATUS_SUCCESS);
577 
578 
579 }
580 
581 /*
582  *  ======== TimerCC32XX_start ========
583  */
TimerCC32XX_start(Timer_Handle handle)584 int32_t TimerCC32XX_start(Timer_Handle handle)
585 {
586     TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
587     TimerCC32XX_Object        *object = handle->object;
588     uint32_t                   interruptMask;
589     uintptr_t                  key;
590 
591     interruptMask = object->timer & (TIMER_TIMB_TIMEOUT | TIMER_TIMA_TIMEOUT);
592 
593     key = HwiP_disable();
594 
595     if (object->isRunning) {
596 
597         HwiP_restore(key);
598 
599         return (Timer_STATUS_ERROR);
600     }
601 
602     object->isRunning = true;
603 
604     if (object->hwiHandle) {
605 
606         TimerIntEnable(hwAttrs->baseAddress, interruptMask);
607     }
608 
609     Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);
610 
611     /* Reload the timer */
612     if (object->timer == TIMER_A) {
613         HWREG(hwAttrs->baseAddress + TIMER_O_TAMR) |= TIMER_TAMR_TAILD;
614     }
615     else {
616         HWREG(hwAttrs->baseAddress + TIMER_O_TBMR) |= TIMER_TBMR_TBILD;
617     }
618 
619     TimerEnable(hwAttrs->baseAddress, object->timer);
620 
621     HwiP_restore(key);
622 
623     if (object->mode == Timer_ONESHOT_BLOCKING) {
624 
625         /* Pend forever, ~0 */
626         SemaphoreP_pend(object->timerSem, ~0);
627     }
628 
629     return (Timer_STATUS_SUCCESS);
630 }
631 
632 /*
633  *  ======== TimerCC32XX_stop ========
634  */
TimerCC32XX_stop(Timer_Handle handle)635 void TimerCC32XX_stop(Timer_Handle handle)
636 {
637     TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
638     TimerCC32XX_Object        *object = handle->object;
639     uint32_t                   interruptMask;
640     uintptr_t                  key;
641     bool                       flag = false;
642 
643     interruptMask = object->timer & (TIMER_TIMB_TIMEOUT | TIMER_TIMA_TIMEOUT);
644 
645     key = HwiP_disable();
646 
647     if (object->isRunning) {
648 
649         object->isRunning = false;
650 
651         /* Post the Semaphore when called from the Hwi */
652         if (object->mode == Timer_ONESHOT_BLOCKING) {
653             flag = true;
654         }
655 
656         TimerDisable(hwAttrs->baseAddress, object->timer);
657         TimerIntDisable(hwAttrs->baseAddress, interruptMask);
658         Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
659     }
660 
661     HwiP_restore(key);
662 
663     if (flag) {
664         SemaphoreP_post(object->timerSem);
665     }
666 }
667