1 /*
2 * Copyright (c) 2025 Cypress Semiconductor Corporation (an Infineon company) or
3 * an affiliate of Cypress Semiconductor Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @brief Low Power timer driver for Infineon CAT1 MCU family.
10 */
11
12 #define DT_DRV_COMPAT infineon_cat1_lp_timer
13
14 #include <zephyr/device.h>
15 #include <zephyr/drivers/timer/system_timer.h>
16 #include <zephyr/irq.h>
17 #include <zephyr/spinlock.h>
18 #include <zephyr/sys_clock.h>
19 #include <zephyr/drivers/gpio.h>
20
21 #include <cyhal_lptimer.h>
22
23 #include <zephyr/logging/log.h>
24 LOG_MODULE_REGISTER(ifx_cat1_lp_timer, CONFIG_KERNEL_LOG_LEVEL);
25
26 /* The application only needs one lptimer. Report an error if more than one is selected. */
27 #if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 1
28 #error Only one LPTIMER instance should be enabled
29 #endif
30
31 #define LPTIMER_INTR_PRIORITY (3u)
32 #define LPTIMER_FREQ (32768u)
33
34 /* We need to know the number of MCWDT instances. Unfortunately, this information is not available
35 * in a header in the HAL code. This was extracted from the cyhal_lptimer.c file in the HAL
36 */
37 #if (defined(CY_IP_MXS40SRSS) || defined(CY_IP_MXS40SSRSS) || defined(CY_IP_MXS28SRSS) || \
38 defined(CY_IP_MXS22SRSS)) && \
39 !((defined(CY_IP_MXS40SRSS) && (CY_IP_MXS40SRSS_VERSION >= 3)) || \
40 ((SRSS_NUM_MCWDT_B) > 0))
41 #define NUM_LPTIMERS SRSS_NUM_MCWDT
42 #else
43 #error "Selected device doesn't support low power timers at this time."
44 #endif
45
46 cyhal_lptimer_t lptimer_obj;
47 static uint32_t last_lptimer_value;
48 static struct k_spinlock lock;
49
lptimer_interrupt_handler(void * handler_arg,cyhal_lptimer_event_t event)50 static void lptimer_interrupt_handler(void *handler_arg, cyhal_lptimer_event_t event)
51 {
52 CY_UNUSED_PARAMETER(handler_arg);
53 CY_UNUSED_PARAMETER(event);
54
55 k_spinlock_key_t key = k_spin_lock(&lock);
56
57 /* announce the elapsed time in ms */
58 uint32_t lptimer_value = cyhal_lptimer_read(&lptimer_obj);
59 uint32_t delta_ticks =
60 ((uint64_t)(lptimer_value - last_lptimer_value) * CONFIG_SYS_CLOCK_TICKS_PER_SEC) /
61 LPTIMER_FREQ;
62 sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? delta_ticks : (delta_ticks > 0));
63 last_lptimer_value += (delta_ticks * LPTIMER_FREQ) / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
64
65 k_spin_unlock(&lock, key);
66 }
67
sys_clock_set_timeout(int32_t ticks,bool idle)68 void sys_clock_set_timeout(int32_t ticks, bool idle)
69 {
70 ARG_UNUSED(idle);
71
72 k_spinlock_key_t key = {0};
73
74 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
75 return;
76 }
77
78 if (ticks == K_TICKS_FOREVER) {
79 key = k_spin_lock(&lock);
80 /* Disable the LPTIMER events */
81 cyhal_lptimer_enable_event(&lptimer_obj, CYHAL_LPTIMER_COMPARE_MATCH,
82 LPTIMER_INTR_PRIORITY, false);
83 k_spin_unlock(&lock, key);
84 return;
85 }
86
87 /* passing ticks==1 means "announce the next tick", ticks value of zero (or even negative)
88 * is legal and treated identically: it simply indicates the kernel would like the next
89 * tick announcement as soon as possible.
90 */
91 if (ticks < 1) {
92 ticks = 1;
93 }
94
95 uint32_t set_ticks = ((uint32_t)(ticks)*LPTIMER_FREQ) / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
96
97 key = k_spin_lock(&lock);
98
99 /* Configure and Enable the LPTIMER events */
100 cyhal_lptimer_enable_event(&lptimer_obj, CYHAL_LPTIMER_COMPARE_MATCH, LPTIMER_INTR_PRIORITY,
101 true);
102 /* Set the delay value for the next wakeup interrupt */
103 cyhal_lptimer_set_delay(&lptimer_obj, set_ticks);
104
105 k_spin_unlock(&lock, key);
106 }
107
sys_clock_elapsed(void)108 uint32_t sys_clock_elapsed(void)
109 {
110 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
111 return 0;
112 }
113
114 k_spinlock_key_t key = k_spin_lock(&lock);
115
116 uint32_t lptimer_value = cyhal_lptimer_read(&lptimer_obj);
117
118 k_spin_unlock(&lock, key);
119
120 /* gives the value of LPTIM counter (ms) since the previous 'announce' */
121 uint64_t ret = (((uint64_t)(lptimer_value - last_lptimer_value)) *
122 CONFIG_SYS_CLOCK_TICKS_PER_SEC) /
123 LPTIMER_FREQ;
124
125 return (uint32_t)ret;
126 }
127
sys_clock_cycle_get_32(void)128 uint32_t sys_clock_cycle_get_32(void)
129 {
130 /* just gives the accumulated count in a number of hw cycles */
131
132 k_spinlock_key_t key = k_spin_lock(&lock);
133
134 uint32_t lp_time = cyhal_lptimer_read(&lptimer_obj);
135
136 k_spin_unlock(&lock, key);
137
138 /* convert lptim count in a nb of hw cycles with precision */
139 uint64_t ret = ((uint64_t)lp_time * sys_clock_hw_cycles_per_sec()) / LPTIMER_FREQ;
140
141 /* convert in hw cycles (keeping 32bit value) */
142 return (uint32_t)ret;
143 }
144
sys_clock_driver_init(void)145 static int sys_clock_driver_init(void)
146 {
147 cy_rslt_t result;
148 cyhal_lptimer_t lptimer_objs[NUM_LPTIMERS];
149
150 /* Currently with the HAL, there is no way to directly/explicitly select the MCWDT
151 * enabled in the <board>.dts file. So, instead, initialize LPTIMERs until we find
152 * the one from the <board>.dts file. Free the others when done.
153 */
154 for (int32_t lptimer_index = 0; lptimer_index < NUM_LPTIMERS; lptimer_index++) {
155 /* Initialize the LPTIMER with default configuration */
156 result = cyhal_lptimer_init(&lptimer_obj);
157
158 if (result != CY_RSLT_SUCCESS) {
159 LOG_ERR("LPTimer instance not found. Error: 0x%08X\n",
160 (unsigned int)result);
161 return -EIO;
162 }
163
164 if ((uint32_t)lptimer_obj.base == DT_INST_REG_ADDR(0)) {
165 for (lptimer_index--; lptimer_index >= 0; lptimer_index--) {
166 cyhal_lptimer_free(&lptimer_objs[lptimer_index]);
167 }
168 break;
169 }
170
171 cyhal_lptimer_free(&lptimer_obj);
172 cyhal_lptimer_init(&lptimer_objs[lptimer_index]);
173 }
174
175 /* Register the callback handler which will be invoked when the interrupt triggers */
176 cyhal_lptimer_register_callback(&lptimer_obj, lptimer_interrupt_handler, NULL);
177
178 if (result != CY_RSLT_SUCCESS) {
179 LOG_ERR("Sys Clock initialization failed. Error: 0x%08X\n", (unsigned int)result);
180 return -EIO;
181 }
182
183 return 0;
184 }
185
186 SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
187