1 /*
2  * Copyright (c) 2021 IoT.bzh
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_INCLUDE_ARCH_ARM_CORTEX_A_R_TIMER_H_
8 #define ZEPHYR_INCLUDE_ARCH_ARM_CORTEX_A_R_TIMER_H_
9 
10 #ifdef CONFIG_ARM_ARCH_TIMER
11 
12 #ifndef _ASMLANGUAGE
13 
14 #include <zephyr/drivers/timer/arm_arch_timer.h>
15 #include <zephyr/sys/device_mmio.h>
16 #include <zephyr/types.h>
17 
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21 
22 #define ARM_ARCH_TIMER_BASE     DT_REG_ADDR_BY_IDX(ARM_TIMER_NODE, 0)
23 #define ARM_ARCH_TIMER_IRQ      ARM_TIMER_VIRTUAL_IRQ
24 #define ARM_ARCH_TIMER_PRIO     ARM_TIMER_VIRTUAL_PRIO
25 #define ARM_ARCH_TIMER_FLAGS    ARM_TIMER_VIRTUAL_FLAGS
26 
27 #define TIMER_CNT_LOWER         0x00
28 #define TIMER_CNT_UPPER         0x04
29 #define TIMER_CTRL              0x08
30 #define TIMER_ISR               0x0c
31 #define TIMER_CMP_LOWER         0x10
32 #define TIMER_CMP_UPPER         0x14
33 
34 #define TIMER_IRQ_ENABLE        BIT(2)
35 #define TIMER_COMP_ENABLE       BIT(1)
36 #define TIMER_ENABLE            BIT(0)
37 
38 #define TIMER_ISR_EVENT_FLAG	BIT(0)
39 
40 DEVICE_MMIO_TOPLEVEL_STATIC(timer_regs, ARM_TIMER_NODE);
41 
42 #define TIMER_REG_GET(offs) (DEVICE_MMIO_TOPLEVEL_GET(timer_regs) + offs)
43 
arm_arch_timer_init(void)44 static ALWAYS_INLINE void arm_arch_timer_init(void)
45 {
46 	DEVICE_MMIO_TOPLEVEL_MAP(timer_regs, K_MEM_CACHE_NONE);
47 }
48 
arm_arch_timer_set_compare(uint64_t val)49 static ALWAYS_INLINE void arm_arch_timer_set_compare(uint64_t val)
50 {
51 	uint32_t lower = (uint32_t)val;
52 	uint32_t upper = (uint32_t)(val >> 32);
53 	uint32_t ctrl;
54 
55 	/* Disable IRQ and comparator */
56 	ctrl = sys_read32(TIMER_REG_GET(TIMER_CTRL));
57 	ctrl &= ~(TIMER_COMP_ENABLE | TIMER_IRQ_ENABLE);
58 	sys_write32(ctrl, TIMER_REG_GET(TIMER_CTRL));
59 
60 	sys_write32(lower, TIMER_REG_GET(TIMER_CMP_LOWER));
61 	sys_write32(upper, TIMER_REG_GET(TIMER_CMP_UPPER));
62 
63 	/* enable comparator back, let set_irq_mask enabling the IRQ again */
64 	ctrl |= TIMER_COMP_ENABLE;
65 	sys_write32(ctrl, TIMER_REG_GET(TIMER_CTRL));
66 }
67 
68 #if defined(CONFIG_ARM_ARCH_TIMER_ERRATUM_740657)
69 
70 /*
71  * R/W access to the event flag register is required for the timer errata
72  * 740657 workaround -> comp. ISR implementation in arm_arch_timer.c.
73  * This functionality is not present in the aarch64 implementation of the
74  * ARM global timer access functions.
75  *
76  * comp. ARM Cortex-A9 processors Software Developers Errata Notice,
77  * ARM document ID032315.
78  */
79 
arm_arch_timer_get_int_status(void)80 static ALWAYS_INLINE uint8_t arm_arch_timer_get_int_status(void)
81 {
82 	return (uint8_t)(sys_read32(TIMER_REG_GET(TIMER_ISR)) & TIMER_ISR_EVENT_FLAG);
83 }
84 
arm_arch_timer_clear_int_status(void)85 static ALWAYS_INLINE void arm_arch_timer_clear_int_status(void)
86 {
87 	sys_write32(TIMER_ISR_EVENT_FLAG, TIMER_REG_GET(TIMER_ISR));
88 }
89 
90 #endif /* CONFIG_ARM_ARCH_TIMER_ERRATUM_740657 */
91 
arm_arch_timer_enable(bool enable)92 static ALWAYS_INLINE void arm_arch_timer_enable(bool enable)
93 {
94 	uint32_t ctrl;
95 
96 	ctrl = sys_read32(TIMER_REG_GET(TIMER_CTRL));
97 	if (enable) {
98 		ctrl |= TIMER_ENABLE;
99 	} else {
100 		ctrl &= ~TIMER_ENABLE;
101 	}
102 
103 	sys_write32(ctrl, TIMER_REG_GET(TIMER_CTRL));
104 }
105 
arm_arch_timer_set_irq_mask(bool mask)106 static ALWAYS_INLINE void arm_arch_timer_set_irq_mask(bool mask)
107 {
108 	uint32_t ctrl;
109 
110 	ctrl = sys_read32(TIMER_REG_GET(TIMER_CTRL));
111 	if (mask) {
112 		ctrl &= ~TIMER_IRQ_ENABLE;
113 	} else {
114 		ctrl |= TIMER_IRQ_ENABLE;
115 		sys_write32(1, TIMER_REG_GET(TIMER_ISR));
116 	}
117 	sys_write32(ctrl, TIMER_REG_GET(TIMER_CTRL));
118 }
119 
arm_arch_timer_count(void)120 static ALWAYS_INLINE uint64_t arm_arch_timer_count(void)
121 {
122 	uint32_t lower;
123 	uint32_t upper, upper_saved;
124 
125 	/* To get the value from the Global Timer Counter register proceed
126 	 * as follows:
127 	 * 1. Read the upper 32-bit timer counter register.
128 	 * 2. Read the lower 32-bit timer counter register.
129 	 * 3. Read the upper 32-bit timer counter register again. If the value
130 	 * is different to the 32-bit upper value read previously,
131 	 * go back to step 2.
132 	 * Otherwise the 64-bit timer counter value is correct.
133 	 */
134 	upper = sys_read32(TIMER_REG_GET(TIMER_CNT_UPPER));
135 	do {
136 		upper_saved = upper;
137 		lower = sys_read32(TIMER_REG_GET(TIMER_CNT_LOWER));
138 		upper = sys_read32(TIMER_REG_GET(TIMER_CNT_UPPER));
139 	} while (upper != upper_saved);
140 
141 	return ((uint64_t)upper) << 32 | lower;
142 }
143 
144 #ifdef __cplusplus
145 }
146 #endif
147 
148 #endif  /* _ASMLANGUAGE */
149 
150 #endif /* CONFIG_ARM_ARCH_TIMER */
151 
152 #endif  /* ZEPHYR_INCLUDE_ARCH_ARM_CORTEX_A_R_TIMER_H_ */
153