1 /*******************************************************************************
2 * File Name: cyhal_ezi2c.c
3 *
4 * Description:
5 * Provides a high level interface for interacting with the Infineon I2C. This is
6 * a wrapper around the lower level PDL API.
7 *
8 ********************************************************************************
9 * \copyright
10 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
11 * an affiliate of Cypress Semiconductor Corporation
12 *
13 * SPDX-License-Identifier: Apache-2.0
14 *
15 * Licensed under the Apache License, Version 2.0 (the "License");
16 * you may not use this file except in compliance with the License.
17 * You may obtain a copy of the License at
18 *
19 *     http://www.apache.org/licenses/LICENSE-2.0
20 *
21 * Unless required by applicable law or agreed to in writing, software
22 * distributed under the License is distributed on an "AS IS" BASIS,
23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 * See the License for the specific language governing permissions and
25 * limitations under the License.
26 *******************************************************************************/
27 
28 #include <stdlib.h>
29 #include <string.h>
30 #include "cyhal_ezi2c.h"
31 #include "cyhal_scb_common.h"
32 #include "cyhal_gpio.h"
33 #include "cyhal_system_impl.h"
34 #include "cyhal_hwmgr.h"
35 #include "cyhal_utils.h"
36 #include "cyhal_irq_impl.h"
37 #include "cyhal_clock.h"
38 
39 #if (CYHAL_DRIVER_AVAILABLE_EZI2C)
40 
41 #if defined(__cplusplus)
42 extern "C"
43 {
44 #endif
45 
_cyhal_ezi2c_convert_activity_status(uint32_t pdl_status)46 static inline cyhal_ezi2c_status_t _cyhal_ezi2c_convert_activity_status(uint32_t pdl_status)
47 {
48     /* Structure to map EZI2C (PDL) status on HAL EZI2C status */
49     static const uint32_t status_map[] =
50     {
51         0U,                                  // Default value
52         (uint32_t)CYHAL_EZI2C_STATUS_READ1,  // CY_SCB_EZI2C_STATUS_READ1
53         (uint32_t)CYHAL_EZI2C_STATUS_WRITE1, // CY_SCB_EZI2C_STATUS_WRITE1
54         (uint32_t)CYHAL_EZI2C_STATUS_READ2,  // CY_SCB_EZI2C_STATUS_READ2
55         (uint32_t)CYHAL_EZI2C_STATUS_WRITE2, // CY_SCB_EZI2C_STATUS_WRITE2
56         (uint32_t)CYHAL_EZI2C_STATUS_BUSY,   // CY_SCB_EZI2C_STATUS_BUSY
57         (uint32_t)CYHAL_EZI2C_STATUS_ERR,    // CY_SCB_EZI2C_STATUS_ERR
58     };
59 
60     cyhal_ezi2c_status_t hal_status = (cyhal_ezi2c_status_t)_cyhal_utils_convert_flags(
61         status_map, sizeof(status_map) / sizeof(uint32_t), pdl_status);
62     if ((hal_status & (CYHAL_EZI2C_STATUS_BUSY | CYHAL_EZI2C_STATUS_ERR)) == 0)
63     {
64         hal_status |= CYHAL_EZI2C_STATUS_OK;
65     }
66     return hal_status;
67 }
68 
69 #if defined (COMPONENT_CAT5)
_cyhal_ezi2c_irq_handler(_cyhal_system_irq_t irqn)70 static void _cyhal_ezi2c_irq_handler(_cyhal_system_irq_t irqn)
71 #else
72 static void _cyhal_ezi2c_irq_handler(void)
73 #endif
74 {
75 #if defined (COMPONENT_CAT5)
76     cyhal_ezi2c_t *obj = (cyhal_ezi2c_t*) _cyhal_scb_get_irq_obj(irqn);
77 #else
78     cyhal_ezi2c_t *obj = (cyhal_ezi2c_t*) _cyhal_scb_get_irq_obj();
79 #endif
80     Cy_SCB_EZI2C_Interrupt(obj->base, &(obj->context));
81 
82     /* Check if callback is registered */
83     cyhal_ezi2c_event_callback_t callback = (cyhal_ezi2c_event_callback_t) obj->callback_data.callback;
84     if (callback != NULL)
85     {
86         /* Check status of EZI2C and verify which events are enabled */
87         cyhal_ezi2c_status_t status = cyhal_ezi2c_get_activity_status(obj);
88         if(status & obj->irq_cause)
89         {
90             (void) (callback) (obj->callback_data.callback_arg, (cyhal_ezi2c_status_t)(status & obj->irq_cause));
91         }
92     }
93 }
94 
95 #if defined (COMPONENT_CAT5)
_cyhal_ezi2c0_irq_handler(void)96 static void _cyhal_ezi2c0_irq_handler(void)
97 {
98     _cyhal_ezi2c_irq_handler(scb_0_interrupt_IRQn);
99 }
100 
_cyhal_ezi2c1_irq_handler(void)101 static void _cyhal_ezi2c1_irq_handler(void)
102 {
103     _cyhal_ezi2c_irq_handler(scb_1_interrupt_IRQn);
104 }
105 
_cyhal_ezi2c2_irq_handler(void)106 static void _cyhal_ezi2c2_irq_handler(void)
107 {
108     _cyhal_ezi2c_irq_handler(scb_2_interrupt_IRQn);
109 }
110 
111 static CY_SCB_IRQ_THREAD_CB_t _cyhal_irq_cb[3] = {_cyhal_ezi2c0_irq_handler, _cyhal_ezi2c1_irq_handler, _cyhal_ezi2c2_irq_handler};
112 #endif
113 
_cyhal_ezi2c_pm_callback_instance(void * obj_ptr,cyhal_syspm_callback_state_t state,cy_en_syspm_callback_mode_t pdl_mode)114 static bool _cyhal_ezi2c_pm_callback_instance(void *obj_ptr, cyhal_syspm_callback_state_t state, cy_en_syspm_callback_mode_t pdl_mode)
115 {
116     cyhal_ezi2c_t *obj = (cyhal_ezi2c_t*)(obj_ptr);
117 
118     cy_stc_syspm_callback_params_t ezi2c_callback_params = {
119         .base = (void *) (obj->base),
120         .context = (void *) &(obj->context)
121     };
122 
123     bool allow = true;
124     switch(state)
125     {
126         case CYHAL_SYSPM_CB_CPU_DEEPSLEEP:
127         case CYHAL_SYSPM_CB_CPU_DEEPSLEEP_RAM:
128             allow = (CY_SYSPM_SUCCESS == Cy_SCB_EZI2C_DeepSleepCallback(&ezi2c_callback_params, pdl_mode));
129             break;
130 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
131         case CYHAL_SYSPM_CB_SYSTEM_HIBERNATE:
132             allow = (CY_SYSPM_SUCCESS == Cy_SCB_EZI2C_HibernateCallback(&ezi2c_callback_params, pdl_mode));
133             break;
134 #endif
135         default:
136             CY_ASSERT(false);
137             break;
138     }
139     return allow;
140 }
141 
_cyhal_ezi2c_setup_resources(cyhal_ezi2c_t * obj,cyhal_gpio_t sda,cyhal_gpio_t scl,const cyhal_clock_t * clk)142 static cy_rslt_t _cyhal_ezi2c_setup_resources(cyhal_ezi2c_t *obj, cyhal_gpio_t sda, cyhal_gpio_t scl, const cyhal_clock_t *clk)
143 {
144     /* Explicitly marked not allocated resources as invalid to prevent freeing them. */
145     obj->resource.type = CYHAL_RSC_INVALID;
146     obj->pin_scl = CYHAL_NC_PIN_VALUE;
147     obj->pin_sda = CYHAL_NC_PIN_VALUE;
148     obj->is_clock_owned = false;
149     obj->two_addresses = false;
150 
151     // pins_blocks will contain bit representation of blocks, that are connected to specified pin
152     // 1 block - 1 bit, so, for example, pin_blocks = 0x00000006 means that certain pin
153     // can belong to next non-reserved blocks SCB2 and SCB1
154     uint32_t pins_blocks = _CYHAL_SCB_AVAILABLE_BLOCKS_MASK;
155     if (NC != sda)
156     {
157         pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(sda, cyhal_pin_map_scb_i2c_sda);
158     }
159     if (NC != scl)
160     {
161         pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(scl, cyhal_pin_map_scb_i2c_scl);
162     }
163 
164     // One (or more) pin does not belong to any SCB instance or all corresponding SCB instances
165     // are reserved
166     if (0 == pins_blocks)
167     {
168         return CYHAL_EZI2C_RSLT_ERR_INVALID_PIN;
169     }
170 
171     uint8_t found_block_idx = 0;
172     while(((pins_blocks >> found_block_idx) & 0x1) == 0)
173     {
174         found_block_idx++;
175     }
176 
177     cyhal_resource_inst_t i2c_rsc = { CYHAL_RSC_SCB, found_block_idx, 0 };
178 
179     /* Reserve the I2C */
180     const cyhal_resource_pin_mapping_t *sda_map = _CYHAL_SCB_FIND_MAP_BLOCK(sda, cyhal_pin_map_scb_i2c_sda, &i2c_rsc);
181     const cyhal_resource_pin_mapping_t *scl_map = _CYHAL_SCB_FIND_MAP_BLOCK(scl, cyhal_pin_map_scb_i2c_scl, &i2c_rsc);
182     if ((NULL == sda_map) || (NULL == scl_map) || !_cyhal_utils_map_resources_equal(sda_map, scl_map))
183     {
184         return CYHAL_EZI2C_RSLT_ERR_INVALID_PIN;
185     }
186 
187     cyhal_resource_inst_t rsc_to_reserve = { CYHAL_RSC_SCB, _cyhal_scb_get_block_index(found_block_idx), 0 };
188     cy_rslt_t result = cyhal_hwmgr_reserve(&rsc_to_reserve);
189 
190     /* Reserve the SDA pin */
191     if (result == CY_RSLT_SUCCESS)
192     {
193         obj->resource = i2c_rsc;
194 
195         result = _cyhal_utils_reserve_and_connect(sda_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_I2C_SDA);
196         if (result == CY_RSLT_SUCCESS)
197             obj->pin_sda = sda;
198     }
199 
200     /* Reserve the SCL pin */
201     if (result == CY_RSLT_SUCCESS)
202     {
203         result = _cyhal_utils_reserve_and_connect(scl_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_I2C_SCL);
204         if (result == CY_RSLT_SUCCESS)
205             obj->pin_scl = scl;
206     }
207 
208     if (result == CY_RSLT_SUCCESS)
209     {
210         if (clk == NULL)
211         {
212             result = _cyhal_utils_allocate_clock(&(obj->clock), &i2c_rsc, CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
213             obj->is_clock_owned = (CY_RSLT_SUCCESS == result);
214         }
215         else
216         {
217             obj->clock = *clk;
218         }
219     }
220 
221     return result;
222 }
223 
_cyhal_ezi2c_init_hw(cyhal_ezi2c_t * obj,const cy_stc_scb_ezi2c_config_t * pdl_cfg)224 static cy_rslt_t _cyhal_ezi2c_init_hw(cyhal_ezi2c_t *obj, const cy_stc_scb_ezi2c_config_t* pdl_cfg)
225 {
226     uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
227     obj->base = _CYHAL_SCB_BASE_ADDRESSES[scb_arr_index];
228     obj->two_addresses = (pdl_cfg->numberOfAddresses == CY_SCB_EZI2C_TWO_ADDRESSES);
229 
230     /* Configure I2C to operate */
231     cy_rslt_t result = Cy_SCB_EZI2C_Init(obj->base, pdl_cfg, &(obj->context));
232 
233     if (CY_RSLT_SUCCESS == result)
234     {
235         _cyhal_scb_update_instance_data(obj->resource.block_num, (void*)obj, &_cyhal_ezi2c_pm_callback_instance);
236         obj->callback_data.callback = NULL;
237         obj->callback_data.callback_arg = NULL;
238         obj->irq_cause = 0;
239 
240         #if defined (COMPONENT_CAT5)
241             Cy_SCB_RegisterInterruptCallback(obj->base, _cyhal_irq_cb[_CYHAL_SCB_IRQ_N[scb_arr_index]]);
242         #endif
243 
244         _cyhal_irq_register(_CYHAL_SCB_IRQ_N[scb_arr_index], CYHAL_ISR_PRIORITY_DEFAULT, (cy_israddress)_cyhal_ezi2c_irq_handler);
245     }
246 
247     return result;
248 }
249 
_cyhal_ezi2c_setup_and_enable(cyhal_ezi2c_t * obj,const cyhal_ezi2c_slave_cfg_t * slave1_cfg,const cyhal_ezi2c_slave_cfg_t * slave2_cfg)250 static void _cyhal_ezi2c_setup_and_enable(cyhal_ezi2c_t *obj, const cyhal_ezi2c_slave_cfg_t *slave1_cfg, const cyhal_ezi2c_slave_cfg_t *slave2_cfg)
251 {
252     CY_ASSERT(NULL != obj);
253     CY_ASSERT(NULL != slave1_cfg);
254 
255     uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
256 
257     /* Configure buffer for communication with master */
258     Cy_SCB_EZI2C_SetBuffer1(obj->base, slave1_cfg->buf, slave1_cfg->buf_size, slave1_cfg->buf_rw_boundary, &(obj->context));
259     /* Check if user set one or two addresses */
260     if (obj->two_addresses && (NULL != slave2_cfg))
261     {
262         Cy_SCB_EZI2C_SetBuffer2(obj->base, slave2_cfg->buf, slave2_cfg->buf_size, slave2_cfg->buf_rw_boundary, &(obj->context));
263     }
264 
265     #if defined (COMPONENT_CAT5)
266         Cy_SCB_EnableInterrupt(obj->base);
267     #endif
268 
269     _cyhal_irq_enable(_CYHAL_SCB_IRQ_N[scb_arr_index]);
270     /* Enable EZI2C to operate */
271     Cy_SCB_EZI2C_Enable(obj->base);
272 }
273 
cyhal_ezi2c_init(cyhal_ezi2c_t * obj,cyhal_gpio_t sda,cyhal_gpio_t scl,const cyhal_clock_t * clk,const cyhal_ezi2c_cfg_t * cfg)274 cy_rslt_t cyhal_ezi2c_init(cyhal_ezi2c_t *obj, cyhal_gpio_t sda, cyhal_gpio_t scl, const cyhal_clock_t *clk, const cyhal_ezi2c_cfg_t *cfg)
275 {
276     CY_ASSERT(NULL != obj);
277     memset(obj, 0, sizeof(cyhal_ezi2c_t));
278 
279     /* Validate input configuration structure */
280     if ((0 == cfg->slave1_cfg.slave_address) || ((cfg->two_addresses) && (0 == cfg->slave2_cfg.slave_address)))
281     {
282         return CYHAL_EZI2C_RSLT_ERR_CHECK_USER_CONFIG;
283     }
284 
285     obj->dc_configured = false;
286     /* Populate configuration structure */
287     const cy_stc_scb_ezi2c_config_t pdl_config =
288     {
289         .numberOfAddresses   = cfg->two_addresses ? CY_SCB_EZI2C_TWO_ADDRESSES : CY_SCB_EZI2C_ONE_ADDRESS,
290         .slaveAddress1       = cfg->slave1_cfg.slave_address,
291         .slaveAddress2       = cfg->two_addresses ? cfg->slave2_cfg.slave_address : 0,
292         .subAddressSize      = (cy_en_scb_ezi2c_sub_addr_size_t)cfg->sub_address_size,
293         .enableWakeFromSleep = cfg->enable_wake_from_sleep,
294     };
295 
296     cy_rslt_t result = _cyhal_ezi2c_setup_resources(obj, sda, scl, clk);
297 
298     if (CY_RSLT_SUCCESS == result)
299     {
300         result = _cyhal_ezi2c_init_hw(obj, &pdl_config);
301     }
302 
303     if (CY_RSLT_SUCCESS == result)
304     {
305         uint32_t dataRate = _cyhal_i2c_set_peri_divider((void *)obj, false, (uint32_t)cfg->data_rate, true);
306         if (dataRate == 0)
307         {
308             /* Can not reach desired data rate */
309             return CYHAL_EZI2C_RSLT_ERR_CAN_NOT_REACH_DR;
310         }
311     }
312 
313     if (result == CY_RSLT_SUCCESS)
314     {
315         _cyhal_ezi2c_setup_and_enable(obj, &cfg->slave1_cfg, &cfg->slave2_cfg);
316     }
317 
318     if (result != CY_RSLT_SUCCESS)
319     {
320         cyhal_ezi2c_free(obj);
321     }
322     return result;
323 }
324 
cyhal_ezi2c_free(cyhal_ezi2c_t * obj)325 void cyhal_ezi2c_free(cyhal_ezi2c_t *obj)
326 {
327     CY_ASSERT(NULL != obj);
328 
329     if (NULL != obj->base)
330     {
331         Cy_SCB_EZI2C_Disable(obj->base, &obj->context);
332         Cy_SCB_EZI2C_DeInit(obj->base);
333         obj->base = NULL;
334     }
335 
336     if (CYHAL_RSC_INVALID != obj->resource.type)
337     {
338         uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
339         _cyhal_scb_update_instance_data(obj->resource.block_num, NULL, NULL);
340         _cyhal_system_irq_t irqn = _CYHAL_SCB_IRQ_N[scb_arr_index];
341         _cyhal_irq_free(irqn);
342 
343         if (!obj->dc_configured)
344         {
345             cyhal_resource_inst_t rsc_to_free = { CYHAL_RSC_SCB, _cyhal_scb_get_block_index(obj->resource.block_num), obj->resource.channel_num };
346             cyhal_hwmgr_free(&(rsc_to_free));
347         }
348         obj->resource.type = CYHAL_RSC_INVALID;
349     }
350 
351     if (!obj->dc_configured)
352     {
353         _cyhal_utils_release_if_used(&(obj->pin_sda));
354         _cyhal_utils_release_if_used(&(obj->pin_scl));
355 
356         if (obj->is_clock_owned)
357         {
358             cyhal_clock_free(&(obj->clock));
359         }
360     }
361 }
362 
cyhal_ezi2c_get_activity_status(cyhal_ezi2c_t * obj)363 cyhal_ezi2c_status_t cyhal_ezi2c_get_activity_status(cyhal_ezi2c_t *obj)
364 {
365     return _cyhal_ezi2c_convert_activity_status(Cy_SCB_EZI2C_GetActivity(obj->base, &(obj->context)));
366 }
367 
cyhal_ezi2c_register_callback(cyhal_ezi2c_t * obj,cyhal_ezi2c_event_callback_t callback,void * callback_arg)368 void cyhal_ezi2c_register_callback(cyhal_ezi2c_t *obj, cyhal_ezi2c_event_callback_t callback, void *callback_arg)
369 {
370     uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
371     obj->callback_data.callback = (cy_israddress) callback;
372     obj->callback_data.callback_arg = callback_arg;
373     cyhal_system_critical_section_exit(savedIntrStatus);
374 }
375 
cyhal_ezi2c_enable_event(cyhal_ezi2c_t * obj,cyhal_ezi2c_status_t event,uint8_t intr_priority,bool enable)376 void cyhal_ezi2c_enable_event(cyhal_ezi2c_t *obj, cyhal_ezi2c_status_t event, uint8_t intr_priority, bool enable)
377 {
378     uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
379     if (enable)
380     {
381         obj->irq_cause |= event;
382     }
383     else
384     {
385         obj->irq_cause &= ~event;
386     }
387 
388     _cyhal_system_irq_t irqn = _CYHAL_SCB_IRQ_N[scb_arr_index];
389     _cyhal_irq_set_priority(irqn, intr_priority);
390 }
391 
cyhal_ezi2c_init_cfg(cyhal_ezi2c_t * obj,const cyhal_ezi2c_configurator_t * cfg,const cyhal_ezi2c_slave_cfg_t * slave1_cfg,const cyhal_ezi2c_slave_cfg_t * slave2_cfg)392 cy_rslt_t cyhal_ezi2c_init_cfg(cyhal_ezi2c_t *obj, const cyhal_ezi2c_configurator_t *cfg,
393                                const cyhal_ezi2c_slave_cfg_t *slave1_cfg, const cyhal_ezi2c_slave_cfg_t *slave2_cfg)
394 {
395     CY_ASSERT(NULL != obj);
396     CY_ASSERT(NULL != cfg);
397     CY_ASSERT(NULL != cfg->config);
398 
399     obj->resource = *cfg->resource;
400     obj->clock = *cfg->clock;
401     obj->is_clock_owned = false;
402     obj->two_addresses = false;
403     obj->dc_configured = true;
404 
405     cy_rslt_t result = _cyhal_ezi2c_init_hw(obj, cfg->config);
406 
407     if (CY_RSLT_SUCCESS == result)
408     {
409         _cyhal_ezi2c_setup_and_enable(obj, slave1_cfg, slave2_cfg);
410     }
411 
412     return result;
413 }
414 
415 #if defined(__cplusplus)
416 }
417 #endif
418 
419 #endif /* CYHAL_DRIVER_AVAILABLE_EZI2C */
420