88ca215eed
Updates the API and types to match updated I2C terminology. Replaces master with controller and slave with target. Updates all drivers to match the changed macros, types, and API signatures. Signed-off-by: Tom Burdick <thomas.burdick@intel.com>
806 lines
23 KiB
C
806 lines
23 KiB
C
/*
|
|
* Copyright (c) 2017 Intel Corporation
|
|
* Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT espressif_esp32_i2c
|
|
|
|
/* Include esp-idf headers first to avoid redefining BIT() macro */
|
|
#include <soc/i2c_reg.h>
|
|
#include <esp32/rom/gpio.h>
|
|
#include <soc/gpio_sig_map.h>
|
|
#include <hal/i2c_ll.h>
|
|
#include <hal/i2c_hal.h>
|
|
#include <hal/gpio_hal.h>
|
|
|
|
#include <soc.h>
|
|
#include <errno.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/drivers/i2c.h>
|
|
#include <zephyr/drivers/interrupt_controller/intc_esp32.h>
|
|
#include <zephyr/drivers/clock_control.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <string.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(i2c_esp32, CONFIG_I2C_LOG_LEVEL);
|
|
|
|
#include "i2c-priv.h"
|
|
|
|
#define I2C_FILTER_CYC_NUM_DEF 7 /* Number of apb cycles filtered by default */
|
|
#define I2C_CLR_BUS_SCL_NUM 9 /* Number of SCL clocks to restore SDA signal */
|
|
#define I2C_CLR_BUS_HALF_PERIOD_US 5 /* Period of SCL clock to restore SDA signal */
|
|
#define I2C_TRANSFER_TIMEOUT_MSEC 500 /* Transfer timeout period */
|
|
|
|
/* Freq limitation when using different clock sources */
|
|
#define I2C_CLK_LIMIT_REF_TICK (1 * 1000 * 1000 / 20) /* REF_TICK, no more than REF_TICK/20*/
|
|
#define I2C_CLK_LIMIT_APB (80 * 1000 * 1000 / 20) /* Limited by APB, no more than APB/20 */
|
|
#define I2C_CLK_LIMIT_RTC (20 * 1000 * 1000 / 20) /* Limited by RTC, no more than RTC/20 */
|
|
#define I2C_CLK_LIMIT_XTAL (40 * 1000 * 1000 / 20) /* Limited by RTC, no more than XTAL/20 */
|
|
|
|
enum i2c_status_t {
|
|
I2C_STATUS_READ, /* read status for current master command */
|
|
I2C_STATUS_WRITE, /* write status for current master command */
|
|
I2C_STATUS_IDLE, /* idle status for current master command */
|
|
I2C_STATUS_ACK_ERROR, /* ack error status for current master command */
|
|
I2C_STATUS_DONE, /* I2C command done */
|
|
I2C_STATUS_TIMEOUT, /* I2C bus status error, and operation timeout */
|
|
};
|
|
|
|
#ifndef SOC_I2C_SUPPORT_HW_CLR_BUS
|
|
struct i2c_esp32_pin {
|
|
const char *gpio_name;
|
|
int sig_out;
|
|
int sig_in;
|
|
gpio_pin_t pin;
|
|
};
|
|
#endif
|
|
|
|
struct i2c_esp32_data {
|
|
i2c_hal_context_t hal;
|
|
struct k_sem cmd_sem;
|
|
struct k_sem transfer_sem;
|
|
volatile enum i2c_status_t status;
|
|
uint32_t dev_config;
|
|
int cmd_idx;
|
|
int irq_line;
|
|
#ifndef SOC_I2C_SUPPORT_HW_CLR_BUS
|
|
const struct device *scl_gpio;
|
|
const struct device *sda_gpio;
|
|
#endif
|
|
};
|
|
|
|
typedef void (*irq_connect_cb)(void);
|
|
|
|
struct i2c_esp32_config {
|
|
int index;
|
|
|
|
const struct device *clock_dev;
|
|
#ifndef SOC_I2C_SUPPORT_HW_CLR_BUS
|
|
const struct i2c_esp32_pin scl;
|
|
const struct i2c_esp32_pin sda;
|
|
#endif
|
|
const struct pinctrl_dev_config *pcfg;
|
|
|
|
const clock_control_subsys_t clock_subsys;
|
|
|
|
const struct {
|
|
bool tx_lsb_first;
|
|
bool rx_lsb_first;
|
|
} mode;
|
|
|
|
int irq_source;
|
|
|
|
const uint32_t default_config;
|
|
const uint32_t bitrate;
|
|
};
|
|
|
|
/* I2C clock characteristic, The order is the same as i2c_sclk_t. */
|
|
static uint32_t i2c_clk_alloc[I2C_SCLK_MAX] = {
|
|
0,
|
|
#if SOC_I2C_SUPPORT_APB
|
|
I2C_CLK_LIMIT_APB, /* I2C APB clock characteristic */
|
|
#endif
|
|
#if SOC_I2C_SUPPORT_XTAL
|
|
I2C_CLK_LIMIT_XTAL, /* I2C XTAL characteristic */
|
|
#endif
|
|
#if SOC_I2C_SUPPORT_RTC
|
|
I2C_CLK_LIMIT_RTC, /* I2C 20M RTC characteristic */
|
|
#endif
|
|
#if SOC_I2C_SUPPORT_REF_TICK
|
|
I2C_CLK_LIMIT_REF_TICK, /* I2C REF_TICK characteristic */
|
|
#endif
|
|
};
|
|
|
|
static i2c_sclk_t i2c_get_clk_src(uint32_t clk_freq)
|
|
{
|
|
for (i2c_sclk_t clk = I2C_SCLK_DEFAULT + 1; clk < I2C_SCLK_MAX; clk++) {
|
|
if (clk_freq <= i2c_clk_alloc[clk]) {
|
|
return clk;
|
|
}
|
|
}
|
|
return I2C_SCLK_MAX; /* flag invalid */
|
|
}
|
|
|
|
#ifndef SOC_I2C_SUPPORT_HW_CLR_BUS
|
|
static int i2c_esp32_config_pin(const struct device *dev)
|
|
{
|
|
const struct i2c_esp32_config *config = dev->config;
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
int ret = 0;
|
|
|
|
if (config->index >= SOC_I2C_NUM) {
|
|
LOG_ERR("Invalid I2C peripheral number");
|
|
return -EINVAL;
|
|
}
|
|
|
|
gpio_pin_set(data->sda_gpio, config->sda.pin, 1);
|
|
ret = gpio_pin_configure(data->sda_gpio, config->sda.pin,
|
|
GPIO_PULL_UP | GPIO_OPEN_DRAIN | GPIO_OUTPUT | GPIO_INPUT);
|
|
esp_rom_gpio_matrix_out(config->sda.pin, config->sda.sig_out, 0, 0);
|
|
esp_rom_gpio_matrix_in(config->sda.pin, config->sda.sig_in, 0);
|
|
|
|
gpio_pin_set(data->scl_gpio, config->scl.pin, 1);
|
|
ret |= gpio_pin_configure(data->scl_gpio, config->scl.pin,
|
|
GPIO_PULL_UP | GPIO_OPEN_DRAIN | GPIO_OUTPUT | GPIO_INPUT);
|
|
esp_rom_gpio_matrix_out(config->scl.pin, config->scl.sig_out, 0, 0);
|
|
esp_rom_gpio_matrix_in(config->scl.pin, config->scl.sig_in, 0);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/* Some slave device will die by accident and keep the SDA in low level,
|
|
* in this case, master should send several clock to make the slave release the bus.
|
|
* Slave mode of ESP32 might also get in wrong state that held the SDA low,
|
|
* in this case, master device could send a stop signal to make esp32 slave release the bus.
|
|
**/
|
|
static void IRAM_ATTR i2c_master_clear_bus(const struct device *dev)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
#ifndef SOC_I2C_SUPPORT_HW_CLR_BUS
|
|
const struct i2c_esp32_config *config = dev->config;
|
|
const int scl_half_period = I2C_CLR_BUS_HALF_PERIOD_US; /* use standard 100kHz data rate */
|
|
int i = 0;
|
|
gpio_pin_t scl_io = config->scl.pin;
|
|
gpio_pin_t sda_io = config->sda.pin;
|
|
|
|
gpio_pin_configure(data->scl_gpio, scl_io, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
|
|
gpio_pin_configure(data->sda_gpio, sda_io, GPIO_OUTPUT | GPIO_OPEN_DRAIN | GPIO_INPUT);
|
|
/* If a SLAVE device was in a read operation when the bus was interrupted, */
|
|
/* the SLAVE device is controlling SDA. If the slave is sending a stream of ZERO bytes, */
|
|
/* it will only release SDA during the ACK bit period. So, this reset code needs */
|
|
/* to synchronize the bit stream with either the ACK bit, or a 1 bit to correctly */
|
|
/* generate a STOP condition. */
|
|
gpio_pin_set(data->scl_gpio, scl_io, 0);
|
|
gpio_pin_set(data->sda_gpio, sda_io, 1);
|
|
esp_rom_delay_us(scl_half_period);
|
|
while (!gpio_pin_get(data->sda_gpio, sda_io) && (i++ < I2C_CLR_BUS_SCL_NUM)) {
|
|
gpio_pin_set(data->scl_gpio, scl_io, 1);
|
|
esp_rom_delay_us(scl_half_period);
|
|
gpio_pin_set(data->scl_gpio, scl_io, 0);
|
|
esp_rom_delay_us(scl_half_period);
|
|
}
|
|
gpio_pin_set(data->sda_gpio, sda_io, 0); /* setup for STOP */
|
|
gpio_pin_set(data->scl_gpio, scl_io, 1);
|
|
esp_rom_delay_us(scl_half_period);
|
|
gpio_pin_set(data->sda_gpio, sda_io, 1); /* STOP, SDA low -> high while SCL is HIGH */
|
|
i2c_esp32_config_pin(dev);
|
|
#else
|
|
i2c_hal_master_clr_bus(&data->hal);
|
|
#endif
|
|
i2c_hal_update_config(&data->hal);
|
|
}
|
|
|
|
static void IRAM_ATTR i2c_hw_fsm_reset(const struct device *dev)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
#ifndef SOC_I2C_SUPPORT_HW_FSM_RST
|
|
const struct i2c_esp32_config *config = dev->config;
|
|
int scl_low_period, scl_high_period;
|
|
int scl_start_hold, scl_rstart_setup;
|
|
int scl_stop_hold, scl_stop_setup;
|
|
int sda_hold, sda_sample;
|
|
int timeout;
|
|
uint8_t filter_cfg;
|
|
|
|
i2c_hal_get_scl_timing(&data->hal, &scl_high_period, &scl_low_period);
|
|
i2c_hal_get_start_timing(&data->hal, &scl_rstart_setup, &scl_start_hold);
|
|
i2c_hal_get_stop_timing(&data->hal, &scl_stop_setup, &scl_stop_hold);
|
|
i2c_hal_get_sda_timing(&data->hal, &sda_sample, &sda_hold);
|
|
i2c_hal_get_tout(&data->hal, &timeout);
|
|
i2c_hal_get_filter(&data->hal, &filter_cfg);
|
|
|
|
/* to reset the I2C hw module, we need re-enable the hw */
|
|
clock_control_off(config->clock_dev, config->clock_subsys);
|
|
i2c_master_clear_bus(dev);
|
|
clock_control_on(config->clock_dev, config->clock_subsys);
|
|
|
|
i2c_hal_master_init(&data->hal, config->index);
|
|
i2c_hal_disable_intr_mask(&data->hal, I2C_LL_INTR_MASK);
|
|
i2c_hal_clr_intsts_mask(&data->hal, I2C_LL_INTR_MASK);
|
|
i2c_hal_set_scl_timing(&data->hal, scl_high_period, scl_low_period);
|
|
i2c_hal_set_start_timing(&data->hal, scl_rstart_setup, scl_start_hold);
|
|
i2c_hal_set_stop_timing(&data->hal, scl_stop_setup, scl_stop_hold);
|
|
i2c_hal_set_sda_timing(&data->hal, sda_sample, sda_hold);
|
|
i2c_hal_set_tout(&data->hal, timeout);
|
|
i2c_hal_set_filter(&data->hal, filter_cfg);
|
|
#else
|
|
i2c_hal_master_fsm_rst(&data->hal);
|
|
i2c_master_clear_bus(dev);
|
|
#endif
|
|
i2c_hal_update_config(&data->hal);
|
|
}
|
|
|
|
static int i2c_esp32_recover(const struct device *dev)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
k_sem_take(&data->transfer_sem, K_FOREVER);
|
|
i2c_hw_fsm_reset(dev);
|
|
k_sem_give(&data->transfer_sem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int i2c_esp32_configure(const struct device *dev, uint32_t dev_config)
|
|
{
|
|
const struct i2c_esp32_config *config = dev->config;
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
if (!(dev_config & I2C_MODE_CONTROLLER)) {
|
|
LOG_ERR("Only I2C Master mode supported.");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
data->dev_config = dev_config;
|
|
|
|
i2c_trans_mode_t tx_mode = I2C_DATA_MODE_MSB_FIRST;
|
|
i2c_trans_mode_t rx_mode = I2C_DATA_MODE_MSB_FIRST;
|
|
|
|
if (config->mode.tx_lsb_first) {
|
|
tx_mode = I2C_DATA_MODE_LSB_FIRST;
|
|
}
|
|
|
|
if (config->mode.rx_lsb_first) {
|
|
rx_mode = I2C_DATA_MODE_LSB_FIRST;
|
|
}
|
|
|
|
i2c_hal_master_init(&data->hal, config->index);
|
|
i2c_hal_set_data_mode(&data->hal, tx_mode, rx_mode);
|
|
i2c_hal_set_filter(&data->hal, I2C_FILTER_CYC_NUM_DEF);
|
|
i2c_hal_update_config(&data->hal);
|
|
|
|
if (config->bitrate == 0) {
|
|
LOG_ERR("Error configuring I2C speed.");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
i2c_hal_set_bus_timing(&data->hal, config->bitrate, i2c_get_clk_src(config->bitrate));
|
|
i2c_hal_update_config(&data->hal);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void IRAM_ATTR i2c_esp32_reset_fifo(const struct device *dev)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
/* reset fifo buffers */
|
|
i2c_hal_txfifo_rst(&data->hal);
|
|
i2c_hal_rxfifo_rst(&data->hal);
|
|
}
|
|
|
|
static int IRAM_ATTR i2c_esp32_transmit(const struct device *dev)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
int ret = 0;
|
|
|
|
/* Start transmission*/
|
|
i2c_hal_update_config(&data->hal);
|
|
i2c_hal_trans_start(&data->hal);
|
|
data->cmd_idx = 0;
|
|
|
|
ret = k_sem_take(&data->cmd_sem, K_MSEC(I2C_TRANSFER_TIMEOUT_MSEC));
|
|
if (ret != 0) {
|
|
/* If the I2C slave is powered off or the SDA/SCL is */
|
|
/* connected to ground, for example, I2C hw FSM would get */
|
|
/* stuck in wrong state, we have to reset the I2C module in this case. */
|
|
i2c_hw_fsm_reset(dev);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
if (data->status == I2C_STATUS_TIMEOUT) {
|
|
i2c_hw_fsm_reset(dev);
|
|
ret = -ETIMEDOUT;
|
|
} else if (data->status == I2C_STATUS_ACK_ERROR) {
|
|
ret = -EFAULT;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void IRAM_ATTR i2c_esp32_master_start(const struct device *dev)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
i2c_hw_cmd_t cmd = {
|
|
.op_code = I2C_LL_CMD_RESTART
|
|
};
|
|
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
|
}
|
|
|
|
static void IRAM_ATTR i2c_esp32_master_stop(const struct device *dev)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
i2c_hw_cmd_t cmd = {
|
|
.op_code = I2C_LL_CMD_STOP
|
|
};
|
|
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
|
}
|
|
|
|
static int IRAM_ATTR i2c_esp32_write_addr(const struct device *dev, uint16_t addr)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
uint8_t addr_len = 1;
|
|
uint8_t addr_byte = addr & 0xFF;
|
|
|
|
data->status = I2C_STATUS_WRITE;
|
|
|
|
/* write address value in tx buffer */
|
|
i2c_hal_write_txfifo(&data->hal, &addr_byte, 1);
|
|
if (data->dev_config & I2C_ADDR_10_BITS) {
|
|
addr_byte = (addr >> 8) & 0xFF;
|
|
i2c_hal_write_txfifo(&data->hal, &addr_byte, 1);
|
|
addr_len++;
|
|
}
|
|
|
|
const i2c_hw_cmd_t cmd_end = {
|
|
.op_code = I2C_LL_CMD_END,
|
|
};
|
|
|
|
i2c_hw_cmd_t cmd = {
|
|
.op_code = I2C_LL_CMD_WRITE,
|
|
.ack_en = true,
|
|
.byte_num = addr_len,
|
|
};
|
|
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd_end, data->cmd_idx++);
|
|
i2c_hal_enable_master_tx_it(&data->hal);
|
|
|
|
return i2c_esp32_transmit(dev);
|
|
}
|
|
|
|
static int IRAM_ATTR i2c_esp32_master_read(const struct device *dev, struct i2c_msg *msg)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
uint8_t rd_filled = 0;
|
|
uint8_t *read_pr = NULL;
|
|
int ret = 0;
|
|
|
|
data->status = I2C_STATUS_READ;
|
|
|
|
i2c_hw_cmd_t cmd = {
|
|
.op_code = I2C_LL_CMD_READ,
|
|
};
|
|
const i2c_hw_cmd_t cmd_end = {
|
|
.op_code = I2C_LL_CMD_END,
|
|
};
|
|
|
|
while (msg->len) {
|
|
rd_filled = (msg->len > SOC_I2C_FIFO_LEN) ? SOC_I2C_FIFO_LEN : (msg->len - 1);
|
|
|
|
read_pr = msg->buf;
|
|
msg->len -= rd_filled;
|
|
|
|
if (rd_filled) {
|
|
cmd.ack_val = 0,
|
|
cmd.byte_num = rd_filled;
|
|
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd_end, data->cmd_idx++);
|
|
i2c_hal_enable_master_rx_it(&data->hal);
|
|
ret = i2c_esp32_transmit(dev);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
i2c_hal_read_rxfifo(&data->hal, read_pr, rd_filled);
|
|
msg->buf += rd_filled;
|
|
}
|
|
|
|
/* I2C master won't acknowledge the last byte read from the
|
|
* slave device. Divide the read command in two segments as
|
|
* recommended by the ESP32 Technical Reference Manual.
|
|
*/
|
|
if (msg->len == 1) {
|
|
cmd.ack_val = 1,
|
|
cmd.byte_num = 1,
|
|
msg->len = 0;
|
|
read_pr = msg->buf;
|
|
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd_end, data->cmd_idx++);
|
|
i2c_hal_enable_master_rx_it(&data->hal);
|
|
ret = i2c_esp32_transmit(dev);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
i2c_hal_read_rxfifo(&data->hal, read_pr, 1);
|
|
msg->buf += 1;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int IRAM_ATTR i2c_esp32_read_msg(const struct device *dev,
|
|
struct i2c_msg *msg, uint16_t addr)
|
|
{
|
|
int ret = 0;
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
/* Set the R/W bit to R */
|
|
addr |= BIT(0);
|
|
|
|
if (msg->flags & I2C_MSG_RESTART) {
|
|
i2c_esp32_master_start(dev);
|
|
ret = i2c_esp32_write_addr(dev, addr);
|
|
if (ret < 0) {
|
|
LOG_ERR("I2C transfer error: %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = i2c_esp32_master_read(dev, msg);
|
|
if (ret < 0) {
|
|
LOG_ERR("I2C transfer error: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (msg->flags & I2C_MSG_STOP) {
|
|
i2c_esp32_master_stop(dev);
|
|
ret = i2c_esp32_transmit(dev);
|
|
if (ret < 0) {
|
|
LOG_ERR("I2C transfer error: %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int IRAM_ATTR i2c_esp32_master_write(const struct device *dev, struct i2c_msg *msg)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
uint8_t wr_filled = 0;
|
|
uint8_t *write_pr = NULL;
|
|
int ret = 0;
|
|
|
|
data->status = I2C_STATUS_WRITE;
|
|
|
|
i2c_hw_cmd_t cmd = {
|
|
.op_code = I2C_LL_CMD_WRITE,
|
|
.ack_en = true,
|
|
};
|
|
|
|
const i2c_hw_cmd_t cmd_end = {
|
|
.op_code = I2C_LL_CMD_END,
|
|
};
|
|
|
|
while (msg->len) {
|
|
wr_filled = (msg->len > SOC_I2C_FIFO_LEN) ? SOC_I2C_FIFO_LEN : msg->len;
|
|
|
|
write_pr = msg->buf;
|
|
msg->buf += wr_filled;
|
|
msg->len -= wr_filled;
|
|
cmd.byte_num = wr_filled;
|
|
|
|
if (wr_filled > 0) {
|
|
i2c_hal_write_txfifo(&data->hal, write_pr, wr_filled);
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd, data->cmd_idx++);
|
|
i2c_hal_write_cmd_reg(&data->hal, cmd_end, data->cmd_idx++);
|
|
i2c_hal_enable_master_tx_it(&data->hal);
|
|
ret = i2c_esp32_transmit(dev);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int IRAM_ATTR i2c_esp32_write_msg(const struct device *dev,
|
|
struct i2c_msg *msg, uint16_t addr)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
int ret = 0;
|
|
|
|
if (msg->flags & I2C_MSG_RESTART) {
|
|
i2c_esp32_master_start(dev);
|
|
ret = i2c_esp32_write_addr(dev, addr);
|
|
if (ret < 0) {
|
|
LOG_ERR("I2C transfer error: %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = i2c_esp32_master_write(dev, msg);
|
|
if (ret < 0) {
|
|
LOG_ERR("I2C transfer error: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (msg->flags & I2C_MSG_STOP) {
|
|
i2c_esp32_master_stop(dev);
|
|
ret = i2c_esp32_transmit(dev);
|
|
if (ret < 0) {
|
|
LOG_ERR("I2C transfer error: %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int IRAM_ATTR i2c_esp32_transfer(const struct device *dev, struct i2c_msg *msgs,
|
|
uint8_t num_msgs, uint16_t addr)
|
|
{
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
struct i2c_msg *current, *next;
|
|
int ret = 0;
|
|
|
|
if (!num_msgs) {
|
|
return 0;
|
|
}
|
|
|
|
/* Check for validity of all messages before transfer */
|
|
current = msgs;
|
|
|
|
/* Add restart flag on first message to send start event */
|
|
current->flags |= I2C_MSG_RESTART;
|
|
|
|
for (int k = 1; k <= num_msgs; k++) {
|
|
if (k < num_msgs) {
|
|
next = current + 1;
|
|
|
|
/* messages of different direction require restart event */
|
|
if ((current->flags & I2C_MSG_RW_MASK) != (next->flags & I2C_MSG_RW_MASK)) {
|
|
if (!(next->flags & I2C_MSG_RESTART)) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* check if there is any stop event in the middle of the transaction */
|
|
if (current->flags & I2C_MSG_STOP) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
} else {
|
|
/* make sure the last message contains stop event */
|
|
current->flags |= I2C_MSG_STOP;
|
|
}
|
|
|
|
current++;
|
|
}
|
|
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
k_sem_take(&data->transfer_sem, K_FOREVER);
|
|
|
|
/* Mask out unused address bits, and make room for R/W bit */
|
|
addr &= BIT_MASK(data->dev_config & I2C_ADDR_10_BITS ? 10 : 7);
|
|
addr <<= 1;
|
|
|
|
for (; num_msgs > 0; num_msgs--, msgs++) {
|
|
|
|
if (data->status == I2C_STATUS_TIMEOUT || i2c_hal_is_bus_busy(&data->hal)) {
|
|
i2c_hw_fsm_reset(dev);
|
|
}
|
|
|
|
/* reset all fifo buffer before start */
|
|
i2c_esp32_reset_fifo(dev);
|
|
|
|
/* These two interrupts some times can not be cleared when the FSM gets stuck. */
|
|
/* So we disable them when these two interrupt occurs and re-enable them here. */
|
|
i2c_hal_disable_intr_mask(&data->hal, I2C_LL_INTR_MASK);
|
|
i2c_hal_clr_intsts_mask(&data->hal, I2C_LL_INTR_MASK);
|
|
|
|
if ((msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
|
|
ret = i2c_esp32_read_msg(dev, msgs, addr);
|
|
} else {
|
|
ret = i2c_esp32_write_msg(dev, msgs, addr);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
k_sem_give(&data->transfer_sem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void IRAM_ATTR i2c_esp32_isr(void *arg)
|
|
{
|
|
const struct device *dev = (const struct device *)arg;
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
i2c_intr_event_t evt_type = I2C_INTR_EVENT_ERR;
|
|
|
|
if (data->status == I2C_STATUS_WRITE) {
|
|
i2c_hal_master_handle_tx_event(&data->hal, &evt_type);
|
|
} else if (data->status == I2C_STATUS_READ) {
|
|
i2c_hal_master_handle_rx_event(&data->hal, &evt_type);
|
|
}
|
|
|
|
if (evt_type == I2C_INTR_EVENT_NACK) {
|
|
data->status = I2C_STATUS_ACK_ERROR;
|
|
} else if (evt_type == I2C_INTR_EVENT_TOUT) {
|
|
data->status = I2C_STATUS_TIMEOUT;
|
|
} else if (evt_type == I2C_INTR_EVENT_ARBIT_LOST) {
|
|
data->status = I2C_STATUS_TIMEOUT;
|
|
} else if (evt_type == I2C_INTR_EVENT_TRANS_DONE) {
|
|
data->status = I2C_STATUS_DONE;
|
|
}
|
|
|
|
k_sem_give(&data->cmd_sem);
|
|
}
|
|
|
|
static const struct i2c_driver_api i2c_esp32_driver_api = {
|
|
.configure = i2c_esp32_configure,
|
|
.transfer = i2c_esp32_transfer,
|
|
.recover_bus = i2c_esp32_recover
|
|
};
|
|
|
|
static int IRAM_ATTR i2c_esp32_init(const struct device *dev)
|
|
{
|
|
const struct i2c_esp32_config *config = dev->config;
|
|
#ifndef SOC_I2C_SUPPORT_HW_CLR_BUS
|
|
struct i2c_esp32_data *data = (struct i2c_esp32_data *const)(dev)->data;
|
|
|
|
if (config->scl.gpio_name == NULL || config->sda.gpio_name == NULL) {
|
|
LOG_ERR("Failed to get GPIO device");
|
|
return -EINVAL;
|
|
}
|
|
|
|
data->scl_gpio = device_get_binding(config->scl.gpio_name);
|
|
if (!data->scl_gpio) {
|
|
LOG_ERR("Failed to get SCL GPIO device");
|
|
return -EINVAL;
|
|
}
|
|
|
|
data->sda_gpio = device_get_binding(config->sda.gpio_name);
|
|
if (!data->sda_gpio) {
|
|
LOG_ERR("Failed to get SDA GPIO device");
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
int ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to configure I2C pins");
|
|
return -EINVAL;
|
|
}
|
|
|
|
clock_control_on(config->clock_dev, config->clock_subsys);
|
|
|
|
esp_intr_alloc(config->irq_source, 0, i2c_esp32_isr, (void *)dev, NULL);
|
|
|
|
return i2c_esp32_configure(dev, config->default_config);
|
|
}
|
|
|
|
#ifndef SOC_I2C_SUPPORT_HW_CLR_BUS
|
|
#define GPIO0_NAME COND_CODE_1(DT_NODE_HAS_STATUS(DT_NODELABEL(gpio0), okay), \
|
|
(DT_LABEL(DT_INST(0, espressif_esp32_gpio))), (NULL))
|
|
#define GPIO1_NAME COND_CODE_1(DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay), \
|
|
(DT_LABEL(DT_INST(1, espressif_esp32_gpio))), (NULL))
|
|
|
|
#define DT_I2C_ESP32_GPIO_NAME(idx, pin) ( \
|
|
DT_INST_PROP(idx, pin) < 32 ? GPIO0_NAME : GPIO1_NAME)
|
|
|
|
#define I2C_ESP32_GET_PIN_INFO(idx) \
|
|
.scl = { \
|
|
.gpio_name = DT_I2C_ESP32_GPIO_NAME(idx, scl_pin), \
|
|
.sig_out = I2CEXT##idx##_SCL_OUT_IDX, \
|
|
.sig_in = I2CEXT##idx##_SCL_IN_IDX, \
|
|
.pin = DT_INST_PROP(idx, scl_pin), \
|
|
}, \
|
|
.sda = { \
|
|
.gpio_name = DT_I2C_ESP32_GPIO_NAME(idx, sda_pin), \
|
|
.sig_out = I2CEXT##idx##_SDA_OUT_IDX, \
|
|
.sig_in = I2CEXT##idx##_SDA_IN_IDX, \
|
|
.pin = DT_INST_PROP(idx, sda_pin), \
|
|
},
|
|
#else
|
|
#define I2C_ESP32_GET_PIN_INFO(idx)
|
|
#endif /* SOC_I2C_SUPPORT_HW_CLR_BUS */
|
|
|
|
#define I2C_ESP32_FREQUENCY(bitrate) \
|
|
(bitrate == I2C_BITRATE_STANDARD ? KHZ(100) \
|
|
: bitrate == I2C_BITRATE_FAST ? KHZ(400) \
|
|
: bitrate == I2C_BITRATE_FAST_PLUS ? MHZ(1) : 0)
|
|
#define I2C_FREQUENCY(idx) \
|
|
I2C_ESP32_FREQUENCY(DT_INST_PROP(idx, clock_frequency))
|
|
|
|
#define ESP32_I2C_INIT(idx) \
|
|
\
|
|
PINCTRL_DT_INST_DEFINE(idx); \
|
|
\
|
|
static struct i2c_esp32_data i2c_esp32_data_##idx = { \
|
|
.hal = { \
|
|
.dev = (i2c_dev_t *) DT_REG_ADDR(DT_NODELABEL(i2c##idx)), \
|
|
}, \
|
|
.cmd_sem = Z_SEM_INITIALIZER( \
|
|
i2c_esp32_data_##idx.cmd_sem, 0, 1), \
|
|
.transfer_sem = Z_SEM_INITIALIZER( \
|
|
i2c_esp32_data_##idx.transfer_sem, 1, 1) \
|
|
}; \
|
|
\
|
|
static const struct i2c_esp32_config i2c_esp32_config_##idx = { \
|
|
.index = idx, \
|
|
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \
|
|
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(idx, offset), \
|
|
I2C_ESP32_GET_PIN_INFO(idx) \
|
|
.mode = { \
|
|
.tx_lsb_first = DT_INST_PROP(idx, tx_lsb), \
|
|
.rx_lsb_first = DT_INST_PROP(idx, rx_lsb), \
|
|
}, \
|
|
.irq_source = ETS_I2C_EXT##idx##_INTR_SOURCE, \
|
|
.bitrate = I2C_FREQUENCY(idx), \
|
|
.default_config = I2C_MODE_CONTROLLER, \
|
|
}; \
|
|
I2C_DEVICE_DT_DEFINE(DT_NODELABEL(i2c##idx), \
|
|
i2c_esp32_init, \
|
|
NULL, \
|
|
&i2c_esp32_data_##idx, \
|
|
&i2c_esp32_config_##idx, \
|
|
POST_KERNEL, \
|
|
CONFIG_I2C_INIT_PRIORITY, \
|
|
&i2c_esp32_driver_api); \
|
|
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(i2c0), okay)
|
|
#ifndef SOC_I2C_SUPPORT_HW_CLR_BUS
|
|
#if !DT_PROP(DT_INST(0, espressif_esp32_i2c), sda_pin) || \
|
|
!DT_PROP(DT_INST(0, espressif_esp32_i2c), scl_pin)
|
|
#error "Missing <sda-pin> and <scl-pin> properties to build for this target."
|
|
#endif
|
|
#else
|
|
#if DT_PROP(DT_INST(0, espressif_esp32_i2c), sda_pin) || \
|
|
DT_PROP(DT_INST(0, espressif_esp32_i2c), scl_pin)
|
|
#error "Properties <sda-pin> and <scl-pin> are not required for this target."
|
|
#endif
|
|
#endif
|
|
ESP32_I2C_INIT(0);
|
|
#endif
|
|
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(i2c1), okay)
|
|
#ifndef SOC_I2C_SUPPORT_HW_CLR_BUS
|
|
#if !DT_PROP(DT_INST(1, espressif_esp32_i2c), sda_pin) || \
|
|
!DT_PROP(DT_INST(1, espressif_esp32_i2c), scl_pin)
|
|
#error "Missing <sda-pin> and <scl-pin> properties to build for this target."
|
|
#endif
|
|
#else
|
|
#if DT_PROP(DT_INST(1, espressif_esp32_i2c), sda_pin) || \
|
|
DT_PROP(DT_INST(1, espressif_esp32_i2c), scl_pin)
|
|
#error "Properties <sda-pin> and <scl-pin> are not required for this target."
|
|
#endif
|
|
#endif
|
|
ESP32_I2C_INIT(1);
|
|
#endif
|