diff options
author | Dino Li <dino.li@ite.com.tw> | 2015-10-01 10:27:35 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2015-10-05 20:31:22 -0700 |
commit | 9e21514972c7d0b21e826e2edb2db7b8be2249ec (patch) | |
tree | 0731879a66af71cc76424020cd61861467b59867 /chip | |
parent | 6a686e6043e50d699250eaf1222357fa4f66c517 (diff) | |
download | chrome-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>
Diffstat (limited to 'chip')
-rw-r--r-- | chip/it83xx/i2c.c | 318 | ||||
-rw-r--r-- | chip/it83xx/intc.c | 25 | ||||
-rw-r--r-- | chip/it83xx/intc.h | 1 | ||||
-rw-r--r-- | chip/it83xx/registers.h | 3 |
4 files changed, 226 insertions, 121 deletions
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 --- */ |