| /* | 
 |  * Copyright (c) 2020 ITE Corporation. All Rights Reserved. | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #define DT_DRV_COMPAT ite_it8xxx2_i2c | 
 |  | 
 | #include <zephyr/drivers/gpio.h> | 
 | #include <zephyr/drivers/i2c.h> | 
 | #include <zephyr/drivers/pinctrl.h> | 
 | #include <zephyr/irq.h> | 
 | #include <zephyr/kernel.h> | 
 | #include <errno.h> | 
 | #include <ilm.h> | 
 | #include <soc.h> | 
 | #include <soc_dt.h> | 
 | #include <zephyr/dt-bindings/i2c/it8xxx2-i2c.h> | 
 | #include <zephyr/pm/policy.h> | 
 | #include <zephyr/sys/util.h> | 
 |  | 
 | #include <zephyr/logging/log.h> | 
 | LOG_MODULE_REGISTER(i2c_ite_it8xxx2, CONFIG_I2C_LOG_LEVEL); | 
 |  | 
 | #include "i2c-priv.h" | 
 |  | 
 | /* Start smbus session from idle state */ | 
 | #define I2C_MSG_START BIT(5) | 
 |  | 
 | #define I2C_LINE_SCL_HIGH BIT(0) | 
 | #define I2C_LINE_SDA_HIGH BIT(1) | 
 | #define I2C_LINE_IDLE (I2C_LINE_SCL_HIGH | I2C_LINE_SDA_HIGH) | 
 |  | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | #define I2C_FIFO_MODE_MAX_SIZE 32 | 
 | #define I2C_FIFO_MODE_TOTAL_LEN 255 | 
 | #define I2C_MSG_BURST_READ_MASK (I2C_MSG_RESTART | I2C_MSG_STOP | I2C_MSG_READ) | 
 | #endif | 
 |  | 
 | struct i2c_it8xxx2_config { | 
 | 	void (*irq_config_func)(void); | 
 | 	uint32_t bitrate; | 
 | 	uint8_t *base; | 
 | 	uint8_t *reg_mstfctrl; | 
 | 	uint8_t i2c_irq_base; | 
 | 	uint8_t port; | 
 | 	uint8_t channel_switch_sel; | 
 | 	/* SCL GPIO cells */ | 
 | 	struct gpio_dt_spec scl_gpios; | 
 | 	/* SDA GPIO cells */ | 
 | 	struct gpio_dt_spec sda_gpios; | 
 | 	/* I2C alternate configuration */ | 
 | 	const struct pinctrl_dev_config *pcfg; | 
 | 	uint32_t clock_gate_offset; | 
 | 	bool fifo_enable; | 
 | 	bool push_pull_recovery; | 
 | }; | 
 |  | 
 | enum i2c_pin_fun { | 
 | 	SCL = 0, | 
 | 	SDA, | 
 | }; | 
 |  | 
 | enum i2c_ch_status { | 
 | 	I2C_CH_NORMAL = 0, | 
 | 	I2C_CH_REPEAT_START, | 
 | 	I2C_CH_WAIT_READ, | 
 | 	I2C_CH_WAIT_NEXT_XFER, | 
 | }; | 
 |  | 
 | struct i2c_it8xxx2_data { | 
 | 	enum i2c_ch_status i2ccs; | 
 | 	struct i2c_msg *active_msg; | 
 | 	struct k_mutex mutex; | 
 | 	struct k_sem device_sync_sem; | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | 	struct i2c_msg *msgs_list; | 
 | 	/* Read or write byte counts. */ | 
 | 	uint32_t bytecnt; | 
 | 	/* Number of messages. */ | 
 | 	uint8_t num_msgs; | 
 | 	uint8_t active_msg_index; | 
 | #endif | 
 | 	/* Index into output data */ | 
 | 	size_t widx; | 
 | 	/* Index into input data */ | 
 | 	size_t ridx; | 
 | 	/* operation freq of i2c */ | 
 | 	uint32_t bus_freq; | 
 | 	/* Error code, if any */ | 
 | 	uint32_t err; | 
 | 	/* address of device */ | 
 | 	uint16_t addr_16bit; | 
 | 	/* Frequency setting */ | 
 | 	uint8_t freq; | 
 | 	/* wait for stop bit interrupt */ | 
 | 	uint8_t stop; | 
 | }; | 
 |  | 
 | enum i2c_host_status { | 
 | 	/* Host busy */ | 
 | 	HOSTA_HOBY = 0x01, | 
 | 	/* Finish Interrupt */ | 
 | 	HOSTA_FINTR = 0x02, | 
 | 	/* Device error */ | 
 | 	HOSTA_DVER = 0x04, | 
 | 	/* Bus error */ | 
 | 	HOSTA_BSER = 0x08, | 
 | 	/* Fail */ | 
 | 	HOSTA_FAIL = 0x10, | 
 | 	/* Not response ACK */ | 
 | 	HOSTA_NACK = 0x20, | 
 | 	/* Time-out error */ | 
 | 	HOSTA_TMOE = 0x40, | 
 | 	/* Byte done status */ | 
 | 	HOSTA_BDS = 0x80, | 
 | 	/* Error bit is set */ | 
 | 	HOSTA_ANY_ERROR = (HOSTA_DVER | HOSTA_BSER | HOSTA_FAIL | | 
 | 			   HOSTA_NACK | HOSTA_TMOE), | 
 | 	/* W/C for next byte */ | 
 | 	HOSTA_NEXT_BYTE = HOSTA_BDS, | 
 | 	/* W/C host status register */ | 
 | 	HOSTA_ALL_WC_BIT = (HOSTA_FINTR | HOSTA_ANY_ERROR | HOSTA_BDS), | 
 | }; | 
 |  | 
 | enum i2c_reset_cause { | 
 | 	I2C_RC_NO_IDLE_FOR_START = 1, | 
 | 	I2C_RC_TIMEOUT, | 
 | }; | 
 |  | 
 | static int i2c_parsing_return_value(const struct device *dev) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 |  | 
 | 	if (!data->err) { | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	if (data->err == ETIMEDOUT) { | 
 | 		/* Connection timed out */ | 
 | 		LOG_ERR("I2C ch%d Address:0x%X Transaction time out.", | 
 | 			config->port, data->addr_16bit); | 
 | 	} else { | 
 | 		LOG_DBG("I2C ch%d Address:0x%X Host error bits message:", | 
 | 			config->port, data->addr_16bit); | 
 | 		/* Host error bits message*/ | 
 | 		if (data->err & HOSTA_TMOE) { | 
 | 			LOG_ERR("Time-out error: hardware time-out error."); | 
 | 		} | 
 | 		if (data->err & HOSTA_NACK) { | 
 | 			LOG_DBG("NACK error: device does not response ACK."); | 
 | 		} | 
 | 		if (data->err & HOSTA_FAIL) { | 
 | 			LOG_ERR("Fail: a processing transmission is killed."); | 
 | 		} | 
 | 		if (data->err & HOSTA_BSER) { | 
 | 			LOG_ERR("BUS error: SMBus has lost arbitration."); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return -EIO; | 
 | } | 
 |  | 
 | static int i2c_get_line_levels(const struct device *dev) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	return (IT8XXX2_SMB_SMBPCTL(base) & | 
 | 		(IT8XXX2_SMB_SMBDCS | IT8XXX2_SMB_SMBCS)); | 
 | } | 
 |  | 
 | static int i2c_is_busy(const struct device *dev) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	return (IT8XXX2_SMB_HOSTA(base) & | 
 | 		(HOSTA_HOBY | HOSTA_ALL_WC_BIT)); | 
 | } | 
 |  | 
 | static int i2c_bus_not_available(const struct device *dev) | 
 | { | 
 | 	if (i2c_is_busy(dev) || | 
 | 		(i2c_get_line_levels(dev) != I2C_LINE_IDLE)) { | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void i2c_reset(const struct device *dev) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	/* bit1, kill current transaction. */ | 
 | 	IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_KILL; | 
 | 	IT8XXX2_SMB_HOCTL(base) = 0; | 
 | 	/* W/C host status register */ | 
 | 	IT8XXX2_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; | 
 | } | 
 |  | 
 | /* | 
 |  * Set i2c standard port (A, B, or C) runs at 400kHz by using timing registers | 
 |  * (offset 0h ~ 7h). | 
 |  */ | 
 | static void i2c_standard_port_timing_regs_400khz(uint8_t port) | 
 | { | 
 | 	/* Port clock frequency depends on setting of timing registers. */ | 
 | 	IT8XXX2_SMB_SCLKTS(port) = 0; | 
 | 	/* Suggested setting of timing registers of 400kHz. */ | 
 | #ifdef CONFIG_SOC_IT8XXX2_EC_BUS_24MHZ | 
 | 	IT8XXX2_SMB_4P7USL = 0x16; | 
 | 	IT8XXX2_SMB_4P0USL = 0x11; | 
 | 	IT8XXX2_SMB_300NS = 0x8; | 
 | 	IT8XXX2_SMB_250NS = 0x8; | 
 | 	IT8XXX2_SMB_45P3USL = 0xff; | 
 | 	IT8XXX2_SMB_45P3USH = 0x3; | 
 | 	IT8XXX2_SMB_4P7A4P0H = 0; | 
 | #else | 
 | 	IT8XXX2_SMB_4P7USL = 0x3; | 
 | 	IT8XXX2_SMB_4P0USL = 0; | 
 | 	IT8XXX2_SMB_300NS = 0x1; | 
 | 	IT8XXX2_SMB_250NS = 0x5; | 
 | 	IT8XXX2_SMB_45P3USL = 0x6a; | 
 | 	IT8XXX2_SMB_45P3USH = 0x1; | 
 | 	IT8XXX2_SMB_4P7A4P0H = 0; | 
 | #endif | 
 | } | 
 |  | 
 | /* Set clock frequency for i2c port A, B , or C */ | 
 | static void i2c_standard_port_set_frequency(const struct device *dev, | 
 | 					    int freq_hz, int freq_set) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 |  | 
 | 	/* | 
 | 	 * If port's clock frequency is 400kHz, we use timing registers | 
 | 	 * for setting. So we can adjust tlow to meet timing. | 
 | 	 * The others use basic 50/100/1000 KHz setting. | 
 | 	 */ | 
 | 	if (freq_hz == I2C_BITRATE_FAST) { | 
 | 		i2c_standard_port_timing_regs_400khz(config->port); | 
 | 	} else { | 
 | 		IT8XXX2_SMB_SCLKTS(config->port) = freq_set; | 
 | 	} | 
 |  | 
 | 	/* This field defines the SMCLK0/1/2 clock/data low timeout. */ | 
 | 	IT8XXX2_SMB_25MS = I2C_CLK_LOW_TIMEOUT; | 
 | } | 
 |  | 
 | static int i2c_it8xxx2_configure(const struct device *dev, | 
 | 				 uint32_t dev_config_raw) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	struct i2c_it8xxx2_data *const data = dev->data; | 
 | 	uint32_t freq_set; | 
 |  | 
 | 	if (!(I2C_MODE_CONTROLLER & dev_config_raw)) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (I2C_ADDR_10_BITS & dev_config_raw) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	data->bus_freq = I2C_SPEED_GET(dev_config_raw); | 
 |  | 
 | 	switch (data->bus_freq) { | 
 | 	case I2C_SPEED_DT: | 
 | 		freq_set = IT8XXX2_SMB_SMCLKS_50K; | 
 | 		break; | 
 | 	case I2C_SPEED_STANDARD: | 
 | 		freq_set = IT8XXX2_SMB_SMCLKS_100K; | 
 | 		break; | 
 | 	case I2C_SPEED_FAST: | 
 | 		freq_set = IT8XXX2_SMB_SMCLKS_400K; | 
 | 		break; | 
 | 	case I2C_SPEED_FAST_PLUS: | 
 | 		freq_set = IT8XXX2_SMB_SMCLKS_1M; | 
 | 		break; | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	i2c_standard_port_set_frequency(dev, config->bitrate, freq_set); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int i2c_it8xxx2_get_config(const struct device *dev, | 
 | 				  uint32_t *dev_config) | 
 | { | 
 | 	struct i2c_it8xxx2_data *const data = dev->data; | 
 | 	uint32_t speed; | 
 |  | 
 | 	if (!data->bus_freq) { | 
 | 		LOG_ERR("The bus frequency is not initially configured."); | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	switch (data->bus_freq) { | 
 | 	case I2C_SPEED_DT: | 
 | 	case I2C_SPEED_STANDARD: | 
 | 	case I2C_SPEED_FAST: | 
 | 	case I2C_SPEED_FAST_PLUS: | 
 | 		speed = I2C_SPEED_SET(data->bus_freq); | 
 | 		break; | 
 | 	default: | 
 | 		return -ERANGE; | 
 | 	} | 
 |  | 
 | 	*dev_config = (I2C_MODE_CONTROLLER | speed); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | void __soc_ram_code i2c_r_last_byte(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	/* | 
 | 	 * bit5, The firmware shall write 1 to this bit | 
 | 	 * when the next byte will be the last byte for i2c read. | 
 | 	 */ | 
 | 	if ((data->active_msg->flags & I2C_MSG_STOP) && | 
 | 	    (data->ridx == data->active_msg->len - 1)) { | 
 | 		IT8XXX2_SMB_HOCTL(base) |= IT8XXX2_SMB_LABY; | 
 | 	} | 
 | } | 
 |  | 
 | void __soc_ram_code i2c_w2r_change_direction(const struct device *dev) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	/* I2C switch direction */ | 
 | 	if (IT8XXX2_SMB_HOCTL2(base) & IT8XXX2_SMB_I2C_SW_EN) { | 
 | 		i2c_r_last_byte(dev); | 
 | 		IT8XXX2_SMB_HOSTA(base) = HOSTA_NEXT_BYTE; | 
 | 	} else { | 
 | 		/* | 
 | 		 * bit2, I2C switch direction wait. | 
 | 		 * bit3, I2C switch direction enable. | 
 | 		 */ | 
 | 		IT8XXX2_SMB_HOCTL2(base) |= IT8XXX2_SMB_I2C_SW_EN | | 
 | 					    IT8XXX2_SMB_I2C_SW_WAIT; | 
 | 		IT8XXX2_SMB_HOSTA(base) = HOSTA_NEXT_BYTE; | 
 | 		i2c_r_last_byte(dev); | 
 | 		IT8XXX2_SMB_HOCTL2(base) &= ~IT8XXX2_SMB_I2C_SW_WAIT; | 
 | 	} | 
 | } | 
 |  | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | void __soc_ram_code i2c_fifo_en_w2r(const struct device *dev, bool enable) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	unsigned int key = irq_lock(); | 
 |  | 
 | 	if (enable) { | 
 | 		if (config->port == SMB_CHANNEL_A) { | 
 | 			IT8XXX2_SMB_I2CW2RF |= IT8XXX2_SMB_MAIF | | 
 | 					       IT8XXX2_SMB_MAIFI; | 
 | 		} else if (config->port == SMB_CHANNEL_B) { | 
 | 			IT8XXX2_SMB_I2CW2RF |= IT8XXX2_SMB_MBCIF | | 
 | 					       IT8XXX2_SMB_MBIFI; | 
 | 		} else if (config->port == SMB_CHANNEL_C) { | 
 | 			IT8XXX2_SMB_I2CW2RF |= IT8XXX2_SMB_MBCIF | | 
 | 					       IT8XXX2_SMB_MCIFI; | 
 | 		} | 
 | 	} else { | 
 | 		if (config->port == SMB_CHANNEL_A) { | 
 | 			IT8XXX2_SMB_I2CW2RF &= ~(IT8XXX2_SMB_MAIF | | 
 | 						 IT8XXX2_SMB_MAIFI); | 
 | 		} else if (config->port == SMB_CHANNEL_B) { | 
 | 			IT8XXX2_SMB_I2CW2RF &= ~(IT8XXX2_SMB_MBCIF | | 
 | 						 IT8XXX2_SMB_MBIFI); | 
 | 		} else if (config->port == SMB_CHANNEL_C) { | 
 | 			IT8XXX2_SMB_I2CW2RF &= ~(IT8XXX2_SMB_MBCIF | | 
 | 						 IT8XXX2_SMB_MCIFI); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	irq_unlock(key); | 
 | } | 
 |  | 
 | void __soc_ram_code i2c_tran_fifo_write_start(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint32_t i; | 
 | 	uint8_t *base = config->base; | 
 | 	volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 |  | 
 | 	/* Clear start flag. */ | 
 | 	data->active_msg->flags &= ~I2C_MSG_START; | 
 | 	/* Enable SMB channel in FIFO mode. */ | 
 | 	*reg_mstfctrl |= IT8XXX2_SMB_FFEN; | 
 | 	/* I2C enable. */ | 
 | 	IT8XXX2_SMB_HOCTL2(base) = IT8XXX2_SMB_SMD_TO_EN | | 
 | 				   IT8XXX2_SMB_I2C_EN | | 
 | 				   IT8XXX2_SMB_SMHEN; | 
 | 	/* Set write byte counts. */ | 
 | 	IT8XXX2_SMB_D0REG(base) = data->active_msg->len; | 
 | 	/* | 
 | 	 * bit[7:1]: Address of the target. | 
 | 	 * bit[0]: Direction of the host transfer. | 
 | 	 */ | 
 | 	IT8XXX2_SMB_TRASLA(base) = (uint8_t)data->addr_16bit << 1; | 
 | 	/* The maximum fifo size is 32 bytes. */ | 
 | 	data->bytecnt = MIN(data->active_msg->len, I2C_FIFO_MODE_MAX_SIZE); | 
 |  | 
 | 	for (i = 0; i < data->bytecnt; i++) { | 
 | 		/* Set host block data byte. */ | 
 | 		IT8XXX2_SMB_HOBDB(base) = *(data->active_msg->buf++); | 
 | 	} | 
 | 	/* Calculate the remaining byte counts. */ | 
 | 	data->bytecnt = data->active_msg->len - data->bytecnt; | 
 | 	/* | 
 | 	 * bit[6] = 1b: Start. | 
 | 	 * bit[4:2] = 111b: Extend command. | 
 | 	 * bit[0] = 1b: Host interrupt enable. | 
 | 	 */ | 
 | 	IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_SRT | | 
 | 				  IT8XXX2_SMB_SMCD_EXTND | | 
 | 				  IT8XXX2_SMB_INTREN; | 
 | } | 
 |  | 
 | void __soc_ram_code i2c_tran_fifo_write_next_block(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint32_t i, _bytecnt; | 
 | 	uint8_t *base = config->base; | 
 | 	volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 |  | 
 | 	/* The maximum fifo size is 32 bytes. */ | 
 | 	_bytecnt = MIN(data->bytecnt, I2C_FIFO_MODE_MAX_SIZE); | 
 |  | 
 | 	for (i = 0; i < _bytecnt; i++) { | 
 | 		/* Set host block data byte. */ | 
 | 		IT8XXX2_SMB_HOBDB(base) = *(data->active_msg->buf++); | 
 | 	} | 
 | 	/* Clear FIFO block done status. */ | 
 | 	*reg_mstfctrl |= IT8XXX2_SMB_BLKDS; | 
 | 	/* Calculate the remaining byte counts. */ | 
 | 	data->bytecnt -= _bytecnt; | 
 | } | 
 |  | 
 | void __soc_ram_code i2c_tran_fifo_write_finish(const struct device *dev) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	/* Clear byte count register. */ | 
 | 	IT8XXX2_SMB_D0REG(base) = 0; | 
 | 	/* W/C */ | 
 | 	IT8XXX2_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; | 
 | 	/* Disable the SMBus host interface. */ | 
 | 	IT8XXX2_SMB_HOCTL2(base) = 0x00; | 
 | } | 
 |  | 
 | int __soc_ram_code i2c_tran_fifo_w2r_change_direction(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	if (++data->active_msg_index >= data->num_msgs) { | 
 | 		LOG_ERR("Current message index is error."); | 
 | 		data->err = EINVAL; | 
 | 		/* W/C */ | 
 | 		IT8XXX2_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; | 
 | 		/* Disable the SMBus host interface. */ | 
 | 		IT8XXX2_SMB_HOCTL2(base) = 0x00; | 
 |  | 
 | 		return 0; | 
 | 	} | 
 | 	/* Set I2C_SW_EN = 1 */ | 
 | 	IT8XXX2_SMB_HOCTL2(base) |= IT8XXX2_SMB_I2C_SW_EN | | 
 | 				    IT8XXX2_SMB_I2C_SW_WAIT; | 
 | 	IT8XXX2_SMB_HOCTL2(base) &= ~IT8XXX2_SMB_I2C_SW_WAIT; | 
 | 	/* Point to the next msg for the read location. */ | 
 | 	data->active_msg = &data->msgs_list[data->active_msg_index]; | 
 | 	/* Set read byte counts. */ | 
 | 	IT8XXX2_SMB_D0REG(base) = data->active_msg->len; | 
 | 	data->bytecnt = data->active_msg->len; | 
 | 	/* W/C I2C W2R FIFO interrupt status. */ | 
 | 	IT8XXX2_SMB_IWRFISTA = BIT(config->port); | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | void __soc_ram_code i2c_tran_fifo_read_start(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 | 	volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 |  | 
 | 	/* Clear start flag. */ | 
 | 	data->active_msg->flags &= ~I2C_MSG_START; | 
 | 	/* Enable SMB channel in FIFO mode. */ | 
 | 	*reg_mstfctrl |= IT8XXX2_SMB_FFEN; | 
 | 	/* I2C enable. */ | 
 | 	IT8XXX2_SMB_HOCTL2(base) = IT8XXX2_SMB_SMD_TO_EN | | 
 | 				   IT8XXX2_SMB_I2C_EN | | 
 | 				   IT8XXX2_SMB_SMHEN; | 
 | 	/* Set read byte counts. */ | 
 | 	IT8XXX2_SMB_D0REG(base) = data->active_msg->len; | 
 | 	/* | 
 | 	 * bit[7:1]: Address of the target. | 
 | 	 * bit[0]: Direction of the host transfer. | 
 | 	 */ | 
 | 	IT8XXX2_SMB_TRASLA(base) = (uint8_t)(data->addr_16bit << 1) | | 
 | 				   IT8XXX2_SMB_DIR; | 
 |  | 
 | 	data->bytecnt = data->active_msg->len; | 
 | 	/* | 
 | 	 * bit[6] = 1b: Start. | 
 | 	 * bit[4:2] = 111b: Extend command. | 
 | 	 * bit[0] = 1b: Host interrupt enable. | 
 | 	 */ | 
 | 	IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_SRT | | 
 | 				  IT8XXX2_SMB_SMCD_EXTND | | 
 | 				  IT8XXX2_SMB_INTREN; | 
 | } | 
 |  | 
 | void __soc_ram_code i2c_tran_fifo_read_next_block(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint32_t i; | 
 | 	uint8_t *base = config->base; | 
 | 	volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 |  | 
 | 	for (i = 0; i < I2C_FIFO_MODE_MAX_SIZE; i++) { | 
 | 		/* To get received data. */ | 
 | 		*(data->active_msg->buf++) = IT8XXX2_SMB_HOBDB(base); | 
 | 	} | 
 | 	/* Clear FIFO block done status. */ | 
 | 	*reg_mstfctrl |= IT8XXX2_SMB_BLKDS; | 
 | 	/* Calculate the remaining byte counts. */ | 
 | 	data->bytecnt -= I2C_FIFO_MODE_MAX_SIZE; | 
 | } | 
 |  | 
 | void __soc_ram_code i2c_tran_fifo_read_finish(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint32_t i; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	for (i = 0; i < data->bytecnt; i++) { | 
 | 		/* To get received data. */ | 
 | 		*(data->active_msg->buf++) = IT8XXX2_SMB_HOBDB(base); | 
 | 	} | 
 | 	/* Clear byte count register. */ | 
 | 	IT8XXX2_SMB_D0REG(base) = 0; | 
 | 	/* W/C */ | 
 | 	IT8XXX2_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; | 
 | 	/* Disable the SMBus host interface. */ | 
 | 	IT8XXX2_SMB_HOCTL2(base) = 0x00; | 
 |  | 
 | } | 
 |  | 
 | int __soc_ram_code i2c_tran_fifo_write_to_read(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 | 	volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 | 	int ret = 1; | 
 |  | 
 | 	if (data->active_msg->flags & I2C_MSG_START) { | 
 | 		/* Enable I2C write to read FIFO mode. */ | 
 | 		i2c_fifo_en_w2r(dev, 1); | 
 | 		i2c_tran_fifo_write_start(dev); | 
 | 	} else { | 
 | 		/* Check block done status. */ | 
 | 		if (*reg_mstfctrl & IT8XXX2_SMB_BLKDS) { | 
 | 			if (IT8XXX2_SMB_HOCTL2(base) & IT8XXX2_SMB_I2C_SW_EN) { | 
 | 				i2c_tran_fifo_read_next_block(dev); | 
 | 			} else { | 
 | 				i2c_tran_fifo_write_next_block(dev); | 
 | 			} | 
 | 		} else if (IT8XXX2_SMB_IWRFISTA & BIT(config->port)) { | 
 | 			/* | 
 | 			 * This function returns 0 on a failure to indicate | 
 | 			 * that the current transaction is completed and | 
 | 			 * returned the data->err. | 
 | 			 */ | 
 | 			ret = i2c_tran_fifo_w2r_change_direction(dev); | 
 | 		} else { | 
 | 			/* Wait finish. */ | 
 | 			if ((IT8XXX2_SMB_HOSTA(base) & HOSTA_FINTR)) { | 
 | 				i2c_tran_fifo_read_finish(dev); | 
 | 				/* Done doing work. */ | 
 | 				ret = 0; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | int __soc_ram_code i2c_tran_fifo_read(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 | 	volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 |  | 
 | 	if (data->active_msg->flags & I2C_MSG_START) { | 
 | 		i2c_tran_fifo_read_start(dev); | 
 | 	} else { | 
 | 		/* Check block done status. */ | 
 | 		if (*reg_mstfctrl & IT8XXX2_SMB_BLKDS) { | 
 | 			i2c_tran_fifo_read_next_block(dev); | 
 | 		} else { | 
 | 			/* Wait finish. */ | 
 | 			if ((IT8XXX2_SMB_HOSTA(base) & HOSTA_FINTR)) { | 
 | 				i2c_tran_fifo_read_finish(dev); | 
 | 				/* Done doing work. */ | 
 | 				return 0; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | int __soc_ram_code i2c_tran_fifo_write(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 | 	volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 |  | 
 | 	if (data->active_msg->flags & I2C_MSG_START) { | 
 | 		i2c_tran_fifo_write_start(dev); | 
 | 	} else { | 
 | 		/* Check block done status. */ | 
 | 		if (*reg_mstfctrl & IT8XXX2_SMB_BLKDS) { | 
 | 			i2c_tran_fifo_write_next_block(dev); | 
 | 		} else { | 
 | 			/* Wait finish. */ | 
 | 			if ((IT8XXX2_SMB_HOSTA(base) & HOSTA_FINTR)) { | 
 | 				i2c_tran_fifo_write_finish(dev); | 
 | 				/* Done doing work. */ | 
 | 				return 0; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | int __soc_ram_code i2c_fifo_transaction(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	/* Any error. */ | 
 | 	if (IT8XXX2_SMB_HOSTA(base) & HOSTA_ANY_ERROR) { | 
 | 		data->err = (IT8XXX2_SMB_HOSTA(base) & HOSTA_ANY_ERROR); | 
 | 	} else { | 
 | 		if (data->num_msgs == 2) { | 
 | 			return i2c_tran_fifo_write_to_read(dev); | 
 | 		} else if (data->active_msg->flags & I2C_MSG_READ) { | 
 | 			return i2c_tran_fifo_read(dev); | 
 | 		} else { | 
 | 			return i2c_tran_fifo_write(dev); | 
 | 		} | 
 | 	} | 
 | 	/* W/C */ | 
 | 	IT8XXX2_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; | 
 | 	/* Disable the SMBus host interface. */ | 
 | 	IT8XXX2_SMB_HOCTL2(base) = 0x00; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | bool __soc_ram_code fifo_mode_allowed(const struct device *dev, | 
 | 				      struct i2c_msg *msgs) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 |  | 
 | 	/* | 
 | 	 * If the transaction of write or read is divided into two | 
 | 	 * transfers(not two messages), the FIFO mode does not support. | 
 | 	 */ | 
 | 	if (data->i2ccs != I2C_CH_NORMAL) { | 
 | 		return false; | 
 | 	} | 
 | 	/* | 
 | 	 * FIFO2 only supports one channel of B or C. If the FIFO of | 
 | 	 * channel is not enabled, it will select PIO mode. | 
 | 	 */ | 
 | 	if (!config->fifo_enable) { | 
 | 		return false; | 
 | 	} | 
 | 	/* | 
 | 	 * When there is only one message, use the FIFO mode transfer | 
 | 	 * directly. | 
 | 	 * Transfer payload too long (>255 bytes), use PIO mode. | 
 | 	 * Write or read of I2C target address without data, used by | 
 | 	 * cmd_i2c_scan. Use PIO mode. | 
 | 	 */ | 
 | 	if (data->num_msgs == 1 && (msgs[0].flags & I2C_MSG_STOP) && | 
 | 	    (msgs[0].len <= I2C_FIFO_MODE_TOTAL_LEN) && (msgs[0].len != 0)) { | 
 | 		return true; | 
 | 	} | 
 | 	/* | 
 | 	 * When there are two messages, we need to judge whether or not there | 
 | 	 * is I2C_MSG_RESTART flag from the second message, and then decide to | 
 | 	 * do the FIFO mode or PIO mode transfer. | 
 | 	 */ | 
 | 	if (data->num_msgs == 2) { | 
 | 		/* | 
 | 		 * The first of two messages must be write. | 
 | 		 * Transfer payload too long (>255 bytes), use PIO mode. | 
 | 		 */ | 
 | 		if (((msgs[0].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) && | 
 | 		    (msgs[0].len <= I2C_FIFO_MODE_TOTAL_LEN)) { | 
 | 			/* | 
 | 			 * The transfer is i2c_burst_read(). | 
 | 			 * | 
 | 			 * e.g. msg[0].flags = I2C_MSG_WRITE; | 
 | 			 *      msg[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | | 
 | 			 *                     I2C_MSG_STOP; | 
 | 			 */ | 
 | 			if ((msgs[1].flags == I2C_MSG_BURST_READ_MASK) && | 
 | 			    (msgs[1].len <= I2C_FIFO_MODE_TOTAL_LEN)) { | 
 | 				return true; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return false; | 
 | } | 
 | #endif | 
 |  | 
 | int __soc_ram_code i2c_tran_read(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	if (data->active_msg->flags & I2C_MSG_START) { | 
 | 		/* i2c enable */ | 
 | 		IT8XXX2_SMB_HOCTL2(base) = IT8XXX2_SMB_SMD_TO_EN | | 
 | 					   IT8XXX2_SMB_I2C_EN | | 
 | 					   IT8XXX2_SMB_SMHEN; | 
 | 		/* | 
 | 		 * bit0, Direction of the host transfer. | 
 | 		 * bit[1:7}, Address of the targeted slave. | 
 | 		 */ | 
 | 		IT8XXX2_SMB_TRASLA(base) = (uint8_t)(data->addr_16bit << 1) | | 
 | 					   IT8XXX2_SMB_DIR; | 
 | 		/* clear start flag */ | 
 | 		data->active_msg->flags &= ~I2C_MSG_START; | 
 | 		/* | 
 | 		 * bit0, Host interrupt enable. | 
 | 		 * bit[2:4}, Extend command. | 
 | 		 * bit5, The firmware shall write 1 to this bit | 
 | 		 *       when the next byte will be the last byte. | 
 | 		 * bit6, start. | 
 | 		 */ | 
 | 		if ((data->active_msg->len == 1) && | 
 | 		    (data->active_msg->flags & I2C_MSG_STOP)) { | 
 | 			IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_SRT | | 
 | 						  IT8XXX2_SMB_LABY | | 
 | 						  IT8XXX2_SMB_SMCD_EXTND | | 
 | 						  IT8XXX2_SMB_INTREN; | 
 | 		} else { | 
 | 			IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_SRT | | 
 | 						  IT8XXX2_SMB_SMCD_EXTND | | 
 | 						  IT8XXX2_SMB_INTREN; | 
 | 		} | 
 | 	} else { | 
 | 		if ((data->i2ccs == I2C_CH_REPEAT_START) || | 
 | 		    (data->i2ccs == I2C_CH_WAIT_READ)) { | 
 | 			if (data->i2ccs == I2C_CH_REPEAT_START) { | 
 | 				/* write to read */ | 
 | 				i2c_w2r_change_direction(dev); | 
 | 			} else { | 
 | 				/* For last byte */ | 
 | 				i2c_r_last_byte(dev); | 
 | 				/* W/C for next byte */ | 
 | 				IT8XXX2_SMB_HOSTA(base) = HOSTA_NEXT_BYTE; | 
 | 			} | 
 | 			data->i2ccs = I2C_CH_NORMAL; | 
 | 		} else if (IT8XXX2_SMB_HOSTA(base) & HOSTA_BDS) { | 
 | 			if (data->ridx < data->active_msg->len) { | 
 | 				/* To get received data. */ | 
 | 				*(data->active_msg->buf++) = IT8XXX2_SMB_HOBDB(base); | 
 | 				data->ridx++; | 
 | 				/* For last byte */ | 
 | 				i2c_r_last_byte(dev); | 
 | 				/* done */ | 
 | 				if (data->ridx == data->active_msg->len) { | 
 | 					data->active_msg->len = 0; | 
 | 					if (data->active_msg->flags & I2C_MSG_STOP) { | 
 | 						/* W/C for finish */ | 
 | 						IT8XXX2_SMB_HOSTA(base) = | 
 | 						HOSTA_NEXT_BYTE; | 
 |  | 
 | 						data->stop = 1; | 
 | 					} else { | 
 | 						data->i2ccs = I2C_CH_WAIT_READ; | 
 | 						return 0; | 
 | 					} | 
 | 				} else { | 
 | 					/* W/C for next byte */ | 
 | 					IT8XXX2_SMB_HOSTA(base) = | 
 | 							HOSTA_NEXT_BYTE; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return 1; | 
 |  | 
 | } | 
 |  | 
 | int __soc_ram_code i2c_tran_write(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	if (data->active_msg->flags & I2C_MSG_START) { | 
 | 		/* i2c enable */ | 
 | 		IT8XXX2_SMB_HOCTL2(base) = IT8XXX2_SMB_SMD_TO_EN | | 
 | 					   IT8XXX2_SMB_I2C_EN | | 
 | 					   IT8XXX2_SMB_SMHEN; | 
 | 		/* | 
 | 		 * bit0, Direction of the host transfer. | 
 | 		 * bit[1:7}, Address of the targeted slave. | 
 | 		 */ | 
 | 		IT8XXX2_SMB_TRASLA(base) = (uint8_t)data->addr_16bit << 1; | 
 | 		/* Send first byte */ | 
 | 		IT8XXX2_SMB_HOBDB(base) = *(data->active_msg->buf++); | 
 |  | 
 | 		data->widx++; | 
 | 		/* clear start flag */ | 
 | 		data->active_msg->flags &= ~I2C_MSG_START; | 
 | 		/* | 
 | 		 * bit0, Host interrupt enable. | 
 | 		 * bit[2:4}, Extend command. | 
 | 		 * bit6, start. | 
 | 		 */ | 
 | 		IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_SRT | | 
 | 					  IT8XXX2_SMB_SMCD_EXTND | | 
 | 					  IT8XXX2_SMB_INTREN; | 
 | 	} else { | 
 | 		/* Host has completed the transmission of a byte */ | 
 | 		if (IT8XXX2_SMB_HOSTA(base) & HOSTA_BDS) { | 
 | 			if (data->widx < data->active_msg->len) { | 
 | 				/* Send next byte */ | 
 | 				IT8XXX2_SMB_HOBDB(base) = *(data->active_msg->buf++); | 
 |  | 
 | 				data->widx++; | 
 | 				/* W/C byte done for next byte */ | 
 | 				IT8XXX2_SMB_HOSTA(base) = HOSTA_NEXT_BYTE; | 
 |  | 
 | 				if (data->i2ccs == I2C_CH_REPEAT_START) { | 
 | 					data->i2ccs = I2C_CH_NORMAL; | 
 | 				} | 
 | 			} else { | 
 | 				/* done */ | 
 | 				data->active_msg->len = 0; | 
 | 				if (data->active_msg->flags & I2C_MSG_STOP) { | 
 | 					/* set I2C_EN = 0 */ | 
 | 					IT8XXX2_SMB_HOCTL2(base) = IT8XXX2_SMB_SMD_TO_EN | | 
 | 								   IT8XXX2_SMB_SMHEN; | 
 | 					/* W/C byte done for finish */ | 
 | 					IT8XXX2_SMB_HOSTA(base) = HOSTA_NEXT_BYTE; | 
 |  | 
 | 					data->stop = 1; | 
 | 				} else { | 
 | 					data->i2ccs = I2C_CH_REPEAT_START; | 
 | 					return 0; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return 1; | 
 |  | 
 | } | 
 |  | 
 | int __soc_ram_code i2c_pio_transaction(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 |  | 
 | 	/* any error */ | 
 | 	if (IT8XXX2_SMB_HOSTA(base) & HOSTA_ANY_ERROR) { | 
 | 		data->err = (IT8XXX2_SMB_HOSTA(base) & HOSTA_ANY_ERROR); | 
 | 	} else { | 
 | 		if (!data->stop) { | 
 | 			/* | 
 | 			 * The return value indicates if there is more data | 
 | 			 * to be read or written. If the return value = 1, | 
 | 			 * it means that the interrupt cannot be disable and | 
 | 			 * continue to transmit data. | 
 | 			 */ | 
 | 			if (data->active_msg->flags & I2C_MSG_READ) { | 
 | 				return i2c_tran_read(dev); | 
 | 			} else { | 
 | 				return i2c_tran_write(dev); | 
 | 			} | 
 | 		} | 
 | 		/* wait finish */ | 
 | 		if (!(IT8XXX2_SMB_HOSTA(base) & HOSTA_FINTR)) { | 
 | 			return 1; | 
 | 		} | 
 | 	} | 
 | 	/* W/C */ | 
 | 	IT8XXX2_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; | 
 | 	/* disable the SMBus host interface */ | 
 | 	IT8XXX2_SMB_HOCTL2(base) = 0x00; | 
 |  | 
 | 	data->stop = 0; | 
 | 	/* done doing work */ | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int i2c_it8xxx2_transfer(const struct device *dev, struct i2c_msg *msgs, | 
 | 				uint8_t num_msgs, uint16_t addr) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	int res, ret; | 
 |  | 
 | 	/* Lock mutex of i2c controller */ | 
 | 	k_mutex_lock(&data->mutex, K_FOREVER); | 
 | 	/* | 
 | 	 * If the transaction of write to read is divided into two | 
 | 	 * transfers, the repeat start transfer uses this flag to | 
 | 	 * exclude checking bus busy. | 
 | 	 */ | 
 | 	if (data->i2ccs == I2C_CH_NORMAL) { | 
 | 		struct i2c_msg *start_msg = &msgs[0]; | 
 |  | 
 | 		/* Make sure we're in a good state to start */ | 
 | 		if (i2c_bus_not_available(dev)) { | 
 | 			/* Recovery I2C bus */ | 
 | 			i2c_recover_bus(dev); | 
 | 			/* | 
 | 			 * After resetting I2C bus, if I2C bus is not available | 
 | 			 * (No external pull-up), drop the transaction. | 
 | 			 */ | 
 | 			if (i2c_bus_not_available(dev)) { | 
 | 				/* Unlock mutex of i2c controller */ | 
 | 				k_mutex_unlock(&data->mutex); | 
 | 				return -EIO; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		start_msg->flags |= I2C_MSG_START; | 
 | 	} | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | 	/* Store num_msgs to data struct. */ | 
 | 	data->num_msgs = num_msgs; | 
 | 	/* Store msgs to data struct. */ | 
 | 	data->msgs_list = msgs; | 
 | 	bool fifo_mode_enable = fifo_mode_allowed(dev, msgs); | 
 |  | 
 | 	if (fifo_mode_enable) { | 
 | 		/* Block to enter power policy. */ | 
 | 		pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); | 
 | 	} | 
 | #endif | 
 | 	for (int i = 0; i < num_msgs; i++) { | 
 |  | 
 | 		data->widx = 0; | 
 | 		data->ridx = 0; | 
 | 		data->err = 0; | 
 | 		data->active_msg = &msgs[i]; | 
 | 		data->addr_16bit = addr; | 
 |  | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | 		data->active_msg_index = 0; | 
 | 		/* | 
 | 		 * Start transaction. | 
 | 		 * The return value indicates if the initial configuration | 
 | 		 * of I2C transaction for read or write has been completed. | 
 | 		 */ | 
 | 		if (fifo_mode_enable) { | 
 | 			if (i2c_fifo_transaction(dev)) { | 
 | 				/* Enable i2c interrupt */ | 
 | 				irq_enable(config->i2c_irq_base); | 
 | 			} | 
 | 		} else | 
 | #endif | 
 | 		{ | 
 | 			if (i2c_pio_transaction(dev)) { | 
 | 				/* Enable i2c interrupt */ | 
 | 				irq_enable(config->i2c_irq_base); | 
 | 			} | 
 | 		} | 
 | 		/* Wait for the transfer to complete */ | 
 | 		/* TODO: the timeout should be adjustable */ | 
 | 		res = k_sem_take(&data->device_sync_sem, K_MSEC(100)); | 
 | 		/* | 
 | 		 * The irq will be enabled at the condition of start or | 
 | 		 * repeat start of I2C. If timeout occurs without being | 
 | 		 * wake up during suspend(ex: interrupt is not fired), | 
 | 		 * the irq should be disabled immediately. | 
 | 		 */ | 
 | 		irq_disable(config->i2c_irq_base); | 
 | 		/* | 
 | 		 * The transaction is dropped on any error(timeout, NACK, fail, | 
 | 		 * bus error, device error). | 
 | 		 */ | 
 | 		if (data->err) { | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		if (res != 0) { | 
 | 			data->err = ETIMEDOUT; | 
 | 			/* reset i2c port */ | 
 | 			i2c_reset(dev); | 
 | 			LOG_ERR("I2C ch%d:0x%X reset cause %d", | 
 | 				config->port, data->addr_16bit, I2C_RC_TIMEOUT); | 
 | 			/* If this message is sent fail, drop the transaction. */ | 
 | 			break; | 
 | 		} | 
 |  | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | 		/* | 
 | 		 * In FIFO mode, messages are compressed into a single | 
 | 		 * transaction. | 
 | 		 */ | 
 | 		if (fifo_mode_enable) { | 
 | 			break; | 
 | 		} | 
 | #endif | 
 | 	} | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | 	if (fifo_mode_enable) { | 
 | 		volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 |  | 
 | 		/* Disable SMB channels in FIFO mode. */ | 
 | 		*reg_mstfctrl &= ~IT8XXX2_SMB_FFEN; | 
 | 		/* Disable I2C write to read FIFO mode. */ | 
 | 		if (data->num_msgs == 2) { | 
 | 			i2c_fifo_en_w2r(dev, 0); | 
 | 		} | 
 | 		/* Permit to enter power policy. */ | 
 | 		pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); | 
 | 	} | 
 | #endif | 
 | 	/* reset i2c channel status */ | 
 | 	if (data->err || (data->active_msg->flags & I2C_MSG_STOP)) { | 
 | 		data->i2ccs = I2C_CH_NORMAL; | 
 | 	} | 
 | 	/* Save return value. */ | 
 | 	ret = i2c_parsing_return_value(dev); | 
 | 	/* Unlock mutex of i2c controller */ | 
 | 	k_mutex_unlock(&data->mutex); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void i2c_it8xxx2_isr(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 |  | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | 	volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 |  | 
 | 	/* If done doing work, wake up the task waiting for the transfer. */ | 
 | 	if (config->fifo_enable && (*reg_mstfctrl & IT8XXX2_SMB_FFEN)) { | 
 | 		if (i2c_fifo_transaction(dev)) { | 
 | 			return; | 
 | 		} | 
 | 	} else | 
 | #endif | 
 | 	{ | 
 | 		if (i2c_pio_transaction(dev)) { | 
 | 			return; | 
 | 		} | 
 | 	} | 
 | 	irq_disable(config->i2c_irq_base); | 
 | 	k_sem_give(&data->device_sync_sem); | 
 | } | 
 |  | 
 | static int i2c_it8xxx2_init(const struct device *dev) | 
 | { | 
 | 	struct i2c_it8xxx2_data *data = dev->data; | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	uint8_t *base = config->base; | 
 | 	uint32_t bitrate_cfg; | 
 | 	int error, status; | 
 |  | 
 | 	/* Initialize mutex and semaphore */ | 
 | 	k_mutex_init(&data->mutex); | 
 | 	k_sem_init(&data->device_sync_sem, 0, K_SEM_MAX_LIMIT); | 
 |  | 
 | 	/* Enable clock to specified peripheral */ | 
 | 	volatile uint8_t *reg = (volatile uint8_t *) | 
 | 		(IT8XXX2_ECPM_BASE + (config->clock_gate_offset >> 8)); | 
 | 	uint8_t reg_mask = config->clock_gate_offset & 0xff; | 
 | 	*reg &= ~reg_mask; | 
 |  | 
 | 	/* Enable SMBus function */ | 
 | 	/* | 
 | 	 * bit0, The SMBus host interface is enabled. | 
 | 	 * bit1, Enable to communicate with I2C device | 
 | 	 *		  and support I2C-compatible cycles. | 
 | 	 * bit4, This bit controls the reset mechanism | 
 | 	 *		  of SMBus master to handle the SMDAT | 
 | 	 *		  line low if 25ms reg timeout. | 
 | 	 */ | 
 | 	IT8XXX2_SMB_HOCTL2(base) = IT8XXX2_SMB_SMD_TO_EN | IT8XXX2_SMB_SMHEN; | 
 | 	/* | 
 | 	 * bit1, Kill SMBus host transaction. | 
 | 	 * bit0, Enable the interrupt for the master interface. | 
 | 	 */ | 
 | 	IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_KILL | IT8XXX2_SMB_SMHEN; | 
 | 	IT8XXX2_SMB_HOCTL(base) = IT8XXX2_SMB_SMHEN; | 
 |  | 
 | 	/* W/C host status register */ | 
 | 	IT8XXX2_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; | 
 | 	IT8XXX2_SMB_HOCTL2(base) = 0x00; | 
 |  | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | 	volatile uint8_t *reg_mstfctrl = config->reg_mstfctrl; | 
 |  | 
 | 	if (config->port == SMB_CHANNEL_B && config->fifo_enable) { | 
 | 		/* Select channel B in FIFO2. */ | 
 | 		*reg_mstfctrl = IT8XXX2_SMB_FFCHSEL2_B; | 
 | 	} else if (config->port == SMB_CHANNEL_C && config->fifo_enable) { | 
 | 		/* Select channel C in FIFO2. */ | 
 | 		*reg_mstfctrl = IT8XXX2_SMB_FFCHSEL2_C; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	/* ChannelA-C switch selection of I2C pin */ | 
 | 	if (config->port == SMB_CHANNEL_A) { | 
 | 		IT8XXX2_SMB_SMB01CHS = (IT8XXX2_SMB_SMB01CHS &= ~GENMASK(2, 0)) | | 
 | 			config->channel_switch_sel; | 
 | 	} else if (config->port == SMB_CHANNEL_B) { | 
 | 		IT8XXX2_SMB_SMB01CHS = (config->channel_switch_sel << 4) | | 
 | 			(IT8XXX2_SMB_SMB01CHS &= ~GENMASK(6, 4)); | 
 | 	} else if (config->port == SMB_CHANNEL_C) { | 
 | 		IT8XXX2_SMB_SMB23CHS = (IT8XXX2_SMB_SMB23CHS &= ~GENMASK(2, 0)) | | 
 | 			config->channel_switch_sel; | 
 | 	} | 
 |  | 
 | 	/* Set clock frequency for I2C ports */ | 
 | 	if (config->bitrate == I2C_BITRATE_STANDARD || | 
 | 		config->bitrate == I2C_BITRATE_FAST || | 
 | 		config->bitrate == I2C_BITRATE_FAST_PLUS) { | 
 | 		bitrate_cfg = i2c_map_dt_bitrate(config->bitrate); | 
 | 	} else { | 
 | 		/* Device tree specified speed */ | 
 | 		bitrate_cfg = I2C_SPEED_DT << I2C_SPEED_SHIFT; | 
 | 	} | 
 |  | 
 | 	error = i2c_it8xxx2_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg); | 
 | 	data->i2ccs = I2C_CH_NORMAL; | 
 |  | 
 | 	if (error) { | 
 | 		LOG_ERR("i2c: failure initializing"); | 
 | 		return error; | 
 | 	} | 
 |  | 
 | 	/* Set the pin to I2C alternate function. */ | 
 | 	status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); | 
 | 	if (status < 0) { | 
 | 		LOG_ERR("Failed to configure I2C pins"); | 
 | 		return status; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int i2c_it8xxx2_recover_bus(const struct device *dev) | 
 | { | 
 | 	const struct i2c_it8xxx2_config *config = dev->config; | 
 | 	int i, status; | 
 |  | 
 | 	/* Output type selection */ | 
 | 	gpio_flags_t flags = GPIO_OUTPUT | (config->push_pull_recovery ? 0 : GPIO_OPEN_DRAIN); | 
 | 	/* Set SCL of I2C as GPIO pin */ | 
 | 	gpio_pin_configure_dt(&config->scl_gpios, flags); | 
 | 	/* Set SDA of I2C as GPIO pin */ | 
 | 	gpio_pin_configure_dt(&config->sda_gpios, flags); | 
 |  | 
 | 	/* | 
 | 	 * In I2C recovery bus, 1ms sleep interval for bitbanging i2c | 
 | 	 * is mainly to ensure that gpio has enough time to go from | 
 | 	 * low to high or high to low. | 
 | 	 */ | 
 | 	/* Pull SCL and SDA pin to high */ | 
 | 	gpio_pin_set_dt(&config->scl_gpios, 1); | 
 | 	gpio_pin_set_dt(&config->sda_gpios, 1); | 
 | 	k_msleep(1); | 
 |  | 
 | 	/* Start condition */ | 
 | 	gpio_pin_set_dt(&config->sda_gpios, 0); | 
 | 	k_msleep(1); | 
 | 	gpio_pin_set_dt(&config->scl_gpios, 0); | 
 | 	k_msleep(1); | 
 |  | 
 | 	/* 9 cycles of SCL with SDA held high */ | 
 | 	for (i = 0; i < 9; i++) { | 
 | 		/* SDA */ | 
 | 		gpio_pin_set_dt(&config->sda_gpios, 1); | 
 | 		/* SCL */ | 
 | 		gpio_pin_set_dt(&config->scl_gpios, 1); | 
 | 		k_msleep(1); | 
 | 		/* SCL */ | 
 | 		gpio_pin_set_dt(&config->scl_gpios, 0); | 
 | 		k_msleep(1); | 
 | 	} | 
 | 	/* SDA */ | 
 | 	gpio_pin_set_dt(&config->sda_gpios, 0); | 
 | 	k_msleep(1); | 
 |  | 
 | 	/* Stop condition */ | 
 | 	gpio_pin_set_dt(&config->scl_gpios, 1); | 
 | 	k_msleep(1); | 
 | 	gpio_pin_set_dt(&config->sda_gpios, 1); | 
 | 	k_msleep(1); | 
 |  | 
 | 	/* Set GPIO back to I2C alternate function of SCL */ | 
 | 	status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); | 
 | 	if (status < 0) { | 
 | 		LOG_ERR("Failed to configure I2C pins"); | 
 | 		return status; | 
 | 	} | 
 |  | 
 | 	/* reset i2c port */ | 
 | 	i2c_reset(dev); | 
 | 	LOG_ERR("I2C ch%d reset cause %d", config->port, | 
 | 		I2C_RC_NO_IDLE_FOR_START); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct i2c_driver_api i2c_it8xxx2_driver_api = { | 
 | 	.configure = i2c_it8xxx2_configure, | 
 | 	.get_config = i2c_it8xxx2_get_config, | 
 | 	.transfer = i2c_it8xxx2_transfer, | 
 | 	.recover_bus = i2c_it8xxx2_recover_bus, | 
 | }; | 
 |  | 
 | #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE | 
 | /* | 
 |  * Sometimes, channel C may write wrong register to the target device. | 
 |  * This issue occurs when FIFO2 is enabled on channel C. The problem | 
 |  * arises because FIFO2 is shared between channel B and channel C. | 
 |  * FIFO2 will be disabled when data access is completed, at which point | 
 |  * FIFO2 is set to the default configuration for channel B. | 
 |  * The byte counter of FIFO2 may be affected by channel B. There is a chance | 
 |  * that channel C may encounter wrong register being written due to FIFO2 | 
 |  * byte counter wrong write after channel B's write operation. | 
 |  */ | 
 | BUILD_ASSERT((DT_PROP(DT_NODELABEL(i2c2), fifo_enable) == false), | 
 | 	     "Channel C cannot use FIFO mode."); | 
 | #endif | 
 |  | 
 | #ifdef CONFIG_SOC_IT8XXX2_EC_BUS_24MHZ | 
 | #define I2C_IT8XXX2_CHECK_SUPPORTED_CLOCK(inst)                                 \ | 
 | 	BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) ==                    \ | 
 | 		     I2C_BITRATE_FAST), "Only supports 400 KHz"); | 
 |  | 
 | DT_INST_FOREACH_STATUS_OKAY(I2C_IT8XXX2_CHECK_SUPPORTED_CLOCK) | 
 | #endif | 
 |  | 
 | #define I2C_ITE_IT8XXX2_INIT(inst)                                              \ | 
 | 	PINCTRL_DT_INST_DEFINE(inst);                                           \ | 
 | 	BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) ==                    \ | 
 | 		     50000) ||                                                  \ | 
 | 		     (DT_INST_PROP(inst, clock_frequency) ==                    \ | 
 | 		     I2C_BITRATE_STANDARD) ||                                   \ | 
 | 		     (DT_INST_PROP(inst, clock_frequency) ==                    \ | 
 | 		     I2C_BITRATE_FAST) ||                                       \ | 
 | 		     (DT_INST_PROP(inst, clock_frequency) ==                    \ | 
 | 		     I2C_BITRATE_FAST_PLUS), "Not support I2C bit rate value"); \ | 
 | 	static void i2c_it8xxx2_config_func_##inst(void);                       \ | 
 | 										\ | 
 | 	static const struct i2c_it8xxx2_config i2c_it8xxx2_cfg_##inst = {       \ | 
 | 		.base = (uint8_t *)(DT_INST_REG_ADDR_BY_IDX(inst, 0)),          \ | 
 | 		.reg_mstfctrl = (uint8_t *)(DT_INST_REG_ADDR_BY_IDX(inst, 1)),  \ | 
 | 		.irq_config_func = i2c_it8xxx2_config_func_##inst,              \ | 
 | 		.bitrate = DT_INST_PROP(inst, clock_frequency),                 \ | 
 | 		.i2c_irq_base = DT_INST_IRQN(inst),                             \ | 
 | 		.port = DT_INST_PROP(inst, port_num),                           \ | 
 | 		.channel_switch_sel = DT_INST_PROP(inst, channel_switch_sel),   \ | 
 | 		.scl_gpios = GPIO_DT_SPEC_INST_GET(inst, scl_gpios),            \ | 
 | 		.sda_gpios = GPIO_DT_SPEC_INST_GET(inst, sda_gpios),            \ | 
 | 		.clock_gate_offset = DT_INST_PROP(inst, clock_gate_offset),     \ | 
 | 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),                   \ | 
 | 		.fifo_enable = DT_INST_PROP(inst, fifo_enable),                 \ | 
 | 		.push_pull_recovery = DT_INST_PROP(inst, push_pull_recovery),   \ | 
 | 	};                                                                      \ | 
 | 										\ | 
 | 	static struct i2c_it8xxx2_data i2c_it8xxx2_data_##inst;                 \ | 
 | 										\ | 
 | 	I2C_DEVICE_DT_INST_DEFINE(inst, i2c_it8xxx2_init,                       \ | 
 | 				  NULL,                                         \ | 
 | 				  &i2c_it8xxx2_data_##inst,                     \ | 
 | 				  &i2c_it8xxx2_cfg_##inst,                      \ | 
 | 				  POST_KERNEL,                                  \ | 
 | 				  CONFIG_I2C_INIT_PRIORITY,                     \ | 
 | 				  &i2c_it8xxx2_driver_api);                     \ | 
 | 										\ | 
 | 	static void i2c_it8xxx2_config_func_##inst(void)                        \ | 
 | 	{                                                                       \ | 
 | 		IRQ_CONNECT(DT_INST_IRQN(inst),                                 \ | 
 | 			0,                                                      \ | 
 | 			i2c_it8xxx2_isr,                                        \ | 
 | 			DEVICE_DT_INST_GET(inst), 0);                           \ | 
 | 	} | 
 |  | 
 | DT_INST_FOREACH_STATUS_OKAY(I2C_ITE_IT8XXX2_INIT) |