diff options
author | Kyoung Kim <kyoung.il.kim@intel.com> | 2018-03-21 12:39:50 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-12-03 20:32:14 -0800 |
commit | 1aeb203a2feb48536454d888469a6bb4e9fb3b46 (patch) | |
tree | d5fea1754d140dc11090b02131e7c4a283c5fb7a | |
parent | 360b6e1a16bed7a5c4b3d83e50a80ebc8aaf69d4 (diff) | |
download | chrome-ec-1aeb203a2feb48536454d888469a6bb4e9fb3b46.tar.gz |
ISH-i2c: support 1MHz, clock stretch, restart
- Support for 2 or more bytes size of command register
in slave device.
- Adjust parameters for FAST and FAST+ modes.
- added Restart and SCL stretch
BUG=b:113238573
TEST=test with FAST/FAST+ mode Elan TrackPad slave device.
Use i2c_xfer() API inside a test task which GPIO interrupt
(Elan device's INT line) wakes.
Change-Id: I1ebcef90d85d6ede90bb8687e20058bdf31bf4e8
Signed-off-by: Kyoung Kim <kyoung.il.kim@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/996799
Commit-Ready: Caveh Jalali <caveh@google.com>
Commit-Ready: Kyoung Il Kim <kyoung.il.kim@intel.com>
Tested-by: Kyoung Il Kim <kyoung.il.kim@intel.com>
Reviewed-by: Caveh Jalali <caveh@google.com>
Reviewed-by: Jett Rink <jettrink@chromium.org>
Reviewed-by: Kyoung Il Kim <kyoung.il.kim@intel.com>
-rw-r--r-- | chip/ish/config_chip.h | 2 | ||||
-rw-r--r-- | chip/ish/i2c.c | 229 | ||||
-rw-r--r-- | chip/ish/ish_i2c.h | 11 |
3 files changed, 158 insertions, 84 deletions
diff --git a/chip/ish/config_chip.h b/chip/ish/config_chip.h index 3e9d8605a3..273bcd0788 100644 --- a/chip/ish/config_chip.h +++ b/chip/ish/config_chip.h @@ -23,6 +23,8 @@ /* Maximum number of deferrable functions */ #define DEFERRABLE_MAX_COUNT 8 +/* this macro causes 'pause' and reduces loop counts inside loop. */ +#define CPU_RELAX() asm volatile("rep; nop" ::: "memory") /****************************************************************************/ /* Memory mapping */ diff --git a/chip/ish/i2c.c b/chip/ish/i2c.c index 9525849085..e467616783 100644 --- a/chip/ish/i2c.c +++ b/chip/ish/i2c.c @@ -7,6 +7,7 @@ #include "common.h" #include "console.h" +#include "config_chip.h" #include "gpio.h" #include "hooks.h" #include "i2c.h" @@ -21,8 +22,7 @@ #define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) #define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args) -#define I2C_FLAG_REPEATED_START_DISABLED 0 -#define EVENT_FLAG_I2C_TIMEOUT TASK_EVENT_CUSTOM(1 << 1) +#define EVENT_FLAG_I2C_TIMEOUT TASK_EVENT_CUSTOM(1 << 7) /*25MHz, 50MHz, 100MHz, 120MHz, 40MHz, 20MHz, 37MHz*/ static uint16_t default_hcnt_scl_100[] = { @@ -34,26 +34,27 @@ static uint16_t default_lcnt_scl_100[] = { }; static uint16_t default_hcnt_scl_400[] = { - 600, 820, 1120, 1066, 600, 600, 450 + 600, 820, 1120, 800, 600, 600, 450 }; static uint16_t default_lcnt_scl_400[] = { - 1320, 1380, 1300, 1300, 1300, 1200, 1250 + 1320, 1380, 1300, 1550, 1300, 1200, 1250 }; static uint16_t default_hcnt_scl_1000[] = { - 260, 260, 260, 260, 260, 260, 260 + 260, 260, 260, 305, 260, 260, 260 }; static uint16_t default_lcnt_scl_1000[] = { - 500, 500, 500, 500, 500, 500, 500 + 500, 500, 500, 525, 500, 500, 500 }; static uint16_t default_hcnt_scl_hs[] = { 160, 300, 160, 166, 175, 150, 162 }; static uint16_t default_lcnt_scl_hs[] = { 320, 340, 320, 325, 325, 300, 297 }; static uint8_t speed_val_arr[] = { - STD_SPEED_VAL, FAST_SPEED_VAL, FAST_PLUS_SPEED_VAL, HIGH_SPEED_VAL}; + STD_SPEED_VAL, FAST_SPEED_VAL, FAST_PLUS_SPEED_VAL, HIGH_SPEED_VAL +}; static uint8_t bus_freq[ISH_I2C_PORT_COUNT] = { I2C_FREQ_120, I2C_FREQ_120, I2C_FREQ_120 @@ -83,24 +84,24 @@ static struct i2c_context i2c_ctxs[ISH_I2C_PORT_COUNT] = { static struct i2c_bus_info board_config[ISH_I2C_PORT_COUNT] = { { .bus_id = 0, - .std_speed.sda_hold = DEFAULT_SDA_HOLD, - .fast_speed.sda_hold = DEFAULT_SDA_HOLD, - .fast_plus_speed.sda_hold = DEFAULT_SDA_HOLD, - .high_speed.sda_hold = DEFAULT_SDA_HOLD, + .std_speed.sda_hold = DEFAULT_SDA_HOLD_STD, + .fast_speed.sda_hold = DEFAULT_SDA_HOLD_FAST, + .fast_plus_speed.sda_hold = DEFAULT_SDA_HOLD_FAST_PLUS, + .high_speed.sda_hold = DEFAULT_SDA_HOLD_HIGH, }, { .bus_id = 1, - .std_speed.sda_hold = DEFAULT_SDA_HOLD, - .fast_speed.sda_hold = DEFAULT_SDA_HOLD, - .fast_plus_speed.sda_hold = DEFAULT_SDA_HOLD, - .high_speed.sda_hold = DEFAULT_SDA_HOLD, + .std_speed.sda_hold = DEFAULT_SDA_HOLD_STD, + .fast_speed.sda_hold = DEFAULT_SDA_HOLD_FAST, + .fast_plus_speed.sda_hold = DEFAULT_SDA_HOLD_FAST_PLUS, + .high_speed.sda_hold = DEFAULT_SDA_HOLD_HIGH, }, { .bus_id = 2, - .std_speed.sda_hold = DEFAULT_SDA_HOLD, - .fast_speed.sda_hold = DEFAULT_SDA_HOLD, - .fast_plus_speed.sda_hold = DEFAULT_SDA_HOLD, - .high_speed.sda_hold = DEFAULT_SDA_HOLD, + .std_speed.sda_hold = DEFAULT_SDA_HOLD_STD, + .fast_speed.sda_hold = DEFAULT_SDA_HOLD_FAST, + .fast_plus_speed.sda_hold = DEFAULT_SDA_HOLD_FAST_PLUS, + .high_speed.sda_hold = DEFAULT_SDA_HOLD_HIGH, }, }; @@ -170,64 +171,63 @@ static void i2c_init_transaction(struct i2c_context *ctx, i2c_mmio_write(base, IC_ENABLE, IC_ENABLE_DISABLE); i2c_mmio_write(base, IC_TAR, (slave_addr << IC_TAR_OFFSET) | - TAR_SPECIAL_VAL | IC_10BITADDR_MASTER_VAL); + TAR_SPECIAL_VAL | IC_10BITADDR_MASTER_VAL); /* set Clock SCL Count */ switch (ctx->speed) { case I2C_SPEED_STD: i2c_mmio_write(base, IC_SS_SCL_HCNT, - NS_2_COUNTERS(bus_info->std_speed.hcnt, + NS_2_COUNTERS(bus_info->std_speed.hcnt, clk_in_val)); i2c_mmio_write(base, IC_SS_SCL_LCNT, - NS_2_COUNTERS(bus_info->std_speed.lcnt, + NS_2_COUNTERS(bus_info->std_speed.lcnt, clk_in_val)); i2c_mmio_write(base, IC_SDA_HOLD, - NS_2_COUNTERS(bus_info->std_speed.sda_hold, + NS_2_COUNTERS(bus_info->std_speed.sda_hold, clk_in_val)); break; case I2C_SPEED_FAST: i2c_mmio_write(base, IC_FS_SCL_HCNT, - NS_2_COUNTERS(bus_info->fast_speed.hcnt, + NS_2_COUNTERS(bus_info->fast_speed.hcnt, clk_in_val)); i2c_mmio_write(base, IC_FS_SCL_LCNT, - NS_2_COUNTERS(bus_info->fast_speed.lcnt, + NS_2_COUNTERS(bus_info->fast_speed.lcnt, clk_in_val)); i2c_mmio_write(base, IC_SDA_HOLD, - NS_2_COUNTERS(bus_info->fast_speed.sda_hold, + NS_2_COUNTERS(bus_info->fast_speed.sda_hold, clk_in_val)); break; - case I2C_SPEED_FAST_PLUS: i2c_mmio_write(base, IC_FS_SCL_HCNT, - NS_2_COUNTERS(bus_info->fast_plus_speed.hcnt, + NS_2_COUNTERS(bus_info->fast_plus_speed.hcnt, clk_in_val)); i2c_mmio_write(base, IC_FS_SCL_LCNT, - NS_2_COUNTERS(bus_info->fast_plus_speed.lcnt, + NS_2_COUNTERS(bus_info->fast_plus_speed.lcnt, clk_in_val)); i2c_mmio_write(base, IC_SDA_HOLD, - NS_2_COUNTERS(bus_info->fast_plus_speed.sda_hold, + NS_2_COUNTERS(bus_info->fast_plus_speed.sda_hold, clk_in_val)); break; case I2C_SPEED_HIGH: i2c_mmio_write(base, IC_HS_SCL_HCNT, - NS_2_COUNTERS(bus_info->high_speed.hcnt, + NS_2_COUNTERS(bus_info->high_speed.hcnt, clk_in_val)); i2c_mmio_write(base, IC_HS_SCL_LCNT, - NS_2_COUNTERS(bus_info->high_speed.lcnt, + NS_2_COUNTERS(bus_info->high_speed.lcnt, clk_in_val)); i2c_mmio_write(base, IC_SDA_HOLD, - NS_2_COUNTERS(bus_info->high_speed.sda_hold, + NS_2_COUNTERS(bus_info->high_speed.sda_hold, clk_in_val)); i2c_mmio_write(base, IC_FS_SCL_HCNT, - NS_2_COUNTERS(bus_info->fast_speed.hcnt, + NS_2_COUNTERS(bus_info->fast_speed.hcnt, clk_in_val)); i2c_mmio_write(base, IC_FS_SCL_LCNT, - NS_2_COUNTERS(bus_info->fast_speed.lcnt, + NS_2_COUNTERS(bus_info->fast_speed.lcnt, clk_in_val)); break; @@ -238,10 +238,10 @@ static void i2c_init_transaction(struct i2c_context *ctx, /* in SPT HW we need to sync between I2C clock and data signals */ con_value = i2c_mmio_read(base, IC_CON); - if (flags & I2C_FLAG_REPEATED_START_DISABLED) - con_value &= ~IC_RESTART_EN_VAL; - else + if (flags != 0) con_value |= IC_RESTART_EN_VAL; + else + con_value &= ~IC_RESTART_EN_VAL; i2c_mmio_write(base, IC_CON, con_value); i2c_mmio_write(base, IC_FS_SPKLEN, spkln[bus_freq[ctx->bus]]); @@ -261,81 +261,137 @@ static void i2c_write_buffer(uint32_t *base, uint8_t len, ++(*cur_index); out = (buffer[i] << DATA_CMD_DAT_OFFSET) | DATA_CMD_WRITE_VAL; - if (*cur_index == total_len) + /* if Write ONLY and Last byte */ + if (*cur_index == total_len) { out |= DATA_CMD_STOP_VAL; + } i2c_mmio_write(base, IC_DATA_CMD, out); } } -static void i2c_write_read_commands(uint32_t *base, uint8_t len) +static void i2c_write_read_commands(uint32_t *base, uint8_t len, int more_data, + unsigned restart_flag) { + /* this routine just set RX FIFO's control bit(s), + * READ command or RESTART */ int i; + uint32_t data_cmd; - for (i = 0; i < len - 1; i++) - i2c_mmio_write(base, IC_DATA_CMD, DATA_CMD_READ_VAL); + for (i = 0; i < len; i++) { + data_cmd = DATA_CMD_READ_VAL; - i2c_mmio_write(base, IC_DATA_CMD, - DATA_CMD_READ_VAL | DATA_CMD_STOP_VAL); + if ((i == 0) && restart_flag) + /* if restart for first byte */ + data_cmd |= DATA_CMD_RESTART_VAL; + + /* if last byte & less than FIFO size + * or only one byte to read */ + if (i == (len - 1) && !more_data) + data_cmd |= DATA_CMD_STOP_VAL; + + i2c_mmio_write(base, IC_DATA_CMD, data_cmd); + } } int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, uint8_t *in, int in_size, int flags) { - int i, is_read = 0; + int i; ssize_t total_len; uint64_t expire_ts; struct i2c_context *ctx; ssize_t curr_index = 0; + int begin_indx; + uint8_t repeat_start = 0; + if (out_size == 0 && in_size == 0) return EC_SUCCESS; - if (in_size > 0) - is_read = 1; + /* assume that if both out_size and in_size are not zero, + * then, it is 'repeated Start' condition. */ + if (in_size != 0 && out_size != 0) + repeat_start = 1; ctx = &i2c_ctxs[port]; ctx->error_flag = 0; + ctx->wait_task_id = task_get_current(); - total_len = is_read ? (1 + in_size) : out_size; - - i2c_init_transaction(ctx, slave_addr, flags); + total_len = in_size + out_size; - /* Write device id */ - i2c_write_buffer(ctx->base, 1, out, &curr_index, total_len); + i2c_init_transaction(ctx, slave_addr, repeat_start); /* Write W data */ - i2c_write_buffer(ctx->base, (is_read ? 0 : out_size - 1), - (is_read ? NULL : out + 1), - &curr_index, total_len); + if (out_size) + i2c_write_buffer(ctx->base, out_size, out, + &curr_index, total_len); + + /* Wait here until Tx is completed so that FIFO becomes empty. + * This is optimized for smaller Tx data size. + * If need to write big data ( > ISH_I2C_FIFO_SIZE ), + * it is better to use Tx FIFO threshold interrupt(as in Rx) for + * better CPU usuage. + * */ + expire_ts = __hw_clock_source_read() + I2C_TX_FLUSH_TIMEOUT_USEC; + if (in_size > (ISH_I2C_FIFO_SIZE - out_size)) { + + while ((i2c_mmio_read(ctx->base, IC_STATUS) & + (1 << IC_STATUS_TFE)) == 0) { + + if (__hw_clock_source_read() >= expire_ts) { + ctx->error_flag = 1; + break; + } + CPU_RELAX(); + } + } - if (is_read) { - /* Write R commands */ - i2c_write_read_commands(ctx->base, in_size); + begin_indx = 0; + while (in_size) { + int rd_size; /* read size for on i2c transaction */ - /* Set rx_theshold */ - i2c_mmio_write(ctx->base, IC_RX_TL, in_size - 1); - } + /* + * check if in_size > ISH_I2C_FIFO_SIZE, then try to read + * FIFO_SIZE each time. + */ + if (in_size > ISH_I2C_FIFO_SIZE) { + rd_size = ISH_I2C_FIFO_SIZE; + in_size -= ISH_I2C_FIFO_SIZE; + } else { + rd_size = in_size; + in_size = 0; + } + /* Set rx_threshold */ + i2c_mmio_write(ctx->base, IC_RX_TL, rd_size - 1); - /* Enable interrupts */ - i2c_intr_switch(ctx->base, - is_read ? ENABLE_READ_INT : ENABLE_WRITE_INT); + i2c_intr_switch(ctx->base, ENABLE_READ_INT); - /* Wait for interrupt */ - ctx->wait_task_id = task_get_current(); - task_wait_event_mask(EVENT_FLAG_I2C_TIMEOUT, -1); - - if ((ctx->interrupts & M_TX_ABRT) == 0) { - if (is_read) { - /* read data */ - for (i = 0; i < in_size; i++) - in[i] = i2c_read_byte(ctx->base, - IC_DATA_CMD, 0); + /* + * RESTART only once for entire i2c transaction. + * assume that if both out_size and in_size are not zero, + * then, it is 'repeated Start' condition. + * set R commands bit, start to read + */ + i2c_write_read_commands(ctx->base, rd_size, in_size, + (begin_indx == 0) && (repeat_start != 0)); + + + /* need timeout in case no ACK from slave */ + task_wait_event_mask(EVENT_FLAG_I2C_TIMEOUT, 2*MSEC); + + if (ctx->interrupts & M_TX_ABRT) { + ctx->error_flag = 1; + break; /* when bus abort, no more reading !*/ } - } else { - ctx->error_flag = 1; - } + /* read data */ + for (i = begin_indx; i < begin_indx + rd_size; i++) + in[i] = i2c_read_byte(ctx->base, + IC_DATA_CMD, 0); + + begin_indx += rd_size; + } /* while (in_size) */ ctx->reason = 0; ctx->interrupts = 0; @@ -359,10 +415,17 @@ int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, static void i2c_interrupt_handler(struct i2c_context *ctx) { +#ifdef INTR_DEBUG + uint32_t raw_intr; + raw_intr = 0x0000FFFF & i2c_mmio_read(ctx->base, IC_RAW_INTR_STAT); +#endif /* check interrupts */ ctx->interrupts = i2c_mmio_read(ctx->base, IC_INTR_STAT); ctx->reason = (uint16_t) i2c_mmio_read(ctx->base, IC_TX_ABRT_SOURCE); - +#ifdef INTR_DEBUG + CPRINTS("INTR_STAT = 0x%04x, TX_ABORT_SRC = 0x%04x, RAW_INTR_STAT = 0x%04x\n", + ctx->interrupts, ctx->reason, raw_intr); +#endif /* disable interrupts */ i2c_intr_switch(ctx->base, DISABLE_INT); task_set_event(ctx->wait_task_id, EVENT_FLAG_I2C_TIMEOUT, 0); @@ -408,18 +471,18 @@ static void i2c_init_hardware(struct i2c_context *ctx) i2c_intr_switch(base, DISABLE_INT); i2c_mmio_write(base, IC_ENABLE, IC_ENABLE_DISABLE); i2c_mmio_write(base, IC_CON, (MASTER_MODE_VAL - | speed_val_arr[ctx->speed] - | IC_RESTART_EN_VAL - | IC_SLAVE_DISABLE_VAL)); + | speed_val_arr[ctx->speed] + | IC_RESTART_EN_VAL + | IC_SLAVE_DISABLE_VAL)); i2c_mmio_write(base, IC_FS_SPKLEN, spkln[bus_freq[ctx->bus]]); i2c_mmio_write(base, IC_HS_SPKLEN, spkln[bus_freq[ctx->bus]]); /* get RX_FIFO and TX_FIFO depth */ ctx->max_rx_depth = i2c_read_byte(base, IC_COMP_PARAM_1, - RX_BUFFER_DEPTH_OFFSET) + 1; + RX_BUFFER_DEPTH_OFFSET) + 1; ctx->max_tx_depth = i2c_read_byte(base, IC_COMP_PARAM_1, - TX_BUFFER_DEPTH_OFFSET) + 1; + TX_BUFFER_DEPTH_OFFSET) + 1; } static void i2c_initial_board_config(struct i2c_context *ctx) diff --git a/chip/ish/ish_i2c.h b/chip/ish/ish_i2c.h index 26242fa363..41571b8bff 100644 --- a/chip/ish/ish_i2c.h +++ b/chip/ish/ish_i2c.h @@ -13,10 +13,18 @@ #define I2C_CALIB_ADDRESS 0x3 #define I2C_INTERRUPT_TIMEOUT (TICKFREQ / 20) #define NS_IN_SEC 1000 -#define DEFAULT_SDA_HOLD 133 +#define DEFAULT_SDA_HOLD 240 +#define DEFAULT_SDA_HOLD_STD 2400 +#define DEFAULT_SDA_HOLD_FAST 600 +#define DEFAULT_SDA_HOLD_FAST_PLUS 300 +#define DEFAULT_SDA_HOLD_HIGH 140 #define NS_2_COUNTERS(ns, clk) ((ns * clk)/NS_IN_SEC) #define COUNTERS_2_NS(counters, clk) (counters * (NANOSECONDS_IN_SEC / \ (clk * HZ_IN_MEGAHZ))) +#define I2C_TX_FLUSH_TIMEOUT_USEC 200 + +#define ISH_I2C_FIFO_SIZE 64 + enum { /* speed mode values */ @@ -92,6 +100,7 @@ enum { IC_ENABLE_DISABLE = 0, /* IC_STATUS OFFSETS */ IC_STATUS_MASTER_ACTIVITY = 5, + IC_STATUS_TFE = 2, /* IC_CON OFFSETS */ MASTER_MODE_OFFSET = 0, SPEED_OFFSET = 1, |