Skip to content

Commit 5601805

Browse files
ananglcarlescufi
authored andcommitted
drivers: i2c_nrfx_twim: Add handling of buffers located in flash
TWIM peripherals cannot perform write transactions from buffers located in flash. The content of such buffers needs to be copied to RAM before the actual transfer can be requested. This commits adds a new property (zephyr,flash-buf-max-size) that informs the driver how much space in RAM needs to be reserved for such copying and adds proper handling of buffers located in flash. This fixes an issue that caused that e.g. the DPS310 sensor driver did not work on nRF SoCs that only have TWIM, not TWI peripherals. Signed-off-by: Andrzej Głąbek <[email protected]>
1 parent 99ce264 commit 5601805

File tree

2 files changed

+94
-44
lines changed

2 files changed

+94
-44
lines changed

drivers/i2c/i2c_nrfx_twim.c

Lines changed: 73 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ struct i2c_nrfx_twim_data {
2020
struct k_sem completion_sync;
2121
volatile nrfx_err_t res;
2222
uint32_t dev_config;
23-
uint16_t concat_buf_size;
24-
uint8_t *concat_buf;
23+
uint8_t *msg_buf;
2524
};
2625

2726
struct i2c_nrfx_twim_config {
2827
nrfx_twim_t twim;
2928
nrfx_twim_config_t config;
29+
uint16_t concat_buf_size;
30+
uint16_t flash_buf_max_size;
3031
};
3132

3233
static inline struct i2c_nrfx_twim_data *get_dev_data(const struct device *dev)
@@ -44,20 +45,22 @@ static int i2c_nrfx_twim_transfer(const struct device *dev,
4445
struct i2c_msg *msgs,
4546
uint8_t num_msgs, uint16_t addr)
4647
{
48+
struct i2c_nrfx_twim_data *dev_data = get_dev_data(dev);
49+
const struct i2c_nrfx_twim_config *dev_config = get_dev_config(dev);
4750
int ret = 0;
48-
uint32_t concat_len = 0;
49-
uint8_t *concat_buf = get_dev_data(dev)->concat_buf;
50-
uint16_t concat_buf_size = get_dev_data(dev)->concat_buf_size;
51+
uint8_t *msg_buf = dev_data->msg_buf;
52+
uint16_t msg_buf_used = 0;
53+
uint16_t concat_buf_size = dev_config->concat_buf_size;
5154
nrfx_twim_xfer_desc_t cur_xfer = {
5255
.address = addr
5356
};
5457

55-
k_sem_take(&(get_dev_data(dev)->transfer_sync), K_FOREVER);
58+
k_sem_take(&dev_data->transfer_sync, K_FOREVER);
5659

5760
/* Dummy take on completion_sync sem to be sure that it is empty */
58-
k_sem_take(&(get_dev_data(dev)->completion_sync), K_NO_WAIT);
61+
k_sem_take(&dev_data->completion_sync, K_NO_WAIT);
5962

60-
nrfx_twim_enable(&get_dev_config(dev)->twim);
63+
nrfx_twim_enable(&dev_config->twim);
6164

6265
for (size_t i = 0; i < num_msgs; i++) {
6366
if (I2C_MSG_ADDR_10_BITS & msgs[i].flags) {
@@ -81,41 +84,60 @@ static int i2c_nrfx_twim_transfer(const struct device *dev,
8184
* already committed to concatenate this message, add it to
8285
* the buffer after verifying there's room.
8386
*/
84-
if (concat_next || (concat_len != 0)) {
85-
if ((concat_len + msgs[i].len) > concat_buf_size) {
87+
if (concat_next || (msg_buf_used != 0)) {
88+
if ((msg_buf_used + msgs[i].len) > concat_buf_size) {
8689
LOG_ERR("Need to use concatenation buffer and "
8790
"provided size is insufficient "
8891
"(%u + %u > %u). "
8992
"Adjust the zephyr,concat-buf-size "
9093
"property in the \"%s\" node.",
91-
concat_len, msgs[i].len,
94+
msg_buf_used, msgs[i].len,
9295
concat_buf_size, dev->name);
9396
ret = -ENOSPC;
9497
break;
9598
}
9699
if (!(msgs[i].flags & I2C_MSG_READ)) {
97-
memcpy(concat_buf + concat_len,
100+
memcpy(msg_buf + msg_buf_used,
98101
msgs[i].buf,
99102
msgs[i].len);
100103
}
101-
concat_len += msgs[i].len;
104+
msg_buf_used += msgs[i].len;
105+
106+
/* TWIM peripherals cannot transfer data directly from
107+
* flash. If a buffer located in flash is provided for
108+
* a write transaction, its content must be copied to
109+
* RAM before the transfer can be requested.
110+
*/
111+
} else if (!(msgs[i].flags & I2C_MSG_READ) &&
112+
!nrfx_is_in_ram(msgs[i].buf)) {
113+
if (msgs[i].len > dev_config->flash_buf_max_size) {
114+
LOG_ERR("Cannot copy flash buffer of size: %u. "
115+
"Adjust the zephyr,flash-buf-max-size "
116+
"property in the \"%s\" node.",
117+
msgs[i].len, dev->name);
118+
ret = -EINVAL;
119+
break;
120+
}
121+
122+
memcpy(msg_buf, msgs[i].buf, msgs[i].len);
123+
msg_buf_used = msgs[i].len;
102124
}
103125

104126
if (concat_next) {
105127
continue;
106128
}
107129

108-
if (concat_len == 0) {
130+
if (msg_buf_used == 0) {
109131
cur_xfer.p_primary_buf = msgs[i].buf;
110132
cur_xfer.primary_length = msgs[i].len;
111133
} else {
112-
cur_xfer.p_primary_buf = concat_buf;
113-
cur_xfer.primary_length = concat_len;
134+
cur_xfer.p_primary_buf = msg_buf;
135+
cur_xfer.primary_length = msg_buf_used;
114136
}
115137
cur_xfer.type = (msgs[i].flags & I2C_MSG_READ) ?
116138
NRFX_TWIM_XFER_RX : NRFX_TWIM_XFER_TX;
117139

118-
nrfx_err_t res = nrfx_twim_xfer(&get_dev_config(dev)->twim,
140+
nrfx_err_t res = nrfx_twim_xfer(&dev_config->twim,
119141
&cur_xfer,
120142
(msgs[i].flags & I2C_MSG_STOP) ?
121143
0 : NRFX_TWIM_FLAG_TX_NO_STOP);
@@ -129,7 +151,7 @@ static int i2c_nrfx_twim_transfer(const struct device *dev,
129151
}
130152
}
131153

132-
ret = k_sem_take(&(get_dev_data(dev)->completion_sync),
154+
ret = k_sem_take(&dev_data->completion_sync,
133155
I2C_TRANSFER_TIMEOUT_MSEC);
134156
if (ret != 0) {
135157
/* Whatever the frequency, completion_sync should have
@@ -149,14 +171,14 @@ static int i2c_nrfx_twim_transfer(const struct device *dev,
149171
* bus from this error.
150172
*/
151173
LOG_ERR("Error on I2C line occurred for message %d", i);
152-
nrfx_twim_disable(&get_dev_config(dev)->twim);
153-
nrfx_twim_bus_recover(get_dev_config(dev)->config.scl,
154-
get_dev_config(dev)->config.sda);
174+
nrfx_twim_disable(&dev_config->twim);
175+
nrfx_twim_bus_recover(dev_config->config.scl,
176+
dev_config->config.sda);
155177
ret = -EIO;
156178
break;
157179
}
158180

159-
res = get_dev_data(dev)->res;
181+
res = dev_data->res;
160182

161183
if (res != NRFX_SUCCESS) {
162184
LOG_ERR("Error 0x%08X occurred for message %d", res, i);
@@ -168,24 +190,24 @@ static int i2c_nrfx_twim_transfer(const struct device *dev,
168190
* content of concatenation buffer has to be copied back into
169191
* buffers provided by user. */
170192
if ((msgs[i].flags & I2C_MSG_READ)
171-
&& cur_xfer.p_primary_buf == concat_buf) {
193+
&& cur_xfer.p_primary_buf == msg_buf) {
172194
int j = i;
173195

174-
while (concat_len >= msgs[j].len) {
175-
concat_len -= msgs[j].len;
196+
while (msg_buf_used >= msgs[j].len) {
197+
msg_buf_used -= msgs[j].len;
176198
memcpy(msgs[j].buf,
177-
concat_buf + concat_len,
199+
msg_buf + msg_buf_used,
178200
msgs[j].len);
179201
j--;
180202
}
181203

182204
}
183205

184-
concat_len = 0;
206+
msg_buf_used = 0;
185207
}
186208

187-
nrfx_twim_disable(&get_dev_config(dev)->twim);
188-
k_sem_give(&(get_dev_data(dev)->transfer_sync));
209+
nrfx_twim_disable(&dev_config->twim);
210+
k_sem_give(&dev_data->transfer_sync);
189211

190212
return ret;
191213
}
@@ -296,12 +318,18 @@ static int twim_nrfx_pm_control(const struct device *dev,
296318
#define I2C_FREQUENCY(idx) \
297319
I2C_NRFX_TWIM_FREQUENCY(DT_PROP(I2C(idx), clock_frequency))
298320

299-
#define CONCAT_BUF_SIZE(idx) DT_PROP(I2C(idx), zephyr_concat_buf_size)
300-
301-
#define I2C_CONCAT_BUF(idx) \
321+
#define CONCAT_BUF_SIZE(idx) \
302322
COND_CODE_1(DT_NODE_HAS_PROP(I2C(idx), zephyr_concat_buf_size), \
303-
(.concat_buf = twim_##idx##_concat_buf, \
304-
.concat_buf_size = CONCAT_BUF_SIZE(idx),), ())
323+
(DT_PROP(I2C(idx), zephyr_concat_buf_size)), (0))
324+
#define FLASH_BUF_MAX_SIZE(idx) \
325+
COND_CODE_1(DT_NODE_HAS_PROP(I2C(idx), zephyr_flash_buf_max_size), \
326+
(DT_PROP(I2C(idx), zephyr_flash_buf_max_size)), (0))
327+
328+
#define USES_MSG_BUF(idx) \
329+
COND_CODE_0(CONCAT_BUF_SIZE(idx), \
330+
(COND_CODE_0(FLASH_BUF_MAX_SIZE(idx), (0), (1))), \
331+
(1))
332+
#define MSG_BUF_SIZE(idx) MAX(CONCAT_BUF_SIZE(idx), FLASH_BUF_MAX_SIZE(idx))
305333

306334
#define I2C_NRFX_TWIM_DEVICE(idx) \
307335
BUILD_ASSERT(I2C_FREQUENCY(idx) != \
@@ -313,24 +341,25 @@ static int twim_nrfx_pm_control(const struct device *dev,
313341
nrfx_isr, nrfx_twim_##idx##_irq_handler, 0); \
314342
return init_twim(dev); \
315343
} \
316-
COND_CODE_1(DT_NODE_HAS_PROP(I2C(idx), zephyr_concat_buf_size), \
317-
(static uint8_t twim_##idx##_concat_buf[CONCAT_BUF_SIZE(idx)];), \
318-
()) \
319-
\
344+
IF_ENABLED(USES_MSG_BUF(idx), \
345+
(static uint8_t twim_##idx##_msg_buf[MSG_BUF_SIZE(idx)];)) \
320346
static struct i2c_nrfx_twim_data twim_##idx##_data = { \
321-
.transfer_sync = Z_SEM_INITIALIZER( \
322-
twim_##idx##_data.transfer_sync, 1, 1), \
323-
.completion_sync = Z_SEM_INITIALIZER( \
324-
twim_##idx##_data.completion_sync, 0, 1), \
325-
I2C_CONCAT_BUF(idx) \
347+
.transfer_sync = Z_SEM_INITIALIZER( \
348+
twim_##idx##_data.transfer_sync, 1, 1), \
349+
.completion_sync = Z_SEM_INITIALIZER( \
350+
twim_##idx##_data.completion_sync, 0, 1), \
351+
IF_ENABLED(USES_MSG_BUF(idx), \
352+
(.msg_buf = twim_##idx##_msg_buf,)) \
326353
}; \
327354
static const struct i2c_nrfx_twim_config twim_##idx##z_config = { \
328355
.twim = NRFX_TWIM_INSTANCE(idx), \
329356
.config = { \
330357
.scl = DT_PROP(I2C(idx), scl_pin), \
331358
.sda = DT_PROP(I2C(idx), sda_pin), \
332359
.frequency = I2C_FREQUENCY(idx), \
333-
} \
360+
}, \
361+
.concat_buf_size = CONCAT_BUF_SIZE(idx), \
362+
.flash_buf_max_size = FLASH_BUF_MAX_SIZE(idx), \
334363
}; \
335364
DEVICE_DT_DEFINE(I2C(idx), \
336365
twim_##idx##_init, \

dts/bindings/i2c/nordic,nrf-twim.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,24 @@ properties:
4040
the SSD1306 display that cannot tolerate a repeated start and
4141
address appearing on the bus between message fragments. For many
4242
devices a concatenation buffer is not necessary.
43+
44+
zephyr,flash-buf-max-size:
45+
type: int
46+
required: false
47+
default: 16
48+
description: |
49+
TWIM peripherals cannot perform write transactions from buffers
50+
located in flash. If such buffers are expected to be used with
51+
a given instance of the TWIM peripheral, this property must be
52+
set to the maximum possible size of those buffers, so that the
53+
driver can reserve enough space in RAM to copy there the contents
54+
of particular buffers before requesting the actual transfers.
55+
56+
If this property is not set to a value adequate for a given
57+
application, write transactions may fail for buffers that are
58+
located in flash, what in turn may cause certain components,
59+
like the DPS310 sensor driver, to not work.
60+
61+
It is recommended to use the same value for this property and for
62+
the zephyr,concat-buf-size one, as both these buffering mechanisms
63+
can utilize the same space in RAM.

0 commit comments

Comments
 (0)