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