From c1f7a09cfd933beb2acf0d8142e7393486619f1b Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Sun, 16 Aug 2015 14:37:48 -0700 Subject: 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 Reviewed-on: https://chromium-review.googlesource.com/295096 Reviewed-by: Randall Spangler --- chip/lm4/i2c.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/i2c.h | 71 ++++++++++++++++++++++++ 2 files changed, 237 insertions(+) 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 , 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