1 /*
2  * Copyright (c) 2025 Renesas Electronics Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT renesas_ra_acmphs
8 
9 #include <zephyr/drivers/comparator.h>
10 #include <zephyr/drivers/pinctrl.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/sys/atomic.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/irq.h>
15 #include <soc.h>
16 #include "r_acmphs.h"
17 
18 LOG_MODULE_REGISTER(acmphs_renesas_ra, CONFIG_COMPARATOR_LOG_LEVEL);
19 
20 #define ACMPHS_RENESAS_RA_FLAG_HS BIT(0)
21 
22 void comp_hs_int_isr(void);
23 
24 struct acmphs_renesas_ra_global_config {
25 	const struct pinctrl_dev_config *pcfg;
26 };
27 
28 struct acmphs_renesas_ra_config {
29 	const struct pinctrl_dev_config *pcfg;
30 };
31 
32 struct acmphs_renesas_ra_data {
33 	const struct device *dev;
34 	acmphs_instance_ctrl_t acmphs;
35 	comparator_cfg_t fsp_config;
36 	comparator_callback_t user_cb;
37 	void *user_cb_data;
38 	atomic_t flags;
39 };
40 
acmphs_renesas_get_output(const struct device * dev)41 static int acmphs_renesas_get_output(const struct device *dev)
42 {
43 	struct acmphs_renesas_ra_data *data = dev->data;
44 	comparator_status_t status;
45 	fsp_err_t fsp_err;
46 
47 	fsp_err = R_ACMPHS_StatusGet(&data->acmphs, &status);
48 	if (FSP_SUCCESS != fsp_err) {
49 		return -EIO;
50 	}
51 
52 	switch (status.state) {
53 	case COMPARATOR_STATE_OUTPUT_LOW:
54 		return 0;
55 
56 	case COMPARATOR_STATE_OUTPUT_HIGH:
57 		return 1;
58 
59 	case COMPARATOR_STATE_OUTPUT_DISABLED:
60 		LOG_ERR("Need to set trigger to open comparator first");
61 		return -EIO;
62 	}
63 
64 	return 0;
65 }
66 
acmphs_renesas_set_trigger(const struct device * dev,enum comparator_trigger trigger)67 static int acmphs_renesas_set_trigger(const struct device *dev, enum comparator_trigger trigger)
68 {
69 	struct acmphs_renesas_ra_data *data = dev->data;
70 
71 	/* Disable interrupt */
72 	data->acmphs.p_reg->CMPCTL &= ~R_ACMPHS0_CMPCTL_CEG_Msk;
73 
74 	switch (trigger) {
75 	case COMPARATOR_TRIGGER_RISING_EDGE:
76 		data->fsp_config.trigger = COMPARATOR_TRIGGER_RISING;
77 		break;
78 
79 	case COMPARATOR_TRIGGER_FALLING_EDGE:
80 		data->fsp_config.trigger = COMPARATOR_TRIGGER_FALLING;
81 		break;
82 
83 	case COMPARATOR_TRIGGER_BOTH_EDGES:
84 		data->fsp_config.trigger = COMPARATOR_TRIGGER_BOTH_EDGE;
85 		break;
86 
87 	case COMPARATOR_TRIGGER_NONE:
88 		data->fsp_config.trigger = COMPARATOR_TRIGGER_NO_EDGE;
89 		break;
90 	}
91 
92 	data->acmphs.p_reg->CMPCTL |= (data->fsp_config.trigger << R_ACMPHS0_CMPCTL_CEG_Pos);
93 
94 	return 0;
95 }
96 
acmphs_renesas_set_trigger_callback(const struct device * dev,comparator_callback_t callback,void * user_data)97 static int acmphs_renesas_set_trigger_callback(const struct device *dev,
98 					       comparator_callback_t callback, void *user_data)
99 {
100 	struct acmphs_renesas_ra_data *data = dev->data;
101 
102 	/* Disable interrupt */
103 	data->acmphs.p_reg->CMPCTL &= ~R_ACMPHS0_CMPCTL_CEG_Msk;
104 
105 	data->user_cb = callback;
106 	data->user_cb_data = user_data;
107 
108 	/* Enable interrupt */
109 	data->acmphs.p_reg->CMPCTL |= (data->fsp_config.trigger << R_ACMPHS0_CMPCTL_CEG_Pos);
110 
111 	return 0;
112 }
113 
acmphs_renesas_trigger_is_pending(const struct device * dev)114 static int acmphs_renesas_trigger_is_pending(const struct device *dev)
115 {
116 	struct acmphs_renesas_ra_data *data = dev->data;
117 
118 	if (data->flags & ACMPHS_RENESAS_RA_FLAG_HS) {
119 		atomic_and(&data->flags, ~ACMPHS_RENESAS_RA_FLAG_HS);
120 		return 1;
121 	}
122 
123 	return 0;
124 }
125 
acmphs_renesas_ra_hs_isr(comparator_callback_args_t * fsp_args)126 static void acmphs_renesas_ra_hs_isr(comparator_callback_args_t *fsp_args)
127 {
128 	const struct device *dev = fsp_args->p_context;
129 	struct acmphs_renesas_ra_data *data = dev->data;
130 	comparator_callback_t cb = data->user_cb;
131 
132 	if (cb != NULL) {
133 		cb(dev, data->user_cb_data);
134 		return;
135 	}
136 
137 	atomic_or(&data->flags, ACMPHS_RENESAS_RA_FLAG_HS);
138 }
139 
acmphs_renesas_ra_global_init(const struct device * dev)140 static int acmphs_renesas_ra_global_init(const struct device *dev)
141 {
142 	const struct acmphs_renesas_ra_global_config *cfg = dev->config;
143 	int ret;
144 
145 	ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
146 	if (ret < 0) {
147 		LOG_DBG("pin VCOUT initial failed");
148 		return ret;
149 	}
150 
151 	return 0;
152 }
153 
acmphs_renesas_ra_init(const struct device * dev)154 static int acmphs_renesas_ra_init(const struct device *dev)
155 {
156 	struct acmphs_renesas_ra_data *data = dev->data;
157 	const struct acmphs_renesas_ra_config *cfg = dev->config;
158 	fsp_err_t fsp_err;
159 	int ret;
160 
161 	ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
162 	if (ret < 0) {
163 		LOG_DBG("pin default initial failed");
164 		return ret;
165 	}
166 
167 	data->fsp_config.p_context = dev;
168 
169 	fsp_err = R_ACMPHS_Open(&data->acmphs, &data->fsp_config);
170 	if (FSP_SUCCESS != fsp_err) {
171 		return -EIO;
172 	}
173 
174 	/*
175 	 * Once the analog comparator is configurate, the program must wait
176 	 * for the ACMPHS stabilization time (300ns enabling + 200ns input switching)
177 	 * before using the comparator.
178 	 */
179 	k_usleep(5);
180 
181 	fsp_err = R_ACMPHS_OutputEnable(&data->acmphs);
182 	if (FSP_SUCCESS != fsp_err) {
183 		return -EIO;
184 	}
185 
186 	return 0;
187 }
188 
189 static DEVICE_API(comparator, acmphs_renesas_ra_api) = {
190 	.get_output = acmphs_renesas_get_output,
191 	.set_trigger = acmphs_renesas_set_trigger,
192 	.set_trigger_callback = acmphs_renesas_set_trigger_callback,
193 	.trigger_is_pending = acmphs_renesas_trigger_is_pending,
194 };
195 
196 PINCTRL_DT_DEFINE(DT_INST(0, renesas_ra_acmphs_global));
197 
198 static const struct acmphs_renesas_ra_global_config acmphs_renesas_ra_global_config = {
199 	.pcfg = PINCTRL_DT_DEV_CONFIG_GET(DT_INST(0, renesas_ra_acmphs_global)),
200 };
201 
202 DEVICE_DT_DEFINE(DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_acmphs_global),
203 		 acmphs_renesas_ra_global_init, NULL, NULL, &acmphs_renesas_ra_global_config,
204 		 PRE_KERNEL_2, CONFIG_COMPARATOR_INIT_PRIORITY, NULL)
205 
206 #define ACMPHS_RENESAS_RA_IRQ_INIT(idx)                                                            \
207 	{                                                                                          \
208 		R_ICU->IELSR_b[DT_INST_IRQ_BY_NAME(idx, hs, irq)].IELS =                           \
209 			BSP_PRV_IELS_ENUM(CONCAT(EVENT_ACMPHS, DT_INST_PROP(idx, channel), _INT)); \
210                                                                                                    \
211 		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(idx, hs, irq),                                     \
212 			    DT_INST_IRQ_BY_NAME(idx, hs, priority), comp_hs_int_isr,               \
213 			    DEVICE_DT_INST_GET(idx), 0);                                           \
214                                                                                                    \
215 		irq_enable(DT_INST_IRQ_BY_NAME(idx, hs, irq));                                     \
216 	}
217 
218 #define FILTER_PARAMETER(idx)                                                                      \
219 	COND_CODE_1(IS_EQ(DT_INST_PROP(idx, noise_filter), 1),                                     \
220 	(COMPARATOR_FILTER_OFF), (UTIL_CAT(COMPARATOR_FILTER_, DT_INST_PROP(idx, noise_filter))))
221 
222 #define INVERT_PARAMETER(idx)                                                                      \
223 	COND_CODE_1(DT_INST_PROP(idx, output_invert_polarity),                                     \
224 	(COMPARATOR_POLARITY_INVERT_ON), (COMPARATOR_POLARITY_INVERT_OFF))
225 
226 #define PIN_OUTPUT_PARAMETER(idx)                                                                  \
227 	COND_CODE_1(DT_INST_PROP(idx, pin_output_enable),                                          \
228 	(COMPARATOR_PIN_OUTPUT_ON), (COMPARATOR_PIN_OUTPUT_OFF))
229 
230 #define IRQ_PARAMETER(idx)                                                                         \
231 	COND_CODE_1(DT_INST_IRQ_HAS_NAME(idx, hs),                                                 \
232 	(DT_INST_IRQ_BY_NAME(idx, hs, irq)), (FSP_INVALID_VECTOR))
233 
234 #define IPL_PARAMETER(idx)                                                                         \
235 	COND_CODE_1(DT_INST_IRQ_HAS_NAME(idx, hs),                                                 \
236 	(DT_INST_IRQ_BY_NAME(idx, hs, priority)), (BSP_IRQ_DISABLED))
237 
238 #define IRQ_INIT_MACRO_FUNCTION(idx)                                                               \
239 	COND_CODE_1(DT_INST_IRQ_HAS_NAME(idx, hs), (ACMPHS_RENESAS_RA_IRQ_INIT(idx);), ())
240 
241 #define ACMPHS_RENESAS_RA_INIT(idx)                                                                \
242 	PINCTRL_DT_INST_DEFINE(idx);                                                               \
243 	static r_acmphs_extended_cfg_t g_acmphs_cfg_extend_##idx = {                               \
244 		.input_voltage = UTIL_CAT(ACMPHS_INPUT_,                                           \
245 					  DT_INST_STRING_UPPER_TOKEN(idx, compare_input_source)),  \
246 		.reference_voltage =                                                               \
247 			UTIL_CAT(ACMPHS_REFERENCE_,                                                \
248 				 DT_INST_STRING_UPPER_TOKEN(idx, reference_input_source)),         \
249 		.maximum_status_retries = 1024,                                                    \
250 	};                                                                                         \
251 	static const struct acmphs_renesas_ra_config acmphs_renesas_ra_config_##idx = {            \
252 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx),                                       \
253 	};                                                                                         \
254 	static struct acmphs_renesas_ra_data acmphs_renesas_ra_data_##idx = {                      \
255 		.dev = DEVICE_DT_INST_GET(idx),                                                    \
256 		.fsp_config =                                                                      \
257 			{                                                                          \
258 				.channel = DT_INST_PROP(idx, channel),                             \
259 				.mode = COMPARATOR_MODE_NORMAL,                                    \
260 				.trigger = COMPARATOR_TRIGGER_NO_EDGE,                             \
261 				.filter = FILTER_PARAMETER(idx),                                   \
262 				.invert = INVERT_PARAMETER(idx),                                   \
263 				.pin_output = PIN_OUTPUT_PARAMETER(idx),                           \
264 				.p_extend = &g_acmphs_cfg_extend_##idx,                            \
265 				.irq = IRQ_PARAMETER(idx),                                         \
266 				.ipl = IPL_PARAMETER(idx),                                         \
267 				.p_callback = acmphs_renesas_ra_hs_isr,                            \
268 			},                                                                         \
269 		.flags = 0,                                                                        \
270 	};                                                                                         \
271 	static int acmphs_renesas_ra_init##idx(const struct device *dev)                           \
272 	{                                                                                          \
273 		IRQ_INIT_MACRO_FUNCTION(idx)                                                       \
274 		return acmphs_renesas_ra_init(dev);                                                \
275 	}                                                                                          \
276                                                                                                    \
277 	DEVICE_DT_INST_DEFINE(idx, acmphs_renesas_ra_init##idx, NULL,                              \
278 			      &acmphs_renesas_ra_data_##idx, &acmphs_renesas_ra_config_##idx,      \
279 			      POST_KERNEL, CONFIG_COMPARATOR_INIT_PRIORITY,                        \
280 			      &acmphs_renesas_ra_api)
281 
282 DT_INST_FOREACH_STATUS_OKAY(ACMPHS_RENESAS_RA_INIT);
283