blob: 677a26effac36c223435c394f814df8a320fcd9f [file] [log] [blame]
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -07001/* i8253.c - Intel 8253 PIT (Programmable Interval Timer) driver */
2
3/*
4 * Copyright (c) 2010-2015 Wind River Systems, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1) Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2) Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * 3) Neither the name of Wind River Systems nor the names of its contributors
17 * may be used to endorse or promote products derived from this software without
18 * 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, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34DESCRIPTION
Allan Stephens51898442015-06-05 10:19:43 -040035This module implements a kernel device driver for the Intel 8253 PIT
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070036(Programmable Interval Timer) device, and provides the standard "system
37clock driver" interfaces.
38
39Channel 0 is programmed to operate in "Interrupt on Terminal Count" mode,
40with the interrupt rate determined by the 'sys_clock_us_per_tick'
41global variable.
42Changing the interrupt rate at runtime is not supported.
43
44Generally, this module is not utilized in Wind River Hypervisor systems;
45instead the Hypervisor tick timer service is utilized to deliver system clock
46ticks into the guest operating system. However, this driver has been modified
47to access the PIT in scenarios where the PIT registers are mapped into a guest.
48An interrupt controller driver will not be utilized, so this driver will
49directly invoke the VIOAPIC APIs to configure/unmask the IRQ.
Anas Nashifea0d0b22015-07-01 17:22:39 -040050 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070051
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070052#include <nanokernel.h>
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070053#include <toolchain.h>
54#include <sections.h>
55#include <limits.h>
Allan Stephens478128c2015-06-19 14:22:44 -040056#include <sys_clock.h>
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070057#include <drivers/system_timer.h>
58
59#ifdef CONFIG_MICROKERNEL
60
61#include <microkernel.h>
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070062
63#endif /* CONFIG_MICROKERNEL */
64
65/*
66 * A board support package's board.h header must provide definitions for the
67 * following constants:
68 *
69 * PIC_REG_ADDR_INTERVAL
70 * PIT_BASE_ADRS
71 * PIT_CLOCK_FREQ
72 * PIT_INT_LVL
73 * PIT_INT_VEC
74 *
75 * ...and the following register access macros:
76 *
77 * PLB_BYTE_REG_WRITE
78 * PLB_BYTE_REG_READ
79 */
80
81#include <board.h>
82
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070083#if defined(CONFIG_TICKLESS_IDLE)
84#define TIMER_SUPPORTS_TICKLESS
85#endif
86
87#if defined(TIMER_SUPPORTS_TICKLESS)
88
89#define TIMER_MODE_PERIODIC 0
90#define TIMER_MODE_PERIODIC_ENT 1
91
92#else /* !TIMER_SUPPORTS_TICKLESS */
93
94#define _i8253TicklessIdleInit() \
95 do {/* nothing */ \
96 } while ((0))
97#define _i8253TicklessIdleSkew() \
98 do {/* nothing */ \
99 } while (0)
100
101#endif /* !TIMER_SUPPORTS_TICKLESS */
102
103/* register definitions */
104
105#define PIT_ADRS(base, reg) (base + (reg * PIT_REG_ADDR_INTERVAL))
106
107#define PIT_CNT0(base) PIT_ADRS(base, 0x00) /* counter/channel 0 */
108#define PIT_CNT1(base) PIT_ADRS(base, 0x01) /* counter/channel 1 */
109#define PIT_CNT2(base) PIT_ADRS(base, 0x02) /* counter/channel 2 */
110#define PIT_CMD(base) PIT_ADRS(base, 0x03) /* control word */
111
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700112#if defined(TIMER_SUPPORTS_TICKLESS)
Yonattan Louiseab9c05a2015-04-27 10:28:36 -0500113extern int32_t _sys_idle_elapsed_ticks;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700114#endif
115
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700116/* interrupt stub memory for irq_connect() */
117
Dmitriy Korovkin6dd108a2015-06-01 14:14:31 -0400118IRQ_CONNECT_STATIC(i8253, PIT_INT_LVL, PIT_INT_PRI, _timer_int_handler, 0);
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700119
120static uint16_t __noinit counterLoadVal; /* computed counter */
Yonattan Louise7770ec22015-05-11 14:57:46 -0500121static volatile uint32_t clock_accumulated_count = 0;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700122static uint16_t _currentLoadVal = 0;
123
124#if defined(TIMER_SUPPORTS_TICKLESS)
125
Yonattan Louisee111f302015-05-08 17:12:54 -0500126static uint16_t idle_original_count = 0;
Yonattan Louise5d861d52015-05-08 17:12:54 -0500127static uint16_t idle_original_ticks = 0;
Yonattan Louised6b04be2015-05-08 17:12:54 -0500128static uint16_t __noinit max_system_ticks;
Yonattan Louise23f1b6f2015-05-08 17:12:55 -0500129static uint16_t __noinit max_load_value;
Yonattan Louise04d87ce2015-05-08 17:12:55 -0500130static uint16_t __noinit timer_idle_skew;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700131
132/* Used to determine if the timer ISR should place the timer in periodic mode */
Yonattan Louisec43a34b2015-05-08 17:12:58 -0500133static unsigned char timer_mode = TIMER_MODE_PERIODIC;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700134#endif /* TIMER_SUPPORTS_TICKLESS */
135
Yonattan Louise751022e2015-05-08 17:12:59 -0500136static uint32_t old_count = 0; /* previous system clock value */
Yonattan Louise0827ff72015-05-08 17:13:00 -0500137static uint32_t old_accumulated_count = 0; /* previous accumulated value value */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700138
139/* externs */
140
141#ifdef CONFIG_MICROKERNEL
Yonattan Louise93a1d7c2015-04-27 10:28:29 -0500142extern struct nano_stack _k_command_stack;
Peter Mitsis6232a852015-05-25 15:34:18 -0400143#endif /* CONFIG_MICROKERNEL */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700144
Anas Nashifea0d0b22015-07-01 17:22:39 -0400145/**
146 *
147 * _i8253CounterRead - read the i8253 counter register's value
148 *
149 * This routine reads the 16 bit value from the i8253 counter register.
150 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400151 * @return counter register's 16 bit value
Anas Nashifea0d0b22015-07-01 17:22:39 -0400152 *
153 * \NOMANUAL
154 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700155
156static inline uint16_t _i8253CounterRead(void)
157{
158 unsigned char lsb; /* least significant byte of counter register */
159 unsigned char msb; /* most significant byte of counter register */
160 uint16_t count; /* value read from i8253 counter register */
161
162 PLB_BYTE_REG_WRITE(0x00, PIT_CMD(PIT_BASE_ADRS));
163
164 /* read counter 0 latched LSB value followed by MSB */
165 lsb = PLB_BYTE_REG_READ(PIT_CNT0(PIT_BASE_ADRS));
166 msb = PLB_BYTE_REG_READ(PIT_CNT0(PIT_BASE_ADRS));
167
168 count = lsb | (((uint16_t)msb) << 8);
169 return count;
170}
171
Anas Nashifea0d0b22015-07-01 17:22:39 -0400172/**
173 *
174 * _i8253CounterSet - set the i8253 counter register's value
175 *
176 * This routine sets the 16 bit value from which the i8253 timer will
177 * decrement and sets that counter register to its value.
178 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400179 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400180 *
181 * \NOMANUAL
182 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700183
184static inline void _i8253CounterSet(
185 uint16_t count /* value from which the counter will decrement */
186 )
187{
188 PLB_BYTE_REG_WRITE((unsigned char)(count & 0xff),
189 PIT_CNT0(PIT_BASE_ADRS));
190 PLB_BYTE_REG_WRITE((unsigned char)((count >> 8) & 0xff),
191 PIT_CNT0(PIT_BASE_ADRS));
192 _currentLoadVal = count;
193}
194
Anas Nashifea0d0b22015-07-01 17:22:39 -0400195/**
196 *
197 * _i8253CounterPeriodic - set the i8253 timer to fire periodically
198 *
199 * This routine sets the i8253 to fire on a periodic basis.
200 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400201 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400202 *
203 * \NOMANUAL
204 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700205
206static inline void _i8253CounterPeriodic(
207 uint16_t count /* value from which the counter will decrement */
208 )
209{
210 PLB_BYTE_REG_WRITE(0x36, PIT_CMD(PIT_BASE_ADRS));
211 _i8253CounterSet(count);
212}
213
214#if defined(TIMER_SUPPORTS_TICKLESS)
Anas Nashifea0d0b22015-07-01 17:22:39 -0400215/**
216 *
217 * _i8253CounterOneShot - set the i8253 timer to fire once only
218 *
219 * This routine sets the i8253 to fire once only.
220 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400221 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400222 *
223 * \NOMANUAL
224 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700225
226static inline void _i8253CounterOneShot(
227 uint16_t count /* value from which the counter will decrement */
228 )
229{
230 PLB_BYTE_REG_WRITE(0x30, PIT_CMD(PIT_BASE_ADRS));
231 _i8253CounterSet(count);
232}
233#endif /* !TIMER_SUPPORTS_TICKLESS */
234
Anas Nashifea0d0b22015-07-01 17:22:39 -0400235/**
236 *
237 * _timer_int_handler - system clock periodic tick handler
238 *
239 * This routine handles the system clock periodic tick interrupt. A TICK_EVENT
240 * event is pushed onto the microkernel stack.
241 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400242 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400243 *
244 * \NOMANUAL
245 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700246
247void _timer_int_handler(void *unusedArg /* not used */
248 )
249{
250 ARG_UNUSED(unusedArg);
251
252#ifdef TIMER_SUPPORTS_TICKLESS
Yonattan Louisec43a34b2015-05-08 17:12:58 -0500253 if (timer_mode == TIMER_MODE_PERIODIC_ENT) {
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700254 _i8253CounterPeriodic(counterLoadVal);
Yonattan Louisec43a34b2015-05-08 17:12:58 -0500255 timer_mode = TIMER_MODE_PERIODIC;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700256 }
257
258 /*
259 * Increment the tick because _timer_idle_exit does not account
260 * for the tick due to the timer interrupt itself. Also, if not in
261 * tickless mode,
262 * _SysIdleElpasedTicks will be 0.
263 */
Yonattan Louiseab9c05a2015-04-27 10:28:36 -0500264 _sys_idle_elapsed_ticks++;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700265
266 /*
267 * If we transistion from 0 elapsed ticks to 1 we need to announce the
268 * tick event to the microkernel. Other cases will have already been
269 * covered by _timer_idle_exit
270 */
271
Yonattan Louiseab9c05a2015-04-27 10:28:36 -0500272 if (_sys_idle_elapsed_ticks == 1) {
Benjamin Walshda262082015-05-01 17:03:38 -0400273 _sys_clock_tick_announce();
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700274 }
275
276 /* accumulate total counter value */
Yonattan Louise7770ec22015-05-11 14:57:46 -0500277 clock_accumulated_count += counterLoadVal * _sys_idle_elapsed_ticks;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700278
279#else
280#if defined(CONFIG_MICROKERNEL)
Benjamin Walshda262082015-05-01 17:03:38 -0400281 _sys_clock_tick_announce();
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700282#endif
283
284 /* accumulate total counter value */
Yonattan Louise7770ec22015-05-11 14:57:46 -0500285 clock_accumulated_count += counterLoadVal;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700286#endif /* TIMER_SUPPORTS_TICKLESS */
287
288 /*
289 * Algorithm tries to compensate lost interrupts if any happened and
290 * prevent the timer from counting backwards
Yonattan Louise751022e2015-05-08 17:12:59 -0500291 * ULONG_MAX / 2 is the maximal value that old_count can be more than
Yonattan Louise7770ec22015-05-11 14:57:46 -0500292 * clock_accumulated_count. If it is more -- consider it as an clock_accumulated_count
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700293 * wrap and do not try to compensate.
294 */
Yonattan Louise751022e2015-05-08 17:12:59 -0500295 if (clock_accumulated_count < old_count) {
296 uint32_t tmp = old_count - clock_accumulated_count;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700297 if ((tmp >= counterLoadVal) && (tmp < (ULONG_MAX / 2))) {
Yonattan Louise7770ec22015-05-11 14:57:46 -0500298 clock_accumulated_count += tmp - tmp % counterLoadVal;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700299 }
300 }
301
302#if defined(CONFIG_NANOKERNEL)
Benjamin Walshda262082015-05-01 17:03:38 -0400303 _sys_clock_tick_announce();
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700304#endif /* CONFIG_NANOKERNEL */
305}
306
307#if defined(TIMER_SUPPORTS_TICKLESS)
Anas Nashifea0d0b22015-07-01 17:22:39 -0400308/**
309 *
310 * _i8253TicklessIdleInit - initialize the tickless idle feature
311 *
312 * This routine initializes the tickless idle feature. Note that maximum
313 * number of ticks that can elapse during a "tickless idle" is limited by
314 * <counterLoadVal>. The larger the value (the lower the tick frequency), the
315 * fewer elapsed ticks during a "tickless idle". Conversely, the smaller the
316 * value (the higher the tick frequency), the more elapsed ticks during a
317 * "tickless idle".
318 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400319 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400320 *
321 * \NOMANUAL
322 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700323
324static void _i8253TicklessIdleInit(void)
325{
Yonattan Louised6b04be2015-05-08 17:12:54 -0500326 max_system_ticks = 0xffff / counterLoadVal;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700327 /* this gives a count that gives the max number of full ticks */
Yonattan Louise23f1b6f2015-05-08 17:12:55 -0500328 max_load_value = max_system_ticks * counterLoadVal;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700329}
330
Anas Nashifea0d0b22015-07-01 17:22:39 -0400331/**
332 *
333 * _i8253TicklessIdleSkew -
334 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400335 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400336 *
337 * \NOMANUAL
338 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700339
340static void _i8253TicklessIdleSkew(void)
341{
342 /* TBD */
Yonattan Louise04d87ce2015-05-08 17:12:55 -0500343 timer_idle_skew = 0;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700344}
345
Anas Nashifea0d0b22015-07-01 17:22:39 -0400346/**
347 *
348 * _timer_idle_enter - Place system timer into idle state
349 *
350 * Re-program the timer to enter into the idle state for the given number of
351 * ticks. It is placed into one shot mode where it will fire in the number of
352 * ticks supplied or the maximum number of ticks that can be programmed into
353 * hardware. A value of -1 means inifinite number of ticks.
354 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700355
356void _timer_idle_enter(int32_t ticks /* system ticks */
357 )
358{
359 uint16_t newCount;
360
361 /*
362 * We're being asked to have the timer fire in "ticks" from now. To
363 * maintain accuracy we must account for the remain time left in the
364 * timer. So we read the count out of it and add it to the requested
365 * time out
366 */
367 newCount = _i8253CounterRead();
368
Yonattan Louised6b04be2015-05-08 17:12:54 -0500369 if (ticks == -1 || ticks > max_system_ticks) {
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700370 /*
371 * We've been asked to fire the timer so far in the future that
372 * the
373 * required count value would not fit in the 16 bit counter
374 * register.
375 * Instead, we program for the maximum programmable interval
376 * minus one
377 * system tick to prevent overflow when the left over count read
378 * earlier
379 * is added.
380 */
Yonattan Louise23f1b6f2015-05-08 17:12:55 -0500381 newCount += max_load_value - counterLoadVal;
Yonattan Louise5d861d52015-05-08 17:12:54 -0500382 idle_original_ticks = max_system_ticks - 1;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700383 } else {
384 /* leave one tick of buffer to have to time react when coming
385 * back ? */
Yonattan Louise5d861d52015-05-08 17:12:54 -0500386 idle_original_ticks = ticks - 1;
387 newCount += idle_original_ticks * counterLoadVal;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700388 }
389
Yonattan Louise04d87ce2015-05-08 17:12:55 -0500390 idle_original_count = newCount - timer_idle_skew;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700391
392 /* Stop/start the timer instead of disabling/enabling the interrupt? */
393 irq_disable(PIT_INT_LVL);
394
Yonattan Louisec43a34b2015-05-08 17:12:58 -0500395 timer_mode = TIMER_MODE_PERIODIC_ENT;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700396
397 /* Program for terminal mode. The PIT equivalent of one shot */
398 _i8253CounterOneShot(newCount);
399
400 irq_enable(PIT_INT_LVL);
401}
402
Anas Nashifea0d0b22015-07-01 17:22:39 -0400403/**
404 *
405 * _timer_idle_exit - handling of tickless idle when interrupted
406 *
407 * The routine is responsible for taking the timer out of idle mode and
408 * generating an interrupt at the next tick interval.
409 *
410 * Note that in this routine, _SysTimerElapsedTicks must be zero because the
411 * ticker has done its work and consumed all the ticks. This has to be true
412 * otherwise idle mode wouldn't have been entered in the first place.
413 *
414 * Called in _IntEnt()
415 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400416 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400417 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700418
419void _timer_idle_exit(void)
420{
421 uint16_t count; /* current value in i8253 counter register */
422
423 /* timer is in idle or off mode, adjust the ticks expired */
424
425 /* request counter 0 to be latched */
426 count = _i8253CounterRead();
427
Yonattan Louisee111f302015-05-08 17:12:54 -0500428 if ((count == 0) || (count >= idle_original_count)) {
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700429 /* Timer expired. Place back in periodic mode */
430 _i8253CounterPeriodic(counterLoadVal);
Yonattan Louisec43a34b2015-05-08 17:12:58 -0500431 timer_mode = TIMER_MODE_PERIODIC;
Yonattan Louise5d861d52015-05-08 17:12:54 -0500432 _sys_idle_elapsed_ticks = idle_original_ticks - 1;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700433 /*
434 * Announce elapsed ticks to the microkernel. Note we are
435 * guaranteed
436 * that the timer ISR will execute first before the tick event
437 * is
438 * serviced.
439 */
Benjamin Walshda262082015-05-01 17:03:38 -0400440 _sys_clock_tick_announce();
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700441 } else {
442 uint16_t elapsed; /* elapsed "counter time" */
443 uint16_t remaining; /* remaing "counter time" */
444
Yonattan Louisee111f302015-05-08 17:12:54 -0500445 elapsed = idle_original_count - count;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700446
447 remaining = elapsed % counterLoadVal;
448
449 /* switch timer to periodic mode */
450 if (remaining == 0) {
451 _i8253CounterPeriodic(counterLoadVal);
Yonattan Louisec43a34b2015-05-08 17:12:58 -0500452 timer_mode = TIMER_MODE_PERIODIC;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700453 } else if (count > remaining) {
454 /* less time remaining to the next tick than was
455 * programmed */
456 _i8253CounterOneShot(remaining);
457 }
458
Yonattan Louiseab9c05a2015-04-27 10:28:36 -0500459 _sys_idle_elapsed_ticks = elapsed / counterLoadVal;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700460
Yonattan Louiseab9c05a2015-04-27 10:28:36 -0500461 if (_sys_idle_elapsed_ticks) {
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700462 /* Announce elapsed ticks to the microkernel */
Benjamin Walshda262082015-05-01 17:03:38 -0400463 _sys_clock_tick_announce();
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700464 }
465 }
466}
467#endif /* !TIMER_SUPPORTS_TICKLESS */
468
Anas Nashifea0d0b22015-07-01 17:22:39 -0400469/**
470 *
471 * timer_driver - initialize and enable the system clock
472 *
473 * This routine is used to program the PIT to deliver interrupts at the
474 * rate specified via the 'sys_clock_us_per_tick' global variable.
475 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400476 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400477 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700478
479void timer_driver(int priority /* priority parameter ignored by this driver */
480 )
481{
482 ARG_UNUSED(priority);
483
484 /* determine the PIT counter value (in timer clock cycles/system tick)
485 */
486
487 counterLoadVal = sys_clock_hw_cycles_per_tick;
488
489 _i8253TicklessIdleInit();
490
491 /* init channel 0 to generate interrupt at the rate of SYS_CLOCK_RATE */
492
493 _i8253CounterPeriodic(counterLoadVal);
494
Dmitriy Korovkin6dd108a2015-06-01 14:14:31 -0400495 IRQ_CONFIG(i8253, PIT_INT_LVL);
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700496
497 _i8253TicklessIdleSkew();
498
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700499 /* Everything has been configured. It is now safe to enable the
500 * interrupt */
501 irq_enable(PIT_INT_LVL);
502}
503
Anas Nashifea0d0b22015-07-01 17:22:39 -0400504/**
505 *
506 * timer_read - read the BSP timer hardware
507 *
508 * This routine returns the current time in terms of timer hardware clock cycles.
509 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400510 * @return up counter of elapsed clock cycles
Anas Nashifea0d0b22015-07-01 17:22:39 -0400511 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700512
513uint32_t timer_read(void)
514{
515 unsigned int key; /* interrupt lock level */
516 uint32_t newCount; /* new system clock value */
517
518#ifdef CONFIG_INT_LATENCY_BENCHMARK
519/*
520 * Expending irq_lock_inline() code since directly calling it would
521 * would end up in infinite recursion.
522 */
Dmitriy Korovkind33ad052015-04-27 13:40:11 -0400523 key = _do_irq_lock_inline();
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700524#else
525 key = irq_lock_inline();
526#endif
527
528 /* counters are down counters so need to subtact from counterLoadVal */
Yonattan Louise7770ec22015-05-11 14:57:46 -0500529 newCount = clock_accumulated_count + _currentLoadVal - _i8253CounterRead();
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700530
531 /*
532 * This algorithm fixes the situation when the timer counter reset
533 * happened before the timer interrupt (due to possible interrupt
534 * disable)
535 */
Yonattan Louise0827ff72015-05-08 17:13:00 -0500536 if ((newCount < old_count) && (clock_accumulated_count == old_accumulated_count)) {
Yonattan Louise751022e2015-05-08 17:12:59 -0500537 uint32_t tmp = old_count - newCount;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700538 newCount += tmp - tmp % _currentLoadVal + _currentLoadVal;
539 }
540
Yonattan Louise751022e2015-05-08 17:12:59 -0500541 old_count = newCount;
Yonattan Louise0827ff72015-05-08 17:13:00 -0500542 old_accumulated_count = clock_accumulated_count;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700543
544#ifdef CONFIG_INT_LATENCY_BENCHMARK
545/*
546 * Expending irq_unlock_inline() code since directly calling it would
547 * would end up in infinite recursion.
548 */
Dmitriy Korovkind33ad052015-04-27 13:40:11 -0400549 if (key & 0x200)
550 _do_irq_unlock_inline();
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700551#else
552 irq_unlock_inline(key);
553#endif
554
555 return newCount;
556}
557
558#if defined(CONFIG_SYSTEM_TIMER_DISABLE)
Anas Nashifea0d0b22015-07-01 17:22:39 -0400559/**
560 *
561 * timer_disable - stop announcing ticks into the kernel
562 *
563 * This routine simply disables the PIT counter such that interrupts are no
564 * longer delivered.
565 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400566 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400567 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700568
569void timer_disable(void)
570{
571 unsigned int key; /* interrupt lock level */
572
573 key = irq_lock();
574
575 PLB_BYTE_REG_WRITE(0x38, PIT_CMD(PIT_BASE_ADRS));
576 PLB_BYTE_REG_WRITE(0, PIT_CNT0(PIT_BASE_ADRS));
577 PLB_BYTE_REG_WRITE(0, PIT_CNT0(PIT_BASE_ADRS));
578
579 irq_unlock(key);
580
581 /* disable interrupt in the interrupt controller */
582
583 irq_disable(PIT_INT_LVL);
584}
585#endif /* CONFIG_SYSTEM_TIMER_DISABLE */