|  | /* | 
|  | * Copyright (c) 2021 BrainCo Inc. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT gd_gd32_i2c | 
|  |  | 
|  | #include <errno.h> | 
|  |  | 
|  | #include <zephyr/drivers/clock_control.h> | 
|  | #include <zephyr/drivers/clock_control/gd32.h> | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/devicetree.h> | 
|  | #include <zephyr/drivers/pinctrl.h> | 
|  | #include <zephyr/drivers/reset.h> | 
|  | #include <zephyr/drivers/i2c.h> | 
|  |  | 
|  | #include <gd32_i2c.h> | 
|  |  | 
|  | #include <zephyr/logging/log.h> | 
|  | #include <zephyr/irq.h> | 
|  | LOG_MODULE_REGISTER(i2c_gd32, CONFIG_I2C_LOG_LEVEL); | 
|  |  | 
|  | #include "i2c-priv.h" | 
|  |  | 
|  | /* Bus error */ | 
|  | #define I2C_GD32_ERR_BERR BIT(0) | 
|  | /* Arbitration lost */ | 
|  | #define I2C_GD32_ERR_LARB BIT(1) | 
|  | /* No ACK received */ | 
|  | #define I2C_GD32_ERR_AERR BIT(2) | 
|  | /* I2C bus busy */ | 
|  | #define I2C_GD32_ERR_BUSY BIT(4) | 
|  |  | 
|  | struct i2c_gd32_config { | 
|  | uint32_t reg; | 
|  | uint32_t bitrate; | 
|  | uint16_t clkid; | 
|  | struct reset_dt_spec reset; | 
|  | const struct pinctrl_dev_config *pcfg; | 
|  | void (*irq_cfg_func)(void); | 
|  | }; | 
|  |  | 
|  | struct i2c_gd32_data { | 
|  | struct k_sem bus_mutex; | 
|  | struct k_sem sync_sem; | 
|  | uint32_t dev_config; | 
|  | uint16_t addr1; | 
|  | uint16_t addr2; | 
|  | uint32_t xfer_len; | 
|  | struct i2c_msg *current; | 
|  | uint8_t errs; | 
|  | bool is_restart; | 
|  | }; | 
|  |  | 
|  | static inline void i2c_gd32_enable_interrupts(const struct i2c_gd32_config *cfg) | 
|  | { | 
|  | I2C_CTL1(cfg->reg) |= I2C_CTL1_ERRIE; | 
|  | I2C_CTL1(cfg->reg) |= I2C_CTL1_EVIE; | 
|  | I2C_CTL1(cfg->reg) |= I2C_CTL1_BUFIE; | 
|  | } | 
|  |  | 
|  | static inline void i2c_gd32_disable_interrupts(const struct i2c_gd32_config *cfg) | 
|  | { | 
|  | I2C_CTL1(cfg->reg) &= ~I2C_CTL1_ERRIE; | 
|  | I2C_CTL1(cfg->reg) &= ~I2C_CTL1_EVIE; | 
|  | I2C_CTL1(cfg->reg) &= ~I2C_CTL1_BUFIE; | 
|  | } | 
|  |  | 
|  | static inline void i2c_gd32_xfer_read(struct i2c_gd32_data *data, | 
|  | const struct i2c_gd32_config *cfg) | 
|  | { | 
|  | data->current->len--; | 
|  | *data->current->buf = I2C_DATA(cfg->reg); | 
|  | data->current->buf++; | 
|  |  | 
|  | if ((data->xfer_len > 0U) && | 
|  | (data->current->len == 0U)) { | 
|  | data->current++; | 
|  | } | 
|  | } | 
|  |  | 
|  | static inline void i2c_gd32_xfer_write(struct i2c_gd32_data *data, | 
|  | const struct i2c_gd32_config *cfg) | 
|  | { | 
|  | data->current->len--; | 
|  | I2C_DATA(cfg->reg) = *data->current->buf; | 
|  | data->current->buf++; | 
|  |  | 
|  | if ((data->xfer_len > 0U) && | 
|  | (data->current->len == 0U)) { | 
|  | data->current++; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void i2c_gd32_handle_rbne(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  |  | 
|  | switch (data->xfer_len) { | 
|  | case 0: | 
|  | /* Unwanted data received, ignore it. */ | 
|  | k_sem_give(&data->sync_sem); | 
|  | break; | 
|  | case 1: | 
|  | /* If total_read_length == 1, read the data directly. */ | 
|  | data->xfer_len--; | 
|  | i2c_gd32_xfer_read(data, cfg); | 
|  |  | 
|  | k_sem_give(&data->sync_sem); | 
|  |  | 
|  | break; | 
|  | case 2: | 
|  | __fallthrough; | 
|  | case 3: | 
|  | /* | 
|  | * If total_read_length == 2, or total_read_length > 3 | 
|  | * and remaining_read_length == 3, disable the RBNE | 
|  | * interrupt. | 
|  | * Remaining data will be read from BTC interrupt. | 
|  | */ | 
|  | I2C_CTL1(cfg->reg) &= ~I2C_CTL1_BUFIE; | 
|  | break; | 
|  | default: | 
|  | /* | 
|  | * If total_read_length > 3 and remaining_read_length > 3, | 
|  | * read the data directly. | 
|  | */ | 
|  | data->xfer_len--; | 
|  | i2c_gd32_xfer_read(data, cfg); | 
|  | break; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | static void i2c_gd32_handle_tbe(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  |  | 
|  | if (data->xfer_len > 0U) { | 
|  | data->xfer_len--; | 
|  | if (data->xfer_len == 0U) { | 
|  | /* | 
|  | * This is the last data to transmit, disable the TBE interrupt. | 
|  | * Use the BTC interrupt to indicate the write data complete state. | 
|  | */ | 
|  | I2C_CTL1(cfg->reg) &= ~I2C_CTL1_BUFIE; | 
|  | } | 
|  | i2c_gd32_xfer_write(data, cfg); | 
|  |  | 
|  | } else { | 
|  | /* Enter stop condition */ | 
|  | I2C_CTL0(cfg->reg) |= I2C_CTL0_STOP; | 
|  |  | 
|  | k_sem_give(&data->sync_sem); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void i2c_gd32_handle_btc(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  |  | 
|  | if (data->current->flags & I2C_MSG_READ) { | 
|  | uint32_t counter = 0U; | 
|  |  | 
|  | switch (data->xfer_len) { | 
|  | case 2: | 
|  | /* Stop condition must be generated before reading the last two bytes. */ | 
|  | I2C_CTL0(cfg->reg) |= I2C_CTL0_STOP; | 
|  |  | 
|  | for (counter = 2U; counter > 0; counter--) { | 
|  | data->xfer_len--; | 
|  | i2c_gd32_xfer_read(data, cfg); | 
|  | } | 
|  |  | 
|  | k_sem_give(&data->sync_sem); | 
|  |  | 
|  | break; | 
|  | case 3: | 
|  | /* Clear ACKEN bit */ | 
|  | I2C_CTL0(cfg->reg) &= ~I2C_CTL0_ACKEN; | 
|  |  | 
|  | data->xfer_len--; | 
|  | i2c_gd32_xfer_read(data, cfg); | 
|  |  | 
|  | break; | 
|  | default: | 
|  | i2c_gd32_handle_rbne(dev); | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | i2c_gd32_handle_tbe(dev); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void i2c_gd32_handle_addsend(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  |  | 
|  | if ((data->current->flags & I2C_MSG_READ) && (data->xfer_len <= 2U)) { | 
|  | I2C_CTL0(cfg->reg) &= ~I2C_CTL0_ACKEN; | 
|  | } | 
|  |  | 
|  | /* Clear ADDSEND bit */ | 
|  | I2C_STAT0(cfg->reg); | 
|  | I2C_STAT1(cfg->reg); | 
|  |  | 
|  | if (data->is_restart) { | 
|  | data->is_restart = false; | 
|  | data->current->flags &= ~I2C_MSG_RW_MASK; | 
|  | data->current->flags |= I2C_MSG_READ; | 
|  | /* Enter repeated start condition */ | 
|  | I2C_CTL0(cfg->reg) |= I2C_CTL0_START; | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | if ((data->current->flags & I2C_MSG_READ) && (data->xfer_len == 1U)) { | 
|  | /* Enter stop condition */ | 
|  | I2C_CTL0(cfg->reg) |= I2C_CTL0_STOP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void i2c_gd32_event_isr(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  | uint32_t stat; | 
|  |  | 
|  | stat = I2C_STAT0(cfg->reg); | 
|  |  | 
|  | if (stat & I2C_STAT0_SBSEND) { | 
|  | if (data->current->flags & I2C_MSG_READ) { | 
|  | I2C_DATA(cfg->reg) = (data->addr1 << 1U) | 1U; | 
|  | } else { | 
|  | I2C_DATA(cfg->reg) = (data->addr1 << 1U) | 0U; | 
|  | } | 
|  | } else if (stat & I2C_STAT0_ADD10SEND) { | 
|  | I2C_DATA(cfg->reg) = data->addr2; | 
|  | } else if (stat & I2C_STAT0_ADDSEND) { | 
|  | i2c_gd32_handle_addsend(dev); | 
|  | /* | 
|  | * Must handle BTC first. | 
|  | * For I2C_STAT0, BTC is the superset of RBNE and TBE. | 
|  | */ | 
|  | } else if (stat & I2C_STAT0_BTC) { | 
|  | i2c_gd32_handle_btc(dev); | 
|  | } else if (stat & I2C_STAT0_RBNE) { | 
|  | i2c_gd32_handle_rbne(dev); | 
|  | } else if (stat & I2C_STAT0_TBE) { | 
|  | i2c_gd32_handle_tbe(dev); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void i2c_gd32_error_isr(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  | uint32_t stat; | 
|  |  | 
|  | stat = I2C_STAT0(cfg->reg); | 
|  |  | 
|  | if (stat & I2C_STAT0_BERR) { | 
|  | I2C_STAT0(cfg->reg) &= ~I2C_STAT0_BERR; | 
|  | data->errs |= I2C_GD32_ERR_BERR; | 
|  | } | 
|  |  | 
|  | if (stat & I2C_STAT0_LOSTARB) { | 
|  | I2C_STAT0(cfg->reg) &= ~I2C_STAT0_LOSTARB; | 
|  | data->errs |= I2C_GD32_ERR_LARB; | 
|  | } | 
|  |  | 
|  | if (stat & I2C_STAT0_AERR) { | 
|  | I2C_STAT0(cfg->reg) &= ~I2C_STAT0_AERR; | 
|  | data->errs |= I2C_GD32_ERR_AERR; | 
|  | } | 
|  |  | 
|  | if (data->errs != 0U) { | 
|  | /* Enter stop condition */ | 
|  | I2C_CTL0(cfg->reg) |= I2C_CTL0_STOP; | 
|  |  | 
|  | k_sem_give(&data->sync_sem); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void i2c_gd32_log_err(struct i2c_gd32_data *data) | 
|  | { | 
|  | if (data->errs & I2C_GD32_ERR_BERR) { | 
|  | LOG_ERR("Bus error"); | 
|  | } | 
|  |  | 
|  | if (data->errs & I2C_GD32_ERR_LARB) { | 
|  | LOG_ERR("Arbitration lost"); | 
|  | } | 
|  |  | 
|  | if (data->errs & I2C_GD32_ERR_AERR) { | 
|  | LOG_ERR("No ACK received"); | 
|  | } | 
|  |  | 
|  | if (data->errs & I2C_GD32_ERR_BUSY) { | 
|  | LOG_ERR("I2C bus busy"); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void i2c_gd32_xfer_begin(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  |  | 
|  | k_sem_reset(&data->sync_sem); | 
|  |  | 
|  | data->errs = 0U; | 
|  | data->is_restart = false; | 
|  |  | 
|  | /* Default to set ACKEN bit. */ | 
|  | I2C_CTL0(cfg->reg) |= I2C_CTL0_ACKEN; | 
|  |  | 
|  | if (data->current->flags & I2C_MSG_READ) { | 
|  | /* For 2 bytes read, use POAP bit to give NACK for the last data receiving. */ | 
|  | if (data->xfer_len == 2U) { | 
|  | I2C_CTL0(cfg->reg) |= I2C_CTL0_POAP; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * For read on 10 bits address mode, start condition will happen twice. | 
|  | * Transfer sequence as below: | 
|  | *   S addr1+W addr2 S addr1+R | 
|  | * Use a is_restart flag to cover this case. | 
|  | */ | 
|  | if (data->dev_config & I2C_ADDR_10_BITS) { | 
|  | data->is_restart = true; | 
|  | data->current->flags &= ~I2C_MSG_RW_MASK; | 
|  | } | 
|  | } | 
|  |  | 
|  | i2c_gd32_enable_interrupts(cfg); | 
|  |  | 
|  | /* Enter repeated start condition */ | 
|  | I2C_CTL0(cfg->reg) |= I2C_CTL0_START; | 
|  | } | 
|  |  | 
|  | static int i2c_gd32_xfer_end(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  |  | 
|  | i2c_gd32_disable_interrupts(cfg); | 
|  |  | 
|  | /* Wait for stop condition is done. */ | 
|  | while (I2C_STAT1(cfg->reg) & I2C_STAT1_I2CBSY) { | 
|  | /* NOP */ | 
|  | } | 
|  |  | 
|  | if (data->errs) { | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_gd32_msg_read(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  |  | 
|  | if (I2C_STAT1(cfg->reg) & I2C_STAT1_I2CBSY) { | 
|  | data->errs = I2C_GD32_ERR_BUSY; | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | i2c_gd32_xfer_begin(dev); | 
|  |  | 
|  | k_sem_take(&data->sync_sem, K_FOREVER); | 
|  |  | 
|  | return i2c_gd32_xfer_end(dev); | 
|  | } | 
|  |  | 
|  | static int i2c_gd32_msg_write(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  |  | 
|  | if (I2C_STAT1(cfg->reg) & I2C_STAT1_I2CBSY) { | 
|  | data->errs = I2C_GD32_ERR_BUSY; | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | i2c_gd32_xfer_begin(dev); | 
|  |  | 
|  | k_sem_take(&data->sync_sem, K_FOREVER); | 
|  |  | 
|  | return i2c_gd32_xfer_end(dev); | 
|  | } | 
|  |  | 
|  | static int i2c_gd32_transfer(const struct device *dev, | 
|  | struct i2c_msg *msgs, | 
|  | uint8_t num_msgs, | 
|  | uint16_t addr) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  | struct i2c_msg *current, *next; | 
|  | uint8_t itr; | 
|  | int err = 0; | 
|  |  | 
|  | current = msgs; | 
|  |  | 
|  | /* First message flags implicitly contain I2C_MSG_RESTART flag. */ | 
|  | current->flags |= I2C_MSG_RESTART; | 
|  |  | 
|  | for (uint8_t i = 1; i <= num_msgs; i++) { | 
|  |  | 
|  | if (i < num_msgs) { | 
|  | next = current + 1; | 
|  |  | 
|  | /* | 
|  | * If there have a R/W transfer state change between messages, | 
|  | * An explicit I2C_MSG_RESTART flag is needed for the second message. | 
|  | */ | 
|  | if ((current->flags & I2C_MSG_RW_MASK) != | 
|  | (next->flags & I2C_MSG_RW_MASK)) { | 
|  | if ((next->flags & I2C_MSG_RESTART) == 0U) { | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Only the last message need I2C_MSG_STOP flag to free the Bus. */ | 
|  | if (current->flags & I2C_MSG_STOP) { | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | if ((current->buf == NULL) || | 
|  | (current->len == 0U)) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | current++; | 
|  | } | 
|  |  | 
|  | k_sem_take(&data->bus_mutex, K_FOREVER); | 
|  |  | 
|  | /* Enable i2c device */ | 
|  | I2C_CTL0(cfg->reg) |= I2C_CTL0_I2CEN; | 
|  |  | 
|  | if (data->dev_config & I2C_ADDR_10_BITS) { | 
|  | data->addr1 = 0xF0 | ((addr & BITS(8, 9)) >> 8U); | 
|  | data->addr2 = addr & BITS(0, 7); | 
|  | } else { | 
|  | data->addr1 = addr & BITS(0, 6); | 
|  | } | 
|  |  | 
|  | for (uint8_t i = 0; i < num_msgs; i = itr) { | 
|  | data->current = &msgs[i]; | 
|  | data->xfer_len = msgs[i].len; | 
|  |  | 
|  | for (itr = i + 1; itr < num_msgs; itr++) { | 
|  | if ((data->current->flags & I2C_MSG_RW_MASK) != | 
|  | (msgs[itr].flags & I2C_MSG_RW_MASK)) { | 
|  | break; | 
|  | } | 
|  | data->xfer_len += msgs[itr].len; | 
|  | } | 
|  |  | 
|  | if (data->current->flags & I2C_MSG_READ) { | 
|  | err = i2c_gd32_msg_read(dev); | 
|  | } else { | 
|  | err = i2c_gd32_msg_write(dev); | 
|  | } | 
|  |  | 
|  | if (err < 0) { | 
|  | i2c_gd32_log_err(data); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Disable I2C device */ | 
|  | I2C_CTL0(cfg->reg) &= ~I2C_CTL0_I2CEN; | 
|  |  | 
|  | k_sem_give(&data->bus_mutex); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static int i2c_gd32_configure(const struct device *dev, | 
|  | uint32_t dev_config) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  | uint32_t pclk1, freq, clkc; | 
|  | int err = 0; | 
|  |  | 
|  | k_sem_take(&data->bus_mutex, K_FOREVER); | 
|  |  | 
|  | /* Disable I2C device */ | 
|  | I2C_CTL0(cfg->reg) &= ~I2C_CTL0_I2CEN; | 
|  |  | 
|  | (void)clock_control_get_rate(GD32_CLOCK_CONTROLLER, | 
|  | (clock_control_subsys_t)&cfg->clkid, | 
|  | &pclk1); | 
|  |  | 
|  | /* i2c clock frequency, us */ | 
|  | freq = pclk1 / 1000000U; | 
|  | if (freq > I2CCLK_MAX) { | 
|  | LOG_ERR("I2C max clock freq %u, current is %u\n", | 
|  | I2CCLK_MAX, freq); | 
|  | err = -ENOTSUP; | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Refer from SoC user manual. | 
|  | * In standard mode: | 
|  | *   T_high = CLKC * T_pclk1 | 
|  | *   T_low  = CLKC * T_pclk1 | 
|  | * | 
|  | * In fast mode and fast mode plus with DTCY=1: | 
|  | *   T_high = 9 * CLKC * T_pclk1 | 
|  | *   T_low  = 16 * CLKC * T_pclk1 | 
|  | * | 
|  | * T_pclk1 is reciprocal of pclk1: | 
|  | *   T_pclk1 = 1 / pclk1 | 
|  | * | 
|  | * T_high and T_low construct the bit transfer: | 
|  | *  T_high + T_low = 1 / bitrate | 
|  | * | 
|  | * And then, we can get the CLKC equation. | 
|  | * Standard mode: | 
|  | *   CLKC = pclk1 / (bitrate * 2) | 
|  | * Fast mode and fast mode plus: | 
|  | *   CLKC = pclk1 / (bitrate * 25) | 
|  | * | 
|  | * Variable list: | 
|  | *   T_high:  high period of the SCL clock | 
|  | *   T_low:   low period of the SCL clock | 
|  | *   T_pclk1: duration of single pclk1 pulse | 
|  | *   pclk1:   i2c device clock frequency | 
|  | *   bitrate: 100 Kbits for standard mode | 
|  | */ | 
|  | switch (I2C_SPEED_GET(dev_config)) { | 
|  | case I2C_SPEED_STANDARD: | 
|  | if (freq < I2CCLK_MIN) { | 
|  | LOG_ERR("I2C standard-mode min clock freq %u, current is %u\n", | 
|  | I2CCLK_MIN, freq); | 
|  | err = -ENOTSUP; | 
|  | goto error; | 
|  | } | 
|  | I2C_CTL1(cfg->reg) &= ~I2C_CTL1_I2CCLK; | 
|  | I2C_CTL1(cfg->reg) |= freq; | 
|  |  | 
|  | /* Standard-mode risetime maximum value: 1000ns */ | 
|  | if (freq == I2CCLK_MAX) { | 
|  | I2C_RT(cfg->reg) = I2CCLK_MAX; | 
|  | } else { | 
|  | I2C_RT(cfg->reg) = freq + 1U; | 
|  | } | 
|  |  | 
|  | /* CLKC = pclk1 / (bitrate * 2) */ | 
|  | clkc = pclk1 / (I2C_BITRATE_STANDARD * 2U); | 
|  |  | 
|  | I2C_CKCFG(cfg->reg) &= ~I2C_CKCFG_CLKC; | 
|  | I2C_CKCFG(cfg->reg) |= clkc; | 
|  | /* standard-mode */ | 
|  | I2C_CKCFG(cfg->reg) &= ~I2C_CKCFG_FAST; | 
|  |  | 
|  | break; | 
|  | case I2C_SPEED_FAST: | 
|  | if (freq < I2CCLK_FM_MIN) { | 
|  | LOG_ERR("I2C fast-mode min clock freq %u, current is %u\n", | 
|  | I2CCLK_FM_MIN, freq); | 
|  | err = -ENOTSUP; | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* Fast-mode risetime maximum value: 300ns */ | 
|  | I2C_RT(cfg->reg) = freq * 300U / 1000U + 1U; | 
|  |  | 
|  | /* CLKC = pclk1 / (bitrate * 25) */ | 
|  | clkc = pclk1 / (I2C_BITRATE_FAST * 25U); | 
|  | if (clkc == 0U) { | 
|  | clkc = 1U; | 
|  | } | 
|  |  | 
|  | /* Default DCTY to 1 */ | 
|  | I2C_CKCFG(cfg->reg) |= I2C_CKCFG_DTCY; | 
|  | I2C_CKCFG(cfg->reg) &= ~I2C_CKCFG_CLKC; | 
|  | I2C_CKCFG(cfg->reg) |= clkc; | 
|  | /* Transfer mode: fast-mode */ | 
|  | I2C_CKCFG(cfg->reg) |= I2C_CKCFG_FAST; | 
|  |  | 
|  | #ifdef I2C_FMPCFG | 
|  | /* Disable transfer mode: fast-mode plus */ | 
|  | I2C_FMPCFG(cfg->reg) &= ~I2C_FMPCFG_FMPEN; | 
|  | #endif /* I2C_FMPCFG */ | 
|  |  | 
|  | break; | 
|  | #ifdef I2C_FMPCFG | 
|  | case I2C_SPEED_FAST_PLUS: | 
|  | if (freq < I2CCLK_FM_PLUS_MIN) { | 
|  | LOG_ERR("I2C fast-mode plus min clock freq %u, current is %u\n", | 
|  | I2CCLK_FM_PLUS_MIN, freq); | 
|  | err = -ENOTSUP; | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* Fast-mode plus risetime maximum value: 120ns */ | 
|  | I2C_RT(cfg->reg) = freq * 120U / 1000U + 1U; | 
|  |  | 
|  | /* CLKC = pclk1 / (bitrate * 25) */ | 
|  | clkc = pclk1 / (I2C_BITRATE_FAST_PLUS * 25U); | 
|  | if (clkc == 0U) { | 
|  | clkc = 1U; | 
|  | } | 
|  |  | 
|  | /* Default DCTY to 1 */ | 
|  | I2C_CKCFG(cfg->reg) |= I2C_CKCFG_DTCY; | 
|  | I2C_CKCFG(cfg->reg) &= ~I2C_CKCFG_CLKC; | 
|  | I2C_CKCFG(cfg->reg) |= clkc; | 
|  | /* Transfer mode: fast-mode */ | 
|  | I2C_CKCFG(cfg->reg) |= I2C_CKCFG_FAST; | 
|  |  | 
|  | /* Enable transfer mode: fast-mode plus */ | 
|  | I2C_FMPCFG(cfg->reg) |= I2C_FMPCFG_FMPEN; | 
|  |  | 
|  | break; | 
|  | #endif /* I2C_FMPCFG */ | 
|  | default: | 
|  | err = -EINVAL; | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | data->dev_config = dev_config; | 
|  | error: | 
|  | k_sem_give(&data->bus_mutex); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static DEVICE_API(i2c, i2c_gd32_driver_api) = { | 
|  | .configure = i2c_gd32_configure, | 
|  | .transfer = i2c_gd32_transfer, | 
|  | #ifdef CONFIG_I2C_RTIO | 
|  | .iodev_submit = i2c_iodev_submit_fallback, | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | static int i2c_gd32_init(const struct device *dev) | 
|  | { | 
|  | struct i2c_gd32_data *data = dev->data; | 
|  | const struct i2c_gd32_config *cfg = dev->config; | 
|  | uint32_t bitrate_cfg; | 
|  | int err; | 
|  |  | 
|  | err = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /* Mutex semaphore to protect the i2c api in multi-thread env. */ | 
|  | k_sem_init(&data->bus_mutex, 1, 1); | 
|  |  | 
|  | /* Sync semaphore to sync i2c state between isr and transfer api. */ | 
|  | k_sem_init(&data->sync_sem, 0, K_SEM_MAX_LIMIT); | 
|  |  | 
|  | (void)clock_control_on(GD32_CLOCK_CONTROLLER, | 
|  | (clock_control_subsys_t)&cfg->clkid); | 
|  |  | 
|  | (void)reset_line_toggle_dt(&cfg->reset); | 
|  |  | 
|  | cfg->irq_cfg_func(); | 
|  |  | 
|  | bitrate_cfg = i2c_map_dt_bitrate(cfg->bitrate); | 
|  |  | 
|  | i2c_gd32_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #define I2C_GD32_INIT(inst)							\ | 
|  | PINCTRL_DT_INST_DEFINE(inst);						\ | 
|  | static void i2c_gd32_irq_cfg_func_##inst(void)				\ | 
|  | {									\ | 
|  | IRQ_CONNECT(DT_INST_IRQ_BY_NAME(inst, event, irq),		\ | 
|  | DT_INST_IRQ_BY_NAME(inst, event, priority),		\ | 
|  | i2c_gd32_event_isr,					\ | 
|  | DEVICE_DT_INST_GET(inst),				\ | 
|  | 0);							\ | 
|  | irq_enable(DT_INST_IRQ_BY_NAME(inst, event, irq));		\ | 
|  | \ | 
|  | IRQ_CONNECT(DT_INST_IRQ_BY_NAME(inst, error, irq),		\ | 
|  | DT_INST_IRQ_BY_NAME(inst, error, priority),		\ | 
|  | i2c_gd32_error_isr,					\ | 
|  | DEVICE_DT_INST_GET(inst),				\ | 
|  | 0);							\ | 
|  | irq_enable(DT_INST_IRQ_BY_NAME(inst, error, irq));		\ | 
|  | }									\ | 
|  | static struct i2c_gd32_data i2c_gd32_data_##inst;			\ | 
|  | const static struct i2c_gd32_config i2c_gd32_cfg_##inst = {		\ | 
|  | .reg = DT_INST_REG_ADDR(inst),					\ | 
|  | .bitrate = DT_INST_PROP(inst, clock_frequency),			\ | 
|  | .clkid = DT_INST_CLOCKS_CELL(inst, id),				\ | 
|  | .reset = RESET_DT_SPEC_INST_GET(inst),				\ | 
|  | .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),			\ | 
|  | .irq_cfg_func = i2c_gd32_irq_cfg_func_##inst,			\ | 
|  | };									\ | 
|  | I2C_DEVICE_DT_INST_DEFINE(inst,						\ | 
|  | i2c_gd32_init, NULL,				\ | 
|  | &i2c_gd32_data_##inst, &i2c_gd32_cfg_##inst,	\ | 
|  | POST_KERNEL, CONFIG_I2C_INIT_PRIORITY,	\ | 
|  | &i2c_gd32_driver_api);			\ | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(I2C_GD32_INIT) |