From 5d00616bf31413eead0dbff4e8baf87bb28e8a51 Mon Sep 17 00:00:00 2001 From: Mieszko Mierunski Date: Mon, 4 May 2020 09:39:48 +0200 Subject: [PATCH] drivers: nrf: Add concatenation buffer to i2c nrfx TWIM driver. Add option to concatenate i2c transfers. If concatenation buffer size is provided then transfers will be concatenated as long as there is space left in buffer. Signed-off-by: Mieszko Mierunski --- drivers/i2c/i2c_nrfx_twim.c | 89 +++++++++++++++++++++++---- dts/bindings/i2c/nordic,nrf-twim.yaml | 9 +++ 2 files changed, 87 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/i2c_nrfx_twim.c b/drivers/i2c/i2c_nrfx_twim.c index 168ce997f93..31e89f64ce0 100644 --- a/drivers/i2c/i2c_nrfx_twim.c +++ b/drivers/i2c/i2c_nrfx_twim.c @@ -8,6 +8,7 @@ #include #include #include +#include #include LOG_MODULE_REGISTER(i2c_nrfx_twim, CONFIG_I2C_LOG_LEVEL); @@ -19,6 +20,8 @@ struct i2c_nrfx_twim_data { struct k_sem completion_sync; volatile nrfx_err_t res; uint32_t dev_config; + uint16_t concat_buf_size; + uint8_t *concat_buf; #ifdef CONFIG_DEVICE_POWER_MANAGEMENT uint32_t pm_state; #endif @@ -44,6 +47,12 @@ static int i2c_nrfx_twim_transfer(struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) { int ret = 0; + uint32_t concat_len = 0; + uint8_t *concat_buf = get_dev_data(dev)->concat_buf; + uint16_t concat_buf_size = get_dev_data(dev)->concat_buf_size; + nrfx_twim_xfer_desc_t cur_xfer = { + .address = addr + }; k_sem_take(&(get_dev_data(dev)->transfer_sync), K_FOREVER); @@ -58,18 +67,44 @@ static int i2c_nrfx_twim_transfer(struct device *dev, struct i2c_msg *msgs, break; } - nrfx_twim_xfer_desc_t cur_xfer = { - .p_primary_buf = msgs[i].buf, - .primary_length = msgs[i].len, - .address = addr, - .type = (msgs[i].flags & I2C_MSG_READ) ? - NRFX_TWIM_XFER_RX : NRFX_TWIM_XFER_TX - }; + bool last_or_non_concatenable = + ((msgs[i].flags & I2C_MSG_STOP) + || !(i + 1 < num_msgs) + || (msgs[i + 1].flags & I2C_MSG_RESTART) + || ((msgs[i + 1].flags & I2C_MSG_READ) + != (msgs[i].flags & I2C_MSG_READ))); + + if ((concat_len != 0) || !last_or_non_concatenable) { + if (msgs[i].len > concat_buf_size) { + LOG_ERR("Concatenation buffer is too small"); + return -ENOSPC; + } + if (!(msgs[i].flags & I2C_MSG_READ)) { + memcpy(concat_buf + concat_len, + msgs[i].buf, + msgs[i].len); + } + concat_len += msgs[i].len; + } + + if (last_or_non_concatenable) { + if (concat_len == 0) { + cur_xfer.p_primary_buf = msgs[i].buf; + cur_xfer.primary_length = msgs[i].len; + } else { + cur_xfer.p_primary_buf = concat_buf; + cur_xfer.primary_length = concat_len; + } + cur_xfer.type = (msgs[i].flags & I2C_MSG_READ) ? + NRFX_TWIM_XFER_RX : NRFX_TWIM_XFER_TX; + } else { + continue; + } nrfx_err_t res = nrfx_twim_xfer(&get_dev_config(dev)->twim, - &cur_xfer, - (msgs[i].flags & I2C_MSG_STOP) ? - 0 : NRFX_TWIM_FLAG_TX_NO_STOP); + &cur_xfer, + (msgs[i].flags & I2C_MSG_STOP) ? + 0 : NRFX_TWIM_FLAG_TX_NO_STOP); if (res != NRFX_SUCCESS) { if (res == NRFX_ERROR_BUSY) { ret = -EBUSY; @@ -99,11 +134,31 @@ static int i2c_nrfx_twim_transfer(struct device *dev, struct i2c_msg *msgs, } res = get_dev_data(dev)->res; + if (res != NRFX_SUCCESS) { LOG_ERR("Error %d occurred for message %d", res, i); ret = -EIO; break; } + + /* If concatenated messages were I2C_MSG_READ type, then + * content of concatenation buffer has to be copied back into + * buffers provided by user. */ + if ((msgs[i].flags & I2C_MSG_READ) + && cur_xfer.p_primary_buf == concat_buf) { + int j = i; + + while (concat_len >= msgs[j].len) { + concat_len -= msgs[j].len; + memcpy(msgs[j].buf, + concat_buf + concat_len, + msgs[j].len); + j--; + } + + } + + concat_len = 0; } nrfx_twim_disable(&get_dev_config(dev)->twim); @@ -244,6 +299,13 @@ static int twim_nrfx_pm_control(struct device *dev, uint32_t ctrl_command, #define I2C_FREQUENCY(idx) \ I2C_NRFX_TWIM_FREQUENCY(DT_PROP(I2C(idx), clock_frequency)) +#define CONCAT_BUF_SIZE(idx) DT_PROP(I2C(idx), concat_buf_size) + +#define I2C_CONCAT_BUF(idx) \ + COND_CODE_1(DT_NODE_HAS_PROP(I2C(idx), concat_buf_size), \ + (.concat_buf = twim_##idx##_concat_buf, \ + .concat_buf_size = CONCAT_BUF_SIZE(idx),), ()) + #define I2C_NRFX_TWIM_DEVICE(idx) \ BUILD_ASSERT(I2C_FREQUENCY(idx) != \ I2C_NRFX_TWIM_INVALID_FREQUENCY, \ @@ -254,11 +316,16 @@ static int twim_nrfx_pm_control(struct device *dev, uint32_t ctrl_command, nrfx_isr, nrfx_twim_##idx##_irq_handler, 0); \ return init_twim(dev); \ } \ + COND_CODE_1(DT_NODE_HAS_PROP(I2C(idx), concat_buf_size), \ + (static uint8_t twim_##idx##_concat_buf[CONCAT_BUF_SIZE(idx)];), \ + ()) \ + \ static struct i2c_nrfx_twim_data twim_##idx##_data = { \ .transfer_sync = Z_SEM_INITIALIZER( \ twim_##idx##_data.transfer_sync, 1, 1), \ .completion_sync = Z_SEM_INITIALIZER( \ - twim_##idx##_data.completion_sync, 0, 1) \ + twim_##idx##_data.completion_sync, 0, 1), \ + I2C_CONCAT_BUF(idx) \ }; \ static const struct i2c_nrfx_twim_config twim_##idx##z_config = { \ .twim = NRFX_TWIM_INSTANCE(idx), \ diff --git a/dts/bindings/i2c/nordic,nrf-twim.yaml b/dts/bindings/i2c/nordic,nrf-twim.yaml index d758dec6995..8e92d2c2edc 100644 --- a/dts/bindings/i2c/nordic,nrf-twim.yaml +++ b/dts/bindings/i2c/nordic,nrf-twim.yaml @@ -6,3 +6,12 @@ description: Nordic nRF family TWIM (TWI master with EasyDMA) compatible: "nordic,nrf-twim" include: nordic,nrf-twi-common.yaml + +properties: + concat-buf-size: + type: int + required: false + description: + If concatenation buffer size is set, then multiple messages in the + same direction, will be concatenated into single transfers as long + as there is space in buffer and no restart or stop flag is set.