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