217 lines
6.0 KiB
C
217 lines
6.0 KiB
C
|
/* Copyright 2018 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.
|
||
|
*/
|
||
|
|
||
|
/* Lux base without battery detection code */
|
||
|
|
||
|
#include "adc.h"
|
||
|
#include "adc_chip.h"
|
||
|
#include "board.h"
|
||
|
#include "chipset.h"
|
||
|
#include "common.h"
|
||
|
#include "console.h"
|
||
|
#include "extpower.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 650
|
||
|
|
||
|
#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 45
|
||
|
|
||
|
#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;
|
||
|
|
||
|
/*
|
||
|
* 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. Enable/disable power to base.
|
||
|
* 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;
|
||
|
|
||
|
current_base_status = status;
|
||
|
|
||
|
/* Enable pull-down if connected. */
|
||
|
gpio_set_level(GPIO_EN_CC_LID_BASE_PULLDN, !connected);
|
||
|
|
||
|
/* We don't enable dual-battery support. Set the base power directly. */
|
||
|
gpio_set_level(GPIO_EN_PPVAR_VAR_BASE, connected);
|
||
|
|
||
|
tablet_set_mode(!connected);
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
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_detect_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_CC_LID_BASE_ADC);
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_CHIPSET_STARTUP, base_detect_enable, HOOK_PRIO_DEFAULT);
|
||
|
|
||
|
static void base_detect_disable(void)
|
||
|
{
|
||
|
/* Disable base detection interrupt and disable power to base. */
|
||
|
gpio_disable_interrupt(GPIO_CC_LID_BASE_ADC);
|
||
|
base_detect_change(BASE_DISCONNECTED);
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, base_detect_disable, HOOK_PRIO_DEFAULT);
|
||
|
|
||
|
static void base_init(void)
|
||
|
{
|
||
|
/*
|
||
|
* Make sure base power and pull-down are off. This will reset the base
|
||
|
* if it is already connected.
|
||
|
*/
|
||
|
gpio_set_level(GPIO_EN_PPVAR_VAR_BASE, 0);
|
||
|
gpio_set_level(GPIO_EN_CC_LID_BASE_PULLDN, 1);
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_INIT, base_init, HOOK_PRIO_DEFAULT+1);
|
||
|
|
||
|
void base_force_state(int state)
|
||
|
{
|
||
|
if (state == 1) {
|
||
|
gpio_disable_interrupt(GPIO_CC_LID_BASE_ADC);
|
||
|
base_detect_change(BASE_CONNECTED);
|
||
|
CPRINTS("BD forced connected");
|
||
|
} else if (state == 0) {
|
||
|
gpio_disable_interrupt(GPIO_CC_LID_BASE_ADC);
|
||
|
base_detect_change(BASE_DISCONNECTED);
|
||
|
CPRINTS("BD forced disconnected");
|
||
|
} else {
|
||
|
hook_call_deferred(&base_detect_deferred_data, 0);
|
||
|
gpio_enable_interrupt(GPIO_CC_LID_BASE_ADC);
|
||
|
CPRINTS("BD forced reset");
|
||
|
}
|
||
|
}
|