/
ambiq_stimer.c
145 lines (107 loc) · 3.31 KB
/
ambiq_stimer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
* Copyright (c) 2023 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ambiq_stimer
/**
* @file
* @brief Ambiq Apollo STIMER-based sys_clock driver
*
*/
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/timer/system_timer.h>
#include <zephyr/sys_clock.h>
#include <zephyr/irq.h>
#include <zephyr/spinlock.h>
/* ambiq-sdk includes */
#include <am_mcu_apollo.h>
#define COUNTER_MAX UINT32_MAX
#define CYC_PER_TICK (sys_clock_hw_cycles_per_sec() \
/ CONFIG_SYS_CLOCK_TICKS_PER_SEC)
#define MAX_TICKS ((k_ticks_t)(COUNTER_MAX / CYC_PER_TICK) - 1)
#define MAX_CYCLES (MAX_TICKS * CYC_PER_TICK)
#define MIN_DELAY 1
#define TIMER_IRQ (DT_INST_IRQN(0))
#if defined(CONFIG_TEST)
const int32_t z_sys_timer_irq_for_test = TIMER_IRQ;
#endif
/* Value of STIMER counter when the previous kernel tick was announced */
static atomic_t g_last_count;
/* Spinlock to sync between Compare ISR and update of Compare register */
static struct k_spinlock g_lock;
static void stimer_isr(const void *arg)
{
ARG_UNUSED(arg);
uint32_t irq_status = am_hal_stimer_int_status_get(false);
if (irq_status & AM_HAL_STIMER_INT_COMPAREA) {
am_hal_stimer_int_clear(AM_HAL_STIMER_INT_COMPAREA);
k_spinlock_key_t key = k_spin_lock(&g_lock);
uint32_t now = am_hal_stimer_counter_get();
uint32_t dticks = (uint32_t)((now - g_last_count) / CYC_PER_TICK);
g_last_count += dticks * CYC_PER_TICK;
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
uint32_t next = g_last_count + CYC_PER_TICK;
if ((int32_t)(next - now) < MIN_DELAY) {
next += CYC_PER_TICK;
}
am_hal_stimer_compare_delta_set(0, next - g_last_count);
}
k_spin_unlock(&g_lock, key);
sys_clock_announce(dticks);
}
}
void sys_clock_set_timeout(int32_t ticks, bool idle)
{
ARG_UNUSED(idle);
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return;
}
if (ticks == K_TICKS_FOREVER) {
return;
}
ticks = MIN(MAX_TICKS, ticks);
/* If tick is 0, set delta cyc to MIN_DELAY to trigger tick isr asap */
uint32_t cyc = MAX(ticks * CYC_PER_TICK, MIN_DELAY);
k_spinlock_key_t key = k_spin_lock(&g_lock);
am_hal_stimer_compare_delta_set(0, cyc);
k_spin_unlock(&g_lock, key);
}
uint32_t sys_clock_elapsed(void)
{
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return 0;
}
k_spinlock_key_t key = k_spin_lock(&g_lock);
uint32_t ret = (am_hal_stimer_counter_get()
- g_last_count) / CYC_PER_TICK;
k_spin_unlock(&g_lock, key);
return ret;
}
uint32_t sys_clock_cycle_get_32(void)
{
return am_hal_stimer_counter_get();
}
static int stimer_init(void)
{
uint32_t oldCfg;
k_spinlock_key_t key = k_spin_lock(&g_lock);
oldCfg = am_hal_stimer_config(AM_HAL_STIMER_CFG_FREEZE);
am_hal_stimer_config((oldCfg & ~(AM_HAL_STIMER_CFG_FREEZE | STIMER_STCFG_CLKSEL_Msk))
| AM_HAL_STIMER_XTAL_32KHZ
| AM_HAL_STIMER_CFG_COMPARE_A_ENABLE);
g_last_count = am_hal_stimer_counter_get();
k_spin_unlock(&g_lock, key);
NVIC_ClearPendingIRQ(TIMER_IRQ);
IRQ_CONNECT(TIMER_IRQ, 0, stimer_isr, 0, 0);
irq_enable(TIMER_IRQ);
am_hal_stimer_int_enable(AM_HAL_STIMER_INT_COMPAREA);
/* Start timer with period CYC_PER_TICK if tickless is not enabled */
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
am_hal_stimer_compare_delta_set(0, CYC_PER_TICK);
}
return 0;
}
SYS_INIT(stimer_init, PRE_KERNEL_2,
CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);