summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDino Li <dino.li@ite.com.tw>2015-10-01 10:27:35 +0800
committerchrome-bot <chrome-bot@chromium.org>2015-10-05 20:31:22 -0700
commit9e21514972c7d0b21e826e2edb2db7b8be2249ec (patch)
tree0731879a66af71cc76424020cd61861467b59867
parent6a686e6043e50d699250eaf1222357fa4f66c517 (diff)
downloadchrome-ec-9e21514972c7d0b21e826e2edb2db7b8be2249ec.tar.gz
it8380dev: fix i2c module
1. i2c interrupts are used. Signed-off-by: Dino Li <dino.li@ite.com.tw> BRANCH=none BUG=none TEST=1. console command 'i2cscan' found devices correctly. 2. console command 'i2cxfer' and 'battery' OK. Change-Id: I4d40488d482318128bc8c549f5c8d3c27abe4a04 Reviewed-on: https://chromium-review.googlesource.com/303001 Commit-Ready: Dino Li <dino.li@ite.com.tw> Tested-by: Dino Li <dino.li@ite.com.tw> Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/it8380dev/board.h4
-rw-r--r--chip/it83xx/i2c.c318
-rw-r--r--chip/it83xx/intc.c25
-rw-r--r--chip/it83xx/intc.h1
-rw-r--r--chip/it83xx/registers.h3
5 files changed, 230 insertions, 121 deletions
diff --git a/board/it8380dev/board.h b/board/it8380dev/board.h
index 1f7c55c469..d42e167f70 100644
--- a/board/it8380dev/board.h
+++ b/board/it8380dev/board.h
@@ -11,6 +11,7 @@
/* Optional features */
#undef CHIP_FAMILY_IT839X
+#define CONFIG_BATTERY_SMART
#define CONFIG_FANS 1
#undef CONFIG_IT83XX_KEYBOARD_KSI_WUC_INT
#define CONFIG_IT83XX_LPC_ACCESS_INT
@@ -37,6 +38,9 @@
#ifndef __ASSEMBLER__
+#define I2C_PORT_CHARGER 2
+#define I2C_PORT_BATTERY 2
+
#include "gpio_signal.h"
enum pwm_channel {
diff --git a/chip/it83xx/i2c.c b/chip/it83xx/i2c.c
index e76228f428..d9e8bbde0a 100644
--- a/chip/it83xx/i2c.c
+++ b/chip/it83xx/i2c.c
@@ -24,8 +24,6 @@
*/
#define I2C_CLK_LOW_TIMEOUT 25 /* ~= 25ms */
-#define I2C_LOOP_DELAY_US 16
-
/* Default maximum time we allow for an I2C transfer */
#define I2C_TIMEOUT_DEFAULT_US (100 * MSEC)
@@ -59,8 +57,7 @@ enum i2c_host_status_mask {
enum i2c_reset_cause {
I2C_RC_NO_IDLE_FOR_START = 1,
- I2C_RC_READ_NO_FINISH,
- I2C_RC_WRITE_NO_FINISH,
+ I2C_RC_TIMEOUT,
};
struct i2c_ch_freq {
@@ -108,6 +105,22 @@ static const struct i2c_pin i2c_pin_regs[] = {
#endif
};
+struct i2c_ctrl_t {
+ uint8_t irq;
+};
+
+const struct i2c_ctrl_t i2c_ctrl_regs[] = {
+ {IT83XX_IRQ_SMB_A},
+ {IT83XX_IRQ_SMB_B},
+ {IT83XX_IRQ_SMB_C},
+};
+
+enum i2c_ch_status {
+ I2C_CH_NORMAL = 0,
+ I2C_CH_W2R,
+ I2C_CH_WAIT_READ,
+};
+
/* I2C port state data */
struct i2c_port_data {
const uint8_t *out; /* Output data pointer */
@@ -120,6 +133,10 @@ struct i2c_port_data {
int err; /* Error code, if any */
uint8_t addr; /* address of device */
uint32_t timeout_us; /* Transaction timeout, or 0 to use default */
+
+ enum i2c_ch_status i2ccs;
+ /* Task waiting on port, or TASK_ID_INVALID if none. */
+ int task_waiting;
};
static struct i2c_port_data pdata[I2C_PORT_COUNT];
@@ -193,139 +210,152 @@ static void i2c_w2r_change_direction(int p)
}
}
-static int i2c_transaction(int p)
+static int i2c_tran_write(int p)
{
- uint32_t num;
struct i2c_port_data *pd = pdata + p;
- /* i2c write */
- if (pd->out_size) {
- 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 slave.
- */
- IT83XX_SMB_TRASLA(p) = pd->addr;
- /* Send first byte */
- IT83XX_SMB_HOBDB(p) = *(pd->out++);
- pd->widx++;
- /*
- * bit0, Host interrupt enable.
- * bit[2:4}, Extend command.
- * bit6, start.
- */
- IT83XX_SMB_HOCTL(p) = 0x5D;
- }
-
- for (num = 0; num < pd->timeout_us; num += I2C_LOOP_DELAY_US) {
- /* 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++;
+ 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 slave.
+ */
+ IT83XX_SMB_TRASLA(p) = pd->addr;
+ /* 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;
+ } else {
+ /* done */
+ pd->out_size = 0;
+ if (pd->in_size > 0) {
+ /* write to read */
+ i2c_w2r_change_direction(p);
} else {
- if (pd->in_size > 0) {
- i2c_w2r_change_direction(p);
- goto write_to_read;
- }
-
- if (pd->flags & I2C_XFER_STOP)
+ if (pd->flags & I2C_XFER_STOP) {
+ /* set I2C_EN = 0 */
IT83XX_SMB_HOCTL2(p) = 0x11;
- else
- break;
+ /* W/C byte done for finish */
+ IT83XX_SMB_HOSTA(p) =
+ HOSTA_NEXT_BYTE;
+ } else {
+ pd->i2ccs = I2C_CH_W2R;
+ return 0;
+ }
}
- /* W/C byte done for next byte or finish */
- IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE;
- /* This bit will be set by termination of a command */
- } else if (IT83XX_SMB_HOSTA(p) & HOSTA_FINTR) {
- break;
}
- /* - 1 since some time was used in the code above */
- udelay(I2C_LOOP_DELAY_US - 1);
- }
-
- pd->err = IT83XX_SMB_HOSTA(p) & HOSTA_ANY_ERROR;
-
- if (num > pd->timeout_us) {
- pd->err = HOSTA_NO_FINISH;
- i2c_reset(p, I2C_RC_WRITE_NO_FINISH);
- }
-
- if (pd->flags & I2C_XFER_STOP) {
- IT83XX_SMB_HOCTL2(p) = 0x00;
- /* W/C */
- IT83XX_SMB_HOSTA(p) = HOSTA_ALL_WC_BIT;
- }
- return pd->err;
- /* i2c read */
- } else if (pd->in_size) {
- 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 slave.
- */
- IT83XX_SMB_TRASLA(p) = pd->addr | 0x01;
- /*
- * 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 {
- i2c_w2r_change_direction(p);
}
+ }
+ return 1;
+}
-write_to_read:
- for (num = 0; num < pd->timeout_us; num += I2C_LOOP_DELAY_US) {
- /* when the host controller has received a byte */
- 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);
- }
-
- if ((pd->ridx == pd->in_size) &&
- (!(pd->flags & I2C_XFER_STOP)))
- break;
+static int i2c_tran_read(int p)
+{
+ struct i2c_port_data *pd = pdata + p;
- /* W/C for next byte or finish */
+ 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 slave.
+ */
+ IT83XX_SMB_TRASLA(p) = pd->addr | 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_W2R) ||
+ (pd->i2ccs == I2C_CH_WAIT_READ)) {
+ if (pd->i2ccs == I2C_CH_W2R) {
+ /* 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;
- /* This bit will be set by termination of a command */
- } else if (IT83XX_SMB_HOSTA(p) & HOSTA_FINTR) {
- break;
}
-
- /* - 1 since some time was used in the code above */
- udelay(I2C_LOOP_DELAY_US - 1);
+ 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;
+}
- pd->err = IT83XX_SMB_HOSTA(p) & HOSTA_ANY_ERROR;
-
- if (num > pd->timeout_us) {
- pd->err = HOSTA_NO_FINISH;
- i2c_reset(p, I2C_RC_READ_NO_FINISH);
- }
+static int i2c_transaction(int p)
+{
+ struct i2c_port_data *pd = pdata + p;
- if (pd->flags & I2C_XFER_STOP) {
- IT83XX_SMB_HOCTL2(p) = 0x00;
- /* W/C */
- IT83XX_SMB_HOSTA(p) = HOSTA_ALL_WC_BIT;
- }
- return pd->err;
+ /* 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;
+ /* done doing work */
return 0;
}
@@ -338,10 +368,16 @@ int chip_i2c_xfer(int port, int slave_addr, 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;
+ if ((pd->i2ccs == I2C_CH_W2R) || (pd->i2ccs == I2C_CH_WAIT_READ)) {
+ if ((flags & I2C_XFER_SINGLE) == I2C_XFER_SINGLE)
+ flags &= ~I2C_XFER_START;
+ }
+
/* Copy data to port struct */
pd->out = out;
pd->out_size = out_size;
@@ -357,13 +393,33 @@ int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size,
if ((flags & I2C_XFER_START) && (i2c_is_busy(port)
|| (IT83XX_SMB_HOSTA(port) & HOSTA_ALL_WC_BIT)
|| (i2c_get_line_levels(port) != I2C_LINE_IDLE))) {
+
/* Attempt to unwedge the port. */
i2c_unwedge(port);
/* reset i2c port */
i2c_reset(port, I2C_RC_NO_IDLE_FOR_START);
}
+
+ 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);
+ events = task_wait_event(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_TIMER) {
+ pd->err = EC_ERROR_TIMEOUT;
+ /* reset i2c port */
+ i2c_reset(port, I2C_RC_TIMEOUT);
+ }
return pd->err;
}
@@ -402,6 +458,24 @@ 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, 0);
+ }
+}
+
static void i2c_freq_changed(void)
{
int i, f;
@@ -455,6 +529,8 @@ static void i2c_init(void)
IT83XX_SMB_HOSTA(p) = HOSTA_ALL_WC_BIT;
IT83XX_SMB_HOCTL2(p) = 0x00;
+
+ pdata[i].task_waiting = TASK_ID_INVALID;
}
i2c_freq_changed();
diff --git a/chip/it83xx/intc.c b/chip/it83xx/intc.c
index ebccb043d1..37d2f18c35 100644
--- a/chip/it83xx/intc.c
+++ b/chip/it83xx/intc.c
@@ -84,3 +84,28 @@ void intc_cpu_int_group_12(void)
}
}
DECLARE_IRQ(CPU_INT_GROUP_12, intc_cpu_int_group_12, 2);
+
+void intc_cpu_int_group_6(void)
+{
+ /* Determine interrupt number. */
+ int intc_group_6 = IT83XX_INTC_IVCT6 - 16;
+
+ switch (intc_group_6) {
+
+ case IT83XX_IRQ_SMB_A:
+ i2c_interrupt(0);
+ break;
+
+ case IT83XX_IRQ_SMB_B:
+ i2c_interrupt(1);
+ break;
+
+ case IT83XX_IRQ_SMB_C:
+ i2c_interrupt(2);
+ break;
+
+ default:
+ break;
+ }
+}
+DECLARE_IRQ(CPU_INT_GROUP_6, intc_cpu_int_group_6, 2);
diff --git a/chip/it83xx/intc.h b/chip/it83xx/intc.h
index a9888f0c0e..6734436fdc 100644
--- a/chip/it83xx/intc.h
+++ b/chip/it83xx/intc.h
@@ -17,5 +17,6 @@ void pm4_ibf_interrupt(void);
void pm5_ibf_interrupt(void);
void lpcrst_interrupt(enum gpio_signal signal);
void peci_interrupt(void);
+void i2c_interrupt(int port);
#endif /* __CROS_EC_INTC_H */
diff --git a/chip/it83xx/registers.h b/chip/it83xx/registers.h
index 53e1ed728e..e8a5821ad5 100644
--- a/chip/it83xx/registers.h
+++ b/chip/it83xx/registers.h
@@ -308,6 +308,9 @@
#define CPU_INT_GROUP_3 251
#define IT83XX_CPU_INT_IRQ_251 3
+#define CPU_INT_GROUP_6 250
+#define IT83XX_CPU_INT_IRQ_250 6
+
#define CPU_INT(irq) CONCAT2(IT83XX_CPU_INT_IRQ_, irq)
/* --- INTC --- */