blob: b9d6ca58f26f52e3deb090e4c81851355ebeae5c [file] [log] [blame]
/*
* Copyright (c) 2020 ITE Corporation. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ite_it8xxx2_i2c
#include <drivers/i2c.h>
#include <errno.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(i2c_ite_it8xxx2);
#include "i2c-priv.h"
#include <soc.h>
#include <sys/util.h>
#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 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, 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, 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, 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, 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 << 1) | 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 << 1;
/* 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);
int res;
/* 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;
}
/* Make sure we're in a good state to start */
if (i2c_bus_not_available(dev)) {
/* reset i2c port */
i2c_reset(dev, I2C_RC_NO_IDLE_FOR_START);
/*
* After resetting I2C bus, if I2C bus is not available
* (No external pull-up), drop the transaction.
*/
if (i2c_bus_not_available(dev)) {
return -EIO;
}
}
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;
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 */
/* TODO: the timeout should be adjustable */
res = k_sem_take(&data->device_sync_sem, K_MSEC(100));
if (res != 0) {
data->err = ETIMEDOUT;
/* reset i2c port */
i2c_reset(dev, I2C_RC_TIMEOUT);
/* If this message is sent fail, drop the transaction. */
break;
}
}
/* 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, K_SEM_MAX_LIMIT);
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, NULL, \
&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)