summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2013-04-26 14:36:38 -0700
committerChromeBot <chrome-bot@google.com>2013-04-30 11:45:52 -0700
commit0a6b7620d6b4ba1a50500a75db3e76162eac5ce0 (patch)
tree8f55cf2a2ee6469981bd27924349bf80de0f295e
parentc08e0ade765bf69fb9ab3f62305a84a4d3d34c1d (diff)
downloadchrome-ec-0a6b7620d6b4ba1a50500a75db3e76162eac5ce0.tar.gz
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 <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/49492 Reviewed-by: Simon Glass <sjg@chromium.org> Commit-Queue: Daniel Erat <derat@chromium.org> Commit-Queue: Simon Glass <sjg@chromium.org>
-rw-r--r--board/daisy/board.c6
-rw-r--r--board/daisy/board.h1
-rw-r--r--board/pit/board.c5
-rw-r--r--board/pit/board.h1
-rw-r--r--board/snow/board.c5
-rw-r--r--board/snow/board.h1
-rw-r--r--board/spring/board.c5
-rw-r--r--board/spring/board.h1
-rw-r--r--chip/lm4/config.h5
-rw-r--r--chip/lm4/i2c.c179
-rw-r--r--chip/stm32/config.h5
-rw-r--r--chip/stm32/i2c.c124
-rw-r--r--common/i2c_common.c175
-rw-r--r--include/i2c.h41
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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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 <slaveaddr>, at
* the specified 8-bit <offset> in the slave's address space. */
int i2c_read16(int port, int slave_addr, int offset, int* data);