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