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