1 /***************************************************************************/ /**
2 * \file cyhal_comp_ctb.c
3 *
4 * \brief
5 * Provides an implementation of the comp HAL on top of the CTB opamps.
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_ctb.h"
29 #include "cyhal_gpio.h"
30 #include "cyhal_analog_common.h"
31 #include "cyhal_hwmgr.h"
32 #include "cyhal_irq_impl.h"
33 
34 #if (_CYHAL_DRIVER_AVAILABLE_COMP_CTB)
35 
36 #if defined(__cplusplus)
37 extern "C"
38 {
39 #endif
40 
41 #define _CYHAL_OPAMP_PER_CTB (2u)
42 
43 static const cy_stc_ctb_opamp_config_t _cyhal_comp_ctb_default_config =
44 {
45     /* oaPower is specified in init */
46 #if defined(CY_IP_MXS40PASS_CTB_INSTANCES)
47     .deepSleep    = CY_CTB_DEEPSLEEP_ENABLE,
48     .oaMode       = CY_CTB_MODE_COMP,
49     .oaPump       = _CYHAL_CTB_PUMP_ENABLE,
50     .oaCompEdge   = CY_CTB_COMP_EDGE_DISABLE,
51     .oaCompLevel  = CY_CTB_COMP_DSI_TRIGGER_OUT_LEVEL,
52     .oaCompBypass = _CYHAL_COMP_CTB_DEFAULT_BYPASS,
53     /* oaCompHyst is specified in init */
54     .oaCompIntrEn = true,
55 #else
56     .outputMode = CY_CTB_MODE_COMP,
57     .pump       = _CYHAL_CTB_PUMP_ENABLE,
58     .compEdge   = CY_CTB_COMP_EDGE_DISABLE,
59     .compLevel  = CY_CTB_COMP_TRIGGER_OUT_LEVEL,
60     .compBypass = _CYHAL_COMP_CTB_DEFAULT_BYPASS,
61     /* compHyst is specified in init */
62     .compIntrEn = true,
63 #endif
64 };
65 
66 static bool _cyhal_comp_ctb_arrays_initialized = false;
67 CY_NOINIT static cyhal_comp_t* _cyhal_comp_ctb_config_structs[_CYHAL_CTB_INSTANCES * _CYHAL_OPAMP_PER_CTB];
68 
69 static const _cyhal_system_irq_t _cyhal_ctb_irq_n[] =
70 {
71 #if (CY_IP_MXS40PASS_CTB_INSTANCES == 1)
72     pass_interrupt_ctbs_IRQn,
73 #elif (CY_IP_M0S8PASS4A_CTB_INSTANCES == 1)
74     pass_0_interrupt_ctbs_IRQn,
75 #elif (_CYHAL_CTB_INSTANCES == 2)
76     pass_0_interrupt_ctbs_IRQn,
77     pass_1_interrupt_ctbs_IRQn,
78 #else
79     #error Unhandled CTB instance count
80 #endif
81 };
82 
83 /** Get the comp config struct for the opamp that caused the current interrupt */
_cyhal_ctb_get_interrupt_source(void)84 static cyhal_comp_t* _cyhal_ctb_get_interrupt_source(void)
85 {
86     uint32_t ctb_num = 0;
87 #if (_CYHAL_CTB_INSTANCES > 1)
88     _cyhal_system_irq_t irqn = _cyhal_irq_get_active();
89     for (uint32_t i = 0; i < sizeof(_cyhal_ctb_irq_n) / sizeof(_cyhal_system_irq_t); i++)
90     {
91         if (_cyhal_ctb_irq_n[i] == irqn)
92         {
93             ctb_num = i;
94         }
95     }
96 #endif
97 
98     CTBM_Type *ctbm = _cyhal_ctb_base[ctb_num];
99     for(uint8_t oa_num = 0; oa_num < _CYHAL_OPAMP_PER_CTB; ++oa_num)
100     {
101         if(Cy_CTB_GetInterruptStatusMasked(ctbm, _cyhal_opamp_convert_sel(oa_num)))
102         {
103             cyhal_comp_t* inst = _cyhal_comp_ctb_config_structs[(ctb_num * _CYHAL_OPAMP_PER_CTB) + oa_num];
104             if (NULL != inst)
105             {
106                 return inst;
107             }
108         }
109     }
110 
111     return NULL;
112 }
113 
_cyhal_comp_ctb_get_enabled_events(cyhal_comp_t * obj)114 static cyhal_comp_event_t _cyhal_comp_ctb_get_enabled_events(cyhal_comp_t * obj)
115 {
116     uint32_t edge_config_val = (0u == obj->resource.channel_num)
117         ? (CTBM_OA_RES0_CTRL(obj->base_ctb) & CTBM_OA_RES0_CTRL_OA0_COMPINT_Msk)
118         : (CTBM_OA_RES1_CTRL(obj->base_ctb) & CTBM_OA_RES1_CTRL_OA1_COMPINT_Msk);
119 
120     switch(edge_config_val)
121     {
122         case CY_CTB_COMP_EDGE_DISABLE:
123             return (cyhal_comp_event_t)0u;
124         case CY_CTB_COMP_EDGE_RISING:
125             return CYHAL_COMP_RISING_EDGE;
126         case CY_CTB_COMP_EDGE_FALLING:
127             return CYHAL_COMP_FALLING_EDGE;
128         case CY_CTB_COMP_EDGE_BOTH:
129             return (cyhal_comp_event_t)(CYHAL_COMP_RISING_EDGE | CYHAL_COMP_FALLING_EDGE);
130         default:
131             CY_ASSERT(false);
132             return (cyhal_comp_event_t)0u;
133     }
134 }
135 
_cyhal_comp_ctb_convert_hal_event(cyhal_comp_event_t event)136 static cy_en_ctb_comp_edge_t _cyhal_comp_ctb_convert_hal_event(cyhal_comp_event_t event)
137 {
138     switch((uint8_t)event)
139     {
140         case 0u:
141             return CY_CTB_COMP_EDGE_DISABLE;
142         case (uint8_t)CYHAL_COMP_RISING_EDGE:
143             return CY_CTB_COMP_EDGE_RISING;
144         case (uint8_t)CYHAL_COMP_FALLING_EDGE:
145             return CY_CTB_COMP_EDGE_FALLING;
146         case (uint8_t)(CYHAL_COMP_RISING_EDGE | CYHAL_COMP_FALLING_EDGE):
147             return CY_CTB_COMP_EDGE_BOTH;
148         default:
149             CY_ASSERT(false);
150             return CY_CTB_COMP_EDGE_DISABLE;
151     }
152 }
153 
_cyhal_comp_ctb_irq_handler(void)154 static void _cyhal_comp_ctb_irq_handler(void)
155 {
156     cyhal_comp_t* obj = _cyhal_ctb_get_interrupt_source();
157     Cy_CTB_ClearInterrupt(obj->base_ctb, _cyhal_opamp_convert_sel(obj->resource.channel_num));
158     cyhal_comp_event_callback_t callback = (cyhal_comp_event_callback_t)obj->callback_data.callback;
159     if(NULL != callback)
160     {
161         // The CTB hardware doesn't directly capture the event, so just pass on the converted mask
162         cyhal_comp_event_t event = _cyhal_comp_ctb_get_enabled_events(obj);
163         callback(obj->callback_data.callback_arg, event);
164     }
165 }
166 
_cyhal_comp_ctb_cfg_init(void)167 static void _cyhal_comp_ctb_cfg_init(void)
168 {
169     if (!_cyhal_comp_ctb_arrays_initialized)
170     {
171         for (uint8_t i = 0; i < _CYHAL_CTB_INSTANCES * _CYHAL_OPAMP_PER_CTB; i++)
172         {
173             _cyhal_comp_ctb_config_structs[i] = NULL;
174         }
175         _cyhal_comp_ctb_arrays_initialized = true;
176     }
177 }
178 
_cyhal_comp_ctb_init_hw(cyhal_comp_t * obj,const cy_stc_ctb_opamp_config_t * cfg)179 cy_rslt_t _cyhal_comp_ctb_init_hw(cyhal_comp_t *obj, const cy_stc_ctb_opamp_config_t* cfg)
180 {
181     obj->base_ctb = _cyhal_ctb_base[obj->resource.block_num];
182     cy_rslt_t result = Cy_CTB_OpampInit(obj->base_ctb, _cyhal_opamp_convert_sel(obj->resource.channel_num), cfg);
183     if(CY_RSLT_SUCCESS == result)
184     {
185         /* Initialize the programmable analog */
186         cyhal_analog_ctb_init(obj->base_ctb);
187 
188         _cyhal_irq_register(_cyhal_ctb_irq_n[obj->resource.block_num], CYHAL_ISR_PRIORITY_DEFAULT, _cyhal_comp_ctb_irq_handler);
189         _cyhal_irq_enable(_cyhal_ctb_irq_n[obj->resource.block_num]);
190         _cyhal_comp_ctb_config_structs[(obj->resource.block_num * _CYHAL_OPAMP_PER_CTB) + obj->resource.channel_num] = obj;
191     }
192     else
193     {
194         obj->base_ctb = NULL;
195     }
196 
197     return result;
198 }
199 
_cyhal_comp_ctb_init(cyhal_comp_t * obj,cyhal_gpio_t vin_p,cyhal_gpio_t vin_m,cyhal_gpio_t output,cyhal_comp_config_t * cfg)200 cy_rslt_t _cyhal_comp_ctb_init(cyhal_comp_t *obj, cyhal_gpio_t vin_p, cyhal_gpio_t vin_m, cyhal_gpio_t output, cyhal_comp_config_t *cfg)
201 {
202     CY_ASSERT(NULL != obj);
203 
204     /* Initial values */
205     cy_rslt_t result = CY_RSLT_SUCCESS;
206     memset(obj, 0, sizeof(cyhal_comp_t));
207     obj->resource.type = CYHAL_RSC_INVALID;
208 
209     /* CTB configuration objects initialization */
210     _cyhal_comp_ctb_cfg_init();
211 
212     /* Validate pins. vin_p and vin_m are mandatory pins, vout is optional. */
213     if ((NC == vin_p) || (NC == vin_m))
214     {
215         result = CYHAL_COMP_RSLT_ERR_INVALID_PIN;
216     }
217 
218     /* Allocate resources */
219     if(CY_RSLT_SUCCESS == result)
220     {
221         result = _cyhal_opamp_init_common(&(obj->resource), CYHAL_COMP_RSLT_ERR_INVALID_PIN, vin_p, vin_m, NC /* vout unused by comparator */, output);
222     }
223 
224     /* Configure the opamp */
225     if (result == CY_RSLT_SUCCESS)
226     {
227         obj->pin_vin_p = vin_p;
228         obj->pin_vin_m = vin_m;
229         obj->pin_out = output;
230         cy_stc_ctb_opamp_config_t config = _cyhal_comp_ctb_default_config;
231 #if defined(CY_IP_MXS40PASS_CTB_INSTANCES)
232         config.oaPower = (cy_en_ctb_power_t)_cyhal_opamp_convert_power(cfg->power);
233         config.oaCompHyst = _CYHAL_COMP_CTB_HIST(cfg->hysteresis);
234 #else
235         config.power = (cy_en_ctb_power_t)_cyhal_opamp_convert_power(cfg->power);
236         config.compHyst = _CYHAL_COMP_CTB_HIST(cfg->hysteresis);
237 #endif
238 
239         result = _cyhal_comp_ctb_init_hw(obj, &config);
240     }
241 
242     if (result == CY_RSLT_SUCCESS)
243     {
244         /* OPAMP Routing. Close input switches for OA0 or OA1. */
245         Cy_CTB_SetAnalogSwitch(obj->base_ctb, _cyhal_opamp_convert_switch(obj->resource.channel_num), _cyhal_opamp_pin_to_mask(obj->resource.channel_num, vin_p, vin_m, NC), _CYHAL_CTB_SW_CLOSE);
246         _cyhal_opamp_set_isolation_switch(obj->resource.channel_num, obj->base_ctb, true);
247     }
248 
249     /* Free OPAMP in case of failure */
250     if (result != CY_RSLT_SUCCESS)
251     {
252         _cyhal_comp_ctb_free(obj);
253     }
254     return result;
255 }
256 
_cyhal_comp_ctb_init_cfg(cyhal_comp_t * obj,const cyhal_comp_configurator_t * cfg)257 cy_rslt_t _cyhal_comp_ctb_init_cfg(cyhal_comp_t *obj, const cyhal_comp_configurator_t *cfg)
258 {
259     /* CTB configuration objects initialization */
260     _cyhal_comp_ctb_cfg_init();
261 
262     cy_rslt_t result = _cyhal_comp_ctb_init_hw(obj, cfg->opamp);
263     if(CY_RSLT_SUCCESS != result)
264     {
265         _cyhal_comp_ctb_free(obj);
266     }
267 
268     return result;
269 }
270 
_cyhal_comp_ctb_free(cyhal_comp_t * obj)271 void _cyhal_comp_ctb_free(cyhal_comp_t *obj)
272 {
273     CY_ASSERT(NULL != obj);
274     CY_ASSERT(_cyhal_comp_ctb_arrays_initialized); /* Should not be freeing if we never initialized anything */
275 
276     if(CYHAL_RSC_INVALID != obj->resource.type)
277     {
278         if(false == obj->owned_by_configurator)
279         {
280             Cy_CTB_SetAnalogSwitch(obj->base_ctb, _cyhal_opamp_convert_switch(obj->resource.channel_num), _cyhal_opamp_pin_to_mask(obj->resource.channel_num, obj->pin_vin_p, obj->pin_vin_m, NC), _CYHAL_CTB_SW_OPEN);
281             _cyhal_opamp_set_isolation_switch(obj->resource.channel_num, obj->base_ctb, false);
282         }
283         cyhal_analog_ctb_free(obj->base_ctb);
284 
285         _cyhal_comp_ctb_config_structs[(obj->resource.block_num * _CYHAL_OPAMP_PER_CTB) + obj->resource.channel_num] = NULL;
286 
287         uint8_t ctb_num = obj->resource.block_num;
288         /* If neither opamp in this ctb is in use, disable the ISR */
289         if((NULL == _cyhal_comp_ctb_config_structs[ctb_num * _CYHAL_OPAMP_PER_CTB])
290             && (NULL == _cyhal_comp_ctb_config_structs[(ctb_num * _CYHAL_OPAMP_PER_CTB) + 1]))
291         {
292             _cyhal_irq_free(_cyhal_ctb_irq_n[obj->resource.block_num]);
293         }
294 
295         if(false == obj->owned_by_configurator)
296         {
297             cyhal_hwmgr_free(&(obj->resource));
298         }
299         obj->base_ctb = NULL;
300         obj->resource.type = CYHAL_RSC_INVALID;
301     }
302 
303     _cyhal_utils_release_if_used(&(obj->pin_vin_p));
304     _cyhal_utils_release_if_used(&(obj->pin_vin_m));
305     _cyhal_utils_release_if_used(&(obj->pin_out));
306 }
307 
_cyhal_comp_ctb_set_power(cyhal_comp_t * obj,cyhal_power_level_t power)308 cy_rslt_t _cyhal_comp_ctb_set_power(cyhal_comp_t *obj, cyhal_power_level_t power)
309 {
310     CY_ASSERT(NULL != obj);
311 
312     cy_en_ctb_power_t power_level = (cy_en_ctb_power_t)_cyhal_opamp_convert_power(power);
313     Cy_CTB_SetPower(obj->base_ctb, _cyhal_opamp_convert_sel(obj->resource.channel_num), power_level, _CYHAL_CTB_PUMP_ENABLE);
314     return CY_RSLT_SUCCESS;
315 }
316 
_cyhal_comp_ctb_configure(cyhal_comp_t * obj,cyhal_comp_config_t * cfg)317 cy_rslt_t _cyhal_comp_ctb_configure(cyhal_comp_t *obj, cyhal_comp_config_t *cfg)
318 {
319     CY_ASSERT(NULL != obj);
320 
321 #if defined(CY_IP_MXS40PASS_CTB_INSTANCES)
322     cy_rslt_t result = _cyhal_comp_ctb_set_power(obj, cfg->power);
323     if(CY_RSLT_SUCCESS == result)
324     {
325         Cy_CTB_CompSetConfig(obj->base_ctb, _cyhal_opamp_convert_sel(obj->resource.channel_num),
326             CY_CTB_COMP_DSI_TRIGGER_OUT_LEVEL, _CYHAL_COMP_CTB_DEFAULT_BYPASS, _CYHAL_COMP_CTB_HIST(cfg->hysteresis));
327     }
328 #else
329     cy_stc_ctb_opamp_config_t config = _cyhal_comp_ctb_default_config;
330     config.power = (cy_en_ctb_power_t)_cyhal_opamp_convert_power(cfg->power);
331     config.compHyst = _CYHAL_COMP_CTB_HIST(cfg->hysteresis);
332     cy_rslt_t result = Cy_CTB_OpampInit(obj->base_ctb, _cyhal_opamp_convert_sel(obj->resource.channel_num), &config);
333 #endif
334 
335     return result;
336 }
337 
_cyhal_comp_ctb_read(cyhal_comp_t * obj)338 bool _cyhal_comp_ctb_read(cyhal_comp_t *obj)
339 {
340     CY_ASSERT(NULL != obj);
341 
342     return (1UL == Cy_CTB_CompGetStatus(obj->base_ctb, _cyhal_opamp_convert_sel(obj->resource.channel_num)));
343 }
344 
_cyhal_comp_ctb_enable_event(cyhal_comp_t * obj,cyhal_comp_event_t event,uint8_t intr_priority,bool enable)345 void _cyhal_comp_ctb_enable_event(cyhal_comp_t *obj, cyhal_comp_event_t event, uint8_t intr_priority, bool enable)
346 {
347     CY_UNUSED_PARAMETER(intr_priority);
348     cyhal_comp_event_t enabled_events = _cyhal_comp_ctb_get_enabled_events(obj);
349     if(enable)
350     {
351         enabled_events |= event;
352     }
353     else
354     {
355         enabled_events &= (~event);
356     }
357 
358     _cyhal_system_irq_t irqn = _cyhal_ctb_irq_n[obj->resource.block_num];
359     _cyhal_irq_set_priority(irqn, intr_priority);
360 
361     cy_en_ctb_comp_edge_t pdl_event = _cyhal_comp_ctb_convert_hal_event(enabled_events);
362     Cy_CTB_CompSetInterruptEdgeType(obj->base_ctb, _cyhal_opamp_convert_sel(obj->resource.channel_num), pdl_event);
363 }
364 
365 #if defined(__cplusplus)
366 }
367 #endif
368 
369 #endif /* _CYHAL_DRIVER_AVAILABLE_COMP_CTB */
370