1 /*
2  * Copyright (c) 2024, Croxel Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/i2c.h>
9 #include <zephyr/drivers/pinctrl.h>
10 #include <nrfx_twi.h>
11 #include "i2c_nrfx_twi_common.h"
12 
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_DECLARE(i2c_nrfx_twi);
15 
i2c_nrfx_twi_init(const struct device * dev)16 int i2c_nrfx_twi_init(const struct device *dev)
17 {
18 	const struct i2c_nrfx_twi_config *config = dev->config;
19 	nrfx_err_t result = nrfx_twi_init(&config->twi, &config->config,
20 					  config->event_handler, (void *)dev);
21 	if (result != NRFX_SUCCESS) {
22 		LOG_ERR("Failed to initialize device: %s",
23 			    dev->name);
24 		return -EBUSY;
25 	}
26 
27 	return 0;
28 }
29 
i2c_nrfx_twi_configure(const struct device * dev,uint32_t dev_config)30 int i2c_nrfx_twi_configure(const struct device *dev, uint32_t dev_config)
31 {
32 	const struct i2c_nrfx_twi_config *config = dev->config;
33 	struct i2c_nrfx_twi_common_data *data = dev->data;
34 	nrfx_twi_t const *inst = &config->twi;
35 
36 	if (I2C_ADDR_10_BITS & dev_config) {
37 		return -EINVAL;
38 	}
39 
40 	switch (I2C_SPEED_GET(dev_config)) {
41 	case I2C_SPEED_STANDARD:
42 		nrf_twi_frequency_set(inst->p_twi, NRF_TWI_FREQ_100K);
43 		break;
44 	case I2C_SPEED_FAST:
45 		nrf_twi_frequency_set(inst->p_twi, NRF_TWI_FREQ_400K);
46 		break;
47 	default:
48 		LOG_ERR("unsupported speed");
49 		return -EINVAL;
50 	}
51 	data->dev_config = dev_config;
52 
53 	return 0;
54 }
55 
i2c_nrfx_twi_recover_bus(const struct device * dev)56 int i2c_nrfx_twi_recover_bus(const struct device *dev)
57 {
58 	const struct i2c_nrfx_twi_config *config = dev->config;
59 	uint32_t scl_pin;
60 	uint32_t sda_pin;
61 	nrfx_err_t err;
62 
63 	scl_pin = nrf_twi_scl_pin_get(config->twi.p_twi);
64 	sda_pin = nrf_twi_sda_pin_get(config->twi.p_twi);
65 
66 	err = nrfx_twi_bus_recover(scl_pin, sda_pin);
67 	return (err == NRFX_SUCCESS ? 0 : -EBUSY);
68 }
69 
i2c_nrfx_twi_msg_transfer(const struct device * dev,uint8_t flags,uint8_t * buf,size_t buf_len,uint16_t i2c_addr,bool more_msgs)70 int i2c_nrfx_twi_msg_transfer(const struct device *dev, uint8_t flags,
71 			      uint8_t *buf, size_t buf_len,
72 			      uint16_t i2c_addr, bool more_msgs)
73 {
74 	const struct i2c_nrfx_twi_config *config = dev->config;
75 	int ret = 0;
76 	uint32_t xfer_flags = 0;
77 	nrfx_err_t res;
78 	nrfx_twi_xfer_desc_t cur_xfer = {
79 		.p_primary_buf = buf,
80 		.primary_length = buf_len,
81 		.address = i2c_addr,
82 		.type = (flags & I2C_MSG_READ) ?
83 			 NRFX_TWI_XFER_RX : NRFX_TWI_XFER_TX,
84 	};
85 
86 	if (flags & I2C_MSG_ADDR_10_BITS) {
87 		LOG_ERR("10-bit I2C Addr devices not supported");
88 		ret = -ENOTSUP;
89 	} else if (!(flags & I2C_MSG_STOP)) {
90 		/* - if the transfer consists of more messages
91 		 *   and the I2C repeated START is not requested
92 		 *   to appear before the next message, suspend
93 		 *   the transfer after the current message,
94 		 *   so that it can be resumed with the next one,
95 		 *   resulting in the two messages merged into
96 		 *   a continuous transfer on the bus
97 		 */
98 		if (more_msgs) {
99 			xfer_flags |= NRFX_TWI_FLAG_SUSPEND;
100 		/* - otherwise, just finish the transfer without
101 		 *   generating the STOP condition, unless the current
102 		 *   message is an RX request, for which such feature
103 		 *   is not supported
104 		 */
105 		} else if (flags & I2C_MSG_READ) {
106 			ret = -ENOTSUP;
107 		} else {
108 			xfer_flags |= NRFX_TWI_FLAG_TX_NO_STOP;
109 		}
110 	}
111 
112 	if (!ret) {
113 		res = nrfx_twi_xfer(&config->twi, &cur_xfer, xfer_flags);
114 		switch (res) {
115 		case NRFX_SUCCESS:
116 			break;
117 		case NRFX_ERROR_BUSY:
118 			ret = -EBUSY;
119 			break;
120 		default:
121 			ret = -EIO;
122 			break;
123 		}
124 	}
125 
126 	return ret;
127 }
128 
129 #ifdef CONFIG_PM_DEVICE
twi_nrfx_pm_action(const struct device * dev,enum pm_device_action action)130 int twi_nrfx_pm_action(const struct device *dev, enum pm_device_action action)
131 {
132 	const struct i2c_nrfx_twi_config *config = dev->config;
133 	struct i2c_nrfx_twi_common_data *data = dev->data;
134 	int ret = 0;
135 
136 	switch (action) {
137 	case PM_DEVICE_ACTION_RESUME:
138 		ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
139 		if (ret < 0) {
140 			return ret;
141 		}
142 		i2c_nrfx_twi_init(dev);
143 		if (data->dev_config) {
144 			i2c_nrfx_twi_configure(dev, data->dev_config);
145 		}
146 		break;
147 
148 	case PM_DEVICE_ACTION_SUSPEND:
149 		nrfx_twi_uninit(&config->twi);
150 
151 		ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
152 		if (ret < 0) {
153 			return ret;
154 		}
155 		break;
156 
157 	default:
158 		ret = -ENOTSUP;
159 	}
160 
161 	return ret;
162 }
163 #endif /* CONFIG_PM_DEVICE */
164