Skip to content

drivers: i2c: wch: Add support i2c target #92942

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 201 additions & 1 deletion drivers/i2c/i2c_wch.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ struct i2c_wch_data {
uint16_t: 16;
};
} current;

#ifdef CONFIG_I2C_TARGET
bool master_active;
bool slave_attached;
struct i2c_target_config *slave_cfg;
#endif /* CONFIG_I2C_TARGET */
};

static void wch_i2c_handle_start_bit(const struct device *dev)
Expand Down Expand Up @@ -137,11 +143,87 @@ static void wch_i2c_handle_rxne(const struct device *dev)
}
}

#if defined(CONFIG_I2C_TARGET)

static void i2c_wch_slave_event_isr(const struct device *dev)
{
const struct i2c_wch_config *cfg = dev->config;
struct i2c_wch_data *data = dev->data;
I2C_TypeDef *regs = cfg->regs;
uint16_t status = regs->STAR1;
const struct i2c_target_callbacks *slave_cb =
data->slave_cfg->callbacks;

if (status & I2C_STAR1_ADDR) {
if ((regs->STAR2 & I2C_STAR2_TRA)
&& (status & I2C_STAR1_TXE)) {
uint8_t val;

slave_cb->read_requested(data->slave_cfg, &val);
regs->DATAR = val;
} else {
slave_cb->write_requested(data->slave_cfg);
}
}

if (status & I2C_STAR1_RXNE) {
uint8_t val = (uint8_t)(regs->DATAR & I2C_DR_DATAR);

if (slave_cb->write_received(data->slave_cfg, val)) {
regs->CTLR1 &= ~I2C_CTLR1_ACK;
}
return;
}

if ((status & I2C_STAR1_TXE)
&& (status & I2C_STAR1_BTF)) {
uint8_t val;

slave_cb->read_processed(data->slave_cfg, &val);
regs->DATAR = val;
return;
}

if (status & I2C_STAR1_STOPF) {
regs->CTLR1 &= regs->CTLR1;
slave_cb->stop(data->slave_cfg);
}

if (status & I2C_STAR1_AF) {
regs->STAR1 &= ~I2C_STAR1_AF;
}
}

static void i2c_wch_slave_error_isr(const struct device *dev)
{
const struct i2c_wch_config *config = dev->config;
I2C_TypeDef *regs = config->regs;
uint16_t status = regs->STAR1;

if (status & I2C_STAR1_BERR) {
LOG_DBG("BERR");
}

if (status & I2C_STAR1_OVR) {
LOG_DBG("OVR");
}
}

#endif /* CONFIG_I2C_TARGET */

static void i2c_wch_event_isr(const struct device *dev)
{
const struct i2c_wch_config *config = dev->config;
struct i2c_wch_data *data = dev->data;
I2C_TypeDef *regs = config->regs;

#if defined(CONFIG_I2C_TARGET)
if (data->slave_attached && !data->master_active) {
i2c_wch_slave_event_isr(dev);
return;
}
#endif

uint16_t status = regs->STAR1;
bool write;

Expand All @@ -163,6 +245,14 @@ static void i2c_wch_error_isr(const struct device *dev)
const struct i2c_wch_config *config = dev->config;
struct i2c_wch_data *data = dev->data;
I2C_TypeDef *regs = config->regs;

#if defined(CONFIG_I2C_TARGET)
if (data->slave_attached && !data->master_active) {
i2c_wch_slave_error_isr(dev);
return;
}
#endif

uint16_t status = regs->STAR1;

if (status & (I2C_STAR1_AF | I2C_STAR1_ARLO | I2C_STAR1_BERR)) {
Expand Down Expand Up @@ -191,6 +281,9 @@ static void wch_i2c_msg_init(const struct device *dev, struct i2c_msg *msg,
data->current.idx = 0U;
data->current.err = 0U;
data->current.addr = addr;
#if defined(CONFIG_I2C_TARGET)
data->master_active = true;
#endif

regs->CTLR1 |= I2C_CTLR1_PE;
regs->CTLR1 |= I2C_CTLR1_ACK;
Expand Down Expand Up @@ -258,14 +351,25 @@ static int32_t wch_i2c_begin_transfer(const struct device *dev, struct i2c_msg *
static void wch_i2c_finish_transfer(const struct device *dev)
{
const struct i2c_wch_config *config = dev->config;
struct i2c_wch_data *data = dev->data;
I2C_TypeDef *regs = config->regs;

wch_i2c_config_interrupts(regs, false);

while (regs->STAR2 & I2C_STAR2_BUSY) {
}

#if defined(CONFIG_I2C_TARGET)
data->master_active = false;
if (!data->slave_attached) {
regs->CTLR1 &= ~I2C_CTLR1_PE;
} else {
wch_i2c_config_interrupts(regs, true);
regs->CTLR1 |= I2C_CTLR1_ACK;
}
#else
regs->CTLR1 &= ~I2C_CTLR1_PE;
#endif
}

static int wch_i2c_configure_timing(I2C_TypeDef *regs, uint32_t clock_rate,
Expand Down Expand Up @@ -376,6 +480,8 @@ static int i2c_wch_init(const struct device *dev)

k_sem_init(&data->xfer_done, 0, 1);

config->irq_config_func(dev);

clk_sys = (clock_control_subsys_t)(uintptr_t)config->clk_id;

err = clock_control_on(config->clk_dev, clk_sys);
Expand All @@ -393,14 +499,108 @@ static int i2c_wch_init(const struct device *dev)
return err;
}

config->irq_config_func(dev);
return 0;
}

#if defined(CONFIG_I2C_TARGET)

int i2c_wch_target_register(const struct device *dev, struct i2c_target_config *config)
{
const struct i2c_wch_config *cfg = dev->config;
struct i2c_wch_data *data = dev->data;
I2C_TypeDef *regs = cfg->regs;
int ret;

if (!config) {
return -EINVAL;
}

if (data->slave_attached) {
return -EBUSY;
}

if (data->master_active) {
return -EBUSY;
}

ret = i2c_wch_configure(dev, I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(cfg->bitrate));
if (ret < 0) {
LOG_ERR("i2c: failure configure");
return ret;
}

data->slave_cfg = config;
if (data->slave_cfg->flags == I2C_TARGET_FLAGS_ADDR_10_BITS) {
return -ENOTSUP;
}

/* Enable I2C */
regs->CTLR1 |= I2C_CTLR1_PE;

/* Set OwnAddress1 */
regs->OADDR1 = (config->address << 1U) | I2C_AcknowledgedAddress_7bit;
data->slave_attached = true;

LOG_DBG("i2c: target registered");

wch_i2c_config_interrupts(regs, true);

/* Enable Acknowledge */
regs->CTLR1 |= I2C_CTLR1_ACK;

return 0;
}

int i2c_wch_target_unregister(const struct device *dev, struct i2c_target_config *config)
{
const struct i2c_wch_config *cfg = dev->config;
struct i2c_wch_data *data = dev->data;
I2C_TypeDef *regs = cfg->regs;
uint16_t status = regs->STAR1;

if (!data->slave_attached) {
return -EINVAL;
}

if (data->master_active) {
return -EBUSY;
}

wch_i2c_config_interrupts(regs, false);

if (status & I2C_STAR1_AF) {
regs->STAR1 &= ~I2C_STAR1_AF;
}

if (status & I2C_STAR1_STOPF) {
(void)(regs->STAR1);
regs->CTLR1 &= regs->CTLR1;
}

if (status & I2C_STAR1_ADDR) {
(void)(regs->STAR1);
(void)(regs->STAR2);
}

/* Disable I2C */
regs->CTLR1 &= ~I2C_CTLR1_PE;

data->slave_attached = false;

LOG_DBG("i2c: slave unregistered");

return 0;
}

#endif /* CONFIG_I2C_TARGET */

static DEVICE_API(i2c, i2c_wch_api) = {
.configure = i2c_wch_configure,
.transfer = i2c_wch_transfer,
#if defined(CONFIG_I2C_TARGET)
.target_register = i2c_wch_target_register,
.target_unregister = i2c_wch_target_unregister,
#endif /* CONFIG_I2C_TARGET */
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
Expand Down
Loading