Commit 8a779fc7 authored by Tim Lin's avatar Tim Lin Committed by Carles Cufi
Browse files

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: default avatarTim Lin <tim2.lin@ite.corp-partner.google.com>
parent 3ae9a358
Loading
Loading
Loading
Loading
+170 −81
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ struct i2c_enhance_config {
	uint8_t prescale_scl_low;
	uint32_t clock_gate_offset;
	bool target_enable;
	bool target_pio_mode;
};

enum i2c_pin_fun {
@@ -137,6 +138,7 @@ struct i2c_enhance_data {
#ifdef CONFIG_I2C_TARGET
	struct i2c_target_config *target_cfg;
	uint32_t buffer_size;
	int target_nack;
	bool target_attached;
#endif
	union {
@@ -209,6 +211,7 @@ enum i2c_reset_cause {
	I2C_RC_TIMEOUT,
};

#ifdef CONFIG_I2C_TARGET
enum enhanced_i2c_target_status {
	/* Time out error */
	E_TARGET_TMOE = 0x08,
@@ -217,6 +220,7 @@ enum enhanced_i2c_target_status {
	/* Time out or lost arbitration */
	E_TARGET_ANY_ERROR = (E_TARGET_TMOE | E_TARGET_ARB),
};
#endif

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) {
		if (IT8XXX2_I2C_CTR(base) & E_ACK) {
			data->err = E_HOSTA_ACK;
			/* STOP */
			IT8XXX2_I2C_CTR(base) = E_FINISH;
		}
	}

@@ -955,23 +961,14 @@ static int i2c_enhance_transfer(const struct device *dev,
}

#ifdef CONFIG_I2C_TARGET
static void target_i2c_isr(const struct device *dev)
static void target_i2c_isr_dma(const struct device *dev,
			       uint8_t interrupt_status)
{
	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_target_callbacks *target_cb = data->target_cfg->callbacks;
	struct i2c_target_dma_buffer *target_buffer = &data->target_buffer;
	uint8_t *base = config->base;
	uint8_t target_status = IT8XXX2_I2C_STR(base);

	/* Any error */
	if (target_status & E_TARGET_ANY_ERROR) {
		goto end;
	}

	/* Interrupt pending */
	if (target_status & IT8XXX2_I2C_INT_PEND) {
		uint8_t interrupt_status = IT8XXX2_I2C_IRQ_ST(base);

	/* Byte counter enable */
	if (interrupt_status & IT8XXX2_I2C_IDW_CLR) {
@@ -1018,10 +1015,85 @@ static void target_i2c_isr(const struct device *dev)
			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)
{
	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;
	uint8_t *base = config->base;
	uint8_t target_status = IT8XXX2_I2C_STR(base);

	/* Any error */
	if (target_status & E_TARGET_ANY_ERROR) {
		goto end;
	}

	/* Interrupt pending */
	if (target_status & IT8XXX2_I2C_INT_PEND) {
		uint8_t interrupt_status = IT8XXX2_I2C_IRQ_ST(base);

		/* Determine whether the transaction uses PIO or DMA mode */
		if (config->target_pio_mode) {
			if (target_i2c_isr_pio(dev, interrupt_status, target_status) < 0) {
				/* NACK */
				IT8XXX2_I2C_CTR(base) &= ~IT8XXX2_I2C_ACK;
				IT8XXX2_I2C_CTR(base) |= IT8XXX2_I2C_HALT;
				data->target_nack = 1;
			}
		} else {
			target_i2c_isr_dma(dev, interrupt_status);
		}
		/* Peripheral finish */
		if (interrupt_status & IT8XXX2_I2C_P_CLR) {
			/* Transfer done callback function */
			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 */
		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;
	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;

	if (!target_cfg) {
@@ -1235,9 +1305,22 @@ static int i2c_enhance_target_register(const struct device *dev,
	/* Interrupt status write clear */
	IT8XXX2_I2C_IRQ_ST(base) = 0xff;

	/* I2C target initial configuration of PIO mode */
	if (config->target_pio_mode) {
		/* Block to enter power policy. */
		pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);

		/* I2C module enable */
		IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_MDL_EN;
	/* I2C target initial configuration of DMA mode */
	} else {
		struct i2c_target_dma_buffer *target_buffer = &data->target_buffer;
		uint32_t in_data_addr, out_data_addr;
		int buf_size = CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE;

		/* Clear read and write data buffer of DMA */
	memset(target_buffer->in_buffer, 0, CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE);
	memset(target_buffer->out_buffer, 0, CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE);
		memset(target_buffer->in_buffer, 0, buf_size);
		memset(target_buffer->out_buffer, 0, buf_size);

		in_data_addr = (uint32_t)target_buffer->in_buffer & 0xffffff;
		out_data_addr = (uint32_t)target_buffer->out_buffer & 0xffffff;
@@ -1258,10 +1341,11 @@ static int i2c_enhance_target_register(const struct device *dev,

		/* Byte counter setting */
		/* This register indicates byte count[10:3]. */
	IT8XXX2_I2C_BYTE_CNT_H(base) = CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE >> 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);
		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
@@ -1273,6 +1357,7 @@ static int i2c_enhance_target_register(const struct device *dev,

		/* 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);
	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. */
	pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
	if (!config->target_pio_mode) {
		chip_permit_idle();
	}

	data->target_cfg = NULL;
	data->target_attached = false;
	data->target_nack = 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),     \
		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),                   \
		.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;                 \
+6 −0
Original line number Diff line number Diff line
@@ -24,3 +24,9 @@ properties:
      This option is used when the I2C target is enabled. It is
      necessary to prevent the target port from being configured
      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.
+2 −0
Original line number Diff line number Diff line
@@ -1321,6 +1321,8 @@ enum chip_pll_mode {
/* 0x55: Slave A FIFO Control */
#define IT8XXX2_SMB_HSAPE             BIT(1)
/* 0x03: Status Register */
#define IT8XXX2_I2C_BYTE_DONE         BIT(7)
#define IT8XXX2_I2C_RW                BIT(2)
#define IT8XXX2_I2C_INT_PEND          BIT(1)
/* 0x04: Data Hold Time */
#define IT8XXX2_I2C_SOFT_RST          BIT(7)