1 /*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <kernel.h>
8 #include <arch/cpu.h>
9 #include <device.h>
10 #include <drivers/timer/system_timer.h>
11 #include <altera_common.h>
12
13 #include "altera_avalon_timer_regs.h"
14 #include "altera_avalon_timer.h"
15
16 /* The old driver "now" API would return a full uptime value. The new
17 * one only requires the driver to track ticks since the last announce
18 * call. Implement the new call in terms of the old one on legacy
19 * drivers by keeping (yet another) uptime value locally.
20 */
21 static uint32_t driver_uptime;
22
23 static uint32_t accumulated_cycle_count;
24
25 static int32_t _sys_idle_elapsed_ticks = 1;
26
wrapped_announce(int32_t ticks)27 static void wrapped_announce(int32_t ticks)
28 {
29 driver_uptime += ticks;
30 sys_clock_announce(ticks);
31 }
32
timer_irq_handler(const void * unused)33 static void timer_irq_handler(const void *unused)
34 {
35 ARG_UNUSED(unused);
36
37 accumulated_cycle_count += k_ticks_to_cyc_floor32(1);
38
39 /* Clear the interrupt */
40 alt_handle_irq((void *)TIMER_0_BASE, TIMER_0_IRQ);
41
42 wrapped_announce(_sys_idle_elapsed_ticks);
43 }
44
sys_clock_driver_init(const struct device * dev)45 int sys_clock_driver_init(const struct device *dev)
46 {
47 ARG_UNUSED(dev);
48
49 IOWR_ALTERA_AVALON_TIMER_PERIODL(TIMER_0_BASE,
50 k_ticks_to_cyc_floor32(1) & 0xFFFF);
51 IOWR_ALTERA_AVALON_TIMER_PERIODH(TIMER_0_BASE,
52 (k_ticks_to_cyc_floor32(1) >> 16) & 0xFFFF);
53
54 IRQ_CONNECT(TIMER_0_IRQ, 0, timer_irq_handler, NULL, 0);
55 irq_enable(TIMER_0_IRQ);
56
57 alt_avalon_timer_sc_init((void *)TIMER_0_BASE, 0,
58 TIMER_0_IRQ, k_ticks_to_cyc_floor32(1));
59
60 return 0;
61 }
62
sys_clock_cycle_get_32(void)63 uint32_t sys_clock_cycle_get_32(void)
64 {
65 /* Per the Altera Embedded IP Peripherals guide, you cannot
66 * use a timer instance for both the system clock and timestamps
67 * at the same time.
68 *
69 * Having this function return accumulated_cycle_count + get_snapshot()
70 * does not work reliably. It's possible for the current countdown
71 * to reset to the next interval before the timer interrupt is
72 * delivered (and accumulated cycle count gets updated). The result
73 * is an unlucky call to this function will appear to jump backward
74 * in time.
75 *
76 * To properly obtain timestamps, the CPU must be configured with
77 * a second timer peripheral instance that is configured to
78 * count down from some large initial 64-bit value. This
79 * is currently unimplemented.
80 */
81 return accumulated_cycle_count;
82 }
83
sys_clock_elapsed(void)84 uint32_t sys_clock_elapsed(void)
85 {
86 return 0;
87 }
88