| /* |
| * 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 const struct i2c_driver_api 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) |