ITE: drivers/i2c/target: Introduce I2C target transfer using PIO mode

Introduce I2C target transfer using the PIO mode. Add an option
"target-pio-mode" in the yaml file, determined by the DTS, to dictate
whether I2C target transfer uses the PIO mode.

Signed-off-by: Tim Lin <tim2.lin@ite.corp-partner.google.com>
This commit is contained in:
Tim Lin 2022-12-12 15:46:25 +08:00 committed by Carles Cufí
commit 8a779fc706
3 changed files with 178 additions and 81 deletions

View file

@ -72,6 +72,7 @@ struct i2c_enhance_config {
uint8_t prescale_scl_low; uint8_t prescale_scl_low;
uint32_t clock_gate_offset; uint32_t clock_gate_offset;
bool target_enable; bool target_enable;
bool target_pio_mode;
}; };
enum i2c_pin_fun { enum i2c_pin_fun {
@ -137,6 +138,7 @@ struct i2c_enhance_data {
#ifdef CONFIG_I2C_TARGET #ifdef CONFIG_I2C_TARGET
struct i2c_target_config *target_cfg; struct i2c_target_config *target_cfg;
uint32_t buffer_size; uint32_t buffer_size;
int target_nack;
bool target_attached; bool target_attached;
#endif #endif
union { union {
@ -209,6 +211,7 @@ enum i2c_reset_cause {
I2C_RC_TIMEOUT, I2C_RC_TIMEOUT,
}; };
#ifdef CONFIG_I2C_TARGET
enum enhanced_i2c_target_status { enum enhanced_i2c_target_status {
/* Time out error */ /* Time out error */
E_TARGET_TMOE = 0x08, E_TARGET_TMOE = 0x08,
@ -217,6 +220,7 @@ enum enhanced_i2c_target_status {
/* Time out or lost arbitration */ /* Time out or lost arbitration */
E_TARGET_ANY_ERROR = (E_TARGET_TMOE | E_TARGET_ARB), E_TARGET_ANY_ERROR = (E_TARGET_TMOE | E_TARGET_ARB),
}; };
#endif
static int i2c_parsing_return_value(const struct device *dev) static int i2c_parsing_return_value(const struct device *dev)
{ {
@ -381,6 +385,8 @@ static int enhanced_i2c_error(const struct device *dev)
} else if ((i2c_str & E_HOSTA_BDS_AND_ACK) == E_HOSTA_BDS) { } else if ((i2c_str & E_HOSTA_BDS_AND_ACK) == E_HOSTA_BDS) {
if (IT8XXX2_I2C_CTR(base) & E_ACK) { if (IT8XXX2_I2C_CTR(base) & E_ACK) {
data->err = E_HOSTA_ACK; data->err = E_HOSTA_ACK;
/* STOP */
IT8XXX2_I2C_CTR(base) = E_FINISH;
} }
} }
@ -955,10 +961,104 @@ static int i2c_enhance_transfer(const struct device *dev,
} }
#ifdef CONFIG_I2C_TARGET #ifdef CONFIG_I2C_TARGET
static void target_i2c_isr_dma(const struct device *dev,
uint8_t interrupt_status)
{
struct i2c_enhance_data *data = dev->data;
const struct i2c_enhance_config *config = dev->config;
const struct i2c_target_callbacks *target_cb = data->target_cfg->callbacks;
struct i2c_target_dma_buffer *target_buffer = &data->target_buffer;
uint8_t *base = config->base;
/* Byte counter enable */
if (interrupt_status & IT8XXX2_I2C_IDW_CLR) {
IT8XXX2_I2C_BYTE_CNT_L(base) |=
(IT8XXX2_I2C_DMA_ADDR_RELOAD |
IT8XXX2_I2C_BYTE_CNT_ENABLE);
}
/* The number of received data exceeds the byte counter setting */
if (interrupt_status & IT8XXX2_I2C_CNT_HOLD) {
LOG_ERR("The excess data written starts "
"from the memory address:%p",
target_buffer->in_buffer +
CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE);
}
/* Controller to write data */
if (interrupt_status & IT8XXX2_I2C_SLVDATAFLG) {
/* Number of receive data in target mode */
data->buffer_size =
((IT8XXX2_I2C_SLV_NUM_H(base) << 8) |
IT8XXX2_I2C_SLV_NUM_L(base)) + 1;
/* Write data done callback function */
target_cb->buf_write_received(data->target_cfg,
target_buffer->in_buffer, data->buffer_size);
}
/* Controller to read data */
if (interrupt_status & IT8XXX2_I2C_IDR_CLR) {
uint32_t len;
uint8_t *rdata = NULL;
/* Clear byte counter setting */
IT8XXX2_I2C_BYTE_CNT_L(base) &=
~(IT8XXX2_I2C_DMA_ADDR_RELOAD |
IT8XXX2_I2C_BYTE_CNT_ENABLE);
/* Read data callback function */
target_cb->buf_read_requested(data->target_cfg,
&rdata, &len);
if (len > CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE) {
LOG_ERR("The bufffer size exceeds "
"I2C_TARGET_IT8XXX2_MAX_BUF_SIZE: len=%d",
len);
} else {
memcpy(target_buffer->out_buffer, rdata, len);
}
}
}
static int target_i2c_isr_pio(const struct device *dev,
uint8_t interrupt_status,
uint8_t target_status)
{
struct i2c_enhance_data *data = dev->data;
const struct i2c_enhance_config *config = dev->config;
const struct i2c_target_callbacks *target_cb = data->target_cfg->callbacks;
int ret = 0;
uint8_t *base = config->base;
uint8_t val;
/* Target ID write flag */
if (interrupt_status & IT8XXX2_I2C_IDW_CLR) {
ret = target_cb->write_requested(data->target_cfg);
}
/* Target ID read flag */
else if (interrupt_status & IT8XXX2_I2C_IDR_CLR) {
if (!target_cb->read_requested(data->target_cfg, &val)) {
IT8XXX2_I2C_DTR(base) = val;
}
}
/* Byte transfer done */
else if (target_status & IT8XXX2_I2C_BYTE_DONE) {
/* Read of write */
if (target_status & IT8XXX2_I2C_RW) {
/* Host receiving, target transmitting */
if (!target_cb->read_processed(data->target_cfg, &val)) {
IT8XXX2_I2C_DTR(base) = val;
}
} else {
/* Host transmitting, target receiving */
val = IT8XXX2_I2C_DRR(base);
ret = target_cb->write_received(data->target_cfg, val);
}
}
return ret;
}
static void target_i2c_isr(const struct device *dev) static void target_i2c_isr(const struct device *dev)
{ {
struct i2c_enhance_data *data = dev->data; struct i2c_enhance_data *data = dev->data;
struct i2c_target_dma_buffer *target_buffer = &data->target_buffer;
const struct i2c_enhance_config *config = dev->config; const struct i2c_enhance_config *config = dev->config;
const struct i2c_target_callbacks *target_cb = data->target_cfg->callbacks; const struct i2c_target_callbacks *target_cb = data->target_cfg->callbacks;
uint8_t *base = config->base; uint8_t *base = config->base;
@ -973,55 +1073,27 @@ static void target_i2c_isr(const struct device *dev)
if (target_status & IT8XXX2_I2C_INT_PEND) { if (target_status & IT8XXX2_I2C_INT_PEND) {
uint8_t interrupt_status = IT8XXX2_I2C_IRQ_ST(base); uint8_t interrupt_status = IT8XXX2_I2C_IRQ_ST(base);
/* Byte counter enable */ /* Determine whether the transaction uses PIO or DMA mode */
if (interrupt_status & IT8XXX2_I2C_IDW_CLR) { if (config->target_pio_mode) {
IT8XXX2_I2C_BYTE_CNT_L(base) |= if (target_i2c_isr_pio(dev, interrupt_status, target_status) < 0) {
(IT8XXX2_I2C_DMA_ADDR_RELOAD | /* NACK */
IT8XXX2_I2C_BYTE_CNT_ENABLE); IT8XXX2_I2C_CTR(base) &= ~IT8XXX2_I2C_ACK;
} IT8XXX2_I2C_CTR(base) |= IT8XXX2_I2C_HALT;
/* The number of received data exceeds the byte counter setting */ data->target_nack = 1;
if (interrupt_status & IT8XXX2_I2C_CNT_HOLD) {
LOG_ERR("The excess data written starts "
"from the memory address:%p",
target_buffer->in_buffer +
CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE);
}
/* Controller to write data */
if (interrupt_status & IT8XXX2_I2C_SLVDATAFLG) {
/* Number of receive data in target mode */
data->buffer_size =
((IT8XXX2_I2C_SLV_NUM_H(base) << 8) |
IT8XXX2_I2C_SLV_NUM_L(base)) + 1;
/* Write data done callback function */
target_cb->buf_write_received(data->target_cfg,
target_buffer->in_buffer, data->buffer_size);
}
/* Controller to read data */
if (interrupt_status & IT8XXX2_I2C_IDR_CLR) {
uint32_t len;
uint8_t *rdata = NULL;
/* Clear byte counter setting */
IT8XXX2_I2C_BYTE_CNT_L(base) &=
~(IT8XXX2_I2C_DMA_ADDR_RELOAD |
IT8XXX2_I2C_BYTE_CNT_ENABLE);
/* Read data callback function */
target_cb->buf_read_requested(data->target_cfg,
&rdata, &len);
if (len > CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE) {
LOG_ERR("The bufffer size exceeds "
"I2C_TARGET_IT8XXX2_MAX_BUF_SIZE: len=%d",
len);
} else {
memcpy(target_buffer->out_buffer, rdata, len);
} }
} else {
target_i2c_isr_dma(dev, interrupt_status);
} }
/* Peripheral finish */ /* Peripheral finish */
if (interrupt_status & IT8XXX2_I2C_P_CLR) { if (interrupt_status & IT8XXX2_I2C_P_CLR) {
/* Transfer done callback function */ /* Transfer done callback function */
target_cb->stop(data->target_cfg); target_cb->stop(data->target_cfg);
if (data->target_nack) {
/* Set acknowledge */
IT8XXX2_I2C_CTR(base) |= IT8XXX2_I2C_ACK;
data->target_nack = 0;
}
} }
/* Write clear the peripheral status */ /* Write clear the peripheral status */
IT8XXX2_I2C_IRQ_ST(base) = interrupt_status; IT8XXX2_I2C_IRQ_ST(base) = interrupt_status;
@ -1200,8 +1272,6 @@ static int i2c_enhance_target_register(const struct device *dev,
{ {
const struct i2c_enhance_config *config = dev->config; const struct i2c_enhance_config *config = dev->config;
struct i2c_enhance_data *data = dev->data; struct i2c_enhance_data *data = dev->data;
struct i2c_target_dma_buffer *target_buffer = &data->target_buffer;
uint32_t in_data_addr, out_data_addr;
uint8_t *base = config->base; uint8_t *base = config->base;
if (!target_cfg) { if (!target_cfg) {
@ -1235,44 +1305,59 @@ static int i2c_enhance_target_register(const struct device *dev,
/* Interrupt status write clear */ /* Interrupt status write clear */
IT8XXX2_I2C_IRQ_ST(base) = 0xff; IT8XXX2_I2C_IRQ_ST(base) = 0xff;
/* Clear read and write data buffer of DMA */ /* I2C target initial configuration of PIO mode */
memset(target_buffer->in_buffer, 0, CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE); if (config->target_pio_mode) {
memset(target_buffer->out_buffer, 0, CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE); /* Block to enter power policy. */
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
in_data_addr = (uint32_t)target_buffer->in_buffer & 0xffffff; /* I2C module enable */
out_data_addr = (uint32_t)target_buffer->out_buffer & 0xffffff; IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_MDL_EN;
/* /* I2C target initial configuration of DMA mode */
* DMA write target address register } else {
* for high order byte struct i2c_target_dma_buffer *target_buffer = &data->target_buffer;
*/ uint32_t in_data_addr, out_data_addr;
IT8XXX2_I2C_RAMH2A(base) = in_data_addr >> 16; int buf_size = CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE;
IT8XXX2_I2C_RAMHA(base) = in_data_addr >> 8;
IT8XXX2_I2C_RAMLA(base) = in_data_addr;
/*
* DMA read target address register
* for high order byte
*/
IT8XXX2_I2C_CMD_ADDH2(base) = out_data_addr >> 16;
IT8XXX2_I2C_RAMHA2(base) = out_data_addr >> 8;
IT8XXX2_I2C_RAMLA2(base) = out_data_addr;
/* Byte counter setting */ /* Clear read and write data buffer of DMA */
/* This register indicates byte count[10:3]. */ memset(target_buffer->in_buffer, 0, buf_size);
IT8XXX2_I2C_BYTE_CNT_H(base) = CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE >> 3; memset(target_buffer->out_buffer, 0, buf_size);
/* This register indicates byte count[2:0]. */
IT8XXX2_I2C_BYTE_CNT_L(base) = CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE &
GENMASK(2, 0);
/* in_data_addr = (uint32_t)target_buffer->in_buffer & 0xffffff;
* The EC processor(CPU) cannot be in the k_cpu_idle() and power out_data_addr = (uint32_t)target_buffer->out_buffer & 0xffffff;
* policy during the transactions with the CQ mode(DMA mode). /*
* Otherwise, the EC processor would be clock gated. * DMA write target address register
*/ * for high order byte
chip_block_idle(); */
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); IT8XXX2_I2C_RAMH2A(base) = in_data_addr >> 16;
IT8XXX2_I2C_RAMHA(base) = in_data_addr >> 8;
IT8XXX2_I2C_RAMLA(base) = in_data_addr;
/*
* DMA read target address register
* for high order byte
*/
IT8XXX2_I2C_CMD_ADDH2(base) = out_data_addr >> 16;
IT8XXX2_I2C_RAMHA2(base) = out_data_addr >> 8;
IT8XXX2_I2C_RAMLA2(base) = out_data_addr;
/* I2C module enable and command queue mode */ /* Byte counter setting */
IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_COMQ_EN | IT8XXX2_I2C_MDL_EN; /* This register indicates byte count[10:3]. */
IT8XXX2_I2C_BYTE_CNT_H(base) =
CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE >> 3;
/* This register indicates byte count[2:0]. */
IT8XXX2_I2C_BYTE_CNT_L(base) =
CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE & GENMASK(2, 0);
/*
* The EC processor(CPU) cannot be in the k_cpu_idle() and power
* policy during the transactions with the CQ mode(DMA mode).
* Otherwise, the EC processor would be clock gated.
*/
chip_block_idle();
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
/* I2C module enable and command queue mode */
IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_COMQ_EN | IT8XXX2_I2C_MDL_EN;
}
ite_intc_isr_clear(config->i2c_irq_base); ite_intc_isr_clear(config->i2c_irq_base);
irq_enable(config->i2c_irq_base); irq_enable(config->i2c_irq_base);
@ -1294,10 +1379,13 @@ static int i2c_enhance_target_unregister(const struct device *dev,
/* Permit to enter power policy and idle mode. */ /* Permit to enter power policy and idle mode. */
pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
chip_permit_idle(); if (!config->target_pio_mode) {
chip_permit_idle();
}
data->target_cfg = NULL; data->target_cfg = NULL;
data->target_attached = false; data->target_attached = false;
data->target_nack = 0;
return 0; return 0;
} }
@ -1343,6 +1431,7 @@ BUILD_ASSERT(IS_ENABLED(CONFIG_I2C_TARGET_BUFFER_MODE),
.clock_gate_offset = DT_INST_PROP(inst, clock_gate_offset), \ .clock_gate_offset = DT_INST_PROP(inst, clock_gate_offset), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
.target_enable = DT_INST_PROP(inst, target_enable), \ .target_enable = DT_INST_PROP(inst, target_enable), \
.target_pio_mode = DT_INST_PROP(inst, target_pio_mode), \
}; \ }; \
\ \
static struct i2c_enhance_data i2c_enhance_data_##inst; \ static struct i2c_enhance_data i2c_enhance_data_##inst; \

View file

@ -24,3 +24,9 @@ properties:
This option is used when the I2C target is enabled. It is This option is used when the I2C target is enabled. It is
necessary to prevent the target port from being configured necessary to prevent the target port from being configured
with I2C host related initialization. with I2C host related initialization.
target-pio-mode:
type: boolean
description: |
This option is used when the I2C target is enabled and it can
support PIO mode for I2C target transfer.

View file

@ -1321,6 +1321,8 @@ enum chip_pll_mode {
/* 0x55: Slave A FIFO Control */ /* 0x55: Slave A FIFO Control */
#define IT8XXX2_SMB_HSAPE BIT(1) #define IT8XXX2_SMB_HSAPE BIT(1)
/* 0x03: Status Register */ /* 0x03: Status Register */
#define IT8XXX2_I2C_BYTE_DONE BIT(7)
#define IT8XXX2_I2C_RW BIT(2)
#define IT8XXX2_I2C_INT_PEND BIT(1) #define IT8XXX2_I2C_INT_PEND BIT(1)
/* 0x04: Data Hold Time */ /* 0x04: Data Hold Time */
#define IT8XXX2_I2C_SOFT_RST BIT(7) #define IT8XXX2_I2C_SOFT_RST BIT(7)