summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Cernekee <cernekee@chromium.org>2015-08-16 14:37:48 -0700
committerChromeOS bot <3su6n15k.default@developer.gserviceaccount.com>2015-08-26 23:11:30 +0000
commitc1f7a09cfd933beb2acf0d8142e7393486619f1b (patch)
tree5abaad0feb7c1735e5a0c694c244109de64816d6
parent84dbc0f161cee9080f90762160ea9cf00301dde7 (diff)
downloadchrome-ec-c1f7a09cfd933beb2acf0d8142e7393486619f1b.tar.gz
lm4: Backport i2c utility functions
Add support for raw mode and for toggling scl/sda manually. This is not an exact match for the code in ToT because a lot of infrastructure is missing in the old link branch, particularly gpio_config_pins and common/{i2c,gpio}.c. BUG=chromium:458878 BRANCH=link TEST=compile-tested only Change-Id: I3ffea8aeec0c87d849bd9c3ac687542d6f2013dd Signed-off-by: Kevin Cernekee <cernekee@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/295096 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--chip/lm4/i2c.c166
-rw-r--r--include/i2c.h71
2 files changed, 237 insertions, 0 deletions
diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c
index a789478e4b..dc07a77636 100644
--- a/chip/lm4/i2c.c
+++ b/chip/lm4/i2c.c
@@ -318,6 +318,172 @@ exit:
return rv;
}
+int get_sda_from_i2c_port(int port, enum gpio_signal *sda)
+{
+ int i;
+
+ /* Find the matching port in i2c_ports[] table. */
+ for (i = 0; i < I2C_PORTS_USED; i++) {
+ if (i2c_ports[i].port == port)
+ break;
+ }
+
+ /* Crash if the port given is not in the i2c_ports[] table. */
+ ASSERT(i != I2C_PORTS_USED);
+
+ /* Check if the SCL and SDA pins have been defined for this port. */
+ if (i2c_ports[i].scl == 0 && i2c_ports[i].sda == 0)
+ return EC_ERROR_INVAL;
+
+ *sda = i2c_ports[i].sda;
+ return EC_SUCCESS;
+}
+
+int get_scl_from_i2c_port(int port, enum gpio_signal *scl)
+{
+ int i;
+
+ /* Find the matching port in i2c_ports[] table. */
+ for (i = 0; i < I2C_PORTS_USED; i++) {
+ if (i2c_ports[i].port == port)
+ break;
+ }
+
+ /* Crash if the port given is not in the i2c_ports[] table. */
+ ASSERT(i != I2C_PORTS_USED);
+
+ /* Check if the SCL and SDA pins have been defined for this port. */
+ if (i2c_ports[i].scl == 0 && i2c_ports[i].sda == 0)
+ return EC_ERROR_INVAL;
+
+ *scl = i2c_ports[i].scl;
+ return EC_SUCCESS;
+}
+
+void i2c_raw_set_scl(int port, int level)
+{
+ enum gpio_signal g;
+
+ if (get_scl_from_i2c_port(port, &g) == EC_SUCCESS)
+ gpio_set_level(g, level);
+}
+
+void i2c_raw_set_sda(int port, int level)
+{
+ enum gpio_signal g;
+
+ if (get_sda_from_i2c_port(port, &g) == EC_SUCCESS)
+ gpio_set_level(g, level);
+}
+
+int i2c_raw_get_scl(int port)
+{
+ enum gpio_signal g;
+ int ret;
+
+ /* If no SCL pin defined for this port, then return 1 to appear idle. */
+ if (get_scl_from_i2c_port(port, &g) != EC_SUCCESS)
+ return 1;
+
+ /* If we are driving the pin low, it must be low. */
+ if (gpio_get_level(g) == 0)
+ return 0;
+
+ /*
+ * Otherwise, we need to toggle it to an input to read the true pin
+ * state.
+ */
+ gpio_set_flags(g, GPIO_INPUT);
+ ret = gpio_get_level(g);
+ gpio_set_flags(g, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
+
+ return ret;
+}
+
+int i2c_raw_get_sda(int port)
+{
+ enum gpio_signal g;
+ int ret;
+
+ /* If no SDA pin defined for this port, then return 1 to appear idle. */
+ if (get_sda_from_i2c_port(port, &g) != EC_SUCCESS)
+ return 1;
+
+ /* If we are driving the pin low, it must be low. */
+ if (gpio_get_level(g) == 0)
+ return 0;
+
+ /*
+ * Otherwise, we need to toggle it to an input to read the true pin
+ * state.
+ */
+ gpio_set_flags(g, GPIO_INPUT);
+ ret = gpio_get_level(g);
+ gpio_set_flags(g, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
+
+ return ret;
+}
+
+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_raw_mode(int port, int enable)
+{
+ enum gpio_signal sda, scl;
+ static struct mutex raw_mode_mutex;
+
+ /* Get the SDA and SCL pins for this port. If none, then return. */
+ if (get_sda_from_i2c_port(port, &sda) != EC_SUCCESS)
+ return EC_ERROR_INVAL;
+ if (get_scl_from_i2c_port(port, &scl) != EC_SUCCESS)
+ return EC_ERROR_INVAL;
+
+ if (enable) {
+ /*
+ * Lock access to raw mode functionality. Note, this is
+ * necessary because when we exit raw mode, we put all I2C
+ * ports into normal mode. This means that if another port
+ * is using the raw mode capabilities, that port will be
+ * re-configured from underneath it.
+ */
+ mutex_lock(&raw_mode_mutex);
+
+ /*
+ * To enable raw mode, take out of alternate function mode and
+ * set the flags to open drain output.
+ */
+ gpio_set_alternate_function(gpio_list[sda].port,
+ gpio_list[sda].mask, 0);
+ gpio_set_alternate_function(gpio_list[scl].port,
+ gpio_list[scl].mask, 0);
+
+ gpio_set_flags(scl, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
+ gpio_set_level(scl, 1);
+ gpio_set_flags(sda, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
+ gpio_set_level(sda, 1);
+ } else {
+ /*
+ * Configure the I2C pins to exit raw mode and return
+ * to normal mode.
+ */
+ gpio_set_alternate_function(gpio_list[sda].port,
+ gpio_list[sda].mask, 3);
+ gpio_set_alternate_function(gpio_list[scl].port,
+ gpio_list[scl].mask, 3);
+
+ gpio_set_flags(scl, GPIO_OUTPUT);
+ gpio_set_flags(sda, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
+
+ /* Unlock mutex, allow other I2C busses to use raw mode. */
+ mutex_unlock(&raw_mode_mutex);
+ return EC_SUCCESS;
+ }
+
+ return EC_SUCCESS;
+}
static int i2c_freq_changed(void)
{
diff --git a/include/i2c.h b/include/i2c.h
index a707e515ce..ff91b28b52 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -22,6 +22,77 @@ struct i2c_port_t {
enum gpio_signal sda; /* Port SDA GPIO line */
};
+#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 when port is in alternate
+ * function mode.
+ *
+ * @param port Port to check
+ */
+int i2c_get_line_levels(int port);
+
+/**
+ * Get GPIO pin for I2C SCL from the i2c port number
+ *
+ * @param port I2C port number
+ * @param sda Pointer to gpio signal to store the SCL gpio at
+ * @return EC_SUCCESS if a valid GPIO point is found, EC_ERROR_INVAL if not
+ */
+int get_scl_from_i2c_port(int port, enum gpio_signal *scl);
+
+/**
+ * Get GPIO pin for I2C SDA from the i2c port number
+ *
+ * @param port I2C port number
+ * @param sda Pointer to gpio signal to store the SDA gpio at
+ * @return EC_SUCCESS if a valid GPIO point is found, EC_ERROR_INVAL if not
+ */
+int get_sda_from_i2c_port(int port, enum gpio_signal *sda);
+
+/**
+ * Get the state of the SCL pin when port is not in alternate function mode.
+ *
+ * @param port I2C port of interest
+ * @return State of SCL pin
+ */
+int i2c_raw_get_scl(int port);
+
+/**
+ * Get the state of the SDA pin when port is not in alternate function mode.
+ *
+ * @param port I2C port of interest
+ * @return State of SDA pin
+ */
+int i2c_raw_get_sda(int port);
+
+/**
+ * Set the state of the SCL pin.
+ *
+ * @param port I2C port of interest
+ * @param level State to set SCL pin to
+ */
+void i2c_raw_set_scl(int port, int level);
+
+/**
+ * Set the state of the SDA pin.
+ *
+ * @param port I2C port of interest
+ * @param level State to set SDA pin to
+ */
+void i2c_raw_set_sda(int port, int level);
+
+/**
+ * Toggle the I2C pins into or out of raw / big-bang mode.
+ *
+ * @param port I2C port of interest
+ * @param enable Flag to enable raw mode or disable it
+ * @return EC_SUCCESS if successful
+ */
+int i2c_raw_mode(int port, int enable);
+
/* 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);