You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by vi...@apache.org on 2018/11/19 17:07:48 UTC
[mynewt-core] 01/07: Initial commit dma i2c
This is an automated email from the ASF dual-hosted git repository.
vipulrahane pushed a commit to branch dma_i2c_new_api
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git
commit 34b7101e04e7ad2f20c989961719685dd46fa5b4
Author: Vipul Rahane <vi...@runtime.io>
AuthorDate: Wed Oct 17 12:58:20 2018 -0700
Initial commit dma i2c
---
hw/drivers/sensors/tsl2561/src/tsl2561_shell.c | 2 +-
hw/hal/include/hal/hal_i2c.h | 9 +
hw/mcu/nordic/nrf52xxx/src/hal_i2c.c | 376 +++++++++++++++++--------
3 files changed, 273 insertions(+), 114 deletions(-)
diff --git a/hw/drivers/sensors/tsl2561/src/tsl2561_shell.c b/hw/drivers/sensors/tsl2561/src/tsl2561_shell.c
index 71201e6..169e9c3 100644
--- a/hw/drivers/sensors/tsl2561/src/tsl2561_shell.c
+++ b/hw/drivers/sensors/tsl2561/src/tsl2561_shell.c
@@ -348,7 +348,7 @@ tsl2561_shell_cmd_en(int argc, char **argv)
/* Update the enable state */
if (argc == 3) {
lval = strtol(argv[2], &endptr, 10); /* Base 10 */
- if (argv[2] != '\0' && *endptr == '\0' &&
+ if (*argv[2] != '\0' && *endptr == '\0' &&
lval >= 0 && lval <= 1) {
rc = tsl2561_enable(&g_sensor_itf, lval);
if (rc) {
diff --git a/hw/hal/include/hal/hal_i2c.h b/hw/hal/include/hal/hal_i2c.h
index be080ca..50fd1df 100644
--- a/hw/hal/include/hal/hal_i2c.h
+++ b/hw/hal/include/hal/hal_i2c.h
@@ -79,6 +79,15 @@ extern "C" {
/** Slave responded to data byte with NACK. */
#define HAL_I2C_ERR_DATA_NACK 5
+/** Overrun error */
+#define HAL_I2C_ERR_OVERRUN 6
+
+/** I2C bus in suspended state, but last op was 0 */
+#define HAL_I2C_ERR_SUSPEND 7
+
+/** I2C bus in stopped state, but last op was 1 */
+#define HAL_I2C_ERR_STOP 8
+
/**
* When sending a packet, use this structure to pass the arguments.
*/
diff --git a/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c b/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c
index b925394..b19e2ad 100644
--- a/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c
+++ b/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c
@@ -26,9 +26,22 @@
#include <hal/hal_gpio.h>
#include <mcu/nrf52_hal.h>
#include "nrf_twim.h"
+#include <console/console.h>
#include <nrf.h>
+#define I2C_WRITE 0
+#define I2C_READ 1
+
+#define WAIT_FOR_EVT(__r, __e)\
+ while (!(__r)->EVENTS_####__e) {\
+ now = os_time_get();\
+ if (OS_TIME_TICK_GT(now, abs_timo)) {\
+ rc = HAL_I2C_ERR_TIMEOUT;\
+ goto err;\
+ }\
+ }
+
#define NRF52_HAL_I2C_MAX (2)
#define NRF52_SCL_PIN_CONF \
@@ -48,17 +61,22 @@
#define NRF52_SDA_PIN_CONF_CLR NRF52_SCL_PIN_CONF_CLR
struct nrf52_hal_i2c {
- NRF_TWI_Type *nhi_regs;
+ NRF_TWIM_Type *nhi_regs;
+ /*
+ * previous last_op, this is just to check if the previous
+ * transaction actually put the I2C bus in the suspended state
+ */
+ int nhi_prev_last_op;
};
#if MYNEWT_VAL(I2C_0)
struct nrf52_hal_i2c hal_twi_i2c0 = {
- .nhi_regs = NRF_TWI0
+ .nhi_regs = NRF_TWIM0
};
#endif
#if MYNEWT_VAL(I2C_1)
struct nrf52_hal_i2c hal_twi_i2c1 = {
- .nhi_regs = NRF_TWI1
+ .nhi_regs = NRF_TWIM1
};
#endif
@@ -180,6 +198,8 @@ hal_i2c_convert_status(int nrf_status)
return HAL_I2C_ERR_DATA_NACK;
} else if (nrf_status & NRF_TWIM_ERROR_ADDRESS_NACK) {
return HAL_I2C_ERR_ADDR_NACK;
+ } else if (nrf_status & TWIM_ERRORSRC_OVERRUN_Msk) {
+ return HAL_I2C_ERR_OVERRUN;
} else {
return HAL_I2C_ERR_UNKNOWN;
}
@@ -257,7 +277,7 @@ int
hal_i2c_init(uint8_t i2c_num, void *usercfg)
{
struct nrf52_hal_i2c *i2c;
- NRF_TWI_Type *regs;
+ NRF_TWIM_Type *regs;
struct nrf52_hal_i2c_cfg *cfg;
uint32_t freq;
int rc;
@@ -275,13 +295,13 @@ hal_i2c_init(uint8_t i2c_num, void *usercfg)
switch (cfg->i2c_frequency) {
case 100:
- freq = TWI_FREQUENCY_FREQUENCY_K100;
+ freq = TWIM_FREQUENCY_FREQUENCY_K100;
break;
case 250:
- freq = TWI_FREQUENCY_FREQUENCY_K250;
+ freq = TWIM_FREQUENCY_FREQUENCY_K250;
break;
case 400:
- freq = TWI_FREQUENCY_FREQUENCY_K400;
+ freq = TWIM_FREQUENCY_FREQUENCY_K400;
break;
default:
rc = HAL_I2C_ERR_INVAL;
@@ -297,179 +317,309 @@ hal_i2c_init(uint8_t i2c_num, void *usercfg)
scl_port->PIN_CNF[cfg->scl_pin] = NRF52_SCL_PIN_CONF;
sda_port->PIN_CNF[cfg->sda_pin] = NRF52_SDA_PIN_CONF;
- regs->PSELSCL = cfg->scl_pin;
- regs->PSELSDA = cfg->sda_pin;
+ regs->PSEL.SCL = cfg->scl_pin;
+ regs->PSEL.SDA = cfg->sda_pin;
regs->FREQUENCY = freq;
- regs->ENABLE = TWI_ENABLE_ENABLE_Enabled;
+ regs->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
+ i2c->nhi_prev_last_op = -1;
return (0);
err:
return (rc);
}
-int
-hal_i2c_master_write(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
- uint32_t timo, uint8_t last_op)
+/*
+ * Starts an I2C transaction either read or write using EasyDMA/TWIM
+ */
+static int
+hal_i2c_handle_transact_start(struct nrf52_hal_i2c *i2c, uint8_t op,
+ uint32_t abs_timo)
{
- struct nrf52_hal_i2c *i2c;
- NRF_TWI_Type *regs;
- int nrf_status;
+ NRF_TWIM_Type *regs;
+ uint32_t now;
int rc;
- int i;
- uint32_t start;
- rc = hal_i2c_resolve(i2c_num, &i2c);
- if (rc != 0) {
- return rc;
- }
regs = i2c->nhi_regs;
- regs->ADDRESS = pdata->address;
-
regs->EVENTS_ERROR = 0;
+
+ /* If a transaction was previously suspended, resume it */
+ if (regs->EVENTS_SUSPENDED) {
+ regs->TASKS_RESUME = 1;
+ }
+
regs->EVENTS_STOPPED = 0;
regs->EVENTS_SUSPENDED = 0;
- regs->SHORTS = 0;
+ if (op == I2C_WRITE) {
+ /* Start an I2C transmit transaction */
+ regs->TASKS_STARTTX = 1;
+ WAIT_FOR_EVT(regs, TXSTARTED);
+ regs->EVENTS_TXSTARTED = 0;
+ } else {
+ /* Start an I2C receive transaction */
+ regs->TASKS_STARTRX = 1;
+ WAIT_FOR_EVT(regs, RXSTARTED);
+ regs->EVENTS_RXSTARTED = 0;
+ }
- regs->TASKS_STARTTX = 1;
- regs->TASKS_RESUME = 1;
+ return 0;
+err:
+ return rc;
+}
- start = os_time_get();
- for (i = 0; i < pdata->len; i++) {
- regs->EVENTS_TXDSENT = 0;
- regs->TXD = pdata->buffer[i];
- while (!regs->EVENTS_TXDSENT && !regs->EVENTS_ERROR) {
- if (os_time_get() - start > timo) {
- rc = HAL_I2C_ERR_TIMEOUT;
- goto err;
- }
- }
- if (regs->EVENTS_ERROR) {
- goto err;
+static int
+hal_i2c_handle_transact_end(NRF_TWIM_Type *regs, uint32_t start,
+ uint32_t abs_timo, uint8_t last_op,
+ uint8_t op)
+{
+ int rc;
+ uint32_t evt;
+ os_time_t now;
+
+ if (last_op) {
+ /* this is to take care of clock stretches as per the datasheet */
+ if (regs->EVENTS_SUSPENDED) {
+ /* If there is a clock stretch, the event would be suspended for
+ * that TWIM interface, it would need to be resumed to finish the
+ * transaction
+ */
+ regs->TASKS_RESUME = 1;
}
}
- /* If last_op is zero it means we dont put a stop at end. */
- if (last_op) {
- regs->EVENTS_STOPPED = 0;
- regs->TASKS_STOP = 1;
- while (!regs->EVENTS_STOPPED && !regs->EVENTS_ERROR) {
- if (os_time_get() - start > timo) {
- rc = HAL_I2C_ERR_TIMEOUT;
- goto err;
+
+ while(1) {
+ /*
+ * Use last_op as the determining factor for the type of event to be
+ * monitored
+ */
+ if (last_op) {
+ evt = regs->EVENTS_STOPPED;
+ } else {
+ evt = regs->EVENTS_SUSPENDED;
+ }
+
+ if (evt) {
+ if (evt == regs->EVENTS_STOPPED) {
+ regs->EVENTS_LASTTX = 0;
+ regs->EVENTS_LASTRX = 0;
}
+ break;
}
+
if (regs->EVENTS_ERROR) {
goto err;
}
+
+ now = os_time_get();
+ if (OS_TIME_TICK_GT(now, abs_timo)) {
+ rc = HAL_I2C_ERR_TIMEOUT;
+ goto err;
+ }
}
- rc = 0;
+ return 0;
+err:
+ return rc;
+}
+
+static int
+hal_i2c_bus_error_detect(struct nrf52_hal_i2c *i2c)
+{
+ int rc;
+ NRF_TWIM_Type *regs;
+
+ regs = i2c->nhi_regs;
+
+ if (i2c->nhi_prev_last_op == 0 && !regs->EVENTS_SUSPENDED) {
+ /*
+ * return HAL_I2C_ERR_SUSPEND if previous last_op was 0 and
+ * the bus was not in the suspended state
+ */
+ rc = HAL_I2C_ERR_SUSPEND;
+ goto err;
+ } else if (i2c->nhi_prev_last_op == 1 &&
+ (!regs->EVENTS_STOPPED && regs->EVENTS_SUSPENDED)) {
+ /*
+ * return HAL_I2C_ERR_STOP if previous last_op was 1 and
+ * the bus is not in the stopped state or in initial state
+ * which is EVENTS_SUSPENDED is 1 and EVENTS_STOPPED is 1
+ */
+ rc = HAL_I2C_ERR_STOP;
+ goto err;
+ }
+ return 0;
err:
+ return rc;
+}
+
+/* Handle errors returned from the TWIM peripheral along with timeouts */
+static int
+hal_i2c_handle_errors(NRF_TWIM_Type *regs, int rc)
+{
+ int nrf_status;
+
+ regs->TASKS_RESUME = 1;
regs->TASKS_STOP = 1;
if (regs->EVENTS_ERROR) {
+ regs->EVENTS_ERROR = 0;
nrf_status = regs->ERRORSRC;
regs->ERRORSRC = nrf_status;
rc = hal_i2c_convert_status(nrf_status);
- } else if (rc == HAL_I2C_ERR_TIMEOUT) {
+ } else if (rc) {
/* Some I2C slave peripherals cause a glitch on the bus when they
- * reset which puts the TWI in an unresponsive state. Disabling and
+ * reset which puts the TWI in an unresponsive state. Disabling and
* re-enabling the TWI returns it to normal operation.
* A clear operation is performed in case one of the devices on
* the bus is in a bad state.
*/
- regs->ENABLE = TWI_ENABLE_ENABLE_Disabled;
- hal_i2c_clear_bus(regs->PSELSCL, regs->PSELSDA);
- regs->ENABLE = TWI_ENABLE_ENABLE_Enabled;
+ regs->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
+ hal_i2c_clear_bus(regs->PSEL.SCL, regs->PSEL.SDA);
+ regs->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
}
- return (rc);
+ return rc;
}
+/* Perform I2C master writes using TWIM/EasyDMA */
int
-hal_i2c_master_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
- uint32_t timo, uint8_t last_op)
+hal_i2c_master_write(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
+ uint32_t timo, uint8_t last_op)
{
- struct nrf52_hal_i2c *i2c;
- NRF_TWI_Type *regs;
- int nrf_status;
int rc;
- int i;
uint32_t start;
+ NRF_TWIM_Type *regs;
+ struct nrf52_hal_i2c *i2c;
+ start = os_time_get();
+
+ /* Resolve the I2C bus */
rc = hal_i2c_resolve(i2c_num, &i2c);
if (rc != 0) {
return rc;
}
+
regs = i2c->nhi_regs;
- start = os_time_get();
+ /* Detect errors on the bus based on the previous and current
+ * condition of the bus
+ */
+ rc = hal_i2c_bus_error_detect(i2c);
+ if (rc) {
+ goto err;
+ }
- if (regs->EVENTS_RXDREADY) {
- /*
- * If previous read was interrupted, flush RXD.
+ /* Configure the TXD registers for EasyDMA access to work with buffers of
+ * specific length and address of the slave
+ */
+ regs->ADDRESS = pdata->address;
+ regs->TXD.MAXCNT = pdata->len;
+ regs->TXD.PTR = (uint32_t)pdata->buffer;
+ regs->TXD.LIST = 0;
+ /* Disable and clear interrupts */
+ regs->INTENCLR = NRF_TWIM_ALL_INTS_MASK;
+ regs->INTEN = 0;
+
+ /* Setup shorts to end transaction based on last_op,
+ * 0 : STOP transaction,
+ * 1 : SUSPEND transaction
+ */
+ if (last_op) {
+ /* EVENT_STOPPED would get set after LASTTX gets set at
+ * the end of the transaction for the last byte
+ */
+ regs->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
+ } else {
+ /* EVENT_SUSPENDED would get set after LASTTX gets set at
+ * the end of the transaction for the last byte
*/
- (void)regs->RXD;
- (void)regs->RXD;
+ regs->SHORTS = TWIM_SHORTS_LASTTX_SUSPEND_Msk;
}
- regs->EVENTS_ERROR = 0;
- regs->EVENTS_STOPPED = 0;
- regs->EVENTS_SUSPENDED = 0;
- regs->EVENTS_RXDREADY = 0;
- regs->ADDRESS = pdata->address;
-
- if (pdata->len == 1 && last_op) {
- regs->SHORTS = TWI_SHORTS_BB_STOP_Msk;
- } else {
- regs->SHORTS = TWI_SHORTS_BB_SUSPEND_Msk;
+ /* Starts an I2C transaction using TWIM/EasyDMA */
+ rc = hal_i2c_handle_transact_start(i2c, I2C_WRITE, start + timo);
+ if (rc) {
+ goto err;
}
- regs->TASKS_STARTRX = 1;
- for (i = 0; i < pdata->len; i++) {
- regs->TASKS_RESUME = 1;
- while (!regs->EVENTS_RXDREADY && !regs->EVENTS_ERROR) {
- if (os_time_get() - start > timo) {
- rc = HAL_I2C_ERR_TIMEOUT;
- goto err;
- }
- }
- if (regs->EVENTS_ERROR) {
- goto err;
- }
- pdata->buffer[i] = regs->RXD;
- if (i == pdata->len - 2) {
- if (last_op) {
- regs->SHORTS = TWI_SHORTS_BB_STOP_Msk;
- }
- }
- regs->EVENTS_RXDREADY = 0;
+ /* Ends an I2C transaction using TWIM/EasyDMA */
+ rc = hal_i2c_handle_transact_end(regs, I2C_WRITE, start, start + timo, last_op);
+ if (rc) {
+ goto err;
}
- return (0);
+ /* Save the current last op to detect bus errors later */
+ i2c->nhi_prev_last_op = last_op;
+ return 0;
err:
- regs->TASKS_STOP = 1;
- regs->SHORTS = TWI_SHORTS_BB_STOP_Msk;
+ return hal_i2c_handle_errors(regs, rc);
+}
- if (regs->EVENTS_ERROR) {
- nrf_status = regs->ERRORSRC;
- regs->ERRORSRC = nrf_status;
- rc = hal_i2c_convert_status(nrf_status);
- } else if (rc == HAL_I2C_ERR_TIMEOUT) {
- /* Some I2C slave peripherals cause a glitch on the bus when they
- * reset which puts the TWI in an unresponsive state. Disabling and
- * re-enabling the TWI returns it to normal operation.
- * A clear operation is performed in case one of the devices on
- * the bus is in a bad state.
- */
- regs->ENABLE = TWI_ENABLE_ENABLE_Disabled;
- hal_i2c_clear_bus(regs->PSELSCL, regs->PSELSDA);
- regs->ENABLE = TWI_ENABLE_ENABLE_Enabled;
+int
+hal_i2c_master_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
+ uint32_t timo, uint8_t last_op)
+{
+ int rc;
+ uint32_t start;
+ NRF_TWIM_Type *regs;
+ struct nrf52_hal_i2c *i2c;
+
+ start = os_time_get();
+
+ /* Resolve the I2C bus */
+ rc = hal_i2c_resolve(i2c_num, &i2c);
+ if (rc != 0) {
+ return rc;
}
- return (rc);
+ regs = i2c->nhi_regs;
+
+ /* Detect errors on the bus based on the previous and current
+ * condition of the bus
+ */
+ rc = hal_i2c_bus_error_detect(i2c);
+ if (rc) {
+ goto err;
+ }
+
+ /* Configure the RXD registers for EasyDMA access to work with buffers of
+ * specific length and address of the slave
+ */
+ regs->ADDRESS = pdata->address;
+ regs->RXD.MAXCNT = pdata->len;
+ regs->RXD.PTR = (uint32_t)pdata->buffer;
+ regs->RXD.LIST = 0;
+ /* Disable and clear interrupts */
+ regs->INTENCLR = NRF_TWIM_ALL_INTS_MASK;
+ regs->INTEN = 0;
+
+ /* Only set short for RX->STOP for last_op:1 since there is no suspend short
+ * available in nrf52832
+ */
+ if (last_op) {
+ regs->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
+ }
+
+ /* Starts an I2C transaction using TWIM/EasyDMA */
+ rc = hal_i2c_handle_transact_start(i2c, I2C_READ, start + timo);
+ if (rc) {
+ goto err;
+ }
+
+ /* Ends an I2C transaction using TWIM/EasyDMA */
+ rc = hal_i2c_handle_transact_end(regs, I2C_READ, start, start + timo, last_op);
+ if (rc) {
+ goto err;
+ }
+
+ /* Save the current last op to detect bus errors later */
+ i2c->nhi_prev_last_op = last_op;
+
+ return 0;
+err:
+ return hal_i2c_handle_errors(regs, rc);
}
int