mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
c3500eb6e0
This will not change generated binaries, only the Doxygen output and source code will have less typos.
754 lines
22 KiB
C
754 lines
22 KiB
C
/*
|
|
* 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 cpu_gd32v
|
|
* @ingroup drivers_periph_i2c
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Low-level I2C driver implementation for GD32VF103
|
|
*
|
|
* @author Gunar Schorcht <gunar@schorcht.net>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
|
|
#include "cpu.h"
|
|
#include "irq.h"
|
|
#include "mutex.h"
|
|
#ifdef MODULE_PM_LAYERED
|
|
#include "pm_layered.h"
|
|
#endif
|
|
#include "panic.h"
|
|
|
|
#include "periph/i2c.h"
|
|
#include "periph/gpio.h"
|
|
#include "periph_conf.h"
|
|
#include "ztimer.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define I2C_TIMEOUT_CYCLES 1000 /* clock cycles */
|
|
#define I2C_IRQ_PRIO (1)
|
|
|
|
#define I2C_ERROR_FLAGS_USED (I2C_STAT0_AERR_Msk | I2C_STAT0_LOSTARB_Msk | \
|
|
I2C_STAT0_BERR_Msk)
|
|
#define I2C_ERROR_FLAGS_OTHER (I2C_STAT0_OUERR_Msk | I2C_STAT0_PECERR_Msk | \
|
|
I2C_STAT0_SMBTO_Msk | I2C_STAT0_SMBALT_Msk)
|
|
#define I2C_ERROR_FLAGS (I2C_ERROR_FLAGS_USED | I2C_ERROR_FLAGS_OTHER)
|
|
|
|
#define I2C_INT_EV_ERR_FLAGS (I2C_CTL1_EVIE_Msk | I2C_CTL1_ERRIE_Msk)
|
|
#define I2C_INT_ALL_FLAGS (I2C_CTL1_BUFIE_Msk | I2C_INT_EV_ERR_FLAGS)
|
|
|
|
/* 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 _wait_for_bus(i2c_t dev);
|
|
static void _wait_for_irq(i2c_t dev);
|
|
static void _irq_handler(unsigned irqn);
|
|
|
|
typedef enum {
|
|
I2C_OK,
|
|
I2C_START_SENT,
|
|
I2C_ADDR_SENT,
|
|
I2C_ADDR10_SENT,
|
|
I2C_BT_COMPLETE,
|
|
I2C_TXB_EMPTY,
|
|
I2C_RXB_NOT_EMPTY,
|
|
I2C_RXB_NOT_EMPTY_BT_COMPLETE,
|
|
I2C_TIMEOUT,
|
|
I2C_ACK_ERR,
|
|
I2C_ARB_LOST,
|
|
I2C_BUS_ERROR,
|
|
I2C_OTHER_ERROR,
|
|
} _i2c_state_t;
|
|
|
|
typedef struct {
|
|
mutex_t dev_lock;
|
|
mutex_t irq_lock;
|
|
_i2c_state_t state;
|
|
void (*isr)(unsigned irqn);
|
|
} _i2c_dev_t;
|
|
|
|
_i2c_dev_t _i2c_dev[] = {
|
|
{
|
|
.dev_lock = MUTEX_INIT,
|
|
.irq_lock = MUTEX_INIT_LOCKED,
|
|
.state = I2C_OK,
|
|
},
|
|
{
|
|
.dev_lock = MUTEX_INIT,
|
|
.irq_lock = MUTEX_INIT_LOCKED,
|
|
.state = I2C_OK,
|
|
},
|
|
};
|
|
|
|
void i2c_init(i2c_t dev)
|
|
{
|
|
assert(dev < I2C_NUMOF);
|
|
|
|
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);
|
|
|
|
/* enable set event interrupt handler and enable the event interrupt */
|
|
clic_set_handler(i2c_config[dev].irqn, _irq_handler);
|
|
clic_enable_interrupt(i2c_config[dev].irqn, CPU_DEFAULT_IRQ_PRIO);
|
|
/* enable set error interrupt handler and enable the error interrupt */
|
|
clic_set_handler(i2c_config[dev].irqn + 1, _irq_handler);
|
|
clic_enable_interrupt(i2c_config[dev].irqn + 1, CPU_DEFAULT_IRQ_PRIO);
|
|
|
|
_init(dev);
|
|
|
|
_i2c_dev[dev].state = I2C_OK;
|
|
}
|
|
|
|
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;
|
|
/* Clear flags */
|
|
i2c->STAT0 &= ~I2C_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);
|
|
|
|
/* lock the device */
|
|
mutex_lock(&_i2c_dev[dev].dev_lock);
|
|
|
|
/* 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(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);
|
|
|
|
/* unlock the device */
|
|
mutex_unlock(&_i2c_dev[dev].dev_lock);
|
|
}
|
|
|
|
static int _i2c_start_cmd(i2c_t dev)
|
|
{
|
|
DEBUG("START cmd, dev=%d\n", dev);
|
|
|
|
I2C_Type *i2c = i2c_config[dev].dev;
|
|
|
|
/* clear error flags */
|
|
i2c->STAT0 &= ~I2C_ERROR_FLAGS;
|
|
|
|
/* send start condition and wait for interrupt */
|
|
i2c->CTL0 |= I2C_CTL0_START_Msk;
|
|
_wait_for_irq(dev);
|
|
|
|
switch (_i2c_dev[dev].state) {
|
|
case I2C_START_SENT:
|
|
return 0;
|
|
case I2C_ARB_LOST:
|
|
return -EAGAIN;
|
|
case I2C_TIMEOUT:
|
|
return -ETIMEDOUT;
|
|
default:
|
|
/* on other errors */
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int _i2c_stop_cmd(i2c_t dev)
|
|
{
|
|
DEBUG("STOP cmd, dev=%d\n", dev);
|
|
|
|
I2C_Type *i2c = i2c_config[dev].dev;
|
|
|
|
/* clear error flags */
|
|
i2c->STAT0 &= ~I2C_ERROR_FLAGS;
|
|
|
|
/* send start condition */
|
|
i2c->CTL0 |= I2C_CTL0_STOP_Msk;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _i2c_stop_cmd_and_wait(i2c_t dev)
|
|
{
|
|
_i2c_stop_cmd(dev);
|
|
return _wait_for_bus(dev);
|
|
}
|
|
|
|
static int _i2c_addr_cmd(i2c_t dev, uint8_t *addr, uint8_t size)
|
|
{
|
|
if (size == 1) {
|
|
DEBUG("ADDR cmd, dev=%d, addr=%02x\n", dev, addr[0]);
|
|
}
|
|
else{
|
|
DEBUG("ADDR cmd, dev=%d, addr=%02x%02x\n", dev, addr[0], addr[1]);
|
|
}
|
|
|
|
I2C_Type *i2c = i2c_config[dev].dev;
|
|
|
|
/* clear error flags */
|
|
i2c->STAT0 &= ~I2C_ERROR_FLAGS;
|
|
|
|
/* read STAT0 followed by writing the first address byte to the
|
|
* DATA register to clear SBSEND and then wait for interrupt */
|
|
i2c->STAT0;
|
|
i2c->DATA = addr[0];
|
|
_wait_for_irq(dev);
|
|
|
|
if ((_i2c_dev[dev].state == I2C_ADDR10_SENT)) {
|
|
/* first byte is sent and indicates a 10 bit address */
|
|
assert(size == 2);
|
|
/* read STAT0 followed by writing the second byte of the 10-bit
|
|
* address to the DATA register to clear ADD10SEND and then wait for
|
|
* interrupt */
|
|
i2c->STAT0;
|
|
i2c->DATA = addr[1];
|
|
_wait_for_irq(dev);
|
|
}
|
|
|
|
switch (_i2c_dev[dev].state) {
|
|
case I2C_ADDR_SENT:
|
|
/* Since the ADDSEND flag is needed to control the ACK bit while
|
|
* receiving bytes, it is intentionally not cleared here, but must
|
|
* be explicitly cleared in `i2c_read_bytes` and `i2c_read_bytes */
|
|
return 0;
|
|
case I2C_ARB_LOST:
|
|
return -EAGAIN;
|
|
case I2C_TIMEOUT:
|
|
return -ETIMEDOUT;
|
|
case I2C_ACK_ERR:
|
|
return -ENXIO;
|
|
default:
|
|
/* on other errors */
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
int _i2c_write_cmd(i2c_t dev, uint8_t data)
|
|
{
|
|
DEBUG("WRITE cmd, dev=%d\n", dev);
|
|
|
|
I2C_Type *i2c = i2c_config[dev].dev;
|
|
|
|
/* clear error flags */
|
|
i2c->STAT0 &= ~I2C_ERROR_FLAGS;
|
|
|
|
/* send data byte and wait for BTC interrupt */
|
|
i2c->DATA = data;
|
|
_wait_for_irq(dev);
|
|
|
|
switch (_i2c_dev[dev].state) {
|
|
case I2C_BT_COMPLETE:
|
|
/* read STAT0 followed by reading DATA to clear the BTC flag */
|
|
i2c->STAT0;
|
|
i2c->DATA;
|
|
return 0;
|
|
case I2C_ARB_LOST:
|
|
return -EAGAIN;
|
|
case I2C_TIMEOUT:
|
|
return -ETIMEDOUT;
|
|
case I2C_ACK_ERR:
|
|
return -EIO;
|
|
default:
|
|
/* on other errors */
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
int _i2c_read_cmd(i2c_t dev, uint8_t *data)
|
|
{
|
|
DEBUG("READ cmd, dev=%d\n", dev);
|
|
|
|
I2C_Type *i2c = i2c_config[dev].dev;
|
|
|
|
/* clear error flags */
|
|
i2c->STAT0 &= ~I2C_ERROR_FLAGS;
|
|
|
|
if (i2c->STAT0 & (I2C_STAT0_RBNE_Msk || I2C_STAT0_BTC_Msk)) {
|
|
_i2c_dev[dev].state = I2C_RXB_NOT_EMPTY;
|
|
}
|
|
else {
|
|
/* buffer interrupts have to be enabled for read */
|
|
i2c_config[dev].dev->CTL1 |= I2C_CTL1_BUFIE_Msk;
|
|
|
|
/* wait for interrupt */
|
|
_wait_for_irq(dev);
|
|
}
|
|
|
|
switch (_i2c_dev[dev].state) {
|
|
case I2C_RXB_NOT_EMPTY_BT_COMPLETE:
|
|
case I2C_RXB_NOT_EMPTY:
|
|
/* RBNE is cleared by reading STAT0 followed by reading the data register */
|
|
i2c->STAT0;
|
|
*data = i2c->DATA;
|
|
return 0;
|
|
case I2C_ARB_LOST:
|
|
return -EAGAIN;
|
|
case I2C_TIMEOUT:
|
|
return -ETIMEDOUT;
|
|
default:
|
|
/* on other errors */
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int _i2c_wait_rbne_btc(i2c_t dev)
|
|
{
|
|
DEBUG("WAIT RNBE+BTC cmd, dev=%d\n", dev);
|
|
|
|
I2C_Type *i2c = i2c_config[dev].dev;
|
|
|
|
/* clear error flags */
|
|
i2c->STAT0 &= ~I2C_ERROR_FLAGS;
|
|
|
|
_wait_for_irq(dev);
|
|
|
|
switch (_i2c_dev[dev].state) {
|
|
case I2C_RXB_NOT_EMPTY_BT_COMPLETE:
|
|
return 0;
|
|
case I2C_ARB_LOST:
|
|
return -EAGAIN;
|
|
case I2C_TIMEOUT:
|
|
return -ETIMEDOUT;
|
|
default:
|
|
/* on other errors */
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t length,
|
|
uint8_t flags)
|
|
{
|
|
assert(dev < I2C_NUMOF);
|
|
assert(data);
|
|
assert(length);
|
|
|
|
DEBUG("i2c_read_bytes, dev=%d len=%d flags=%02x\n", dev, length, flags);
|
|
|
|
I2C_Type *i2c = i2c_config[dev].dev;
|
|
|
|
/* 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 res;
|
|
|
|
_i2c_dev[dev].state = I2C_OK;
|
|
|
|
/* set the ACK bit */
|
|
i2c->CTL0 |= I2C_CTL0_ACKEN_Msk;
|
|
/* clear the POAP bit to indicate that ACK bit controls the current byte */
|
|
i2c->CTL0 &= ~I2C_CTL0_POAP_Msk;
|
|
|
|
if (!(flags & I2C_NOSTOP)) {
|
|
/* Since the I2C controller allows to receive up to two bytes before
|
|
* the application has to react, receiving a single byte, two bytes
|
|
* or more than two bytes needs a different handling for correct
|
|
* reception if the reception is not to be continued (I2C_NOSTOP
|
|
* is not set) */
|
|
if (length == 1) {
|
|
/* If a single byte is to be received clear the ACK bit before
|
|
* any byte is received and keep the POAP bit cleared to indicate
|
|
* that the ACK bit controls the ACK of the currecnt byte. */
|
|
i2c->CTL0 &= ~I2C_CTL0_ACKEN_Msk;
|
|
}
|
|
else if (length == 2) {
|
|
/* If exactly 2 bytes are to be received, keep the ACK bit set but
|
|
* also set the POAP bit to indicate that the ACK bit controls the
|
|
* next byte that is received in shift register. The ACK bit is then
|
|
* cleared after the ADDSEND flag is cleared and thus the reception
|
|
* of first byte has already been started. Thus an ACK is generated
|
|
* for the first byte and a NACK for the second byte. */
|
|
i2c->CTL0 |= I2C_CTL0_POAP_Msk;
|
|
}
|
|
/* In all other cases the ACK flag is kept set while the POAP flag
|
|
* is kept cleared, so that the ACK bit always controls the byte
|
|
* currently to be received in the shift register. To clear the ACK bit
|
|
* before the last byte is received in the shift register, reading of
|
|
* bytes from the DATA register is stopped when there are 3 bytes left
|
|
* to receive until the BTC flag is set. The BTC flag then indicates
|
|
* that the DATA register then contains the third last byte and the
|
|
* shift register the second last byte. The ACK flag is then cleared
|
|
* before reading of the bytes from the DATA register continues. */
|
|
}
|
|
|
|
/* if I2C_NOSTART is not set, send START condition and ADDR */
|
|
if (!(flags & I2C_NOSTART)) {
|
|
res = _i2c_start_cmd(dev);
|
|
if (res != 0) {
|
|
_i2c_stop_cmd_and_wait(dev);
|
|
return res;
|
|
}
|
|
|
|
/* address handling */
|
|
if (flags & I2C_ADDR10) {
|
|
/* prepare 10 bit address bytes */
|
|
uint8_t addr10[2];
|
|
addr10[0] = 0xf0 | (addr & 0x0300) >> 7 | I2C_READ;
|
|
addr10[1] = addr & 0xff;
|
|
/* send ADDR without read flag */
|
|
res = _i2c_addr_cmd(dev, addr10, 2);
|
|
}
|
|
else {
|
|
/* send ADDR without read flag */
|
|
uint8_t addr7 = addr << 1 | I2C_READ;
|
|
res = _i2c_addr_cmd(dev, &addr7, 1);
|
|
}
|
|
if (res != 0) {
|
|
_i2c_stop_cmd_and_wait(dev);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
/* read STAT0 followed by reading STAT1 to clear the ADDSEND flag */
|
|
i2c->STAT0;
|
|
i2c->STAT1;
|
|
|
|
/* read data */
|
|
uint8_t *buffer = (uint8_t*)data;
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
if (!(flags & I2C_NOSTOP)) {
|
|
/* if the reception is not to be continued (I2C_NOSTOP is not set) */
|
|
if (i == 0 && (length == 2)) {
|
|
/* The shift register already receives the first byte after
|
|
* the ADDSEND flag has been cleared. Since the POAP bit is
|
|
* set in the case that exactly two bytes are to be received,
|
|
* the change of the ACK bit during the reception of a byte
|
|
* does not affect the generated ACK of the byte currently
|
|
* received, but of the byte to be received next. Since the
|
|
* ACK bit and the POAP bit were set in this case, an ACK
|
|
* is generated for the first byte. Clearing the ACK bit here
|
|
* then generates a NACK for the next (last) byte. */
|
|
i2c->CTL0 &= ~I2C_CTL0_ACKEN_Msk;
|
|
}
|
|
else if (i == (length - 3)) {
|
|
/* To clear the ACK flag before the last byte is received
|
|
* in the shift register, reading of bytes from the DATA
|
|
* register is stopped when there are 3 bytes left to
|
|
* receive until the BTC flag is set. The BTC flag is then
|
|
* set when the third last received byte is in DATA register
|
|
* and the second last byte is in shift register. */
|
|
res = _i2c_wait_rbne_btc(dev);
|
|
if (res != 0) {
|
|
_i2c_stop_cmd_and_wait(dev);
|
|
return res;
|
|
}
|
|
/* The ACK flag is then cleared before reading of the bytes
|
|
* from the DATA register continues to generate an NACK
|
|
* for the last byte. */
|
|
i2c->CTL0 &= ~I2C_CTL0_ACKEN_Msk;
|
|
}
|
|
}
|
|
res = _i2c_read_cmd(dev, buffer + i);
|
|
if (res != 0) {
|
|
_i2c_stop_cmd_and_wait(dev);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
if (!(flags & I2C_NOSTOP)) {
|
|
/* If the transfer is not to be continued (I2C_NOSTOP is not set), set
|
|
* the STOP bit and wait a short time until the I2CBSY bit is cleared. */
|
|
return _i2c_stop_cmd_and_wait(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data,
|
|
size_t length, uint8_t flags)
|
|
{
|
|
assert(dev < I2C_NUMOF);
|
|
assert(data);
|
|
assert(length);
|
|
|
|
DEBUG("i2c_write_bytes, dev=%d len=%d flags=%02x\n", dev, length, flags);
|
|
|
|
int res;
|
|
|
|
_i2c_dev[dev].state = I2C_OK;
|
|
|
|
/* if I2C_NOSTART is not set, send START condition and ADDR */
|
|
if (!(flags & I2C_NOSTART)) {
|
|
res = _i2c_start_cmd(dev);
|
|
if (res != 0) {
|
|
_i2c_stop_cmd_and_wait(dev);
|
|
return res;
|
|
}
|
|
|
|
/* address handling */
|
|
if (flags & I2C_ADDR10) {
|
|
/* prepare 10 bit address bytes */
|
|
uint8_t addr10[2];
|
|
addr10[0] = 0xf0 | (addr & 0x0300) >> 7;
|
|
addr10[1] = addr & 0xff;
|
|
/* send ADDR without read flag */
|
|
res = _i2c_addr_cmd(dev, addr10, 2);
|
|
}
|
|
else {
|
|
/* send ADDR without read flag */
|
|
uint8_t addr7 = addr << 1;
|
|
res = _i2c_addr_cmd(dev, &addr7, 1);
|
|
}
|
|
if (res != 0) {
|
|
_i2c_stop_cmd_and_wait(dev);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
/* read STAT0 followed by reading STAT1 to clear the ADDSEND flag */
|
|
i2c_config[dev].dev->STAT0;
|
|
i2c_config[dev].dev->STAT1;
|
|
|
|
/* send data */
|
|
uint8_t *buffer = (uint8_t*)data;
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
res = _i2c_write_cmd(dev, buffer[i]);
|
|
if (res != 0) {
|
|
_i2c_stop_cmd_and_wait(dev);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
if (!(flags & I2C_NOSTOP)) {
|
|
/* If the transfer is not to be continued (I2C_NOSTOP is not set), set
|
|
* the STOP bit and wait a short time until the I2CBSY bit is cleared. */
|
|
return _i2c_stop_cmd_and_wait(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void _i2c_transfer_timeout(void *arg)
|
|
{
|
|
i2c_t dev = (i2c_t)(uintptr_t)arg;
|
|
|
|
/* set result to timeout */
|
|
_i2c_dev[dev].state = I2C_TIMEOUT;
|
|
|
|
/* wake up the thread that is waiting for the results */
|
|
mutex_unlock(&_i2c_dev[dev].irq_lock);
|
|
}
|
|
|
|
static void _wait_for_irq(i2c_t dev)
|
|
{
|
|
#if defined(MODULE_ZTIMER_USEC)
|
|
ztimer_t timer = { .callback = _i2c_transfer_timeout,
|
|
.arg = (void *)dev };
|
|
uint32_t timeout = ((I2C_TIMEOUT_CYCLES * MHZ(1)) / i2c_config[dev].speed) + 1;
|
|
ztimer_set(ZTIMER_USEC, &timer, timeout);
|
|
#elif defined(MODULE_ZTIMER_MSEC)
|
|
ztimer_t timer = { .callback = _i2c_transfer_timeout,
|
|
.arg = (void *)dev };
|
|
uint32_t timeout = ((I2C_TIMEOUT_CYCLES * KHZ(1)) / i2c_config[dev].speed) + 1;
|
|
ztimer_set(ZTIMER_MSEC, &timer, timeout);
|
|
#else
|
|
#warning "I2C timeout handling requires to use ztimer_msec or ztimer_usec"
|
|
#endif
|
|
|
|
/* enable only event and error interrupts, buffer interrupts are only used in read */
|
|
i2c_config[dev].dev->CTL1 |= I2C_INT_EV_ERR_FLAGS;
|
|
|
|
/* wait for buffer, event or error interrupt */
|
|
mutex_lock(&_i2c_dev[dev].irq_lock);
|
|
|
|
#if defined(MODULE_ZTIMER_USEC)
|
|
ztimer_remove(ZTIMER_USEC, &timer);
|
|
#elif defined(MODULE_ZTIMER_MSEC)
|
|
ztimer_remove(ZTIMER_MSEC, &timer);
|
|
#endif
|
|
|
|
if (_i2c_dev[dev].state == I2C_TIMEOUT) {
|
|
DEBUG("error: timeout, dev=%d\n", dev);
|
|
}
|
|
}
|
|
|
|
#define TICK_TIMEOUT 0x00000fffUL
|
|
|
|
static inline int _wait_for_bus(i2c_t dev)
|
|
{
|
|
uint16_t tick = TICK_TIMEOUT;
|
|
/* short busy waiting for the bus becoming free (I2CBSY is cleared) */
|
|
while ((i2c_config[dev].dev->STAT1 & I2C_STAT1_I2CBSY_Msk) && tick--) {}
|
|
if (!tick) {
|
|
DEBUG("error: timeout, dev=%d\n", dev);
|
|
return -ETIMEDOUT;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void _irq_handler(unsigned irqn)
|
|
{
|
|
static_assert(I2C0_ER_IRQn == I2C0_EV_IRQn + 1);
|
|
static_assert(I2C1_ER_IRQn == I2C1_EV_IRQn + 1);
|
|
|
|
i2c_t dev = ((irqn == i2c_config[0].irqn) ||
|
|
(irqn == (i2c_config[0].irqn + 1))) ? I2C_DEV(0) : I2C_DEV(1);
|
|
|
|
assert(dev < I2C_NUMOF);
|
|
|
|
I2C_Type *i2c = i2c_config[dev].dev;
|
|
|
|
assert(i2c != NULL);
|
|
|
|
unsigned state = i2c->STAT0;
|
|
|
|
/* disable buffer, event and error interrupts */
|
|
i2c->CTL1 &= ~(I2C_INT_ALL_FLAGS);
|
|
|
|
DEBUG("STAT0: %08x, dev=%d\n", state, dev);
|
|
|
|
if (state & I2C_ERROR_FLAGS) {
|
|
/* any error happened */
|
|
if (state & I2C_ERROR_FLAGS_USED) {
|
|
if (state & I2C_STAT0_LOSTARB_Msk) {
|
|
DEBUG("error: arbitration lost, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_ARB_LOST;
|
|
}
|
|
else if (state & I2C_STAT0_AERR_Msk) {
|
|
DEBUG("error: ACK error, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_ACK_ERR;
|
|
}
|
|
else if (state & I2C_STAT0_BERR_Msk) {
|
|
DEBUG("error: bus error, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_BUS_ERROR;
|
|
}
|
|
}
|
|
if (state & I2C_ERROR_FLAGS_OTHER) {
|
|
/* PEC calculation and SMBus are not used,
|
|
* according errors are simply handled as other errors */
|
|
DEBUG("error: other error, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_OTHER_ERROR;
|
|
}
|
|
/* clear interrupt flags for errors */
|
|
i2c->CTL1 &= ~(I2C_ERROR_FLAGS);
|
|
}
|
|
else if (state & I2C_STAT0_SBSEND_Msk) {
|
|
DEBUG("START sent, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_START_SENT;
|
|
}
|
|
else if (state & I2C_STAT0_ADDSEND_Msk) {
|
|
DEBUG("ADDR sent, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_ADDR_SENT;
|
|
}
|
|
else if (state & I2C_STAT0_ADD10SEND_Msk) {
|
|
DEBUG("ADDR10 first byte sent, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_ADDR10_SENT;
|
|
}
|
|
else if (state & I2C_STAT0_RBNE_Msk) {
|
|
if (state & I2C_STAT0_BTC_Msk) {
|
|
DEBUG("RX buffer not empty + BT completed, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_RXB_NOT_EMPTY_BT_COMPLETE;
|
|
}
|
|
else {
|
|
DEBUG("RX buffer not empty, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_RXB_NOT_EMPTY;
|
|
}
|
|
}
|
|
else if (state & I2C_STAT0_BTC_Msk) {
|
|
DEBUG("BT completed, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_BT_COMPLETE;
|
|
}
|
|
else if (state & I2C_STAT0_TBE_Msk) {
|
|
DEBUG("TX buffer empty, dev=%d\n", dev);
|
|
_i2c_dev[dev].state = I2C_TXB_EMPTY;
|
|
}
|
|
else {
|
|
_i2c_dev[dev].state = I2C_OK;
|
|
}
|
|
|
|
mutex_unlock(&_i2c_dev[dev].irq_lock);
|
|
}
|