blob: fb0945a8959136cc2679ae7d26962ad975ef1506 [file] [log] [blame]
/* Copyright 2020 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* lindar board-specific configuration */
#include "button.h"
#include "cbi_ec_fw_config.h"
#include "common.h"
#include "driver/accel_lis2dh.h"
#include "driver/accelgyro_lsm6dsm.h"
#include "driver/bc12/pi3usb9201.h"
#include "driver/ppc/sn5s330.h"
#include "driver/ppc/syv682x.h"
#include "driver/sync.h"
#include "driver/tcpm/ps8xxx.h"
#include "driver/tcpm/rt1715.h"
#include "driver/tcpm/tcpci.h"
#include "driver/tcpm/tusb422.h"
#include "extpower.h"
#include "fan.h"
#include "fan_chip.h"
#include "gpio.h"
#include "hooks.h"
#include "keyboard_scan.h"
#include "lid_switch.h"
#include "power.h"
#include "power_button.h"
#include "pwm.h"
#include "pwm_chip.h"
#include "switch.h"
#include "system.h"
#include "tablet_mode.h"
#include "task.h"
#include "uart.h"
#include "usb_mux.h"
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
#include "usbc_ppc.h"
#include "util.h"
/* Must come after other header files and interrupt handler declarations */
#include "gpio_list.h"
#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ##args)
#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ##args)
/* Keyboard scan setting */
__override struct keyboard_scan_config keyscan_config = {
/* Increase from 50 us, because KSO_02 passes through the H1. */
.output_settle_us = 80,
/* Other values should be the same as the default configuration. */
.debounce_down_us = 9 * MSEC,
.debounce_up_us = 30 * MSEC,
.scan_period_us = 3 * MSEC,
.min_post_scan_delay_us = 1000,
.poll_timeout_us = 100 * MSEC,
.actual_key_mask = {
0x14, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff,
0xa4, 0xff, 0xfe, 0x55, 0xfa, 0xca /* full set */
},
};
/******************************************************************************/
/*
* FW_CONFIG defaults for Malefor if the CBI data is not initialized.
*/
union volteer_cbi_fw_config fw_config_defaults = {
.usb_db = DB_USB3_NO_A,
};
static void board_init(void)
{
if (ec_cfg_has_tabletmode()) {
/* Enable gpio interrupt for base accelgyro sensor */
gpio_enable_interrupt(GPIO_EC_IMU_INT_L);
} else {
motion_sensor_count = 0;
/* Device is clamshell only */
tablet_set_mode(0, TABLET_TRIGGER_LID);
/* Gyro is not present, don't allow line to float */
gpio_set_flags(GPIO_EC_IMU_INT_L, GPIO_INPUT | GPIO_PULL_DOWN);
}
/*
* TODO: b/154447182 - Malefor will control power LED and battery LED
* independently, and keep the max brightness of power LED and battery
* LED as 50%.
*/
pwm_enable(PWM_CH_LED4_SIDESEL, 1);
pwm_set_duty(PWM_CH_LED4_SIDESEL, 50);
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
int board_is_i2c_port_powered(int port)
{
if (port != I2C_PORT_LIGHTBAR)
return 1;
/*
* Lightbar rails are off in S5/G3
* Refer CL-2739008.
*/
return chipset_in_state(CHIPSET_STATE_ANY_OFF) ? 0 : 1;
}
int board_is_lid_angle_tablet_mode(void)
{
return ec_cfg_has_tabletmode();
}
/* Enable or disable input devices, based on tablet mode or chipset state */
__override void lid_angle_peripheral_enable(int enable)
{
if (ec_cfg_has_tabletmode()) {
if (chipset_in_state(CHIPSET_STATE_ANY_OFF) ||
tablet_get_mode())
enable = 0;
keyboard_scan_enable(enable, KB_SCAN_DISABLE_LID_ANGLE);
}
}
/******************************************************************************/
/* Sensors */
/* Lid and base Sensor mutex */
static struct mutex g_lid_accel_mutex;
static struct mutex g_base_mutex;
/* Lid and base accel private data */
static struct stprivate_data g_lis2dh_data;
static struct lsm6dsm_data lsm6dsm_data = LSM6DSM_DATA;
/* Matrix to rotate lid and base sensor into standard reference frame */
static const mat33_fp_t lid_standard_ref = { { 0, FLOAT_TO_FP(1), 0 },
{ FLOAT_TO_FP(1), 0, 0 },
{ 0, 0, FLOAT_TO_FP(-1) } };
static const mat33_fp_t base_standard_ref = { { FLOAT_TO_FP(1), 0, 0 },
{ 0, FLOAT_TO_FP(-1), 0 },
{ 0, 0, FLOAT_TO_FP(-1) } };
struct motion_sensor_t motion_sensors[] = {
[LID_ACCEL] = {
.name = "Lid Accel",
.active_mask = SENSOR_ACTIVE_S0_S3,
.chip = MOTIONSENSE_CHIP_LIS2DE,
.type = MOTIONSENSE_TYPE_ACCEL,
.location = MOTIONSENSE_LOC_LID,
.drv = &lis2dh_drv,
.mutex = &g_lid_accel_mutex,
.drv_data = &g_lis2dh_data,
.port = I2C_PORT_SENSOR,
.i2c_spi_addr_flags = LIS2DH_ADDR1_FLAGS,
.rot_standard_ref = &lid_standard_ref,
.min_frequency = LIS2DH_ODR_MIN_VAL,
.max_frequency = LIS2DH_ODR_MAX_VAL,
.default_range = 2, /* g, to support tablet mode */
.config = {
/* EC use accel for angle detection */
[SENSOR_CONFIG_EC_S0] = {
.odr = 10000 | ROUND_UP_FLAG,
},
/* Sensor on in S3 */
[SENSOR_CONFIG_EC_S3] = {
.odr = 10000 | ROUND_UP_FLAG,
},
},
},
[BASE_ACCEL] = {
.name = "Base Accel",
.active_mask = SENSOR_ACTIVE_S0_S3,
.chip = MOTIONSENSE_CHIP_LSM6DSM,
.type = MOTIONSENSE_TYPE_ACCEL,
.location = MOTIONSENSE_LOC_BASE,
.drv = &lsm6dsm_drv,
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_ACCEL),
.port = I2C_PORT_SENSOR,
.i2c_spi_addr_flags = LSM6DSM_ADDR0_FLAGS,
.rot_standard_ref = &base_standard_ref,
.default_range = 4, /* g, to meet CDD 7.3.1/C-1-4 reqs */
.min_frequency = LSM6DSM_ODR_MIN_VAL,
.max_frequency = LSM6DSM_ODR_MAX_VAL,
.config = {
/* EC use accel for angle detection */
[SENSOR_CONFIG_EC_S0] = {
.odr = 13000 | ROUND_UP_FLAG,
.ec_rate = 100 * MSEC,
},
/* Sensor on for angle detection */
[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_LSM6DSM,
.type = MOTIONSENSE_TYPE_GYRO,
.location = MOTIONSENSE_LOC_BASE,
.drv = &lsm6dsm_drv,
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_GYRO),
.port = I2C_PORT_SENSOR,
.i2c_spi_addr_flags = LSM6DSM_ADDR0_FLAGS,
.default_range = 1000 | ROUND_UP_FLAG, /* dps */
.rot_standard_ref = &base_standard_ref,
.min_frequency = LSM6DSM_ODR_MIN_VAL,
.max_frequency = LSM6DSM_ODR_MAX_VAL,
},
};
unsigned int motion_sensor_count = ARRAY_SIZE(motion_sensors);
/******************************************************************************/
/* Physical fans. These are logically separate from pwm_channels. */
const struct fan_conf fan_conf_0 = {
.flags = FAN_USE_RPM_MODE,
.ch = MFT_CH_0, /* Use MFT id to control fan */
.pgood_gpio = -1,
.enable_gpio = GPIO_EN_PP5000_FAN,
};
/*
* Fan specs from datasheet:
* Max speed 5900 rpm (+/- 7%), minimum duty cycle 30%.
* Minimum speed not specified by RPM. Set minimum RPM to max speed (with
* margin) x 30%.
* 5900 x 1.07 x 0.30 = 1894, round up to 1900
*/
const struct fan_rpm fan_rpm_0 = {
.rpm_min = 1900,
.rpm_start = 1900,
.rpm_max = 5900,
};
const struct fan_t fans[FAN_CH_COUNT] = {
[FAN_CH_0] = {
.conf = &fan_conf_0,
.rpm = &fan_rpm_0,
},
};
/******************************************************************************/
/* EC thermal management configuration */
/*
* Tiger Lake specifies 100 C as maximum TDP temperature. THRMTRIP# occurs at
* 130 C. However, sensor is located next to DDR, so we need to use the lower
* DDR temperature limit (100 C)
*/
const static struct ec_thermal_config thermal_cpu = {
.temp_host = {
[EC_TEMP_THRESH_HIGH] = C_TO_K(90),
[EC_TEMP_THRESH_HALT] = C_TO_K(100),
},
.temp_host_release = {
[EC_TEMP_THRESH_HIGH] = C_TO_K(85),
},
.temp_fan_off = C_TO_K(30),
.temp_fan_max = C_TO_K(60),
};
/*
* Inductor limits - used for both charger and PP3300 regulator
*
* Need to use the lower of the charger IC, PP3300 regulator, and the inductors
*
* Charger max recommended temperature 100C, max absolute temperature 125C
* PP3300 regulator: operating range -40 C to 145 C
*
* Inductors: limit of 125c
* PCB: limit is 100c
*/
const static struct ec_thermal_config thermal_inductor = {
.temp_host = {
[EC_TEMP_THRESH_HIGH] = C_TO_K(90),
[EC_TEMP_THRESH_HALT] = C_TO_K(100),
},
.temp_host_release = {
[EC_TEMP_THRESH_HIGH] = C_TO_K(85),
},
.temp_fan_off = C_TO_K(30),
.temp_fan_max = C_TO_K(60),
};
struct ec_thermal_config thermal_params[] = {
[TEMP_SENSOR_1_CHARGER] = thermal_inductor,
[TEMP_SENSOR_2_PP3300_REGULATOR] = thermal_inductor,
[TEMP_SENSOR_3_DDR_SOC] = thermal_cpu,
[TEMP_SENSOR_4_FAN] = thermal_cpu,
};
BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT);
/******************************************************************************/
/* MFT channels. These are logically separate from pwm_channels. */
const struct mft_t mft_channels[] = {
[MFT_CH_0] = {
.module = NPCX_MFT_MODULE_1,
.clk_src = TCKC_LFCLK,
.pwm_id = PWM_CH_FAN,
},
};
BUILD_ASSERT(ARRAY_SIZE(mft_channels) == MFT_CH_COUNT);
/******************************************************************************/
/* I2C port map configuration */
const struct i2c_port_t i2c_ports[] = {
{
.name = "sensor",
.port = I2C_PORT_SENSOR,
.kbps = 400,
.scl = GPIO_EC_I2C0_SENSOR_SCL,
.sda = GPIO_EC_I2C0_SENSOR_SDA,
},
{
.name = "usb_c0",
.port = I2C_PORT_USB_C0,
.kbps = 1000,
.scl = GPIO_EC_I2C1_USB_C0_SCL,
.sda = GPIO_EC_I2C1_USB_C0_SDA,
},
{
.name = "usb_c1",
.port = I2C_PORT_USB_C1,
.kbps = 1000,
.scl = GPIO_EC_I2C2_USB_C1_SCL,
.sda = GPIO_EC_I2C2_USB_C1_SDA,
},
{
.name = "lightbar",
.port = I2C_PORT_LIGHTBAR,
.kbps = 400,
.scl = GPIO_EC_I2C3_LEDBAR_SCL,
.sda = GPIO_EC_I2C3_LEDBAR_SDA,
},
{
.name = "power",
.port = I2C_PORT_POWER,
.kbps = 100,
.scl = GPIO_EC_I2C5_POWER_SCL,
.sda = GPIO_EC_I2C5_POWER_SDA,
},
{
.name = "eeprom",
.port = I2C_PORT_EEPROM,
.kbps = 400,
.scl = GPIO_EC_I2C7_EEPROM_SCL,
.sda = GPIO_EC_I2C7_EEPROM_SDA,
},
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
/******************************************************************************/
/* PWM configuration */
const struct pwm_t pwm_channels[] = {
[PWM_CH_LED4_SIDESEL] = {
.channel = 7,
.flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP,
/* Run at a higher frequency than the color PWM signals to avoid
* timing-based color shifts.
*/
.freq = 4800,
},
[PWM_CH_FAN] = {
.channel = 5,
.flags = PWM_CONFIG_OPEN_DRAIN,
.freq = 25000
},
[PWM_CH_KBLIGHT] = {
.channel = 3,
.flags = 0,
/*
* Set PWM frequency to multiple of 50 Hz and 60 Hz to prevent
* flicker. Higher frequencies consume similar average power to
* lower PWM frequencies, but higher frequencies record a much
* lower maximum power.
*/
.freq = 2400,
},
};
BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
static void kb_backlight_enable(void)
{
if (ec_cfg_has_keyboard_backlight() == 1)
gpio_set_level(GPIO_EC_KB_BL_EN, 1);
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, kb_backlight_enable, HOOK_PRIO_DEFAULT);
static void kb_backlight_disable(void)
{
if (ec_cfg_has_keyboard_backlight() == 1)
gpio_set_level(GPIO_EC_KB_BL_EN, 0);
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, kb_backlight_disable, HOOK_PRIO_DEFAULT);
/* USB-A charging control */
const int usb_port_enable[USB_PORT_COUNT] = {
GPIO_EN_PP5000_USBA,
};
static void ps8815_reset(void)
{
int val;
gpio_set_level(GPIO_USB_C1_RT_RST_ODL, 0);
msleep(GENERIC_MAX(PS8XXX_RESET_DELAY_MS, PS8815_PWR_H_RST_H_DELAY_MS));
gpio_set_level(GPIO_USB_C1_RT_RST_ODL, 1);
msleep(PS8815_FW_INIT_DELAY_MS);
/*
* b/144397088
* ps8815 firmware 0x01 needs special configuration
*/
CPRINTS("%s: patching ps8815 registers", __func__);
if (i2c_read8(I2C_PORT_USB_C1, PS8XXX_I2C_ADDR1_P2_FLAGS, 0x0f, &val) ==
EC_SUCCESS)
CPRINTS("ps8815: reg 0x0f was %02x", val);
if (i2c_write8(I2C_PORT_USB_C1, PS8XXX_I2C_ADDR1_P2_FLAGS, 0x0f,
0x31) == EC_SUCCESS)
CPRINTS("ps8815: reg 0x0f set to 0x31");
if (i2c_read8(I2C_PORT_USB_C1, PS8XXX_I2C_ADDR1_P2_FLAGS, 0x0f, &val) ==
EC_SUCCESS)
CPRINTS("ps8815: reg 0x0f now %02x", val);
}
void board_reset_pd_mcu(void)
{
ps8815_reset();
usb_mux_hpd_update(USBC_PORT_C1, USB_PD_MUX_HPD_LVL_DEASSERTED |
USB_PD_MUX_HPD_IRQ_DEASSERTED);
}
/******************************************************************************/
/* USBC PPC configuration */
struct ppc_config_t ppc_chips[] = {
[USBC_PORT_C0] = {
.i2c_port = I2C_PORT_USB_C0,
.i2c_addr_flags = SN5S330_ADDR0_FLAGS,
.drv = &sn5s330_drv,
},
[USBC_PORT_C1] = {
.i2c_port = I2C_PORT_USB_C1,
.i2c_addr_flags = SYV682X_ADDR0_FLAGS,
.drv = &syv682x_drv,
},
};
BUILD_ASSERT(ARRAY_SIZE(ppc_chips) == USBC_PORT_COUNT);
unsigned int ppc_cnt = ARRAY_SIZE(ppc_chips);
/******************************************************************************/
/* PPC support routines */
void ppc_interrupt(enum gpio_signal signal)
{
switch (signal) {
case GPIO_USB_C0_PPC_INT_ODL:
sn5s330_interrupt(USBC_PORT_C0);
break;
case GPIO_USB_C1_PPC_INT_ODL:
syv682x_interrupt(USBC_PORT_C1);
break;
default:
break;
}
}
/******************************************************************************/
/* BC1.2 charger detect configuration */
const struct pi3usb9201_config_t pi3usb9201_bc12_chips[] = {
[USBC_PORT_C0] = {
.i2c_port = I2C_PORT_USB_C0,
.i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS,
},
[USBC_PORT_C1] = {
.i2c_port = I2C_PORT_USB_C1,
.i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS,
},
};
BUILD_ASSERT(ARRAY_SIZE(pi3usb9201_bc12_chips) == USBC_PORT_COUNT);
/******************************************************************************/
/* USBC TCPC configuration */
struct tcpc_config_t tcpc_config[] = {
[USBC_PORT_C0] = {
.bus_type = EC_BUS_TYPE_I2C,
.i2c_info = {
.port = I2C_PORT_USB_C0,
.addr_flags = RT1715_I2C_ADDR_FLAGS,
},
.drv = &rt1715_tcpm_drv,
},
[USBC_PORT_C1] = {
.bus_type = EC_BUS_TYPE_I2C,
.i2c_info = {
.port = I2C_PORT_USB_C1,
.addr_flags = PS8XXX_I2C_ADDR1_FLAGS,
},
.flags = TCPC_FLAGS_TCPCI_REV2_0
| TCPC_FLAGS_TCPCI_REV2_0_NO_VSAFE0V,
.drv = &ps8xxx_tcpm_drv,
},
};
BUILD_ASSERT(ARRAY_SIZE(tcpc_config) == USBC_PORT_COUNT);
BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT == USBC_PORT_COUNT);
/******************************************************************************/
/* USBC mux configuration - Tiger Lake includes internal mux */
static const struct usb_mux_chain usbc1_usb3_db_retimer = {
.mux =
&(const struct usb_mux){
.usb_port = USBC_PORT_C1,
.driver = &tcpci_tcpm_usb_mux_driver,
.hpd_update = &ps8xxx_tcpc_update_hpd_status,
},
.next = NULL,
};
const struct usb_mux_chain usb_muxes[] = {
[USBC_PORT_C0] = {
.mux = &(const struct usb_mux) {
.usb_port = USBC_PORT_C0,
.driver = &virtual_usb_mux_driver,
.hpd_update = &virtual_hpd_update,
},
.next = NULL,
},
[USBC_PORT_C1] = {
.mux = &(const struct usb_mux) {
.usb_port = USBC_PORT_C1,
.driver = &virtual_usb_mux_driver,
.hpd_update = &virtual_hpd_update,
},
.next = &usbc1_usb3_db_retimer,
},
};
BUILD_ASSERT(ARRAY_SIZE(usb_muxes) == USBC_PORT_COUNT);
static void board_tcpc_init(void)
{
/* Don't reset TCPCs after initial reset */
if (!system_jumped_late())
board_reset_pd_mcu();
/* Enable PPC interrupts. */
gpio_enable_interrupt(GPIO_USB_C0_PPC_INT_ODL);
gpio_enable_interrupt(GPIO_USB_C1_PPC_INT_ODL);
/* Enable TCPC interrupts. */
gpio_enable_interrupt(GPIO_USB_C0_TCPC_INT_ODL);
gpio_enable_interrupt(GPIO_USB_C1_TCPC_INT_ODL);
/* Enable BC1.2 interrupts. */
gpio_enable_interrupt(GPIO_USB_C0_BC12_INT_ODL);
gpio_enable_interrupt(GPIO_USB_C1_BC12_INT_ODL);
if (get_board_id() <= 1) {
tcpc_config[USBC_PORT_C0].i2c_info.addr_flags =
TUSB422_I2C_ADDR_FLAGS;
tcpc_config[USBC_PORT_C0].drv = &tusb422_tcpm_drv;
tcpc_config[USBC_PORT_C0].flags = 0;
}
}
DECLARE_HOOK(HOOK_INIT, board_tcpc_init, HOOK_PRIO_INIT_CHIPSET);
/******************************************************************************/
/* TCPC support routines */
uint16_t tcpc_get_alert_status(void)
{
uint16_t status = 0;
/*
* Check which port has the ALERT line set
*/
if (!gpio_get_level(GPIO_USB_C0_TCPC_INT_ODL))
status |= PD_STATUS_TCPC_ALERT_0;
if (!gpio_get_level(GPIO_USB_C1_TCPC_INT_ODL))
status |= PD_STATUS_TCPC_ALERT_1;
return status;
}
int ppc_get_alert_status(int port)
{
if (port == USBC_PORT_C0)
return gpio_get_level(GPIO_USB_C0_PPC_INT_ODL) == 0;
else
return gpio_get_level(GPIO_USB_C1_PPC_INT_ODL) == 0;
}