From 0a6b7620d6b4ba1a50500a75db3e76162eac5ce0 Mon Sep 17 00:00:00 2001 From: Randall Spangler Date: Fri, 26 Apr 2013 14:36:38 -0700 Subject: Move i2cread and i2cwrite functions to i2c_common Also moves the handy i2cscan command to i2c_common. The platform-dependent interface is now i2c_xfer(). Still more to do in follow-up CLs; for example, i2c_read_string() has platform-dependent implementation, and the i2c/i2cread console commands aren't common yet. BUG=chrome-os-partner:18969 BRANCH=none TEST=i2cscan on link, spring Change-Id: Ia53d57beaa157bece293a4262257e20b4107589e Signed-off-by: Randall Spangler Reviewed-on: https://gerrit.chromium.org/gerrit/49492 Reviewed-by: Simon Glass Commit-Queue: Daniel Erat Commit-Queue: Simon Glass --- board/daisy/board.c | 6 ++ board/daisy/board.h | 1 + board/pit/board.c | 5 ++ board/pit/board.h | 1 + board/snow/board.c | 5 ++ board/snow/board.h | 1 + board/spring/board.c | 5 ++ board/spring/board.h | 1 + chip/lm4/config.h | 5 +- chip/lm4/i2c.c | 179 +++++++-------------------------------------------- chip/stm32/config.h | 5 +- chip/stm32/i2c.c | 124 ++++++++++++----------------------- common/i2c_common.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++-- include/i2c.h | 41 ++++++++++++ 14 files changed, 309 insertions(+), 245 deletions(-) diff --git a/board/daisy/board.c b/board/daisy/board.c index 0961df5a2f..72f601abb1 100644 --- a/board/daisy/board.c +++ b/board/daisy/board.c @@ -96,6 +96,12 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"KB_OUT12", GPIO_C, (1<<7), GPIO_KB_OUTPUT, NULL}, }; +/* I2C ports */ +const struct i2c_port_t i2c_ports[I2C_PORTS_USED] = { + {"0", 0, 100}, + {"1", 1, 100}, +}; + /* Auto detect I2C host port * Daisy board has two I2C ports, I2C1(0) and I2C2(1), that can be configured * as host. PMU chip is connected directly to the EC, and hence can be used diff --git a/board/daisy/board.h b/board/daisy/board.h index eed6d5df7a..d5826f4265 100644 --- a/board/daisy/board.h +++ b/board/daisy/board.h @@ -56,6 +56,7 @@ #define I2C_PORT_BATTERY I2C_PORT_HOST #define I2C_PORT_CHARGER I2C_PORT_HOST #define I2C_PORT_SLAVE 1 +#define I2C_PORTS_USED 2 /* Since host could be on either 0 or 1 */ /* Timer selection */ #define TIM_CLOCK_MSB 3 diff --git a/board/pit/board.c b/board/pit/board.c index 54d5a96980..c7c124df6d 100644 --- a/board/pit/board.c +++ b/board/pit/board.c @@ -86,6 +86,11 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"KB_OUT12", GPIO_A, (1<<13), GPIO_KB_OUTPUT, NULL}, }; +/* I2C ports */ +const struct i2c_port_t i2c_ports[I2C_PORTS_USED] = { + {"host", I2C_PORT_HOST, 100}, +}; + void board_config_post_gpio_init(void) { /* I2C SCL/SDA on PB10-11 and PB6-7 */ diff --git a/board/pit/board.h b/board/pit/board.h index 8c246d3f3e..1009ef490a 100644 --- a/board/pit/board.h +++ b/board/pit/board.h @@ -62,6 +62,7 @@ #define I2C_PORT_BATTERY I2C_PORT_HOST #define I2C_PORT_CHARGER I2C_PORT_HOST #define I2C_PORT_SLAVE 1 +#define I2C_PORTS_USED 1 /* Timer selection */ #define TIM_CLOCK_MSB 3 diff --git a/board/snow/board.c b/board/snow/board.c index 4efe5dab39..0f911e03fd 100644 --- a/board/snow/board.c +++ b/board/snow/board.c @@ -93,6 +93,11 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"KB_OUT12", GPIO_C, (1<<7), GPIO_KB_OUTPUT, NULL}, }; +/* I2C ports */ +const struct i2c_port_t i2c_ports[I2C_PORTS_USED] = { + {"host", I2C_PORT_HOST, 100}, +}; + void board_config_pre_init(void) { uint32_t val; diff --git a/board/snow/board.h b/board/snow/board.h index 9d14ef939e..517509abcb 100644 --- a/board/snow/board.h +++ b/board/snow/board.h @@ -58,6 +58,7 @@ #define I2C_PORT_BATTERY I2C_PORT_HOST #define I2C_PORT_CHARGER I2C_PORT_HOST #define I2C_PORT_SLAVE 1 +#define I2C_PORTS_USED 1 #define GPIO_AP_CLAIM GPIO_SPI1_NSS /* AP claims bus */ #define GPIO_EC_CLAIM GPIO_SPI1_MISO /* EC claims bus */ diff --git a/board/spring/board.c b/board/spring/board.c index a81e38cf5f..40118cc309 100644 --- a/board/spring/board.c +++ b/board/spring/board.c @@ -107,6 +107,11 @@ const struct adc_t adc_channels[ADC_CH_COUNT] = { [ADC_CH_USB_DN_SNS] = {"USB_DN_SNS", 3300, 4096, 0, STM32_AIN(4)}, }; +/* I2C ports */ +const struct i2c_port_t i2c_ports[I2C_PORTS_USED] = { + {"host", I2C_PORT_HOST, 100}, +}; + void board_config_pre_init(void) { uint32_t val; diff --git a/board/spring/board.h b/board/spring/board.h index 5fd7c0b878..69b942a94a 100644 --- a/board/spring/board.h +++ b/board/spring/board.h @@ -58,6 +58,7 @@ #define I2C_PORT_BATTERY I2C_PORT_HOST #define I2C_PORT_CHARGER I2C_PORT_HOST #define I2C_PORT_SLAVE 1 +#define I2C_PORTS_USED 1 /* Low battery threshold. In mAh. */ #define BATTERY_AP_OFF_LEVEL 1 diff --git a/chip/lm4/config.h b/chip/lm4/config.h index 46a0b125cf..95727c0c84 100644 --- a/chip/lm4/config.h +++ b/chip/lm4/config.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. */ @@ -21,6 +21,9 @@ /* Maximum number of deferrable functions */ #define DEFERRABLE_MAX_COUNT 8 +/* Number of I2C ports */ +#define I2C_PORT_COUNT 6 + /****************************************************************************/ /* Memory mapping */ diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c index 4a47011c15..506538eebf 100644 --- a/chip/lm4/i2c.c +++ b/chip/lm4/i2c.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. */ @@ -19,8 +19,6 @@ #define CPUTS(outstr) cputs(CC_I2C, outstr) #define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args) -#define NUM_PORTS 6 /* Number of physical ports */ - /* Flags for writes to MCS */ #define LM4_I2C_MCS_RUN (1 << 0) #define LM4_I2C_MCS_START (1 << 1) @@ -39,13 +37,7 @@ #define LM4_I2C_MCS_BUSBSY (1 << 6) #define LM4_I2C_MCS_CLKTO (1 << 7) -#define START 1 -#define STOP 1 -#define NO_START 0 -#define NO_STOP 0 - -static task_id_t task_waiting_on_port[NUM_PORTS]; -static struct mutex port_mutex[NUM_PORTS]; +static task_id_t task_waiting_on_port[I2C_PORT_COUNT]; extern const struct i2c_port_t i2c_ports[I2C_PORTS_USED]; /** @@ -98,24 +90,17 @@ static int wait_idle(int port) return EC_SUCCESS; } -/** - * Transmit one block of raw data, then receive one block of raw data. - * - * @param port Port to access - * @param slave_addr Slave device address - * @param out Data to send - * @param out_size Number of bytes to send - * @param in Destination buffer for received data - * @param in_size Number of bytes to receive - * @param start Did smbus session started from idle state? - * @param stop Can session be terminated with smbus stop bit? - * @return EC_SUCCESS, or non-zero if error. - */ -static int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, - uint8_t *in, int in_size, int start, int stop) +int i2c_get_line_levels(int port) +{ + /* Conveniently, MBMON bit (1 << 1) is SDA and (1 << 0) is SCL. */ + return LM4_I2C_MBMON(port) & 0x03; +} + +int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, + uint8_t *in, int in_size, int flags) { int rv, i; - int started = start ? 0 : 1; + int started = (flags & I2C_XFER_START) ? 0 : 1; uint32_t reg_mcs; if (out_size == 0 && in_size == 0) @@ -165,7 +150,8 @@ static int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, * Send stop bit if the stop flag is on, and caller * doesn't expect to receive data. */ - if (stop && in_size == 0 && i == (out_size - 1)) + if ((flags & I2C_XFER_STOP) && in_size == 0 && + i == (out_size - 1)) reg_mcs |= LM4_I2C_MCS_STOP; LM4_I2C_MCS(port) = reg_mcs; @@ -196,7 +182,7 @@ static int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, reg_mcs |= LM4_I2C_MCS_START; } /* ACK all bytes except the last one */ - if (stop && i == (in_size - 1)) + if ((flags & I2C_XFER_STOP) && i == (in_size - 1)) reg_mcs |= LM4_I2C_MCS_STOP; else reg_mcs |= LM4_I2C_MCS_ACK; @@ -217,89 +203,13 @@ static int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, return EC_SUCCESS; } -int i2c_read16(int port, int slave_addr, int offset, int *data) -{ - int rv; - uint8_t reg, buf[2]; - - reg = offset & 0xff; - /* I2C read 16-bit word: transmit 8-bit offset, and read 16bits */ - mutex_lock(port_mutex + port); - rv = i2c_xfer(port, slave_addr, ®, 1, buf, 2, START, STOP); - mutex_unlock(port_mutex + port); - - if (rv) - return rv; - - if (slave_addr & I2C_FLAG_BIG_ENDIAN) - *data = ((int)buf[0] << 8) | buf[1]; - else - *data = ((int)buf[1] << 8) | buf[0]; - - return EC_SUCCESS; -} - -int i2c_write16(int port, int slave_addr, int offset, int data) -{ - int rv; - uint8_t buf[3]; - - buf[0] = offset & 0xff; - - if (slave_addr & I2C_FLAG_BIG_ENDIAN) { - buf[1] = (data >> 8) & 0xff; - buf[2] = data & 0xff; - } else { - buf[1] = data & 0xff; - buf[2] = (data >> 8) & 0xff; - } - - mutex_lock(port_mutex + port); - rv = i2c_xfer(port, slave_addr, buf, 3, 0, 0, START, STOP); - mutex_unlock(port_mutex + port); - - return rv; -} - -int i2c_read8(int port, int slave_addr, int offset, int* data) -{ - int rv; - uint8_t reg, val; - - reg = offset; - - mutex_lock(port_mutex + port); - rv = i2c_xfer(port, slave_addr, ®, 1, &val, 1, START, STOP); - mutex_unlock(port_mutex + port); - - if (!rv) - *data = val; - - return rv; -} - -int i2c_write8(int port, int slave_addr, int offset, int data) -{ - int rv; - uint8_t buf[2]; - - buf[0] = offset; - buf[1] = data; - - mutex_lock(port_mutex + port); - rv = i2c_xfer(port, slave_addr, buf, 2, 0, 0, START, STOP); - mutex_unlock(port_mutex + port); - - return rv; -} - int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data, int len) { int rv; uint8_t reg, block_length; - mutex_lock(port_mutex + port); + i2c_lock(port, 1); reg = offset; /* @@ -307,7 +217,7 @@ int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data, * session open without a stop. */ rv = i2c_xfer(port, slave_addr, ®, 1, &block_length, 1, - START, NO_STOP); + I2C_XFER_START); if (rv) goto exit; @@ -315,11 +225,11 @@ int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data, block_length = len - 1; rv = i2c_xfer(port, slave_addr, 0, 0, data, block_length, - NO_START, STOP); + I2C_XFER_STOP); data[block_length] = 0; exit: - mutex_unlock(port_mutex + port); + i2c_lock(port, 0); return rv; } @@ -398,7 +308,7 @@ static void i2c_init(void) configure_gpio(); /* No tasks are waiting on ports */ - for (i = 0; i < NUM_PORTS; i++) + for (i = 0; i < I2C_PORT_COUNT; i++) task_waiting_on_port[i] = TASK_ID_INVALID; /* Initialize ports as master, with interrupts enabled */ @@ -452,39 +362,6 @@ DECLARE_IRQ(LM4_IRQ_I2C5, i2c5_interrupt, 2); /*****************************************************************************/ /* Console commands */ -static void scan_bus(int port, const char *desc) -{ - int rv; - int a; - - ccprintf("Scanning %d %s", port, desc); - - /* Don't scan a busy port, since reads will just fail / time out */ - a = LM4_I2C_MBMON(port); - if ((a & 0x03) != 0x03) { - ccprintf(": port busy (SDA=%d, SCL=%d)\n", - (LM4_I2C_MBMON(port) & 0x02) ? 1 : 0, - (LM4_I2C_MBMON(port) & 0x01) ? 1 : 0); - return; - } - - mutex_lock(port_mutex + port); - - for (a = 0; a < 0x100; a += 2) { - ccputs("."); - - /* Do a single read */ - LM4_I2C_MSA(port) = a | 0x01; - LM4_I2C_MCS(port) = 0x07; - rv = wait_idle(port); - if (rv == EC_SUCCESS) - ccprintf("\n 0x%02x", a); - } - - mutex_unlock(port_mutex + port); - ccputs("\n"); -} - static int command_i2cread(int argc, char **argv) { int port, addr, count = 1; @@ -515,7 +392,7 @@ static int command_i2cread(int argc, char **argv) } ccprintf("Reading %d bytes from %d:0x%02x:", count, port, addr); - mutex_lock(port_mutex + port); + i2c_lock(port, 1); LM4_I2C_MSA(port) = addr | 0x01; for (i = 0; i < count; i++) { if (i == 0) @@ -524,13 +401,13 @@ static int command_i2cread(int argc, char **argv) LM4_I2C_MCS(port) = (i == count - 1 ? 0x05 : 0x09); rv = wait_idle(port); if (rv != EC_SUCCESS) { - mutex_unlock(port_mutex + port); + i2c_lock(port, 0); return rv; } d = LM4_I2C_MDR(port) & 0xff; ccprintf(" 0x%02x", d); } - mutex_unlock(port_mutex + port); + i2c_lock(port, 0); ccputs("\n"); return EC_SUCCESS; } @@ -539,15 +416,3 @@ DECLARE_CONSOLE_COMMAND(i2cread, command_i2cread, "Read from I2C", NULL); -static int command_scan(int argc, char **argv) -{ - int i; - - for (i = 0; i < I2C_PORTS_USED; i++) - scan_bus(i2c_ports[i].port, i2c_ports[i].name); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(i2cscan, command_scan, - NULL, - "Scan I2C ports for devices", - NULL); diff --git a/chip/stm32/config.h b/chip/stm32/config.h index 989cafa087..ef97180ebd 100644 --- a/chip/stm32/config.h +++ b/chip/stm32/config.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. */ @@ -34,6 +34,9 @@ /* Maximum number of deferrable functions */ #define DEFERRABLE_MAX_COUNT 8 +/* Number of I2C ports */ +#define I2C_PORT_COUNT 2 + /* support programming on-chip flash */ #define CONFIG_FLASH diff --git a/chip/stm32/i2c.c b/chip/stm32/i2c.c index 2b968b0dd2..8680f59521 100644 --- a/chip/stm32/i2c.c +++ b/chip/stm32/i2c.c @@ -57,7 +57,6 @@ */ #define I2C_BITBANG_DELAY_US 5 -#define NUM_PORTS 2 #define I2C1 STM32_I2C1_PORT #define I2C2 STM32_I2C2_PORT @@ -80,22 +79,21 @@ enum { STOP_SENT_RETRY_US = 150, }; -static const struct dma_option dma_tx_option[NUM_PORTS] = { +static const struct dma_option dma_tx_option[I2C_PORT_COUNT] = { {DMAC_I2C1_TX, (void *)&STM32_I2C_DR(I2C1), DMA_MSIZE_BYTE | DMA_PSIZE_HALF_WORD}, {DMAC_I2C2_TX, (void *)&STM32_I2C_DR(I2C2), DMA_MSIZE_BYTE | DMA_PSIZE_HALF_WORD}, }; -static const struct dma_option dma_rx_option[NUM_PORTS] = { +static const struct dma_option dma_rx_option[I2C_PORT_COUNT] = { {DMAC_I2C1_RX, (void *)&STM32_I2C_DR(I2C1), DMA_MSIZE_BYTE | DMA_PSIZE_HALF_WORD}, {DMAC_I2C2_RX, (void *)&STM32_I2C_DR(I2C2), DMA_MSIZE_BYTE | DMA_PSIZE_HALF_WORD}, }; -static uint16_t i2c_sr1[NUM_PORTS]; -static struct mutex i2c_mutex; +static uint16_t i2c_sr1[I2C_PORT_COUNT]; /* Buffer for host commands (including version, error code and checksum) */ static uint8_t host_buffer[EC_HOST_PARAM_SIZE + 4]; @@ -710,7 +708,7 @@ cr_cleanup: STM32_I2C_CR1(port) = (1 << 10) | (1 << 0); } -static int i2c_master_transmit(int port, int slave_addr, uint8_t *data, +static int i2c_master_transmit(int port, int slave_addr, const uint8_t *data, int size, int stop) { int rv, rv_start; @@ -804,32 +802,18 @@ static int i2c_master_receive(int port, int slave_addr, uint8_t *data, return wait_until_stop_sent(port); } -/** - * Perform an I2C transaction, involve a write, and optional read. - * - * @param port I2C port to use (e.g. I2C_PORT_HOST) - * @param slave_addr Slave address of chip to access on I2C bus - * @param out Buffer containing bytes to output - * @param out_bytes Number of bytes to send (must be >0) - * @param in Buffer to place input bytes - * @param in_bytes Number of bytse to receive - * @return 0 if ok, else ER_ERROR... - */ -static int i2c_xfer(int port, int slave_addr, uint8_t *out, int out_bytes, - uint8_t *in, int in_bytes) +int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes, + uint8_t *in, int in_bytes, int flags) { int rv; - ASSERT(out && out_bytes); - ASSERT(in || !in_bytes); + /* TODO: support start/stop flags */ - disable_sleep(SLEEP_MASK_I2C); - mutex_lock(&i2c_mutex); + ASSERT(out || !out_bytes); + ASSERT(in || !in_bytes); - if (i2c_claim(port)) { - rv = EC_ERROR_BUSY; - goto err_claim; - } + if (i2c_claim(port)) + return EC_ERROR_BUSY; disable_i2c_interrupt(port); @@ -843,58 +827,25 @@ static int i2c_xfer(int port, int slave_addr, uint8_t *out, int out_bytes, i2c_release(port); -err_claim: - mutex_unlock(&i2c_mutex); - enable_sleep(SLEEP_MASK_I2C); - - return rv; -} - -int i2c_read16(int port, int slave_addr, int offset, int *data) -{ - uint8_t reg, buf[2]; - int rv; - - reg = offset & 0xff; - rv = i2c_xfer(port, slave_addr, ®, 1, buf, 2); - - *data = (buf[1] << 8) | buf[0]; - return rv; } -int i2c_write16(int port, int slave_addr, int offset, int data) -{ - uint8_t buf[3]; - - buf[0] = offset & 0xff; - buf[1] = data & 0xff; - buf[2] = (data >> 8) & 0xff; - - return i2c_xfer(port, slave_addr, buf, sizeof(buf), NULL, 0); -} - -int i2c_read8(int port, int slave_addr, int offset, int *data) +int i2c_get_line_levels(int port) { - uint8_t reg, buf[1]; - int rv; - - reg = offset & 0xff; - rv = i2c_xfer(port, slave_addr, ®, 1, buf, 1); - - *data = buf[0]; - - return rv; -} + enum gpio_signal sda, scl; -int i2c_write8(int port, int slave_addr, int offset, int data) -{ - uint8_t buf[2]; + ASSERT(port == I2C1 || port == I2C2); - buf[0] = offset & 0xff; - buf[1] = data & 0xff; + if (port == I2C1) { + sda = GPIO_I2C1_SDA; + scl = GPIO_I2C1_SCL; + } else { + sda = GPIO_I2C2_SDA; + scl = GPIO_I2C2_SCL; + } - return i2c_xfer(port, slave_addr, buf, sizeof(buf), NULL, 0); + return (gpio_get_level(sda) ? I2C_LINE_SDA_HIGH : 0) | + (gpio_get_level(scl) ? I2C_LINE_SCL_HIGH : 0); } int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data, @@ -902,23 +853,32 @@ int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data, { int rv; uint8_t reg, block_length; + + /* + * TODO: when i2c_xfer() supports start/stop bits, won't need a temp + * buffer, and this code can merge with the LM4 implementation and + * move to i2c_common.c. + */ uint8_t buffer[SMBUS_MAX_BLOCK + 1]; if ((len <= 0) || (len > SMBUS_MAX_BLOCK)) return EC_ERROR_INVAL; - reg = offset; - rv = i2c_xfer(port, slave_addr, ®, 1, buffer, SMBUS_MAX_BLOCK + 1); - if (rv) - return rv; + i2c_lock(port, 1); - /* the length of the block is the first byte of the returned buffer */ - block_length = MIN(buffer[0], len - 1); - buffer[block_length + 1] = 0; - - memcpy(data, buffer+1, block_length + 1); + reg = offset; + rv = i2c_xfer(port, slave_addr, ®, 1, buffer, SMBUS_MAX_BLOCK + 1, + I2C_XFER_SINGLE); + if (rv == EC_SUCCESS) { + /* Block length is the first byte of the returned buffer */ + block_length = MIN(buffer[0], len - 1); + buffer[block_length + 1] = 0; + + memcpy(data, buffer+1, block_length + 1); + } - return EC_SUCCESS; + i2c_lock(port, 0); + return rv; } /*****************************************************************************/ diff --git a/common/i2c_common.c b/common/i2c_common.c index 28779af945..24159c0b16 100644 --- a/common/i2c_common.c +++ b/common/i2c_common.c @@ -1,15 +1,123 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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 host commands for Chrome EC */ +/* I2C cross-platform code for Chrome EC */ +#include "clock.h" +#include "console.h" #include "host_command.h" #include "i2c.h" #include "system.h" +#include "task.h" +#include "util.h" +#include "watchdog.h" -int i2c_command_read(struct host_cmd_handler_args *args) +extern const struct i2c_port_t i2c_ports[I2C_PORTS_USED]; + +static struct mutex port_mutex[I2C_PORT_COUNT]; + +void i2c_lock(int port, int lock) +{ + if (lock) { +#ifdef CHIP_stm32 + /* Don't allow deep sleep when I2C port is locked */ + disable_sleep(SLEEP_MASK_I2C); +#endif + mutex_lock(port_mutex + port); + } else { + mutex_unlock(port_mutex + port); +#ifdef CHIP_stm32 + /* Allow deep sleep again after I2C port is unlocked */ + enable_sleep(SLEEP_MASK_I2C); +#endif + } +} + +int i2c_read16(int port, int slave_addr, int offset, int *data) +{ + int rv; + uint8_t reg, buf[2]; + + reg = offset & 0xff; + /* I2C read 16-bit word: transmit 8-bit offset, and read 16bits */ + i2c_lock(port, 1); + rv = i2c_xfer(port, slave_addr, ®, 1, buf, 2, I2C_XFER_SINGLE); + i2c_lock(port, 0); + + if (rv) + return rv; + + if (slave_addr & I2C_FLAG_BIG_ENDIAN) + *data = ((int)buf[0] << 8) | buf[1]; + else + *data = ((int)buf[1] << 8) | buf[0]; + + return EC_SUCCESS; +} + +int i2c_write16(int port, int slave_addr, int offset, int data) +{ + int rv; + uint8_t buf[3]; + + buf[0] = offset & 0xff; + + if (slave_addr & I2C_FLAG_BIG_ENDIAN) { + buf[1] = (data >> 8) & 0xff; + buf[2] = data & 0xff; + } else { + buf[1] = data & 0xff; + buf[2] = (data >> 8) & 0xff; + } + + i2c_lock(port, 1); + rv = i2c_xfer(port, slave_addr, buf, 3, NULL, 0, I2C_XFER_SINGLE); + i2c_lock(port, 0); + + return rv; +} + +int i2c_read8(int port, int slave_addr, int offset, int *data) +{ + int rv; + /* We use buf[1] here so it's aligned for DMA on STM32 */ + uint8_t reg, buf[1]; + + reg = offset; + + i2c_lock(port, 1); + rv = i2c_xfer(port, slave_addr, ®, 1, buf, 1, I2C_XFER_SINGLE); + i2c_lock(port, 0); + + if (!rv) + *data = buf[0]; + + return rv; +} + +int i2c_write8(int port, int slave_addr, int offset, int data) +{ + int rv; + uint8_t buf[2]; + + buf[0] = offset; + buf[1] = data; + + i2c_lock(port, 1); + rv = i2c_xfer(port, slave_addr, buf, 2, 0, 0, I2C_XFER_SINGLE); + i2c_lock(port, 0); + + return rv; +} + +/*****************************************************************************/ +/* Host commands */ + +/* TODO: replace with single I2C passthru command */ + +static int i2c_command_read(struct host_cmd_handler_args *args) { const struct ec_params_i2c_read *p = args->params; struct ec_response_i2c_read *r = args->response; @@ -32,7 +140,7 @@ int i2c_command_read(struct host_cmd_handler_args *args) } DECLARE_HOST_COMMAND(EC_CMD_I2C_READ, i2c_command_read, EC_VER_MASK(0)); -int i2c_command_write(struct host_cmd_handler_args *args) +static int i2c_command_write(struct host_cmd_handler_args *args) { const struct ec_params_i2c_write *p = args->params; int rv = -1; @@ -51,3 +159,62 @@ int i2c_command_write(struct host_cmd_handler_args *args) return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_I2C_WRITE, i2c_command_write, EC_VER_MASK(0)); + +/*****************************************************************************/ +/* Console commands */ + +static void scan_bus(int port, const char *desc) +{ + int a; + uint8_t tmp; + + ccprintf("Scanning %d %s", port, desc); + + /* Don't scan a busy port, since reads will just fail / time out */ + a = i2c_get_line_levels(port); + if (a != I2C_LINE_IDLE) { + ccprintf(": port busy (SDA=%d, SCL=%d)\n", + (a & I2C_LINE_SDA_HIGH) ? 1 : 0, + (a & I2C_LINE_SCL_HIGH) ? 1 : 0); + return; + } + + i2c_lock(port, 1); + + for (a = 0; a < 0x100; a += 2) { + watchdog_reload(); /* Otherwise a full scan trips watchdog */ + ccputs("."); + +#ifdef CHIP_lm4 + /* Do a single read */ + if (!i2c_xfer(port, a, NULL, 0, &tmp, 1, I2C_XFER_SINGLE)) +#else + /* + * Hope that address 0 exists, because the i2c_xfer() + * implementation on STM32 can't read a byte without writing + * one first. + * + * TODO: remove when that limitation is fixed. + */ + tmp = 0; + if (!i2c_xfer(port, a, &tmp, 1, &tmp, 1, I2C_XFER_SINGLE)) +#endif + ccprintf("\n 0x%02x", a); + } + + i2c_lock(port, 0); + ccputs("\n"); +} + +static int command_scan(int argc, char **argv) +{ + int i; + + for (i = 0; i < I2C_PORTS_USED; i++) + scan_bus(i2c_ports[i].port, i2c_ports[i].name); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(i2cscan, command_scan, + NULL, + "Scan I2C ports for devices", + NULL); diff --git a/include/i2c.h b/include/i2c.h index d14008a2d0..86535c1fa8 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -20,6 +20,47 @@ struct i2c_port_t { int kbps; /* Speed in kbps */ }; +/* Flags for i2c_xfer() */ +#define I2C_XFER_START (1 << 0) /* Start smbus session from idle state */ +#define I2C_XFER_STOP (1 << 1) /* Terminate smbus session with stop bit */ +#define I2C_XFER_SINGLE (I2C_XFER_START | I2C_XFER_STOP) /* One transaction */ + +/** + * Transmit one block of raw data, then receive one block of raw data. + * + * This is a low-level platform-dependent function used by the other functions + * below. It must be called between i2c_lock(port, 1) and i2c_lock(port, 0). + * + * @param port Port to access + * @param slave_addr Slave device address + * @param out Data to send + * @param out_size Number of bytes to send + * @param in Destination buffer for received data + * @param in_size Number of bytes to receive + * @param flags Flags (see I2C_XFER_* above) + * @return EC_SUCCESS, or non-zero if error. + */ +int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, + uint8_t *in, int in_size, int flags); + +#define I2C_LINE_SCL_HIGH (1 << 0) +#define I2C_LINE_SDA_HIGH (1 << 1) +#define I2C_LINE_IDLE (I2C_LINE_SCL_HIGH | I2C_LINE_SDA_HIGH) + +/** + * Return raw I/O line levels (I2C_LINE_*) for a port. + * + * @param port Port to check + */ +int i2c_get_line_levels(int port); + +/** + * Lock / unlock an I2C port. + * @param port Port to lock + * @param lock 1 to lock, 0 to unlock + */ +void i2c_lock(int port, int lock); + /* Read a 16-bit register from the slave at 8-bit slave address , at * the specified 8-bit in the slave's address space. */ int i2c_read16(int port, int slave_addr, int offset, int* data); -- cgit v1.2.1