diff options
author | Wai-Hong Tam <waihong@google.com> | 2020-09-17 14:35:12 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-09-23 19:46:57 +0000 |
commit | 79de579bf0278a1ab6d8ba6573e70855ec26d687 (patch) | |
tree | 161ac24e7ddf9823b1df01049198524c5c78075f /board/coachz | |
parent | a4e14b97193fe079d18e929ce9d28b586d5418de (diff) | |
download | chrome-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/coachz')
-rw-r--r-- | board/coachz/base_detect.c | 256 | ||||
-rw-r--r-- | board/coachz/board.c | 8 | ||||
-rw-r--r-- | board/coachz/board.h | 6 | ||||
-rw-r--r-- | board/coachz/build.mk | 2 | ||||
-rw-r--r-- | board/coachz/gpio.inc | 7 |
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) */ |