You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
717 lines
17 KiB
717 lines
17 KiB
/* |
|
* 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)
|
|
|