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