summaryrefslogtreecommitdiff
path: root/board/poppy/base_detect_lux.c
diff options
context:
space:
mode:
authorNicolas Boichat <drinkcat@chromium.org>2017-12-28 08:07:46 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-01-26 13:25:38 -0800
commitecfa69663813ec41aa79ed01c4af2f36723fe194 (patch)
treeb22df6c3e1b3bfc55f36c357cb168c25d1109491 /board/poppy/base_detect_lux.c
parenta5c21c4ffe16eeee3da97b1138aa6b5556983e7d (diff)
downloadchrome-ec-ecfa69663813ec41aa79ed01c4af2f36723fe194.tar.gz
lux: Base detection code
Lux base detection is a little trickier than previous bases, as the detection levels switch around when GPIO_EC_COMM_PD gets enabled: - When disconnected, low levels (~550mV) mean base got connected, and high levels (~3300mV) mean base is still disconnected. - When connected, low levels (~43mV) mean base got disconnected, and high levels (~2346mV) mean base is still connected. On reset, when base_status is unknown, we enable GPIO_EC_COMM_PD, to be able to differentiate between connected and disconnected. BRANCH=none BUG=b:67029560 TEST=Connect lux/wand, check that base gets detected correctly. TEST=Type reboot in EC console, base gets detected as well. Change-Id: I742def8e378a93c08e2dcc155b06cbca814e7fd8 Signed-off-by: Nicolas Boichat <drinkcat@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/845543 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'board/poppy/base_detect_lux.c')
-rw-r--r--board/poppy/base_detect_lux.c198
1 files changed, 197 insertions, 1 deletions
diff --git a/board/poppy/base_detect_lux.c b/board/poppy/base_detect_lux.c
index 49192f368c..749a59833d 100644
--- a/board/poppy/base_detect_lux.c
+++ b/board/poppy/base_detect_lux.c
@@ -5,9 +5,205 @@
/* Lux 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 "task.h"
+#include "timer.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_USB, 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)
+
+/*
+ * When base is disconnected, and gets connected:
+ * Lid has 1M pull-up, base has 200K pull-down, so the ADC
+ * value should be around 200/(200+1000)*3300 = 550.
+ *
+ * Idle value should be ~3300: lid has 1M pull-up, and nothing else (i.e. ADC
+ * maxing out at 2813).
+ */
+#define BASE_DISCONNECTED_CONNECT_MIN_MV 450
+#define BASE_DISCONNECTED_CONNECT_MAX_MV 600
+
+#define BASE_DISCONNECTED_MIN_MV 2800
+#define BASE_DISCONNECTED_MAX_MV (ADC_MAX_VOLT+1)
+
+/*
+ * When base is connected, then gets disconnected:
+ * Lid has 1M pull-up, lid has 10.0K pull-down, so the ADC
+ * value should be around 10.0/(10.0+1000)*3300 = 33.
+ *
+ * Idle level when connected should be:
+ * Lid has 10K pull-down, base has 5.1K pull-up, so the ADC value should be
+ * around 10.0/(10.0+5.1)*3300 = 2185 (actual value is 2153 as there is still
+ * a 1M pull-up on lid, and 200K pull-down on base).
+ */
+#define BASE_CONNECTED_DISCONNECT_MIN_MV 20
+#define BASE_CONNECTED_DISCONNECT_MAX_MV 40
+
+#define BASE_CONNECTED_MIN_MV 2050
+#define BASE_CONNECTED_MAX_MV 2300
+
+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,
+};
+
+static enum base_status current_base_status;
+
+/**
+ * Board-specific routine to indicate if the base is connected.
+ */
+int board_is_base_connected(void)
+{
+ return current_base_status == BASE_CONNECTED;
+}
+
+/**
+ * Board-specific routine to enable power distribution between lid and base
+ * (current can flow both ways).
+ */
+void board_enable_base_power(int enable)
+{
+ CPRINTS("Base power %sabled", enable ? "en" : "dis");
+ gpio_set_level(GPIO_PPVAR_VAR_BASE, enable);
+}
+
+/*
+ * This function is called whenever there is a change in the base detect
+ * status. Actions taken include:
+ * 1. Enable/disable pull-down on half-duplex UART line
+ * 2. Disable power transfer between lid and base when unplugged.
+ * 3. Indicate mode change to host.
+ * 4. 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;
+
+ /* Enable pull-down if connected. */
+ gpio_set_level(GPIO_EC_COMM_PD, !connected);
+ /* Disable power to/from base as quickly as possible. */
+ if (!connected)
+ board_enable_base_power(0);
+
+ /*
+ * Wake the charger task (it is responsible for enabling power to the
+ * base, and providing OTG power to the base if required).
+ */
+ task_wake(TASK_ID_CHARGER);
+
+ tablet_set_mode(!connected);
+ host_set_single_event(EC_HOST_EVENT_MODE_CHANGE);
+ current_base_status = status;
+}
+
+static void print_base_detect_value(const char *str, int v)
+{
+ CPRINTS("Base %s. ADC: %d", str, v);
+}
+
+static void base_detect_deferred(void)
+{
+ uint64_t time_now = get_time().val;
+ int v;
+
+ 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)
+ goto retry;
+
+ if (current_base_status == BASE_CONNECTED) {
+ if (v >= BASE_CONNECTED_DISCONNECT_MIN_MV &&
+ v <= BASE_CONNECTED_DISCONNECT_MAX_MV) {
+ print_base_detect_value("disconnected", v);
+ base_detect_change(BASE_DISCONNECTED);
+ return;
+ } else if (v >= BASE_CONNECTED_MIN_MV &&
+ v <= BASE_CONNECTED_MAX_MV) {
+ /* Still connected. */
+ return;
+ }
+ } else { /* Disconnected or unknown. */
+ if (v >= BASE_DISCONNECTED_CONNECT_MIN_MV &&
+ v <= BASE_DISCONNECTED_CONNECT_MAX_MV) {
+ print_base_detect_value("connected", v);
+ base_detect_change(BASE_CONNECTED);
+ return;
+ } else if (v >= BASE_DISCONNECTED_MIN_MV &&
+ v <= BASE_DISCONNECTED_MAX_MV) {
+ if (current_base_status == BASE_UNKNOWN) {
+ print_base_detect_value("disconnected", v);
+ base_detect_change(BASE_DISCONNECTED);
+ }
+ /* Still disconnected. */
+ return;
+ }
+ }
+
+retry:
+ print_base_detect_value("status unclear", v);
+ /* Unclear base status, schedule again in a while. */
+ hook_call_deferred(&base_detect_deferred_data,
+ BASE_DETECT_RETRY_US);
+}
void base_detect_interrupt(enum gpio_signal signal)
{
- /* TODO(b:67029560): Implement base detection for lux. */
+ uint64_t time_now = get_time().val;
+
+ if (base_detect_debounce_time <= time_now)
+ hook_call_deferred(&base_detect_deferred_data,
+ BASE_DETECT_DEBOUNCE_US);
+
+ base_detect_debounce_time = time_now + BASE_DETECT_DEBOUNCE_US;
+}
+
+static void base_init(void)
+{
+ /*
+ * Make sure base power and pull-down are off. This will reset the base
+ * if it is already connected.
+ */
+ board_enable_base_power(0);
+ gpio_set_level(GPIO_EC_COMM_PD, 1);
+
+ /* Enable base detection interrupt. */
+ hook_call_deferred(&base_detect_deferred_data, BASE_DETECT_DEBOUNCE_US);
+ gpio_enable_interrupt(GPIO_BASE_DET_A);
}
+DECLARE_HOOK(HOOK_INIT, base_init, HOOK_PRIO_DEFAULT+1);