summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/i2c_bitbang.c13
-rw-r--r--common/i2c_controller.c19
-rw-r--r--common/main.c18
-rw-r--r--include/i2c_bitbang.h8
4 files changed, 54 insertions, 4 deletions
diff --git a/common/i2c_bitbang.c b/common/i2c_bitbang.c
index 86d76a8b47..9e961d730e 100644
--- a/common/i2c_bitbang.c
+++ b/common/i2c_bitbang.c
@@ -11,6 +11,7 @@
#include "util.h"
#define CPUTS(str) cputs(CC_I2C, str)
+#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)
static int started;
@@ -336,6 +337,18 @@ exit:
return err;
}
+void enable_i2c_raw_mode(bool enable)
+{
+ int i;
+
+ for (i = 0; i < i2c_bitbang_ports_used; i++) {
+ if (i2c_raw_mode(i2c_bitbang_ports[i].port, enable))
+ CPRINTS("I2C p%d: Failed to %s raw mode",
+ i2c_bitbang_ports[i].port,
+ enable ? "enable" : "disable");
+ }
+}
+
const struct i2c_drv bitbang_drv = {
.xfer = &i2c_bitbang_xfer
};
diff --git a/common/i2c_controller.c b/common/i2c_controller.c
index f2d1cf4a5f..92234bc600 100644
--- a/common/i2c_controller.c
+++ b/common/i2c_controller.c
@@ -100,13 +100,23 @@ const struct i2c_port_t *get_i2c_port(const int port)
{
int i;
- /* Find the matching port in i2c_ports[] table. */
- for (i = 0; i < i2c_ports_used; i++) {
- if (i2c_ports[i].port == port)
- return &i2c_ports[i];
+ /*
+ * If the EC's I2C driver implementation is task event based and the
+ * I2C is accessed before the task is initialized, it causes the system
+ * panic hence these I2C will fall back to bitbang mode if enabled at
+ * board level and will again switch back to event based I2C upon task
+ * initialization.
+ */
+ if (task_start_called()) {
+ /* Find the matching port in i2c_ports[] table. */
+ for (i = 0; i < i2c_ports_used; i++) {
+ if (i2c_ports[i].port == port)
+ return &i2c_ports[i];
+ }
}
if (IS_ENABLED(CONFIG_I2C_BITBANG)) {
+ /* Find the matching port in i2c_bitbang_ports[] table. */
for (i = 0; i < i2c_bitbang_ports_used; i++) {
if (i2c_bitbang_ports[i].port == port)
return &i2c_bitbang_ports[i];
@@ -137,6 +147,7 @@ __maybe_unused static int chip_i2c_xfer_with_notify(
* remove the flag so it won't confuse chip driver.
*/
no_pec_af &= ~I2C_FLAG_PEC;
+
if (i2c_port->drv)
ret = i2c_port->drv->xfer(i2c_port, no_pec_af,
out, out_size, in, in_size, flags);
diff --git a/common/main.c b/common/main.c
index d9fbe94a1e..0e18580373 100644
--- a/common/main.c
+++ b/common/main.c
@@ -19,6 +19,7 @@
#include "gpio.h"
#include "hooks.h"
#include "i2c.h"
+#include "i2c_bitbang.h"
#include "keyboard_scan.h"
#include "link_defs.h"
#include "lpc.h"
@@ -193,7 +194,15 @@ test_mockable __keep int main(void)
* pretty early, so let's initialize the controller now.
*/
i2c_init();
+
+ /*
+ * Enable I2C raw mode for the ports which need pre-task i2c
+ * transactions.
+ */
+ if (IS_ENABLED(CONFIG_I2C_BITBANG))
+ enable_i2c_raw_mode(true);
}
+
#ifdef HAS_TASK_KEYSCAN
keyboard_scan_init();
#endif
@@ -247,6 +256,15 @@ test_mockable __keep int main(void)
#endif /* !CONFIG_VBOOT_EFS && CONFIG_RWSIG && !HAS_TASK_RWSIG */
/*
+ * Disable I2C raw mode for the ports which needed pre-task i2c
+ * transactions as the task is about to start and the I2C can resume
+ * to event based transactions.
+ */
+ if (IS_ENABLED(CONFIG_I2C_BITBANG) &&
+ IS_ENABLED(CONFIG_I2C_CONTROLLER))
+ enable_i2c_raw_mode(false);
+
+ /*
* Print the init time. Not completely accurate because it can't take
* into account the time before timer_init(), but it'll at least catch
* the majority of the time.
diff --git a/include/i2c_bitbang.h b/include/i2c_bitbang.h
index f3c07baf76..d550f1a582 100644
--- a/include/i2c_bitbang.h
+++ b/include/i2c_bitbang.h
@@ -12,6 +12,14 @@ extern const struct i2c_drv bitbang_drv;
extern const struct i2c_port_t i2c_bitbang_ports[];
extern const unsigned int i2c_bitbang_ports_used;
+/**
+ * Enable I2C raw mode for the ports which need pre-task
+ * I2C transactions in bitbang mode.
+ *
+ * @param enable Enable/disable the I2C raw mode
+ */
+void enable_i2c_raw_mode(bool enable);
+
/* expose static functions for testing */
#ifdef TEST_BUILD
int bitbang_start_cond(const struct i2c_port_t *i2c_port);