ec: add a io-sweep function

Add a "ec io-sweep" function to test if a series of GPIO are connected
correctly to the EC.

BUG=none
TEST=ec io_sweep

Change-Id: I9cafe8d9e9c25f807f2f8fefeb6075adfd353550
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec-aic-tester/+/6182581
Tested-by: Fabio Baltieri <fabiobaltieri@google.com>
Commit-Queue: Keith Short <keithshort@chromium.org>
Reviewed-by: Keith Short <keithshort@chromium.org>
diff --git a/firmware/boards/google/ec_aic_tester/ec_aic_tester.dts b/firmware/boards/google/ec_aic_tester/ec_aic_tester.dts
index 97db69c..a5e247b 100644
--- a/firmware/boards/google/ec_aic_tester/ec_aic_tester.dts
+++ b/firmware/boards/google/ec_aic_tester/ec_aic_tester.dts
@@ -106,6 +106,43 @@
 	aic-pins {
 		compatible = "cros,aic-pins";
 		ec-rst-gpios = <&gpioe 3 GPIO_ACTIVE_LOW>;
+		io-gpios = <&gpioc 2 GPIO_ACTIVE_HIGH>, /* CCD_MODE_L */
+			   <&gpioc 4 GPIO_ACTIVE_HIGH>, /* EN_PP5000_FAN */
+			   <&gpioe 13 GPIO_ACTIVE_HIGH>, /* I2C_MECC_PDC4_INT_L */
+
+			   /* TODO: figure out why some signals are not getting
+			    * through on the NPCX AIC board
+			    */
+
+			   /* KSI */
+			   /* <&gpiok 0 GPIO_ACTIVE_HIGH>, */ /* MCU_OUT_KBD_SCANIN_A_ODL */
+			   <&gpiok 1 GPIO_ACTIVE_HIGH>, /* MCU_OUT_KBD_SCANIN_B_ODL */
+			   <&gpiok 2 GPIO_ACTIVE_HIGH>, /* MCU_OUT_KBD_SCANIN_C_ODL */
+			   /* <&gpiok 3 GPIO_ACTIVE_HIGH>, */ /* MCU_OUT_KBD_SCANIN_D_ODL */
+			   <&gpiok 4 GPIO_ACTIVE_HIGH>, /* MCU_OUT_KBD_SCANIN_E_ODL */
+			   <&gpiok 5 GPIO_ACTIVE_HIGH>, /* MCU_OUT_KBD_SCANIN_F_ODL */
+			   <&gpiok 6 GPIO_ACTIVE_HIGH>, /* MCU_OUT_KBD_SCANIN_G_ODL */
+			   <&gpiok 7 GPIO_ACTIVE_HIGH>, /* MCU_OUT_KBD_SCANIN_H_ODL */
+
+			   /* KSO */
+			   /* <&gpioj 0 GPIO_ACTIVE_HIGH>, */ /* MCU_IN_KBD_SCANOUT_A */
+			   /* <&gpioj 1 GPIO_ACTIVE_HIGH>, */ /* MCU_IN_KBD_SCANOUT_B */
+			   <&gpioj 2 GPIO_ACTIVE_HIGH>, /* MCU_IN_KBD_SCANOUT_C */
+			   <&gpioj 3 GPIO_ACTIVE_HIGH>, /* MCU_IN_KBD_SCANOUT_D */
+			   <&gpioj 4 GPIO_ACTIVE_HIGH>, /* MCU_IN_KBD_SCANOUT_E */
+			   <&gpioj 5 GPIO_ACTIVE_HIGH>, /* MCU_IN_KBD_SCANOUT_F */
+			   /* <&gpioj 6 GPIO_ACTIVE_HIGH>, */ /* MCU_IN_KBD_SCANOUT_G */
+			   /* <&gpioj 7 GPIO_ACTIVE_HIGH>, */ /* MCU_IN_KBD_SCANOUT_H */
+			   /* <&gpioj 8 GPIO_ACTIVE_HIGH>, */ /* MCU_IN_KBD_SCANOUT_J */
+			   /* <&gpioj 9 GPIO_ACTIVE_HIGH>, */ /* MCU_IN_KBD_SCANOUT_K */
+			   <&gpioj 10 GPIO_ACTIVE_HIGH>, /* MCU_IN_KBD_SCANOUT_L */
+			   <&gpioj 11 GPIO_ACTIVE_HIGH>, /* MCU_IN_KBD_SCANOUT_M */
+			   <&gpioj 12 GPIO_ACTIVE_HIGH>, /* MCU_IN_KBD_SCANOUT_N */
+			   <&gpioj 13 GPIO_ACTIVE_HIGH>, /* MCU_IN_KBD_SCANOUT_P */
+			   <&gpioj 14 GPIO_ACTIVE_HIGH>, /* MCU_IN_KBD_SCANOUT_Q */
+			   <&gpioj 15 GPIO_ACTIVE_HIGH>; /* MCU_IN_KBD_SCANOUT_R */
+			   /* <&gpioh 3 GPIO_ACTIVE_HIGH>, */ /* MCU_IN_KBD_SCANOUT_S */
+			   /* <&gpioj 4 GPIO_ACTIVE_HIGH>; */ /* MCU_IN_KBD_SCANOUT_T */
 	};
 
 	npcx-boot {
diff --git a/firmware/dts/bindings/cros,aic-pins.yaml b/firmware/dts/bindings/cros,aic-pins.yaml
index 4b57bb9..ab14aa4 100644
--- a/firmware/dts/bindings/cros,aic-pins.yaml
+++ b/firmware/dts/bindings/cros,aic-pins.yaml
@@ -11,3 +11,7 @@
   ec-rst-gpios:
     type: phandle-array
     required: true
+
+  io-gpios:
+    type: phandle-array
+    required: true
diff --git a/firmware/src/ec.c b/firmware/src/ec.c
index 23cf607..fd537d7 100644
--- a/firmware/src/ec.c
+++ b/firmware/src/ec.c
@@ -13,6 +13,7 @@
 #include <zephyr/kernel.h>
 #include <zephyr/logging/log.h>
 #include <zephyr/shell/shell.h>
+#include <zephyr/sys/util.h>
 
 LOG_MODULE_REGISTER(ec, LOG_LEVEL_INF);
 
@@ -22,6 +23,9 @@
 static const struct gpio_dt_spec ec_rst_gpio =
 	GPIO_DT_SPEC_INST_GET(0, ec_rst_gpios);
 
+static const struct gpio_dt_spec ec_io_gpio[] = { DT_INST_FOREACH_PROP_ELEM_SEP(
+	0, io_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, )) };
+
 static const struct device *reg_pp3300_mecc_core =
 	DEVICE_DT_GET(DT_NODELABEL(pp3300_mecc_core));
 static const struct device *reg_pp3300_mecc_z =
@@ -88,6 +92,75 @@
 INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_NODELABEL(gpio_keys)), button_input_cb,
 		      NULL);
 
+static int expect_pin(const struct shell *sh, uint8_t i)
+{
+	/* TODO: make it work with more than 32 pins */
+	uint32_t state = 0;
+	uint32_t expect = BIT(i);
+
+	for (uint8_t j = 0; j < ARRAY_SIZE(ec_io_gpio); j++) {
+		const struct gpio_dt_spec *gpio = &ec_io_gpio[j];
+		int val;
+
+		val = gpio_pin_get_dt(gpio);
+		if (val) {
+			state |= BIT(j);
+		}
+	}
+
+	if (state != expect) {
+		shell_print(sh, "unexpected state (%d) %08x != %08x", i, state,
+			    expect);
+		return -1;
+	}
+
+	shell_info(sh, "match state (%d) %08x", i, state);
+	return 0;
+}
+
+#define IO_SWEEP_TIMEOUT_MS 5000
+
+static int cmd_io_sweep(const struct shell *sh, size_t argc, char **argv)
+{
+	uint64_t start_time;
+	int ret;
+
+	for (uint8_t i = 0; i < ARRAY_SIZE(ec_io_gpio); i++) {
+		const struct gpio_dt_spec *gpio = &ec_io_gpio[i];
+
+		ret = gpio_pin_configure_dt(gpio, GPIO_INPUT);
+		if (ret < 0) {
+			LOG_ERR("gpio configuration failed: %d", ret);
+			return ret;
+		}
+	}
+
+	start_time = k_uptime_get();
+	for (uint8_t i = 0; i < ARRAY_SIZE(ec_io_gpio); i++) {
+		while (true) {
+			if (k_uptime_get() - start_time > IO_SWEEP_TIMEOUT_MS) {
+				goto timeout;
+			}
+
+			ret = expect_pin(sh, i);
+			if (ret < 0) {
+				k_sleep(K_MSEC(100));
+				continue;
+			}
+			break;
+		}
+	}
+
+	shell_info(sh, "sweep matched all pins successfully");
+
+	return 0;
+
+timeout:
+	shell_error(sh, "timeout");
+
+	return -ETIME;
+}
+
 void ec_reset(void)
 {
 	gpio_pin_set_dt(&ec_rst_gpio, 1);
@@ -120,10 +193,13 @@
 	return 0;
 }
 
-SHELL_STATIC_SUBCMD_SET_CREATE(
-	ec_cmds, SHELL_CMD_ARG(reset, NULL, "reset", cmd_ec_reset, 1, 0),
+/* clang-format off */
+SHELL_STATIC_SUBCMD_SET_CREATE(ec_cmds,
+	SHELL_CMD_ARG(io_sweep, NULL, "io_sweep", cmd_io_sweep, 1, 0),
 	SHELL_CMD_ARG(power, NULL, "power on|off", cmd_ec_power, 2, 0),
+	SHELL_CMD_ARG(reset, NULL, "reset", cmd_ec_reset, 1, 0),
 	SHELL_SUBCMD_SET_END);
+/* clang-format on */
 
 SHELL_CMD_REGISTER(ec, &ec_cmds, "EC control commands", NULL);