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