diff options
author | Vijay Hiremath <vijay.p.hiremath@intel.com> | 2018-04-10 06:08:59 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-04-14 12:46:59 -0700 |
commit | 867a567229ebe1502565e390736c0b77b2c610d9 (patch) | |
tree | 912d60a76baf5aebb3ae29ab1bfb71f15583831d /board/glkrvp_ite | |
parent | cfcac78e626ce08ccc1c45c91c61127b6088e80f (diff) | |
download | chrome-ec-867a567229ebe1502565e390736c0b77b2c610d9.tar.gz |
glkrvp_ite: Initial bring-up code
Initial bring-up code for GLKRVP using ITE8320 MECC.
BUG=b:77798195
BRANCH=none
TEST=Able to boot to OS with host communication disabled
Coreboot image.
Change-Id: Iad0e22c9e7ef1c36889ef5e7f7e3f5a121f234e1
Signed-off-by: Vijay Hiremath <vijay.p.hiremath@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/1003766
Commit-Ready: Vijay P Hiremath <vijay.p.hiremath@intel.com>
Tested-by: Vijay P Hiremath <vijay.p.hiremath@intel.com>
Reviewed-by: Jett Rink <jettrink@chromium.org>
Diffstat (limited to 'board/glkrvp_ite')
-rw-r--r-- | board/glkrvp_ite/battery.c | 237 | ||||
-rw-r--r-- | board/glkrvp_ite/board.c | 211 | ||||
-rw-r--r-- | board/glkrvp_ite/board.h | 168 | ||||
-rw-r--r-- | board/glkrvp_ite/build.mk | 14 | ||||
-rw-r--r-- | board/glkrvp_ite/chg_usb_pd.c | 253 | ||||
-rw-r--r-- | board/glkrvp_ite/ec.tasklist | 37 | ||||
-rw-r--r-- | board/glkrvp_ite/gpio.inc | 112 | ||||
-rw-r--r-- | board/glkrvp_ite/usb_pd_policy.c | 347 |
8 files changed, 1379 insertions, 0 deletions
diff --git a/board/glkrvp_ite/battery.c b/board/glkrvp_ite/battery.c new file mode 100644 index 0000000000..d9d4bf9bf3 --- /dev/null +++ b/board/glkrvp_ite/battery.c @@ -0,0 +1,237 @@ +/* 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 "charger_profile_override.h" +#include "console.h" +#include "ioexpander_pca9555.h" +#include "util.h" + +#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) + +/* Shutdown mode parameter to write to manufacturer access register */ +#define SB_SHUTDOWN_DATA 0x0010 + +enum fast_chg_voltage_ranges { + VOLTAGE_RANGE_0, + VOLTAGE_RANGE_1, + VOLTAGE_RANGE_2, +}; + +enum temp_range { + TEMP_RANGE_0, + TEMP_RANGE_1, + TEMP_RANGE_2, + TEMP_RANGE_3, + TEMP_RANGE_4, + TEMP_RANGE_5, +}; + +/* keep track of previous charge profile info */ +static const struct fast_charge_profile *prev_chg_profile_info; + +/* SMP-CA-445 battery & BQ30Z554 fuel gauge */ +static const struct battery_info batt_info_smp_ca445 = { + .voltage_max = 8700, /* mV */ + .voltage_normal = 7600, + + /* + * Actual value 6000mV, added 100mV for charger accuracy so that + * unwanted low VSYS_Prochot# assertion can be avoided. + */ + .voltage_min = 6100, + .precharge_current = 150, /* mA */ + .start_charging_min_c = 0, + .start_charging_max_c = 45, + .charging_min_c = 0, + .charging_max_c = 45, + .discharging_min_c = -20, + .discharging_max_c = 60, +}; + +const struct battery_info *battery_get_info(void) +{ + static struct battery_info batt_info; + + if (battery_is_present() == BP_YES) + return &batt_info_smp_ca445; + + /* + * In no battery condition, to avoid voltage drop on VBATA set + * the battery minimum voltage to the battery maximum voltage. + */ + + batt_info = batt_info_smp_ca445; + batt_info.voltage_min = batt_info.voltage_max; + + return &batt_info; +} + +static const struct fast_charge_profile fast_charge_smp_ca445_info[] = { + /* < 0C */ + [TEMP_RANGE_0] = { + .temp_c = TEMPC_TENTHS_OF_DEG(-1), + .current_mA = { + [VOLTAGE_RANGE_0] = 0, + [VOLTAGE_RANGE_1] = 0, + [VOLTAGE_RANGE_2] = 0, + }, + }, + + /* 0C >= && <=15C */ + [TEMP_RANGE_1] = { + .temp_c = TEMPC_TENTHS_OF_DEG(15), + .current_mA = { + [VOLTAGE_RANGE_0] = 890, + [VOLTAGE_RANGE_1] = 445, + [VOLTAGE_RANGE_2] = 445, + }, + }, + + /* 15C > && <=20C */ + [TEMP_RANGE_2] = { + .temp_c = TEMPC_TENTHS_OF_DEG(20), + .current_mA = { + [VOLTAGE_RANGE_0] = 1335, + [VOLTAGE_RANGE_1] = 1335, + [VOLTAGE_RANGE_2] = 1335, + }, + }, + + /* 20C > && <=45C */ + [TEMP_RANGE_3] = { + .temp_c = TEMPC_TENTHS_OF_DEG(45), + .current_mA = { + [VOLTAGE_RANGE_0] = 2225, + [VOLTAGE_RANGE_1] = 2225, + [VOLTAGE_RANGE_2] = 2225, + }, + }, + + /* 45C > && <=55C */ + [TEMP_RANGE_4] = { + .temp_c = TEMPC_TENTHS_OF_DEG(55), + .current_mA = { + [VOLTAGE_RANGE_0] = 1335, + [VOLTAGE_RANGE_1] = 1335, + [VOLTAGE_RANGE_2] = 0, + }, + }, + + /* > 55C */ + [TEMP_RANGE_5] = { + .temp_c = TEMPC_TENTHS_OF_DEG(CHARGER_PROF_TEMP_C_LAST_RANGE), + .current_mA = { + [VOLTAGE_RANGE_0] = 0, + [VOLTAGE_RANGE_1] = 0, + [VOLTAGE_RANGE_2] = 0, + }, + }, +}; + +static const struct fast_charge_params fast_chg_params_smp_ca445 = { + .total_temp_ranges = ARRAY_SIZE(fast_charge_smp_ca445_info), + .default_temp_range_profile = TEMP_RANGE_3, + .voltage_mV = { + [VOLTAGE_RANGE_0] = 8000, + [VOLTAGE_RANGE_1] = 8200, + [VOLTAGE_RANGE_2] = CHARGER_PROF_VOLTAGE_MV_LAST_RANGE, + }, + .chg_profile_info = &fast_charge_smp_ca445_info[0], +}; + +/* + * This can override the smart battery's charging profile. To make a change, + * modify one or more of requested_voltage, requested_current, or state. + * Leave everything else unchanged. + * + * Return the next poll period in usec, or zero to use the default (which is + * state dependent). + */ +int charger_profile_override(struct charge_state_data *curr) +{ + /* + * If battery present and not in cut off and almost full + * then if it does not want charge then discharge on AC + */ + if ((battery_is_present() == BP_YES) && + !(curr->batt.flags & BATT_FLAG_WANT_CHARGE) && + (curr->batt.status & STATUS_FULLY_CHARGED)) { + charger_discharge_on_ac(1); + curr->state = ST_DISCHARGE; + return 0; + } + + charger_discharge_on_ac(0); + + return charger_profile_override_common(curr, + &fast_chg_params_smp_ca445, + &prev_chg_profile_info, + batt_info_smp_ca445.voltage_max); +} + +int board_cut_off_battery(void) +{ + int rv; + + /* Ship mode command must be sent twice to take effect */ + rv = sb_write(SB_MANUFACTURER_ACCESS, SB_SHUTDOWN_DATA); + if (rv != EC_SUCCESS) + return rv; + + return sb_write(SB_MANUFACTURER_ACCESS, SB_SHUTDOWN_DATA); +} + +static inline int batt_smp_cos4870_is_initialized(void) +{ + int batt_status; + + return battery_status(&batt_status) ? 0 : + batt_status & STATUS_INITIALIZED; +} + +enum battery_present battery_hw_present(void) +{ + int data; + int rv; + + rv = pca9555_read(I2C_PORT_PCA555_PMIC_BATT_GPIO, + I2C_ADDR_PCA555_PMIC_BATT_GPIO, + PCA9555_CMD_INPUT_PORT_0, &data); + + /* GPIO is low when the battery is physically present */ + return rv || (data & PCA9555_IO_5) ? BP_NO : BP_YES; +} + +/* + * Physical detection of battery. + */ +enum battery_present battery_is_present(void) +{ + static enum battery_present batt_pres_prev = BP_NOT_SURE; + enum battery_present batt_pres; + + /* Get the physical hardware status */ + batt_pres = battery_hw_present(); + + /* + * Make sure battery status is implemented, I2C transactions are + * success & the battery status is Initialized to find out if it + * is a working battery and it is not in the cut-off mode. + * + * FETs are turned off after Power Shutdown time. + * The device will wake up when a voltage is applied to PACK. + * Battery status will be inactive until it is initialized. + */ + if (batt_pres == BP_YES && batt_pres_prev != batt_pres && + !battery_is_cut_off() && !batt_smp_cos4870_is_initialized()) + batt_pres = BP_NO; + + batt_pres_prev = batt_pres; + + return batt_pres; +} diff --git a/board/glkrvp_ite/board.c b/board/glkrvp_ite/board.c new file mode 100644 index 0000000000..2741f7511b --- /dev/null +++ b/board/glkrvp_ite/board.c @@ -0,0 +1,211 @@ +/* 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. + */ + +/* Intel GLK-RVP-ITE board-specific configuration */ + +#include "button.h" +#include "chipset.h" +#include "console.h" +#include "ec2i_chip.h" +#include "extpower.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.h" +#include "i2c.h" +#include "ioexpander_pca9555.h" +#include "keyboard_scan.h" +#include "lid_switch.h" +#include "power.h" +#include "power_button.h" +#include "spi.h" +#include "switch.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "uart.h" +#include "util.h" + +#include "gpio_list.h" + +#define I2C_PORT_PCA555_BOARD_ID_GPIO IT83XX_I2C_CH_C +#define I2C_ADDR_PCA555_BOARD_ID_GPIO 0x40 + +/* power signal list. Must match order of enum power_signal. */ +const struct power_signal_info power_signal_list[] = { + {GPIO_RSMRST_L_PGOOD, POWER_SIGNAL_ACTIVE_HIGH, "RSMRST_L"}, + {GPIO_PCH_SLP_S0_L, + POWER_SIGNAL_ACTIVE_HIGH | POWER_SIGNAL_DISABLE_AT_BOOT, + "SLP_S0_DEASSERTED"}, + {GPIO_PCH_SLP_S3_L, POWER_SIGNAL_ACTIVE_HIGH, "SLP_S3_DEASSERTED"}, + {GPIO_PCH_SLP_S4_L, POWER_SIGNAL_ACTIVE_HIGH, "SLP_S4_DEASSERTED"}, + {GPIO_SUSPWRNACK, POWER_SIGNAL_ACTIVE_HIGH, + "SUSPWRNACK_DEASSERTED"}, + + {GPIO_ALL_SYS_PGOOD, POWER_SIGNAL_ACTIVE_HIGH, "ALL_SYS_PGOOD"}, +}; +BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); + +/* I2C ports */ +const struct i2c_port_t i2c_ports[] = { + {"charger", IT83XX_I2C_CH_A, 100, GPIO_I2C_A_SCL, GPIO_I2C_A_SDA}, + {"typec", IT83XX_I2C_CH_B, 400, GPIO_I2C_B_SCL, GPIO_I2C_B_SDA}, + {"pmic", IT83XX_I2C_CH_C, 100, GPIO_I2C_C_SCL, GPIO_I2C_C_SDA}, + {"ext_io", IT83XX_I2C_CH_E, 400, GPIO_I2C_E_SCL, GPIO_I2C_E_SDA}, +}; +const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); + +/* Wake-up pins for hibernate */ +const enum gpio_signal hibernate_wake_pins[] = { + GPIO_AC_PRESENT, + GPIO_LID_OPEN, + GPIO_POWER_BUTTON_L, +}; +const int hibernate_wake_pins_used = ARRAY_SIZE(hibernate_wake_pins); + +/* Called by APL power state machine when transitioning from G3 to S5 */ +static void chipset_pre_init(void) +{ + int data; + + if (pca9555_read(I2C_PORT_PCA555_PMIC_BATT_GPIO, + I2C_ADDR_PCA555_PMIC_BATT_GPIO, + PCA9555_CMD_OUTPUT_PORT_0, &data)) + return; + + /* + * No need to re-init PMIC since settings are sticky across sysjump. + * However, be sure to check that PMIC is already enabled. If it is + * then there's no need to re-sequence the PMIC. + */ + if (system_jumped_to_this_image() && (data & PCA9555_IO_0)) + return; + + /* Enable SOC_3P3_EN_L: Set the Output port O0.1 to low level */ + data &= ~PCA9555_IO_1; + pca9555_write(I2C_PORT_PCA555_PMIC_BATT_GPIO, + I2C_ADDR_PCA555_PMIC_BATT_GPIO, + PCA9555_CMD_OUTPUT_PORT_0, data); + + /* TODO: Find out from the spec */ + msleep(10); + + /* Enable PMIC_EN: Set the Output port O0.0 to high level */ + pca9555_write(I2C_PORT_PCA555_PMIC_BATT_GPIO, + I2C_ADDR_PCA555_PMIC_BATT_GPIO, PCA9555_CMD_OUTPUT_PORT_0, + data | PCA9555_IO_0); +} +DECLARE_HOOK(HOOK_CHIPSET_PRE_INIT, chipset_pre_init, HOOK_PRIO_DEFAULT); + + +/* Initialize board. */ +static void board_init(void) +{ +} +DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_FIRST); + +/* Called on AP S5 -> S3 transition */ +static void board_chipset_startup(void) +{ +} +DECLARE_HOOK(HOOK_CHIPSET_STARTUP, board_chipset_startup, HOOK_PRIO_DEFAULT); + +/* Called on AP S3 -> S5 transition */ +static void board_chipset_shutdown(void) +{ +} +DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, board_chipset_shutdown, HOOK_PRIO_DEFAULT); + +void chipset_do_shutdown(void) +{ + int data; + + if (pca9555_read(I2C_PORT_PCA555_PMIC_BATT_GPIO, + I2C_ADDR_PCA555_PMIC_BATT_GPIO, + PCA9555_CMD_OUTPUT_PORT_0, &data)) + return; + + /* Disable SOC_3P3_EN_L: Set the Output port O0.1 to high level */ + data |= PCA9555_IO_1; + pca9555_write(I2C_PORT_PCA555_PMIC_BATT_GPIO, + I2C_ADDR_PCA555_PMIC_BATT_GPIO, + PCA9555_CMD_OUTPUT_PORT_0, data); + + /* TODO: Find out from the spec */ + msleep(10); + + /* Disable PMIC_EN: Set the Output port O0.0 to low level */ + pca9555_write(I2C_PORT_PCA555_PMIC_BATT_GPIO, + I2C_ADDR_PCA555_PMIC_BATT_GPIO, PCA9555_CMD_OUTPUT_PORT_0, + data & ~PCA9555_IO_0); +} + +void board_hibernate_late(void) +{ +} + +void board_hibernate(void) +{ + /* + * To support hibernate called from console commands, ectool commands + * and key sequence, shutdown the AP before hibernating. + */ + chipset_do_shutdown(); + + /* Added delay to allow AP to settle down */ + msleep(100); +} + +int board_get_version(void) +{ + int data; + + if (pca9555_read(I2C_PORT_PCA555_BOARD_ID_GPIO, + I2C_ADDR_PCA555_BOARD_ID_GPIO, PCA9555_CMD_INPUT_PORT_1, &data)) + return -1; + + return data & 0x0f; +} + +static void pmic_init(void) +{ + /* No need to re-init PMIC since settings are sticky across sysjump. */ + if (system_jumped_to_this_image()) + return; + + /* + * PMIC INIT + * Configure Port O0.0 as Output port - PMIC_EN + * Configure Port O0.1 as Output port - SOC_3P3_EN_L + */ + pca9555_write(I2C_PORT_PCA555_PMIC_BATT_GPIO, + I2C_ADDR_PCA555_PMIC_BATT_GPIO, + PCA9555_CMD_CONFIGURATION_PORT_0, 0xfc); + + /* + * Set the Output port O0.0 to low level - PMIC_EN + * Set the Output port O0.1 to high level - SOC_3P3_EN_L + * + * POR of PCA9555 port is input with high impedance hence explicitly + * configure the SOC_3P3_EN_L to high level. + */ + pca9555_write(I2C_PORT_PCA555_PMIC_BATT_GPIO, + I2C_ADDR_PCA555_PMIC_BATT_GPIO, + PCA9555_CMD_OUTPUT_PORT_0, 0xfe); +} +DECLARE_HOOK(HOOK_INIT, pmic_init, HOOK_PRIO_INIT_I2C + 1); + +/* Keyboard scan setting */ +struct keyboard_scan_config keyscan_config = { + .output_settle_us = 35, + .debounce_down_us = 5 * MSEC, + .debounce_up_us = 40 * 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 */ + }, +}; diff --git a/board/glkrvp_ite/board.h b/board/glkrvp_ite/board.h new file mode 100644 index 0000000000..d21b6c4cdb --- /dev/null +++ b/board/glkrvp_ite/board.h @@ -0,0 +1,168 @@ +/* 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. + */ + +/* Intel GLK-RVP-ITE board-specific configuration */ + +#ifndef __CROS_EC_BOARD_H +#define __CROS_EC_BOARD_H + +/* + * Allow dangerous commands. + * TODO: Remove this config before production. + */ +#define CONFIG_SYSTEM_UNLOCKED + +#define CC_DEFAULT (CC_ALL & ~(CC_MASK(CC_EVENTS) | CC_MASK(CC_LPC))) +#undef CONFIG_HOSTCMD_DEBUG_MODE + +/* + * By default, enable all console messages excepted HC, ACPI and event: + * The sensor stack is generating a lot of activity. + */ +#define CONFIG_HOSTCMD_DEBUG_MODE HCDEBUG_OFF + +/* EC console commands */ + +/* Battery */ +#define CONFIG_BATTERY_CUT_OFF +#define CONFIG_BATTERY_PRESENT_CUSTOM +#define CONFIG_BATTERY_SMART + +/* Charger */ +#define CONFIG_CHARGE_MANAGER +#define CONFIG_CHARGER +#define CONFIG_CHARGER_DISCHARGE_ON_AC +#define CONFIG_CHARGER_INPUT_CURRENT 512 +#define CONFIG_CHARGER_ISL9238 +#define CONFIG_CHARGER_PROFILE_OVERRIDE +#define CONFIG_CHARGER_PROFILE_OVERRIDE_COMMON +#undef CONFIG_CHARGER_PROFILE_VOLTAGE_RANGES +#define CONFIG_CHARGER_PROFILE_VOLTAGE_RANGES 3 +#define CONFIG_CHARGER_SENSE_RESISTOR 10 +#define CONFIG_CHARGER_SENSE_RESISTOR_AC 10 +#define CONFIG_CHARGER_V2 +#undef CONFIG_EXTPOWER_DEBOUNCE_MS +#define CONFIG_EXTPOWER_DEBOUNCE_MS 200 +#define CONFIG_EXTPOWER_GPIO + +/* DC Jack charge ports */ +#undef CONFIG_DEDICATED_CHARGE_PORT_COUNT +#define CONFIG_DEDICATED_CHARGE_PORT_COUNT 1 +#define DEDICATED_CHARGE_PORT 2 + +/* Keyboard */ +#define CONFIG_KEYBOARD_BOARD_CONFIG +#define CONFIG_KEYBOARD_PROTOCOL_8042 + +/* UART */ +#define CONFIG_LOW_POWER_IDLE + +/* USB-A config */ + +/* USB PD config */ +#define CONFIG_USB_PD_DUAL_ROLE +#define CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT TYPEC_RP_3A0 +#define CONFIG_USB_PD_PORT_COUNT 2 +#define CONFIG_USB_PD_TCPM_TCPCI +#define CONFIG_USB_PD_TRY_SRC +#define CONFIG_USB_PD_VBUS_DETECT_TCPC +#define CONFIG_USB_POWER_DELIVERY + +/* USB MUX */ +#define CONFIG_USBC_SS_MUX +#define CONFIG_USB_MUX_PS8743 + +/* SoC / PCH */ +#define CONFIG_ESPI +#define CONFIG_LPC +#define CONFIG_CHIPSET_GEMINILAKE +#define CONFIG_CHIPSET_RESET_HOOK +#define CONFIG_POWER_BUTTON +#define CONFIG_POWER_BUTTON_X86 +#define CONFIG_POWER_COMMON +#define CONFIG_POWER_S0IX +#define CONFIG_POWER_TRACK_HOST_SLEEP_STATE + +/* EC */ +#define CONFIG_BOARD_VERSION +#define CONFIG_BOARD_SPECIFIC_VERSION +#define CONFIG_VOLUME_BUTTONS +#define CONFIG_LID_SWITCH +#define CONFIG_WP_ALWAYS + +/* Verified boot */ +#define CONFIG_SHA256_UNROLLED +#define CONFIG_VBOOT_HASH +/* + * Enable 1 slot of secure temporary storage to support + * suspend/resume with read/write memory training. + */ +#define CONFIG_VSTORE +#define CONFIG_VSTORE_SLOT_COUNT 1 + +/* Optional feature - used by ITE */ +#define CONFIG_IT83XX_FLASH_CLOCK_48MHZ + +/* I2C ports */ +#define CONFIG_I2C +#define CONFIG_I2C_MASTER +#define CONFIG_IT83XX_SMCLK2_ON_GPC7 + +#define I2C_PORT_CHARGER IT83XX_I2C_CH_A +#define I2C_PORT_BATTERY IT83XX_I2C_CH_A +#define I2C_PORT_USB_MUX IT83XX_I2C_CH_B + +#define I2C_PORT_PCA555_PMIC_BATT_GPIO IT83XX_I2C_CH_C +#define I2C_ADDR_PCA555_PMIC_BATT_GPIO 0x42 + +/* EC exclude modules */ +#undef CONFIG_ADC +#undef CONFIG_WATCHDOG + +#ifndef __ASSEMBLER__ + +#include "gpio_signal.h" +#include "registers.h" + +enum power_signal { + X86_RSMRST_N = 0, + X86_SLP_S0_N, + X86_SLP_S3_N, + X86_SLP_S4_N, + X86_SUSPWRDNACK, + + X86_ALL_SYS_PG, /* PMIC_EC_PWROK_OD */ + + /* Number of X86 signals */ + POWER_SIGNAL_COUNT +}; + +enum adc_channel { + ADC_VBUS, + ADC_CH_COUNT, +}; + +int board_get_version(void); + +/* TODO: Verify the numbers below. */ +#define PD_POWER_SUPPLY_TURN_ON_DELAY 30000 /* us */ +#define PD_POWER_SUPPLY_TURN_OFF_DELAY 250000 /* us */ + +/* Define typical operating power and max power */ +#define PD_OPERATING_POWER_MW 15000 +#define PD_MAX_POWER_MW 45000 +#define PD_MAX_CURRENT_MA 3000 +#define PD_MAX_VOLTAGE_MV 20000 +#define DC_JACK_MAX_VOLTAGE_MV 19000 + +/* Reset PD MCU */ +void board_reset_pd_mcu(void); +void tcpc_alert_event(enum gpio_signal signal); +void board_charging_enable(int port, int enable); +void board_vbus_enable(int port, int enable); + +#endif /* !__ASSEMBLER__ */ + +#endif /* __CROS_EC_BOARD_H */ diff --git a/board/glkrvp_ite/build.mk b/board/glkrvp_ite/build.mk new file mode 100644 index 0000000000..bc1e6c21cc --- /dev/null +++ b/board/glkrvp_ite/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 +# + +#it8320 +CHIP:=it83xx + +board-y=board.o +board-$(CONFIG_BATTERY_SMART)+=battery.o +board-$(CONFIG_USB_POWER_DELIVERY)+=chg_usb_pd.o usb_pd_policy.o diff --git a/board/glkrvp_ite/chg_usb_pd.c b/board/glkrvp_ite/chg_usb_pd.c new file mode 100644 index 0000000000..b0c8488175 --- /dev/null +++ b/board/glkrvp_ite/chg_usb_pd.c @@ -0,0 +1,253 @@ +/* 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 "charge_manager.h" +#include "charge_state_v2.h" +#include "console.h" +#include "hooks.h" +#include "task.h" +#include "tcpci.h" +#include "system.h" +#include "usb_mux.h" +#include "util.h" + +#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) +#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) + +#define PTN5110_EXT_GPIO_CONFIG 0x92 +#define PTN5110_EXT_GPIO_CONTROL 0x93 + +#define PTN5110_EXT_GPIO_FRS_EN (1 << 6) +#define PTN5110_EXT_GPIO_EN_SRC (1 << 5) +#define PTN5110_EXT_GPIO_EN_SNK1 (1 << 4) +#define PTN5110_EXT_GPIO_IILIM_5V_VBUS_L (1 << 3) + +enum glkrvp_charge_ports { + TYPE_C_PORT_0, + TYPE_C_PORT_1, + DC_JACK_PORT_0 = DEDICATED_CHARGE_PORT, +}; + +const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = { + {IT83XX_I2C_CH_B, 0xA0, &tcpci_tcpm_drv, TCPC_ALERT_ACTIVE_LOW}, + {IT83XX_I2C_CH_B, 0xA4, &tcpci_tcpm_drv, TCPC_ALERT_ACTIVE_LOW}, +}; +BUILD_ASSERT(ARRAY_SIZE(tcpc_config) == CONFIG_USB_PD_PORT_COUNT); + +struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = { + { + .port_addr = 0x20, + .driver = &ps874x_usb_mux_driver, + }, + { + .port_addr = 0x22, + .driver = &ps874x_usb_mux_driver, + }, +}; + +/* TODO: Implement this function and move to appropriate file */ +void usb_charger_set_switches(int port, enum usb_switch setting) +{ +} + +static int board_charger_port_is_sourcing_vbus(int port) +{ + int reg; + + /* DC Jack can't source VBUS */ + if (port == DC_JACK_PORT_0) + return 0; + + if (tcpc_read(port, PTN5110_EXT_GPIO_CONTROL, ®)) + return 0; + + return !!(reg & PTN5110_EXT_GPIO_EN_SRC); +} + +static int ptn5110_ext_gpio_enable(int port, int enable, int gpio) +{ + int reg; + int rv; + + rv = tcpc_read(port, PTN5110_EXT_GPIO_CONTROL, ®); + if (rv) + return rv; + + if (enable) + reg |= gpio; + else + reg &= ~gpio; + + return tcpc_write(port, PTN5110_EXT_GPIO_CONTROL, reg); +} + +void board_charging_enable(int port, int enable) +{ + ptn5110_ext_gpio_enable(port, enable, PTN5110_EXT_GPIO_EN_SNK1); +} + +void board_vbus_enable(int port, int enable) +{ + ptn5110_ext_gpio_enable(port, enable, PTN5110_EXT_GPIO_EN_SRC); +} + +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 +} + +void board_tcpc_init(void) +{ + /* Only reset TCPC if not sysjump */ + if (!system_jumped_to_this_image()) + board_reset_pd_mcu(); + + /* Enable TCPC0/1 interrupt */ + gpio_enable_interrupt(GPIO_USB_C0_PD_INT_ODL); + gpio_enable_interrupt(GPIO_USB_C1_PD_INT_ODL); +} +DECLARE_HOOK(HOOK_INIT, board_tcpc_init, HOOK_PRIO_INIT_I2C + 1); + +int board_tcpc_post_init(int port) +{ + int reg; + int rv; + + rv = tcpc_read(port, PTN5110_EXT_GPIO_CONFIG, ®); + if (rv) + return rv; + + /* Configure PTN5110 External GPIOs as output */ + reg |= PTN5110_EXT_GPIO_EN_SRC | PTN5110_EXT_GPIO_EN_SNK1 | + PTN5110_EXT_GPIO_IILIM_5V_VBUS_L; + rv = tcpc_write(port, PTN5110_EXT_GPIO_CONFIG, reg); + if (rv) + return rv; + + return ptn5110_ext_gpio_enable(port, 1, + PTN5110_EXT_GPIO_IILIM_5V_VBUS_L); +} + +/* Reset PD MCU */ +void board_reset_pd_mcu(void) +{ + /* TODO: Add reset logic */ +} + +static inline int board_dc_jack_present(void) +{ + return !gpio_get_level(GPIO_DC_JACK_PRESENT_L); +} + +static void board_dc_jack_handle(void) +{ + struct charge_port_info charge_dc_jack; + + /* System is booted from DC Jack */ + if (board_dc_jack_present()) { + charge_dc_jack.current = (PD_MAX_POWER_MW * 1000) / + DC_JACK_MAX_VOLTAGE_MV; + charge_dc_jack.voltage = DC_JACK_MAX_VOLTAGE_MV; + } else { + charge_dc_jack.current = 0; + charge_dc_jack.voltage = USB_CHARGER_VOLTAGE_MV; + } + + charge_manager_update_charge(CHARGE_SUPPLIER_DEDICATED, + DC_JACK_PORT_0, &charge_dc_jack); +} +DECLARE_HOOK(HOOK_AC_CHANGE, board_dc_jack_handle, HOOK_PRIO_FIRST); + +static void board_charge_init(void) +{ + int port, supplier; + struct charge_port_info charge_init = { + .current = 0, + .voltage = USB_CHARGER_VOLTAGE_MV, + }; + + /* Initialize all charge suppliers to seed the charge manager */ + for (port = 0; port < CHARGE_PORT_COUNT; port++) { + for (supplier = 0; supplier < CHARGE_SUPPLIER_COUNT; supplier++) + charge_manager_update_charge(supplier, port, + &charge_init); + } + + board_dc_jack_handle(); +} +DECLARE_HOOK(HOOK_INIT, board_charge_init, HOOK_PRIO_DEFAULT); + +int board_set_active_charge_port(int port) +{ + /* charge port is a realy physical port */ + int is_real_port = (port >= 0 && + port < CHARGE_PORT_COUNT); + /* check if we are source vbus on that port */ + int source = board_charger_port_is_sourcing_vbus(port); + + if (is_real_port && source) { + CPRINTS("Skip enable p%d", port); + return EC_ERROR_INVAL; + } + + /* + * Do not enable Type-C port if the DC Jack is present. + * When the Type-C is active port, hardware circuit will + * block DC jack from enabling +VADP_OUT. + */ + if (port != DC_JACK_PORT_0 && board_dc_jack_present()) { + CPRINTS("DC Jack present, Skip enable p%d", port); + return EC_ERROR_INVAL; + } + + /* Make sure non-charging port is disabled */ + switch (port) { + case TYPE_C_PORT_0: + board_charging_enable(TYPE_C_PORT_1, 0); + board_charging_enable(TYPE_C_PORT_0, 1); + break; + case TYPE_C_PORT_1: + board_charging_enable(TYPE_C_PORT_0, 0); + board_charging_enable(TYPE_C_PORT_1, 1); + break; + case DC_JACK_PORT_0: + case CHARGE_PORT_NONE: + default: + /* Disable both Type-C ports */ + board_charging_enable(TYPE_C_PORT_0, 0); + board_charging_enable(TYPE_C_PORT_1, 0); + break; + } + + return EC_SUCCESS; +} + +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; + + if (!gpio_get_level(GPIO_USB_C1_PD_INT_ODL)) + status |= PD_STATUS_TCPC_ALERT_1; + + return status; +} + +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 adc_read_channel(enum adc_channel ch) +{ + return 0; +} diff --git a/board/glkrvp_ite/ec.tasklist b/board/glkrvp_ite/ec.tasklist new file mode 100644 index 0000000000..64550e561b --- /dev/null +++ b/board/glkrvp_ite/ec.tasklist @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/* Intel GLK-RVP-ITE board-specific configuration */ + +/* + * 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' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + * + * For USB PD tasks, IDs must be in consecutive order and correspond to + * the port which they are for. See TASK_ID_TO_PD_PORT() macro. + */ + +#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_NOTEST(CHIPSET, chipset_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \ + TASK_NOTEST(PDCMD, pd_command_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(HOSTCMD, host_command_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(CONSOLE, console_task, NULL, VENTI_TASK_STACK_SIZE) \ + TASK_ALWAYS(POWERBTN, power_button_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(PD_C1, pd_task, NULL, LARGER_TASK_STACK_SIZE) diff --git a/board/glkrvp_ite/gpio.inc b/board/glkrvp_ite/gpio.inc new file mode 100644 index 0000000000..bd37b1dd61 --- /dev/null +++ b/board/glkrvp_ite/gpio.inc @@ -0,0 +1,112 @@ +/* -*- 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. + */ + +/* Intel GLK-RVP-ITE board-specific configuration */ + +/* + * Declare symbolic names for all the GPIOs that we care about. + * Note: Those with interrupt handlers must be declared first. + */ + +/* Power sequencing interrupts */ +GPIO_INT(SUSPWRNACK, PIN(E, 1), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(PCH_SLP_S0_L, PIN(F, 0), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(PCH_SLP_S3_L, PIN(F, 2), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(PCH_SLP_S4_L, PIN(F, 3), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(RSMRST_L_PGOOD,PIN(G, 6), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(ALL_SYS_PGOOD, PIN(I, 7), GPIO_INT_BOTH, power_signal_interrupt) + +/* Button interrupts */ +GPIO_INT(VOLUME_UP_L, PIN(D, 5), GPIO_INT_BOTH | GPIO_PULL_UP, button_interrupt) +GPIO_INT(VOLUME_DOWN_L, PIN(D, 6), GPIO_INT_BOTH | GPIO_PULL_UP, button_interrupt) +GPIO_INT(LID_OPEN, PIN(E, 2), GPIO_INT_BOTH | GPIO_PULL_UP, lid_interrupt) +GPIO_INT(POWER_BUTTON_L,PIN(E, 4), GPIO_INT_BOTH, power_button_interrupt) + +GPIO_INT(AC_PRESENT, PIN(A, 6), GPIO_INT_BOTH, extpower_interrupt) + +GPIO_INT(UART1_RX, PIN(B, 0), GPIO_INT_FALLING, uart_deepsleep_interrupt) /* UART1 RX input */ + +/* Type-C interrupts */ +UNIMPLEMENTED(USB_C0_PD_INT_ODL) +UNIMPLEMENTED(USB_C1_PD_INT_ODL) + +UNIMPLEMENTED(WP_L) + +/* Power sequencing GPIOs */ +UNIMPLEMENTED(SYS_RESET_L) +GPIO(PCH_RSMRST_L, PIN(C, 6), GPIO_OUT_LOW) +GPIO(PCH_PWRBTN_L, PIN(D, 0), GPIO_ODR_HIGH) +GPIO(PCH_SYS_PWROK, PIN(K, 4), GPIO_OUT_LOW) +GPIO(SMC_SHUTDOWN, PIN(K, 5), GPIO_OUT_LOW | GPIO_PULL_DOWN) +/* + * PCH_PROCHOT_ODL is primarily for monitoring the PROCHOT# signal which is + * normally driven by the PMIC. The EC can also drive this signal in the event + * that the ambient or charger temperature sensors exceeds their thresholds. + */ +GPIO(CPU_PROCHOT, PIN(E, 5), GPIO_INPUT) /* PCH_PROCHOT_ODL */ + +/* Host communication GPIOs */ +GPIO(PCH_WAKE_L, PIN(L, 0), GPIO_ODR_HIGH) +GPIO(PCH_PLTRST_L, PIN(E, 3), GPIO_INPUT | GPIO_PULL_UP) + +GPIO(DC_JACK_PRESENT_L, PIN(C, 0), GPIO_INPUT) /* DC Jack presence coming from +V3P3_A_KBC */ +GPIO(USBC_LDO_ENABLE, PIN(K, 0), GPIO_OUT_HIGH) /* USB TCPC to enable LDO in dead battery */ +UNIMPLEMENTED(ENABLE_BACKLIGHT) +GPIO(ENTERING_RW, PIN(C, 5), GPIO_OUTPUT) /* EC_ENTERING_RW */ + +/* + * I2C pins should be configured as inputs until I2C module is + * initialized. This will avoid driving the lines unintentionally. + */ +GPIO(I2C_A_SCL, PIN(B, 3), GPIO_INPUT) +GPIO(I2C_A_SDA, PIN(B, 4), GPIO_INPUT) +GPIO(I2C_B_SCL, PIN(C, 1), GPIO_INPUT) +GPIO(I2C_B_SDA, PIN(C, 2), GPIO_INPUT) +#ifdef CONFIG_IT83XX_SMCLK2_ON_GPC7 +GPIO(I2C_C_SCL, PIN(C, 7), GPIO_INPUT) +#else +GPIO(I2C_C_SCL, PIN(F, 6), GPIO_INPUT) +#endif +GPIO(I2C_C_SDA, PIN(F, 7), GPIO_INPUT) +GPIO(I2C_E_SCL, PIN(E, 0), GPIO_INPUT) +GPIO(I2C_E_SDA, PIN(E, 7), GPIO_INPUT) + +/* LPC / eSPI signals */ +#if 0 +GPIO(LPC_ESPI_RST, PIN(D, 2), GPIO_INPUT) +GPIO(LAD_EIO_0, PIN(M, 0), GPIO_INPUT) +GPIO(LAD_EIO_1, PIN(M, 1), GPIO_INPUT) +GPIO(LAD_EIO_2, PIN(M, 2), GPIO_INPUT) +GPIO(LAD_EIO_3, PIN(M, 3), GPIO_INPUT) +GPIO(LPC_ESPI_CLK, PIN(M, 4), GPIO_INPUT) +GPIO(LFRAME_ESPI_CS, PIN(M, 5), GPIO_INPUT) +GPIO(SERIRQ_ALERT, PIN(M, 6), GPIO_INPUT) +#endif + +/* Unused pins 3.3V & Interruptable */ + +/* Unused pins: VSPI 3.3V or 1.8V & Interruptable */ + +/* Unused pins 3.3V & Non-Interruptable */ + +/* eSPI: VHIF Unused pins 1.8V & Interruptable */ + +/* eSPI: VHIF Unused pins 1.8V & Non-Interruptable */ + +/* Alternate pins for UART */ +ALTERNATE(PIN_MASK(B, 0x03), 1, MODULE_UART, GPIO_PULL_UP) /* UART1 */ + +/* Alternate pins for I2C */ +ALTERNATE(PIN_MASK(B, 0x18), 1, MODULE_I2C, 0) /* I2C A SCL/SDA B3/B4 */ +ALTERNATE(PIN_MASK(C, 0x06), 1, MODULE_I2C, 0) /* I2C B SCL/SDA C1/C2 */ +#ifdef CONFIG_IT83XX_SMCLK2_ON_GPC7 +ALTERNATE(PIN_MASK(C, 0x80), 1, MODULE_I2C, 0) /* I2C C SCL C7 */ +#else +ALTERNATE(PIN_MASK(F, 0x40), 1, MODULE_I2C, 0) /* I2C C SCL F6 */ +#endif +ALTERNATE(PIN_MASK(F, 0x80), 1, MODULE_I2C, 0) /* I2C C SDA F7 */ +ALTERNATE(PIN_MASK(E, 0x81), 1, MODULE_I2C, 0) /* I2C E SCL/SDA E0/E7 */ diff --git a/board/glkrvp_ite/usb_pd_policy.c b/board/glkrvp_ite/usb_pd_policy.c new file mode 100644 index 0000000000..5d5eb05283 --- /dev/null +++ b/board/glkrvp_ite/usb_pd_policy.c @@ -0,0 +1,347 @@ +/* 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 "charge_manager.h" +#include "compile_time_macros.h" +#include "console.h" +#include "gpio.h" +#include "stddef.h" +#include "system.h" +#include "usb_mux.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) + +/* TODO: fill in correct source and sink capabilities */ +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_src_pdo_max[] = { + PDO_FIXED(5000, 3000, PDO_FIXED_FLAGS), +}; +const int pd_src_pdo_max_cnt = ARRAY_SIZE(pd_src_pdo_max); + +const uint32_t pd_snk_pdo[] = { + PDO_FIXED(5000, 500, PDO_FIXED_FLAGS), + PDO_BATT(4750, 21000, 15000), + PDO_VAR(4750, 21000, 3000), +}; +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 */ +} + +int pd_set_power_supply_ready(int port) +{ + /* Disable charging */ + board_charging_enable(port, 0); + + /* Provide VBUS */ + board_vbus_enable(port, 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) +{ + /* Disable VBUS */ + board_vbus_enable(port, 0); + + /* notify host of power info change */ + pd_send_host_event(PD_EVENT_POWER_CHANGE); +} + +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; +} + +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); +} + +int pd_check_vconn_swap(int port) +{ + /* in G3, do not allow vconn swap since pp5000_A rail is off */ + /* TODO: return gpio_get_level(GPIO_PMIC_EN); */ + return 1; +} + +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, is_latest; + + /* 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]); + + is_latest = pd_dev_store_rw_hash(port, + dev_id, + payload + 1, + is_rw ? + SYSTEM_IMAGE_RW : + SYSTEM_IMAGE_RO); + + /* + * Send update host event unless our RW hash is + * already known to be the latest update RW. + */ + if (!is_rw || !is_latest) + pd_send_host_event(PD_EVENT_UPDATE_DEVICE); + + 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: + /* TODO: 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]; +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 mf_pref = PD_VDO_DPSTS_MF_PREF(dp_status[port]); + int pin_mode = pd_dfp_dp_get_pin_mode(port, dp_status[port]); + + if (!pin_mode) + return 0; + + usb_mux_set(port, mf_pref ? TYPEC_MUX_DOCK : TYPEC_MUX_DP, + USB_SWITCH_CONNECT, pd_get_polarity(port)); + + 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; + if (!(dp_flags[port] & DP_FLAGS_HPD_HI_PENDING)) + return; + /* TODO: Update HPD to host */ +} + +static int svdm_dp_attention(int port, uint32_t *payload) +{ + int lvl = PD_VDO_DPSTS_HPD_LVL(payload[1]); + + /* TODO: Read HPD IRQ */ + + dp_status[port] = payload[1]; + if (!(dp_flags[port] & DP_FLAGS_DP_ON)) { + if (lvl) + dp_flags[port] |= DP_FLAGS_HPD_HI_PENDING; + return 1; + } + /* TODO: Update HPD to host */ + + /* ack */ + return 1; +} + +static void svdm_exit_dp_mode(int port) +{ + svdm_safe_dp_mode(port); + /* TODO: Update HPD to host */ +} + +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 */ |