1 /*
2 * Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or
3 * an affiliate of Cypress Semiconductor Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @brief I2C driver for Infineon CAT1 MCU family.
10 */
11
12 #define DT_DRV_COMPAT infineon_cat1_i2c
13
14 #include <zephyr/drivers/i2c.h>
15 #include <zephyr/drivers/pinctrl.h>
16 #include <cyhal_i2c.h>
17 #include <cyhal_utils_impl.h>
18 #include <cyhal_utils_impl.h>
19 #include <cyhal_scb_common.h>
20
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(i2c_infineon_cat1, CONFIG_I2C_LOG_LEVEL);
23
24 #define I2C_CAT1_EVENTS_MASK (CYHAL_I2C_MASTER_WR_CMPLT_EVENT | CYHAL_I2C_MASTER_RD_CMPLT_EVENT | \
25 CYHAL_I2C_MASTER_ERR_EVENT)
26
27 #define I2C_CAT1_SLAVE_EVENTS_MASK \
28 (CYHAL_I2C_SLAVE_READ_EVENT | CYHAL_I2C_SLAVE_WRITE_EVENT | \
29 CYHAL_I2C_SLAVE_RD_BUF_EMPTY_EVENT | CYHAL_I2C_SLAVE_RD_CMPLT_EVENT | \
30 CYHAL_I2C_SLAVE_WR_CMPLT_EVENT | CYHAL_I2C_SLAVE_RD_BUF_EMPTY_EVENT | \
31 CYHAL_I2C_SLAVE_ERR_EVENT)
32
33 /* States for ASYNC operations */
34 #define CAT1_I2C_PENDING_NONE (0U)
35 #define CAT1_I2C_PENDING_RX (1U)
36 #define CAT1_I2C_PENDING_TX (2U)
37 #define CAT1_I2C_PENDING_TX_RX (3U)
38
39 /* I2C speed */
40 #define CAT1_I2C_SPEED_STANDARD_HZ (100000UL)
41 #define CAT1_I2C_SPEED_FAST_HZ (400000UL)
42 #define CAT1_I2C_SPEED_FAST_PLUS_HZ (1000000UL)
43
44 /* Data structure */
45 struct ifx_cat1_i2c_data {
46 cyhal_i2c_t obj;
47 cyhal_i2c_cfg_t cfg;
48 struct k_sem operation_sem;
49 struct k_sem transfer_sem;
50 uint32_t error_status;
51 uint32_t async_pending;
52 cyhal_resource_inst_t hw_resource;
53 cyhal_clock_t clock;
54 struct i2c_target_config *p_target_config;
55 uint8_t i2c_target_wr_byte;
56 uint8_t target_wr_buffer[CONFIG_I2C_INFINEON_CAT1_TARGET_BUF];
57 };
58
59 /* Device config structure */
60 struct ifx_cat1_i2c_config {
61 uint32_t master_frequency;
62 CySCB_Type *reg_addr;
63 const struct pinctrl_dev_config *pcfg;
64 uint8_t irq_priority;
65 };
66
67 /* Default SCB/I2C configuration structure */
68 static const cy_stc_scb_i2c_config_t _cyhal_i2c_default_config = {
69 .i2cMode = CY_SCB_I2C_MASTER,
70 .useRxFifo = false,
71 .useTxFifo = true,
72 .slaveAddress = 0U,
73 .slaveAddressMask = 0U,
74 .acceptAddrInFifo = false,
75 .ackGeneralAddr = false,
76 .enableWakeFromSleep = false,
77 .enableDigitalFilter = false,
78 .lowPhaseDutyCycle = 8U,
79 .highPhaseDutyCycle = 8U,
80 };
81
_get_hw_block_num(CySCB_Type * reg_addr)82 static int32_t _get_hw_block_num(CySCB_Type *reg_addr)
83 {
84 extern const uint8_t _CYHAL_SCB_BASE_ADDRESS_INDEX[_SCB_ARRAY_SIZE];
85 extern CySCB_Type *const _CYHAL_SCB_BASE_ADDRESSES[_SCB_ARRAY_SIZE];
86
87 uint32_t i;
88
89 for (i = 0u; i < _SCB_ARRAY_SIZE; i++) {
90 if (_CYHAL_SCB_BASE_ADDRESSES[i] == reg_addr) {
91 return _CYHAL_SCB_BASE_ADDRESS_INDEX[i];
92 }
93 }
94
95 return -ENOMEM;
96 }
97
98 #ifdef CONFIG_I2C_INFINEON_CAT1_ASYNC
ifx_master_event_handler(void * callback_arg,cyhal_i2c_event_t event)99 static void ifx_master_event_handler(void *callback_arg, cyhal_i2c_event_t event)
100 {
101 const struct device *dev = (const struct device *) callback_arg;
102 struct ifx_cat1_i2c_data *data = dev->data;
103
104 if (((CYHAL_I2C_MASTER_ERR_EVENT | CYHAL_I2C_SLAVE_ERR_EVENT) & event) != 0U) {
105 /* In case of error abort transfer */
106 (void)cyhal_i2c_abort_async(&data->obj);
107 data->error_status = 1;
108 }
109
110 /* Release semaphore if operation complete
111 * When we have pending TX, RX operations, the semaphore will be released
112 * after TX, RX complete.
113 */
114 if (((data->async_pending == CAT1_I2C_PENDING_TX_RX) &&
115 ((CYHAL_I2C_MASTER_RD_CMPLT_EVENT & event) != 0U)) ||
116 (data->async_pending != CAT1_I2C_PENDING_TX_RX)) {
117
118 /* Release semaphore (After I2C async transfer is complete) */
119 k_sem_give(&data->transfer_sem);
120 }
121
122 if (0 != (CYHAL_I2C_SLAVE_READ_EVENT & event)) {
123 if (data->p_target_config->callbacks->read_requested) {
124 data->p_target_config->callbacks->read_requested(data->p_target_config,
125 &data->i2c_target_wr_byte);
126 data->obj.context.slaveTxBufferIdx = 0;
127 data->obj.context.slaveTxBufferCnt = 0;
128 data->obj.context.slaveTxBufferSize = 1;
129 data->obj.context.slaveTxBuffer = &data->i2c_target_wr_byte;
130 }
131 }
132
133 if (0 != (CYHAL_I2C_SLAVE_RD_BUF_EMPTY_EVENT & event)) {
134 if (data->p_target_config->callbacks->read_processed) {
135 data->p_target_config->callbacks->read_processed(data->p_target_config,
136 &data->i2c_target_wr_byte);
137 data->obj.context.slaveTxBufferIdx = 0;
138 data->obj.context.slaveTxBufferCnt = 0;
139 data->obj.context.slaveTxBufferSize = 1;
140 data->obj.context.slaveTxBuffer = &data->i2c_target_wr_byte;
141 }
142 }
143
144 if (0 != (CYHAL_I2C_SLAVE_WRITE_EVENT & event)) {
145 cyhal_i2c_slave_config_write_buffer(&data->obj, data->target_wr_buffer,
146 CONFIG_I2C_INFINEON_CAT1_TARGET_BUF);
147 if (data->p_target_config->callbacks->write_requested) {
148 data->p_target_config->callbacks->write_requested(data->p_target_config);
149 }
150 }
151
152 if (0 != (CYHAL_I2C_SLAVE_WR_CMPLT_EVENT & event)) {
153 if (data->p_target_config->callbacks->write_received) {
154 for (int i = 0; i < data->obj.context.slaveRxBufferIdx; i++) {
155 data->p_target_config->callbacks->write_received(
156 data->p_target_config, data->target_wr_buffer[i]);
157 }
158 }
159 if (data->p_target_config->callbacks->stop) {
160 data->p_target_config->callbacks->stop(data->p_target_config);
161 }
162 }
163
164 if (0 != (CYHAL_I2C_SLAVE_RD_CMPLT_EVENT & event)) {
165 if (data->p_target_config->callbacks->stop) {
166 data->p_target_config->callbacks->stop(data->p_target_config);
167 }
168 }
169 }
170 #endif
171
ifx_cat1_i2c_configure(const struct device * dev,uint32_t dev_config)172 static int ifx_cat1_i2c_configure(const struct device *dev, uint32_t dev_config)
173 {
174 struct ifx_cat1_i2c_data *data = dev->data;
175 cy_rslt_t rslt;
176 int ret;
177
178 if (dev_config != 0) {
179 switch (I2C_SPEED_GET(dev_config)) {
180 case I2C_SPEED_STANDARD:
181 data->cfg.frequencyhal_hz = CAT1_I2C_SPEED_STANDARD_HZ;
182 break;
183 case I2C_SPEED_FAST:
184 data->cfg.frequencyhal_hz = CAT1_I2C_SPEED_FAST_HZ;
185 break;
186 case I2C_SPEED_FAST_PLUS:
187 data->cfg.frequencyhal_hz = CAT1_I2C_SPEED_FAST_PLUS_HZ;
188 break;
189 default:
190 LOG_ERR("Unsupported speed");
191 return -ERANGE;
192 }
193
194 /* This is deprecated and could be ignored in the future */
195 if (dev_config & I2C_ADDR_10_BITS) {
196 LOG_ERR("10-bit addressing mode is not supported");
197 return -EIO;
198 }
199 }
200
201 /* Acquire semaphore (block I2C operation for another thread) */
202 ret = k_sem_take(&data->operation_sem, K_FOREVER);
203 if (ret) {
204 return -EIO;
205 }
206
207 /* Configure the I2C resource to be master */
208 rslt = cyhal_i2c_configure(&data->obj, &data->cfg);
209 if (rslt != CY_RSLT_SUCCESS) {
210 LOG_ERR("cyhal_i2c_configure failed with err 0x%x", rslt);
211 k_sem_give(&data->operation_sem);
212 return -EIO;
213 }
214
215 #ifdef CONFIG_I2C_INFINEON_CAT1_ASYNC
216 /* Register an I2C event callback handler */
217 cyhal_i2c_register_callback(&data->obj, ifx_master_event_handler, (void *)dev);
218 #endif
219 /* Release semaphore */
220 k_sem_give(&data->operation_sem);
221 return 0;
222 }
223
ifx_cat1_i2c_get_config(const struct device * dev,uint32_t * dev_config)224 static int ifx_cat1_i2c_get_config(const struct device *dev, uint32_t *dev_config)
225 {
226 struct ifx_cat1_i2c_data *data = dev->data;
227 uint32_t config;
228
229 switch (data->cfg.frequencyhal_hz) {
230 case CAT1_I2C_SPEED_STANDARD_HZ:
231 config = I2C_SPEED_SET(I2C_SPEED_STANDARD);
232 break;
233 case CAT1_I2C_SPEED_FAST_HZ:
234 config = I2C_SPEED_SET(I2C_SPEED_FAST);
235 break;
236 case CAT1_I2C_SPEED_FAST_PLUS_HZ:
237 config = I2C_SPEED_SET(I2C_SPEED_FAST_PLUS);
238 break;
239 default:
240 LOG_ERR("Unsupported speed");
241 return -ERANGE;
242 }
243
244 /* Return current configuration */
245 *dev_config = config | I2C_MODE_CONTROLLER;
246
247 return 0;
248 }
249
ifx_cat1_i2c_msg_validate(struct i2c_msg * msg,uint8_t num_msgs)250 static int ifx_cat1_i2c_msg_validate(struct i2c_msg *msg, uint8_t num_msgs)
251 {
252 for (uint32_t i = 0u; i < num_msgs; i++) {
253 if ((I2C_MSG_ADDR_10_BITS & msg[i].flags) || (msg[i].buf == NULL)) {
254 return -EINVAL;
255 }
256 }
257 return 0;
258 }
259
ifx_cat1_i2c_transfer(const struct device * dev,struct i2c_msg * msg,uint8_t num_msgs,uint16_t addr)260 static int ifx_cat1_i2c_transfer(const struct device *dev, struct i2c_msg *msg, uint8_t num_msgs,
261 uint16_t addr)
262 {
263 struct ifx_cat1_i2c_data *data = dev->data;
264 cy_rslt_t rslt = CY_RSLT_SUCCESS;
265 int ret;
266
267 if (!num_msgs) {
268 return 0;
269 }
270
271 /* Acquire semaphore (block I2C transfer for another thread) */
272 ret = k_sem_take(&data->operation_sem, K_FOREVER);
273 if (ret) {
274 return -EIO;
275 }
276
277 /* This function checks if msg.buf is not NULL and if
278 * target address is not 10 bit.
279 */
280 if (ifx_cat1_i2c_msg_validate(msg, num_msgs) != 0) {
281 k_sem_give(&data->operation_sem);
282 return -EINVAL;
283 }
284
285 #ifdef CONFIG_I2C_INFINEON_CAT1_ASYNC
286 const struct ifx_cat1_i2c_config *const config = dev->config;
287
288 struct i2c_msg *tx_msg;
289 struct i2c_msg *rx_msg;
290
291 data->error_status = 0;
292 data->async_pending = CAT1_I2C_PENDING_NONE;
293
294 /* Enable I2C Interrupt */
295 cyhal_i2c_enable_event(&data->obj, (cyhal_i2c_event_t)I2C_CAT1_EVENTS_MASK,
296 config->irq_priority, true);
297
298 for (uint32_t i = 0u; i < num_msgs; i++) {
299 tx_msg = NULL;
300 rx_msg = NULL;
301
302 if ((msg[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
303 tx_msg = &msg[i];
304 data->async_pending = CAT1_I2C_PENDING_TX;
305 }
306
307 if ((msg[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
308 rx_msg = &msg[i];
309 data->async_pending = CAT1_I2C_PENDING_TX;
310 }
311
312 if ((tx_msg != NULL) && ((i + 1U) < num_msgs) &&
313 ((msg[i + 1U].flags & I2C_MSG_RW_MASK) == I2C_MSG_READ)) {
314 rx_msg = &msg[i + 1U];
315 i++;
316 data->async_pending = CAT1_I2C_PENDING_TX_RX;
317 }
318
319 /* Initiate master write and read transfer
320 * using tx_buff and rx_buff respectively
321 */
322 rslt = cyhal_i2c_master_transfer_async(&data->obj, addr,
323 (tx_msg == NULL) ? NULL : tx_msg->buf,
324 (tx_msg == NULL) ? 0u : tx_msg->len,
325 (rx_msg == NULL) ? NULL : rx_msg->buf,
326 (rx_msg == NULL) ? 0u : rx_msg->len);
327
328 if (rslt != CY_RSLT_SUCCESS) {
329 k_sem_give(&data->operation_sem);
330 return -EIO;
331 }
332
333 /* Acquire semaphore (block I2C async transfer for another thread) */
334 ret = k_sem_take(&data->transfer_sem, K_FOREVER);
335 if (ret) {
336 k_sem_give(&data->operation_sem);
337 return -EIO;
338 }
339
340 /* If error_status != 1 we have error during transfer async.
341 * error_status is handling in master_event_handler function.
342 */
343 if (data->error_status != 0) {
344 /* Release semaphore */
345 k_sem_give(&data->operation_sem);
346 return -EIO;
347 }
348 }
349
350 /* Disable I2C Interrupt */
351 cyhal_i2c_enable_event(&data->obj, (cyhal_i2c_event_t)
352 I2C_CAT1_EVENTS_MASK, config->irq_priority, false);
353 #else
354 for (uint32_t i = 0u; i < num_msgs; i++) {
355 bool stop_flag = ((msg[i].flags & I2C_MSG_STOP) != 0u) ? true : false;
356
357 if ((msg[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
358 rslt = cyhal_i2c_master_write(&data->obj,
359 addr, msg[i].buf, msg[i].len, 0, stop_flag);
360 }
361 if ((msg[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
362 rslt = cyhal_i2c_master_read(&data->obj,
363 addr, msg[i].buf, msg[i].len, 0, stop_flag);
364 }
365
366 if (rslt != CY_RSLT_SUCCESS) {
367 /* Release semaphore */
368 k_sem_give(&data->operation_sem);
369 return -EIO;
370 }
371 }
372 #endif
373 /* Release semaphore (After I2C transfer is complete) */
374 k_sem_give(&data->operation_sem);
375 return 0;
376 }
377
ifx_cat1_i2c_init(const struct device * dev)378 static int ifx_cat1_i2c_init(const struct device *dev)
379 {
380 struct ifx_cat1_i2c_data *data = dev->data;
381 const struct ifx_cat1_i2c_config *config = dev->config;
382 cy_rslt_t result;
383 int ret;
384
385 /* Configuration structure to initialisation I2C */
386 cyhal_i2c_configurator_t i2c_init_cfg = {
387 .resource = &data->hw_resource,
388 .config = &_cyhal_i2c_default_config,
389 .clock = &data->clock,
390 };
391
392 /* Dedicate SCB HW resource */
393 data->hw_resource.type = CYHAL_RSC_SCB;
394 data->hw_resource.block_num = _get_hw_block_num(config->reg_addr);
395
396
397 /* Configure semaphores */
398 ret = k_sem_init(&data->transfer_sem, 0, 1);
399 if (ret) {
400 return ret;
401 }
402
403 ret = k_sem_init(&data->operation_sem, 1, 1);
404 if (ret) {
405 return ret;
406 }
407
408 /* Configure dt provided device signals when available */
409 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
410 if (ret < 0) {
411 return ret;
412 }
413
414 /* Allocating clock for I2C driver */
415 result = _cyhal_utils_allocate_clock(&data->clock, &data->hw_resource,
416 CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
417 if (result != CY_RSLT_SUCCESS) {
418 return -ENOTSUP;
419 }
420
421 /* Assigns a programmable divider to a selected IP block */
422 en_clk_dst_t clk_idx = _cyhal_scb_get_clock_index(i2c_init_cfg.resource->block_num);
423
424 result = _cyhal_utils_peri_pclk_assign_divider(clk_idx, i2c_init_cfg.clock);
425 if (result != CY_RSLT_SUCCESS) {
426 return -ENOTSUP;
427 }
428
429 /* Initialize the I2C peripheral */
430 result = cyhal_i2c_init_cfg(&data->obj, &i2c_init_cfg);
431 if (result != CY_RSLT_SUCCESS) {
432 return -ENOTSUP;
433 }
434 data->obj.is_clock_owned = true;
435
436 /* Store Master initial configuration */
437 data->cfg.is_slave = false;
438 data->cfg.address = 0;
439 data->cfg.frequencyhal_hz = config->master_frequency;
440
441 if (ifx_cat1_i2c_configure(dev, 0) != 0) {
442 /* Free I2C resource */
443 cyhal_i2c_free(&data->obj);
444 }
445 return 0;
446 }
447
ifx_cat1_i2c_target_register(const struct device * dev,struct i2c_target_config * cfg)448 static int ifx_cat1_i2c_target_register(const struct device *dev, struct i2c_target_config *cfg)
449 {
450 struct ifx_cat1_i2c_data *data = (struct ifx_cat1_i2c_data *)dev->data;
451 const struct ifx_cat1_i2c_config *config = dev->config;
452
453 if (!cfg) {
454 return -EINVAL;
455 }
456
457 if (cfg->flags & I2C_TARGET_FLAGS_ADDR_10_BITS) {
458 return -ENOTSUP;
459 }
460
461 data->p_target_config = cfg;
462 data->cfg.is_slave = true;
463 data->cfg.address = data->p_target_config->address;
464 data->cfg.frequencyhal_hz = 100000;
465
466 if (ifx_cat1_i2c_configure(dev, I2C_SPEED_SET(I2C_SPEED_FAST)) != 0) {
467 /* Free I2C resource */
468 cyhal_i2c_free(&data->obj);
469 /* Release semaphore */
470 k_sem_give(&data->operation_sem);
471 return -EIO;
472 }
473
474 cyhal_i2c_enable_event(&data->obj, (cyhal_i2c_event_t)I2C_CAT1_SLAVE_EVENTS_MASK,
475 config->irq_priority, true);
476 return 0;
477 }
478
ifx_cat1_i2c_target_unregister(const struct device * dev,struct i2c_target_config * cfg)479 static int ifx_cat1_i2c_target_unregister(const struct device *dev, struct i2c_target_config *cfg)
480 {
481 struct ifx_cat1_i2c_data *data = (struct ifx_cat1_i2c_data *)dev->data;
482 const struct ifx_cat1_i2c_config *config = dev->config;
483
484 /* Acquire semaphore (block I2C operation for another thread) */
485 k_sem_take(&data->operation_sem, K_FOREVER);
486
487 cyhal_i2c_free(&data->obj);
488 data->p_target_config = NULL;
489 cyhal_i2c_enable_event(&data->obj, (cyhal_i2c_event_t)I2C_CAT1_SLAVE_EVENTS_MASK,
490 config->irq_priority, false);
491
492 /* Release semaphore */
493 k_sem_give(&data->operation_sem);
494 return 0;
495 }
496
497 /* I2C API structure */
498 static DEVICE_API(i2c, i2c_cat1_driver_api) = {
499 .configure = ifx_cat1_i2c_configure,
500 .transfer = ifx_cat1_i2c_transfer,
501 .get_config = ifx_cat1_i2c_get_config,
502 .target_register = ifx_cat1_i2c_target_register,
503 .target_unregister = ifx_cat1_i2c_target_unregister,
504 #ifdef CONFIG_I2C_RTIO
505 .iodev_submit = i2c_iodev_submit_fallback,
506 #endif
507 };
508
509 /* Macros for I2C instance declaration */
510 #define INFINEON_CAT1_I2C_INIT(n) \
511 PINCTRL_DT_INST_DEFINE(n); \
512 \
513 static struct ifx_cat1_i2c_data ifx_cat1_i2c_data##n; \
514 \
515 static const struct ifx_cat1_i2c_config i2c_cat1_cfg_##n = { \
516 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
517 .master_frequency = DT_INST_PROP_OR(n, clock_frequency, 100000), \
518 .reg_addr = (CySCB_Type *)DT_INST_REG_ADDR(n), \
519 .irq_priority = DT_INST_IRQ(n, priority), \
520 }; \
521 \
522 I2C_DEVICE_DT_INST_DEFINE(n, ifx_cat1_i2c_init, NULL, &ifx_cat1_i2c_data##n, \
523 &i2c_cat1_cfg_##n, POST_KERNEL, \
524 CONFIG_I2C_INIT_PRIORITY, &i2c_cat1_driver_api);
525
526 DT_INST_FOREACH_STATUS_OKAY(INFINEON_CAT1_I2C_INIT)
527