From dc5ef9e359c7491ee9d476d8c4b0aef4e4142ae6 Mon Sep 17 00:00:00 2001 From: Yilun Lin Date: Thu, 3 May 2018 10:13:59 +0800 Subject: kukui: Initial setup for ec. Setup initial files and GPIOs for kukui. TEST=make BOARD=kukui -j BUG=b:80159522 b:110243480 BRANCH=none Change-Id: I5c5f1f08d0a0c8e1336a27019ea01b2e0850d3c1 Signed-off-by: Yilun Lin Signed-off-by: Nicolas Boichat Reviewed-on: https://chromium-review.googlesource.com/1041505 Commit-Ready: ChromeOS CL Exonerator Bot Tested-by: Yilun Lin --- board/kukui/battery.c | 170 ++++++++++++++++++ board/kukui/board.c | 409 ++++++++++++++++++++++++++++++++++++++++++++ board/kukui/board.h | 209 ++++++++++++++++++++++ board/kukui/build.mk | 14 ++ board/kukui/ec.tasklist | 29 ++++ board/kukui/gpio.inc | 91 ++++++++++ board/kukui/led.c | 33 ++++ board/kukui/usb_pd_policy.c | 361 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 1316 insertions(+) create mode 100644 board/kukui/battery.c create mode 100644 board/kukui/board.c create mode 100644 board/kukui/board.h create mode 100644 board/kukui/build.mk create mode 100644 board/kukui/ec.tasklist create mode 100644 board/kukui/gpio.inc create mode 100644 board/kukui/led.c create mode 100644 board/kukui/usb_pd_policy.c (limited to 'board/kukui') diff --git a/board/kukui/battery.c b/board/kukui/battery.c new file mode 100644 index 0000000000..b030845e1a --- /dev/null +++ b/board/kukui/battery.c @@ -0,0 +1,170 @@ +/* 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. + * + * Battery pack vendor provided charging profile + */ + +#include "battery.h" +#include "battery_smart.h" +#include "charge_state.h" +#include "console.h" +#include "driver/battery/max17055.h" +#include "driver/charger/rt946x.h" +#include "ec_commands.h" +#include "extpower.h" +#include "gpio.h" +#include "hooks.h" +#include "util.h" + +#define TEMP_OUT_OF_RANGE TEMP_ZONE_COUNT + +/* We have only one battery now. */ +#define BATT_ID 0 + +enum battery_type { + BATTERY_SIMPLO = 0, + BATTERY_COUNT +}; + +static const struct battery_info info[] = { + [BATTERY_SIMPLO] = { + .voltage_max = 4400, + .voltage_normal = 3860, + .voltage_min = 3000, + .precharge_current = 256, + .start_charging_min_c = 0, + .start_charging_max_c = 45, + .charging_min_c = 0, + .charging_max_c = 60, + .discharging_min_c = -20, + .discharging_max_c = 60, + }, +}; + +static const struct max17055_batt_profile batt_profile[] = { + [BATTERY_SIMPLO] = { + .is_ez_config = 1, + .design_cap = MAX17055_DESIGNCAP_REG(6910), + .ichg_term = MAX17055_ICHGTERM_REG(235), + .v_empty_detect = MAX17055_VEMPTY_REG(3000, 3600), + }, +}; + +const struct battery_info *battery_get_info(void) +{ + return &info[BATT_ID]; +} + +const struct max17055_batt_profile *max17055_get_batt_profile(void) +{ + return &batt_profile[BATT_ID]; +} + +int board_cut_off_battery(void) +{ + return rt946x_cutoff_battery(); +} + +enum battery_disconnect_state battery_get_disconnect_state(void) +{ + if (battery_is_present() == BP_YES) + return BATTERY_NOT_DISCONNECTED; + return BATTERY_DISCONNECTED; +} + +int charger_profile_override(struct charge_state_data *curr) +{ + /* battery temp in 0.1 deg C */ + int bat_temp_c = curr->batt.temperature - 2731; + + /* + * Keep track of battery temperature range: + * + * ZONE_0 ZONE_1 ZONE_2 + * -----+--------+--------+------------+----- Temperature (C) + * t0 t1 t2 t3 + */ + enum { + TEMP_ZONE_0, /* t0 < bat_temp_c <= t1 */ + TEMP_ZONE_1, /* t1 < bat_temp_c <= t2 */ + TEMP_ZONE_2, /* t2 < bat_temp_c <= t3 */ + TEMP_ZONE_COUNT + } temp_zone; + + static struct { + int temp_min; /* 0.1 deg C */ + int temp_max; /* 0.1 deg C */ + int desired_current; /* mA */ + int desired_voltage; /* mV */ + } temp_zones[BATTERY_COUNT][TEMP_ZONE_COUNT] = { + [BATTERY_SIMPLO] = { + {0, 150, 1772, 4400}, /* TEMP_ZONE_0 */ + {150, 450, 4020, 4400}, /* TEMP_ZONE_1 */ + {450, 600, 3350, 4300}, /* TEMP_ZONE_2 */ + }, + }; + BUILD_ASSERT(ARRAY_SIZE(temp_zones[0]) == TEMP_ZONE_COUNT); + BUILD_ASSERT(ARRAY_SIZE(temp_zones) == BATTERY_COUNT); + + if ((curr->batt.flags & BATT_FLAG_BAD_TEMPERATURE) || + (bat_temp_c < temp_zones[BATT_ID][0].temp_min) || + (bat_temp_c >= temp_zones[BATT_ID][TEMP_ZONE_COUNT - 1].temp_max)) + temp_zone = TEMP_OUT_OF_RANGE; + else { + for (temp_zone = 0; temp_zone < TEMP_ZONE_COUNT; temp_zone++) { + if (bat_temp_c < + temp_zones[BATT_ID][temp_zone].temp_max) + break; + } + } + + if (curr->state != ST_CHARGE) + return 0; + + switch (temp_zone) { + case TEMP_ZONE_0: + case TEMP_ZONE_1: + case TEMP_ZONE_2: + curr->requested_current = + temp_zones[BATT_ID][temp_zone].desired_current; + curr->requested_voltage = + temp_zones[BATT_ID][temp_zone].desired_voltage; + break; + case TEMP_OUT_OF_RANGE: + curr->requested_current = curr->requested_voltage = 0; + curr->batt.flags &= ~BATT_FLAG_WANT_CHARGE; + curr->state = ST_IDLE; + break; + } + + return 0; +} + +static void board_charge_termination(void) +{ + static uint8_t te; + /* Enable charge termination when we are sure battery is present. */ + if (!te && battery_is_present() == BP_YES) { + if (!rt946x_enable_charge_termination(1)) + te = 1; + } +} +DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, + board_charge_termination, + HOOK_PRIO_DEFAULT); + +/* Customs options controllable by host command. */ +#define PARAM_FASTCHARGE (CS_PARAM_CUSTOM_PROFILE_MIN + 0) + +enum ec_status charger_profile_override_get_param(uint32_t param, + uint32_t *value) +{ + return EC_RES_INVALID_PARAM; +} + +enum ec_status charger_profile_override_set_param(uint32_t param, + uint32_t value) +{ + return EC_RES_INVALID_PARAM; +} diff --git a/board/kukui/board.c b/board/kukui/board.c new file mode 100644 index 0000000000..431cf5b14b --- /dev/null +++ b/board/kukui/board.c @@ -0,0 +1,409 @@ +/* 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. + */ + +#include "adc.h" +#include "adc_chip.h" +#include "backlight.h" +#include "button.h" +#include "charge_manager.h" +#include "charge_state.h" +#include "charger.h" +#include "chipset.h" +#include "common.h" +#include "console.h" +#include "driver/accelgyro_bmi160.h" +#include "driver/charger/rt946x.h" +#include "driver/sync.h" +#include "driver/tcpm/fusb302.h" +#include "driver/temp_sensor/tmp432.h" +#include "ec_commands.h" +#include "extpower.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.h" +#include "i2c.h" +#include "power.h" +#include "power_button.h" +#include "pwm.h" +#include "pwm_chip.h" +#include "registers.h" +#include "spi.h" +#include "switch.h" +#include "system.h" +#include "task.h" +#include "tcpm.h" +#include "temp_sensor.h" +#include "temp_sensor_chip.h" +#include "thermal.h" +#include "timer.h" +#include "usb_charge.h" +#include "usb_mux.h" +#include "usb_pd_tcpm.h" +#include "util.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 signal) +{ +#ifdef HAS_TASK_PDCMD + /* Exchange status with TCPCs */ + host_command_pd_send_status(PD_CHARGE_NO_CHANGE); +#endif +} + +static void warm_reset_request_interrupt(enum gpio_signal signal) +{ + CPRINTS("AP wants warm reset"); + chipset_reset(); +} + +static void ap_watchdog_interrupt(enum gpio_signal signal) +{ + CPRINTS("AP watchdog triggered."); + cflush(); + /* TODO(b:109900671): Handle AP watchdog, when necessary. */ +} + +#include "gpio_list.h" + +/******************************************************************************/ +/* ADC channels. Must be in the exactly same order as in enum adc_channel. */ +const struct adc_t adc_channels[] = { + [ADC_BOARD_ID] = {"BOARD_ID", 3300, 4096, 0, STM32_AIN(10)}, +}; +BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); + +/******************************************************************************/ +/* I2C ports */ +const struct i2c_port_t i2c_ports[] = { + {"charger", I2C_PORT_CHARGER, 400, GPIO_I2C1_SCL, GPIO_I2C1_SDA}, + {"tcpc0", I2C_PORT_TCPC0, 400, GPIO_I2C1_SCL, GPIO_I2C1_SDA}, + {"battery", I2C_PORT_BATTERY, 400, GPIO_I2C2_SCL, GPIO_I2C2_SDA}, + {"accelgyro", I2C_PORT_ACCEL, 400, GPIO_I2C2_SCL, GPIO_I2C2_SDA}, +}; +const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); + +/* power signal list. Must match order of enum power_signal. */ +const struct power_signal_info power_signal_list[] = { + {GPIO_AP_IN_SLEEP_L, POWER_SIGNAL_ACTIVE_LOW, "AP_IN_S3_L"}, + {GPIO_PMIC_EC_RESETB, POWER_SIGNAL_ACTIVE_HIGH, "PMIC_PWR_GOOD"}, +}; +BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); + +#ifdef CONFIG_TEMP_SENSOR_TMP432 +/* Temperature sensors data; must be in same order as enum temp_sensor_id. */ +const struct temp_sensor_t temp_sensors[] = { + {"TMP432_Internal", TEMP_SENSOR_TYPE_BOARD, tmp432_get_val, + TMP432_IDX_LOCAL, 4}, + {"TMP432_Sensor_1", TEMP_SENSOR_TYPE_BOARD, tmp432_get_val, + TMP432_IDX_REMOTE1, 4}, + {"TMP432_Sensor_2", TEMP_SENSOR_TYPE_BOARD, tmp432_get_val, + TMP432_IDX_REMOTE2, 4}, +}; +BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); + +/* + * Thermal limits for each temp sensor. All temps are in degrees K. Must be in + * same order as enum temp_sensor_id. To always ignore any temp, use 0. + */ +struct ec_thermal_config thermal_params[] = { + {{0, 0, 0}, 0, 0}, /* TMP432_Internal */ + {{0, 0, 0}, 0, 0}, /* TMP432_Sensor_1 */ + {{0, 0, 0}, 0, 0}, /* TMP432_Sensor_2 */ +}; +BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT); +#endif + +/******************************************************************************/ +/* SPI devices */ +const struct spi_device_t spi_devices[] = { +}; +const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices); + +/******************************************************************************/ +const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = { + {I2C_PORT_TCPC0, FUSB302_I2C_SLAVE_ADDR, &fusb302_tcpm_drv}, +}; + +struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = { + { + .port_addr = 0, + .driver = &virtual_usb_mux_driver, + .hpd_update = &virtual_hpd_update, + }, +}; + +void board_reset_pd_mcu(void) +{ +} + +uint16_t tcpc_get_alert_status(void) +{ + uint16_t status = 0; + + if (!gpio_get_level(GPIO_USB_C0_PD_INT_ODL)) + status |= PD_STATUS_TCPC_ALERT_0; + + return status; +} + +int board_set_active_charge_port(int charge_port) +{ + CPRINTS("New chg p%d", charge_port); + + switch (charge_port) { + case 0: + /* Don't charge from a source port */ + if (board_vbus_source_enabled(charge_port)) + return -1; + break; + case CHARGE_PORT_NONE: + /* + * To ensure the fuel gauge (max17055) is always powered + * even when battery is disconnected, keep VBAT rail on but + * set the charging current to minimum. + */ + charger_set_current(0); + break; + default: + panic("Invalid charge port\n"); + break; + } + + return EC_SUCCESS; +} + +void board_set_charge_limit(int port, int supplier, int charge_ma, + int max_ma, int charge_mv) +{ + charge_set_input_current_limit(MAX(charge_ma, + CONFIG_CHARGER_INPUT_CURRENT), charge_mv); +} + +int extpower_is_present(void) +{ + /* + * The charger will indicate VBUS presence if we're sourcing 5V, + * so exclude such ports. + */ + if (board_vbus_source_enabled(0)) + return 0; + else + return tcpm_get_vbus_level(0); +} + +int pd_snk_is_vbus_provided(int port) +{ + if (port) + panic("Invalid charge port\n"); + + return rt946x_is_vbus_ready(); +} + +static void board_init(void) +{ + /* Enable TCPC alert interrupts */ + gpio_enable_interrupt(GPIO_USB_C0_PD_INT_ODL); + + /* Enable charger interrupts */ + gpio_enable_interrupt(GPIO_CHARGER_INT_ODL); + + /* Enable reboot / shutdown / sleep control inputs from AP */ + gpio_enable_interrupt(GPIO_WARM_RESET_REQ); + gpio_enable_interrupt(GPIO_AP_EC_WATCHDOG_L); + gpio_enable_interrupt(GPIO_AP_IN_SLEEP_L); + + /* Enable interrupts from BMI160 sensor. */ + gpio_enable_interrupt(GPIO_ACCEL_INT_ODL); + + /* Enable interrupt for the camera vsync. */ + gpio_enable_interrupt(GPIO_SYNC_INT); + + /* Enable interrupt from PMIC. */ + gpio_enable_interrupt(GPIO_PMIC_EC_RESETB); +} +DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT); + +void board_config_pre_init(void) +{ + STM32_RCC_AHBENR |= STM32_RCC_HB_DMA1; + /* + * Remap USART1: + * + * Ch4: USART1_TX / Ch5: USART1_RX + */ + STM32_DMA_CSELR(STM32_DMAC_CH4) = (1 << 15) | (1 << 19); +} + +enum kukui_board_version { + BOARD_VERSION_UNKNOWN = -1, + BOARD_VERSION_REV0 = 0, + BOARD_VERSION_REV1 = 1, + BOARD_VERSION_REV2 = 2, + BOARD_VERSION_REV3 = 3, + BOARD_VERSION_REV4 = 4, + BOARD_VERSION_REV5 = 5, + BOARD_VERSION_REV6 = 6, + BOARD_VERSION_REV7 = 7, + BOARD_VERSION_REV8 = 8, + BOARD_VERSION_REV9 = 9, + BOARD_VERSION_REV10 = 10, + BOARD_VERSION_REV11 = 11, + BOARD_VERSION_REV12 = 12, + BOARD_VERSION_REV13 = 13, + BOARD_VERSION_REV14 = 14, + BOARD_VERSION_REV15 = 15, + BOARD_VERSION_COUNT, +}; + +struct { + enum kukui_board_version version; + int expect_mv; +} const kukui_boards[] = { + { BOARD_VERSION_REV0, 109 }, /* 51.1K , 2.2K(gru 3.3K) ohm */ + { BOARD_VERSION_REV1, 211 }, /* 51.1k , 6.8K ohm */ + { BOARD_VERSION_REV2, 319 }, /* 51.1K , 11K ohm */ + { BOARD_VERSION_REV3, 427 }, /* 56K , 17.4K ohm */ + { BOARD_VERSION_REV4, 542 }, /* 51.1K , 22K ohm */ + { BOARD_VERSION_REV5, 666 }, /* 51.1K , 30K ohm */ + { BOARD_VERSION_REV6, 781 }, /* 51.1K , 39.2K ohm */ + { BOARD_VERSION_REV7, 900 }, /* 56K , 56K ohm */ + { BOARD_VERSION_REV8, 1023 }, /* 47K , 61.9K ohm */ + { BOARD_VERSION_REV9, 1137 }, /* 47K , 80.6K ohm */ + { BOARD_VERSION_REV10, 1240 }, /* 56K , 124K ohm */ + { BOARD_VERSION_REV11, 1343 }, /* 51.1K , 150K ohm */ + { BOARD_VERSION_REV12, 1457 }, /* 47K , 200K ohm */ + { BOARD_VERSION_REV13, 1576 }, /* 47K , 330K ohm */ + { BOARD_VERSION_REV14, 1684 }, /* 47K , 680K ohm */ + { BOARD_VERSION_REV15, 1800 }, /* 56K , NC */ +}; +BUILD_ASSERT(ARRAY_SIZE(kukui_boards) == BOARD_VERSION_COUNT); + +#define THRESHOLD_MV 56 /* Simply assume 1800/16/2 */ + +int board_get_version(void) +{ + static int version = BOARD_VERSION_UNKNOWN; + int mv; + int i; + + if (version != BOARD_VERSION_UNKNOWN) + return version; + + gpio_set_level(GPIO_EC_BOARD_ID_EN_L, 0); + /* Wait to allow cap charge */ + msleep(10); + mv = adc_read_channel(ADC_BOARD_ID); + + if (mv == ADC_READ_ERROR) + mv = adc_read_channel(ADC_BOARD_ID); + + gpio_set_level(GPIO_EC_BOARD_ID_EN_L, 1); + + for (i = 0; i < BOARD_VERSION_COUNT; ++i) { + if (mv < kukui_boards[i].expect_mv + THRESHOLD_MV) { + version = kukui_boards[i].version; + break; + } + } + + /* + * Disable ADC module after we detect the board version, + * since this is the only thing ADC module needs to do + * for this board. + */ + if (version != BOARD_VERSION_UNKNOWN) + adc_disable(); + + return version; +} + +/* Motion sensors */ +/* Mutexes */ +static struct mutex g_base_mutex; + +static struct bmi160_drv_data_t g_bmi160_data; + +/* Matrix to rotate accelerometer into standard reference frame */ +const matrix_3x3_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[] = { + /* + * Note: bmi160: supports accelerometer and gyro sensor + * Requirement: accelerometer sensor must init before gyro sensor + * DO NOT change the order of the following table. + */ + [LID_ACCEL] = { + .name = "Accel", + .active_mask = SENSOR_ACTIVE_S0_S3, + .chip = MOTIONSENSE_CHIP_BMI160, + .type = MOTIONSENSE_TYPE_ACCEL, + .location = MOTIONSENSE_LOC_LID, + .drv = &bmi160_drv, + .mutex = &g_base_mutex, + .drv_data = &g_bmi160_data, + .port = I2C_PORT_ACCEL, + .addr = BMI160_ADDR0, + .rot_standard_ref = &base_standard_ref, + .default_range = 4, /* g */ + .min_frequency = BMI160_ACCEL_MIN_FREQ, + .max_frequency = BMI160_ACCEL_MAX_FREQ, + .config = { + /* Enable accel in S0 */ + [SENSOR_CONFIG_EC_S0] = { + .odr = 10000 | ROUND_UP_FLAG, + .ec_rate = 100 * MSEC, + }, + }, + }, + [LID_GYRO] = { + .name = "Gyro", + .active_mask = SENSOR_ACTIVE_S0_S3, + .chip = MOTIONSENSE_CHIP_BMI160, + .type = MOTIONSENSE_TYPE_GYRO, + .location = MOTIONSENSE_LOC_LID, + .drv = &bmi160_drv, + .mutex = &g_base_mutex, + .drv_data = &g_bmi160_data, + .port = I2C_PORT_ACCEL, + .addr = BMI160_ADDR0, + .default_range = 1000, /* dps */ + .rot_standard_ref = &base_standard_ref, + .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 board_allow_i2c_passthru(int port) +{ + return (port == I2C_PORT_VIRTUAL_BATTERY); +} + +int tablet_get_mode(void) +{ + /* Always in tablet mode */ + return 1; +} + +void usb_charger_set_switches(int port, enum usb_switch setting) +{ +} diff --git a/board/kukui/board.h b/board/kukui/board.h new file mode 100644 index 0000000000..1da4fe0236 --- /dev/null +++ b/board/kukui/board.h @@ -0,0 +1,209 @@ +/* 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. + */ + +/* Configuration for Kukui */ + +#ifndef __CROS_EC_BOARD_H +#define __CROS_EC_BOARD_H + +/* Optional modules */ +#define CONFIG_ADC +#undef CONFIG_ADC_WATCHDOG +#define CONFIG_CHIPSET_MT8183 +#define CONFIG_CMD_ACCELS +#define CONFIG_CMD_RTC +#define CONFIG_EMULATED_SYSRQ +#undef CONFIG_HIBERNATE +#define CONFIG_HOSTCMD_RTC +#define CONFIG_I2C +#define CONFIG_I2C_MASTER +#define CONFIG_I2C_VIRTUAL_BATTERY +#define CONFIG_I2C_PASSTHRU_RESTRICTED +#define CONFIG_LED_COMMON +#define CONFIG_LOW_POWER_IDLE +#define CONFIG_POWER_COMMON +#define CONFIG_SPI +#define CONFIG_SPI_MASTER +#define CONFIG_STM_HWTIMER32 +#define CONFIG_STM32_CLOCK_LSE +#define CONFIG_SWITCH +#define CONFIG_WATCHDOG_HELP + +#define CONFIG_SYSTEM_UNLOCKED /* Allow dangerous commands for testing */ + +#undef CONFIG_UART_CONSOLE +#define CONFIG_UART_CONSOLE 1 +#define CONFIG_UART_RX_DMA + +/* Region sizes are no longer a power of 2 so we can't enable MPU */ +#undef CONFIG_MPU + +/* Optional features */ +#define CONFIG_BOARD_PRE_INIT +#define CONFIG_BOARD_VERSION_CUSTOM +#define CONFIG_BUTTON_TRIGGERED_RECOVERY +#define CONFIG_CHARGER_ILIM_PIN_DISABLED +#define CONFIG_FORCE_CONSOLE_RESUME +#define CONFIG_HOST_COMMAND_STATUS + +/* Required for FAFT */ +#define CONFIG_CMD_BUTTON + +/* By default, set hcdebug to off */ +#undef CONFIG_HOSTCMD_DEBUG_MODE +#define CONFIG_HOSTCMD_DEBUG_MODE HCDEBUG_OFF +#undef CONFIG_LID_SWITCH +#undef CONFIG_LTO +#define CONFIG_POWER_BUTTON +#define CONFIG_POWER_BUTTON_IGNORE_LID +#define CONFIG_POWER_TRACK_HOST_SLEEP_STATE +#define CONFIG_SOFTWARE_PANIC +#define CONFIG_VBOOT_HASH +#define CONFIG_VOLUME_BUTTONS + +#define CONFIG_CHARGER +#define CONFIG_CHARGER_MT6370 +#define CONFIG_CHARGER_INPUT_CURRENT 512 +#define CONFIG_CHARGER_V2 +#define CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON 2 +#define CONFIG_CHARGER_LIMIT_POWER_THRESH_BAT_PCT 2 +#define CONFIG_CHARGER_LIMIT_POWER_THRESH_CHG_MW 15000 +#define CONFIG_CHARGER_PROFILE_OVERRIDE +#define CONFIG_CHARGER_DISCHARGE_ON_AC +#define CONFIG_CHARGER_OTG +#define CONFIG_USB_CHARGER +#define CONFIG_USB_MUX_VIRTUAL + +/* Increase tx buffer size, as we'd like to stream EC log to AP. */ +#undef CONFIG_UART_TX_BUF_SIZE +#define CONFIG_UART_TX_BUF_SIZE 4096 + +/* Motion Sensors */ +#define CONFIG_ACCELGYRO_BMI160 +#define CONFIG_ACCEL_INTERRUPTS +#define CONFIG_ACCELGYRO_BMI160_INT_EVENT TASK_EVENT_CUSTOM(4) + +/* Camera VSYNC */ +#define CONFIG_SYNC +#define CONFIG_SYNC_COMMAND +#define CONFIG_SYNC_INT_EVENT TASK_EVENT_CUSTOM(5) + +/* To be able to indicate the device is in tablet mode. */ +#define CONFIG_TABLET_MODE_SWITCH + +/* FIFO size is in power of 2. */ +#define CONFIG_ACCEL_FIFO 256 +#define CONFIG_ACCEL_FIFO_THRES (CONFIG_ACCEL_FIFO / 3) + +/* USB PD config */ +#define CONFIG_CHARGE_MANAGER +#define CONFIG_USB_POWER_DELIVERY +#define CONFIG_USB_PD_ALT_MODE +#define CONFIG_USB_PD_ALT_MODE_DFP +#define CONFIG_USB_PD_DISCHARGE_GPIO +#define CONFIG_USB_PD_DUAL_ROLE +#define CONFIG_USB_PD_LOGGING +#define CONFIG_USB_PD_PORT_COUNT 1 +#define CONFIG_USB_PD_TCPM_FUSB302 +#define CONFIG_USB_PD_VBUS_DETECT_TCPC +#define CONFIG_USB_PD_5V_CHARGER_CTRL +#define ADC_VBUS -1 +#define CONFIG_USBC_SS_MUX +#define CONFIG_USBC_VCONN +#define CONFIG_USBC_VCONN_SWAP +#define CONFIG_USB_PD_COMM_LOCKED + +#define CONFIG_BATTERY_CUT_OFF +#define CONFIG_BATTERY_PRESENT_CUSTOM +#define CONFIG_BATTERY_REVIVE_DISCONNECT +#define CONFIG_BATTERY_MAX17055 + +/* Battery parameters for max17055 ModelGauge m5 algorithm. */ +#define BATTERY_MAX17055_RSENSE 5 /* m-ohm */ +#define BATTERY_DESIRED_CHARGING_CURRENT 2000 /* mA */ + +#define PD_OPERATING_POWER_MW 15000 +#define PD_MAX_POWER_MW ((PD_MAX_VOLTAGE_MV * PD_MAX_CURRENT_MA) / 1000) +#define PD_MAX_CURRENT_MA 3000 +#define PD_MAX_VOLTAGE_MV 5500 + +#define PD_POWER_SUPPLY_TURN_ON_DELAY 30000 /* us */ +#define PD_POWER_SUPPLY_TURN_OFF_DELAY 50000 /* us */ +#define PD_VCONN_SWAP_DELAY 5000 /* us */ + +/* Timer selection */ +#define TIM_CLOCK32 2 +#define TIM_WATCHDOG 7 + +/* 48 MHz SYSCLK clock frequency */ +#define CPU_CLOCK 48000000 + +/* Optional for testing */ +#undef CONFIG_PECI +#undef CONFIG_PSTORE + +/* Modules we want to exclude */ +#undef CONFIG_CMD_BATTFAKE +#undef CONFIG_CMD_FLASH +#undef CONFIG_CMD_HASH +#undef CONFIG_CMD_MD +#undef CONFIG_CMD_POWERINDEBUG +#undef CONFIG_CMD_TIMERINFO + +#define CONFIG_TASK_PROFILING + +/* I2C ports */ +#define I2C_PORT_CHARGER 0 +#define I2C_PORT_TCPC0 0 +#define I2C_PORT_BATTERY 1 +#define I2C_PORT_VIRTUAL_BATTERY I2C_PORT_BATTERY +#define I2C_PORT_ACCEL 1 + +/* Route sbs host requests to virtual battery driver */ +#define VIRTUAL_BATTERY_ADDR 0x16 + +/* Enable Accel over SPI */ +#define CONFIG_SPI_ACCEL_PORT 0 /* The first SPI master port (SPI2) */ + +#define CONFIG_KEYBOARD_PROTOCOL_MKBP +#define CONFIG_MKBP_EVENT +/* Define the MKBP events which are allowed to wakeup AP in S3. */ +#define CONFIG_MKBP_WAKEUP_MASK \ + (EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON) |\ + EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) + +#ifndef __ASSEMBLER__ + +enum adc_channel { + /* Real ADC channels begin here */ + ADC_BOARD_ID = 0, + ADC_CH_COUNT +}; + +/* power signal definitions */ +enum power_signal { + AP_IN_S3_L, + PMIC_PWR_GOOD, + + /* Number of signals */ + POWER_SIGNAL_COUNT, +}; + +/* Motion sensors */ +enum sensor_id { + LID_ACCEL = 0, + LID_GYRO, + VSYNC, +}; + +#include "gpio_signal.h" +#include "registers.h" + +void board_reset_pd_mcu(void); +int board_get_version(void); + +#endif /* !__ASSEMBLER__ */ + +#endif /* __CROS_EC_BOARD_H */ diff --git a/board/kukui/build.mk b/board/kukui/build.mk new file mode 100644 index 0000000000..7e238d3ac9 --- /dev/null +++ b/board/kukui/build.mk @@ -0,0 +1,14 @@ +# -*- makefile -*- +# 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. +# +# Board specific files build +# +# +# STmicro STM32F098VC +CHIP:=stm32 +CHIP_FAMILY:=stm32f0 +CHIP_VARIANT:=stm32f09x + +board-y=battery.o board.o usb_pd_policy.o led.o diff --git a/board/kukui/ec.tasklist b/board/kukui/ec.tasklist new file mode 100644 index 0000000000..414c235375 --- /dev/null +++ b/board/kukui/ec.tasklist @@ -0,0 +1,29 @@ +/* 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. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_ALWAYS(n, r, d, s) for base tasks and + * TASK_NOTEST(n, r, d, s) for tasks that can be excluded in test binaries, + * where : + * 'n' is the name of the task + * 'r' is the main routine of the task + * 'd' is an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TASK_LIST \ + TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(CHARGER, charger_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(USB_CHG, usb_charger_task, NULL, TASK_STACK_SIZE) \ + TASK_NOTEST(CHIPSET, chipset_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(MOTIONSENSE, motion_sense_task, NULL, VENTI_TASK_STACK_SIZE) \ + TASK_NOTEST(PDCMD, pd_command_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(HOSTCMD, host_command_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) + diff --git a/board/kukui/gpio.inc b/board/kukui/gpio.inc new file mode 100644 index 0000000000..20c91d9b60 --- /dev/null +++ b/board/kukui/gpio.inc @@ -0,0 +1,91 @@ +/* -*- mode: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. + */ + +/* + * Declare symbolic names for all the GPIOs that we care about. + * Note: Those with interrupt handlers must be declared first. + */ + +/* Interrupts */ +GPIO_INT(SPI1_NSS, PIN(A, 15), GPIO_INT_BOTH, + spi_event) +GPIO_INT(USB_C0_PD_INT_ODL, PIN(B, 1), GPIO_INT_FALLING | GPIO_PULL_UP, + tcpc_alert_event) +GPIO_INT(VOLUME_UP_L, PIN(B, 10), GPIO_INT_BOTH | GPIO_PULL_UP, + button_interrupt) /* EC_VOLUP_BTN_ODL */ +GPIO_INT(VOLUME_DOWN_L, PIN(B, 11), GPIO_INT_BOTH | GPIO_PULL_UP, + button_interrupt) /* EC_VOLDN_BTN_ODL */ +GPIO_INT(POWER_BUTTON_L, PIN(A, 0), GPIO_INT_BOTH | GPIO_PULL_UP, + power_button_interrupt) /* EC_PWR_BTN_ODL */ + +GPIO_INT(AP_IN_SLEEP_L, PIN(C, 12), GPIO_INT_BOTH | GPIO_PULL_DOWN, + power_signal_interrupt) +GPIO_INT(PMIC_EC_RESETB, PIN(B, 7), GPIO_INT_BOTH | GPIO_PULL_UP, + power_signal_interrupt) +GPIO_INT(WARM_RESET_REQ, PIN(A, 3), GPIO_INT_RISING | GPIO_PULL_DOWN, + warm_reset_request_interrupt) +GPIO_INT(AP_EC_WATCHDOG_L, PIN(C, 2), GPIO_INT_FALLING | GPIO_PULL_UP, + ap_watchdog_interrupt) + +GPIO_INT(ACCEL_INT_ODL, PIN(A, 4), GPIO_INT_FALLING | GPIO_SEL_1P8V | GPIO_PULL_UP, + bmi160_interrupt) +GPIO_INT(SYNC_INT, PIN(A, 5), GPIO_INT_RISING | GPIO_PULL_DOWN, + sync_interrupt) +GPIO_INT(CHARGER_INT_ODL, PIN(C, 13), GPIO_INPUT | GPIO_PULL_UP, + rt946x_interrupt) + +/* Voltage rails control pins */ +GPIO(PP3300_S0_EN, PIN(B, 6), GPIO_OUT_LOW) +GPIO(PP1800_S3_EN, PIN(C, 7), GPIO_OUT_LOW) +GPIO(PP3300_S3_EN, PIN(A, 8), GPIO_OUT_LOW) + +/* Reset pins */ +GPIO(AP_SYS_RST_L, PIN(C, 11), GPIO_ODR_LOW) +GPIO(PMIC_WATCHDOG_L, PIN(C, 3), GPIO_ODR_HIGH) +GPIO(PMIC_EN_ODL, PIN(C, 10), GPIO_OUT_LOW) +GPIO(PMIC_FORCE_RESET, PIN(A, 2), GPIO_OUT_LOW) +GPIO(MT6370_RST_L, PIN(F, 0), GPIO_OUT_LOW) + +/* + * I2C pins should be configured as inputs until I2C module is + * initialized. This will avoid driving the lines unintentionally. + */ +GPIO(I2C1_SCL, PIN(B, 8), GPIO_INPUT) +GPIO(I2C1_SDA, PIN(B, 9), GPIO_INPUT) +GPIO(I2C2_SCL, PIN(A, 11), GPIO_INPUT) +GPIO(I2C2_SDA, PIN(A, 12), GPIO_INPUT) + +/* Analog pins */ +GPIO(BOARD_ID, PIN(C, 0), GPIO_ANALOG) + +/* Other input pins */ +GPIO(WP_L, PIN(C, 8), GPIO_INPUT) /* EC_FLASH_WP_ODL */ +GPIO(CCD_MODE_ODL, PIN(C, 9), GPIO_INPUT | GPIO_PULL_UP) +GPIO(BOOT0, PIN(F, 11), GPIO_INPUT) + +/* Other output pins */ +GPIO(ENTERING_RW, PIN(C, 6), GPIO_ODR_HIGH) /* EC_ENTERING_RW_ODL */ +GPIO(EC_INT_L, PIN(B, 12), GPIO_ODR_HIGH) /* EC_AP_INT_ODL */ +GPIO(EC_BOARD_ID_EN_L, PIN(C, 15), GPIO_ODR_HIGH) /* EC_BOARD_ID_EN_ODL */ +GPIO(USB_C0_DISCHARGE, PIN(B, 0), GPIO_OUT_LOW) +GPIO(USB_C0_DP_OE_L, PIN(C, 5), GPIO_OUT_HIGH) +GPIO(USB_C0_DP_POLARITY, PIN(C, 14), GPIO_OUT_LOW) +GPIO(USB_C0_HPD_OD, PIN(F, 1), GPIO_OUT_LOW) +GPIO(OTG_EN, PIN(A, 6), GPIO_OUT_LOW) +GPIO(BOOTBLOCK_MUX_OE, PIN(C, 4), GPIO_ODR_HIGH) +GPIO(BOOTBLOCK_EN_L, PIN(C, 1), GPIO_ODR_HIGH) + +/* USART1: PA9/PA10 */ +ALTERNATE(PIN_MASK(A, 0x0600), 1, MODULE_UART, 0) +/* I2C MASTER: PB8/9 */ +ALTERNATE(PIN_MASK(B, 0x0300), 1, MODULE_I2C, GPIO_ODR_HIGH | GPIO_PULL_UP) +/* I2C MASTER: PA11/12 */ +ALTERNATE(PIN_MASK(B, 0x1800), 5, MODULE_I2C, GPIO_ODR_HIGH | GPIO_PULL_UP) +/* SPI SLAVE: PB3/4/5/13/14/15 */ +ALTERNATE(PIN_MASK(B, 0xE038), 0, MODULE_SPI, 0) +/* SPI SLAVE CS: PA15 */ +ALTERNATE(PIN_MASK(A, 0x8000), 0, MODULE_SPI, 0) diff --git a/board/kukui/led.c b/board/kukui/led.c new file mode 100644 index 0000000000..68e7570303 --- /dev/null +++ b/board/kukui/led.c @@ -0,0 +1,33 @@ +/* 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. + * + * Battery LED control for Kukui board. + *TODO(b:80160408): Implement mt6370 led driver. + */ + +#include "hooks.h" +#include "led_common.h" + +/* LEDs on Kukui are active low. */ +#define BAT_LED_ON 0 +#define BAT_LED_OFF 1 + +const enum ec_led_id supported_led_ids[] = { EC_LED_ID_BATTERY_LED }; + +const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); + +void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) +{ +} + +int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness) +{ + return EC_SUCCESS; +} + +/* Called by hook task every 1 sec */ +static void led_second(void) +{ +} +DECLARE_HOOK(HOOK_SECOND, led_second, HOOK_PRIO_DEFAULT); diff --git a/board/kukui/usb_pd_policy.c b/board/kukui/usb_pd_policy.c new file mode 100644 index 0000000000..67f74c6715 --- /dev/null +++ b/board/kukui/usb_pd_policy.c @@ -0,0 +1,361 @@ +/* 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. + */ + +#include "atomic.h" +#include "charger.h" +#include "charge_manager.h" +#include "common.h" +#include "console.h" +#include "driver/charger/rt946x.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "usb_mux.h" +#include "usb_pd.h" +#include "usb_pd_tcpm.h" + +#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) +#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) + +#define PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\ + PDO_FIXED_COMM_CAP) + +const uint32_t pd_src_pdo[] = { + PDO_FIXED(5000, 1500, PDO_FIXED_FLAGS), +}; +const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo); + +const uint32_t pd_snk_pdo[] = { + PDO_FIXED(5000, 500, PDO_FIXED_FLAGS), + PDO_BATT(4750, + (int)(PD_MAX_VOLTAGE_MV * 1.05), + PD_OPERATING_POWER_MW), + PDO_VAR(4750, + (int)(PD_MAX_VOLTAGE_MV * 1.05), + PD_MAX_CURRENT_MA), +}; +const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo); + +int pd_is_valid_input_voltage(int mv) +{ + return 1; +} + +void pd_transition_voltage(int idx) +{ + /* No-operation: we are always 5V */ +} + +static uint8_t vbus_en; + +int board_vbus_source_enabled(int port) +{ + return vbus_en; +} + +int pd_set_power_supply_ready(int port) +{ + + pd_set_vbus_discharge(port, 0); + /* Provide VBUS */ + vbus_en = 1; + charger_enable_otg_power(1); + + /* notify host of power info change */ + pd_send_host_event(PD_EVENT_POWER_CHANGE); + + return EC_SUCCESS; /* we are ready */ +} + +void pd_power_supply_reset(int port) +{ + int prev_en; + + prev_en = vbus_en; + /* Disable VBUS */ + vbus_en = 0; + charger_enable_otg_power(0); + /* Enable discharge if we were previously sourcing 5V */ + if (prev_en) + pd_set_vbus_discharge(port, 1); + + /* notify host of power info change */ + pd_send_host_event(PD_EVENT_POWER_CHANGE); +} + +void typec_set_source_current_limit(int port, int rp) +{ + /* No-operation */ +} + +int pd_board_checks(void) +{ + return EC_SUCCESS; +} + +int pd_check_power_swap(int port) +{ + /* + * Allow power swap as long as we are acting as a dual role device, + * otherwise assume our role is fixed (not in S0 or console command + * to fix our role). + */ + return pd_get_dual_role() == PD_DRP_TOGGLE_ON ? 1 : 0; +} + +int pd_check_data_swap(int port, int data_role) +{ + /* Allow data swap if we are a UFP, otherwise don't allow */ + return (data_role == PD_ROLE_UFP) ? 1 : 0; +} + +int pd_check_vconn_swap(int port) +{ + /* + * VCONN is provided directly by the battery (PPVAR_SYS) + * but use the same rules as power swap. + */ + return pd_get_dual_role() == PD_DRP_TOGGLE_ON ? 1 : 0; +} + +void pd_execute_data_swap(int port, int data_role) +{ + /* Do nothing */ +} + +void pd_check_pr_role(int port, int pr_role, int flags) +{ + /* + * If partner is dual-role power and dualrole toggling is on, consider + * if a power swap is necessary. + */ + if ((flags & PD_FLAGS_PARTNER_DR_POWER) && + pd_get_dual_role() == PD_DRP_TOGGLE_ON) { + /* + * If we are a sink and partner is not externally powered, then + * swap to become a source. If we are source and partner is + * externally powered, swap to become a sink. + */ + int partner_extpower = flags & PD_FLAGS_PARTNER_EXTPOWER; + + if ((!partner_extpower && pr_role == PD_ROLE_SINK) || + (partner_extpower && pr_role == PD_ROLE_SOURCE)) + pd_request_power_swap(port); + } +} + +void pd_check_dr_role(int port, int dr_role, int flags) +{ + /* If UFP, try to switch to DFP */ + if ((flags & PD_FLAGS_PARTNER_DR_DATA) && dr_role == PD_ROLE_UFP) + pd_request_data_swap(port); +} +/* ----------------- Vendor Defined Messages ------------------ */ +const struct svdm_response svdm_rsp = { + .identity = NULL, + .svids = NULL, + .modes = NULL, +}; + +int pd_custom_vdm(int port, int cnt, uint32_t *payload, + uint32_t **rpayload) +{ + int cmd = PD_VDO_CMD(payload[0]); + uint16_t dev_id = 0; + int is_rw; + + /* make sure we have some payload */ + if (cnt == 0) + return 0; + + switch (cmd) { + case VDO_CMD_VERSION: + /* guarantee last byte of payload is null character */ + *(payload + cnt - 1) = 0; + CPRINTF("version: %s\n", (char *)(payload+1)); + break; + case VDO_CMD_READ_INFO: + case VDO_CMD_SEND_INFO: + /* copy hash */ + if (cnt == 7) { + dev_id = VDO_INFO_HW_DEV_ID(payload[6]); + is_rw = VDO_INFO_IS_RW(payload[6]); + + CPRINTF("DevId:%d.%d SW:%d RW:%d\n", + HW_DEV_ID_MAJ(dev_id), + HW_DEV_ID_MIN(dev_id), + VDO_INFO_SW_DBG_VER(payload[6]), + is_rw); + } else if (cnt == 6) { + /* really old devices don't have last byte */ + pd_dev_store_rw_hash(port, dev_id, payload + 1, + SYSTEM_IMAGE_UNKNOWN); + } + break; + case VDO_CMD_CURRENT: + CPRINTF("Current: %dmA\n", payload[1]); + break; + case VDO_CMD_FLIP: + usb_mux_flip(port); + break; +#ifdef CONFIG_USB_PD_LOGGING + case VDO_CMD_GET_LOG: + pd_log_recv_vdm(port, cnt, payload); + break; +#endif /* CONFIG_USB_PD_LOGGING */ + } + + return 0; +} + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP +static int dp_flags[CONFIG_USB_PD_PORT_COUNT]; +/* DP Status VDM as returned by UFP */ +static uint32_t dp_status[CONFIG_USB_PD_PORT_COUNT]; + +static void svdm_safe_dp_mode(int port) +{ + /* make DP interface safe until configure */ + dp_flags[port] = 0; + dp_status[port] = 0; + usb_mux_set(port, TYPEC_MUX_NONE, + USB_SWITCH_CONNECT, pd_get_polarity(port)); +} + +static int svdm_enter_dp_mode(int port, uint32_t mode_caps) +{ + /* Only enter mode if device is DFP_D capable */ + if (mode_caps & MODE_DP_SNK) { + svdm_safe_dp_mode(port); + return 0; + } + + return -1; +} + +static int svdm_dp_status(int port, uint32_t *payload) +{ + int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT); + + payload[0] = VDO(USB_SID_DISPLAYPORT, 1, + CMD_DP_STATUS | VDO_OPOS(opos)); + payload[1] = VDO_DP_STATUS(0, /* HPD IRQ ... not applicable */ + 0, /* HPD level ... not applicable */ + 0, /* exit DP? ... no */ + 0, /* usb mode? ... no */ + 0, /* multi-function ... no */ + (!!(dp_flags[port] & DP_FLAGS_DP_ON)), + 0, /* power low? ... no */ + (!!(dp_flags[port] & DP_FLAGS_DP_ON))); + return 2; +}; + +static int svdm_dp_config(int port, uint32_t *payload) +{ + int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT); + int pin_mode = pd_dfp_dp_get_pin_mode(port, dp_status[port]); + + if (!pin_mode) + return 0; + + payload[0] = VDO(USB_SID_DISPLAYPORT, 1, + CMD_DP_CONFIG | VDO_OPOS(opos)); + payload[1] = VDO_DP_CFG(pin_mode, /* pin mode */ + 1, /* DPv1.3 signaling */ + 2); /* UFP connected */ + return 2; +}; + +static void svdm_dp_post_config(int port) +{ + dp_flags[port] |= DP_FLAGS_DP_ON; +} + +static int svdm_dp_attention(int port, uint32_t *payload) +{ + const struct usb_mux *mux = &usb_muxes[port]; + int lvl = PD_VDO_DPSTS_HPD_LVL(payload[1]); + int irq = PD_VDO_DPSTS_HPD_IRQ(payload[1]); + int mf_pref = PD_VDO_DPSTS_MF_PREF(payload[1]); + + dp_status[port] = payload[1]; + + mux->hpd_update(port, lvl, irq); + + if (lvl) + usb_mux_set(port, mf_pref ? TYPEC_MUX_DOCK : TYPEC_MUX_DP, + USB_SWITCH_CONNECT, pd_get_polarity(port)); + else + usb_mux_set(port, mf_pref ? TYPEC_MUX_USB : TYPEC_MUX_NONE, + USB_SWITCH_CONNECT, pd_get_polarity(port)); + + return 1; +} + +static void svdm_exit_dp_mode(int port) +{ + const struct usb_mux *mux = &usb_muxes[port]; + + svdm_safe_dp_mode(port); + mux->hpd_update(port, 0, 0); +} + +static int svdm_enter_gfu_mode(int port, uint32_t mode_caps) +{ + /* Always enter GFU mode */ + return 0; +} + +static void svdm_exit_gfu_mode(int port) +{ +} + +static int svdm_gfu_status(int port, uint32_t *payload) +{ + /* + * This is called after enter mode is successful, send unstructured + * VDM to read info. + */ + pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_READ_INFO, NULL, 0); + return 0; +} + +static int svdm_gfu_config(int port, uint32_t *payload) +{ + return 0; +} + +static int svdm_gfu_attention(int port, uint32_t *payload) +{ + return 0; +} + +const struct svdm_amode_fx supported_modes[] = { + { + .svid = USB_SID_DISPLAYPORT, + .enter = &svdm_enter_dp_mode, + .status = &svdm_dp_status, + .config = &svdm_dp_config, + .post_config = &svdm_dp_post_config, + .attention = &svdm_dp_attention, + .exit = &svdm_exit_dp_mode, + }, + { + .svid = USB_VID_GOOGLE, + .enter = &svdm_enter_gfu_mode, + .status = &svdm_gfu_status, + .config = &svdm_gfu_config, + .attention = &svdm_gfu_attention, + .exit = &svdm_exit_gfu_mode, + } +}; +const int supported_modes_cnt = ARRAY_SIZE(supported_modes); +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ + -- cgit v1.2.1