summaryrefslogtreecommitdiff
path: root/board
diff options
context:
space:
mode:
authorWai-Hong Tam <waihong@google.com>2020-09-17 14:35:12 -0700
committerCommit Bot <commit-bot@chromium.org>2020-09-23 19:46:57 +0000
commit79de579bf0278a1ab6d8ba6573e70855ec26d687 (patch)
tree161ac24e7ddf9823b1df01049198524c5c78075f /board
parenta4e14b97193fe079d18e929ce9d28b586d5418de (diff)
downloadchrome-ec-79de579bf0278a1ab6d8ba6573e70855ec26d687.tar.gz
Coachz: Implement the base detection
The implementation is based on the poppy reference, with the following changes: * Use proper GPIOs and ADC channel * Not notify ACPI, which is invalid on ARM boards * Update comments BRANCH=None BUG=b:168712053 TEST=Built the Coachz image. Change-Id: Ie0b0eaf81cdcbd2b2219eef0c895fbd41971d1e4 Signed-off-by: Wai-Hong Tam <waihong@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2416998 Reviewed-by: Ting Shen <phoenixshen@chromium.org>
Diffstat (limited to 'board')
-rw-r--r--board/coachz/base_detect.c256
-rw-r--r--board/coachz/board.c8
-rw-r--r--board/coachz/board.h6
-rw-r--r--board/coachz/build.mk2
-rw-r--r--board/coachz/gpio.inc7
5 files changed, 275 insertions, 4 deletions
diff --git a/board/coachz/base_detect.c b/board/coachz/base_detect.c
new file mode 100644
index 0000000000..24ea3d51ef
--- /dev/null
+++ b/board/coachz/base_detect.c
@@ -0,0 +1,256 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Coachz base detection code */
+
+#include "adc.h"
+#include "adc_chip.h"
+#include "board.h"
+#include "chipset.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "system.h"
+#include "tablet_mode.h"
+#include "timer.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args)
+
+/* Base detection and debouncing */
+#define BASE_DETECT_DEBOUNCE_US (20 * MSEC)
+
+/*
+ * If the base status is unclear (i.e. not within expected ranges, read
+ * the ADC value again every 500ms.
+ */
+#define BASE_DETECT_RETRY_US (500 * MSEC)
+
+/*
+ * TODO(b:169094188): Clarify the pull-up value 601K or 100K?
+ *
+ * Lid has 604K pull-up, base has 30.1K pull-down, so the
+ * ADC value should be around 30.1/(604+30.1)*3300 = 156
+ *
+ * We add a significant margin on the maximum value, due to noise on the line,
+ * especially when PWM is active. See b/64193554 for details.
+ */
+#define BASE_DETECT_MIN_MV 120
+#define BASE_DETECT_MAX_MV 300
+
+/*
+ * TODO(b:169094188): Clarify the pull-down present?
+ *
+ * When the base is connected in reverse, it presents a 100K pull-down,
+ * so the ADC value should be around 100/(604+100)*3300 = 469
+ */
+#define BASE_DETECT_REVERSE_MIN_MV 450
+#define BASE_DETECT_REVERSE_MAX_MV 500
+
+/* Minimum ADC value to indicate base is disconnected for sure */
+#define BASE_DETECT_DISCONNECT_MIN_MV 1500
+
+/*
+ * Base EC pulses detection pin for 500 us to signal out of band USB wake (that
+ * can be used to wake system from deep S3).
+ */
+#define BASE_DETECT_PULSE_MIN_US 400
+#define BASE_DETECT_PULSE_MAX_US 650
+
+static uint64_t base_detect_debounce_time;
+
+static void base_detect_deferred(void);
+DECLARE_DEFERRED(base_detect_deferred);
+
+enum base_status {
+ BASE_UNKNOWN = 0,
+ BASE_DISCONNECTED = 1,
+ BASE_CONNECTED = 2,
+ BASE_CONNECTED_REVERSE = 3,
+};
+
+static enum base_status current_base_status;
+
+/*
+ * This function is called whenever there is a change in the base detect
+ * status. Actions taken include:
+ * 1. Change in power to base
+ * 2. Indicate mode change to host.
+ * 3. Indicate tablet mode to host. Current assumption is that if base is
+ * disconnected then the system is in tablet mode, else if the base is
+ * connected, then the system is not in tablet mode.
+ */
+static void base_detect_change(enum base_status status)
+{
+ int connected = (status == BASE_CONNECTED);
+
+ if (current_base_status == status)
+ return;
+
+ CPRINTS("Base %sconnected", connected ? "" : "not ");
+ gpio_set_level(GPIO_EN_BASE, connected);
+ tablet_set_mode(!connected);
+ current_base_status = status;
+}
+
+/* Measure detection pin pulse duration (used to wake AP from deep S3). */
+static uint64_t pulse_start;
+static uint32_t pulse_width;
+
+static void print_base_detect_value(int v, int tmp_pulse_width)
+{
+ CPRINTS("%s = %d (pulse %d)", adc_channels[ADC_BASE_DET].name,
+ v, tmp_pulse_width);
+}
+
+static void base_detect_deferred(void)
+{
+ uint64_t time_now = get_time().val;
+ int v;
+ uint32_t tmp_pulse_width = pulse_width;
+ static int reverse_debounce = 1;
+
+ if (base_detect_debounce_time > time_now) {
+ hook_call_deferred(&base_detect_deferred_data,
+ base_detect_debounce_time - time_now);
+ return;
+ }
+
+ v = adc_read_channel(ADC_BASE_DET);
+ if (v == ADC_READ_ERROR)
+ return;
+
+ print_base_detect_value(v, tmp_pulse_width);
+
+ if (v >= BASE_DETECT_REVERSE_MIN_MV &&
+ v <= BASE_DETECT_REVERSE_MAX_MV) {
+ /*
+ * If we are unlucky when we sample the ADC, we may think that
+ * the base is connected in reverse, while this may just be a
+ * transient. Force debouncing a little longer in that case.
+ */
+ if (current_base_status == BASE_CONNECTED_REVERSE)
+ return;
+
+ if (reverse_debounce == 0) {
+ base_detect_change(BASE_CONNECTED_REVERSE);
+ return;
+ }
+
+ reverse_debounce = 0;
+ hook_call_deferred(&base_detect_deferred_data,
+ BASE_DETECT_DEBOUNCE_US);
+ return;
+ }
+ /* Reset reverse debounce */
+ reverse_debounce = 1;
+
+ if (v >= BASE_DETECT_MIN_MV && v <= BASE_DETECT_MAX_MV) {
+ if (current_base_status != BASE_CONNECTED) {
+ base_detect_change(BASE_CONNECTED);
+ } else if (tmp_pulse_width >= BASE_DETECT_PULSE_MIN_US &&
+ tmp_pulse_width <= BASE_DETECT_PULSE_MAX_US) {
+ CPRINTS("Sending event to AP");
+ host_set_single_event(EC_HOST_EVENT_KEY_PRESSED);
+ }
+ return;
+ }
+
+ if (v >= BASE_DETECT_DISCONNECT_MIN_MV) {
+ base_detect_change(BASE_DISCONNECTED);
+ return;
+ }
+
+ /* Unclear base status, schedule again in a while. */
+ hook_call_deferred(&base_detect_deferred_data, BASE_DETECT_RETRY_US);
+}
+
+static inline int detect_pin_connected(enum gpio_signal det_pin)
+{
+ return gpio_get_level(det_pin) == 0;
+}
+
+void base_detect_interrupt(enum gpio_signal signal)
+{
+ uint64_t time_now = get_time().val;
+
+ if (base_detect_debounce_time <= time_now) {
+ /*
+ * Detect and measure detection pin pulse, when base is
+ * connected. Only a single pulse is measured over a debounce
+ * period. If no pulse, or multiple pulses are detected,
+ * pulse_width is set to 0.
+ */
+ if (current_base_status == BASE_CONNECTED &&
+ !detect_pin_connected(signal)) {
+ pulse_start = time_now;
+ } else {
+ pulse_start = 0;
+ }
+ pulse_width = 0;
+
+ hook_call_deferred(&base_detect_deferred_data,
+ BASE_DETECT_DEBOUNCE_US);
+ } else {
+ if (current_base_status == BASE_CONNECTED &&
+ detect_pin_connected(signal) && !pulse_width &&
+ pulse_start) {
+ /* First pulse within period. */
+ pulse_width = time_now - pulse_start;
+ } else {
+ pulse_start = 0;
+ pulse_width = 0;
+ }
+ }
+
+ base_detect_debounce_time = time_now + BASE_DETECT_DEBOUNCE_US;
+}
+
+static void base_enable(void)
+{
+ /* Enable base detection interrupt. */
+ base_detect_debounce_time = get_time().val;
+ hook_call_deferred(&base_detect_deferred_data, 0);
+ gpio_enable_interrupt(GPIO_BASE_DET_L);
+}
+DECLARE_HOOK(HOOK_CHIPSET_STARTUP, base_enable, HOOK_PRIO_DEFAULT);
+
+static void base_disable(void)
+{
+ /* Disable base detection interrupt and disable power to base. */
+ gpio_disable_interrupt(GPIO_BASE_DET_L);
+ base_detect_change(BASE_DISCONNECTED);
+}
+DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, base_disable, HOOK_PRIO_DEFAULT);
+
+static void base_init(void)
+{
+ /*
+ * If we jumped to this image and chipset is already in S0, enable
+ * base.
+ */
+ if (system_jumped_late() && chipset_in_state(CHIPSET_STATE_ON))
+ base_enable();
+}
+DECLARE_HOOK(HOOK_INIT, base_init, HOOK_PRIO_DEFAULT+1);
+
+void base_force_state(int state)
+{
+ if (state == 1) {
+ gpio_disable_interrupt(GPIO_BASE_DET_L);
+ base_detect_change(BASE_CONNECTED);
+ CPRINTS("BD forced connected");
+ } else if (state == 0) {
+ gpio_disable_interrupt(GPIO_BASE_DET_L);
+ base_detect_change(BASE_DISCONNECTED);
+ CPRINTS("BD forced disconnected");
+ } else {
+ base_enable();
+ CPRINTS("BD forced reset");
+ }
+}
diff --git a/board/coachz/board.c b/board/coachz/board.c
index 6790138526..3e9b46655b 100644
--- a/board/coachz/board.c
+++ b/board/coachz/board.c
@@ -153,6 +153,14 @@ const struct adc_t adc_channels[] = {
2,
0
},
+ /* Base detection */
+ [ADC_BASE_DET] = {
+ "BASE_DET",
+ NPCX_ADC_CH5,
+ ADC_MAX_VOLT,
+ ADC_READ_MAX + 1,
+ 0
+ },
};
BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
diff --git a/board/coachz/board.h b/board/coachz/board.h
index 9279e43296..47f5f71641 100644
--- a/board/coachz/board.h
+++ b/board/coachz/board.h
@@ -42,6 +42,9 @@
TASK_EVENT_MOTION_SENSOR_INTERRUPT(LID_ACCEL)
#define OPT3001_I2C_ADDR_FLAGS OPT3001_I2C_ADDR1_FLAGS
+#define CONFIG_TABLET_MODE
+#define CONFIG_TABLET_MODE_SWITCH
+
/* GPIO alias */
#define GPIO_AC_PRESENT GPIO_CHG_ACOK_OD
#define GPIO_WP_L GPIO_EC_FLASH_WP_ODL
@@ -56,6 +59,7 @@ enum adc_channel {
ADC_VBUS,
ADC_AMON_BMON,
ADC_PSYS,
+ ADC_BASE_DET,
ADC_CH_COUNT
};
@@ -88,6 +92,8 @@ int board_vbus_sink_enable(int port, int enable);
/* Reset all TCPCs. */
void board_reset_pd_mcu(void);
void board_set_tcpc_power_mode(int port, int mode);
+/* Base detection */
+void base_detect_interrupt(enum gpio_signal signal);
#endif /* !defined(__ASSEMBLER__) */
diff --git a/board/coachz/build.mk b/board/coachz/build.mk
index a044fa58cb..5b6ecb0398 100644
--- a/board/coachz/build.mk
+++ b/board/coachz/build.mk
@@ -11,4 +11,4 @@ CHIP_FAMILY:=npcx7
CHIP_VARIANT:=npcx7m6fc
BASEBOARD:=trogdor
-board-y=battery.o board.o led.o
+board-y=battery.o board.o led.o base_detect.o
diff --git a/board/coachz/gpio.inc b/board/coachz/gpio.inc
index 128620d949..d60d49fd77 100644
--- a/board/coachz/gpio.inc
+++ b/board/coachz/gpio.inc
@@ -39,6 +39,8 @@ GPIO_INT(POWER_GOOD, PIN(5, 4), GPIO_INT_BOTH | GPIO_PULL_DOWN, chipset_p
GPIO_INT(WARM_RESET_L, PIN(F, 4), GPIO_INT_BOTH | GPIO_SEL_1P8V, chipset_warm_reset_interrupt) /* AP warm reset */
GPIO_INT(AP_EC_SPI_CS_L, PIN(5, 3), GPIO_INT_FALLING | GPIO_PULL_DOWN, shi_cs_event) /* EC SPI Chip Select */
+GPIO_INT(BASE_DET_L, PIN(3, 7), GPIO_INT_BOTH, base_detect_interrupt) /* Detachable base attached? */
+
/* Sensor interrupts */
GPIO_INT(ACCEL_GYRO_INT_L, PIN(A, 0), GPIO_INT_FALLING | GPIO_SEL_1P8V, bmi160_interrupt) /* Accelerometer/gyro interrupt */
@@ -85,9 +87,7 @@ GPIO(WLC_IRQ_CONN, PIN(7, 4), GPIO_INPUT)
GPIO(WLC_NRST_CONN, PIN(C, 5), GPIO_INPUT)
/* Base detection */
-/* TODO(b/168712053): Implement the base detection */
-GPIO(BASE_DET_L, PIN(3, 7), GPIO_INPUT)
-GPIO(EN_BASE, PIN(0, 4), GPIO_INPUT)
+GPIO(EN_BASE, PIN(0, 4), GPIO_OUT_LOW) /* Enable power to detachable base */
/* USB-C */
GPIO(USB_C0_PD_RST_L, PIN(F, 1), GPIO_ODR_HIGH) /* Port-0 TCPC chip reset, actaully Open-Drain */
@@ -149,6 +149,7 @@ ALTERNATE(PIN_MASK(8, 0x80), 1, MODULE_I2C, 0) /* I2C1 SCL (GPIO87)
ALTERNATE(PIN_MASK(3, 0x48), 1, MODULE_I2C, 0) /* I2C5 (GPIO33/36) */
ALTERNATE(PIN_MASK(B, 0x0C), 1, MODULE_I2C, GPIO_SEL_1P8V) /* I2C7 (GPIOB2/B3) - 1.8V */
ALTERNATE(PIN_MASK(D, 0x03), 1, MODULE_I2C, 0) /* I2C3 (GPIOD0/D1) */
+ALTERNATE(PIN_MASK(3, 0x80), 0, MODULE_ADC, 0) /* ADC5 (GPIO37) */
ALTERNATE(PIN_MASK(4, 0x1C), 0, MODULE_ADC, 0) /* ADC1 (GPIO44), ADC2 (GPIO43), ADC3 (GPIO42) */
ALTERNATE(PIN_MASK(4, 0xC0), 1, MODULE_SPI, GPIO_SEL_1P8V) /* SHI_SDO (GPIO47), SHI_SDI (GPIO46) */
ALTERNATE(PIN_MASK(5, 0x28), 1, MODULE_SPI, GPIO_SEL_1P8V) /* SHI_SCLK (GPIO55), SHI_CS# (GPIO53) */