summaryrefslogtreecommitdiff
path: root/chip/it83xx/i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/it83xx/i2c.c')
-rw-r--r--chip/it83xx/i2c.c946
1 files changed, 0 insertions, 946 deletions
diff --git a/chip/it83xx/i2c.c b/chip/it83xx/i2c.c
deleted file mode 100644
index 5aa8f8a460..0000000000
--- a/chip/it83xx/i2c.c
+++ /dev/null
@@ -1,946 +0,0 @@
-/* Copyright 2015 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/* I2C module for Chrome EC */
-
-#include "clock.h"
-#include "common.h"
-#include "console.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "i2c.h"
-#include "registers.h"
-#include "task.h"
-#include "timer.h"
-#include "util.h"
-
-#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)
-
-/* Default maximum time we allow for an I2C transfer */
-#define I2C_TIMEOUT_DEFAULT_US (100 * MSEC)
-
-enum enhanced_i2c_transfer_direct {
- TX_DIRECT,
- RX_DIRECT,
-};
-
-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 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 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 peripheral 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 i2c_reset_cause {
- I2C_RC_NO_IDLE_FOR_START = 1,
- I2C_RC_TIMEOUT,
-};
-
-struct i2c_ch_freq {
- int kbps;
- uint8_t freq_set;
-};
-
-static const struct i2c_ch_freq i2c_freq_select[] = {
- { 50, 1},
- { 100, 2},
- { 400, 3},
- { 1000, 4},
-};
-
-struct i2c_pin {
- volatile uint8_t *pin_clk;
- volatile uint8_t *pin_data;
- volatile uint8_t *pin_clk_ctrl;
- volatile uint8_t *pin_data_ctrl;
- 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[] = {
- { &IT83XX_GPIO_GPCRB3, &IT83XX_GPIO_GPCRB4,
- &IT83XX_GPIO_GPDRB, &IT83XX_GPIO_GPDRB,
- &IT83XX_GPIO_GPDMRB, &IT83XX_GPIO_GPDMRB,
- 0x08, 0x10},
- { &IT83XX_GPIO_GPCRC1, &IT83XX_GPIO_GPCRC2,
- &IT83XX_GPIO_GPDRC, &IT83XX_GPIO_GPDRC,
- &IT83XX_GPIO_GPDMRC, &IT83XX_GPIO_GPDMRC,
- 0x02, 0x04},
-#ifdef CONFIG_IT83XX_SMCLK2_ON_GPC7
- { &IT83XX_GPIO_GPCRC7, &IT83XX_GPIO_GPCRF7,
- &IT83XX_GPIO_GPDRC, &IT83XX_GPIO_GPDRF,
- &IT83XX_GPIO_GPDMRC, &IT83XX_GPIO_GPDMRF,
- 0x80, 0x80},
-#else
- { &IT83XX_GPIO_GPCRF6, &IT83XX_GPIO_GPCRF7,
- &IT83XX_GPIO_GPDRF, &IT83XX_GPIO_GPDRF,
- &IT83XX_GPIO_GPDMRF, &IT83XX_GPIO_GPDMRF,
- 0x40, 0x80},
-#endif
- { &IT83XX_GPIO_GPCRH1, &IT83XX_GPIO_GPCRH2,
- &IT83XX_GPIO_GPDRH, &IT83XX_GPIO_GPDRH,
- &IT83XX_GPIO_GPDMRH, &IT83XX_GPIO_GPDMRH,
- 0x02, 0x04},
- { &IT83XX_GPIO_GPCRE0, &IT83XX_GPIO_GPCRE7,
- &IT83XX_GPIO_GPDRE, &IT83XX_GPIO_GPDRE,
- &IT83XX_GPIO_GPDMRE, &IT83XX_GPIO_GPDMRE,
- 0x01, 0x80},
- { &IT83XX_GPIO_GPCRA4, &IT83XX_GPIO_GPCRA5,
- &IT83XX_GPIO_GPDRA, &IT83XX_GPIO_GPDRA,
- &IT83XX_GPIO_GPDMRA, &IT83XX_GPIO_GPDMRA,
- 0x10, 0x20},
-};
-
-struct i2c_ctrl_t {
- uint8_t irq;
- enum clock_gate_offsets clock_gate;
- int reg_shift;
-};
-
-const struct i2c_ctrl_t i2c_ctrl_regs[] = {
- {IT83XX_IRQ_SMB_A, CGC_OFFSET_SMBA, -1},
- {IT83XX_IRQ_SMB_B, CGC_OFFSET_SMBB, -1},
- {IT83XX_IRQ_SMB_C, CGC_OFFSET_SMBC, -1},
- {IT83XX_IRQ_SMB_D, CGC_OFFSET_SMBD, 3},
- {IT83XX_IRQ_SMB_E, CGC_OFFSET_SMBE, 0},
- {IT83XX_IRQ_SMB_F, CGC_OFFSET_SMBF, 1},
-};
-
-enum i2c_ch_status {
- I2C_CH_NORMAL = 0,
- I2C_CH_REPEAT_START,
- I2C_CH_WAIT_READ,
- I2C_CH_WAIT_NEXT_XFER,
-};
-
-/* I2C port state data */
-struct i2c_port_data {
- const uint8_t *out; /* Output data pointer */
- int out_size; /* Output data to transfer, in bytes */
- uint8_t *in; /* Input data pointer */
- int in_size; /* Input data to transfer, in bytes */
- int flags; /* Flags (I2C_XFER_*) */
- int widx; /* Index into output data */
- int ridx; /* Index into input data */
- int err; /* Error code, if any */
- uint8_t addr_8bit; /* address of device */
- uint32_t timeout_us; /* Transaction timeout, or 0 to use default */
- uint8_t freq; /* Frequency setting */
-
- enum i2c_ch_status i2ccs;
- /* Task waiting on port, or TASK_ID_INVALID if none. */
- volatile int task_waiting;
-};
-static struct i2c_port_data pdata[I2C_PORT_COUNT];
-
-static int i2c_ch_reg_shift(int p)
-{
- /*
- * only enhanced port needs to be changed the parameter of registers
- */
- ASSERT(p >= I2C_STANDARD_PORT_COUNT && p < I2C_PORT_COUNT);
-
- /*
- * The registers of i2c enhanced ports are not sequential.
- * This routine transfers the i2c port number to related
- * parameter of registers.
- *
- * IT83xx chip : i2c enhanced ports - channel D,E,F
- * channel D registers : 0x3680 ~ 0x36FF
- * channel E registers : 0x3500 ~ 0x357F
- * channel F registers : 0x3580 ~ 0x35FF
- */
- return i2c_ctrl_regs[p].reg_shift;
-}
-
-static void i2c_reset(int p, int cause)
-{
- int p_ch;
-
- if (p < I2C_STANDARD_PORT_COUNT) {
- /* bit1, kill current transaction. */
- IT83XX_SMB_HOCTL(p) = 0x2;
- IT83XX_SMB_HOCTL(p) = 0;
- /* W/C host status register */
- IT83XX_SMB_HOSTA(p) = HOSTA_ALL_WC_BIT;
- } else {
- /* Shift register */
- p_ch = i2c_ch_reg_shift(p);
- /* State reset and hardware reset */
- IT83XX_I2C_CTR(p_ch) = E_STS_AND_HW_RST;
- }
- CPRINTS("I2C ch%d reset cause %d", p, cause);
-}
-
-static void i2c_r_last_byte(int p)
-{
- struct i2c_port_data *pd = pdata + p;
-
- /*
- * bit5, The firmware shall write 1 to this bit
- * when the next byte will be the last byte for i2c read.
- */
- if ((pd->flags & I2C_XFER_STOP) && (pd->ridx == pd->in_size - 1))
- IT83XX_SMB_HOCTL(p) |= 0x20;
-}
-
-static void i2c_w2r_change_direction(int p)
-{
- /* I2C switch direction */
- if (IT83XX_SMB_HOCTL2(p) & 0x08) {
- i2c_r_last_byte(p);
- IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE;
- } else {
- /*
- * bit2, I2C switch direction wait.
- * bit3, I2C switch direction enable.
- */
- IT83XX_SMB_HOCTL2(p) |= 0x0C;
- IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE;
- i2c_r_last_byte(p);
- IT83XX_SMB_HOCTL2(p) &= ~0x04;
- }
-}
-
-static void i2c_pio_trans_data(int p, enum enhanced_i2c_transfer_direct direct,
- uint8_t data, int first_byte)
-{
- struct i2c_port_data *pd = pdata + p;
- int p_ch;
- int nack = 0;
-
- /* Shift register */
- p_ch = i2c_ch_reg_shift(p);
-
- if (first_byte) {
- /* First byte must be peripheral address. */
- IT83XX_I2C_DTR(p_ch) =
- data | (direct == RX_DIRECT ? BIT(0) : 0);
- /* start or repeat start signal. */
- IT83XX_I2C_CTR(p_ch) = E_START_ID;
- } else {
- if (direct == TX_DIRECT)
- /* Transmit data */
- IT83XX_I2C_DTR(p_ch) = data;
- else {
- /*
- * Receive data.
- * Last byte should be NACK in the end of read cycle
- */
- if (((pd->ridx + 1) == pd->in_size) &&
- (pd->flags & I2C_XFER_STOP))
- nack = 1;
- }
- /* Set hardware reset to start next transmission */
- IT83XX_I2C_CTR(p_ch) =
- E_INT_EN | E_MODE_SEL | E_HW_RST | (nack ? 0 : E_ACK);
- }
-}
-
-static int i2c_tran_write(int p)
-{
- struct i2c_port_data *pd = pdata + p;
-
- if (pd->flags & I2C_XFER_START) {
- /* i2c enable */
- IT83XX_SMB_HOCTL2(p) = 0x13;
- /*
- * bit0, Direction of the host transfer.
- * bit[1:7}, Address of the targeted peripheral.
- */
- IT83XX_SMB_TRASLA(p) = pd->addr_8bit;
- /* Send first byte */
- IT83XX_SMB_HOBDB(p) = *(pd->out++);
- pd->widx++;
- /* clear start flag */
- pd->flags &= ~I2C_XFER_START;
- /*
- * bit0, Host interrupt enable.
- * bit[2:4}, Extend command.
- * bit6, start.
- */
- IT83XX_SMB_HOCTL(p) = 0x5D;
- } else {
- /* Host has completed the transmission of a byte */
- if (IT83XX_SMB_HOSTA(p) & HOSTA_BDS) {
- if (pd->widx < pd->out_size) {
- /* Send next byte */
- IT83XX_SMB_HOBDB(p) = *(pd->out++);
- pd->widx++;
- /* W/C byte done for next byte */
- IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE;
- if (pd->i2ccs == I2C_CH_REPEAT_START) {
- pd->i2ccs = I2C_CH_NORMAL;
- task_enable_irq(i2c_ctrl_regs[p].irq);
- }
- } else {
- /* done */
- pd->out_size = 0;
- if (pd->in_size > 0) {
- /* write to read */
- i2c_w2r_change_direction(p);
- } else {
- if (pd->flags & I2C_XFER_STOP) {
- /* set I2C_EN = 0 */
- IT83XX_SMB_HOCTL2(p) = 0x11;
- /* W/C byte done for finish */
- IT83XX_SMB_HOSTA(p) =
- HOSTA_NEXT_BYTE;
- } else {
- pd->i2ccs = I2C_CH_REPEAT_START;
- return 0;
- }
- }
- }
- }
- }
- return 1;
-}
-
-static int i2c_tran_read(int p)
-{
- struct i2c_port_data *pd = pdata + p;
-
- if (pd->flags & I2C_XFER_START) {
- /* i2c enable */
- IT83XX_SMB_HOCTL2(p) = 0x13;
- /*
- * bit0, Direction of the host transfer.
- * bit[1:7}, Address of the targeted peripheral.
- */
- IT83XX_SMB_TRASLA(p) = pd->addr_8bit | 0x01;
- /* clear start flag */
- pd->flags &= ~I2C_XFER_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 ((1 == pd->in_size) && (pd->flags & I2C_XFER_STOP))
- IT83XX_SMB_HOCTL(p) = 0x7D;
- else
- IT83XX_SMB_HOCTL(p) = 0x5D;
- } else {
- if ((pd->i2ccs == I2C_CH_REPEAT_START) ||
- (pd->i2ccs == I2C_CH_WAIT_READ)) {
- if (pd->i2ccs == I2C_CH_REPEAT_START) {
- /* write to read */
- i2c_w2r_change_direction(p);
- } else {
- /* For last byte */
- i2c_r_last_byte(p);
- /* W/C for next byte */
- IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE;
- }
- pd->i2ccs = I2C_CH_NORMAL;
- task_enable_irq(i2c_ctrl_regs[p].irq);
- } else if (IT83XX_SMB_HOSTA(p) & HOSTA_BDS) {
- if (pd->ridx < pd->in_size) {
- /* To get received data. */
- *(pd->in++) = IT83XX_SMB_HOBDB(p);
- pd->ridx++;
- /* For last byte */
- i2c_r_last_byte(p);
- /* done */
- if (pd->ridx == pd->in_size) {
- pd->in_size = 0;
- if (pd->flags & I2C_XFER_STOP) {
- /* W/C for finish */
- IT83XX_SMB_HOSTA(p) =
- HOSTA_NEXT_BYTE;
- } else {
- pd->i2ccs = I2C_CH_WAIT_READ;
- return 0;
- }
- } else {
- /* W/C for next byte */
- IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE;
- }
- }
- }
- }
- return 1;
-}
-
-static void enhanced_i2c_start(int p)
-{
- /* Shift register */
- int p_ch = i2c_ch_reg_shift(p);
-
- /* State reset and hardware reset */
- IT83XX_I2C_CTR(p_ch) = E_STS_AND_HW_RST;
- /* Set i2c frequency */
- IT83XX_I2C_PSR(p_ch) = pdata[p].freq;
- IT83XX_I2C_HSPR(p_ch) = pdata[p].freq;
- /*
- * Set time out register.
- * I2C D/E/F clock/data low timeout.
- */
- IT83XX_I2C_TOR(p_ch) = I2C_CLK_LOW_TIMEOUT;
- /* bit1: Enable enhanced i2c module */
- IT83XX_I2C_CTR1(p_ch) = BIT(1);
-}
-
-static int enhanced_i2c_tran_write(int p)
-{
- struct i2c_port_data *pd = pdata + p;
- uint8_t out_data;
- int p_ch;
-
- /* Shift register */
- p_ch = i2c_ch_reg_shift(p);
-
- if (pd->flags & I2C_XFER_START) {
- /* Clear start bit */
- pd->flags &= ~I2C_XFER_START;
- enhanced_i2c_start(p);
- /* Send ID */
- i2c_pio_trans_data(p, TX_DIRECT, pd->addr_8bit, 1);
- } else {
- /* Host has completed the transmission of a byte */
- if (pd->widx < pd->out_size) {
- out_data = *(pd->out++);
- pd->widx++;
-
- /* Send Byte */
- i2c_pio_trans_data(p, TX_DIRECT, out_data, 0);
- if (pd->i2ccs == I2C_CH_WAIT_NEXT_XFER) {
- pd->i2ccs = I2C_CH_NORMAL;
- task_enable_irq(i2c_ctrl_regs[p].irq);
- }
- } else {
- /* done */
- pd->out_size = 0;
- if (pd->in_size > 0) {
- /* Write to read protocol */
- pd->i2ccs = I2C_CH_REPEAT_START;
- /* Repeat Start */
- i2c_pio_trans_data(p, RX_DIRECT,
- pd->addr_8bit, 1);
- } else {
- if (pd->flags & I2C_XFER_STOP) {
- IT83XX_I2C_CTR(p_ch) = E_FINISH;
- /* wait for stop bit interrupt*/
- return 1;
- }
- /* Direct write with direct read */
- pd->i2ccs = I2C_CH_WAIT_NEXT_XFER;
- return 0;
- }
- }
- }
- return 1;
-}
-
-static int enhanced_i2c_tran_read(int p)
-{
- struct i2c_port_data *pd = pdata + p;
- uint8_t in_data = 0;
- int p_ch;
-
- /* Shift register */
- p_ch = i2c_ch_reg_shift(p);
-
- if (pd->flags & I2C_XFER_START) {
- /* clear start flag */
- pd->flags &= ~I2C_XFER_START;
- enhanced_i2c_start(p);
- /* Direct read */
- pd->i2ccs = I2C_CH_WAIT_READ;
- /* Send ID */
- i2c_pio_trans_data(p, RX_DIRECT, pd->addr_8bit, 1);
- } else {
- if (pd->i2ccs) {
- if (pd->i2ccs == I2C_CH_REPEAT_START) {
- pd->i2ccs = I2C_CH_NORMAL;
- /* Receive data */
- i2c_pio_trans_data(p, RX_DIRECT, in_data, 0);
- } else if (pd->i2ccs == I2C_CH_WAIT_READ) {
- pd->i2ccs = I2C_CH_NORMAL;
- /* Receive data */
- i2c_pio_trans_data(p, RX_DIRECT, in_data, 0);
- /* Turn on irq before next direct read */
- task_enable_irq(i2c_ctrl_regs[p].irq);
- } else {
- /* Write to read */
- pd->i2ccs = I2C_CH_WAIT_READ;
- /* Send ID */
- i2c_pio_trans_data(p, RX_DIRECT,
- pd->addr_8bit, 1);
- task_enable_irq(i2c_ctrl_regs[p].irq);
- }
- } else {
- if (pd->ridx < pd->in_size) {
- /* read data */
- *(pd->in++) = IT83XX_I2C_DRR(p_ch);
- pd->ridx++;
-
- /* done */
- if (pd->ridx == pd->in_size) {
- pd->in_size = 0;
- if (pd->flags & I2C_XFER_STOP) {
- pd->i2ccs = I2C_CH_NORMAL;
- IT83XX_I2C_CTR(p_ch) = E_FINISH;
- /* wait for stop bit interrupt*/
- return 1;
- }
- /* End the transaction */
- pd->i2ccs = I2C_CH_WAIT_READ;
- return 0;
- }
- /* read next byte */
- i2c_pio_trans_data(p, RX_DIRECT, in_data, 0);
- }
- }
- }
- return 1;
-}
-
-static int enhanced_i2c_error(int p)
-{
- struct i2c_port_data *pd = pdata + p;
- /* Shift register */
- int p_ch = i2c_ch_reg_shift(p);
- int i2c_str = IT83XX_I2C_STR(p_ch);
-
- if (i2c_str & E_HOSTA_ANY_ERROR) {
- pd->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(p_ch) & E_ACK)
- pd->err = E_HOSTA_ACK;
- }
-
- return pd->err;
-}
-
-static int i2c_transaction(int p)
-{
- struct i2c_port_data *pd = pdata + p;
- int p_ch;
-
- if (p < I2C_STANDARD_PORT_COUNT) {
- /* any error */
- if (IT83XX_SMB_HOSTA(p) & HOSTA_ANY_ERROR) {
- pd->err = (IT83XX_SMB_HOSTA(p) & HOSTA_ANY_ERROR);
- } else {
- /* i2c write */
- if (pd->out_size)
- return i2c_tran_write(p);
- /* i2c read */
- else if (pd->in_size)
- return i2c_tran_read(p);
- /* wait finish */
- if (!(IT83XX_SMB_HOSTA(p) & HOSTA_FINTR))
- return 1;
- }
- /* W/C */
- IT83XX_SMB_HOSTA(p) = HOSTA_ALL_WC_BIT;
- /* disable the SMBus host interface */
- IT83XX_SMB_HOCTL2(p) = 0x00;
- } else {
- /* no error */
- if (!(enhanced_i2c_error(p))) {
- /* i2c write */
- if (pd->out_size)
- return enhanced_i2c_tran_write(p);
- /* i2c read */
- else if (pd->in_size)
- return enhanced_i2c_tran_read(p);
- }
- p_ch = i2c_ch_reg_shift(p);
- IT83XX_I2C_CTR(p_ch) = E_STS_AND_HW_RST;
- IT83XX_I2C_CTR1(p_ch) = 0;
- }
- /* done doing work */
- return 0;
-}
-
-int i2c_is_busy(int port)
-{
- int p_ch;
-
- if (port < I2C_STANDARD_PORT_COUNT)
- return (IT83XX_SMB_HOSTA(port) &
- (HOSTA_HOBY | HOSTA_ALL_WC_BIT));
-
- p_ch = i2c_ch_reg_shift(port);
- return (IT83XX_I2C_STR(p_ch) & E_HOSTA_BB);
-}
-
-int chip_i2c_xfer(int port, uint16_t addr_flags,
- const uint8_t *out, int out_size,
- uint8_t *in, int in_size, int flags)
-{
- struct i2c_port_data *pd = pdata + port;
- uint32_t events = 0;
-
- if (out_size == 0 && in_size == 0)
- return EC_SUCCESS;
-
- /*
- * Make the below i2c transaction work:
- * - i2c_xfer with I2C_XFER_START flag
- * - i2c_xfer with I2C_XFER_START flag
- * - xxx
- * - i2c_xfer with I2C_XFER_STOP flag
- */
- if (pd->i2ccs)
- flags &= ~I2C_XFER_START;
-
- /* Copy data to port struct */
- pd->out = out;
- pd->out_size = out_size;
- pd->in = in;
- pd->in_size = in_size;
- pd->flags = flags;
- pd->widx = 0;
- pd->ridx = 0;
- pd->err = 0;
- pd->addr_8bit = I2C_STRIP_FLAGS(addr_flags) << 1;
-
- /* Make sure we're in a good state to start */
- if ((flags & I2C_XFER_START) && (i2c_is_busy(port)
- || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) {
-
- /* Attempt to unwedge the port. */
- pd->err = i2c_unwedge(port);
-
- /* reset i2c port */
- i2c_reset(port, I2C_RC_NO_IDLE_FOR_START);
-
- /* Return if port is still wedged */
- if (pd->err)
- return pd->err;
- }
-
- pd->task_waiting = task_get_current();
- if (pd->flags & I2C_XFER_START) {
- pd->i2ccs = I2C_CH_NORMAL;
- /* enable i2c interrupt */
- task_clear_pending_irq(i2c_ctrl_regs[port].irq);
- task_enable_irq(i2c_ctrl_regs[port].irq);
- }
- /* Start transaction */
- i2c_transaction(port);
- /* Wait for transfer complete or timeout */
- events = task_wait_event_mask(TASK_EVENT_I2C_IDLE, pd->timeout_us);
- /* disable i2c interrupt */
- task_disable_irq(i2c_ctrl_regs[port].irq);
- pd->task_waiting = TASK_ID_INVALID;
-
- /* Handle timeout */
- if (!(events & TASK_EVENT_I2C_IDLE)) {
- pd->err = EC_ERROR_TIMEOUT;
- /* reset i2c port */
- i2c_reset(port, I2C_RC_TIMEOUT);
- }
-
- /* reset i2c channel status */
- if (pd->err)
- pd->i2ccs = I2C_CH_NORMAL;
-
- return pd->err;
-}
-
-int i2c_raw_get_scl(int port)
-{
- enum gpio_signal g;
-
- if (get_scl_from_i2c_port(port, &g) == EC_SUCCESS)
- return !!(*i2c_pin_regs[port].mirror_clk &
- i2c_pin_regs[port].clk_mask);
-
- /* If no SCL pin defined for this port, then return 1 to appear idle */
- return 1;
-}
-
-int i2c_raw_get_sda(int port)
-{
- enum gpio_signal g;
-
- if (get_sda_from_i2c_port(port, &g) == EC_SUCCESS)
- return !!(*i2c_pin_regs[port].mirror_data &
- i2c_pin_regs[port].data_mask);
-
- /* If no SDA pin defined for this port, then return 1 to appear idle */
- return 1;
-}
-
-int i2c_get_line_levels(int port)
-{
- int pin_sts = 0;
-
- if (port < I2C_STANDARD_PORT_COUNT)
- return IT83XX_SMB_SMBPCTL(port) & 0x03;
-
- if (*i2c_pin_regs[port].mirror_clk & i2c_pin_regs[port].clk_mask)
- pin_sts |= I2C_LINE_SCL_HIGH;
- if (*i2c_pin_regs[port].mirror_data & i2c_pin_regs[port].data_mask)
- pin_sts |= I2C_LINE_SDA_HIGH;
-
- return pin_sts;
-}
-
-void i2c_set_timeout(int port, uint32_t timeout)
-{
- pdata[port].timeout_us = timeout ? timeout : I2C_TIMEOUT_DEFAULT_US;
-}
-
-void i2c_interrupt(int port)
-{
- int id = pdata[port].task_waiting;
-
- /* Clear the interrupt status */
- task_clear_pending_irq(i2c_ctrl_regs[port].irq);
-
- /* If no task is waiting, just return */
- if (id == TASK_ID_INVALID)
- return;
-
- /* If done doing work, wake up the task waiting for the transfer */
- if (!i2c_transaction(port)) {
- task_disable_irq(i2c_ctrl_regs[port].irq);
- task_set_event(id, TASK_EVENT_I2C_IDLE);
- }
-}
-
-/*
- * 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(int 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(int port, int freq_khz)
-{
- /*
- * 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(port);
- } else {
- for (int f = ARRAY_SIZE(i2c_freq_select) - 1; f >= 0; f--) {
- if (freq_khz >= i2c_freq_select[f].kbps) {
- IT83XX_SMB_SCLKTS(port) =
- i2c_freq_select[f].freq_set;
- break;
- }
- }
- }
-
- /* 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(int port, int freq_khz)
-{
- int port_reg_shift, clk_div, psr;
-
- /* Get base address of i2c enhanced port's registers. */
- port_reg_shift = i2c_ch_reg_shift(port);
- /*
- * Let psr(Prescale) = IT83XX_I2C_PSR(port_reg_shift)
- * 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 = (IT83XX_ECPM_SCDCR2 & 0x0F) + 1;
- /* Calculate PSR value */
- psr = (PLL_CLOCK / (clk_div * (2 * 1000 * freq_khz))) - 2;
- /* Set psr value under 0xFD */
- if (psr > 0xFD)
- psr = 0xFD;
-
- /* Set I2C Speed */
- IT83XX_I2C_PSR(port_reg_shift) = (psr & 0xFF);
- IT83XX_I2C_HSPR(port_reg_shift) = (psr & 0xFF);
- /* Backup */
- pdata[port].freq = (psr & 0xFF);
- }
-}
-
-static void i2c_freq_changed(void)
-{
- int i, freq, port;
-
- /* Set clock frequency for I2C ports */
- for (i = 0; i < i2c_ports_used; i++) {
- freq = i2c_ports[i].kbps;
- port = i2c_ports[i].port;
- if (port < I2C_STANDARD_PORT_COUNT)
- i2c_standard_port_set_frequency(port, freq);
- else
- i2c_enhanced_port_set_frequency(port, freq);
- }
-}
-DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_changed, HOOK_PRIO_DEFAULT);
-
-void i2c_init(void)
-{
- int i, p, p_ch;
-
- /* Configure GPIOs */
- gpio_config_module(MODULE_I2C, 1);
-
-#ifdef CONFIG_IT83XX_SMCLK2_ON_GPC7
- /* bit7, 0: SMCLK2 is located on GPF6, 1: SMCLK2 is located on GPC7 */
- IT83XX_GPIO_GRC7 |= 0x80;
-#endif
-
- /* Enable I2C function. */
- for (i = 0; i < i2c_ports_used; i++) {
- /* I2c port mapping. */
- p = i2c_ports[i].port;
-
- clock_enable_peripheral(i2c_ctrl_regs[p].clock_gate, 0, 0);
-
- if (p < 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(p) = 0x11;
- /*
- * bit1, Kill SMBus host transaction.
- * bit0, Enable the interrupt for the master interface.
- */
- IT83XX_SMB_HOCTL(p) = 0x03;
- IT83XX_SMB_HOCTL(p) = 0x01;
- /* W/C host status register */
- IT83XX_SMB_HOSTA(p) = HOSTA_ALL_WC_BIT;
- IT83XX_SMB_HOCTL2(p) = 0x00;
- } else {
- /* Shift register */
- p_ch = i2c_ch_reg_shift(p);
- switch (p) {
- case IT83XX_I2C_CH_D:
- #ifndef CONFIG_UART_HOST
- /* Enable SMBus D channel */
- IT83XX_GPIO_GRC2 |= 0x20;
- #endif
- break;
- case IT83XX_I2C_CH_E:
- /* Enable SMBus E channel */
- IT83XX_GCTRL_PMER1 |= 0x01;
- break;
- case IT83XX_I2C_CH_F:
- /* Enable SMBus F channel */
- IT83XX_GCTRL_PMER1 |= 0x02;
- break;
- }
- /* Software reset */
- IT83XX_I2C_DHTR(p_ch) |= 0x80;
- IT83XX_I2C_DHTR(p_ch) &= 0x7F;
- /* State reset and hardware reset */
- IT83XX_I2C_CTR(p_ch) = E_STS_AND_HW_RST;
- /* bit1, Module enable */
- IT83XX_I2C_CTR1(p_ch) = 0;
- }
- pdata[i].task_waiting = TASK_ID_INVALID;
- }
-
- i2c_freq_changed();
-
- for (i = 0; i < I2C_PORT_COUNT; i++) {
- /* Use default timeout */
- i2c_set_timeout(i, 0);
- }
-}