diff --git a/CODEOWNERS b/CODEOWNERS index 747ba99ef42..fcdc946d1b0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -208,8 +208,10 @@ /drivers/i2s/i2s_ll_stm32* @avisconti /drivers/i2c/i2c_common.c @sjg20 /drivers/i2c/i2c_emul.c @sjg20 +/drivers/i2c/i2c_ite_it8xxx2.c @GTLin08 /drivers/i2c/i2c_shell.c @nashif /drivers/i2c/Kconfig.i2c_emul @sjg20 +/drivers/i2c/Kconfig.it8xxx2 @GTLin08 /drivers/i2c/slave/*eeprom* @henrikbrixandersen /drivers/i2s/*litex* @mateusz-holenko @kgugala @pgielda /drivers/ieee802154/ @jukkar @tbursztyka diff --git a/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts b/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts index 51fefb6db79..368b3a76f34 100644 --- a/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts +++ b/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts @@ -26,6 +26,30 @@ &gpiof { status = "okay"; }; +&i2c0 { + status = "okay"; + clock-frequency = ; +}; +&i2c1 { + status = "okay"; + clock-frequency = ; +}; +&i2c2 { + status = "okay"; + clock-frequency = ; +}; +&i2c3 { + status = "okay"; + clock-frequency = ; +}; +&i2c4 { + status = "okay"; + clock-frequency = ; +}; +&i2c5 { + status = "okay"; + clock-frequency = ; +}; &uart1 { status = "okay"; current-speed = <115200>; diff --git a/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig b/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig index 0a020006050..2adbe386f3e 100644 --- a/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig +++ b/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig @@ -27,3 +27,5 @@ CONFIG_LINKER_ORPHAN_SECTION_PLACE=y CONFIG_COMPILER_OPT="-mcmodel=medlow" CONFIG_GPIO=y CONFIG_GPIO_ITE_IT8XXX2=y +CONFIG_I2C=y +CONFIG_I2C_ITE_IT8XXX2=y diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index 76991d0a28b..ac5a84fe762 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -9,6 +9,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_CC13XX_CC26XX i2c_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_I2C_CC32XX i2c_cc32xx.c) zephyr_library_sources_ifdef(CONFIG_I2C_ESP32 i2c_esp32.c) zephyr_library_sources_ifdef(CONFIG_I2C_GPIO i2c_gpio.c) +zephyr_library_sources_ifdef(CONFIG_I2C_ITE_IT8XXX2 i2c_ite_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_I2C_IMX i2c_imx.c) zephyr_library_sources_ifdef(CONFIG_I2C_LPC11U6X i2c_lpc11u6x.c) zephyr_library_sources_ifdef(CONFIG_I2C_XEC i2c_mchp_xec.c) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 921331f1400..fbed4e5a864 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -32,6 +32,7 @@ source "drivers/i2c/Kconfig.gpio" source "drivers/i2c/Kconfig.xec" source "drivers/i2c/Kconfig.nrfx" source "drivers/i2c/Kconfig.i2c_emul" +source "drivers/i2c/Kconfig.it8xxx2" source "drivers/i2c/Kconfig.sbcon" source "drivers/i2c/Kconfig.sifive" source "drivers/i2c/Kconfig.stm32" diff --git a/drivers/i2c/Kconfig.it8xxx2 b/drivers/i2c/Kconfig.it8xxx2 new file mode 100644 index 00000000000..22adc789544 --- /dev/null +++ b/drivers/i2c/Kconfig.it8xxx2 @@ -0,0 +1,9 @@ +# Copyright (c) 2020 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +config I2C_ITE_IT8XXX2 + bool "ITE IT8XXX2 I2C driver" + help + Enable I2C support on it8xxx2_evb. + Supported Speeds: 100kHz, 400kHz and 1MHz. + This driver supports repeated start. diff --git a/drivers/i2c/i2c_ite_it8xxx2.c b/drivers/i2c/i2c_ite_it8xxx2.c new file mode 100644 index 00000000000..1bf664096ca --- /dev/null +++ b/drivers/i2c/i2c_ite_it8xxx2.c @@ -0,0 +1,891 @@ +/* + * Copyright (c) 2020 ITE Corporation. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ite_it8xxx2_i2c + +#include +#include +#include + LOG_MODULE_REGISTER(i2c_ite_it8xxx2); +#include "i2c-priv.h" +#include +#include + +#define DEV_CFG(dev) \ + ((const struct i2c_it8xxx2_config * const)(dev)->config) +#define DEV_DATA(dev) \ + ((struct i2c_it8xxx2_data * const)(dev)->data) + +#define I2C_STANDARD_PORT_COUNT 3 +/* Default PLL frequency. */ +#define PLL_CLOCK 48000000 + +struct i2c_it8xxx2_config { + void (*irq_config_func)(void); + uint32_t bitrate; + uint8_t *base; + uint8_t i2c_irq_base; + uint8_t port; +}; + +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 *msgs; + struct k_sem device_sync_sem; + /* Index into output data */ + size_t widx; + /* Index into input data */ + size_t ridx; + /* 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 enhanced_i2c_transfer_direct { + TX_DIRECT, + RX_DIRECT, +}; + +enum enhanced_i2c_ctl { + /* Hardware reset */ + E_HW_RST = 0x01, + /* Stop */ + E_STOP = 0x02, + /* Start & Repeat start */ + E_START = 0x04, + /* Acknowledge */ + E_ACK = 0x08, + /* State reset */ + E_STS_RST = 0x10, + /* Mode select */ + E_MODE_SEL = 0x20, + /* I2C interrupt enable */ + E_INT_EN = 0x40, + /* 0 : Standard mode , 1 : Receive mode */ + E_RX_MODE = 0x80, + /* State reset and hardware reset */ + E_STS_AND_HW_RST = (E_STS_RST | E_HW_RST), + /* Generate start condition and transmit slave address */ + E_START_ID = (E_INT_EN | E_MODE_SEL | E_ACK | E_START | E_HW_RST), + /* Generate stop condition */ + E_FINISH = (E_INT_EN | E_MODE_SEL | E_ACK | E_STOP | E_HW_RST), +}; + +enum enhanced_i2c_host_status { + /* ACK receive */ + E_HOSTA_ACK = 0x01, + /* Interrupt pending */ + E_HOSTA_INTP = 0x02, + /* Read/Write */ + E_HOSTA_RW = 0x04, + /* Time out error */ + E_HOSTA_TMOE = 0x08, + /* Arbitration lost */ + E_HOSTA_ARB = 0x10, + /* Bus busy */ + E_HOSTA_BB = 0x20, + /* Address match */ + E_HOSTA_AM = 0x40, + /* Byte done status */ + E_HOSTA_BDS = 0x80, + /* time out or lost arbitration */ + E_HOSTA_ANY_ERROR = (E_HOSTA_TMOE | E_HOSTA_ARB), + /* Byte transfer done and ACK receive */ + E_HOSTA_BDS_AND_ACK = (E_HOSTA_BDS | E_HOSTA_ACK), +}; + +enum i2c_reset_cause { + I2C_RC_NO_IDLE_FOR_START = 1, + I2C_RC_TIMEOUT, +}; + +/* 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) + +struct i2c_pin { + volatile uint8_t *mirror_clk; + volatile uint8_t *mirror_data; + uint8_t clk_mask; + uint8_t data_mask; +}; + +static const struct i2c_pin i2c_pin_regs[] = { + { &GPDMRB, &GPDMRB, 0x08, 0x10}, + { &GPDMRC, &GPDMRC, 0x02, 0x04}, + { &GPDMRF, &GPDMRF, 0x40, 0x80}, + { &GPDMRH, &GPDMRH, 0x02, 0x04}, + { &GPDMRE, &GPDMRE, 0x01, 0x80}, + { &GPDMRA, &GPDMRA, 0x10, 0x20}, +}; + +static int i2c_get_line_levels(const struct device *dev) +{ + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + int pin_sts = 0; + + if (config->port < I2C_STANDARD_PORT_COUNT) { + return IT83XX_SMB_SMBPCTL(base) & 0x03; + } + + if (*i2c_pin_regs[config->port].mirror_clk & + i2c_pin_regs[config->port].clk_mask) { + pin_sts |= I2C_LINE_SCL_HIGH; + } + if (*i2c_pin_regs[config->port].mirror_data & + i2c_pin_regs[config->port].data_mask) { + pin_sts |= I2C_LINE_SDA_HIGH; + } + + return pin_sts; +} + +static int i2c_is_busy(const struct device *dev) +{ + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + + if (config->port < I2C_STANDARD_PORT_COUNT) { + return (IT83XX_SMB_HOSTA(base) & + (HOSTA_HOBY | HOSTA_ALL_WC_BIT)); + } + + return (IT83XX_I2C_STR(base) & E_HOSTA_BB); +} + +static void i2c_reset(const struct device *dev, int cause) +{ + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + + if (config->port < I2C_STANDARD_PORT_COUNT) { + /* bit1, kill current transaction. */ + IT83XX_SMB_HOCTL(base) = 0x2; + IT83XX_SMB_HOCTL(base) = 0; + /* W/C host status register */ + IT83XX_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; + } else { + /* State reset and hardware reset */ + IT83XX_I2C_CTR(base) = E_STS_AND_HW_RST; + } + printk("I2C ch%d reset cause %d\n", config->port, cause); +} + +/* + * 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. */ + IT83XX_SMB_SCLKTS(port) = 0; + /* Suggested setting of timing registers of 400kHz. */ + IT83XX_SMB_4P7USL = 0x6; + IT83XX_SMB_4P0USL = 0; + IT83XX_SMB_300NS = 0x1; + IT83XX_SMB_250NS = 0x2; + IT83XX_SMB_45P3USL = 0x6a; + IT83XX_SMB_45P3USH = 0x1; + IT83XX_SMB_4P7A4P0H = 0; +} + +/* Set clock frequency for i2c port A, B , or C */ +static void i2c_standard_port_set_frequency(const struct device *dev, + int freq_khz, int freq_set) +{ + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + + /* + * 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_khz == 400) { + i2c_standard_port_timing_regs_400khz(config->port); + } else { + IT83XX_SMB_SCLKTS(config->port) = freq_set; + } + + /* This field defines the SMCLK0/1/2 clock/data low timeout. */ + IT83XX_SMB_25MS = I2C_CLK_LOW_TIMEOUT; +} + +/* Set clock frequency for i2c port D, E , or F */ +static void i2c_enhanced_port_set_frequency(const struct device *dev, + int freq_khz) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint32_t clk_div, psr; + uint8_t *base = config->base; + + /* + * Let psr(Prescale) = IT83XX_I2C_PSR(p_ch) + * Then, 1 SCL cycle = 2 x (psr + 2) x SMBus clock cycle + * SMBus clock = PLL_CLOCK / clk_div + * SMBus clock cycle = 1 / SMBus clock + * 1 SCL cycle = 1 / (1000 x freq) + * 1 / (1000 x freq) = + * 2 x (psr + 2) x (1 / (PLL_CLOCK / clk_div)) + * psr = ((PLL_CLOCK / clk_div) x + * (1 / (1000 x freq)) x (1 / 2)) - 2 + */ + if (freq_khz) { + /* Get SMBus clock divide value */ + clk_div = (SCDCR2 & 0x0F) + 1U; + /* Calculate PSR value */ + psr = (PLL_CLOCK / (clk_div * (2U * 1000U * freq_khz))) - 2U; + /* Set psr value under 0xFD */ + if (psr > 0xFD) { + psr = 0xFD; + } + + /* Set I2C Speed */ + IT83XX_I2C_PSR(base) = psr & 0xFF; + IT83XX_I2C_HSPR(base) = psr & 0xFF; + /* Backup */ + data->freq = psr & 0xFF; + } + +} + +static int i2c_it8xxx2_configure(const struct device *dev, + uint32_t dev_config_raw) +{ + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint32_t freq, freq_set; + + if (!(I2C_MODE_MASTER & dev_config_raw)) { + return -EINVAL; + } + + if (I2C_ADDR_10_BITS & dev_config_raw) { + return -EINVAL; + } + + switch (I2C_SPEED_GET(dev_config_raw)) { + case I2C_SPEED_STANDARD: + freq = 100; + freq_set = 2; + break; + case I2C_SPEED_FAST: + freq = 400; + freq_set = 3; + break; + case I2C_SPEED_FAST_PLUS: + freq = 1000; + freq_set = 4; + break; + default: + return -EINVAL; + } + + if (config->port < I2C_STANDARD_PORT_COUNT) { + i2c_standard_port_set_frequency(dev, freq, freq_set); + } else { + i2c_enhanced_port_set_frequency(dev, freq); + } + + return 0; +} + +static int enhanced_i2c_error(const struct device *dev) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + uint32_t i2c_str = IT83XX_I2C_STR(base); + + if (i2c_str & E_HOSTA_ANY_ERROR) { + data->err = i2c_str & E_HOSTA_ANY_ERROR; + /* device does not respond ACK */ + } else if ((i2c_str & E_HOSTA_BDS_AND_ACK) == E_HOSTA_BDS) { + if (IT83XX_I2C_CTR(base) & E_ACK) + data->err = E_HOSTA_ACK; + } + + return data->err; +} + +static void enhanced_i2c_start(const struct device *dev) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + + /* State reset and hardware reset */ + IT83XX_I2C_CTR(base) = E_STS_AND_HW_RST; + /* Set i2c frequency */ + IT83XX_I2C_PSR(base) = data->freq; + IT83XX_I2C_HSPR(base) = data->freq; + /* + * Set time out register. + * I2C D/E/F clock/data low timeout. + */ + IT83XX_I2C_TOR(base) = I2C_CLK_LOW_TIMEOUT; + /* bit1: Enable enhanced i2c module */ + IT83XX_I2C_CTR1(base) = BIT(1); +} + +static void i2c_pio_trans_data(const struct device *dev, + enum enhanced_i2c_transfer_direct direct, + uint16_t trans_data, int first_byte) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + uint32_t nack = 0; + + if (first_byte) { + /* First byte must be slave address. */ + IT83XX_I2C_DTR(base) = trans_data | + (direct == RX_DIRECT ? BIT(0) : 0); + /* start or repeat start signal. */ + IT83XX_I2C_CTR(base) = E_START_ID; + } else { + if (direct == TX_DIRECT) { + /* Transmit data */ + IT83XX_I2C_DTR(base) = (uint8_t)trans_data; + } else { + /* + * Receive data. + * Last byte should be NACK in the end of read cycle + */ + if (((data->ridx + 1) == data->msgs->len) && + (data->msgs->flags & I2C_MSG_STOP)) { + nack = 1; + } + } + /* Set hardware reset to start next transmission */ + IT83XX_I2C_CTR(base) = E_INT_EN | E_MODE_SEL | + E_HW_RST | (nack ? 0 : E_ACK); + } +} + +static int enhanced_i2c_tran_read(const struct device *dev) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + uint8_t in_data = 0; + + if (data->msgs->flags & I2C_MSG_START) { + /* clear start flag */ + data->msgs->flags &= ~I2C_MSG_START; + enhanced_i2c_start(dev); + /* Direct read */ + data->i2ccs = I2C_CH_WAIT_READ; + /* Send ID */ + i2c_pio_trans_data(dev, RX_DIRECT, data->addr_16bit, 1); + } else { + if (data->i2ccs) { + if (data->i2ccs == I2C_CH_WAIT_READ) { + data->i2ccs = I2C_CH_NORMAL; + /* Receive data */ + i2c_pio_trans_data(dev, RX_DIRECT, in_data, 0); + + /* data->msgs->flags == I2C_MSG_RESTART */ + } else { + /* Write to read */ + data->i2ccs = I2C_CH_WAIT_READ; + /* Send ID */ + i2c_pio_trans_data(dev, RX_DIRECT, + data->addr_16bit, 1); + } + /* Turn on irq before next direct read */ + irq_enable(config->i2c_irq_base); + } else { + if (data->ridx < data->msgs->len) { + /* read data */ + *(data->msgs->buf++) = IT83XX_I2C_DRR(base); + data->ridx++; + /* done */ + if (data->ridx == data->msgs->len) { + data->msgs->len = 0; + if (data->msgs->flags & I2C_MSG_STOP) { + data->i2ccs = I2C_CH_NORMAL; + IT83XX_I2C_CTR(base) = E_FINISH; + /* wait for stop bit interrupt */ + data->stop = 1; + return 1; + } + /* End the transaction */ + data->i2ccs = I2C_CH_WAIT_READ; + return 0; + } + /* read next byte */ + i2c_pio_trans_data(dev, RX_DIRECT, in_data, 0); + } + } + } + return 1; +} + +static int enhanced_i2c_tran_write(const struct device *dev) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + uint8_t out_data; + + if (data->msgs->flags & I2C_MSG_START) { + /* Clear start bit */ + data->msgs->flags &= ~I2C_MSG_START; + enhanced_i2c_start(dev); + /* Send ID */ + i2c_pio_trans_data(dev, TX_DIRECT, data->addr_16bit, 1); + } else { + /* Host has completed the transmission of a byte */ + if (data->widx < data->msgs->len) { + out_data = *(data->msgs->buf++); + data->widx++; + + /* Send Byte */ + i2c_pio_trans_data(dev, TX_DIRECT, out_data, 0); + if (data->i2ccs == I2C_CH_WAIT_NEXT_XFER) { + data->i2ccs = I2C_CH_NORMAL; + irq_enable(config->i2c_irq_base); + } + } else { + /* done */ + data->msgs->len = 0; + if (data->msgs->flags & I2C_MSG_STOP) { + IT83XX_I2C_CTR(base) = E_FINISH; + /* wait for stop bit interrupt */ + data->stop = 1; + } else { + /* Direct write with direct read */ + data->i2ccs = I2C_CH_WAIT_NEXT_XFER; + return 0; + } + } + } + return 1; +} + +static void i2c_r_last_byte(const struct device *dev) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + 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->msgs->flags & I2C_MSG_STOP) && + (data->ridx == data->msgs->len - 1)) { + IT83XX_SMB_HOCTL(base) |= 0x20; + } +} + +static void i2c_w2r_change_direction(const struct device *dev) +{ + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + + /* I2C switch direction */ + if (IT83XX_SMB_HOCTL2(base) & 0x08) { + i2c_r_last_byte(dev); + IT83XX_SMB_HOSTA(base) = HOSTA_NEXT_BYTE; + } else { + /* + * bit2, I2C switch direction wait. + * bit3, I2C switch direction enable. + */ + IT83XX_SMB_HOCTL2(base) |= 0x0C; + IT83XX_SMB_HOSTA(base) = HOSTA_NEXT_BYTE; + i2c_r_last_byte(dev); + IT83XX_SMB_HOCTL2(base) &= ~0x04; + } +} + +static int i2c_tran_read(const struct device *dev) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + + if (data->msgs->flags & I2C_MSG_START) { + /* i2c enable */ + IT83XX_SMB_HOCTL2(base) = 0x13; + /* + * bit0, Direction of the host transfer. + * bit[1:7}, Address of the targeted slave. + */ + IT83XX_SMB_TRASLA(base) = (uint8_t)data->addr_16bit | 0x01; + /* clear start flag */ + data->msgs->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->msgs->len == 1) && + (data->msgs->flags & I2C_MSG_STOP)) { + IT83XX_SMB_HOCTL(base) = 0x7D; + } else { + IT83XX_SMB_HOCTL(base) = 0x5D; + } + } 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 */ + IT83XX_SMB_HOSTA(base) = HOSTA_NEXT_BYTE; + } + data->i2ccs = I2C_CH_NORMAL; + irq_enable(config->i2c_irq_base); + } else if (IT83XX_SMB_HOSTA(base) & HOSTA_BDS) { + if (data->ridx < data->msgs->len) { + /* To get received data. */ + *(data->msgs->buf++) = IT83XX_SMB_HOBDB(base); + data->ridx++; + /* For last byte */ + i2c_r_last_byte(dev); + /* done */ + if (data->ridx == data->msgs->len) { + data->msgs->len = 0; + if (data->msgs->flags & I2C_MSG_STOP) { + /* W/C for finish */ + IT83XX_SMB_HOSTA(base) = + HOSTA_NEXT_BYTE; + + data->stop = 1; + } else { + data->i2ccs = I2C_CH_WAIT_READ; + return 0; + } + } else { + /* W/C for next byte */ + IT83XX_SMB_HOSTA(base) = + HOSTA_NEXT_BYTE; + } + } + } + } + return 1; + +} + +static int i2c_tran_write(const struct device *dev) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + + if (data->msgs->flags & I2C_MSG_START) { + /* i2c enable */ + IT83XX_SMB_HOCTL2(base) = 0x13; + /* + * bit0, Direction of the host transfer. + * bit[1:7}, Address of the targeted slave. + */ + IT83XX_SMB_TRASLA(base) = (uint8_t)data->addr_16bit; + /* Send first byte */ + IT83XX_SMB_HOBDB(base) = *(data->msgs->buf++); + + data->widx++; + /* clear start flag */ + data->msgs->flags &= ~I2C_MSG_START; + /* + * bit0, Host interrupt enable. + * bit[2:4}, Extend command. + * bit6, start. + */ + IT83XX_SMB_HOCTL(base) = 0x5D; + } else { + /* Host has completed the transmission of a byte */ + if (IT83XX_SMB_HOSTA(base) & HOSTA_BDS) { + if (data->widx < data->msgs->len) { + /* Send next byte */ + IT83XX_SMB_HOBDB(base) = *(data->msgs->buf++); + + data->widx++; + /* W/C byte done for next byte */ + IT83XX_SMB_HOSTA(base) = HOSTA_NEXT_BYTE; + + if (data->i2ccs == I2C_CH_REPEAT_START) { + data->i2ccs = I2C_CH_NORMAL; + irq_enable(config->i2c_irq_base); + } + } else { + /* done */ + data->msgs->len = 0; + if (data->msgs->flags & I2C_MSG_STOP) { + /* set I2C_EN = 0 */ + IT83XX_SMB_HOCTL2(base) = 0x11; + /* W/C byte done for finish */ + IT83XX_SMB_HOSTA(base) = + HOSTA_NEXT_BYTE; + + data->stop = 1; + } else { + data->i2ccs = I2C_CH_REPEAT_START; + return 0; + } + } + } + } + return 1; + +} + +static int i2c_transaction(const struct device *dev) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + + if (config->port < I2C_STANDARD_PORT_COUNT) { + /* any error */ + if (IT83XX_SMB_HOSTA(base) & HOSTA_ANY_ERROR) { + data->err = (IT83XX_SMB_HOSTA(base) & HOSTA_ANY_ERROR); + } else { + if (!data->stop) { + if (data->msgs->flags & I2C_MSG_READ) { + return i2c_tran_read(dev); + } else { + return i2c_tran_write(dev); + } + } + /* wait finish */ + if (!(IT83XX_SMB_HOSTA(base) & HOSTA_FINTR)) { + return 1; + } + } + /* W/C */ + IT83XX_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; + /* disable the SMBus host interface */ + IT83XX_SMB_HOCTL2(base) = 0x00; + } else { + + /* no error */ + if (!(enhanced_i2c_error(dev))) { + if (!data->stop) { + /* i2c read */ + if (data->msgs->flags & I2C_MSG_READ) { + return enhanced_i2c_tran_read(dev); + /* i2c write */ + } else { + return enhanced_i2c_tran_write(dev); + } + } + } + IT83XX_I2C_CTR(base) = E_STS_AND_HW_RST; + IT83XX_I2C_CTR1(base) = 0; + } + 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(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + + /* Check for NULL pointers */ + if (dev == NULL) { + LOG_ERR("Device handle is NULL"); + return -EINVAL; + } + if (msgs == NULL) { + LOG_ERR("Device message is NULL"); + return -EINVAL; + } + + msgs->flags |= I2C_MSG_START; + + for (int i = 0; i < num_msgs; i++) { + + data->widx = 0; + data->ridx = 0; + data->err = 0; + data->msgs = &(msgs[i]); + data->addr_16bit = addr; + + /* Make sure we're in a good state to start */ + if ((msgs->flags & I2C_MSG_START) && (i2c_is_busy(dev) + || (i2c_get_line_levels(dev) != I2C_LINE_IDLE))) { + /* reset i2c port */ + i2c_reset(dev, I2C_RC_NO_IDLE_FOR_START); + } + if (msgs->flags & I2C_MSG_START) { + data->i2ccs = I2C_CH_NORMAL; + /* enable i2c interrupt */ + irq_enable(config->i2c_irq_base); + } + /* Start transaction */ + i2c_transaction(dev); + /* Wait for the transfer to complete */ + k_sem_take(&data->device_sync_sem, K_FOREVER); + } + + /* reset i2c channel status */ + if (data->err) { + data->i2ccs = I2C_CH_NORMAL; + } + + return data->err; +} + +static void i2c_it8xxx2_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + + /* If done doing work, wake up the task waiting for the transfer */ + if (!i2c_transaction(dev)) { + k_sem_give(&data->device_sync_sem); + irq_disable(config->i2c_irq_base); + } +} + +static int i2c_it8xxx2_init(const struct device *dev) +{ + struct i2c_it8xxx2_data *data = DEV_DATA(dev); + const struct i2c_it8xxx2_config *config = DEV_CFG(dev); + uint8_t *base = config->base; + uint32_t bitrate_cfg, offset = 0; + int error; + + k_sem_init(&data->device_sync_sem, 0, UINT_MAX); + + switch ((uint32_t)base) { + case DT_REG_ADDR(DT_NODELABEL(i2c0)): + offset = CGC_OFFSET_SMBA; + break; + case DT_REG_ADDR(DT_NODELABEL(i2c1)): + offset = CGC_OFFSET_SMBB; + break; + case DT_REG_ADDR(DT_NODELABEL(i2c2)): + offset = CGC_OFFSET_SMBC; + break; + case DT_REG_ADDR(DT_NODELABEL(i2c3)): + offset = CGC_OFFSET_SMBD; + /* Enable SMBus D channel */ + GCR2 |= SMB3E; + break; + case DT_REG_ADDR(DT_NODELABEL(i2c4)): + offset = CGC_OFFSET_SMBE; + /* Enable SMBus E channel */ + PMER1 |= 0x01; + break; + case DT_REG_ADDR(DT_NODELABEL(i2c5)): + offset = CGC_OFFSET_SMBF; + /* Enable SMBus F channel */ + PMER1 |= 0x02; + break; + } + + /* Enable I2C function. */ + volatile uint8_t *reg = (volatile uint8_t *) + (IT83XX_ECPM_BASE + (offset >> 8)); + uint8_t reg_mask = offset & 0xff; + *reg &= ~reg_mask; + + if (config->port < I2C_STANDARD_PORT_COUNT) { + /* + * 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. + */ + IT83XX_SMB_HOCTL2(base) = 0x11; + /* + * bit1, Kill SMBus host transaction. + * bit0, Enable the interrupt for the master interface. + */ + IT83XX_SMB_HOCTL(base) = 0x03; + IT83XX_SMB_HOCTL(base) = 0x01; + + /* W/C host status register */ + IT83XX_SMB_HOSTA(base) = HOSTA_ALL_WC_BIT; + IT83XX_SMB_HOCTL2(base) = 0x00; + + } else { + /* Software reset */ + IT83XX_I2C_DHTR(base) |= 0x80; + IT83XX_I2C_DHTR(base) &= 0x7F; + /* State reset and hardware reset */ + IT83XX_I2C_CTR(base) = E_STS_AND_HW_RST; + /* bit1, Module enable */ + IT83XX_I2C_CTR1(base) = 0; + } + + bitrate_cfg = i2c_map_dt_bitrate(config->bitrate); + error = i2c_it8xxx2_configure(dev, I2C_MODE_MASTER | bitrate_cfg); + + if (error) { + LOG_ERR("i2c: failure initializing"); + return error; + } + + return 0; +} + +static const struct i2c_driver_api i2c_it8xxx2_driver_api = { + .configure = i2c_it8xxx2_configure, + .transfer = i2c_it8xxx2_transfer, +}; + +#define I2C_ITE_IT8XXX2_INIT(idx) \ + static void i2c_it8xxx2_config_func_##idx(void); \ + \ + static const struct i2c_it8xxx2_config i2c_it8xxx2_cfg_##idx = { \ + .base = (uint8_t *)(DT_INST_REG_ADDR(idx)), \ + .irq_config_func = i2c_it8xxx2_config_func_##idx, \ + .bitrate = DT_INST_PROP(idx, clock_frequency), \ + .i2c_irq_base = DT_INST_IRQN(idx), \ + .port = DT_INST_PROP(idx, port_num), \ + }; \ + \ + static struct i2c_it8xxx2_data i2c_it8xxx2_data_##idx; \ + \ + DEVICE_DT_INST_DEFINE(idx, \ + &i2c_it8xxx2_init, &device_pm_control_nop, \ + &i2c_it8xxx2_data_##idx, \ + &i2c_it8xxx2_cfg_##idx, POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &i2c_it8xxx2_driver_api); \ + \ + static void i2c_it8xxx2_config_func_##idx(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(idx), \ + 0, \ + i2c_it8xxx2_isr, \ + DEVICE_DT_INST_GET(idx), 0); \ + } + +DT_INST_FOREACH_STATUS_OKAY(I2C_ITE_IT8XXX2_INIT) diff --git a/dts/bindings/i2c/ite,it8xxx2-i2c.yaml b/dts/bindings/i2c/ite,it8xxx2-i2c.yaml new file mode 100644 index 00000000000..17dc166dd39 --- /dev/null +++ b/dts/bindings/i2c/ite,it8xxx2-i2c.yaml @@ -0,0 +1,23 @@ +# Copyright (c) 2020 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +description: ITE it8xxx2 I2C + +compatible: "ite,it8xxx2-i2c" + +include: i2c-controller.yaml + +properties: + reg: + required: true + + label: + required: true + + interrupts: + required: true + + port-num: + type: int + required: true + description: Ordinal identifying the port diff --git a/dts/riscv/it8xxx2.dtsi b/dts/riscv/it8xxx2.dtsi index 4d599f2502a..5a08dec892d 100644 --- a/dts/riscv/it8xxx2.dtsi +++ b/dts/riscv/it8xxx2.dtsi @@ -7,6 +7,7 @@ #include #include +#include / { #address-cells = <1>; @@ -155,5 +156,71 @@ interrupt-parent = <&intc>; status = "okay"; }; + i2c0: i2c@f01c40 { + compatible = "ite,it8xxx2-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x00f01c40 0x0040>; + interrupts = <9 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&intc>; + status = "disabled"; + label = "I2C_0"; + port-num = <0>; + }; + i2c1: i2c@f01c80 { + compatible = "ite,it8xxx2-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x00f01c80 0x0040>; + interrupts = <10 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&intc>; + status = "disabled"; + label = "I2C_1"; + port-num = <1>; + }; + i2c2: i2c@f01cc0 { + compatible = "ite,it8xxx2-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x00f01cc0 0x0040>; + interrupts = <16 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&intc>; + status = "disabled"; + label = "I2C_2"; + port-num = <2>; + }; + i2c3: i2c@f03680 { + compatible = "ite,it8xxx2-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x00f03680 0x0080>; + interrupts = <4 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&intc>; + status = "disabled"; + label = "I2C_3"; + port-num = <3>; + }; + i2c4: i2c@f03500 { + compatible = "ite,it8xxx2-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x00f03500 0x0080>; + interrupts = <152 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&intc>; + status = "disabled"; + label = "I2C_4"; + port-num = <4>; + }; + i2c5: i2c@f03580 { + compatible = "ite,it8xxx2-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x00f03580 0x0080>; + interrupts = <153 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&intc>; + status = "disabled"; + label = "I2C_5"; + port-num = <5>; + }; }; }; diff --git a/soc/riscv/riscv-ite/common/chip_chipregs.h b/soc/riscv/riscv-ite/common/chip_chipregs.h index 4c699845d8f..0172ad045b8 100644 --- a/soc/riscv/riscv-ite/common/chip_chipregs.h +++ b/soc/riscv/riscv-ite/common/chip_chipregs.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 ITE Corporation. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -1209,6 +1209,11 @@ #define HOSTA_DVER BIT(2) #define HOSTA_FINTR BIT(1) #define HOSTA_HOBY BIT(0) +#define HOSTA_ANY_ERROR (HOSTA_DVER | HOSTA_BSER | \ + HOSTA_FAIL | HOSTA_NACK | HOSTA_TMOE) +#define HOSTA_NEXT_BYTE HOSTA_BDS +#define HOSTA_ALL_WC_BIT (HOSTA_FINTR | \ + HOSTA_ANY_ERROR | HOSTA_BDS) #define HOCTL_A ECREG(EC_REG_BASE_ADDR + 0x1C41) #define HOCTL_B ECREG(EC_REG_BASE_ADDR + 0x1C81) @@ -1936,4 +1941,81 @@ #define CE_CTRL_1ST ECREG(EC_REG_BASE_ADDR + 0x3C00) #define CE_RNG ECREG(EC_REG_BASE_ADDR + 0x3C20) + + +/* + * Clock and Power Management (ECPM) + */ +#define IT83XX_ECPM_BASE 0x00F01E00 + +#define IT83XX_ECPM_CGCTRL4R_OFF 0x09 + +#define CGC_OFFSET_SMBF ((IT83XX_ECPM_CGCTRL4R_OFF << 8) | 0x80) +#define CGC_OFFSET_SMBE ((IT83XX_ECPM_CGCTRL4R_OFF << 8) | 0x40) +#define CGC_OFFSET_SMBD ((IT83XX_ECPM_CGCTRL4R_OFF << 8) | 0x20) +#define CGC_OFFSET_SMBC ((IT83XX_ECPM_CGCTRL4R_OFF << 8) | 0x10) +#define CGC_OFFSET_SMBB ((IT83XX_ECPM_CGCTRL4R_OFF << 8) | 0x08) +#define CGC_OFFSET_SMBA ((IT83XX_ECPM_CGCTRL4R_OFF << 8) | 0x04) + +/* + * The count number of the counter for 25 ms register. + * The 25 ms register is calculated by (count number *1.024 kHz). + */ + +#define I2C_CLK_LOW_TIMEOUT 255 /* ~=249 ms */ + +/* SMBus/I2C Interface (SMB/I2C) */ +#define IT83XX_SMB_BASE 0x00F01C00 +#define IT83XX_SMB_4P7USL ECREG(IT83XX_SMB_BASE+0x00) +#define IT83XX_SMB_4P0USL ECREG(IT83XX_SMB_BASE+0x01) +#define IT83XX_SMB_300NS ECREG(IT83XX_SMB_BASE+0x02) +#define IT83XX_SMB_250NS ECREG(IT83XX_SMB_BASE+0x03) +#define IT83XX_SMB_25MS ECREG(IT83XX_SMB_BASE+0x04) +#define IT83XX_SMB_45P3USL ECREG(IT83XX_SMB_BASE+0x05) +#define IT83XX_SMB_45P3USH ECREG(IT83XX_SMB_BASE+0x06) +#define IT83XX_SMB_4P7A4P0H ECREG(IT83XX_SMB_BASE+0x07) +#define IT83XX_SMB_SLVISELR ECREG(IT83XX_SMB_BASE+0x08) +#define IT83XX_SMB_SCLKTS(ch) ECREG(IT83XX_SMB_BASE+0x09+ch) +#define IT83XX_SMB_CHSEF ECREG(IT83XX_SMB_BASE+0x11) +#define IT83XX_SMB_CHSAB ECREG(IT83XX_SMB_BASE+0x20) +#define IT83XX_SMB_CHSCD ECREG(IT83XX_SMB_BASE+0x21) +#define IT83XX_SMB_HOSTA(base) ECREG(base+0x00) +#define IT83XX_SMB_HOCTL(base) ECREG(base+0x01) +#define IT83XX_SMB_HOCMD(base) ECREG(base+0x02) +#define IT83XX_SMB_TRASLA(base) ECREG(base+0x03) +#define IT83XX_SMB_D0REG(base) ECREG(base+0x04) +#define IT83XX_SMB_D1REG(base) ECREG(base+0x05) +#define IT83XX_SMB_HOBDB(base) ECREG(base+0x06) +#define IT83XX_SMB_PECERC(base) ECREG(base+0x07) +#define IT83XX_SMB_SMBPCTL(base) ECREG(base+0x0A) +#define IT83XX_SMB_HOCTL2(base) ECREG(base+0x10) + +/** + * Enhanced SMBus/I2C Interface + * Ch_D: 0x00F03680, Ch_E: 0x00F03500, Ch_F: 0x00F03580 + * Ch_D: ch = 0x03, Ch_E: ch = 0x00, Ch_F: ch = 0x01 + */ +#define IT83XX_I2C_DRR(base) ECREG(base+0x00) +#define IT83XX_I2C_PSR(base) ECREG(base+0x01) +#define IT83XX_I2C_HSPR(base) ECREG(base+0x02) +#define IT83XX_I2C_STR(base) ECREG(base+0x03) +#define IT83XX_I2C_DHTR(base) ECREG(base+0x04) +#define IT83XX_I2C_TOR(base) ECREG(base+0x05) +#define IT83XX_I2C_DTR(base) ECREG(base+0x08) +#define IT83XX_I2C_CTR(base) ECREG(base+0x09) +#define IT83XX_I2C_CTR1(base) ECREG(base+0x0A) +#define IT83XX_I2C_BYTE_CNT_L(base) ECREG(base+0x0C) +#define IT83XX_I2C_IRQ_ST(base) ECREG(base+0x0D) +#define IT83XX_I2C_IDR(base) ECREG(base+0x06) +#define IT83XX_I2C_TOS(base) ECREG(base+0x07) +#define IT83XX_I2C_IDR2(base) ECREG(base+0x1F) +#define IT83XX_I2C_RAMHA(base) ECREG(base+0x23) +#define IT83XX_I2C_RAMLA(base) ECREG(base+0x24) +#define IT83XX_I2C_RAMHA2(base) ECREG(base+0x2B) +#define IT83XX_I2C_RAMLA2(base) ECREG(base+0x2C) +#define IT83XX_I2C_CMD_ADDH(base) ECREG(base+0x25) +#define IT83XX_I2C_CMD_ADDL(base) ECREG(base+0x26) +#define IT83XX_I2C_RAMH2A(base) ECREG(base+0x50) +#define IT83XX_I2C_CMD_ADDH2(base) ECREG(base+0x52) + #endif /* CHIP_CHIPREGS_H */