1 /*******************************************************************************
2 * \file cyhal_comp_lp.c
3 *
4 * \brief
5 * Provides an implementation of the comp HAL on top of the LP (low power) comp.
6 *
7 ********************************************************************************
8 * \copyright
9 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
10 * an affiliate of Cypress Semiconductor Corporation
11 *
12 * SPDX-License-Identifier: Apache-2.0
13 *
14 * Licensed under the Apache License, Version 2.0 (the "License");
15 * you may not use this file except in compliance with the License.
16 * You may obtain a copy of the License at
17 *
18 * http://www.apache.org/licenses/LICENSE-2.0
19 *
20 * Unless required by applicable law or agreed to in writing, software
21 * distributed under the License is distributed on an "AS IS" BASIS,
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 * See the License for the specific language governing permissions and
24 * limitations under the License.
25 *******************************************************************************/
26
27 #include <string.h>
28 #include "cyhal_comp_lp.h"
29 #include "cyhal_analog_common.h"
30 #include "cyhal_hwmgr.h"
31 #include "cyhal_irq_impl.h"
32
33 #if (_CYHAL_DRIVER_AVAILABLE_COMP_LP)
34
35 #if defined(__cplusplus)
36 extern "C"
37 {
38 #endif
39
40 #define _CYHAL_COMP_PER_LP (2u)
41 #define _CYHAL_COMP_GET_CHANNEL(channel_num) ((1u == (channel_num)) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0)
42
43 #if defined(CY_IP_MXLPCOMP_INSTANCES)
44 #define _CYHAL_LPCOMP_INSTANCES CY_IP_MXLPCOMP_INSTANCES
45
46 #define _cyhal_lpcomp_init(base, channel, config) \
47 Cy_LPComp_Init((base), (channel), (config))
48
49 #define _cyhal_lpcomp_enable(base, channel) \
50 Cy_LPComp_Enable((base), (channel))
51
52 #define _cyhal_lpcomp_disable(base, channel) \
53 Cy_LPComp_Disable((base), (channel))
54
55 #define _cyhal_lpcomp_setinterrupttriggermode(base, channel, intType) \
56 Cy_LPComp_SetInterruptTriggerMode((base), (channel), (intType))
57
58 #elif defined(CY_IP_M0S8LPCOMP_INSTANCES)
59 #define _CYHAL_LPCOMP_INSTANCES CY_IP_M0S8LPCOMP_INSTANCES
60
61 #define _cyhal_lpcomp_init(base, channel, config) \
62 Cy_LPComp_Init((base), (channel), (config), &(obj->context))
63
64 #define _cyhal_lpcomp_enable(base, channel) \
65 Cy_LPComp_Enable((base), (channel), &(obj->context))
66
67 #define _cyhal_lpcomp_disable(base, channel) \
68 Cy_LPComp_Disable((base), (channel), &(obj->context))
69
70 #define _cyhal_lpcomp_setinterrupttriggermode(base, channel, intType) \
71 Cy_LPComp_SetInterruptTriggerMode((base), (channel), (intType), &(obj->context))
72
73 #else
74 #error Unhandled LPCOMP IP Block
75 #endif
76
77
78 static cyhal_comp_t* _cyhal_comp_lp_config_structs[_CYHAL_LPCOMP_INSTANCES * _CYHAL_COMP_PER_LP];
79
80 LPCOMP_Type *const _cyhal_lpcomp_base[] =
81 {
82 #if (_CYHAL_LPCOMP_INSTANCES == 1)
83 LPCOMP,
84 #else
85 #error Unhandled LP_COMP instance count
86 #endif
87 };
88
89 static const _cyhal_system_irq_t _cyhal_lp_comp_irq_n[] =
90 {
91 #if (_CYHAL_LPCOMP_INSTANCES == 1)
92 lpcomp_interrupt_IRQn,
93 #else
94 #error Unhandled LP_COMP instance count
95 #endif
96 };
97
_cyhal_lpcomp_get_block_from_irqn(_cyhal_system_irq_t irqn)98 static uint8_t _cyhal_lpcomp_get_block_from_irqn(_cyhal_system_irq_t irqn)
99 {
100 switch (irqn)
101 {
102 #if (_CYHAL_LPCOMP_INSTANCES == 1)
103 case lpcomp_interrupt_IRQn:
104 return 0;
105 #else
106 #error Unhandled LP_COMP instance count
107 #endif
108 default:
109 CY_ASSERT(false); // Should never be called
110 return 0;
111 }
112 }
113
_cyhal_comp_lp_convert_power(cyhal_power_level_t hal_power,cy_en_lpcomp_pwr_t * pdl_power)114 static cy_rslt_t _cyhal_comp_lp_convert_power(cyhal_power_level_t hal_power, cy_en_lpcomp_pwr_t* pdl_power)
115 {
116 cy_rslt_t rslt = CY_RSLT_SUCCESS;
117 switch(hal_power)
118 {
119 case CYHAL_POWER_LEVEL_LOW:
120 *pdl_power = CY_LPCOMP_MODE_ULP;
121 break;
122 #if defined(CY_IP_M0S8LPCOMP_INSTANCES)
123 case CYHAL_POWER_LEVEL_DEFAULT:
124 case CYHAL_POWER_LEVEL_MEDIUM:
125 *pdl_power = CY_LPCOMP_MODE_SLOW;
126 break;
127 case CYHAL_POWER_LEVEL_HIGH:
128 *pdl_power = CY_LPCOMP_MODE_FAST;
129 break;
130 default:
131 CY_ASSERT(false);
132 rslt = CYHAL_COMP_RSLT_ERR_BAD_ARGUMENT;
133 break;
134 #elif defined(CY_IP_MXLPCOMP_INSTANCES)
135 case CYHAL_POWER_LEVEL_OFF:
136 *pdl_power = CY_LPCOMP_MODE_OFF;
137 break;
138 case CYHAL_POWER_LEVEL_DEFAULT:
139 case CYHAL_POWER_LEVEL_MEDIUM:
140 *pdl_power = CY_LPCOMP_MODE_LP;
141 break;
142 case CYHAL_POWER_LEVEL_HIGH:
143 *pdl_power = CY_LPCOMP_MODE_NORMAL;
144 break;
145 default:
146 CY_ASSERT(false);
147 rslt = CYHAL_COMP_RSLT_ERR_BAD_ARGUMENT;
148 break;
149 #endif
150 }
151 return rslt;
152 }
153
_cyhal_comp_lp_get_enabled_events(cyhal_comp_t * obj)154 static cyhal_comp_event_t _cyhal_comp_lp_get_enabled_events(cyhal_comp_t * obj)
155 {
156 switch((cy_en_lpcomp_int_t)obj->irq_cause)
157 {
158 case CY_LPCOMP_INTR_DISABLE:
159 return (cyhal_comp_event_t)0u;
160 case CY_LPCOMP_INTR_RISING:
161 return CYHAL_COMP_RISING_EDGE;
162 case CY_LPCOMP_INTR_FALLING:
163 return CYHAL_COMP_FALLING_EDGE;
164 case CY_LPCOMP_INTR_BOTH:
165 return (cyhal_comp_event_t)(CYHAL_COMP_RISING_EDGE | CYHAL_COMP_FALLING_EDGE);
166 default:
167 CY_ASSERT(false);
168 return (cyhal_comp_event_t)0u;
169 }
170 }
171
_cyhal_comp_lp_convert_hal_event(cyhal_comp_event_t event)172 static cy_en_lpcomp_int_t _cyhal_comp_lp_convert_hal_event(cyhal_comp_event_t event)
173 {
174 switch((uint8_t)event)
175 {
176 case 0u:
177 return CY_LPCOMP_INTR_DISABLE;
178 case (uint8_t)CYHAL_COMP_RISING_EDGE:
179 return CY_LPCOMP_INTR_RISING;
180 case (uint8_t)CYHAL_COMP_FALLING_EDGE:
181 return CY_LPCOMP_INTR_FALLING;
182 case (uint8_t)(CYHAL_COMP_RISING_EDGE | CYHAL_COMP_FALLING_EDGE):
183 return CY_LPCOMP_INTR_BOTH;
184 default:
185 CY_ASSERT(false);
186 return CY_LPCOMP_INTR_DISABLE;
187 }
188 }
189
_cyhal_comp_lp_irq_handler(void)190 static void _cyhal_comp_lp_irq_handler(void)
191 {
192 _cyhal_system_irq_t irqn = _cyhal_irq_get_active();
193 uint8_t block = _cyhal_lpcomp_get_block_from_irqn(irqn);
194 uint32_t instance_num = (uint32_t) (Cy_LPComp_GetInterruptStatusMasked(_cyhal_lpcomp_base[block]) >> 1) ? 1 : 0;
195
196 cyhal_comp_t* obj = _cyhal_comp_lp_config_structs[(block * _CYHAL_COMP_PER_LP) + instance_num];
197 if (NULL != obj)
198 {
199 Cy_LPComp_ClearInterrupt(obj->base_lpcomp, (uint32_t) (1u << obj->resource.channel_num));
200
201 cyhal_comp_event_callback_t callback = (cyhal_comp_event_callback_t)obj->callback_data.callback;
202 if(NULL != callback)
203 {
204 cyhal_comp_event_t event = _cyhal_comp_lp_get_enabled_events(obj);
205 callback(obj->callback_data.callback_arg, event);
206 }
207 }
208 }
209
_cyhal_comp_lp_init_hw(cyhal_comp_t * obj,const cy_stc_lpcomp_config_t * cfg)210 cy_rslt_t _cyhal_comp_lp_init_hw(cyhal_comp_t *obj, const cy_stc_lpcomp_config_t* cfg)
211 {
212 obj->base_lpcomp = _cyhal_lpcomp_base[obj->resource.block_num];
213 cy_rslt_t result = _cyhal_lpcomp_init(obj->base_lpcomp, _CYHAL_COMP_GET_CHANNEL(obj->resource.channel_num), cfg);
214
215 if(CY_RSLT_SUCCESS == result)
216 {
217 Cy_LPComp_SetInterruptMask(obj->base_lpcomp, (uint32_t) (1u << obj->resource.channel_num));
218
219 /* Initialize the programmable analog */
220 _cyhal_lpcomp_enable(obj->base_lpcomp, _CYHAL_COMP_GET_CHANNEL(obj->resource.channel_num));
221
222 _cyhal_comp_lp_config_structs[(obj->resource.block_num * _CYHAL_COMP_PER_LP) + obj->resource.channel_num] = obj;
223 _cyhal_irq_register(_cyhal_lp_comp_irq_n[obj->resource.block_num], CYHAL_ISR_PRIORITY_DEFAULT, _cyhal_comp_lp_irq_handler);
224 _cyhal_irq_enable(_cyhal_lp_comp_irq_n[obj->resource.block_num]);
225 }
226 return result;
227 }
228
_cyhal_comp_lp_init(cyhal_comp_t * obj,cyhal_gpio_t vin_p,cyhal_gpio_t vin_m,cyhal_gpio_t output,cyhal_comp_config_t * cfg)229 cy_rslt_t _cyhal_comp_lp_init(cyhal_comp_t *obj, cyhal_gpio_t vin_p, cyhal_gpio_t vin_m, cyhal_gpio_t output, cyhal_comp_config_t *cfg)
230 {
231 CY_ASSERT(NULL != obj);
232
233 /* Initial values */
234 cy_rslt_t result = CY_RSLT_SUCCESS;
235 memset(obj, 0, sizeof(cyhal_comp_t));
236 obj->irq_cause = 0;
237
238 /* Mark pins in obj NC until they are successfully reserved */
239 obj->pin_vin_p = NC;
240 obj->pin_vin_m = NC;
241 obj->pin_out = NC;
242
243 obj->base_lpcomp = NULL;
244 obj->resource.type = CYHAL_RSC_INVALID;
245
246 /* Validate pins. vin_p and vin_m are mandatory pins, output is optional. */
247 if ((NC == vin_p) || (NC == vin_m))
248 {
249 result = CYHAL_COMP_RSLT_ERR_INVALID_PIN;
250 }
251
252 /* Get mapping for pins */
253 const cyhal_resource_pin_mapping_t *vin_p_map = (NC != vin_p) ? _CYHAL_UTILS_GET_RESOURCE(vin_p, cyhal_pin_map_lpcomp_inp_comp) : NULL;
254 const cyhal_resource_pin_mapping_t *vin_m_map = (NC != vin_m) ? _CYHAL_UTILS_GET_RESOURCE(vin_m, cyhal_pin_map_lpcomp_inn_comp) : NULL;
255 const cyhal_resource_pin_mapping_t *output_map = (NC != output) ? _CYHAL_UTILS_GET_RESOURCE(output, cyhal_pin_map_lpcomp_dsi_comp) : NULL;
256
257 /* Verify if mapping successful */
258 if ((NULL == vin_p_map) || (NULL == vin_m_map) || ((NC != output) && (NULL == output_map)))
259 {
260 result = CYHAL_COMP_RSLT_ERR_INVALID_PIN;
261 }
262
263 if ((CY_RSLT_SUCCESS == result) &&
264 ((NULL != vin_m_map && (false == _cyhal_utils_map_resources_equal(vin_p_map, vin_m_map)))
265 || (NULL != output_map && (false == _cyhal_utils_map_resources_equal(vin_p_map, output_map)))))
266 {
267 result = CYHAL_COMP_RSLT_ERR_INVALID_PIN;
268 }
269
270 /* Reserve resources */
271 if (NULL != vin_p_map)
272 {
273 cyhal_resource_inst_t rsc = { CYHAL_RSC_LPCOMP, vin_p_map->block_num, vin_p_map->channel_num };
274 if(CY_RSLT_SUCCESS == result)
275 {
276 result = cyhal_hwmgr_reserve(&rsc);
277 }
278
279 if(CY_RSLT_SUCCESS == result)
280 {
281 obj->resource = rsc;
282 result = _cyhal_utils_reserve_and_connect(vin_p_map, CYHAL_PIN_MAP_DRIVE_MODE_LPCOMP_INP_COMP);
283 }
284
285 if(CY_RSLT_SUCCESS == result)
286 {
287 obj->pin_vin_p = vin_p;
288 result = _cyhal_utils_reserve_and_connect(vin_m_map, CYHAL_PIN_MAP_DRIVE_MODE_LPCOMP_INN_COMP);
289 }
290
291 if(CY_RSLT_SUCCESS == result)
292 {
293 obj->pin_vin_m = vin_m;
294 if(NC != output)
295 {
296 result = _cyhal_utils_reserve_and_connect(output_map, CYHAL_PIN_MAP_DRIVE_MODE_LPCOMP_DSI_COMP);
297 }
298 }
299
300 if(CY_RSLT_SUCCESS == result)
301 {
302 obj->pin_out = output;
303 }
304
305 if (result == CY_RSLT_SUCCESS)
306 {
307 /* Configure LPComp output mode and hysteresis for channel 0 or 1 */
308 cy_stc_lpcomp_config_t comp_lp_config;
309
310 comp_lp_config.outputMode = CY_LPCOMP_OUT_DIRECT;
311 comp_lp_config.intType = CY_LPCOMP_INTR_DISABLE;
312 comp_lp_config.hysteresis = cfg->hysteresis ? CY_LPCOMP_HYST_ENABLE : CY_LPCOMP_HYST_DISABLE;
313 result = _cyhal_comp_lp_convert_power(cfg->power, &(comp_lp_config.power));
314
315 if (result == CY_RSLT_SUCCESS)
316 {
317 result = _cyhal_comp_lp_init_hw(obj, &comp_lp_config);
318 }
319
320 if (result == CY_RSLT_SUCCESS)
321 {
322 #if defined(CY_IP_MXLPCOMP_INSTANCES)
323 /* Set both terminals to GPIO inputs */
324 Cy_LPComp_SetInputs(obj->base_lpcomp, _CYHAL_COMP_GET_CHANNEL(obj->resource.channel_num), CY_LPCOMP_SW_GPIO, CY_LPCOMP_SW_GPIO);
325 #endif
326
327 /* Set channel 0 or 1 power mode - Ultra Low Power mode */
328 Cy_LPComp_SetPower(obj->base_lpcomp, _CYHAL_COMP_GET_CHANNEL(obj->resource.channel_num), comp_lp_config.power);
329 }
330 }
331 }
332
333 /* Free LP_COMP in case of failure */
334 if (result != CY_RSLT_SUCCESS)
335 {
336 _cyhal_comp_lp_free(obj);
337 }
338 return result;
339 }
340
_cyhal_comp_lp_init_cfg(cyhal_comp_t * obj,const cyhal_comp_configurator_t * cfg)341 cy_rslt_t _cyhal_comp_lp_init_cfg(cyhal_comp_t *obj, const cyhal_comp_configurator_t *cfg)
342 {
343 cy_rslt_t result = _cyhal_comp_lp_init_hw(obj, cfg->lpcomp);
344 if(CY_RSLT_SUCCESS != result)
345 {
346 _cyhal_comp_lp_free(obj);
347 }
348
349 return result;
350 }
351
_cyhal_comp_lp_free(cyhal_comp_t * obj)352 void _cyhal_comp_lp_free(cyhal_comp_t *obj)
353 {
354 CY_ASSERT(NULL != obj);
355
356 if(CYHAL_RSC_INVALID != obj->resource.type)
357 {
358 _cyhal_comp_lp_config_structs[(obj->resource.block_num * _CYHAL_COMP_PER_LP) + obj->resource.channel_num] = NULL;
359
360 /* Free the programmable analog */
361 uint8_t lpcomp_num = obj->resource.block_num;
362 if((NULL == _cyhal_comp_lp_config_structs[(lpcomp_num * _CYHAL_COMP_PER_LP)])
363 && (NULL == _cyhal_comp_lp_config_structs[(lpcomp_num * _CYHAL_COMP_PER_LP) + 1]))
364 {
365 _cyhal_irq_free(_cyhal_lp_comp_irq_n[lpcomp_num]);
366 }
367
368 if(NULL != obj->base_lpcomp)
369 {
370 _cyhal_lpcomp_disable(obj->base_lpcomp, _CYHAL_COMP_GET_CHANNEL(obj->resource.channel_num));
371 }
372 if(false == obj->owned_by_configurator)
373 {
374 cyhal_hwmgr_free(&(obj->resource));
375 }
376 obj->base_lpcomp = NULL;
377 obj->resource.type = CYHAL_RSC_INVALID;
378 }
379
380 _cyhal_utils_release_if_used(&(obj->pin_vin_p));
381 _cyhal_utils_release_if_used(&(obj->pin_vin_m));
382 _cyhal_utils_release_if_used(&(obj->pin_out));
383 }
384
_cyhal_comp_lp_configure(cyhal_comp_t * obj,cyhal_comp_config_t * cfg)385 cy_rslt_t _cyhal_comp_lp_configure(cyhal_comp_t *obj, cyhal_comp_config_t *cfg)
386 {
387 CY_ASSERT(NULL != obj);
388 cy_rslt_t result = CY_RSLT_SUCCESS;
389
390 result = _cyhal_comp_lp_set_power(obj, cfg->power);
391 if (result == CY_RSLT_SUCCESS)
392 {
393 Cy_LPComp_SetHysteresis(obj->base_lpcomp, _CYHAL_COMP_GET_CHANNEL(obj->resource.channel_num), cfg->hysteresis ? CY_LPCOMP_HYST_ENABLE : CY_LPCOMP_HYST_DISABLE);
394 }
395 return result;
396 }
397
_cyhal_comp_lp_set_power(cyhal_comp_t * obj,cyhal_power_level_t power)398 cy_rslt_t _cyhal_comp_lp_set_power(cyhal_comp_t *obj, cyhal_power_level_t power)
399 {
400 CY_ASSERT(NULL != obj);
401 cy_en_lpcomp_pwr_t power_level;
402 cy_rslt_t rslt = _cyhal_comp_lp_convert_power(power, &power_level);
403 if (CY_RSLT_SUCCESS == rslt)
404 {
405 Cy_LPComp_SetPower(obj->base_lpcomp, _CYHAL_COMP_GET_CHANNEL(obj->resource.channel_num), power_level);
406 }
407 return rslt;
408 }
409
_cyhal_comp_lp_read(cyhal_comp_t * obj)410 bool _cyhal_comp_lp_read(cyhal_comp_t *obj)
411 {
412 CY_ASSERT(NULL != obj);
413 return (1UL == Cy_LPComp_GetCompare(obj->base_lpcomp, _CYHAL_COMP_GET_CHANNEL(obj->resource.channel_num)));
414 }
415
_cyhal_comp_lp_enable_event(cyhal_comp_t * obj,cyhal_comp_event_t event,uint8_t intr_priority,bool enable)416 void _cyhal_comp_lp_enable_event(cyhal_comp_t *obj, cyhal_comp_event_t event, uint8_t intr_priority, bool enable)
417 {
418 if(enable)
419 {
420 obj->irq_cause |= event;
421 }
422 else
423 {
424 obj->irq_cause &= (~event);
425 }
426
427 _cyhal_system_irq_t irqn = _cyhal_lp_comp_irq_n[obj->resource.block_num];
428 _cyhal_irq_set_priority(irqn, intr_priority);
429
430 cy_en_lpcomp_int_t pdl_event = _cyhal_comp_lp_convert_hal_event((cyhal_comp_event_t)obj->irq_cause);
431 _cyhal_lpcomp_setinterrupttriggermode(obj->base_lpcomp, _CYHAL_COMP_GET_CHANNEL(obj->resource.channel_num), pdl_event);
432 }
433
434 #if defined(__cplusplus)
435 }
436 #endif
437
438 #endif /* _CYHAL_DRIVER_AVAILABLE_COMP_LP */
439