Skip to content

Commit 4d1ca27

Browse files
committed
[nrf noup] drivers: i2c_nrfx_twim: add asynchronous write and exclusive access API
This commit provides an extension to the i2c_nrfx_twim driver. It introduces possibility to: - acquire/release exclusive access to the i2c bus - perform asynchronous i2c write transfers. The provided API is necessary for use cases when, i2c transfers must be started from interrupt context, while still allowing to share the i2c bus in multi-tasking environment. Signed-off-by: Andrzej Kuros <[email protected]>
1 parent 4ba36f2 commit 4d1ca27

File tree

2 files changed

+131
-6
lines changed

2 files changed

+131
-6
lines changed

drivers/i2c/i2c_nrfx_twim.c

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
#include <zephyr/drivers/i2c.h>
8+
#include <zephyr/drivers/i2c/i2c_nrfx_twim.h>
89
#include <zephyr/dt-bindings/i2c/i2c.h>
910
#include <zephyr/pm/device.h>
1011
#include <zephyr/pm/device_runtime.h>
@@ -31,8 +32,64 @@ struct i2c_nrfx_twim_data {
3132
struct k_sem transfer_sync;
3233
struct k_sem completion_sync;
3334
volatile nrfx_err_t res;
35+
i2c_nrfx_twim_async_transfer_handler_t async_transfer_handler;
36+
void *async_transfer_handler_ctx;
3437
};
3538

39+
int i2c_nrfx_twim_exclusive_access_acquire(const struct device *dev, k_timeout_t timeout)
40+
{
41+
struct i2c_nrfx_twim_data *dev_data = dev->data;
42+
int ret;
43+
44+
ret = k_sem_take(&dev_data->transfer_sync, timeout);
45+
46+
if (ret == 0) {
47+
(void)pm_device_runtime_get(dev);
48+
}
49+
50+
return ret;
51+
}
52+
53+
void i2c_nrfx_twim_exclusive_access_release(const struct device *dev)
54+
{
55+
struct i2c_nrfx_twim_data *dev_data = dev->data;
56+
57+
(void)pm_device_runtime_put(dev);
58+
59+
k_sem_give(&dev_data->transfer_sync);
60+
}
61+
62+
int i2c_nrfx_twim_async_transfer_begin(const struct device *dev, struct i2c_msg *msg, uint16_t addr,
63+
i2c_nrfx_twim_async_transfer_handler_t handler, void *ctx)
64+
{
65+
struct i2c_nrfx_twim_data *dev_data = dev->data;
66+
const struct i2c_nrfx_twim_common_config *dev_config = dev->config;
67+
uint8_t *buf;
68+
69+
if (I2C_MSG_ADDR_10_BITS & msg->flags) {
70+
return -ENOTSUP;
71+
}
72+
73+
if (!nrf_dma_accessible_check(&dev_config->twim, msg->buf)) {
74+
if (msg->len > dev_config->msg_buf_size) {
75+
return -ENOSPC;
76+
}
77+
memcpy(dev_config->msg_buf, msg->buf, msg->len);
78+
buf = dev_config->msg_buf;
79+
} else {
80+
buf = msg->buf;
81+
}
82+
83+
if (handler == NULL) {
84+
return -ENOTSUP;
85+
}
86+
87+
dev_data->async_transfer_handler = handler;
88+
dev_data->async_transfer_handler_ctx = ctx;
89+
90+
return i2c_nrfx_twim_msg_transfer(dev, msg->flags, buf, msg->len, addr);
91+
}
92+
3693
static int i2c_nrfx_twim_transfer(const struct device *dev,
3794
struct i2c_msg *msgs,
3895
uint8_t num_msgs, uint16_t addr)
@@ -46,13 +103,11 @@ static int i2c_nrfx_twim_transfer(const struct device *dev,
46103
uint8_t *buf;
47104
uint16_t buf_len;
48105

49-
k_sem_take(&dev_data->transfer_sync, K_FOREVER);
106+
(void)i2c_nrfx_twim_exclusive_access_acquire(dev, K_FOREVER);
50107

51108
/* Dummy take on completion_sync sem to be sure that it is empty */
52109
k_sem_take(&dev_data->completion_sync, K_NO_WAIT);
53110

54-
(void)pm_device_runtime_get(dev);
55-
56111
for (size_t i = 0; i < num_msgs; i++) {
57112
if (I2C_MSG_ADDR_10_BITS & msgs[i].flags) {
58113
ret = -ENOTSUP;
@@ -164,9 +219,7 @@ static int i2c_nrfx_twim_transfer(const struct device *dev,
164219
msg_buf_used = 0;
165220
}
166221

167-
(void)pm_device_runtime_put(dev);
168-
169-
k_sem_give(&dev_data->transfer_sync);
222+
i2c_nrfx_twim_exclusive_access_release(dev);
170223

171224
return ret;
172225
}
@@ -191,6 +244,15 @@ static void event_handler(nrfx_twim_evt_t const *p_event, void *p_context)
191244
break;
192245
}
193246

247+
if (dev_data->async_transfer_handler != NULL) {
248+
i2c_nrfx_twim_async_transfer_handler_t handler = dev_data->async_transfer_handler;
249+
void *ctx = dev_data->async_transfer_handler_ctx;
250+
251+
dev_data->async_transfer_handler = NULL;
252+
handler(dev, dev_data->res == NRFX_SUCCESS ? 0 : -EIO, ctx);
253+
return;
254+
}
255+
194256
k_sem_give(&dev_data->completion_sync);
195257
}
196258

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_DRIVERS_I2C_NRFX_TWIM_H
8+
#define ZEPHYR_INCLUDE_DRIVERS_I2C_NRFX_TWIM_H
9+
10+
#include <zephyr/kernel.h>
11+
#include <zephyr/drivers/i2c.h>
12+
13+
/** @brief Acquires exclusive access to the i2c bus controller.
14+
*
15+
* @param dev Pointer to the device structure for an I2C controller
16+
* driver configured in controller mode.
17+
* @param timeout Timeout for waiting to acquire exclusive access.
18+
*
19+
* @retval 0 If successful.
20+
* @retval -EBUSY Returned without waiting.
21+
* @retval -EAGAIN Waiting period timed out,
22+
* or the underlying semaphore was reset during the waiting period.
23+
*/
24+
int i2c_nrfx_twim_exclusive_access_acquire(const struct device *dev, k_timeout_t timeout);
25+
26+
/** @brief Releases exclusive access to the i2c bus controller.
27+
*
28+
* @param dev Pointer to the device structure for an I2C controller
29+
* driver on which @ref i2c_nrfx_twim_exclusive_access_acquire
30+
* has been successfully called.
31+
*/
32+
void i2c_nrfx_twim_exclusive_access_release(const struct device *dev);
33+
34+
/** @brief Type of callback that finishes an asynchronous transfer.
35+
*
36+
* @param dev Pointer to the device structure for an I2C controller driver.
37+
* @param res Result of a transfer.
38+
* @param ctx Opaque pointer for providing a context passed to
39+
* @ref i2c_nrfx_twim_async_transfer_begin .
40+
*/
41+
typedef void (*i2c_nrfx_twim_async_transfer_handler_t)(const struct device *dev, int res,
42+
void *ctx);
43+
44+
/** @brief Begins an asynchronous write transfer on a I2C bus.
45+
*
46+
* @note This function may be called only when the exclusive access to the bus is granted.
47+
* See @ref i2c_nrfx_twim_exclusive_access_acquire .
48+
*
49+
* @param dev Pointer to the device structure for an I2C controller
50+
* driver configured in controller mode.
51+
* @param msg Message to transfer.
52+
* @param addr Address of the I2C target device.
53+
* @param handler Pointer to a callback function that will be called from an I2C
54+
* interrupt handler that notifies end of the transfer.
55+
* @param ctx Opaque pointer passed to the @p handler for passing context.
56+
*
57+
* @return @c 0 on success, the @p handler will be called in future.
58+
* Other values indicate an error, the @p handler will not be called.
59+
*/
60+
int i2c_nrfx_twim_async_transfer_begin(const struct device *dev, struct i2c_msg *msg, uint16_t addr,
61+
i2c_nrfx_twim_async_transfer_handler_t handler, void *ctx);
62+
63+
#endif /* ZEPHYR_INCLUDE_DRIVERS_I2C_NRFX_TWIM_H */

0 commit comments

Comments
 (0)