1 /*
2 * Copyright (c) 2016 Linaro Limited.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT arm_cmsdk_dtimer
8
9 #include <limits.h>
10
11 #include <zephyr/drivers/counter.h>
12 #include <zephyr/device.h>
13 #include <errno.h>
14 #include <zephyr/init.h>
15 #include <zephyr/irq.h>
16 #include <soc.h>
17 #include <zephyr/drivers/clock_control/arm_clock_control.h>
18 #include <zephyr/irq.h>
19
20 #include "dualtimer_cmsdk_apb.h"
21
22 typedef void (*dtimer_config_func_t)(const struct device *dev);
23
24 struct dtmr_cmsdk_apb_cfg {
25 struct counter_config_info info;
26 volatile struct dualtimer_cmsdk_apb *dtimer;
27 dtimer_config_func_t dtimer_config_func;
28 /* Dualtimer Clock control in Active State */
29 const struct arm_clock_control_t dtimer_cc_as;
30 /* Dualtimer Clock control in Sleep State */
31 const struct arm_clock_control_t dtimer_cc_ss;
32 /* Dualtimer Clock control in Deep Sleep State */
33 const struct arm_clock_control_t dtimer_cc_dss;
34 };
35
36 struct dtmr_cmsdk_apb_dev_data {
37 counter_top_callback_t top_callback;
38 void *top_user_data;
39 uint32_t load;
40 };
41
dtmr_cmsdk_apb_start(const struct device * dev)42 static int dtmr_cmsdk_apb_start(const struct device *dev)
43 {
44 const struct dtmr_cmsdk_apb_cfg * const cfg = dev->config;
45 struct dtmr_cmsdk_apb_dev_data *data = dev->data;
46
47 /* Set the timer reload to count */
48 cfg->dtimer->timer1load = data->load;
49
50 /* Enable the dualtimer in 32 bit mode */
51 cfg->dtimer->timer1ctrl = (DUALTIMER_CTRL_EN | DUALTIMER_CTRL_SIZE_32);
52
53 return 0;
54 }
55
dtmr_cmsdk_apb_stop(const struct device * dev)56 static int dtmr_cmsdk_apb_stop(const struct device *dev)
57 {
58 const struct dtmr_cmsdk_apb_cfg * const cfg = dev->config;
59
60 /* Disable the dualtimer */
61 cfg->dtimer->timer1ctrl = 0x0;
62
63 return 0;
64 }
65
dtmr_cmsdk_apb_get_value(const struct device * dev,uint32_t * ticks)66 static int dtmr_cmsdk_apb_get_value(const struct device *dev, uint32_t *ticks)
67 {
68 const struct dtmr_cmsdk_apb_cfg * const cfg = dev->config;
69 struct dtmr_cmsdk_apb_dev_data *data = dev->data;
70
71 *ticks = data->load - cfg->dtimer->timer1value;
72 return 0;
73 }
74
dtmr_cmsdk_apb_set_top_value(const struct device * dev,const struct counter_top_cfg * top_cfg)75 static int dtmr_cmsdk_apb_set_top_value(const struct device *dev,
76 const struct counter_top_cfg *top_cfg)
77 {
78 const struct dtmr_cmsdk_apb_cfg * const cfg = dev->config;
79 struct dtmr_cmsdk_apb_dev_data *data = dev->data;
80
81 data->top_callback = top_cfg->callback;
82 data->top_user_data = top_cfg->user_data;
83
84 /* Store the reload value */
85 data->load = top_cfg->ticks;
86
87 /* Set the timer to count */
88 if (top_cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
89 /*
90 * Write to background load register will not affect
91 * the current value of the counter.
92 */
93 cfg->dtimer->timer1bgload = top_cfg->ticks;
94 } else {
95 /*
96 * Write to load register will also set
97 * the current value of the counter.
98 */
99 cfg->dtimer->timer1load = top_cfg->ticks;
100 }
101
102 /* Enable IRQ */
103 cfg->dtimer->timer1ctrl |= (DUALTIMER_CTRL_INTEN
104 | DUALTIMER_CTRL_MODE);
105
106 return 0;
107 }
108
dtmr_cmsdk_apb_get_top_value(const struct device * dev)109 static uint32_t dtmr_cmsdk_apb_get_top_value(const struct device *dev)
110 {
111 struct dtmr_cmsdk_apb_dev_data *data = dev->data;
112
113 uint32_t ticks = data->load;
114
115 return ticks;
116 }
117
dtmr_cmsdk_apb_get_pending_int(const struct device * dev)118 static uint32_t dtmr_cmsdk_apb_get_pending_int(const struct device *dev)
119 {
120 const struct dtmr_cmsdk_apb_cfg * const cfg = dev->config;
121
122 return cfg->dtimer->timer1ris;
123 }
124
125 static DEVICE_API(counter, dtmr_cmsdk_apb_api) = {
126 .start = dtmr_cmsdk_apb_start,
127 .stop = dtmr_cmsdk_apb_stop,
128 .get_value = dtmr_cmsdk_apb_get_value,
129 .set_top_value = dtmr_cmsdk_apb_set_top_value,
130 .get_pending_int = dtmr_cmsdk_apb_get_pending_int,
131 .get_top_value = dtmr_cmsdk_apb_get_top_value,
132 };
133
dtmr_cmsdk_apb_isr(const struct device * dev)134 static void dtmr_cmsdk_apb_isr(const struct device *dev)
135 {
136 struct dtmr_cmsdk_apb_dev_data *data = dev->data;
137 const struct dtmr_cmsdk_apb_cfg * const cfg = dev->config;
138
139 cfg->dtimer->timer1intclr = DUALTIMER_INTCLR;
140 if (data->top_callback) {
141 data->top_callback(dev, data->top_user_data);
142 }
143 }
144
dtmr_cmsdk_apb_init(const struct device * dev)145 static int dtmr_cmsdk_apb_init(const struct device *dev)
146 {
147 const struct dtmr_cmsdk_apb_cfg * const cfg = dev->config;
148
149 #ifdef CONFIG_CLOCK_CONTROL
150 /* Enable clock for subsystem */
151 const struct device *const clk = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0));
152
153 if (!device_is_ready(clk)) {
154 return -ENODEV;
155 }
156
157 #ifdef CONFIG_SOC_SERIES_BEETLE
158 clock_control_on(clk, (clock_control_subsys_t) &cfg->dtimer_cc_as);
159 clock_control_on(clk, (clock_control_subsys_t) &cfg->dtimer_cc_ss);
160 clock_control_on(clk, (clock_control_subsys_t) &cfg->dtimer_cc_dss);
161 #endif /* CONFIG_SOC_SERIES_BEETLE */
162 #endif /* CONFIG_CLOCK_CONTROL */
163
164 cfg->dtimer_config_func(dev);
165
166 return 0;
167 }
168
169 #define DTIMER_CMSDK_REG(inst) \
170 ((volatile struct dualtimer_cmsdk_apb *)DT_INST_REG_ADDR(inst))
171
172 #define DTIMER_CMSDK_INIT(inst) \
173 static void dtimer_cmsdk_apb_config_##inst(const struct device *dev); \
174 \
175 static const struct dtmr_cmsdk_apb_cfg \
176 dtmr_cmsdk_apb_cfg_##inst = { \
177 .info = { \
178 .max_top_value = UINT32_MAX, \
179 .freq = 24000000U, \
180 .flags = COUNTER_CONFIG_INFO_COUNT_UP, \
181 .channels = 0U, \
182 }, \
183 .dtimer = DTIMER_CMSDK_REG(inst), \
184 .dtimer_config_func = dtimer_cmsdk_apb_config_##inst, \
185 .dtimer_cc_as = {.bus = CMSDK_APB, \
186 .state = SOC_ACTIVE, \
187 .device = DT_INST_REG_ADDR(inst),}, \
188 .dtimer_cc_ss = {.bus = CMSDK_APB, \
189 .state = SOC_SLEEP, \
190 .device = DT_INST_REG_ADDR(inst),}, \
191 .dtimer_cc_dss = {.bus = CMSDK_APB, \
192 .state = SOC_DEEPSLEEP, \
193 .device = DT_INST_REG_ADDR(inst),}, \
194 }; \
195 \
196 static struct dtmr_cmsdk_apb_dev_data \
197 dtmr_cmsdk_apb_dev_data_##inst = { \
198 .load = UINT_MAX, \
199 }; \
200 \
201 DEVICE_DT_INST_DEFINE(inst, \
202 dtmr_cmsdk_apb_init, \
203 NULL, \
204 &dtmr_cmsdk_apb_dev_data_##inst, \
205 &dtmr_cmsdk_apb_cfg_##inst, POST_KERNEL, \
206 CONFIG_COUNTER_INIT_PRIORITY, \
207 &dtmr_cmsdk_apb_api); \
208 \
209 static void dtimer_cmsdk_apb_config_##inst(const struct device *dev) \
210 { \
211 IRQ_CONNECT(DT_INST_IRQN(inst), \
212 DT_INST_IRQ(inst, priority), \
213 dtmr_cmsdk_apb_isr, \
214 DEVICE_DT_INST_GET(inst), \
215 0); \
216 irq_enable(DT_INST_IRQN(inst)); \
217 }
218
219 DT_INST_FOREACH_STATUS_OKAY(DTIMER_CMSDK_INIT)
220