Skip to content

Commit 47d1e38

Browse files
RuibinChangkartben
authored andcommitted
drivers/counter: implement it51xxx counter driver
Implement counter driver for ITE it51xxx series chip. Signed-off-by: Ruibin Chang <Ruibin.Chang@ite.com.tw>
1 parent ec6b34d commit 47d1e38

File tree

8 files changed

+419
-0
lines changed

8 files changed

+419
-0
lines changed

drivers/counter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_AMBIQ counter_ambiq_ti
1515
zephyr_library_sources_ifdef(CONFIG_COUNTER_GECKO_RTCC counter_gecko_rtcc.c)
1616
zephyr_library_sources_ifdef(CONFIG_COUNTER_GECKO_STIMER counter_gecko_stimer.c)
1717
zephyr_library_sources_ifdef(CONFIG_COUNTER_IMX_EPIT counter_imx_epit.c)
18+
zephyr_library_sources_ifdef(CONFIG_COUNTER_ITE_IT51XXX counter_ite_it51xxx.c)
1819
zephyr_library_sources_ifdef(CONFIG_COUNTER_ITE_IT8XXX2 counter_ite_it8xxx2.c)
1920
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_CTIMER counter_mcux_ctimer.c)
2021
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_RTC counter_mcux_rtc.c)

drivers/counter/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ source "drivers/counter/Kconfig.nrfx"
4242

4343
source "drivers/counter/Kconfig.imx_epit"
4444

45+
source "drivers/counter/Kconfig.ite_it51xxx"
46+
4547
source "drivers/counter/Kconfig.ite_it8xxx2"
4648

4749
source "drivers/counter/Kconfig.stm32_rtc"

drivers/counter/Kconfig.ite_it51xxx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 ITE Corporation. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config COUNTER_ITE_IT51XXX
5+
bool "ITE IT51XXX Counter driver"
6+
default y
7+
depends on DT_HAS_ITE_IT51XXX_COUNTER_ENABLED
8+
help
9+
Enable counter support for the ITE IT51XXX.

drivers/counter/counter_ite_it51xxx.c

Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
/*
2+
* Copyright (c) 2025 ITE Corporation.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT ite_it51xxx_counter
8+
9+
#include <soc.h>
10+
#include <zephyr/drivers/counter.h>
11+
#include <zephyr/logging/log.h>
12+
13+
LOG_MODULE_REGISTER(counter_it51xxx, CONFIG_COUNTER_LOG_LEVEL);
14+
15+
/* 0x30 (offset 0x00): External Timer 7 Control Register (used as alarm counter) */
16+
#define REG_TIMER_ET7CTRL 0x00
17+
#define TIMER_ETTC BIT(2)
18+
#define TIMER_RST_EN (TIMER_ETRST | TIMER_ETEN)
19+
#define TIMER_ETRST BIT(1)
20+
#define TIMER_ETEN BIT(0)
21+
/* 0x31 (offset 0x01): External Timer 7 Prescaler Register */
22+
#define REG_TIMER_ET7PSR 0x01
23+
#define TIMER_ETPSR_32768HZ 0x00
24+
/* 0x34 (offset 0x04): External Timer 7 Counter Register */
25+
#define REG_TIMER_ET7CNTLLR 0x04
26+
/* 0x38 (offset 0x08): External Timer 8 Control Register (used as top counter) */
27+
#define REG_TIMER_ET8CTRL 0x08
28+
/* 0x39 (offset 0x09): External Timer 8 Prescaler Register */
29+
#define REG_TIMER_ET8PSR 0x09
30+
/* 0x3C (offset 0x0C): External Timer 8 Counter Register */
31+
#define REG_TIMER_ET8CNTLLR 0x0C
32+
/* 0x58 (offset 0x28): External Timer 7 Counter Observation Register */
33+
#define REG_TIMER_ET7CNTOLR 0x28
34+
/* 0x5C (offset 0x2C): External Timer 8 Counter Observation Register */
35+
#define REG_TIMER_ET8CNTOLR 0x2C
36+
37+
struct counter_it51xxx_config {
38+
struct counter_config_info info;
39+
mm_reg_t base;
40+
/* Alarm timer irq number */
41+
int alarm_irq;
42+
/* Alarm timer irq trigger mode */
43+
int alarm_flag;
44+
/* Top timer irq number */
45+
int top_irq;
46+
/* Top timer irq trigger mode */
47+
int top_flag;
48+
void (*irq_config_func)(const struct device *dev);
49+
};
50+
51+
struct counter_it51xxx_data {
52+
counter_top_callback_t top_callback;
53+
/* User data passed to top callback function */
54+
void *top_user_data;
55+
counter_alarm_callback_t alarm_callback;
56+
/* User data passed to alarm callback function */
57+
void *alarm_user_data;
58+
};
59+
60+
static inline void counter_it51xxx_alarm_timer_disable(const struct device *dev)
61+
{
62+
const struct counter_it51xxx_config *config = dev->config;
63+
64+
irq_disable(config->alarm_irq);
65+
/* Read clear terminal count and disable alarm timer */
66+
sys_write8(sys_read8(config->base + REG_TIMER_ET7CTRL) & ~TIMER_ETEN,
67+
config->base + REG_TIMER_ET7CTRL);
68+
ite_intc_isr_clear(config->alarm_irq);
69+
}
70+
71+
static int counter_it51xxx_start(const struct device *dev)
72+
{
73+
const struct counter_it51xxx_config *config = dev->config;
74+
75+
LOG_DBG("Start top timer");
76+
77+
sys_write8(TIMER_RST_EN, config->base + REG_TIMER_ET8CTRL);
78+
79+
return 0;
80+
}
81+
82+
static int counter_it51xxx_stop(const struct device *dev)
83+
{
84+
const struct counter_it51xxx_config *config = dev->config;
85+
86+
LOG_DBG("Stop top timer");
87+
88+
sys_write8(sys_read8(config->base + REG_TIMER_ET8CTRL) & ~TIMER_ETEN,
89+
config->base + REG_TIMER_ET8CTRL);
90+
91+
return 0;
92+
}
93+
94+
static int counter_it51xxx_get_value(const struct device *dev, uint32_t *ticks)
95+
{
96+
const struct counter_it51xxx_config *config = dev->config;
97+
98+
/* Critical section */
99+
unsigned int key = irq_lock();
100+
101+
/* Workaround for reading observation register latch issue */
102+
sys_read32(config->base + REG_TIMER_ET8CNTOLR);
103+
sys_read32(config->base + REG_TIMER_ET8PSR);
104+
*ticks = sys_read32(config->base + REG_TIMER_ET8CNTOLR);
105+
106+
irq_unlock(key);
107+
108+
return 0;
109+
}
110+
111+
static int counter_it51xxx_set_alarm(const struct device *dev, uint8_t chan_id,
112+
const struct counter_alarm_cfg *alarm_cfg)
113+
{
114+
const struct counter_it51xxx_config *config = dev->config;
115+
struct counter_it51xxx_data *data = dev->data;
116+
117+
ARG_UNUSED(chan_id);
118+
119+
if (alarm_cfg->callback == NULL) {
120+
LOG_ERR("Alarm timer callback can't be NULL");
121+
return -EINVAL;
122+
}
123+
124+
if (alarm_cfg->ticks > sys_read32(config->base + REG_TIMER_ET8CNTLLR)) {
125+
LOG_ERR("Alarm timer ticks can't be bigger than top ticks");
126+
return -EINVAL;
127+
}
128+
129+
/* There is an active alarm timer, so alarm timer can't be updated. */
130+
if (data->alarm_callback != NULL) {
131+
return -EBUSY;
132+
}
133+
134+
/*
135+
* Interrupts are only triggered when ticks reaches 0,
136+
* so only relative alarms are supported.
137+
*/
138+
if (alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) {
139+
LOG_ERR("COUNTER_ALARM_CFG_ABSOLUTE flag is not supported");
140+
return -ENOTSUP;
141+
}
142+
143+
irq_disable(config->alarm_irq);
144+
145+
/* Disable alarm timer */
146+
sys_write8(sys_read8(config->base + REG_TIMER_ET7CTRL) & ~TIMER_ETEN,
147+
config->base + REG_TIMER_ET7CTRL);
148+
149+
data->alarm_callback = alarm_cfg->callback;
150+
data->alarm_user_data = alarm_cfg->user_data;
151+
152+
/* Set alarm timer ticks */
153+
sys_write32(alarm_cfg->ticks, config->base + REG_TIMER_ET7CNTLLR);
154+
LOG_DBG("Set alarm timer ticks 0x%x", alarm_cfg->ticks);
155+
156+
ite_intc_isr_clear(config->alarm_irq);
157+
158+
/* Read clear terminal count, enable, and reset alarm timer */
159+
sys_write8(sys_read8(config->base + REG_TIMER_ET7CTRL) | TIMER_RST_EN,
160+
config->base + REG_TIMER_ET7CTRL);
161+
162+
irq_enable(config->alarm_irq);
163+
164+
return 0;
165+
}
166+
167+
static int counter_it51xxx_cancel_alarm(const struct device *dev, uint8_t chan_id)
168+
{
169+
struct counter_it51xxx_data *data = dev->data;
170+
171+
if (chan_id != 0) {
172+
LOG_ERR("Invalid channel id %u", chan_id);
173+
return -ENOTSUP;
174+
}
175+
176+
counter_it51xxx_alarm_timer_disable(dev);
177+
178+
data->alarm_callback = NULL;
179+
data->alarm_user_data = NULL;
180+
181+
LOG_DBG("Alarm timer is canceled");
182+
183+
return 0;
184+
}
185+
186+
static int counter_it51xxx_set_top_value(const struct device *dev,
187+
const struct counter_top_cfg *top_cfg)
188+
{
189+
const struct counter_it51xxx_config *config = dev->config;
190+
struct counter_it51xxx_data *data = dev->data;
191+
192+
if (top_cfg == NULL) {
193+
LOG_ERR("Top timer configuration can't be NULL");
194+
return -EINVAL;
195+
}
196+
197+
if (top_cfg->ticks == 0) {
198+
LOG_ERR("Top timer ticks can't be set to zero");
199+
return -EINVAL;
200+
}
201+
202+
if (top_cfg->ticks > config->info.max_top_value) {
203+
LOG_ERR("Top timer ticks only support 32 bits");
204+
return -ENOTSUP;
205+
}
206+
207+
/* There is an active alarm timer, so top timer can't be updated. */
208+
if (data->alarm_callback) {
209+
return -EBUSY;
210+
}
211+
212+
/* Top timer ticks cannot be updated without reset */
213+
if (top_cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
214+
LOG_ERR("COUNTER_ALARM_CFG_ABSOLUTE flag is not supported");
215+
return -ENOTSUP;
216+
}
217+
218+
irq_disable(config->top_irq);
219+
220+
/* Disable top timer */
221+
sys_write8(sys_read8(config->base + REG_TIMER_ET8CTRL) & ~TIMER_ETEN,
222+
config->base + REG_TIMER_ET8CTRL);
223+
224+
data->top_callback = top_cfg->callback;
225+
data->top_user_data = top_cfg->user_data;
226+
227+
/* Set top timer ticks */
228+
sys_write32(top_cfg->ticks, config->base + REG_TIMER_ET8CNTLLR);
229+
LOG_DBG("Set top timer ticks 0x%x", top_cfg->ticks);
230+
231+
ite_intc_isr_clear(config->top_irq);
232+
233+
/* Read clear terminal count, enable, and reset top timer */
234+
sys_write8(sys_read8(config->base + REG_TIMER_ET8CTRL) | TIMER_RST_EN,
235+
config->base + REG_TIMER_ET8CTRL);
236+
237+
irq_enable(config->top_irq);
238+
239+
return 0;
240+
}
241+
242+
static uint32_t counter_it51xxx_get_top_value(const struct device *dev)
243+
{
244+
const struct counter_it51xxx_config *config = dev->config;
245+
246+
return sys_read32(config->base + REG_TIMER_ET8CNTLLR);
247+
}
248+
249+
static void counter_it51xxx_alarm_isr(const struct device *dev)
250+
{
251+
struct counter_it51xxx_data *data = dev->data;
252+
counter_alarm_callback_t alarm_cb;
253+
void *user_data;
254+
uint32_t ticks;
255+
256+
LOG_DBG("Alarm timer ISR");
257+
258+
/* Alarm is one-shot, so disable interrupt and callback */
259+
if (data->alarm_callback) {
260+
alarm_cb = data->alarm_callback;
261+
data->alarm_callback = NULL;
262+
user_data = data->alarm_user_data;
263+
264+
counter_it51xxx_get_value(dev, &ticks);
265+
266+
alarm_cb(dev, 0, ticks, user_data);
267+
}
268+
269+
counter_it51xxx_alarm_timer_disable(dev);
270+
}
271+
272+
static void counter_it51xxx_top_isr(const struct device *dev)
273+
{
274+
const struct counter_it51xxx_config *config = dev->config;
275+
struct counter_it51xxx_data *data = dev->data;
276+
277+
LOG_DBG("Top timer ISR");
278+
279+
if (data->top_callback) {
280+
data->top_callback(dev, data->top_user_data);
281+
}
282+
283+
/* Read clear top timer terminal count */
284+
sys_read8(config->base + REG_TIMER_ET8CTRL);
285+
286+
ite_intc_isr_clear(config->top_irq);
287+
}
288+
289+
static int counter_it51xxx_init(const struct device *dev)
290+
{
291+
const struct counter_it51xxx_config *config = dev->config;
292+
uint8_t et7_ctrl = sys_read8(config->base + REG_TIMER_ET7CTRL);
293+
uint8_t et8_ctrl = sys_read8(config->base + REG_TIMER_ET8CTRL);
294+
295+
/* First time enable: enable and re-start timer -> disable timer */
296+
sys_write8(et7_ctrl | TIMER_RST_EN, config->base + REG_TIMER_ET7CTRL);
297+
sys_write8(et7_ctrl & ~TIMER_ETEN, config->base + REG_TIMER_ET7CTRL);
298+
sys_write8(et8_ctrl | TIMER_RST_EN, config->base + REG_TIMER_ET8CTRL);
299+
sys_write8(et8_ctrl & ~TIMER_ETEN, config->base + REG_TIMER_ET8CTRL);
300+
301+
/* Set rising edge trigger of alarm timer and top timer */
302+
ite_intc_irq_polarity_set(config->alarm_irq, config->alarm_flag);
303+
ite_intc_irq_polarity_set(config->top_irq, config->top_flag);
304+
305+
/* Clear interrupt status of alarm timer and top timer */
306+
ite_intc_isr_clear(config->alarm_irq);
307+
ite_intc_isr_clear(config->top_irq);
308+
309+
/* Select clock source of alarm timer and top timer */
310+
sys_write8(TIMER_ETPSR_32768HZ, config->base + REG_TIMER_ET7PSR);
311+
sys_write8(TIMER_ETPSR_32768HZ, config->base + REG_TIMER_ET8PSR);
312+
313+
/* Set top value ticks to top timer */
314+
sys_write32(config->info.max_top_value, config->base + REG_TIMER_ET8CNTLLR);
315+
316+
config->irq_config_func(dev);
317+
318+
LOG_DBG("Max top timer ticks = 0x%x", config->info.max_top_value);
319+
LOG_DBG("Clock frequency = %d", config->info.freq);
320+
LOG_DBG("Channels = %d", config->info.channels);
321+
322+
return 0;
323+
}
324+
325+
static DEVICE_API(counter, counter_it51xxx_driver_api) = {
326+
.start = counter_it51xxx_start,
327+
.stop = counter_it51xxx_stop,
328+
.get_value = counter_it51xxx_get_value,
329+
.set_alarm = counter_it51xxx_set_alarm,
330+
.cancel_alarm = counter_it51xxx_cancel_alarm,
331+
.set_top_value = counter_it51xxx_set_top_value,
332+
.get_top_value = counter_it51xxx_get_top_value,
333+
};
334+
335+
#define COUNTER_IT51XXX_INIT(inst) \
336+
static void counter_it51xxx_cfg_func_##inst(const struct device *dev) \
337+
{ \
338+
IRQ_CONNECT(DT_INST_IRQN_BY_IDX(inst, 0), 0, counter_it51xxx_alarm_isr, \
339+
DEVICE_DT_INST_GET(inst), 0); \
340+
IRQ_CONNECT(DT_INST_IRQN_BY_IDX(inst, 1), 0, counter_it51xxx_top_isr, \
341+
DEVICE_DT_INST_GET(inst), 0); \
342+
} \
343+
\
344+
static struct counter_it51xxx_config counter_it51xxx_config_##inst = { \
345+
.info = \
346+
{ \
347+
.max_top_value = UINT32_MAX, \
348+
.freq = 32768, \
349+
.flags = 0, \
350+
.channels = 1, \
351+
}, \
352+
.base = DT_INST_REG_ADDR(inst), \
353+
.alarm_irq = DT_INST_IRQN_BY_IDX(inst, 0), \
354+
.alarm_flag = DT_INST_IRQ_BY_IDX(inst, 0, flags), \
355+
.top_irq = DT_INST_IRQN_BY_IDX(inst, 1), \
356+
.top_flag = DT_INST_IRQ_BY_IDX(inst, 1, flags), \
357+
.irq_config_func = counter_it51xxx_cfg_func_##inst, \
358+
}; \
359+
\
360+
static struct counter_it51xxx_data counter_it51xxx_data_##inst; \
361+
\
362+
DEVICE_DT_INST_DEFINE(inst, &counter_it51xxx_init, NULL, &counter_it51xxx_data_##inst, \
363+
&counter_it51xxx_config_##inst, POST_KERNEL, \
364+
CONFIG_COUNTER_INIT_PRIORITY, &counter_it51xxx_driver_api);
365+
366+
DT_INST_FOREACH_STATUS_OKAY(COUNTER_IT51XXX_INIT)
367+
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
368+
"only one ite,it51xxx-counter compatible node can be supported");

0 commit comments

Comments
 (0)