diff options
author | Tristan Honscheid <honscheid@google.com> | 2022-09-26 18:25:44 -0600 |
---|---|---|
committer | Chromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2022-10-04 19:18:36 +0000 |
commit | f079a846def4856bb955d3b038171ff44d4887bb (patch) | |
tree | 7eea3b4aeb6bedc387e54b20073832c1ad8f2487 | |
parent | 23b4ab16f3f7e8f11f9198cfc35a722a2eaa3ef7 (diff) | |
download | chrome-ec-f079a846def4856bb955d3b038171ff44d4887bb.tar.gz |
zephyr: tests: Move non-Zephyr code out of i2c_controller.c
This CL moves 76 SLOC that is not used in Zephyr out of i2c_controller.c
and in to i2c_controller_cros_ec.c. Add a guard to that file so it
cannot be built with CONFIG_ZEPHYR defined.
BRANCH=None
BUG=b:248311206
TEST=make BOARD=brya -j
LOW_COVERAGE_REASON=No new code, moving non-Zephyr code to new file
Signed-off-by: Tristan Honscheid <honscheid@google.com>
Change-Id: I3f71c15baf87d2e59851022d784f2257521cfef6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3922974
Reviewed-by: Yuval Peress <peress@google.com>
Code-Coverage: Zoss <zoss-cl-coverage@prod.google.com>
-rw-r--r-- | common/i2c_controller.c | 218 | ||||
-rw-r--r-- | common/i2c_controller_cros_ec.c | 228 |
2 files changed, 228 insertions, 218 deletions
diff --git a/common/i2c_controller.c b/common/i2c_controller.c index e2f4149b16..b51f726369 100644 --- a/common/i2c_controller.c +++ b/common/i2c_controller.c @@ -30,13 +30,6 @@ #include "i2c/i2c.h" #endif /* CONFIG_ZEPHYR */ -/* Delay for bitbanging i2c corresponds roughly to 100kHz. */ -#define I2C_BITBANG_DELAY_US 5 - -/* Number of attempts to unwedge each pin. */ -#define UNWEDGE_SCL_ATTEMPTS 10 -#define UNWEDGE_SDA_ATTEMPTS 3 - #define CPUTS(outstr) cputs(CC_I2C, outstr) #define CPRINTS(format, args...) cprints(CC_I2C, format, ##args) #define CPRINTF(format, args...) cprintf(CC_I2C, format, ##args) @@ -866,217 +859,6 @@ int i2c_write_block(const int port, const uint16_t addr_flags, int offset, return rv; } -#ifndef CONFIG_ZEPHYR -int get_sda_from_i2c_port(int port, enum gpio_signal *sda) -{ - const struct i2c_port_t *i2c_port = get_i2c_port(port); - - /* Crash if the port given is not in the i2c_ports[] table. */ - ASSERT(i2c_port); - - /* Check if the SCL and SDA pins have been defined for this port. */ - if (i2c_port->scl == 0 && i2c_port->sda == 0) - return EC_ERROR_INVAL; - - *sda = i2c_port->sda; - return EC_SUCCESS; -} - -int get_scl_from_i2c_port(int port, enum gpio_signal *scl) -{ - const struct i2c_port_t *i2c_port = get_i2c_port(port); - - /* Crash if the port given is not in the i2c_ports[] table. */ - ASSERT(i2c_port); - - /* Check if the SCL and SDA pins have been defined for this port. */ - if (i2c_port->scl == 0 && i2c_port->sda == 0) - return EC_ERROR_INVAL; - - *scl = i2c_port->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_mode(int port, int enable) -{ - enum gpio_signal sda, scl; - int ret_sda, ret_scl; - - /* 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) { - int raw_gpio_mode_flags = GPIO_ODR_HIGH; - - /* If the CLK line is 1.8V, then ensure we set 1.8V mode */ - if ((gpio_list + scl)->flags & GPIO_SEL_1P8V) - raw_gpio_mode_flags |= GPIO_SEL_1P8V; - - /* - * To enable raw mode, take out of alternate function mode and - * set the flags to open drain output. - */ - ret_sda = gpio_config_pin(MODULE_I2C, sda, 0); - ret_scl = gpio_config_pin(MODULE_I2C, scl, 0); - - gpio_set_flags(scl, raw_gpio_mode_flags); - gpio_set_flags(sda, raw_gpio_mode_flags); - } else { - /* - * Configure the I2C pins to exit raw mode and return - * to normal mode. - */ - ret_sda = gpio_config_pin(MODULE_I2C, sda, 1); - ret_scl = gpio_config_pin(MODULE_I2C, scl, 1); - } - - return ret_sda == EC_SUCCESS ? ret_scl : ret_sda; -} - -/* - * Unwedge the i2c bus for the given port. - * - * Some devices on our i2c busses keep power even if we get a reset. That - * means that they could be part way through a transaction and could be - * driving the bus in a way that makes it hard for us to talk on the bus. - * ...or they might listen to the next transaction and interpret it in a - * weird way. - * - * Note that devices could be in one of several states: - * - If a device got interrupted in a write transaction it will be watching - * for additional data to finish its write. It will probably be looking to - * ack the data (drive the data line low) after it gets everything. - * - If a device got interrupted while responding to a register read, it will - * be watching for clocks and will drive data out when it sees clocks. At - * the moment it might be trying to send out a 1 (so both clock and data - * may be high) or it might be trying to send out a 0 (so it's driving data - * low). - * - * We attempt to unwedge the bus by doing: - * - If SCL is being held low, then a peripheral is clock extending. The only - * thing we can do is try to wait until the peripheral stops clock extending. - * - Otherwise, we will toggle the clock until the peripheral releases the SDA - * line. Once the SDA line is released, try to send a STOP bit. Rinse and - * repeat until either the bus is normal, or we run out of attempts. - * - * Note this should work for most devices, but depending on the peripheral's - * i2c state machine, it may not be possible to unwedge the bus. - */ -int i2c_unwedge(int port) -{ - int i, j; - int ret = EC_SUCCESS; - -#ifdef CONFIG_I2C_BUS_MAY_BE_UNPOWERED - /* - * Don't try to unwedge the port if we know it's unpowered; it's futile. - */ - if (!board_is_i2c_port_powered(port)) { - CPRINTS("Skipping i2c unwedge, bus not powered."); - return EC_ERROR_NOT_POWERED; - } -#endif /* CONFIG_I2C_BUS_MAY_BE_UNPOWERED */ - - /* Try to put port in to raw bit bang mode. */ - if (i2c_raw_mode(port, 1) != EC_SUCCESS) - return EC_ERROR_UNKNOWN; - - /* - * If clock is low, wait for a while in case of clock stretched - * by a peripheral. - */ - if (!i2c_raw_get_scl(port)) { - for (i = 0;; i++) { - if (i >= UNWEDGE_SCL_ATTEMPTS) { - /* - * If we get here, a peripheral is holding the - * clock low and there is nothing we can do. - */ - CPRINTS("I2C%d unwedge failed, " - "SCL is held low", - port); - ret = EC_ERROR_UNKNOWN; - goto unwedge_done; - } - udelay(I2C_BITBANG_DELAY_US); - if (i2c_raw_get_scl(port)) - break; - } - } - - if (i2c_raw_get_sda(port)) - goto unwedge_done; - - CPRINTS("I2C%d unwedge called with SDA held low", port); - - /* Keep trying to unwedge the SDA line until we run out of attempts. */ - for (i = 0; i < UNWEDGE_SDA_ATTEMPTS; i++) { - /* Drive the clock high. */ - i2c_raw_set_scl(port, 1); - udelay(I2C_BITBANG_DELAY_US); - - /* - * Clock through the problem by clocking out 9 bits. If - * peripheral releases the SDA line, then we can stop clocking - * bits and send a STOP. - */ - for (j = 0; j < 9; j++) { - if (i2c_raw_get_sda(port)) - break; - - i2c_raw_set_scl(port, 0); - udelay(I2C_BITBANG_DELAY_US); - i2c_raw_set_scl(port, 1); - udelay(I2C_BITBANG_DELAY_US); - } - - /* Take control of SDA line and issue a STOP command. */ - i2c_raw_set_sda(port, 0); - udelay(I2C_BITBANG_DELAY_US); - i2c_raw_set_sda(port, 1); - udelay(I2C_BITBANG_DELAY_US); - - /* Check if the bus is unwedged. */ - if (i2c_raw_get_sda(port) && i2c_raw_get_scl(port)) - break; - } - - if (!i2c_raw_get_sda(port)) { - CPRINTS("I2C%d unwedge failed, SDA still low", port); - ret = EC_ERROR_UNKNOWN; - } - if (!i2c_raw_get_scl(port)) { - CPRINTS("I2C%d unwedge failed, SCL still low", port); - ret = EC_ERROR_UNKNOWN; - } - -unwedge_done: - /* Take port out of raw bit bang mode. */ - i2c_raw_mode(port, 0); - - return ret; -} -#endif /* !CONFIG_ZEPHYR */ - int i2c_freq_to_khz(enum i2c_freq freq) { switch (freq) { diff --git a/common/i2c_controller_cros_ec.c b/common/i2c_controller_cros_ec.c index e8c25e14ca..aafeb504ce 100644 --- a/common/i2c_controller_cros_ec.c +++ b/common/i2c_controller_cros_ec.c @@ -3,6 +3,10 @@ * found in the LICENSE file. */ +#include "builtin/assert.h" +#include "system.h" +#include "gpio.h" + #include "i2c_bitbang.h" #include "i2c_private.h" #include "i2c.h" @@ -15,6 +19,230 @@ * (CrOS EC) builds. */ +#ifdef CONFIG_ZEPHYR +#error "Don't use this source in Zephyr builds" +#endif + +/* Delay for bitbanging i2c corresponds roughly to 100kHz. */ +#define I2C_BITBANG_DELAY_US 5 + +/* Number of attempts to unwedge each pin. */ +#define UNWEDGE_SCL_ATTEMPTS 10 +#define UNWEDGE_SDA_ATTEMPTS 3 + +#define CPUTS(outstr) cputs(CC_I2C, outstr) +#define CPRINTS(format, args...) cprints(CC_I2C, format, ##args) +#define CPRINTF(format, args...) cprintf(CC_I2C, format, ##args) + +int get_sda_from_i2c_port(int port, enum gpio_signal *sda) +{ + const struct i2c_port_t *i2c_port = get_i2c_port(port); + + /* Crash if the port given is not in the i2c_ports[] table. */ + ASSERT(i2c_port); + + /* Check if the SCL and SDA pins have been defined for this port. */ + if (i2c_port->scl == 0 && i2c_port->sda == 0) + return EC_ERROR_INVAL; + + *sda = i2c_port->sda; + return EC_SUCCESS; +} + +int get_scl_from_i2c_port(int port, enum gpio_signal *scl) +{ + const struct i2c_port_t *i2c_port = get_i2c_port(port); + + /* Crash if the port given is not in the i2c_ports[] table. */ + ASSERT(i2c_port); + + /* Check if the SCL and SDA pins have been defined for this port. */ + if (i2c_port->scl == 0 && i2c_port->sda == 0) + return EC_ERROR_INVAL; + + *scl = i2c_port->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_mode(int port, int enable) +{ + enum gpio_signal sda, scl; + int ret_sda, ret_scl; + + /* 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) { + int raw_gpio_mode_flags = GPIO_ODR_HIGH; + + /* If the CLK line is 1.8V, then ensure we set 1.8V mode */ + if ((gpio_list + scl)->flags & GPIO_SEL_1P8V) + raw_gpio_mode_flags |= GPIO_SEL_1P8V; + + /* + * To enable raw mode, take out of alternate function mode and + * set the flags to open drain output. + */ + ret_sda = gpio_config_pin(MODULE_I2C, sda, 0); + ret_scl = gpio_config_pin(MODULE_I2C, scl, 0); + + gpio_set_flags(scl, raw_gpio_mode_flags); + gpio_set_flags(sda, raw_gpio_mode_flags); + } else { + /* + * Configure the I2C pins to exit raw mode and return + * to normal mode. + */ + ret_sda = gpio_config_pin(MODULE_I2C, sda, 1); + ret_scl = gpio_config_pin(MODULE_I2C, scl, 1); + } + + return ret_sda == EC_SUCCESS ? ret_scl : ret_sda; +} + +/* + * Unwedge the i2c bus for the given port. + * + * Some devices on our i2c busses keep power even if we get a reset. That + * means that they could be part way through a transaction and could be + * driving the bus in a way that makes it hard for us to talk on the bus. + * ...or they might listen to the next transaction and interpret it in a + * weird way. + * + * Note that devices could be in one of several states: + * - If a device got interrupted in a write transaction it will be watching + * for additional data to finish its write. It will probably be looking to + * ack the data (drive the data line low) after it gets everything. + * - If a device got interrupted while responding to a register read, it will + * be watching for clocks and will drive data out when it sees clocks. At + * the moment it might be trying to send out a 1 (so both clock and data + * may be high) or it might be trying to send out a 0 (so it's driving data + * low). + * + * We attempt to unwedge the bus by doing: + * - If SCL is being held low, then a peripheral is clock extending. The only + * thing we can do is try to wait until the peripheral stops clock extending. + * - Otherwise, we will toggle the clock until the peripheral releases the SDA + * line. Once the SDA line is released, try to send a STOP bit. Rinse and + * repeat until either the bus is normal, or we run out of attempts. + * + * Note this should work for most devices, but depending on the peripheral's + * i2c state machine, it may not be possible to unwedge the bus. + */ +int i2c_unwedge(int port) +{ + int i, j; + int ret = EC_SUCCESS; + +#ifdef CONFIG_I2C_BUS_MAY_BE_UNPOWERED + /* + * Don't try to unwedge the port if we know it's unpowered; it's futile. + */ + if (!board_is_i2c_port_powered(port)) { + CPRINTS("Skipping i2c unwedge, bus not powered."); + return EC_ERROR_NOT_POWERED; + } +#endif /* CONFIG_I2C_BUS_MAY_BE_UNPOWERED */ + + /* Try to put port in to raw bit bang mode. */ + if (i2c_raw_mode(port, 1) != EC_SUCCESS) + return EC_ERROR_UNKNOWN; + + /* + * If clock is low, wait for a while in case of clock stretched + * by a peripheral. + */ + if (!i2c_raw_get_scl(port)) { + for (i = 0;; i++) { + if (i >= UNWEDGE_SCL_ATTEMPTS) { + /* + * If we get here, a peripheral is holding the + * clock low and there is nothing we can do. + */ + CPRINTS("I2C%d unwedge failed, " + "SCL is held low", + port); + ret = EC_ERROR_UNKNOWN; + goto unwedge_done; + } + udelay(I2C_BITBANG_DELAY_US); + if (i2c_raw_get_scl(port)) + break; + } + } + + if (i2c_raw_get_sda(port)) + goto unwedge_done; + + CPRINTS("I2C%d unwedge called with SDA held low", port); + + /* Keep trying to unwedge the SDA line until we run out of attempts. */ + for (i = 0; i < UNWEDGE_SDA_ATTEMPTS; i++) { + /* Drive the clock high. */ + i2c_raw_set_scl(port, 1); + udelay(I2C_BITBANG_DELAY_US); + + /* + * Clock through the problem by clocking out 9 bits. If + * peripheral releases the SDA line, then we can stop clocking + * bits and send a STOP. + */ + for (j = 0; j < 9; j++) { + if (i2c_raw_get_sda(port)) + break; + + i2c_raw_set_scl(port, 0); + udelay(I2C_BITBANG_DELAY_US); + i2c_raw_set_scl(port, 1); + udelay(I2C_BITBANG_DELAY_US); + } + + /* Take control of SDA line and issue a STOP command. */ + i2c_raw_set_sda(port, 0); + udelay(I2C_BITBANG_DELAY_US); + i2c_raw_set_sda(port, 1); + udelay(I2C_BITBANG_DELAY_US); + + /* Check if the bus is unwedged. */ + if (i2c_raw_get_sda(port) && i2c_raw_get_scl(port)) + break; + } + + if (!i2c_raw_get_sda(port)) { + CPRINTS("I2C%d unwedge failed, SDA still low", port); + ret = EC_ERROR_UNKNOWN; + } + if (!i2c_raw_get_scl(port)) { + CPRINTS("I2C%d unwedge failed, SCL still low", port); + ret = EC_ERROR_UNKNOWN; + } + +unwedge_done: + /* Take port out of raw bit bang mode. */ + i2c_raw_mode(port, 0); + + return ret; +} + #ifdef CONFIG_CMD_I2C_SCAN static void scan_bus(int port, const char *desc) { |