summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJun Lin <CHLin56@nuvoton.com>2021-09-15 18:10:31 +0800
committerCommit Bot <commit-bot@chromium.org>2021-10-05 21:11:46 +0000
commit6c04fcf77da3166983496eadf987d45400fdfef4 (patch)
treea80c2b079d63537aa40e87c1caa6bca0a4f7563a
parent0241b0f8f9c59cf7dfc43d3cbbb3572b021144e3 (diff)
downloadchrome-ec-6c04fcf77da3166983496eadf987d45400fdfef4.tar.gz
i2c: Use bitbang mode for pre-task i2c transactions
In Intel ADL+_RVP, the keyboard is scanned by a discrete I/O expander IC and it is connected to EC via the I2C interface. EC needs to initialize the IC via I2C transaction before the task scheduling starts. It may cause the system panic if the EC's I2C driver is implemented by task-event-based. With this CL, if any of the I2C ports that need pre-task I2C transaction are defined as bitbang ports along with regular I2C ports will switch to the bitbang (GPIO) mode before the task starts and will again switch back to event based I2C upon task initialization. BRANCH=none BUG=b:199374643 TEST=issue an I2C transaction before the task scheduling starts; make sure the transaction success without panic on npcx9_evb. TEST=pass "make buildall" Signed-off-by: Jun Lin <CHLin56@nuvoton.com> Change-Id: I65460d2b612328f25bce60561bbb82995dd1cfdf Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3161869 Tested-by: Vijay Hiremath <vijay.p.hiremath@intel.com> Reviewed-by: Keith Short <keithshort@chromium.org> Commit-Queue: Keith Short <keithshort@chromium.org>
-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);