1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/esp32/periph/i2c_hw.c

658 lines
20 KiB
C

/*
* Copyright (C) 2018 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_esp32
* @ingroup drivers_periph_i2c
* @{
*
* @file
* @brief Low-level I2C driver implementation for ESP32 SDK
*
* @note The hardware implementation seems to be very poor and faulty.
* I2C commands in the I2C command pipeline are not executed
* sporadically. A number of ACK errors and timeouts caused by
* protocol errors are the result. You should use the hardware
* implementation only if they can be tolerated.
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
/*
* The I2C peripheral driver uses a mixture of the high level functions and
* the HAL driver. While complex functions like the configuration of the I2C
* interface use the high level interface, the HAL interface is used by data
* transfer functions for better performance and better control.
*/
#include <errno.h>
#include "gpio_arch.h"
#include "irq_arch.h"
#include "log.h"
#include "macros/units.h"
#include "mutex.h"
#include "periph_conf.h"
#include "periph/i2c.h"
#include "ztimer.h"
#include "esp_attr.h"
#include "driver/i2c.h"
#include "hal/i2c_hal.h"
#include "hal/interrupt_controller_types.h"
#include "hal/interrupt_controller_ll.h"
#include "rom/ets_sys.h"
#include "soc/i2c_reg.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/* Ensure that the I2Cn_* symbols define I2C_DEV(n) */
#if !defined(I2C0_SPEED) && defined(I2C1_SPEED)
#error "I2C_DEV(1) is used but I2C_DEV(0) is not defined"
#endif
typedef struct {
i2c_speed_t clk_freq; /* clock freuency in Hz according to bus speed */
uint8_t cmd; /* command index of a transfer*/
uint8_t cmd_op; /* main command op code of a transfer */
uint8_t len; /* data length of a transfer */
uint32_t status; /* results of a transfer */
mutex_t cmd_lock; /* command execution locking */
mutex_t dev_lock; /* device locking */
} _i2c_bus_t;
static i2c_hal_context_t _i2c_hw[] = {
#if defined(I2C0_SPEED)
{ .dev = I2C_LL_GET_HW(0) },
#endif
#if defined(I2C1_SPEED)
{ .dev = I2C_LL_GET_HW(1) },
#endif
};
_Static_assert(I2C_NUMOF == ARRAY_SIZE(_i2c_hw),
"Size of bus descriptor table doesn't match I2C_NUMOF");
_Static_assert(I2C_NUMOF <= I2C_NUMOF_MAX,
"Number of defined I2C devices is greater than the number of supported devices");
static _i2c_bus_t _i2c_bus[I2C_NUMOF] = {
#if defined(I2C0_SPEED)
{
.cmd_lock = MUTEX_INIT_LOCKED,
.dev_lock = MUTEX_INIT,
},
#endif
#if defined(I2C1_SPEED)
{
.cmd_lock = MUTEX_INIT_LOCKED,
.dev_lock = MUTEX_INIT,
},
#endif
};
/* functions used from ESP-IDF driver that are not exposed in API */
extern esp_err_t i2c_hw_fsm_reset(i2c_port_t i2c_num);
/* forward declaration of internal functions */
static void _i2c_start_cmd(i2c_t dev);
static void _i2c_stop_cmd(i2c_t dev);
static void _i2c_end_cmd(i2c_t dev);
static void _i2c_write_cmd(i2c_t dev, const uint8_t* data, uint8_t len);
static void _i2c_read_cmd(i2c_t dev, uint8_t len, bool last);
static void _i2c_transfer(i2c_t dev);
static void _i2c_intr_handler(void *arg);
static int _i2c_status_to_errno(i2c_t dev);
void i2c_init(i2c_t dev)
{
_Static_assert(I2C_NUMOF <= I2C_NUMOF_MAX, "Too many I2C devices defined");
_Static_assert(I2C_NUMOF == ARRAY_SIZE(_i2c_hw),
"Number of hardware descriptors doesn't match the I2C_NUMOF");
assert(dev < I2C_NUMOF);
/* According to the Technical Reference Manual, only FAST mode is supported,
* but FAST PLUS mode seems to work also. */
assert(i2c_config[dev].speed <= I2C_SPEED_FAST_PLUS);
/* GPIOs for SCL and SDA signals must not already be used for peripherals */
if (((gpio_get_pin_usage(i2c_config[dev].scl) != _I2C) &&
(gpio_get_pin_usage(i2c_config[dev].scl) != _GPIO)) ||
((gpio_get_pin_usage(i2c_config[dev].sda) != _I2C) &&
(gpio_get_pin_usage(i2c_config[dev].sda) != _GPIO))) {
LOG_TAG_ERROR("i2c", "GPIO%u and/or GPIO%u are used for %s/%s and "
"cannot be used as I2C interface\n",
i2c_config[dev].scl, i2c_config[dev].sda,
gpio_get_pin_usage_str(i2c_config[dev].scl),
gpio_get_pin_usage_str(i2c_config[dev].sda));
assert(0);
}
_i2c_bus[dev].cmd = 0;
_i2c_bus[dev].len = 0;
i2c_acquire(dev);
i2c_config_t cfg = {};
cfg.mode = I2C_MODE_MASTER;
cfg.sda_io_num = i2c_config[dev].sda;
cfg.scl_io_num = i2c_config[dev].scl;
cfg.sda_pullup_en = i2c_config[dev].sda_pullup;
cfg.scl_pullup_en = i2c_config[dev].scl_pullup;
#if defined(SOC_I2C_SUPPORT_RTC) && !defined(CPU_FAM_ESP32S3)
cfg.clk_flags = I2C_SCLK_SRC_FLAG_LIGHT_SLEEP;
#endif
switch (i2c_config[dev].speed) {
case I2C_SPEED_LOW:
cfg.master.clk_speed = 10 * KHZ(1);
break;
case I2C_SPEED_NORMAL:
cfg.master.clk_speed = 100 * KHZ(1);
break;
case I2C_SPEED_FAST:
cfg.master.clk_speed = 400 * KHZ(1);
break;
case I2C_SPEED_FAST_PLUS:
cfg.master.clk_speed = 1000 * KHZ(1);
break;
case I2C_SPEED_HIGH:
cfg.master.clk_speed = 3400 * KHZ(1);
break;
default:
LOG_TAG_ERROR("i2c", "Invalid speed value in %s\n", __func__);
assert(0);
}
_i2c_bus[dev].clk_freq = cfg.master.clk_speed;
/* configures the GPIOs, sets the bus timing and enables the periphery */
i2c_param_config(dev, &cfg);
#if defined(SOC_I2C_SUPPORT_APB)
/* If I2C clock is derived from APB clock, the bus timing parameters
* have to be corrected if the APB clock is less than 80 MHz */
extern uint32_t rtc_clk_apb_freq_get(void);
uint32_t apb_clk = rtc_clk_apb_freq_get();
if (apb_clk < MHZ(80)) {
i2c_clk_cal_t clk_cfg;
i2c_ll_cal_bus_clk(apb_clk, cfg.master.clk_speed, &clk_cfg);
i2c_ll_set_bus_timing(_i2c_hw[dev].dev, &clk_cfg);
}
#endif
/* store the usage type in GPIO table */
gpio_set_pin_usage(i2c_config[dev].scl, _I2C);
gpio_set_pin_usage(i2c_config[dev].sda, _I2C);
/* route all I2C interrupt sources to same the CPU interrupt */
intr_matrix_set(PRO_CPU_NUM, i2c_periph_signal[dev].irq, CPU_INUM_I2C);
/* set interrupt handler and enable the CPU interrupt */
intr_cntrl_ll_set_int_handler(CPU_INUM_I2C, _i2c_intr_handler, NULL);
intr_cntrl_ll_enable_interrupts(BIT(CPU_INUM_I2C));
i2c_release(dev);
}
void i2c_acquire(i2c_t dev)
{
DEBUG ("%s\n", __func__);
assert(dev < I2C_NUMOF);
mutex_lock(&_i2c_bus[dev].dev_lock);
}
void i2c_release(i2c_t dev)
{
DEBUG ("%s\n", __func__);
assert(dev < I2C_NUMOF);
mutex_unlock(&_i2c_bus[dev].dev_lock);
}
int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags)
{
DEBUG ("%s dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
assert(dev < I2C_NUMOF);
assert(len > 0);
assert(data != NULL);
int res;
_i2c_bus[dev].cmd_op = I2C_LL_CMD_READ;
_i2c_bus[dev].cmd = 0;
/* reset TX/RX FIFO queue */
i2c_hal_txfifo_rst(&_i2c_hw[dev]);
i2c_hal_rxfifo_rst(&_i2c_hw[dev]);
/* if I2C_NOSTART is not set, START condition and ADDR is used */
if (!(flags & I2C_NOSTART)) {
/* send START condition */
_i2c_start_cmd(dev);
/* address handling */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr10[2];
addr10[0] = 0xf0 | (addr & 0x0300) >> 7 | I2C_MASTER_READ;
addr10[1] = addr & 0xff;
/* send ADDR with read flag */
_i2c_write_cmd(dev, addr10, 2);
}
else {
/* send ADDR with read flag */
uint8_t addr7 = (addr << 1 | I2C_READ);
_i2c_write_cmd(dev, &addr7, 1);
}
}
/* read data bytes in blocks of SOC_I2C_FIFO_LEN bytes */
uint32_t off = 0;
/* if len > SOC_I2C_FIFO_LEN read SOC_I2C_FIFO_LEN bytes at a time */
while (len > SOC_I2C_FIFO_LEN) {
/* read one block of data bytes command */
_i2c_bus[dev].len = SOC_I2C_FIFO_LEN;
_i2c_read_cmd(dev, SOC_I2C_FIFO_LEN, false);
_i2c_end_cmd(dev);
_i2c_transfer(dev);
res = _i2c_status_to_errno(dev);
if (res) {
return res;
}
/* if transfer was successful, fetch the data from I2C RAM */
i2c_hal_read_rxfifo(&_i2c_hw[dev], data + off, len);
/* reset RX FIFO queue */
i2c_hal_rxfifo_rst(&_i2c_hw[dev]);
len -= SOC_I2C_FIFO_LEN;
off += SOC_I2C_FIFO_LEN;
}
/* read remaining data bytes command with a final NAK */
_i2c_bus[dev].len = len;
_i2c_read_cmd(dev, len, true);
/* if I2C_NOSTOP flag is not set, send STOP condition is used */
if (!(flags & I2C_NOSTOP)) {
/* send STOP condition */
_i2c_stop_cmd(dev);
}
else {
/* otherwise place end command in pipeline */
_i2c_end_cmd(dev);
}
/* finish operation by executing the command pipeline */
_i2c_transfer(dev);
if ((res = _i2c_status_to_errno(dev))) {
return res;
}
/* fetch the data from RX FIFO */
i2c_hal_read_rxfifo(&_i2c_hw[dev], data + off, len);
/* return 0 on success */
return 0;
}
int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint8_t flags)
{
DEBUG ("%s dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
assert(dev < I2C_NUMOF);
assert(len > 0);
assert(data != NULL);
int res;
_i2c_bus[dev].cmd_op = I2C_LL_CMD_WRITE;
_i2c_bus[dev].cmd = 0;
/* reset TX FIFO queue */
i2c_hal_txfifo_rst(&_i2c_hw[dev]);
/* if I2C_NOSTART is not set, START condition and ADDR is used */
if (!(flags & I2C_NOSTART)) {
/* send START condition */
_i2c_start_cmd(dev);
/* address handling */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr10[2];
addr10[0] = 0xf0 | (addr & 0x0300) >> 7 | I2C_MASTER_WRITE;
addr10[1] = addr & 0xff;
/* send ADDR without read flag */
_i2c_write_cmd(dev, addr10, 2);
}
else {
/* send ADDR without read flag */
uint8_t addr7 = addr << 1;
_i2c_write_cmd(dev, &addr7, 1);
}
}
/* send data bytes in blocks of SOC_I2C_FIFO_LEN bytes */
uint32_t off = 0;
uint32_t tx_fifo_free;
/* get available TX FIFO space */
i2c_hal_get_txfifo_cnt(&_i2c_hw[dev], &tx_fifo_free);
/* if len > SOC_I2C_FIFO_LEN write SOC_I2C_FIFO_LEN bytes at a time */
while (len > tx_fifo_free) {
/* send one block of data bytes */
_i2c_bus[dev].len = tx_fifo_free;
_i2c_write_cmd(dev, (uint8_t*)data + off, tx_fifo_free);
_i2c_end_cmd(dev);
_i2c_transfer(dev);
res = _i2c_status_to_errno(dev);
if (res) {
return res;
}
len -= tx_fifo_free;
off += tx_fifo_free;
/* reset TX FIFO queue */
i2c_hal_txfifo_rst(&_i2c_hw[dev]);
/* update available TX FIFO space */
i2c_hal_get_txfifo_cnt(&_i2c_hw[dev], &tx_fifo_free);
}
/* write remaining data bytes command */
_i2c_bus[dev].len = len;
_i2c_write_cmd(dev, (uint8_t*)data + off, len);
/* if I2C_NOSTOP flag is not set, send STOP condition is used */
if (!(flags & I2C_NOSTOP)) {
/* send STOP condition */
_i2c_stop_cmd(dev);
}
else {
/* otherwise place end command in pipeline */
_i2c_end_cmd(dev);
}
/* finish operation by executing the command pipeline */
_i2c_transfer(dev);
return _i2c_status_to_errno(dev);
}
#if defined(CPU_FAM_ESP32)
#define I2C_NACK_INT_ENA_M I2C_ACK_ERR_INT_ENA_M
#endif
/*
* @note:
* It is a known problem that the ESP32x I2C hardware implementation checks
* the ACK when the next command in the pipeline is executed. That is,
* for a segmented write operation of type `START ADDR DATA0 STOP` the
* `I2C_END_DETECT_INT` and the `I2C_NACK_INT` are triggered simultaneously
* in case of acknowledge errors. In this case it is not clear from the
* interrupt status whether the acknolegdement error has occurred for the
* ADDR or the DATA0 byte. The only way to find out is to check the remaining
* TX FIFO length. If the number of bytes in the TX-FIFO is still higher
* (for two-byte addresses) or equal (for one-byte addresses) to the length of
* the data, the acknolegdement error occurred for the ADDR field.
*/
static int _i2c_status_to_errno(i2c_t dev)
{
if (_i2c_bus[dev].status & I2C_ARBITRATION_LOST_INT_ENA_M) {
LOG_TAG_DEBUG("i2c", "arbitration lost dev=%u\n", dev);
return -EAGAIN;
}
if (_i2c_bus[dev].status & I2C_NACK_INT_ENA_M) {
LOG_TAG_DEBUG("i2c", "ack error dev=%u\n", dev);
if (_i2c_bus[dev].cmd_op == I2C_LL_CMD_WRITE) {
/*
* @note: In a write transfer, an ACK error can happen for the
* address field as well as for data. If the FIFO still contains
* all data bytes, the ACK error happened in address field and we
* have to return -ENXIO. Otherwise, the ACK error happened in data
* field and we have to return -EIO.
*/
uint32_t cnt;
i2c_hal_get_txfifo_cnt(&_i2c_hw[dev], &cnt);
return ((SOC_I2C_FIFO_LEN - cnt) >= _i2c_bus[dev].len) ? -ENXIO : -EIO;
}
else {
/*
* @note: In a read transfer, an ACK is only expected for the
* address field. Thus, an ACK error can only happen for the address
* field. Therefore, we always return -ENXIO in case of an ACK
* error.
*/
return -ENXIO;
}
}
if (_i2c_bus[dev].status & I2C_TIME_OUT_INT_ENA_M) {
LOG_TAG_DEBUG("i2c", "bus timeout dev=%u\n", dev);
i2c_hw_fsm_reset(dev);
return -ETIMEDOUT;
}
return 0;
}
static void _i2c_start_cmd(i2c_t dev)
{
DEBUG ("%s\n", __func__);
/* place START condition command in command queue */
i2c_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_RESTART };
i2c_hal_write_cmd_reg(&_i2c_hw[dev], cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_stop_cmd(i2c_t dev)
{
DEBUG ("%s\n", __func__);
/* place STOP condition command in command queue */
i2c_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_STOP };
i2c_hal_write_cmd_reg(&_i2c_hw[dev], cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_end_cmd(i2c_t dev)
{
DEBUG ("%s\n", __func__);
/* place END command for continues data transmission in command queue */
i2c_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_END };
i2c_hal_write_cmd_reg(&_i2c_hw[dev], cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_write_cmd(i2c_t dev, const uint8_t* data, uint8_t len)
{
DEBUG ("%s dev=%u data=%p len=%d\n", __func__, dev, data, len);
if (len > SOC_I2C_FIFO_LEN) {
LOG_TAG_ERROR("i2c", "Maximum number of bytes (32 bytes) that can be "
"sent with on transfer reached\n");
return;
}
/* store the data in TX FIFO */
i2c_hal_write_txfifo(&_i2c_hw[dev], (uint8_t *)data, len);
/* place WRITE command for multiple bytes in command queue */
i2c_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_WRITE,
.byte_num = len,
.ack_en = 1,
.ack_exp = 0,
.ack_val = 0 };
i2c_hal_write_cmd_reg(&_i2c_hw[dev], cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_read_cmd(i2c_t dev, uint8_t len, bool last)
{
DEBUG ("%s dev=%u len=%d\n", __func__, dev, len);
if (len < 1 || len > SOC_I2C_FIFO_LEN) {
/* at least one byte has to be read */
LOG_TAG_ERROR("i2c", "At least one byte has to be read\n");
return;
}
if (len > 1)
{
/* place READ command for len-1 bytes with positive ack in command queue*/
i2c_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_READ,
.byte_num = len-1,
.ack_en = 0,
.ack_exp = 0,
.ack_val = 0 };
i2c_hal_write_cmd_reg(&_i2c_hw[dev], cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
/* place READ command for last byte with negative ack in last segment in command queue*/
i2c_hw_cmd_t cmd = { .op_code = I2C_LL_CMD_READ,
.byte_num = 1,
.ack_en = 0,
.ack_exp = 0,
.ack_val = last ? 1 : 0 };
i2c_hal_write_cmd_reg(&_i2c_hw[dev], cmd, _i2c_bus[dev].cmd);
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
/* a maximum transfer 33 byte * 9 clock cycles * 1/100000 s takes at
* I2C_SPEED_NORMAL about 450 us, at I2C_SPEED_NORMAL_LOW about 4.5 ms */
#define I2C_TRANSFER_TIMEOUT 640
/* combination of all transfer interrupts */
#define I2C_LL_MASTER_INT (I2C_LL_MASTER_TX_INT | I2C_LL_MASTER_RX_INT)
void _i2c_transfer_timeout(void *arg)
{
i2c_t dev = (i2c_t)(uintptr_t)arg;
/* reset the hardware if I2C got stuck */
i2c_hw_fsm_reset(dev);
/* set result to timeout */
_i2c_bus[dev].status = I2C_TIME_OUT_INT_ENA_M;
/* wake up the thread that is waiting for the results */
mutex_unlock(&_i2c_bus[dev].cmd_lock);
}
/* Transfer of commands in I2C controller command pipeline */
static void _i2c_transfer(i2c_t dev)
{
DEBUG("%s cmd=%d\n", __func__, _i2c_bus[dev].cmd);
/* disable and enable all transmission interrupts and clear current status */
i2c_hal_clr_intsts_mask(&_i2c_hw[dev], I2C_LL_MASTER_INT);
i2c_hal_enable_intr_mask(&_i2c_hw[dev], I2C_LL_MASTER_INT);
/* set a timer for the case the I2C hardware gets stuck */
#if defined(MODULE_ZTIMER_MSEC)
uint32_t timeout = ((I2C_TRANSFER_TIMEOUT * KHZ(1)) / _i2c_bus[dev].clk_freq) + 1;
ztimer_t timer = { .callback = _i2c_transfer_timeout,
.arg = (void*)(uintptr_t)dev };
ztimer_set(ZTIMER_MSEC, &timer, timeout);
#endif
/* start the execution of commands in command pipeline */
_i2c_bus[dev].status = 0;
i2c_hal_update_config(&_i2c_hw[dev]);
i2c_hal_trans_start(&_i2c_hw[dev]);
/* wait for transfer results and remove timeout timer*/
mutex_lock(&_i2c_bus[dev].cmd_lock);
#if defined(MODULE_ZTIMER_MSEC)
ztimer_remove(ZTIMER_MSEC, &timer);
#endif
/* returned from transmission */
DEBUG("%s status=%08"PRIx32"\n", __func__, _i2c_bus[dev].status);
/* reset command and data index */
_i2c_bus[dev].cmd = 0;
}
static void IRAM_ATTR _i2c_intr_handler(void *arg)
{
/* to satisfy the compiler */
(void)arg;
irq_isr_enter();
/* all I2C peripheral interrupt sources are routed to the same interrupt,
so we have to use the status register to distinguish interruptees */
for (unsigned dev = 0; dev < I2C_NUMOF; dev++) {
uint32_t mask = i2c_ll_get_intsts_mask(_i2c_hw[dev].dev);
/* test for transfer related interrupts */
if (mask) {
_i2c_bus[dev].status = mask;
/* disable all interrupts and clear pending interrupts */
i2c_hal_clr_intsts_mask(&_i2c_hw[dev], I2C_LL_MASTER_INT);
i2c_hal_disable_intr_mask(&_i2c_hw[dev], I2C_LL_MASTER_INT);
/* wake up the thread that is waiting for the results */
mutex_unlock(&_i2c_bus[dev].cmd_lock);
}
}
irq_isr_exit();
}
void i2c_print_config(void)
{
if (I2C_NUMOF) {
for (unsigned dev = 0; dev < I2C_NUMOF; dev++) {
printf("\tI2C_DEV(%u)\tscl=%d sda=%d\n",
dev, i2c_config[dev].scl, i2c_config[dev].sda);
}
}
else {
LOG_TAG_INFO("i2c", "no I2C devices\n");
}
}