diff --git a/boards/common/gd32v/include/cfg_i2c_default.h b/boards/common/gd32v/include/cfg_i2c_default.h new file mode 100644 index 000000000000..8b3e60f921f5 --- /dev/null +++ b/boards/common/gd32v/include/cfg_i2c_default.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup boards_common_gd32v + * @{ + * + * @file + * @brief Default I2C configuration for GD32VF103 boards + * + * @author Gunar Schorcht + */ + +#ifndef CFG_I2C_DEFAULT_H +#define CFG_I2C_DEFAULT_H + +#include "periph_cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name I2C configuration + * + * @note This board may require external pullup resistors for i2c operation. + * @{ + */ +static const i2c_conf_t i2c_config[] = { + { + .dev = I2C0, + .speed = I2C_SPEED_NORMAL, + .scl_pin = GPIO_PIN(PORT_B, 6), + .sda_pin = GPIO_PIN(PORT_B, 7), + .rcu_mask = RCU_APB1EN_I2C0EN_Msk, + .irqn = I2C0_EV_IRQn, + }, + { + .dev = I2C1, + .speed = I2C_SPEED_NORMAL, + .scl_pin = GPIO_PIN(PORT_B, 10), + .sda_pin = GPIO_PIN(PORT_B, 11), + .rcu_mask = RCU_APB1EN_I2C1EN_Msk, + .irqn = I2C1_EV_IRQn, + } +}; + +#define I2C_NUMOF ARRAY_SIZE(i2c_config) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* CFG_I2C_DEFAULT_H */ +/** @} */ diff --git a/boards/seeedstudio-gd32/Kconfig b/boards/seeedstudio-gd32/Kconfig index d81aa9f0f6ce..b0dcf5773c7d 100644 --- a/boards/seeedstudio-gd32/Kconfig +++ b/boards/seeedstudio-gd32/Kconfig @@ -14,6 +14,7 @@ config BOARD_SEEEDSTUDIO_GD32 select CPU_MODEL_GD32VF103VBT6 select BOARD_HAS_HXTAL select BOARD_HAS_LXTAL + select HAS_PERIPH_I2C select HAS_PERIPH_PWM select HAS_PERIPH_TIMER select HAS_PERIPH_UART diff --git a/boards/seeedstudio-gd32/Makefile.features b/boards/seeedstudio-gd32/Makefile.features index fd23f163dc35..7f8956607c5f 100644 --- a/boards/seeedstudio-gd32/Makefile.features +++ b/boards/seeedstudio-gd32/Makefile.features @@ -1,6 +1,7 @@ CPU_MODEL = gd32vf103vbt6 # Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/boards/seeedstudio-gd32/doc.txt b/boards/seeedstudio-gd32/doc.txt index dc5a40712658..1ddffa1445f7 100644 --- a/boards/seeedstudio-gd32/doc.txt +++ b/boards/seeedstudio-gd32/doc.txt @@ -42,7 +42,7 @@ on-board components: | UART | 2 | yes | | USART | 3 | yes | | SPI | 3 | no | -| I2C | 2 x Fast Mode 400 kHz | no | +| I2C | 2 x Fast Mode 400 kHz | yes | | I2S | 2 | no | | CAN | 2 x CAN 2.0B with up to 1 Mbps | no | | PWM | 6 Channels | yes | @@ -64,15 +64,19 @@ MCU pins and their configuration in RIOT. | MCU Pin | MCU Peripheral | RIOT Peripheral | Board Function | Remark | |:--------|:---------------|:-----------------|:---------------|:-----------------------------| -| PA0 | | | BTN0 | | +| PA0 | | BTN0 | KEY1 | | | PA9 | USART0 TX | UART_DEV(0) TX | UART TX | | | PA10 | USART0 RX | UART_DEV(0) RX | UART RX | | | PB0 | | PWM_DEV(0) CH0 | LED1 green | | | PB1 | | PWM_DEV(0) CH1 | LED2 blue | | | PB5 | | | LED0 red | | +| PB6 | I2C0 SCL | I2C_DEV(0) SCL | | | +| PB7 | I2C0 SDA | I2C_DEV(0) SDA | | | | PB8 | | PWM_DEV(1) CH0 | | N/A if CAN is used | -| PB9 | | PWM_DEV(2) CH1 | | N/A if CAN is used | -| PC13 | | | BTN1 | | +| PB9 | | PWM_DEV(1) CH1 | | N/A if CAN is used | +| PB10 | I2C1 SCL | I2C_DEV(1) SCL | | | +| PB11 | I2C1 SDA | I2C_DEV(1) SDA | | | +| PC13 | | BTN1 | KEY2 | | ## Flash the board diff --git a/boards/seeedstudio-gd32/include/periph_conf.h b/boards/seeedstudio-gd32/include/periph_conf.h index 9dfcd5a448de..64b667b112bb 100644 --- a/boards/seeedstudio-gd32/include/periph_conf.h +++ b/boards/seeedstudio-gd32/include/periph_conf.h @@ -36,6 +36,7 @@ #include "periph_cpu.h" #include "periph_common_conf.h" +#include "cfg_i2c_default.h" #include "cfg_timer_default.h" #include "cfg_uart_default.h" diff --git a/boards/sipeed-longan-nano/Kconfig b/boards/sipeed-longan-nano/Kconfig index 9fea5ef3c429..ecd1fa339fc5 100644 --- a/boards/sipeed-longan-nano/Kconfig +++ b/boards/sipeed-longan-nano/Kconfig @@ -14,6 +14,7 @@ config BOARD_SIPEED_LONGAN_NANO select CPU_MODEL_GD32VF103CBT6 select BOARD_HAS_HXTAL select BOARD_HAS_LXTAL + select HAS_PERIPH_I2C select HAS_PERIPH_PWM select HAS_PERIPH_TIMER select HAS_PERIPH_UART diff --git a/boards/sipeed-longan-nano/Makefile.features b/boards/sipeed-longan-nano/Makefile.features index 3571940267fd..1fc54d3a1887 100644 --- a/boards/sipeed-longan-nano/Makefile.features +++ b/boards/sipeed-longan-nano/Makefile.features @@ -1,6 +1,7 @@ CPU_MODEL = gd32vf103cbt6 # Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/boards/sipeed-longan-nano/doc.txt b/boards/sipeed-longan-nano/doc.txt index c72536792727..43fa392ba4f7 100644 --- a/boards/sipeed-longan-nano/doc.txt +++ b/boards/sipeed-longan-nano/doc.txt @@ -36,7 +36,7 @@ on-board components: | UART | - | yes | | USART | 3 | yes | | SPI | 3 | no | -| I2C | 2 x Fast Mode 400 kHz | no | +| I2C | 2 x Fast Mode 400 kHz | yes | | I2S | 2 | no | | CAN | 2 x CAN 2.0B with up to 1 Mbps | no | | PWM | 6 Channels | yes | @@ -58,12 +58,17 @@ MCU pins and their configuration in RIOT. | MCU Pin | MCU Peripheral | RIOT Peripheral | Board Function | Remark | |:--------|:---------------|:-----------------|:---------------|:-----------------------------| +| PA0 | BOOT0 | | BTN0 | | | PA1 | | PWM_DEV(0) CH0 | LED1 green | | | PA2 | | PWM_DEV(0) CH1 | LED2 blue | | | PA9 | USART0 TX | UART_DEV(0) TX | UART TX | | | PA10 | USART0 RX | UART_DEV(0) RX | UART RX | | +| PB6 | I2C0 SCL | I2C_DEV(0) SCL | | | +| PB7 | I2C0 SDA | I2C_DEV(0) SDA | | | | PB8 | | PWM_DEV(1) CH0 | | N/A if CAN is used | -| PB9 | | PWM_DEV(2) CH1 | | N/A if CAN is used | +| PB9 | | PWM_DEV(1) CH1 | | N/A if CAN is used | +| PB10 | I2C1 SCL | I2C_DEV(1) SCL | | | +| PB11 | I2C1 SDA | I2C_DEV(1) SDA | | | | PC13 | | | LED0 red | | ## Flashing the Device diff --git a/boards/sipeed-longan-nano/include/periph_conf.h b/boards/sipeed-longan-nano/include/periph_conf.h index d80a7d11d5d9..7b3ea51c0c2e 100644 --- a/boards/sipeed-longan-nano/include/periph_conf.h +++ b/boards/sipeed-longan-nano/include/periph_conf.h @@ -36,6 +36,7 @@ #include "periph_cpu.h" #include "periph_common_conf.h" +#include "cfg_i2c_default.h" #include "cfg_timer_default.h" #include "cfg_uart_default.h" diff --git a/cpu/gd32v/include/periph_cpu.h b/cpu/gd32v/include/periph_cpu.h index c8d59a5d920f..1effb186c767 100644 --- a/cpu/gd32v/include/periph_cpu.h +++ b/cpu/gd32v/include/periph_cpu.h @@ -24,6 +24,7 @@ #include "cpu.h" #include "clic.h" #include "kernel_defines.h" +#include "macros/units.h" #ifdef __cplusplus extern "C" { @@ -243,8 +244,10 @@ typedef struct { */ #define HAVE_I2C_SPEED_T typedef enum { - I2C_SPEED_NORMAL, /**< normal mode: ~100kbit/s */ - I2C_SPEED_FAST, /**< fast mode: ~400kbit/s */ + I2C_SPEED_LOW = KHZ(10), /**< low speed mode: ~10kit/s */ + I2C_SPEED_NORMAL = KHZ(100), /**< normal mode: ~100kbit/s */ + I2C_SPEED_FAST = KHZ(400), /**< fast mode: ~400kbit/s */ + I2C_SPEED_FAST_PLUS = MHZ(1), /**< fast plus mode: ~1Mbit/s */ } i2c_speed_t; /** @} */ #endif /* ndef DOXYGEN */ @@ -253,10 +256,12 @@ typedef enum { * @brief I2C configuration options */ typedef struct { - uint32_t addr; /**< device base address */ - gpio_t scl; /**< SCL pin */ - gpio_t sda; /**< SDA pin */ - i2c_speed_t speed; /**< I2C speed */ + I2C_Type *dev; /**< i2c device */ + i2c_speed_t speed; /**< i2c bus speed */ + gpio_t scl_pin; /**< scl pin number */ + gpio_t sda_pin; /**< sda pin number */ + uint32_t rcu_mask; /**< bit in clock enable register */ + uint8_t irqn; /**< I2C event interrupt number */ } i2c_conf_t; /** diff --git a/cpu/gd32v/include/vendor/gd32vf103_periph.h b/cpu/gd32v/include/vendor/gd32vf103_periph.h index 2b7727bb98b4..70cb840f5689 100644 --- a/cpu/gd32v/include/vendor/gd32vf103_periph.h +++ b/cpu/gd32v/include/vendor/gd32vf103_periph.h @@ -11549,8 +11549,8 @@ typedef struct { /*!< (@ 0x40002C00) WWDGT Struct //#define GPIOC_BASE 0x40011000UL //#define GPIOD_BASE 0x40011400UL //#define GPIOE_BASE 0x40011800UL -//#define I2C0_BASE 0x40005400UL -//#define I2C1_BASE 0x40005800UL +#define I2C0_BASE 0x40005400UL +#define I2C1_BASE 0x40005800UL //#define ECLIC_BASE 0xD2000000UL //#define PMU_BASE 0x40007000UL //#define RCU_BASE 0x40021000UL diff --git a/cpu/gd32v/periph/i2c.c b/cpu/gd32v/periph/i2c.c new file mode 100644 index 000000000000..dc98f92f3a19 --- /dev/null +++ b/cpu/gd32v/periph/i2c.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * 2014 FU Berlin + * 2018 Inria + * 2018 HAW Hamburg + * 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_gd32v + * @ingroup drivers_periph_i2c + * @{ + * + * @file + * @brief Low-level I2C driver implementation + * + * This driver is a modified copy of the I2C driver for the STM32F1 family. + * + * @note This implementation only implements the 7-bit addressing polling mode. + * + * @author Peter Kietzmann + * @author Hauke Petersen + * @author Thomas Eichinger + * @author Kaspar Schleiser + * @author Toon Stegen + * @author Vincent Dupont + * @author Víctor Ariño + * @author Alexandre Abadie + * @author Kevin Weiss + * @author Gunar Schorcht + * + * @} + */ + +#include +#include +#include + +#include "cpu.h" +#include "irq.h" +#include "mutex.h" +#include "pm_layered.h" +#include "panic.h" + +#include "periph/i2c.h" +#include "periph/gpio.h" +#include "periph_conf.h" + +/* Some DEBUG statements may cause delays that alter i2c functionality */ +#define ENABLE_DEBUG 0 +#include "debug.h" + +#define TICK_TIMEOUT (0xFFFF) + +#define I2C_IRQ_PRIO (1) +#define I2C_FLAG_READ (I2C_READ) +#define I2C_FLAG_WRITE (0) + +#define ERROR_FLAGS (I2C_STAT0_AERR_Msk | I2C_STAT0_LOSTARB_Msk | I2C_STAT0_BERR_Msk) + +/* static function definitions */ +static void _init(i2c_t dev); +static void _init_pins(i2c_t dev); +static void _init_clk(I2C_Type *i2c, uint32_t speed); +static void _deinit_pins(i2c_t dev); + +static int _start(I2C_Type *dev, uint8_t address_byte, uint8_t flags, + size_t length); +static int _stop(I2C_Type *dev); + +static int _is_sr1_mask_set(I2C_Type *i2c, uint32_t mask, uint8_t flags); +static inline int _wait_for_bus(I2C_Type *i2c); + +/** + * @brief Array holding one pre-initialized mutex for each I2C device + */ +static mutex_t locks[I2C_NUMOF]; + +void i2c_init(i2c_t dev) +{ + assert(dev < I2C_NUMOF); + + mutex_init(&locks[dev]); + + assert(i2c_config[dev].dev != NULL); + + /* Configure pins in idle state as open drain outputs to keep the bus lines + * in HIGH state */ + _deinit_pins(dev); + + periph_clk_en(APB1, i2c_config[dev].rcu_mask); + + _init(dev); + + periph_clk_dis(APB1, i2c_config[dev].rcu_mask); +} + +static void _init_pins(i2c_t dev) +{ + /* This is needed in case the remapped pins are used */ + if (i2c_config[dev].scl_pin == GPIO_PIN(PORT_B, 8) || + i2c_config[dev].sda_pin == GPIO_PIN(PORT_B, 9)) { + /* The remapping periph clock must first be enabled */ + RCU->APB2EN |= RCU_APB2EN_AFEN_Msk; + /* Then the remap can occur */ + AFIO->PCF0 |= AFIO_PCF0_I2C0_REMAP_Msk; + } + gpio_init_af(i2c_config[dev].scl_pin, GPIO_AF_OUT_OD); + gpio_init_af(i2c_config[dev].sda_pin, GPIO_AF_OUT_OD); +} + +static void _init_clk(I2C_Type *i2c, uint32_t speed) +{ + /* disable device and set ACK bit */ + i2c->CTL0 = I2C_CTL0_ACKEN_Msk; + /* configure I2C clock */ + i2c->CTL1 = (CLOCK_APB1 / MHZ(1)) | I2C_CTL1_ERRIE_Msk; + i2c->CKCFG = CLOCK_APB1 / (2 * speed); + i2c->RT = (CLOCK_APB1 / 1000000) + 1; + /* configure device */ + i2c->SADDR0 |= (1 << 14); /* datasheet: bit 14 should be kept 1 */ + i2c->SADDR0 &= ~I2C_SADDR0_ADDFORMAT_Msk; /* make sure we are in 7-bit address mode */ + /* Clear flags */ + i2c->STAT0 &= ~ERROR_FLAGS; + /* enable device */ + i2c->CTL0 |= I2C_CTL0_I2CEN_Msk; +} + +static void _init(i2c_t dev) +{ + I2C_Type *i2c = i2c_config[dev].dev; + + /* make peripheral soft reset */ + i2c->CTL0 |= I2C_CTL0_SRESET_Msk; + i2c->CTL0 &= ~I2C_CTL0_SRESET_Msk; + + /* configure I2C clock */ + _init_clk(i2c, i2c_config[dev].speed); +} + +static void _deinit_pins(i2c_t dev) +{ + /* GD32V doesn't support GPIO_OD_PU mode, i.e. external pull-ups required */ + gpio_init(i2c_config[dev].scl_pin, GPIO_OD); + gpio_init(i2c_config[dev].sda_pin, GPIO_OD); + gpio_set(i2c_config[dev].scl_pin); + gpio_set(i2c_config[dev].sda_pin); +} + +void i2c_acquire(i2c_t dev) +{ + assert(dev < I2C_NUMOF); + + mutex_lock(&locks[dev]); + + /* block DEEP_SLEEP mode */ + pm_block(GD32V_PM_DEEPSLEEP); + + periph_clk_en(APB1, i2c_config[dev].rcu_mask); + + /* set the alternate function of the pins */ + _init_pins(dev); + + /* enable device */ + i2c_config[dev].dev->CTL0 |= I2C_CTL0_I2CEN_Msk; +} + +void i2c_release(i2c_t dev) +{ + assert(dev < I2C_NUMOF); + + /* disable device */ + i2c_config[dev].dev->CTL0 &= ~(I2C_CTL0_I2CEN_Msk); + + _wait_for_bus(i2c_config[dev].dev); + + /* Disabling the clock switches off the I2C controller, which results in + * LOW bus lines. To avoid that the used GPIOs then draw some milliamps + * of current via the pull-up resistors, the used GPIOs are set back to + * GPIO_OD mode and HIGH. */ + _deinit_pins(dev); + + periph_clk_dis(APB1, i2c_config[dev].rcu_mask); + + /* unblock DEEP_SLEEP mode */ + pm_unblock(GD32V_PM_DEEPSLEEP); + + mutex_unlock(&locks[dev]); +} + +int i2c_read_bytes(i2c_t dev, uint16_t address, void *data, size_t length, + uint8_t flags) +{ + assert(dev < I2C_NUMOF); + + I2C_Type *i2c = i2c_config[dev].dev; + DEBUG("[i2c] read_bytes: Starting\n"); + + /* Repeated start of read operations is not supported. This is exactly the + * case if the previous transfer was a read operation (I2C_STAT1_TR == 0) + * and was not terminated by a STOP condition (I2C_STAT1_I2CBSY == 1) and + * the START condition is to be used (I2C_NOSTART == 0). + */ + if (((i2c->STAT1 & (I2C_STAT1_I2CBSY_Msk | I2C_STAT1_TR_Msk)) == I2C_STAT1_I2CBSY_Msk) && + !(flags & I2C_NOSTART)) { + return -EOPNOTSUPP; + } + + int ret = _start(i2c, (address << 1) | I2C_FLAG_READ, flags, length); + if (ret < 0) { + if (ret == -ETIMEDOUT) { + _init(dev); + } + return ret; + } + + for (size_t i = 0; i < length; i++) { + if (i + 1 == length && !(flags & I2C_NOSTOP)) { + /* If data is already in the buffer we must clear before sending + * a stop. If I2C_NOSTOP was called up to two extra bytes may be + * clocked out on the line however they get ignored in the firmware.*/ + if ((i2c->STAT0 & I2C_STAT0_RBNE_Msk) && (length == 1)) { + ((uint8_t*)data)[i] = i2c->DATA; + return _stop(i2c); + } + /* STOP must also be sent before final read */ + ret = _stop(i2c); + if (ret < 0) { + return ret; + } + } + /* Wait for reception to complete */ + ret = _is_sr1_mask_set(i2c, I2C_STAT0_RBNE_Msk, flags); + if (ret < 0) { + return ret; + } + ((uint8_t*)data)[i] = i2c->DATA; + } + DEBUG("[i2c] read_bytes: Finished reading bytes\n"); + if (flags & I2C_NOSTOP) { + return 0; + } + return _wait_for_bus(i2c); +} + +int i2c_write_bytes(i2c_t dev, uint16_t address, const void *data, + size_t length, uint8_t flags) +{ + assert(dev < I2C_NUMOF); + + int ret; + + I2C_Type *i2c = i2c_config[dev].dev; + assert(i2c != NULL); + DEBUG("[i2c] write_bytes: Starting\n"); + /* Length is 0 in start since we don't need to preset the stop bit */ + ret = _start(i2c, (address << 1) | I2C_FLAG_WRITE, flags, 0); + if (ret < 0) { + if (ret == -ETIMEDOUT) { + _init(dev); + } + return ret; + } + + /* Send out data bytes */ + for (size_t i = 0; i < length; i++) { + DEBUG("[i2c] write_bytes: Waiting for TX reg to be free\n"); + ret = _is_sr1_mask_set(i2c, I2C_STAT0_TBE_Msk, flags); + if (ret < 0) { + return ret; + } + DEBUG("[i2c] write_bytes: TX is free so send byte\n"); + i2c->DATA = ((uint8_t*)data)[i]; + } + /* Wait for tx reg to be empty so other calls will no interfere */ + ret = _is_sr1_mask_set(i2c, I2C_STAT0_TBE_Msk, flags); + if (ret < 0) { + return ret; + } + if (flags & I2C_NOSTOP) { + return 0; + } + else { + /* End transmission */ + DEBUG("[i2c] write_bytes: Ending transmission\n"); + ret = _stop(i2c); + if (ret < 0) { + return ret; + } + DEBUG("[i2c] write_bytes: STOP condition was send out\n"); + } + + return _wait_for_bus(i2c); +} + +static int _start(I2C_Type *i2c, uint8_t address_byte, uint8_t flags, + size_t length) +{ + assert(i2c != NULL); + + if ((flags & I2C_ADDR10) || + (!(i2c->STAT1 & I2C_STAT1_I2CBSY_Msk) && (flags & I2C_NOSTART))) { + return -EOPNOTSUPP; + } + + /* Clear flags */ + i2c->STAT0 &= ~ERROR_FLAGS; + + if (!(flags & I2C_NOSTART)) { + DEBUG("[i2c] start: Generate start condition\n"); + /* Generate start condition */ + i2c->CTL0 |= I2C_CTL0_START_Msk | I2C_CTL0_ACKEN_Msk; + + /* Wait for SB flag to be set */ + int ret = _is_sr1_mask_set(i2c, I2C_STAT0_SBSEND_Msk, flags & ~I2C_NOSTOP); + if (ret < 0) { + return ret; + } + DEBUG("[i2c] start: Start condition generated\n"); + + DEBUG("[i2c] start: Generating address\n"); + /* Send address and read/write flag */ + if ((i2c->STAT0 & I2C_STAT0_SBSEND_Msk)) { + i2c->DATA = (address_byte); + } + if (!(flags & I2C_NOSTOP) && length == 1) { + i2c->CTL0 &= ~(I2C_CTL0_ACKEN_Msk); + } + /* Wait for ADDR flag to be set */ + ret = _is_sr1_mask_set(i2c, I2C_STAT0_ADDSEND_Msk, flags & ~I2C_NOSTOP); + if (ret == -EIO){ + /* Since NACK happened during start it means no device connected */ + return -ENXIO; + } + + /* Wait until I2C_STAT0_ADDSEND is cleared. To clear I2C_STAT0_ADDSEND + * it is necessary to read STAT0 followed by reading STAT1 */ + while ((i2c->STAT0 & I2C_STAT0_ADDSEND_Msk) && i2c->STAT1) { } + + if (!(flags & I2C_NOSTOP) && length == 1) { + /* Stop must also be sent before final read */ + i2c->CTL0 |= (I2C_CTL0_STOP_Msk); + } + DEBUG("[i2c] start: Address generated\n"); + return ret; + } + return 0; +} + +static int _is_sr1_mask_set(I2C_Type *i2c, uint32_t mask, uint8_t flags) +{ + DEBUG("[i2c] _is_sr1_mask_set: waiting to set %04X\n", (uint16_t)mask); + uint16_t tick = TICK_TIMEOUT; + while (tick--) { + uint32_t sr1 = i2c->STAT0; + if (sr1 & I2C_STAT0_AERR_Msk) { + DEBUG("[i2c] is_sr1_mask_set: NACK received\n"); + i2c->STAT0 &= ~ERROR_FLAGS; + if (!(flags & I2C_NOSTOP)) { + _stop(i2c); + } + return -EIO; + } + if ((sr1 & I2C_STAT0_LOSTARB_Msk) || (sr1 & I2C_STAT0_BERR_Msk)) { + DEBUG("[i2c] is_sr1_mask_set: arb lost or bus ERROR_FLAGS\n"); + i2c->STAT0 &= ~ERROR_FLAGS; + _stop(i2c); + return -EAGAIN; + } + if (sr1 & mask) { + i2c->STAT0 &= ~ERROR_FLAGS; + return 0; + } + } + /* + * If timeout occurs this means a problem that must be handled on a higher + * level. A SWRST is recommended by the datasheet. + */ + i2c->STAT0 &= ~ERROR_FLAGS; + _stop(i2c); + return -ETIMEDOUT; +} + +static int _stop(I2C_Type *i2c) +{ + /* send STOP condition */ + DEBUG("[i2c] stop: Generate stop condition\n"); + i2c->CTL0 &= ~(I2C_CTL0_ACKEN_Msk); + i2c->CTL0 |= I2C_CTL0_STOP_Msk; + uint16_t tick = TICK_TIMEOUT; + while ((i2c->CTL0 & I2C_CTL0_STOP_Msk) && tick--) {} + if (!tick) { + return -ETIMEDOUT; + } + DEBUG("[i2c] stop: Stop condition succeeded\n"); + if (_wait_for_bus(i2c) < 0) { + return -ETIMEDOUT; + } + DEBUG("[i2c] stop: Bus is free\n"); + return 0; +} + +static inline int _wait_for_bus(I2C_Type *i2c) +{ + uint16_t tick = TICK_TIMEOUT; + while ((i2c->STAT1 & I2C_STAT1_I2CBSY_Msk) && tick--) {} + if (!tick) { + return -ETIMEDOUT; + } + return 0; +} diff --git a/dist/tools/doccheck/generic_exclude_patterns b/dist/tools/doccheck/generic_exclude_patterns index 4999c7e7cc44..a180398fc3f3 100644 --- a/dist/tools/doccheck/generic_exclude_patterns +++ b/dist/tools/doccheck/generic_exclude_patterns @@ -20,6 +20,8 @@ warning: Member HDC1000_PARAM_ADDR \(macro definition\) of warning: Member HDC1000_PARAM_I2C \(macro definition\) of warning: Member HDC1000_PARAM_RENEW_INTERVAL \(macro definition\) of warning: Member HDC1000_PARAM_RES \(macro definition\) of +warning: Member i2c_config\[\] \(variable\) of +warning: Member I2C_NUMOF \(macro definition\) of warning: Member LED[0-9]_ENABLE_PORT \(macro definition\) of warning: Member LED[0-9]_IS_INVERTED \(macro definition\) of warning: Member LED[0-9]_MASK \(macro definition\) of