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