chrome-ec/board/waddledoo/board.c

383 lines
9.4 KiB
C

/* 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.
*/
/* Waddledoo board-specific configuration */
#include "adc_chip.h"
#include "button.h"
#include "charge_manager.h"
#include "charge_state_v2.h"
#include "charger.h"
#include "common.h"
#include "compile_time_macros.h"
#include "driver/accel_bma2x2.h"
#include "driver/accelgyro_bmi160.h"
#include "driver/bc12/pi3usb9201.h"
#include "driver/sync.h"
#include "driver/tcpm/raa489000.h"
#include "driver/tcpm/tcpci.h"
#include "driver/usb_mux/pi3usb3x532.h"
#include "extpower.h"
#include "gpio.h"
#include "hooks.h"
#include "i2c.h"
#include "keyboard_scan.h"
#include "lid_switch.h"
#include "motion_sense.h"
#include "power.h"
#include "power_button.h"
#include "stdbool.h"
#include "switch.h"
#include "tablet_mode.h"
#include "task.h"
#include "usb_mux.h"
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
static void tcpc_alert_event(enum gpio_signal s)
{
int port = (s == GPIO_USB_C0_INT_ODL) ? 0 : 1;
schedule_deferred_pd_interrupt(port);
}
static void usb_c0_interrupt(enum gpio_signal s)
{
/*
* The interrupt line is shared between the TCPC and BC 1.2 detection
* chip. Therefore we'll need to check both ICs.
*/
tcpc_alert_event(s);
task_set_event(TASK_ID_USB_CHG_P0, USB_CHG_EVENT_BC12, 0);
}
static void sub_usb_c1_interrupt(enum gpio_signal s)
{
/*
* The interrupt line is shared between the TCPC and BC 1.2 detection
* chip. Therefore we'll need to check both ICs.
*/
tcpc_alert_event(s);
task_set_event(TASK_ID_USB_CHG_P1, USB_CHG_EVENT_BC12, 0);
}
#include "gpio_list.h"
void board_init(void)
{
gpio_enable_interrupt(GPIO_USB_C0_INT_ODL);
gpio_enable_interrupt(GPIO_SUB_USB_C1_INT_ODL);
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
void board_reset_pd_mcu(void)
{
/*
* TODO(b:147316511): Here we could issue a digital reset to the IC,
* unsure if we actually want to do that or not yet.
*/
}
int board_is_sourcing_vbus(int port)
{
int regval;
tcpc_read(port, TCPC_REG_POWER_STATUS, &regval);
return !!(regval & TCPC_REG_POWER_STATUS_SOURCING_VBUS);
}
int board_set_active_charge_port(int port)
{
int is_real_port = (port >= 0 &&
port < CONFIG_USB_PD_PORT_MAX_COUNT);
int i;
int old_port;
if (!is_real_port && port != CHARGE_PORT_NONE)
return EC_ERROR_INVAL;
old_port = charge_manager_get_active_charge_port();
CPRINTS("New chg p%d", port);
/* Disable all ports. */
if (port == CHARGE_PORT_NONE) {
for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++)
tcpc_write(i, TCPC_REG_COMMAND,
TCPC_REG_COMMAND_SNK_CTRL_LOW);
return EC_SUCCESS;
}
/* Check if port is sourcing VBUS. */
if (board_is_sourcing_vbus(port)) {
CPRINTS("Skip enable p%d", port);
return EC_ERROR_INVAL;
}
/*
* Turn off the other ports' sink path FETs, before enabling the
* requested charge port.
*/
for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
if (i == port)
continue;
if (tcpc_write(i, TCPC_REG_COMMAND,
TCPC_REG_COMMAND_SNK_CTRL_LOW))
CPRINTS("p%d: sink path disable failed.", i);
}
/*
* Stop the charger IC from switching while changing ports. Otherwise,
* we can overcurrent the adapter we're switching to. (crbug.com/926056)
*/
if (old_port != CHARGE_PORT_NONE)
charger_discharge_on_ac(1);
/* Enable requested charge port. */
if (tcpc_write(port, TCPC_REG_COMMAND,
TCPC_REG_COMMAND_SNK_CTRL_HIGH)) {
CPRINTS("p%d: sink path enable failed.", port);
charger_discharge_on_ac(0);
return EC_ERROR_UNKNOWN;
}
/* Allow the charger IC to begin/continue switching. */
charger_discharge_on_ac(0);
return EC_SUCCESS;
}
void board_set_charge_limit(int port, int supplier, int charge_ma,
int max_ma, int charge_mv)
{
int icl = MAX(charge_ma, CONFIG_CHARGER_INPUT_CURRENT);
/*
* TODO(b:147463641): Characterize the input current limit in case that
* a scaling needs to be applied here.
*/
charge_set_input_current_limit(icl, charge_mv);
}
/* Sensors */
static struct mutex g_lid_mutex;
static struct mutex g_base_mutex;
static struct accelgyro_saved_data_t g_bma253_data;
static struct bmi160_drv_data_t g_bmi160_data;
struct motion_sensor_t motion_sensors[] = {
[LID_ACCEL] = {
.name = "Lid Accel",
.active_mask = SENSOR_ACTIVE_S0_S3,
.chip = MOTIONSENSE_CHIP_BMA255,
.type = MOTIONSENSE_TYPE_ACCEL,
.location = MOTIONSENSE_LOC_LID,
.drv = &bma2x2_accel_drv,
.mutex = &g_lid_mutex,
.drv_data = &g_bma253_data,
.port = I2C_PORT_SENSOR,
.i2c_spi_addr_flags = BMA2x2_I2C_ADDR1_FLAGS,
.rot_standard_ref = NULL,
.default_range = 2,
.min_frequency = BMA255_ACCEL_MIN_FREQ,
.max_frequency = BMA255_ACCEL_MAX_FREQ,
.config = {
[SENSOR_CONFIG_EC_S0] = {
.odr = 10000 | ROUND_UP_FLAG,
},
[SENSOR_CONFIG_EC_S3] = {
.odr = 10000 | ROUND_UP_FLAG,
},
},
},
[BASE_ACCEL] = {
.name = "Base Accel",
.active_mask = SENSOR_ACTIVE_S0_S3,
.chip = MOTIONSENSE_CHIP_BMI160,
.type = MOTIONSENSE_TYPE_ACCEL,
.location = MOTIONSENSE_LOC_BASE,
.drv = &bmi160_drv,
.mutex = &g_base_mutex,
.drv_data = &g_bmi160_data,
.port = I2C_PORT_SENSOR,
.i2c_spi_addr_flags = BMI160_ADDR0_FLAGS,
.rot_standard_ref = NULL,
.default_range = 4,
.min_frequency = BMI160_ACCEL_MIN_FREQ,
.max_frequency = BMI160_ACCEL_MAX_FREQ,
.config = {
[SENSOR_CONFIG_EC_S0] = {
.odr = 13000 | ROUND_UP_FLAG,
.ec_rate = 100 * MSEC,
},
[SENSOR_CONFIG_EC_S3] = {
.odr = 10000 | ROUND_UP_FLAG,
.ec_rate = 100 * MSEC,
},
},
},
[BASE_GYRO] = {
.name = "Base Gyro",
.active_mask = SENSOR_ACTIVE_S0_S3,
.chip = MOTIONSENSE_CHIP_BMI160,
.type = MOTIONSENSE_TYPE_GYRO,
.location = MOTIONSENSE_LOC_BASE,
.drv = &bmi160_drv,
.mutex = &g_base_mutex,
.drv_data = &g_bmi160_data,
.port = I2C_PORT_SENSOR,
.i2c_spi_addr_flags = BMI160_ADDR0_FLAGS,
.default_range = 1000, /* dps */
.rot_standard_ref = NULL,
.min_frequency = BMI160_GYRO_MIN_FREQ,
.max_frequency = BMI160_GYRO_MAX_FREQ,
},
[VSYNC] = {
.name = "Camera VSYNC",
.active_mask = SENSOR_ACTIVE_S0,
.chip = MOTIONSENSE_CHIP_GPIO,
.type = MOTIONSENSE_TYPE_SYNC,
.location = MOTIONSENSE_LOC_CAMERA,
.drv = &sync_drv,
.default_range = 0,
.min_frequency = 0,
.max_frequency = 1,
},
};
const unsigned int motion_sensor_count = ARRAY_SIZE(motion_sensors);
int extpower_is_present(void)
{
/*
* TODO(b:146651593) We can likely use the charger IC to determine VBUS
* presence.
*/
return pd_snk_is_vbus_provided(0) || pd_snk_is_vbus_provided(1);
}
int pd_snk_is_vbus_provided(int port)
{
int regval = 0;
tcpc_read(port, TCPC_REG_POWER_STATUS, &regval);
return regval & TCPC_REG_POWER_STATUS_VBUS_PRES;
}
const struct pi3usb9201_config_t pi3usb9201_bc12_chips[] = {
{
.i2c_port = I2C_PORT_USB_C0,
.i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS,
},
{
.i2c_port = I2C_PORT_SUB_USB_C1,
.i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS,
},
};
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_MAX_COUNT] = {
{
.bus_type = EC_BUS_TYPE_I2C,
.i2c_info = {
.port = I2C_PORT_USB_C0,
.addr_flags = RAA489000_TCPC0_I2C_FLAGS,
},
.flags = TCPC_FLAGS_TCPCI_V2_0,
.drv = &raa489000_tcpm_drv,
},
{
.bus_type = EC_BUS_TYPE_I2C,
.i2c_info = {
.port = I2C_PORT_SUB_USB_C1,
.addr_flags = RAA489000_TCPC0_I2C_FLAGS,
},
.flags = TCPC_FLAGS_TCPCI_V2_0,
.drv = &raa489000_tcpm_drv,
},
};
struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_MAX_COUNT] = {
{
.port_addr = MUX_PORT_AND_ADDR(I2C_PORT_USB_C0, 0x54),
.driver = &pi3usb3x532_usb_mux_driver,
},
{
.port_addr = MUX_PORT_AND_ADDR(I2C_PORT_SUB_USB_C1, 0x54),
.driver = &pi3usb3x532_usb_mux_driver,
}
};
uint16_t tcpc_get_alert_status(void)
{
uint16_t status = 0;
int regval;
/*
* The interrupt line is shared between the TCPC and BC1.2 detector IC.
* Therefore, go out and actually read the alert registers to report the
* alert status.
*/
if (!gpio_get_level(GPIO_USB_C0_INT_ODL)) {
if (!tcpc_read16(0, TCPC_REG_ALERT, &regval)) {
/* The TCPCI v1.0 spec says to ignore bits 14:12. */
if (!(tcpc_config[0].flags & TCPC_FLAGS_TCPCI_V2_0))
regval &= ~((1 << 14) | (1 << 13) | (1 << 12));
if (regval)
status |= PD_STATUS_TCPC_ALERT_0;
}
}
if (!gpio_get_level(GPIO_SUB_USB_C1_INT_ODL)) {
if (!tcpc_read16(1, TCPC_REG_ALERT, &regval)) {
/* TCPCI spec v1.0 says to ignore bits 14:12. */
if (!(tcpc_config[1].flags & TCPC_FLAGS_TCPCI_V2_0))
regval &= ~((1 << 14) | (1 << 13) | (1 << 12));
if (regval)
status |= PD_STATUS_TCPC_ALERT_1;
}
}
return status;
}
#ifndef TEST_BUILD
/* This callback disables keyboard when convertibles are fully open */
void lid_angle_peripheral_enable(int enable)
{
int chipset_in_s0 = chipset_in_state(CHIPSET_STATE_ON);
/*
* If the lid is in tablet position via other sensors,
* ignore the lid angle, which might be faulty then
* disable keyboard.
*/
if (tablet_get_mode())
enable = 0;
if (enable) {
keyboard_scan_enable(1, KB_SCAN_DISABLE_LID_ANGLE);
} else {
/*
* Ensure that the chipset is off before disabling the keyboard.
* When the chipset is on, the EC keeps the keyboard enabled and
* the AP decides whether to ignore input devices or not.
*/
if (!chipset_in_s0)
keyboard_scan_enable(0, KB_SCAN_DISABLE_LID_ANGLE);
}
}
#endif