You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by an...@apache.org on 2019/01/09 12:31:54 UTC

[mynewt-core] branch master updated (d75dce4 -> 5a5d530)

This is an automated email from the ASF dual-hosted git repository.

andk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git.


    from d75dce4  Merge pull request #1590 from JuulLabs/bq27z561_flash_write
     new c75848e  hw/bus: Add I2C bus driver for nRF52xxx TWIM (DMA)
     new 5a5d530  hw/bus/i2c_nrf52_twim: Fix minor code issues

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../include/bus/drivers/i2c_nrf52_twim.h}          |  16 +-
 hw/bus/drivers/{i2c_hal => i2c_nrf52_twim}/pkg.yml |   4 +-
 hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c | 659 +++++++++++++++++++++
 .../bus/drivers/i2c_nrf52_twim}/syscfg.yml         |  20 +-
 hw/mcu/nordic/nrf52xxx/pkg.yml                     |   5 +-
 hw/mcu/nordic/nrf52xxx/src/nrf52_periph.c          |  16 +
 hw/mcu/nordic/nrf52xxx/syscfg.yml                  |   6 +
 7 files changed, 704 insertions(+), 22 deletions(-)
 copy hw/bus/drivers/{i2c_hal/include/bus/drivers/i2c_hal.h => i2c_nrf52_twim/include/bus/drivers/i2c_nrf52_twim.h} (78%)
 copy hw/bus/drivers/{i2c_hal => i2c_nrf52_twim}/pkg.yml (90%)
 create mode 100644 hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c
 copy {mgmt/oicmgr => hw/bus/drivers/i2c_nrf52_twim}/syscfg.yml (65%)


[mynewt-core] 01/02: hw/bus: Add I2C bus driver for nRF52xxx TWIM (DMA)

Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit c75848e1a7b2121e0b27f89713f1740f69d2a2e8
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Mon Dec 17 18:30:11 2018 +0100

    hw/bus: Add I2C bus driver for nRF52xxx TWIM (DMA)
    
    This adds optimized I2C bus driver which can be used with nRF52xxx MCUs
    instead of default i2c_hal driver.
    
    The difference here is that it uses TWIM instead of TWI which means it
    uses DMA for transfer in the background and is synchronized by semaphore
    instead of using busy loops. This allows other tasks to execute while
    transfer is ongoing.
    
    Driver can be automatically enabled with new syscfg in MCU package:
        MCU_BUS_DRIVER_I2C_USE_TWIM: 1
    
    The only limitation here compared to i2c_hal driver is that it's not
    possible to make a read operation with NOSTOP flag. This is due to
    missing shortcut in TWIM peripheral to trigger TASK_SUSPEND on
    EVENT_LASTRX. It would be possible to workaround this with PPI, however
    I do not think read with NOSTOP is really that useful - i2c_hal does
    support this properly and should be used instead if needed.
    
    Workarounds implemented in hal_i2c are also present in driver as it
    looks like TWIM also suffers from the same lockup issue. For debugging
    purposes stats were added which count how many times each workaround
    has been applied.
---
 .../include/bus/drivers/i2c_nrf52_twim.h           |  69 +++
 hw/bus/drivers/i2c_nrf52_twim/pkg.yml              |  28 +
 hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c | 659 +++++++++++++++++++++
 hw/bus/drivers/i2c_nrf52_twim/syscfg.yml           |  30 +
 hw/mcu/nordic/nrf52xxx/pkg.yml                     |   5 +-
 hw/mcu/nordic/nrf52xxx/src/nrf52_periph.c          |  16 +
 hw/mcu/nordic/nrf52xxx/syscfg.yml                  |   6 +
 7 files changed, 812 insertions(+), 1 deletion(-)

diff --git a/hw/bus/drivers/i2c_nrf52_twim/include/bus/drivers/i2c_nrf52_twim.h b/hw/bus/drivers/i2c_nrf52_twim/include/bus/drivers/i2c_nrf52_twim.h
new file mode 100644
index 0000000..8b2f1f0
--- /dev/null
+++ b/hw/bus/drivers/i2c_nrf52_twim/include/bus/drivers/i2c_nrf52_twim.h
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef HW_BUS_DRIVERS_I2C_NRF52_TWIM_H_
+#define HW_BUS_DRIVERS_I2C_NRF52_TWIM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include "os/os_dev.h"
+#include "bus/drivers/i2c_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Initialize os_dev as I2C bus device using nRF52xxx TWIM peripheral
+ *
+ * This can be passed as a parameter to os_dev_create() when creating os_dev
+ * object for I2C device, however it's recommended to create devices using helper
+ * like bus_i2c_hal_dev_create().
+ *
+ * @param node  Node device object
+ * @param arg   Node configuration struct (struct bus_node_cfg)
+ */
+int
+bus_i2c_nrf52_twim_dev_init_func(struct os_dev *odev, void *arg);
+
+/**
+ * Create I2C bus device using nRF52xxx TWIM peripheral
+ *
+ * This is a convenient helper and recommended way to create os_dev for bus I2C
+ * device instead of calling os_dev_create() directly.
+ *
+ * @param name  Name of device
+ * @param dev   Device state object
+ * @param cfg   Configuration
+ */
+static inline int
+bus_i2c_nrf52_twim_dev_create(const char *name, struct bus_i2c_dev *dev,
+                       struct bus_i2c_dev_cfg *cfg)
+{
+    struct os_dev *odev = (struct os_dev *)dev;
+
+    return os_dev_create(odev, name, OS_DEV_INIT_PRIMARY, 0,
+                         bus_i2c_nrf52_twim_dev_init_func, cfg);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HW_BUS_DRIVERS_I2C_NRF52_TWIM_H_ */
diff --git a/hw/bus/drivers/i2c_nrf52_twim/pkg.yml b/hw/bus/drivers/i2c_nrf52_twim/pkg.yml
new file mode 100644
index 0000000..12fa494
--- /dev/null
+++ b/hw/bus/drivers/i2c_nrf52_twim/pkg.yml
@@ -0,0 +1,28 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: hw/bus/drivers/i2c_nrf52_twim
+pkg.description: I2C bus driver using nRF52xxx TWIM peripheral
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - hw/bus
+    - hw/bus/drivers/i2c_common
diff --git a/hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c b/hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c
new file mode 100644
index 0000000..0d21107
--- /dev/null
+++ b/hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c
@@ -0,0 +1,659 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include "defs/error.h"
+#include "hal/hal_gpio.h"
+#include "bus/bus.h"
+#include "bus/bus_debug.h"
+#include "bus/bus_driver.h"
+#include "bus/drivers/i2c_common.h"
+#include "mcu/nrf52_hal.h"
+#include "nrfx.h"
+
+#define TWIM_GPIO_PIN_CNF \
+    ((GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |  \
+     (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) |      \
+     (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |      \
+     (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |   \
+     (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos))
+
+#if MYNEWT_VAL(I2C_NRF52_TWIM_STAT)
+STATS_SECT_START(twim_stats_section)
+    STATS_SECT_ENTRY(sda_lo_err)        /* SDA pulled low on r/w */
+    STATS_SECT_ENTRY(sda_lo_err_nrecov) /* SDA pulled low on r/w (not recovered)*/
+    STATS_SECT_ENTRY(scl_hi_err)        /* SCL unresponsive */
+    STATS_SECT_ENTRY(scl_hi_err_nrecov) /* SCL unresponsive (not recovered)*/
+STATS_SECT_END
+
+STATS_NAME_START(twim_stats_section)
+    STATS_NAME(twim_stats_section, sda_lo_err)
+    STATS_NAME(twim_stats_section, sda_lo_err_nrecov)
+    STATS_NAME(twim_stats_section, scl_hi_err)
+    STATS_NAME(twim_stats_section, scl_hi_err_nrecov)
+STATS_NAME_END(twim_stats_section)
+#endif
+
+struct twim {
+    NRF_TWIM_Type *nrf_twim;
+    int irqn;
+    void (* isr)(void);
+};
+
+static void twim0_irq_handler(void);
+static void twim1_irq_handler(void);
+
+static const struct twim twims[TWIM_COUNT] = {
+    {
+        .nrf_twim = NRF_TWIM0,
+        .irqn = SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn,
+        .isr = twim0_irq_handler,
+    },
+    {
+        .nrf_twim = NRF_TWIM1,
+        .irqn = SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn,
+        .isr = twim1_irq_handler,
+    },
+};
+
+struct twim_dev_data {
+    struct os_sem sem;
+    uint32_t errorsrc;
+    bool suspended;
+
+#if MYNEWT_VAL(I2C_NRF52_TWIM_STAT)
+    STATS_SECT_DECL(twim_stats_section) stats;
+#endif
+};
+
+static struct bus_i2c_dev *twim_devs[TWIM_COUNT];
+static struct twim_dev_data twim_devs_data[TWIM_COUNT];
+
+static void
+twim_irq_handler(struct bus_i2c_dev *dev)
+{
+    NRF_TWIM_Type *nrf_twim = twims[dev->cfg.i2c_num].nrf_twim;
+    struct twim_dev_data *dd = &twim_devs_data[dev->cfg.i2c_num];
+
+    nrf_twim->INTEN = 0;
+
+    if (nrf_twim->EVENTS_STOPPED) {
+        nrf_twim->EVENTS_STOPPED = 0;
+    }
+
+    if (nrf_twim->EVENTS_SUSPENDED) {
+        nrf_twim->EVENTS_SUSPENDED = 0;
+    }
+
+    if (nrf_twim->EVENTS_ERROR) {
+        nrf_twim->EVENTS_ERROR = 0;
+    }
+
+    dd->errorsrc = nrf_twim->ERRORSRC;
+    nrf_twim->ERRORSRC = dd->errorsrc;
+
+    os_sem_release(&twim_devs_data[dev->cfg.i2c_num].sem);
+}
+
+static void
+twim0_irq_handler(void)
+{
+    os_trace_isr_enter();
+
+    assert(twim_devs[0]);
+    twim_irq_handler(twim_devs[0]);
+
+    os_trace_isr_exit();
+}
+
+static void
+twim1_irq_handler(void)
+{
+    os_trace_isr_enter();
+
+    assert(twim_devs[1]);
+    twim_irq_handler(twim_devs[1]);
+
+    os_trace_isr_exit();
+}
+
+static int
+nrf_twim_translate_twim(int twim_err)
+{
+    if (twim_err & TWIM_ERRORSRC_DNACK_Msk) {
+        return SYS_EREMOTEIO;
+    } else if (twim_err & TWIM_ERRORSRC_ANACK_Msk) {
+        return SYS_ENOENT;
+    } else if (twim_err & TWIM_ERRORSRC_OVERRUN_Msk) {
+        return SYS_EIO;
+    }
+
+    return SYS_EUNKNOWN;
+}
+
+static inline NRF_TWIM_Type *
+nrf_twim_resolve(int twim_no)
+{
+    if ((twim_no < 0) || (twim_no >= TWIM_COUNT)) {
+        return NULL;
+    }
+
+    return twims[twim_no].nrf_twim;
+}
+
+static void
+nrf_twim_delay_us(uint32_t us)
+{
+    asm volatile (".syntax unified          \n"
+                  "loop: subs %0, %0, #1    \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      nop                \n"
+                  "      bne loop           \n"
+                  : "+r" (us));
+}
+
+static void
+nrf_twim_fix_sda(NRF_TWIM_Type *nrf_twim, struct twim_dev_data *dd)
+{
+    NRF_GPIO_Type *nrf_gpio;
+    int pin_scl;
+    int pin_sda;
+    int i;
+
+    /*
+     * TWIM controller won't start when SDA line is pulled low. While unlikely
+     * to happen, it has been observed that if MCU is reset while transmission
+     * from other device is in progress, such device may be stuck with SDA pulled
+     * low as it is waiting for clock pulses on SCL line. To avoid this we just
+     * check SDA line and add extra SCL pulses in sw if needed.
+     */
+
+    pin_scl = nrf_twim->PSEL.SCL;
+    pin_sda = nrf_twim->PSEL.SDA;
+
+    if (hal_gpio_read(pin_sda)) {
+        return;
+    }
+
+#if MYNEWT_VAL(I2C_NRF52_TWIM_STAT)
+    STATS_INC(dd->stats, sda_lo_err);
+#endif
+
+    nrf_twim->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
+
+    nrf_gpio = HAL_GPIO_PORT(pin_scl);
+    nrf_gpio->DIRSET = 1 << HAL_GPIO_INDEX(pin_scl);
+
+    /* Toggle SCL until SDA is cleared */
+    for (i = 0; i < 8; i++) {
+        hal_gpio_write(pin_scl, 0);
+        nrf_twim_delay_us(4);
+        hal_gpio_write(pin_scl, 1);
+        nrf_twim_delay_us(4);
+
+        if (hal_gpio_read(pin_sda)) {
+            break;
+        }
+    }
+
+    nrf_gpio->DIRCLR = 1 << HAL_GPIO_INDEX(pin_scl);
+
+    nrf_gpio = HAL_GPIO_PORT(pin_sda);
+    nrf_gpio->DIRSET = 1 << HAL_GPIO_INDEX(pin_sda);
+
+    /* Make proper STOP condition */
+    hal_gpio_write(pin_sda, 0);
+    nrf_twim_delay_us(4);
+    hal_gpio_write(pin_sda, 1);
+
+    nrf_gpio->DIRCLR = 1 << HAL_GPIO_INDEX(pin_sda);
+
+    nrf_twim->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
+
+#if MYNEWT_VAL(I2C_NRF52_TWIM_STAT)
+    if (!hal_gpio_read(pin_sda)) {
+        STATS_INC(dd->stats, sda_lo_err_nrecov);
+    }
+#endif
+}
+
+static void
+nrf_twim_start_task(NRF_TWIM_Type *nrf_twim, struct twim_dev_data *dd,
+                    __O uint32_t *task_start, __IO uint32_t *event_last)
+{
+    static const int max_attempt = 2;
+    uint32_t end_ticks;
+    int attempt = 1;
+    int pin_scl;
+
+    pin_scl = nrf_twim->PSEL.SCL;
+
+    /*
+     * TWIM controller seems to have the same issue as TWI controller which makes
+     * it unresponsive on glitch on I2C bus. It has been observed that LP5523
+     * releases SDA line mid-ack which looks like premature STOP condition on bus
+     * followed by STOP condition from TWIM controller. After this sequence,
+     * TWIM controller needs to be reset in order to work properly.
+     *
+     * To workaround this problem we can just check if SCL line is pulled low
+     * after triggering start task as this indicates some activity and means
+     * that controller is responsive.
+     */
+
+    do {
+        *event_last = 0;
+        *task_start = 1;
+
+        end_ticks = os_cputime_get32() +
+                    os_cputime_usecs_to_ticks(MYNEWT_VAL(I2C_NRF52_TWIM_SCL_RECOVERY_DELAY_USEC));
+
+        do {
+            /*
+             * Wait for either low state on SCL or last byte event, in case
+             * we started polling after activity on bus has already finished.
+             */
+            if (!hal_gpio_read(pin_scl) || *event_last) {
+                return;
+            }
+        } while (CPUTIME_LT(os_cputime_get32(), end_ticks));
+
+#if MYNEWT_VAL(I2C_NRF52_TWIM_STAT)
+        if (attempt == 1) {
+            STATS_INC(dd->stats, scl_hi_err);
+        }
+#endif
+
+        attempt++;
+
+        if (attempt > max_attempt) {
+            break;
+        }
+
+        /*
+         * Need to explicitly stop here as otherwise TWIM will send address on
+         * the bus just after being enabled again.
+         */
+        nrf_twim->TASKS_STOP = 1;
+        nrf_twim->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
+        nrf_twim->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
+    } while (1);
+
+#if MYNEWT_VAL(I2C_NRF52_TWIM_STAT)
+    STATS_INC(dd->stats, scl_hi_err_nrecov);
+#endif
+}
+
+static int
+bus_i2c_nrf52_twim_init_node(struct bus_dev *bdev, struct bus_node *bnode, void *arg)
+{
+    struct bus_i2c_node *node = (struct bus_i2c_node *)bnode;
+    struct bus_i2c_node_cfg *cfg = arg;
+
+    BUS_DEBUG_POISON_NODE(node);
+
+    node->freq = cfg->freq;
+    node->addr = cfg->addr;
+    node->quirks = cfg->quirks;
+
+    return 0;
+}
+
+static int
+bus_i2c_nrf52_twim_enable(struct bus_dev *bdev)
+{
+    struct bus_i2c_dev *dev = (struct bus_i2c_dev *)bdev;
+    NRF_TWIM_Type *nrf_twim;
+
+    BUS_DEBUG_VERIFY_DEV(dev);
+
+    nrf_twim = twims[dev->cfg.i2c_num].nrf_twim;
+
+    nrf_twim->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
+    nrf_twim->INTEN = 0;
+
+    return 0;
+}
+
+static int
+bus_i2c_nrf52_twim_configure(struct bus_dev *bdev, struct bus_node *bnode)
+{
+    struct bus_i2c_dev *dev = (struct bus_i2c_dev *)bdev;
+    struct bus_i2c_node *node = (struct bus_i2c_node *)bnode;
+    struct bus_i2c_node *current_node = (struct bus_i2c_node *)bdev->configured_for;
+    NRF_TWIM_Type *nrf_twim;
+    int rc;
+
+    BUS_DEBUG_VERIFY_DEV(dev);
+    BUS_DEBUG_VERIFY_NODE(node);
+
+    nrf_twim = twims[dev->cfg.i2c_num].nrf_twim;
+
+    nrf_twim->ADDRESS = node->addr;
+
+    if (current_node && (current_node->freq == node->freq)) {
+        return 0;
+    }
+
+    rc = 0;
+
+    switch (node->freq) {
+    case 100:
+        nrf_twim->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K100;
+        break;
+    case 250:
+        nrf_twim->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K250;
+        break;
+    case 380:
+        nrf_twim->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K380;
+        break;
+    case 400:
+        nrf_twim->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K400;
+        break;
+    default:
+        rc = SYS_EIO;
+    }
+
+    return rc;
+}
+
+
+static int
+bus_i2c_nrf52_twim_read(struct bus_dev *bdev, struct bus_node *bnode,
+                        uint8_t *buf, uint16_t length, os_time_t timeout,
+                        uint16_t flags)
+{
+    struct bus_i2c_dev *dev = (struct bus_i2c_dev *)bdev;
+    struct bus_i2c_node *node = (struct bus_i2c_node *)bnode;
+    struct twim_dev_data *dd;
+    NRF_TWIM_Type *nrf_twim;
+    int rc;
+
+    BUS_DEBUG_VERIFY_DEV(dev);
+    BUS_DEBUG_VERIFY_NODE(node);
+
+    if (flags & BUS_F_NOSTOP) {
+        /*
+         * There's no shortcut available for LASTRX->SUSPEND so we can only
+         * stop after receiving last byte - return not supported if NOSTOP was
+         * requested for this read.
+         *
+         * XXX we may use PPI to workaround for missing shortcut but it's
+         * probably not really that useful and not worth the effort.
+         */
+        return SYS_ENOTSUP;
+    }
+
+    nrf_twim = twims[dev->cfg.i2c_num].nrf_twim;
+    dd = &twim_devs_data[dev->cfg.i2c_num];
+
+    if (!dd->suspended) {
+        nrf_twim_fix_sda(nrf_twim, dd);
+    }
+
+    nrf_twim->RXD.PTR = (uint32_t)buf;
+    nrf_twim->RXD.MAXCNT = length;
+    nrf_twim->RXD.LIST = 0;
+    nrf_twim->INTEN = TWIM_INTEN_ERROR_Msk | TWIM_INTENCLR_STOPPED_Msk;
+    nrf_twim->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
+
+    nrf_twim->EVENTS_STOPPED = 0;
+    nrf_twim->EVENTS_ERROR = 0;
+    nrf_twim->EVENTS_SUSPENDED = 0;
+    nrf_twim->EVENTS_RXSTARTED = 0;
+    nrf_twim->TASKS_RESUME = 1;
+
+    nrf_twim_start_task(nrf_twim, dd, &nrf_twim->TASKS_STARTRX,
+                        &nrf_twim->EVENTS_LASTRX);
+
+    rc = os_sem_pend(&twim_devs_data[dev->cfg.i2c_num].sem, timeout);
+    nrf_twim->INTEN = 0;
+    if (rc == OS_TIMEOUT) {
+        rc = SYS_ETIMEOUT;
+    } else if (rc) {
+        rc = SYS_EUNKNOWN;
+    } else if (dd->errorsrc) {
+        rc = nrf_twim_translate_twim(dd->errorsrc);
+    }
+
+    if (rc) {
+        nrf_twim->TASKS_RESUME = 1;
+        nrf_twim->TASKS_STOP = 1;
+    }
+
+    dd->suspended = false;
+
+    return rc;
+}
+
+static int
+bus_i2c_nrf52_twim_write(struct bus_dev *bdev, struct bus_node *bnode,
+                         const uint8_t *buf, uint16_t length, os_time_t timeout,
+                         uint16_t flags)
+{
+    struct bus_i2c_dev *dev = (struct bus_i2c_dev *)bdev;
+    struct bus_i2c_node *node = (struct bus_i2c_node *)bnode;
+    struct twim_dev_data *dd;
+    NRF_TWIM_Type *nrf_twim;
+    uint8_t last_op;
+    int rc;
+
+    BUS_DEBUG_VERIFY_DEV(dev);
+    BUS_DEBUG_VERIFY_NODE(node);
+
+    last_op = !(flags & BUS_F_NOSTOP);
+
+    nrf_twim = twims[dev->cfg.i2c_num].nrf_twim;
+    dd = &twim_devs_data[dev->cfg.i2c_num];
+
+    if (!dd->suspended) {
+        nrf_twim_fix_sda(nrf_twim, dd);
+    }
+
+    nrf_twim->TXD.MAXCNT = length;
+    nrf_twim->INTEN = 0;
+
+    nrf_twim->TXD.PTR = (uint32_t)buf;
+    nrf_twim->TXD.LIST = 0;
+    nrf_twim->INTENSET = TWIM_INTEN_ERROR_Msk;
+    if (last_op) {
+        nrf_twim->INTENSET = TWIM_INTENSET_STOPPED_Msk;
+        nrf_twim->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
+    } else {
+        nrf_twim->INTENSET = TWIM_INTENSET_SUSPENDED_Msk;
+        nrf_twim->SHORTS = TWIM_SHORTS_LASTTX_SUSPEND_Msk;
+    }
+
+    nrf_twim->EVENTS_ERROR = 0;
+    nrf_twim->EVENTS_STOPPED = 0;
+    nrf_twim->EVENTS_SUSPENDED = 0;
+    nrf_twim->EVENTS_TXSTARTED = 0;
+    nrf_twim->TASKS_RESUME = 1;
+
+    nrf_twim_start_task(nrf_twim, dd, &nrf_twim->TASKS_STARTTX,
+                        &nrf_twim->EVENTS_LASTTX);
+
+    rc = os_sem_pend(&twim_devs_data[dev->cfg.i2c_num].sem, timeout);
+    nrf_twim->INTEN = 0;
+    if (rc == OS_TIMEOUT) {
+        rc = SYS_ETIMEOUT;
+    } else if (rc) {
+        rc = SYS_EUNKNOWN;
+    } else if (dd->errorsrc) {
+        rc = nrf_twim_translate_twim(dd->errorsrc);
+    }
+
+    if (rc) {
+        nrf_twim->TASKS_RESUME = 1;
+        nrf_twim->TASKS_STOP = 1;
+    }
+
+    dd->suspended = !rc && !last_op;
+
+    return rc;
+}
+
+static int bus_i2c_nrf52_twim_disable(struct bus_dev *bdev)
+{
+    struct bus_i2c_dev *dev = (struct bus_i2c_dev *)bdev;
+    NRF_TWIM_Type *nrf_twim;
+
+    BUS_DEBUG_VERIFY_DEV(dev);
+
+    nrf_twim = twims[dev->cfg.i2c_num].nrf_twim;
+    nrf_twim->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
+
+    return 0;
+}
+
+static const struct bus_dev_ops bus_i2c_nrf52_twim_ops = {
+    .init_node = bus_i2c_nrf52_twim_init_node,
+    .enable = bus_i2c_nrf52_twim_enable,
+    .configure = bus_i2c_nrf52_twim_configure,
+    .read = bus_i2c_nrf52_twim_read,
+    .write = bus_i2c_nrf52_twim_write,
+    .disable = bus_i2c_nrf52_twim_disable,
+};
+
+int
+bus_i2c_nrf52_twim_dev_init_func(struct os_dev *odev, void *arg)
+{
+    struct bus_i2c_dev *dev = (struct bus_i2c_dev *)odev;
+    struct bus_i2c_dev_cfg *cfg = arg;
+    const struct twim *twim;
+    struct twim_dev_data *dd;
+    NRF_TWIM_Type *nrf_twim;
+    NRF_GPIO_Type *nrf_gpio;
+#if MYNEWT_VAL(I2C_NRF52_TWIM_STAT)
+    char *stats_name;
+#endif
+    int rc;
+
+    BUS_DEBUG_POISON_DEV(dev);
+
+    if (twim_devs[cfg->i2c_num]) {
+        return SYS_EALREADY;
+    }
+
+    nrf_twim = nrf_twim_resolve(cfg->i2c_num);
+    if (!nrf_twim) {
+        return SYS_ENODEV;
+    }
+
+    twim = &twims[cfg->i2c_num];
+    dd = &twim_devs_data[cfg->i2c_num];
+
+    dev->cfg = *cfg;
+    twim_devs[cfg->i2c_num] = dev;
+
+    /*
+     * Setup GPIOs for SCL and SDA so they are in proper state when TWIM
+     * controller is disabled.
+     */
+    nrf_gpio = HAL_GPIO_PORT(cfg->pin_scl);
+    nrf_gpio->PIN_CNF[HAL_GPIO_INDEX(cfg->pin_scl)] = TWIM_GPIO_PIN_CNF;
+    nrf_gpio = HAL_GPIO_PORT(cfg->pin_sda);
+    nrf_gpio->PIN_CNF[HAL_GPIO_INDEX(cfg->pin_sda)] = TWIM_GPIO_PIN_CNF;
+    hal_gpio_write(cfg->pin_scl, 1);
+    hal_gpio_write(cfg->pin_sda, 1);
+
+    NVIC_DisableIRQ(twim->irqn);
+    NVIC_SetVector(twim->irqn, (uint32_t)twim->isr);
+    NVIC_SetPriority(twim->irqn, (1 << __NVIC_PRIO_BITS) - 1);
+    NVIC_ClearPendingIRQ(twim->irqn);
+    NVIC_EnableIRQ(twim->irqn);
+
+    nrf_twim->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
+    nrf_twim->PSEL.SCL = cfg->pin_scl;
+    nrf_twim->PSEL.SDA = cfg->pin_sda;
+    nrf_twim->FREQUENCY = 0;
+
+    nrf_twim_fix_sda(nrf_twim, dd);
+
+    os_sem_init(&dd->sem, 0);
+
+#if MYNEWT_VAL(I2C_NRF52_TWIM_STAT)
+    asprintf(&stats_name, "i2c_nrf52_twim%d", cfg->i2c_num);
+    /* XXX should we assert or return error on failure? */
+    stats_init_and_reg(STATS_HDR(dd->stats),
+                       STATS_SIZE_INIT_PARMS(dd->stats, STATS_SIZE_32),
+                       STATS_NAME_INIT_PARMS(twim_stats_section),
+                       stats_name);
+#endif
+
+    rc = bus_dev_init_func(odev, (void*)&bus_i2c_nrf52_twim_ops);
+    assert(rc == 0);
+
+    return 0;
+}
diff --git a/hw/bus/drivers/i2c_nrf52_twim/syscfg.yml b/hw/bus/drivers/i2c_nrf52_twim/syscfg.yml
new file mode 100644
index 0000000..6639015
--- /dev/null
+++ b/hw/bus/drivers/i2c_nrf52_twim/syscfg.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+    I2C_NRF52_TWIM_STAT:
+        description: >
+            Enable statistics for driver errors and applied workarounds.
+        value: 1
+    I2C_NRF52_TWIM_SCL_RECOVERY_DELAY_USEC:
+        description: >
+            Time to wait for activity on SCL line after triggering start task
+            before restarting TWIM controller. This is to recover from state
+            where controller is unresponsive due to glitch on I2C bus.
+            Note: Default value seems to work fine, but may need to be tuned.
+        value: 500
diff --git a/hw/mcu/nordic/nrf52xxx/pkg.yml b/hw/mcu/nordic/nrf52xxx/pkg.yml
index 10e127b..ce19ab4 100644
--- a/hw/mcu/nordic/nrf52xxx/pkg.yml
+++ b/hw/mcu/nordic/nrf52xxx/pkg.yml
@@ -31,8 +31,11 @@ pkg.deps:
     - "@apache-mynewt-core/hw/hal"
 
 pkg.deps.BUS_DRIVER_PRESENT:
-    - "@apache-mynewt-core/hw/bus/drivers/i2c_hal"
     - "@apache-mynewt-core/hw/bus/drivers/spi_hal"
+pkg.deps.'BUS_DRIVER_PRESENT && MCU_BUS_DRIVER_I2C_USE_TWIM':
+    - "@apache-mynewt-core/hw/bus/drivers/i2c_nrf52_twim"
+pkg.deps.'BUS_DRIVER_PRESENT && !MCU_BUS_DRIVER_I2C_USE_TWIM':
+    - "@apache-mynewt-core/hw/bus/drivers/i2c_hal"
 
 # NRF52810 doesn't support SPI/I2C (Use SPIM/I2CM instead)
 pkg.ign_files.BSP_NRF52810:
diff --git a/hw/mcu/nordic/nrf52xxx/src/nrf52_periph.c b/hw/mcu/nordic/nrf52xxx/src/nrf52_periph.c
index 8af9612..71f636c 100644
--- a/hw/mcu/nordic/nrf52xxx/src/nrf52_periph.c
+++ b/hw/mcu/nordic/nrf52xxx/src/nrf52_periph.c
@@ -27,8 +27,12 @@
 #if MYNEWT_VAL(BUS_DRIVER_PRESENT)
 #include "bus/bus.h"
 #if MYNEWT_VAL(I2C_0) || MYNEWT_VAL(I2C_1)
+#if MYNEWT_VAL(MCU_BUS_DRIVER_I2C_USE_TWIM)
+#include "bus/drivers/i2c_nrf52_twim.h"
+#else
 #include "bus/drivers/i2c_hal.h"
 #endif
+#endif
 #if MYNEWT_VAL(SPI_0_MASTER) || MYNEWT_VAL(SPI_1_MASTER) || MYNEWT_VAL(SPI_2_MASTER)
 #include "bus/drivers/spi_hal.h"
 #endif
@@ -337,9 +341,15 @@ nrf52_periph_create_i2c(void)
 
 #if MYNEWT_VAL(I2C_0)
 #if MYNEWT_VAL(BUS_DRIVER_PRESENT)
+#if MYNEWT_VAL(MCU_BUS_DRIVER_I2C_USE_TWIM)
+    rc = bus_i2c_nrf52_twim_dev_create("i2c0", &i2c0_bus,
+                                       (struct bus_i2c_dev_cfg *)&i2c0_cfg);
+    assert(rc == 0);
+#else
     rc = bus_i2c_hal_dev_create("i2c0", &i2c0_bus,
                                 (struct bus_i2c_dev_cfg *)&i2c0_cfg);
     assert(rc == 0);
+#endif
 #else
     rc = hal_i2c_init(0, (void *)&hal_i2c0_cfg);
     assert(rc == 0);
@@ -347,9 +357,15 @@ nrf52_periph_create_i2c(void)
 #endif
 #if MYNEWT_VAL(I2C_1)
 #if MYNEWT_VAL(BUS_DRIVER_PRESENT)
+#if MYNEWT_VAL(MCU_BUS_DRIVER_I2C_USE_TWIM)
+    rc = bus_i2c_nrf52_twim_dev_create("i2c1", &i2c1_bus,
+                                       (struct bus_i2c_dev_cfg *)&i2c1_cfg);
+    assert(rc == 0);
+#else
     rc = bus_i2c_hal_dev_create("i2c1", &i2c1_bus,
                                 (struct bus_i2c_dev_cfg *)&i2c1_cfg);
     assert(rc == 0);
+#endif
 #else
     rc = hal_i2c_init(1, (void *)&hal_i2c1_cfg);
     assert(rc == 0);
diff --git a/hw/mcu/nordic/nrf52xxx/syscfg.yml b/hw/mcu/nordic/nrf52xxx/syscfg.yml
index 31820b9..50c8bdf 100644
--- a/hw/mcu/nordic/nrf52xxx/syscfg.yml
+++ b/hw/mcu/nordic/nrf52xxx/syscfg.yml
@@ -45,6 +45,12 @@ syscfg.defs:
             Note: Default value seems to work fine, but may need to be tuned.
         value: 100
 
+    MCU_BUS_DRIVER_I2C_USE_TWIM:
+        description: >
+            Enables usage of i2c_nrf52_twim bus driver for I2C.
+            If disabled, standard i2c_hal driver is used.
+        value: 0
+
 # MCU peripherals definitions
     I2C_0:
         description: 'Enable nRF52xxx I2C (TWI) 0'


[mynewt-core] 02/02: hw/bus/i2c_nrf52_twim: Fix minor code issues

Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 5a5d530955a2648bde9a972d768e11c217de2f5b
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Wed Jan 9 13:12:58 2019 +0100

    hw/bus/i2c_nrf52_twim: Fix minor code issues
---
 hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c b/hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c
index 0d21107..08d5618 100644
--- a/hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c
+++ b/hw/bus/drivers/i2c_nrf52_twim/src/i2c_nrf52_twim.c
@@ -108,7 +108,7 @@ twim_irq_handler(struct bus_i2c_dev *dev)
     dd->errorsrc = nrf_twim->ERRORSRC;
     nrf_twim->ERRORSRC = dd->errorsrc;
 
-    os_sem_release(&twim_devs_data[dev->cfg.i2c_num].sem);
+    os_sem_release(&dd->sem);
 }
 
 static void
@@ -293,7 +293,7 @@ static void
 nrf_twim_start_task(NRF_TWIM_Type *nrf_twim, struct twim_dev_data *dd,
                     __O uint32_t *task_start, __IO uint32_t *event_last)
 {
-    static const int max_attempt = 2;
+    const int max_attempt = 2;
     uint32_t end_ticks;
     int attempt = 1;
     int pin_scl;
@@ -312,7 +312,7 @@ nrf_twim_start_task(NRF_TWIM_Type *nrf_twim, struct twim_dev_data *dd,
      * that controller is responsive.
      */
 
-    do {
+    while (1) {
         *event_last = 0;
         *task_start = 1;
 
@@ -348,7 +348,7 @@ nrf_twim_start_task(NRF_TWIM_Type *nrf_twim, struct twim_dev_data *dd,
         nrf_twim->TASKS_STOP = 1;
         nrf_twim->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
         nrf_twim->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
-    } while (1);
+    };
 
 #if MYNEWT_VAL(I2C_NRF52_TWIM_STAT)
     STATS_INC(dd->stats, scl_hi_err_nrecov);
@@ -477,7 +477,7 @@ bus_i2c_nrf52_twim_read(struct bus_dev *bdev, struct bus_node *bnode,
     nrf_twim_start_task(nrf_twim, dd, &nrf_twim->TASKS_STARTRX,
                         &nrf_twim->EVENTS_LASTRX);
 
-    rc = os_sem_pend(&twim_devs_data[dev->cfg.i2c_num].sem, timeout);
+    rc = os_sem_pend(&dd->sem, timeout);
     nrf_twim->INTEN = 0;
     if (rc == OS_TIMEOUT) {
         rc = SYS_ETIMEOUT;
@@ -544,7 +544,7 @@ bus_i2c_nrf52_twim_write(struct bus_dev *bdev, struct bus_node *bnode,
     nrf_twim_start_task(nrf_twim, dd, &nrf_twim->TASKS_STARTTX,
                         &nrf_twim->EVENTS_LASTTX);
 
-    rc = os_sem_pend(&twim_devs_data[dev->cfg.i2c_num].sem, timeout);
+    rc = os_sem_pend(&dd->sem, timeout);
     nrf_twim->INTEN = 0;
     if (rc == OS_TIMEOUT) {
         rc = SYS_ETIMEOUT;