1 /*
2 * Copyright (c) 2018, Nordic Semiconductor ASA
3 * Copyright (c) 2024, Croxel Inc
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/drivers/i2c.h>
9 #include <zephyr/drivers/i2c/rtio.h>
10 #include <zephyr/dt-bindings/i2c/i2c.h>
11 #include <zephyr/pm/device.h>
12 #include <zephyr/drivers/pinctrl.h>
13 #include <soc.h>
14 #include <nrfx_twi.h>
15 #include "i2c_nrfx_twi_common.h"
16
17 #include <zephyr/logging/log.h>
18 #include <zephyr/irq.h>
19 LOG_MODULE_REGISTER(i2c_nrfx_twi, CONFIG_I2C_LOG_LEVEL);
20
21 struct i2c_nrfx_twi_rtio_data {
22 uint32_t dev_config;
23 bool twi_enabled;
24 struct i2c_rtio *ctx;
25 };
26
27 /* Enforce dev_config matches the same offset as the common structure,
28 * otherwise common API won't be compatible with i2c_nrfx_twi_rtio.
29 */
30 BUILD_ASSERT(
31 offsetof(struct i2c_nrfx_twi_rtio_data, dev_config) ==
32 offsetof(struct i2c_nrfx_twi_common_data, dev_config)
33 );
34
35 static void i2c_nrfx_twi_rtio_complete(const struct device *dev, int status);
36
i2c_nrfx_twi_rtio_msg_start(const struct device * dev,uint8_t flags,uint8_t * buf,size_t buf_len,uint16_t i2c_addr)37 static bool i2c_nrfx_twi_rtio_msg_start(const struct device *dev, uint8_t flags,
38 uint8_t *buf, size_t buf_len, uint16_t i2c_addr)
39 {
40 const struct i2c_nrfx_twi_config *config = dev->config;
41 struct i2c_nrfx_twi_rtio_data *const dev_data = dev->data;
42 struct i2c_rtio *ctx = dev_data->ctx;
43 int ret = 0;
44
45 /** Enabling while already enabled ends up in a failed assertion: skip it. */
46 if (!dev_data->twi_enabled) {
47 nrfx_twi_enable(&config->twi);
48 dev_data->twi_enabled = true;
49 }
50
51 ret = i2c_nrfx_twi_msg_transfer(dev, flags, buf, buf_len, i2c_addr, false);
52 if (ret != 0) {
53 nrfx_twi_disable(&config->twi);
54 dev_data->twi_enabled = false;
55
56 return i2c_rtio_complete(ctx, ret);
57 }
58
59 return false;
60 }
61
i2c_nrfx_twi_rtio_start(const struct device * dev)62 static bool i2c_nrfx_twi_rtio_start(const struct device *dev)
63 {
64 struct i2c_nrfx_twi_rtio_data *const dev_data = dev->data;
65 struct i2c_rtio *ctx = dev_data->ctx;
66 struct rtio_sqe *sqe = &ctx->txn_curr->sqe;
67 struct i2c_dt_spec *dt_spec = sqe->iodev->data;
68
69 switch (sqe->op) {
70 case RTIO_OP_RX:
71 return i2c_nrfx_twi_rtio_msg_start(dev, I2C_MSG_READ | sqe->iodev_flags,
72 sqe->rx.buf, sqe->rx.buf_len, dt_spec->addr);
73 case RTIO_OP_TINY_TX:
74 return i2c_nrfx_twi_rtio_msg_start(dev, I2C_MSG_WRITE | sqe->iodev_flags,
75 (uint8_t *)sqe->tiny_tx.buf,
76 sqe->tiny_tx.buf_len, dt_spec->addr);
77 case RTIO_OP_TX:
78 return i2c_nrfx_twi_rtio_msg_start(dev, I2C_MSG_WRITE | sqe->iodev_flags,
79 (uint8_t *)sqe->tx.buf,
80 sqe->tx.buf_len, dt_spec->addr);
81 case RTIO_OP_I2C_CONFIGURE:
82 (void)i2c_nrfx_twi_configure(dev, sqe->i2c_config);
83 return false;
84 case RTIO_OP_I2C_RECOVER:
85 (void)i2c_nrfx_twi_recover_bus(dev);
86 return false;
87 default:
88 LOG_ERR("Invalid op code %d for submission %p\n", sqe->op, (void *)sqe);
89 return i2c_rtio_complete(ctx, -EINVAL);
90 }
91 }
92
i2c_nrfx_twi_rtio_complete(const struct device * dev,int status)93 static void i2c_nrfx_twi_rtio_complete(const struct device *dev, int status)
94 {
95 /** Finalize if there are no more pending xfers */
96 const struct i2c_nrfx_twi_config *config = dev->config;
97 struct i2c_nrfx_twi_rtio_data *data = dev->data;
98 struct i2c_rtio *const ctx = data->ctx;
99
100 if (i2c_rtio_complete(ctx, status)) {
101 (void)i2c_nrfx_twi_rtio_start(dev);
102 } else {
103 nrfx_twi_disable(&config->twi);
104 data->twi_enabled = false;
105 }
106 }
107
i2c_nrfx_twi_rtio_configure(const struct device * dev,uint32_t i2c_config)108 static int i2c_nrfx_twi_rtio_configure(const struct device *dev, uint32_t i2c_config)
109 {
110 struct i2c_rtio *const ctx = ((struct i2c_nrfx_twi_rtio_data *)
111 dev->data)->ctx;
112
113 return i2c_rtio_configure(ctx, i2c_config);
114 }
115
i2c_nrfx_twi_rtio_transfer(const struct device * dev,struct i2c_msg * msgs,uint8_t num_msgs,uint16_t addr)116 static int i2c_nrfx_twi_rtio_transfer(const struct device *dev, struct i2c_msg *msgs,
117 uint8_t num_msgs, uint16_t addr)
118 {
119 struct i2c_rtio *const ctx = ((struct i2c_nrfx_twi_rtio_data *)
120 dev->data)->ctx;
121
122 return i2c_rtio_transfer(ctx, msgs, num_msgs, addr);
123 }
124
i2c_nrfx_twi_rtio_recover_bus(const struct device * dev)125 static int i2c_nrfx_twi_rtio_recover_bus(const struct device *dev)
126 {
127 struct i2c_rtio *const ctx = ((struct i2c_nrfx_twi_rtio_data *)
128 dev->data)->ctx;
129
130 return i2c_rtio_recover(ctx);
131 }
132
event_handler(nrfx_twi_evt_t const * p_event,void * p_context)133 static void event_handler(nrfx_twi_evt_t const *p_event, void *p_context)
134 {
135 const struct device *dev = p_context;
136 int status = 0;
137
138 if (i2c_nrfx_twi_get_evt_result(p_event) != NRFX_SUCCESS) {
139 status = -EIO;
140 }
141
142 i2c_nrfx_twi_rtio_complete(dev, status);
143 }
144
i2c_nrfx_twi_rtio_submit(const struct device * dev,struct rtio_iodev_sqe * iodev_seq)145 static void i2c_nrfx_twi_rtio_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_seq)
146 {
147 struct i2c_nrfx_twi_rtio_data *data = dev->data;
148 struct i2c_rtio *const ctx = data->ctx;
149
150 if (i2c_rtio_submit(ctx, iodev_seq)) {
151 (void)i2c_nrfx_twi_rtio_start(dev);
152 }
153 }
154
155 static DEVICE_API(i2c, i2c_nrfx_twi_rtio_driver_api) = {
156 .configure = i2c_nrfx_twi_rtio_configure,
157 .transfer = i2c_nrfx_twi_rtio_transfer,
158 .recover_bus = i2c_nrfx_twi_rtio_recover_bus,
159 .iodev_submit = i2c_nrfx_twi_rtio_submit,
160 };
161
162 #define I2C_NRFX_TWI_RTIO_DEVICE(idx) \
163 NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(I2C(idx)); \
164 BUILD_ASSERT(I2C_FREQUENCY(idx) != \
165 I2C_NRFX_TWI_INVALID_FREQUENCY, \
166 "Wrong I2C " #idx " frequency setting in dts"); \
167 static int twi_##idx##_init(const struct device *dev) \
168 { \
169 IRQ_CONNECT(DT_IRQN(I2C(idx)), DT_IRQ(I2C(idx), priority), \
170 nrfx_isr, nrfx_twi_##idx##_irq_handler, 0); \
171 const struct i2c_nrfx_twi_config *config = dev->config; \
172 const struct i2c_nrfx_twi_rtio_data *dev_data = dev->data; \
173 int err = pinctrl_apply_state(config->pcfg, \
174 PINCTRL_STATE_DEFAULT); \
175 if (err < 0) { \
176 return err; \
177 } \
178 i2c_rtio_init(dev_data->ctx, dev); \
179 return i2c_nrfx_twi_init(dev); \
180 } \
181 I2C_RTIO_DEFINE(_i2c##idx##_twi_rtio, \
182 DT_INST_PROP_OR(n, sq_size, CONFIG_I2C_RTIO_SQ_SIZE), \
183 DT_INST_PROP_OR(n, cq_size, CONFIG_I2C_RTIO_CQ_SIZE)); \
184 static struct i2c_nrfx_twi_rtio_data twi_##idx##_data = { \
185 .ctx = &_i2c##idx##_twi_rtio, \
186 }; \
187 PINCTRL_DT_DEFINE(I2C(idx)); \
188 static const struct i2c_nrfx_twi_config twi_##idx##z_config = { \
189 .twi = NRFX_TWI_INSTANCE(idx), \
190 .config = { \
191 .skip_gpio_cfg = true, \
192 .skip_psel_cfg = true, \
193 .frequency = I2C_FREQUENCY(idx), \
194 }, \
195 .event_handler = event_handler, \
196 .pcfg = PINCTRL_DT_DEV_CONFIG_GET(I2C(idx)), \
197 }; \
198 PM_DEVICE_DT_DEFINE(I2C(idx), twi_nrfx_pm_action); \
199 I2C_DEVICE_DT_DEFINE(I2C(idx), \
200 twi_##idx##_init, \
201 PM_DEVICE_DT_GET(I2C(idx)), \
202 &twi_##idx##_data, \
203 &twi_##idx##z_config, \
204 POST_KERNEL, \
205 CONFIG_I2C_INIT_PRIORITY, \
206 &i2c_nrfx_twi_rtio_driver_api)
207
208 #ifdef CONFIG_HAS_HW_NRF_TWI0
209 I2C_NRFX_TWI_RTIO_DEVICE(0);
210 #endif
211
212 #ifdef CONFIG_HAS_HW_NRF_TWI1
213 I2C_NRFX_TWI_RTIO_DEVICE(1);
214 #endif
215