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