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