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