From 55680f70d2a1e61c193fc78ff1d51c7437803683 Mon Sep 17 00:00:00 2001 From: Dtrain Hsu Date: Thu, 10 Feb 2022 10:33:08 +0800 Subject: kinox: Initial EC image Create the initial EC image for the kinox variant by copying the brask reference board EC files into a new directory named for the variant. (Auto-Generated by create_initial_ec_image.sh version 1.5.0). BUG=b:215049181 BRANCH=None TEST=make BOARD=kinox Signed-off-by: Dtrain Hsu Change-Id: I604216664a0efc690ba9cd2c0c071a0e4650b228 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3448054 Reviewed-by: Zhuohao Lee Commit-Queue: Zhuohao Lee --- board/kinox/board.c | 199 +++++++++++++++++++++ board/kinox/board.h | 216 +++++++++++++++++++++++ board/kinox/build.mk | 21 +++ board/kinox/ec.tasklist | 28 +++ board/kinox/fans.c | 50 ++++++ board/kinox/gpio.inc | 182 ++++++++++++++++++++ board/kinox/i2c.c | 78 +++++++++ board/kinox/led.c | 252 +++++++++++++++++++++++++++ board/kinox/pwm.c | 45 +++++ board/kinox/sensors.c | 115 +++++++++++++ board/kinox/usbc_config.c | 401 +++++++++++++++++++++++++++++++++++++++++++ board/kinox/usbc_config.h | 20 +++ board/kinox/vif_override.xml | 3 + 13 files changed, 1610 insertions(+) create mode 100644 board/kinox/board.c create mode 100644 board/kinox/board.h create mode 100644 board/kinox/build.mk create mode 100644 board/kinox/ec.tasklist create mode 100644 board/kinox/fans.c create mode 100644 board/kinox/gpio.inc create mode 100644 board/kinox/i2c.c create mode 100644 board/kinox/led.c create mode 100644 board/kinox/pwm.c create mode 100644 board/kinox/sensors.c create mode 100644 board/kinox/usbc_config.c create mode 100644 board/kinox/usbc_config.h create mode 100644 board/kinox/vif_override.xml diff --git a/board/kinox/board.c b/board/kinox/board.c new file mode 100644 index 0000000000..0349ea2cf3 --- /dev/null +++ b/board/kinox/board.c @@ -0,0 +1,199 @@ +/* Copyright 2022 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 "assert.h" +#include "button.h" +#include "charge_manager.h" +#include "charge_state_v2.h" +#include "common.h" +#include "compile_time_macros.h" +#include "console.h" +#include "cros_board_info.h" +#include "gpio.h" +#include "gpio_signal.h" +#include "power_button.h" +#include "hooks.h" +#include "power.h" +#include "switch.h" +#include "throttle_ap.h" +#include "usbc_config.h" + +#include "gpio_list.h" /* Must come after other header files. */ + +/* Console output macros */ +#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args) +#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) + +/******************************************************************************/ +/* USB-A charging control */ + +const int usb_port_enable[USB_PORT_COUNT] = { + GPIO_EN_PP5000_USBA, +}; +BUILD_ASSERT(ARRAY_SIZE(usb_port_enable) == USB_PORT_COUNT); + +/******************************************************************************/ + +int board_set_active_charge_port(int port) +{ + CPRINTS("Requested charge port change to %d", port); + + /* + * The charge manager may ask us to switch to no charger if we're + * running off USB-C only but upstream doesn't support PD. It requires + * that we accept this switch otherwise it triggers an assert and EC + * reset; it's not possible to boot the AP anyway, but we want to avoid + * resetting the EC so we can continue to do the "low power" LED blink. + */ + if (port == CHARGE_PORT_NONE) + return EC_SUCCESS; + + if (port < 0 || CHARGE_PORT_COUNT <= port) + return EC_ERROR_INVAL; + + if (port == charge_manager_get_active_charge_port()) + return EC_SUCCESS; + + /* Don't charge from a source port */ + if (board_vbus_source_enabled(port)) + return EC_ERROR_INVAL; + + if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) { + int bj_active, bj_requested; + + if (charge_manager_get_active_charge_port() != CHARGE_PORT_NONE) + /* Change is only permitted while the system is off */ + return EC_ERROR_INVAL; + + /* + * Current setting is no charge port but the AP is on, so the + * charge manager is out of sync (probably because we're + * reinitializing after sysjump). Reject requests that aren't + * in sync with our outputs. + */ + bj_active = !gpio_get_level(GPIO_EN_PPVAR_BJ_ADP_L); + bj_requested = port == CHARGE_PORT_BARRELJACK; + if (bj_active != bj_requested) + return EC_ERROR_INVAL; + } + + CPRINTS("New charger p%d", port); + + switch (port) { + case CHARGE_PORT_TYPEC0: + case CHARGE_PORT_TYPEC1: + case CHARGE_PORT_TYPEC2: + gpio_set_level(GPIO_EN_PPVAR_BJ_ADP_L, 1); + break; + case CHARGE_PORT_BARRELJACK: + /* Make sure BJ adapter is sourcing power */ + if (gpio_get_level(GPIO_BJ_ADP_PRESENT_ODL)) + return EC_ERROR_INVAL; + gpio_set_level(GPIO_EN_PPVAR_BJ_ADP_L, 0); + break; + default: + return EC_ERROR_INVAL; + } + + return EC_SUCCESS; +} + +void board_set_charge_limit(int port, int supplier, int charge_ma, + int max_ma, int charge_mv) +{ +} + +/******************************************************************************/ +/* + * Barrel jack power supply handling + * + * EN_PPVAR_BJ_ADP_L must default active to ensure we can power on when the + * barrel jack is connected, and the USB-C port can bring the EC up fine in + * dead-battery mode. Both the USB-C and barrel jack switches do reverse + * protection, so we're safe to turn one on then the other off- but we should + * only do that if the system is off since it might still brown out. + */ + +/* + * Barrel-jack power adapter ratings. + */ +static const struct { + int voltage; + int current; +} bj_power[] = { + { /* 0 - 135W (also default) */ + .voltage = 19500, + .current = 6920 + }, + { /* 1 - 230W */ + .voltage = 19500, + .current = 11800 + }, +}; + +static unsigned int ec_config_get_bj_power(void) +{ + uint32_t fw_config; + unsigned int bj; + + cbi_get_fw_config(&fw_config); + bj = (fw_config & EC_CFG_BJ_POWER_MASK) >> EC_CFG_BJ_POWER_L; + /* Out of range value defaults to 0 */ + if (bj >= ARRAY_SIZE(bj_power)) + bj = 0; + return bj; +} + +#define ADP_DEBOUNCE_MS 1000 /* Debounce time for BJ plug/unplug */ +/* Debounced connection state of the barrel jack */ +static int8_t adp_connected = -1; +static void adp_connect_deferred(void) +{ + struct charge_port_info pi = { 0 }; + int connected = !gpio_get_level(GPIO_BJ_ADP_PRESENT_ODL); + + /* Debounce */ + if (connected == adp_connected) + return; + if (connected) { + unsigned int bj = ec_config_get_bj_power(); + + pi.voltage = bj_power[bj].voltage; + pi.current = bj_power[bj].current; + } + charge_manager_update_charge(CHARGE_SUPPLIER_DEDICATED, + DEDICATED_CHARGE_PORT, &pi); + adp_connected = connected; +} +DECLARE_DEFERRED(adp_connect_deferred); + +/* IRQ for BJ plug/unplug. It shouldn't be called if BJ is the power source. */ +void adp_connect_interrupt(enum gpio_signal signal) +{ + hook_call_deferred(&adp_connect_deferred_data, ADP_DEBOUNCE_MS * MSEC); +} + +static void adp_state_init(void) +{ + ASSERT(CHARGE_PORT_ENUM_COUNT == CHARGE_PORT_COUNT); + /* + * Initialize all charge suppliers to 0. The charge manager waits until + * all ports have reported in before doing anything. + */ + for (int i = 0; i < CHARGE_PORT_COUNT; i++) { + for (int j = 0; j < CHARGE_SUPPLIER_COUNT; j++) + charge_manager_update_charge(j, i, NULL); + } + + /* Report charge state from the barrel jack. */ + adp_connect_deferred(); +} +DECLARE_HOOK(HOOK_INIT, adp_state_init, HOOK_PRIO_CHARGE_MANAGER_INIT + 1); + +static void board_init(void) +{ + gpio_enable_interrupt(GPIO_BJ_ADP_PRESENT_ODL); +} +DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT); diff --git a/board/kinox/board.h b/board/kinox/board.h new file mode 100644 index 0000000000..9bc5af7abe --- /dev/null +++ b/board/kinox/board.h @@ -0,0 +1,216 @@ +/* Copyright 2022 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. + */ + +/* Brask board configuration */ + +#ifndef __CROS_EC_BOARD_H +#define __CROS_EC_BOARD_H + +#include "compile_time_macros.h" + +/* Baseboard features */ +#include "baseboard.h" + +#define CONFIG_MP2964 + +/* Barrel Jack */ +#define DEDICATED_CHARGE_PORT 3 + +/* HDMI CEC */ +#define CONFIG_CEC +#define CEC_GPIO_OUT GPIO_HDMI_CEC_OUT +#define CEC_GPIO_IN GPIO_HDMI_CEC_IN +#define CEC_GPIO_PULL_UP GPIO_HDMI_CEC_PULL_UP + +/* USB Type A Features */ +#define USB_PORT_COUNT 4 +#define CONFIG_USB_PORT_POWER_DUMB + +/* USB Type C and USB PD defines */ +#define CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY + +#define CONFIG_IO_EXPANDER +#define CONFIG_IO_EXPANDER_NCT38XX +#define CONFIG_IO_EXPANDER_PORT_COUNT 2 + +#define CONFIG_USB_PD_PPC +#define CONFIG_USB_PD_TCPM_RT1715 +#define CONFIG_USBC_RETIMER_INTEL_BB + +#define CONFIG_USBC_RETIMER_KB800X +#define CONFIG_KB800X_CUSTOM_XBAR +#define CONFIG_USBC_PPC_SYV682X +#undef CONFIG_SYV682X_HV_ILIM +#define CONFIG_SYV682X_HV_ILIM SYV682X_HV_ILIM_5_50 + +/* TODO: b/177608416 - measure and check these values on brya */ +#define PD_POWER_SUPPLY_TURN_ON_DELAY 30000 /* us */ +#define PD_POWER_SUPPLY_TURN_OFF_DELAY 30000 /* us */ +#define PD_VCONN_SWAP_DELAY 5000 /* us */ + +/* The design should support up to 100W. */ +/* TODO(b/197702356): Set the max PD to 60W now and change it + * to 100W after we verify it. + */ +#define PD_OPERATING_POWER_MW CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON +#define PD_MAX_POWER_MW 100000 +#define PD_MAX_CURRENT_MA 5000 +#define PD_MAX_VOLTAGE_MV 20000 + +/* + * Macros for GPIO signals used in common code that don't match the + * schematic names. Signal names in gpio.inc match the schematic and are + * then redefined here to so it's more clear which signal is being used for + * which purpose. + */ +#define GPIO_AC_PRESENT GPIO_ACOK_OD +#define GPIO_CPU_PROCHOT GPIO_EC_PROCHOT_ODL +#define GPIO_EC_INT_L GPIO_EC_PCH_INT_ODL +#define GPIO_ENTERING_RW GPIO_EC_ENTERING_RW +#define GPIO_PACKET_MODE_EN GPIO_EC_GSC_PACKET_MODE +#define GPIO_PCH_PWRBTN_L GPIO_EC_PCH_PWR_BTN_ODL +#define GPIO_PCH_RSMRST_L GPIO_EC_PCH_RSMRST_L +#define GPIO_PCH_RTCRST GPIO_EC_PCH_RTCRST +#define GPIO_PCH_SLP_S0_L GPIO_SYS_SLP_S0IX_L +#define GPIO_PCH_SLP_S3_L GPIO_SLP_S3_L +#define GPIO_TEMP_SENSOR_POWER GPIO_SEQ_EC_DSW_PWROK + +/* + * GPIO_EC_PCH_INT_ODL is used for MKBP events as well as a PCH wakeup + * signal. + */ +#define GPIO_PCH_WAKE_L GPIO_EC_PCH_INT_ODL +#define GPIO_PG_EC_ALL_SYS_PWRGD GPIO_SEQ_EC_ALL_SYS_PG +#define GPIO_PG_EC_DSW_PWROK GPIO_SEQ_EC_DSW_PWROK +#define GPIO_PG_EC_RSMRST_ODL GPIO_SEQ_EC_RSMRST_ODL +#define GPIO_POWER_BUTTON_L GPIO_GSC_EC_PWR_BTN_ODL +#define GPIO_SYS_RESET_L GPIO_SYS_RST_ODL +#define GPIO_WP_L GPIO_EC_WP_ODL +#define GPIO_RECOVERY_L GPIO_EC_RECOVERY_BTN_OD +#define GPIO_RECOVERY_L_2 GPIO_GSC_EC_RECOVERY_BTN_OD + +/* I2C Bus Configuration */ + +#define I2C_PORT_DP_REDRIVER NPCX_I2C_PORT0_0 + +#define I2C_PORT_USB_C0_C2_TCPC NPCX_I2C_PORT1_0 +#define I2C_PORT_USB_C1_TCPC NPCX_I2C_PORT4_1 + +#define I2C_PORT_USB_C0_C2_PPC NPCX_I2C_PORT2_0 +#define I2C_PORT_USB_C1_PPC NPCX_I2C_PORT6_1 + +#define I2C_PORT_USB_C0_C2_BC12 NPCX_I2C_PORT2_0 +#define I2C_PORT_USB_C1_BC12 NPCX_I2C_PORT6_1 + +#define I2C_PORT_USB_C0_C2_MUX NPCX_I2C_PORT3_0 +#define I2C_PORT_USB_C1_MUX NPCX_I2C_PORT6_1 + +#define I2C_PORT_QI NPCX_I2C_PORT5_0 +#define I2C_PORT_EEPROM NPCX_I2C_PORT7_0 +#define I2C_PORT_MP2964 NPCX_I2C_PORT7_0 + +#define I2C_ADDR_EEPROM_FLAGS 0x50 + +#define I2C_ADDR_MP2964_FLAGS 0x20 + +#define USBC_PORT_C0_BB_RETIMER_I2C_ADDR 0x58 +#define USBC_PORT_C2_BB_RETIMER_I2C_ADDR 0x59 + +/* Enabling Thunderbolt-compatible mode */ +#define CONFIG_USB_PD_TBT_COMPAT_MODE + +/* Enabling USB4 mode */ +#define CONFIG_USB_PD_USB4 + +/* Retimer */ +#define CONFIG_USBC_RETIMER_FW_UPDATE + +/* Thermal features */ +#define CONFIG_THERMISTOR +#define CONFIG_TEMP_SENSOR +#define CONFIG_TEMP_SENSOR_POWER +#define CONFIG_STEINHART_HART_3V3_30K9_47K_4050B + +/* ADC */ +#define CONFIG_ADC + +/* + * TODO(b/197478860): Enable the fan control. We need + * to check the sensor value and adjust the fan speed. + */ + #define CONFIG_FANS FAN_CH_COUNT + +/* Include math_util for bitmask_uint64 used in pd_timers */ +#define CONFIG_MATH_UTIL + +#ifndef __ASSEMBLER__ + +#include "gpio_signal.h" /* needed by registers.h */ +#include "registers.h" +#include "usbc_config.h" + +enum charge_port { + CHARGE_PORT_TYPEC0, + CHARGE_PORT_TYPEC1, + CHARGE_PORT_TYPEC2, + CHARGE_PORT_BARRELJACK, + CHARGE_PORT_ENUM_COUNT +}; + +enum adc_channel { + ADC_TEMP_SENSOR_1_CPU, + ADC_TEMP_SENSOR_2_CPU_VR, + ADC_TEMP_SENSOR_3_WIFI, + ADC_TEMP_SENSOR_4_DIMM, + ADC_VBUS, + ADC_CH_COUNT +}; + +enum temp_sensor_id { + TEMP_SENSOR_1_CPU, + TEMP_SENSOR_2_CPU_VR, + TEMP_SENSOR_3_WIFI, + TEMP_SENSOR_4_DIMM, + TEMP_SENSOR_COUNT +}; + +enum ioex_port { + IOEX_C0_NCT38XX = 0, + IOEX_C2_NCT38XX, + IOEX_PORT_COUNT +}; + +enum pwm_channel { + PWM_CH_LED_GREEN, /* PWM0 */ + PWM_CH_FAN, /* PWM5 */ + PWM_CH_LED_RED, /* PWM2 */ + PWM_CH_COUNT +}; + +enum fan_channel { + FAN_CH_0 = 0, + FAN_CH_COUNT +}; + +enum mft_channel { + MFT_CH_0 = 0, + MFT_CH_COUNT +}; + +/* + * firmware config fields + */ +/* + * Barrel-jack power (4 bits). + */ +#define EC_CFG_BJ_POWER_L 0 +#define EC_CFG_BJ_POWER_H 3 +#define EC_CFG_BJ_POWER_MASK GENMASK(EC_CFG_BJ_POWER_H, EC_CFG_BJ_POWER_L) + +extern void adp_connect_interrupt(enum gpio_signal signal); + +#endif /* !__ASSEMBLER__ */ + +#endif /* __CROS_EC_BOARD_H */ diff --git a/board/kinox/build.mk b/board/kinox/build.mk new file mode 100644 index 0000000000..00fc2723cd --- /dev/null +++ b/board/kinox/build.mk @@ -0,0 +1,21 @@ +# -*- makefile -*- +# Copyright 2022 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. +# +# Brask board specific files build +# + +CHIP:=npcx +CHIP_FAMILY:=npcx9 +CHIP_VARIANT:=npcx9m3f +BASEBOARD:=brask + +board-y= +board-y+=board.o +board-y+=fans.o +board-y+=i2c.o +board-y+=led.o +board-y+=pwm.o +board-y+=sensors.o +board-y+=usbc_config.o diff --git a/board/kinox/ec.tasklist b/board/kinox/ec.tasklist new file mode 100644 index 0000000000..ebabe8ef51 --- /dev/null +++ b/board/kinox/ec.tasklist @@ -0,0 +1,28 @@ +/* Copyright 2022 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. + */ + +/* + * See CONFIG_TASK_LIST in config.h for details. + * + * USB_CHG_Px tasks must be contiguous (see USB_CHG_PORT_TO_TASK_ID(x)). + * PD_Cx tasks must be contiguous (see PD_PORT_TO_TASK_ID(x)) + */ + +#define CONFIG_TASK_LIST \ + TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(USB_CHG_P0, usb_charger_task, 0, TASK_STACK_SIZE) \ + TASK_ALWAYS(USB_CHG_P1, usb_charger_task, 0, TASK_STACK_SIZE) \ + TASK_ALWAYS(USB_CHG_P2, usb_charger_task, 0, TASK_STACK_SIZE) \ + TASK_NOTEST(CHIPSET, chipset_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(USB_MUX, usb_mux_task, NULL, VENTI_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_ALWAYS(PD_C0, pd_task, NULL, VENTI_TASK_STACK_SIZE) \ + TASK_ALWAYS(PD_C1, pd_task, NULL, VENTI_TASK_STACK_SIZE) \ + TASK_ALWAYS(PD_C2, pd_task, NULL, VENTI_TASK_STACK_SIZE) \ + TASK_ALWAYS(PD_INT_C0, pd_shared_alert_task, (BIT(2) | BIT(0)), LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(PD_INT_C1, pd_interrupt_handler_task, 1, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(CEC, cec_task, NULL, LARGER_TASK_STACK_SIZE) diff --git a/board/kinox/fans.c b/board/kinox/fans.c new file mode 100644 index 0000000000..f2a70636d0 --- /dev/null +++ b/board/kinox/fans.c @@ -0,0 +1,50 @@ +/* Copyright 2022 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. + */ + +/* Physical fans. These are logically separate from pwm_channels. */ + +#include "common.h" +#include "compile_time_macros.h" +#include "console.h" +#include "fan_chip.h" +#include "fan.h" +#include "hooks.h" +#include "pwm.h" + +/* MFT channels. These are logically separate from pwm_channels. */ +const struct mft_t mft_channels[] = { + [MFT_CH_0] = { + .module = NPCX_MFT_MODULE_2, + .clk_src = TCKC_LFCLK, + .pwm_id = PWM_CH_FAN, + }, +}; +BUILD_ASSERT(ARRAY_SIZE(mft_channels) == MFT_CH_COUNT); + +static 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, +}; + +/* + * TOOD(b/197478860): need to update for real fan + * + * Prototype fan spins at about 7200 RPM at 100% PWM. + * Set minimum at around 30% PWM. + */ +static const struct fan_rpm fan_rpm_0 = { + .rpm_min = 2200, + .rpm_start = 2200, + .rpm_max = 7200, +}; + +const struct fan_t fans[FAN_CH_COUNT] = { + [FAN_CH_0] = { + .conf = &fan_conf_0, + .rpm = &fan_rpm_0, + }, +}; diff --git a/board/kinox/gpio.inc b/board/kinox/gpio.inc new file mode 100644 index 0000000000..85ec312dde --- /dev/null +++ b/board/kinox/gpio.inc @@ -0,0 +1,182 @@ +/* -*- mode:c -*- + * + * Copyright 2022 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. + */ + +/* INTERRUPT GPIOs: */ +GPIO_INT(ACOK_OD, PIN(0, 0), GPIO_INT_BOTH, extpower_interrupt) +GPIO_INT(EC_PROCHOT_IN_L, PIN(F, 0), GPIO_INT_BOTH, throttle_ap_prochot_input_interrupt) +GPIO_INT(EC_WP_ODL, PIN(A, 1), GPIO_INT_BOTH, switch_interrupt) +GPIO_INT(GSC_EC_PWR_BTN_ODL, PIN(0, 1), GPIO_INT_BOTH, power_button_interrupt) +GPIO_INT(SEQ_EC_ALL_SYS_PG, PIN(F, 4), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(SEQ_EC_DSW_PWROK, PIN(C, 7), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(SEQ_EC_RSMRST_ODL, PIN(E, 2), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(SLP_S3_L, PIN(A, 5), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(SLP_SUS_L, PIN(F, 1), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(SYS_SLP_S0IX_L, PIN(D, 5), GPIO_INT_BOTH, power_signal_interrupt) +GPIO_INT(USB_C0_BC12_INT_ODL, PIN(C, 6), GPIO_INT_FALLING, bc12_interrupt) +GPIO_INT(USB_C0_C2_TCPC_INT_ODL, PIN(E, 0), GPIO_INT_FALLING, tcpc_alert_event) +GPIO_INT(USB_C0_PPC_INT_ODL, PIN(6, 2), GPIO_INT_FALLING, ppc_interrupt) +GPIO_INT(USB_C0_RT_INT_ODL, PIN(B, 1), GPIO_INT_FALLING, retimer_interrupt) +GPIO_INT(USB_C1_BC12_INT_ODL, PIN(5, 0), GPIO_INT_FALLING, bc12_interrupt) +GPIO_INT(USB_C1_PPC_INT_ODL, PIN(F, 5), GPIO_INT_FALLING, ppc_interrupt) +GPIO_INT(USB_C1_TCPC_INT_ODL, PIN(A, 2), GPIO_INT_FALLING, tcpc_alert_event) +GPIO_INT(USB_C2_BC12_INT_ODL, PIN(8, 3), GPIO_INT_FALLING, bc12_interrupt) +GPIO_INT(USB_C2_PPC_INT_ODL, PIN(7, 0), GPIO_INT_FALLING, ppc_interrupt) +GPIO_INT(USB_C2_RT_INT_ODL, PIN(4, 1), GPIO_INT_FALLING, retimer_interrupt) +GPIO_INT(BJ_ADP_PRESENT_ODL, PIN(8, 2), GPIO_INT_BOTH | GPIO_PULL_UP, adp_connect_interrupt) +GPIO_INT(EC_RECOVERY_BTN_OD, PIN(2, 3), GPIO_INT_BOTH, button_interrupt) + +/* CCD */ +GPIO(CCD_MODE_ODL, PIN(E, 5), GPIO_INPUT) + +/* Security */ +GPIO(EC_ENTERING_RW, PIN(0, 3), GPIO_OUT_LOW) +GPIO(EC_GSC_PACKET_MODE, PIN(7, 5), GPIO_OUT_LOW) + +/* Fan */ +GPIO(EN_PP5000_FAN, PIN(6, 1), GPIO_OUT_HIGH) + +/* ADC, need to check the usage */ +GPIO(ANALOG_PPVAR_PWR_IN_IMON_EC, PIN(4, 2), GPIO_INPUT) + +/* Display */ +GPIO(DP_CONN_OC_ODL, PIN(2, 5), GPIO_INPUT) +GPIO(HDMI_CONN_OC_ODL, PIN(2, 4), GPIO_INPUT) + +/* BarrelJack */ +GPIO(EN_PPVAR_BJ_ADP_L, PIN(0, 7), GPIO_OUT_LOW) + +/* Chipset PCH */ +GPIO(EC_PCHHOT_ODL, PIN(7, 4), GPIO_INPUT) +GPIO(EC_PCH_INT_ODL, PIN(B, 0), GPIO_ODR_HIGH) +GPIO(EC_PCH_RSMRST_L, PIN(A, 6), GPIO_OUT_LOW) +GPIO(EC_PCH_RTCRST, PIN(7, 6), GPIO_OUT_LOW) +GPIO(EC_PCH_SYS_PWROK, PIN(3, 7), GPIO_OUT_LOW) +GPIO(EC_PCH_WAKE_ODL, PIN(C, 0), GPIO_ODR_HIGH) +GPIO(EC_PROCHOT_ODL, PIN(6, 3), GPIO_ODR_HIGH) +GPIO(EN_S5_RAILS, PIN(B, 6), GPIO_OUT_LOW) +GPIO(PCH_PWROK, PIN(7, 2), GPIO_OUT_LOW) +GPIO(SYS_RST_ODL, PIN(C, 5), GPIO_ODR_HIGH) +GPIO(VCCST_PWRGD_OD, PIN(A, 4), GPIO_ODR_LOW) +GPIO(IMVP9_VRRDY_OD, PIN(6, 0), GPIO_INPUT) +GPIO(CPU_C10_GATE_L, PIN(6, 7), GPIO_INPUT) + +/* Button */ +GPIO(EC_PCH_PWR_BTN_ODL, PIN(C, 1), GPIO_ODR_HIGH) +GPIO(GSC_EC_RECOVERY_BTN_OD, PIN(2, 2), GPIO_INPUT) + +/* NFC */ +/* TODO(b/194068530): Enable NFC */ +GPIO(NFC_COIL_ACT_L, PIN(D, 4), GPIO_INPUT) +GPIO(NFC_LOW_POWER_MODE, PIN(9, 5), GPIO_OUT_HIGH) +GPIO(NFC_CARD_DET_L, PIN(A, 3), GPIO_INPUT) +GPIO(EN_NFC_BUZZER, PIN(0, 5), GPIO_OUT_LOW) + +/* Wireless Charger */ +/* TODO(b/191418683): Implement Qi Driver */ +GPIO(EC_QI_PWR, PIN(D, 2), GPIO_OUT_HIGH) +GPIO(EC_I2C_QI_RESET_L, PIN(9, 3), GPIO_OUT_HIGH) +GPIO(EC_I2C_QI_INT_ODL, PIN(9, 6), GPIO_INPUT) + +/* HDMI CEC */ +/* TODO(b/197474873): Enable HDMI CEC */ +GPIO(HDMI_CEC_IN, PIN(4, 0), GPIO_INPUT) +GPIO(HDMI_CEC_OUT, PIN(D, 3), GPIO_OUT_HIGH | GPIO_OPEN_DRAIN) +GPIO(HDMI_CEC_PULL_UP, PIN(C, 2), GPIO_OUT_HIGH) + +/* I2C SCL/SDA */ +GPIO(EC_I2C_QI_SCL, PIN(3, 3), GPIO_INPUT) +GPIO(EC_I2C_QI_SDA, PIN(3, 6), GPIO_INPUT) +GPIO(EC_I2C_MISC_SCL_R, PIN(B, 3), GPIO_INPUT) +GPIO(EC_I2C_MISC_SDA_R, PIN(B, 2), GPIO_INPUT) +GPIO(EC_I2C_DP_SCL, PIN(B, 5), GPIO_INPUT) +GPIO(EC_I2C_DP_SDA, PIN(B, 4), GPIO_INPUT) +GPIO(EC_I2C_USB_C0_C2_PPC_SCL, PIN(9, 2), GPIO_INPUT) +GPIO(EC_I2C_USB_C0_C2_PPC_SDA, PIN(9, 1), GPIO_INPUT) +GPIO(EC_I2C_USB_C0_C2_RT_SCL, PIN(D, 1), GPIO_INPUT) +GPIO(EC_I2C_USB_C0_C2_RT_SDA, PIN(D, 0), GPIO_INPUT) +GPIO(EC_I2C_USB_C0_C2_TCPC_SCL, PIN(9, 0), GPIO_INPUT) +GPIO(EC_I2C_USB_C0_C2_TCPC_SDA, PIN(8, 7), GPIO_INPUT) +GPIO(EC_I2C_USB_C1_MIX_SCL, PIN(E, 4), GPIO_INPUT) +GPIO(EC_I2C_USB_C1_MIX_SDA, PIN(E, 3), GPIO_INPUT) +GPIO(EC_I2C_USB_C1_TCPC_SCL, PIN(F, 3), GPIO_INPUT) +GPIO(EC_I2C_USB_C1_TCPC_SDA, PIN(F, 2), GPIO_INPUT) + +/* USBA */ +GPIO(EN_PP5000_USBA, PIN(D, 7), GPIO_OUT_LOW) +GPIO(USB_A0_OC_ODL, PIN(3, 1), GPIO_INPUT | GPIO_PULL_UP) +GPIO(USB_A1_OC_ODL, PIN(3, 0), GPIO_INPUT | GPIO_PULL_UP) +GPIO(USB_A2_OC_ODL, PIN(2, 7), GPIO_INPUT | GPIO_PULL_UP) +GPIO(USB_A3_OC_ODL, PIN(2, 6), GPIO_INPUT | GPIO_PULL_UP) +GPIO(USB_A0_STATUS_L, PIN(2, 1), GPIO_INPUT) +GPIO(USB_A1_STATUS_L, PIN(2, 0), GPIO_INPUT) +GPIO(USB_A2_STATUS_L, PIN(1, 7), GPIO_INPUT) +GPIO(USB_A3_STATUS_L, PIN(1, 6), GPIO_INPUT) +GPIO(USB_A_LOW_PWR0_OD, PIN(1, 5), GPIO_INPUT | GPIO_PULL_DOWN) +GPIO(USB_A_LOW_PWR1_OD, PIN(1, 4), GPIO_INPUT | GPIO_PULL_DOWN) +GPIO(USB_A_LOW_PWR2_OD, PIN(1, 1), GPIO_INPUT | GPIO_PULL_DOWN) +GPIO(USB_A_LOW_PWR3_OD, PIN(1, 0), GPIO_INPUT | GPIO_PULL_DOWN) +GPIO(USB_A_OC_SOC_L, PIN(8, 0), GPIO_OUT_HIGH) + +/* LED */ +/* TODO(b/197471359): LED implementation */ +GPIO(LED_GREEN_L, PIN(C, 3), GPIO_OUT_LOW) +GPIO(LED_RED_L, PIN(C, 4), GPIO_OUT_LOW) + +/* USBC */ +GPIO(USB_C0_C2_TCPC_RST_ODL, PIN(A, 7), GPIO_ODR_LOW) +GPIO(USB_C1_FRS_EN, PIN(9, 4), GPIO_OUT_LOW) +GPIO(USB_C1_RT_INT_ODL, PIN(A, 0), GPIO_INPUT) +GPIO(USB_C1_RT_RST_R_L, PIN(0, 2), GPIO_OUT_LOW) + +/* GPIO02_P2 to PU */ +/* GPIO03_P2 to PU */ +IOEX(USB_C0_OC_ODL, EXPIN(IOEX_C0_NCT38XX, 0, 4), GPIO_ODR_HIGH) +IOEX(USB_C0_FRS_EN, EXPIN(IOEX_C0_NCT38XX, 0, 6), GPIO_LOW) +IOEX(USB_C0_RT_RST_ODL, EXPIN(IOEX_C0_NCT38XX, 0, 7), GPIO_ODR_LOW) + +IOEX(USB_C2_RT_RST_ODL, EXPIN(IOEX_C2_NCT38XX, 0, 2), GPIO_ODR_LOW) +IOEX(USB_C1_OC_ODL, EXPIN(IOEX_C2_NCT38XX, 0, 3), GPIO_ODR_HIGH) +IOEX(USB_C2_OC_ODL, EXPIN(IOEX_C2_NCT38XX, 0, 4), GPIO_ODR_HIGH) +IOEX(USB_C2_FRS_EN, EXPIN(IOEX_C2_NCT38XX, 0, 6), GPIO_LOW) +/* GPIO07_P2 to PU */ + +/* UART alternate functions */ +ALTERNATE(PIN_MASK(6, 0x30), 0, MODULE_UART, 0) /* GPIO64/CR_SIN1, GPO65/CR_SOUT1/FLPRG1_L */ + +/* I2C alternate functions */ +ALTERNATE(PIN_MASK(3, 0x48), 0, MODULE_I2C, 0) /* GPIO33/I2C5_SCL0/CTS_L, GPIO36/RTS_L/I2C5_SDA0 */ +ALTERNATE(PIN_MASK(8, 0x80), 0, MODULE_I2C, 0) /* GPIO87/I2C1_SDA0 */ +ALTERNATE(PIN_MASK(9, 0x07), 0, MODULE_I2C, 0) /* GPIO92/I2C2_SCL0, GPIO91/I2C2_SDA0, GPIO90/I2C1_SCL0 */ +ALTERNATE(PIN_MASK(B, 0x0c), 0, MODULE_I2C, 0) /* GPIOB3/I2C7_SCL0/DCD_L, GPIOB2/I2C7_SDA0/DSR_L */ +ALTERNATE(PIN_MASK(B, 0x30), 0, MODULE_I2C, 0) /* GPIOB5/I2C0_SCL0, GPIOB4/I2C0_SDA0 */ +ALTERNATE(PIN_MASK(D, 0x03), 0, MODULE_I2C, 0) /* GPIOD1/I2C3_SCL0, GPIOD0/I2C3_SDA0 */ +ALTERNATE(PIN_MASK(E, 0x18), 0, MODULE_I2C, 0) /* GPIOE4/I2C6_SCL1/I3C_SCL, GPIOE3/I2C6_SDA1/I3C_SDA */ +ALTERNATE(PIN_MASK(F, 0x0c), 0, MODULE_I2C, 0) /* GPIOF3/I2C4_SCL1, GPIOF2/I2C4_SDA1 */ + +/* PWM alternate functions */ +ALTERNATE(PIN_MASK(7, 0x08), 0, MODULE_PWM, 0) /* GPIO73/TA2 */ +ALTERNATE(PIN_MASK(B, 0x80), 0, MODULE_PWM, 0) /* GPIOB7/PWM5 */ +ALTERNATE(PIN_MASK(C, 0x18), 0, MODULE_PWM, 0) /* GPIOC4/PWM2, GPIOC3/PWM0 */ + +/* ADC alternate functions */ +ALTERNATE(PIN_MASK(3, 0x10), 0, MODULE_ADC, 0) /* GPIO34/PS2_DAT2/ADC6 */ +ALTERNATE(PIN_MASK(4, 0x38), 0, MODULE_ADC, 0) /* GPIO45/ADC0, GPIO44/ADC1, GPIO43/ADC2 */ +ALTERNATE(PIN_MASK(E, 0x02), 0, MODULE_ADC, 0) /* GPIOE1/ADC7 */ + +/* Unused Pins */ +UNUSED(PIN(D, 6)) /* GPOD6/CR_SOUT3/SHDF_ESPI_L */ +UNUSED(PIN(3, 2)) /* GPO32/TRIS_L */ +UNUSED(PIN(3, 5)) /* GPO35/CR_SOUT4/TEST_L */ +UNUSED(PIN(6, 6)) /* GPIO66 */ +UNUSED(PIN(8, 1)) /* GPIO81/PECI_DATA */ +UNUSED(PIN(5, 6)) /* GPIO56/CLKRUN# */ +UNUSED(PIN(9, 7)) /* GPIO97 */ +UNUSED(PIN(8, 6)) /* GPIO86/TXD/CR_SOUT2 */ +UNUSED(PIN(1, 3)) /* KSO06/GPO13/GP_SEL# */ +UNUSED(PIN(1, 2)) /* KSO07/GPO12/JEN# */ +UNUSED(PIN(0, 6)) /* KSO11/GPIO06/P80_CLK */ +UNUSED(PIN(0, 4)) /* KSO13/GPIO04 */ \ No newline at end of file diff --git a/board/kinox/i2c.c b/board/kinox/i2c.c new file mode 100644 index 0000000000..9c92852461 --- /dev/null +++ b/board/kinox/i2c.c @@ -0,0 +1,78 @@ +/* Copyright 2022 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 "common.h" +#include "compile_time_macros.h" + +#include "i2c.h" + +/* I2C port map configuration */ +const struct i2c_port_t i2c_ports[] = { + { + /* I2C0 */ + .name = "dp_redriver", + .port = I2C_PORT_DP_REDRIVER, + .kbps = 400, + .scl = GPIO_EC_I2C_DP_SCL, + .sda = GPIO_EC_I2C_DP_SDA, + }, + { + /* I2C1 */ + .name = "tcpc0,2", + .port = I2C_PORT_USB_C0_C2_TCPC, + .kbps = 1000, + .scl = GPIO_EC_I2C_USB_C0_C2_TCPC_SCL, + .sda = GPIO_EC_I2C_USB_C0_C2_TCPC_SDA, + }, + { + /* I2C2 */ + .name = "ppc0,2", + .port = I2C_PORT_USB_C0_C2_PPC, + .kbps = 1000, + .scl = GPIO_EC_I2C_USB_C0_C2_PPC_SCL, + .sda = GPIO_EC_I2C_USB_C0_C2_PPC_SDA, + }, + { + /* I2C3 */ + .name = "retimer0,2", + .port = I2C_PORT_USB_C0_C2_MUX, + .kbps = 1000, + .scl = GPIO_EC_I2C_USB_C0_C2_RT_SCL, + .sda = GPIO_EC_I2C_USB_C0_C2_RT_SDA, + }, + { + /* I2C4 C1 TCPC */ + .name = "tcpc1", + .port = I2C_PORT_USB_C1_TCPC, + .kbps = 400, + .scl = GPIO_EC_I2C_USB_C1_TCPC_SCL, + .sda = GPIO_EC_I2C_USB_C1_TCPC_SDA, + }, + { + /* I2C5 */ + .name = "wireless_charger", + .port = I2C_PORT_QI, + .kbps = 400, + .scl = GPIO_EC_I2C_QI_SCL, + .sda = GPIO_EC_I2C_QI_SDA, + }, + { + /* I2C6 */ + .name = "ppc1", + .port = I2C_PORT_USB_C1_PPC, + .kbps = 1000, + .scl = GPIO_EC_I2C_USB_C1_MIX_SCL, + .sda = GPIO_EC_I2C_USB_C1_MIX_SDA, + }, + { + /* I2C7 */ + .name = "eeprom", + .port = I2C_PORT_EEPROM, + .kbps = 400, + .scl = GPIO_EC_I2C_MISC_SCL_R, + .sda = GPIO_EC_I2C_MISC_SDA_R, + }, +}; +const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); diff --git a/board/kinox/led.c b/board/kinox/led.c new file mode 100644 index 0000000000..636804c7a6 --- /dev/null +++ b/board/kinox/led.c @@ -0,0 +1,252 @@ +/* Copyright 2022 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. + * + * Power LED control for Brask. + * Solid green - active power + * Green flashing - suspended + * Red flashing - alert + * Solid red - critical + */ + +#include "chipset.h" +#include "console.h" +#include "ec_commands.h" +#include "gpio.h" +#include "hooks.h" +#include "led_common.h" +#include "pwm.h" +#include "timer.h" +#include "util.h" + +#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args) + +/* + * Due to the CSME-Lite processing, upon startup the CPU transitions through + * S0->S3->S5->S3->S0, causing the LED to turn on/off/on, so + * delay turning off the LED during suspend/shutdown. + */ +#define LED_CPU_DELAY_MS (2000 * MSEC) + +const enum ec_led_id supported_led_ids[] = {EC_LED_ID_POWER_LED}; +const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); + +enum led_color { + LED_OFF = 0, + LED_RED, + LED_GREEN, + + /* Number of colors, not a color itself */ + LED_COLOR_COUNT +}; + +static int set_color_power(enum led_color color, int duty) +{ + int green = 0; + int red = 0; + + if (duty < 0 || 100 < duty) + return EC_ERROR_UNKNOWN; + + switch (color) { + case LED_OFF: + break; + case LED_GREEN: + green = 1; + break; + case LED_RED: + red = 1; + break; + default: + return EC_ERROR_UNKNOWN; + } + + if (red) + pwm_set_duty(PWM_CH_LED_RED, duty); + else + pwm_set_duty(PWM_CH_LED_RED, 0); + + if (green) + pwm_set_duty(PWM_CH_LED_GREEN, duty); + else + pwm_set_duty(PWM_CH_LED_GREEN, 0); + + return EC_SUCCESS; +} + +static int set_color(enum ec_led_id id, enum led_color color, int duty) +{ + switch (id) { + case EC_LED_ID_POWER_LED: + return set_color_power(color, duty); + default: + return EC_ERROR_UNKNOWN; + } +} + +#define LED_PULSE_US (2 * SECOND) +/* 40 msec for nice and smooth transition. */ +#define LED_PULSE_TICK_US (40 * MSEC) + +/* + * When pulsing is enabled, brightness is incremented by every + * usec from 0 to 100% in LED_PULSE_US usec. Then it's decremented + * likewise in LED_PULSE_US usec. + */ +static struct { + uint32_t interval; + int duty_inc; + enum led_color color; + int duty; +} led_pulse; + +#define CONFIG_TICK(interval, color) \ + config_tick((interval), 100 / (LED_PULSE_US / (interval)), (color)) + +static void config_tick(uint32_t interval, int duty_inc, enum led_color color) +{ + led_pulse.interval = interval; + led_pulse.duty_inc = duty_inc; + led_pulse.color = color; + led_pulse.duty = 0; +} + +static void pulse_power_led(enum led_color color) +{ + set_color(EC_LED_ID_POWER_LED, color, led_pulse.duty); + if (led_pulse.duty + led_pulse.duty_inc > 100) + led_pulse.duty_inc = led_pulse.duty_inc * -1; + else if (led_pulse.duty + led_pulse.duty_inc < 0) + led_pulse.duty_inc = led_pulse.duty_inc * -1; + led_pulse.duty += led_pulse.duty_inc; +} + +static void led_tick(void); +DECLARE_DEFERRED(led_tick); +static void led_tick(void) +{ + uint32_t elapsed; + uint32_t next = 0; + uint32_t start = get_time().le.lo; + + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + pulse_power_led(led_pulse.color); + elapsed = get_time().le.lo - start; + next = led_pulse.interval > elapsed ? led_pulse.interval - elapsed : 0; + hook_call_deferred(&led_tick_data, next); +} + +static void led_suspend(void) +{ + CONFIG_TICK(LED_PULSE_TICK_US, LED_GREEN); + led_tick(); +} +DECLARE_DEFERRED(led_suspend); + +static void led_shutdown(void) +{ + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + set_color(EC_LED_ID_POWER_LED, LED_OFF, 0); +} +DECLARE_DEFERRED(led_shutdown); + +static void led_shutdown_hook(void) +{ + hook_call_deferred(&led_tick_data, -1); + hook_call_deferred(&led_suspend_data, -1); + hook_call_deferred(&led_shutdown_data, LED_CPU_DELAY_MS); +} +DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, led_shutdown_hook, HOOK_PRIO_DEFAULT); + +static void led_suspend_hook(void) +{ + hook_call_deferred(&led_shutdown_data, -1); + hook_call_deferred(&led_suspend_data, LED_CPU_DELAY_MS); +} +DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, led_suspend_hook, HOOK_PRIO_DEFAULT); + +static void led_resume(void) +{ + /* + * Assume there is no race condition with led_tick, which also + * runs in hook_task. + */ + hook_call_deferred(&led_tick_data, -1); + /* + * Avoid invoking the suspend/shutdown delayed hooks. + */ + hook_call_deferred(&led_suspend_data, -1); + hook_call_deferred(&led_shutdown_data, -1); + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + set_color(EC_LED_ID_POWER_LED, LED_GREEN, 100); +} +DECLARE_HOOK(HOOK_CHIPSET_RESUME, led_resume, HOOK_PRIO_DEFAULT); + +void led_alert(int enable) +{ + if (enable) { + /* Overwrite the current signal */ + config_tick(1 * SECOND, 100, LED_RED); + led_tick(); + } else { + /* Restore the previous signal */ + if (chipset_in_state(CHIPSET_STATE_ON)) + led_resume(); + else if (chipset_in_state(CHIPSET_STATE_SUSPEND)) + led_suspend_hook(); + else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) + led_shutdown_hook(); + } +} + +void show_critical_error(void) +{ + hook_call_deferred(&led_tick_data, -1); + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + set_color(EC_LED_ID_POWER_LED, LED_RED, 100); +} + +static int command_led(int argc, char **argv) +{ + enum ec_led_id id = EC_LED_ID_POWER_LED; + + if (argc < 2) + return EC_ERROR_PARAM_COUNT; + + if (!strcasecmp(argv[1], "debug")) { + led_auto_control(id, !led_auto_control_is_enabled(id)); + ccprintf("o%s\n", led_auto_control_is_enabled(id) ? "ff" : "n"); + } else if (!strcasecmp(argv[1], "off")) { + set_color(id, LED_OFF, 0); + } else if (!strcasecmp(argv[1], "red")) { + set_color(id, LED_RED, 100); + } else if (!strcasecmp(argv[1], "green")) { + set_color(id, LED_GREEN, 100); + } else if (!strcasecmp(argv[1], "alert")) { + led_alert(1); + } else if (!strcasecmp(argv[1], "crit")) { + show_critical_error(); + } else { + return EC_ERROR_PARAM1; + } + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(led, command_led, + "[debug|red|green|off|alert|crit]", + "Turn on/off LED."); + +void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) +{ + brightness_range[EC_LED_COLOR_RED] = 100; + brightness_range[EC_LED_COLOR_GREEN] = 100; +} + +int led_set_brightness(enum ec_led_id id, const uint8_t *brightness) +{ + if (brightness[EC_LED_COLOR_RED]) + return set_color(id, LED_RED, brightness[EC_LED_COLOR_RED]); + else if (brightness[EC_LED_COLOR_GREEN]) + return set_color(id, LED_GREEN, brightness[EC_LED_COLOR_GREEN]); + else + return set_color(id, LED_OFF, 0); +} diff --git a/board/kinox/pwm.c b/board/kinox/pwm.c new file mode 100644 index 0000000000..125d507a82 --- /dev/null +++ b/board/kinox/pwm.c @@ -0,0 +1,45 @@ +/* Copyright 2022 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 "common.h" + +#include "compile_time_macros.h" +#include "hooks.h" +#include "pwm.h" +#include "pwm_chip.h" + +const struct pwm_t pwm_channels[] = { + [PWM_CH_LED_GREEN] = { + .channel = 0, + .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP, + .freq = 2000 + }, + [PWM_CH_FAN] = { + .channel = 5, + .flags = PWM_CONFIG_OPEN_DRAIN | PWM_CONFIG_DSLEEP, + .freq = 1000 + }, + [PWM_CH_LED_RED] = { + .channel = 2, + .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP, + .freq = 2000 + }, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + +static void board_pwm_init(void) +{ + /* + * TODO(b/197478860): Turn on the fan at 100% by default + * We need to find tune the fan speed according to the + * thermal sensor value. + */ + pwm_enable(PWM_CH_FAN, 1); + pwm_set_duty(PWM_CH_FAN, 100); + + pwm_enable(PWM_CH_LED_RED, 1); + pwm_enable(PWM_CH_LED_GREEN, 1); +} +DECLARE_HOOK(HOOK_INIT, board_pwm_init, HOOK_PRIO_DEFAULT); diff --git a/board/kinox/sensors.c b/board/kinox/sensors.c new file mode 100644 index 0000000000..b3a492b61a --- /dev/null +++ b/board/kinox/sensors.c @@ -0,0 +1,115 @@ +/* Copyright 2022 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 "common.h" +#include "adc_chip.h" +#include "hooks.h" +#include "temp_sensor.h" +#include "thermal.h" +#include "temp_sensor/thermistor.h" + +/* ADC configuration */ +const struct adc_t adc_channels[] = { + [ADC_TEMP_SENSOR_1_CPU] = { + .name = "TEMP_CPU", + .input_ch = NPCX_ADC_CH0, + .factor_mul = ADC_MAX_VOLT, + .factor_div = ADC_READ_MAX + 1, + .shift = 0, + }, + [ADC_TEMP_SENSOR_2_CPU_VR] = { + .name = "TEMP_CPU_VR", + .input_ch = NPCX_ADC_CH1, + .factor_mul = ADC_MAX_VOLT, + .factor_div = ADC_READ_MAX + 1, + .shift = 0, + }, + [ADC_TEMP_SENSOR_3_WIFI] = { + .name = "TEMP_WIFI", + .input_ch = NPCX_ADC_CH6, + .factor_mul = ADC_MAX_VOLT, + .factor_div = ADC_READ_MAX + 1, + .shift = 0, + }, + [ADC_TEMP_SENSOR_4_DIMM] = { + .name = "TEMP_DIMM", + .input_ch = NPCX_ADC_CH7, + .factor_mul = ADC_MAX_VOLT, + .factor_div = ADC_READ_MAX + 1, + .shift = 0, + }, + [ADC_VBUS] = { /* 5/39 voltage divider */ + .name = "VBUS", + .input_ch = NPCX_ADC_CH2, + .factor_mul = ADC_MAX_VOLT * 39, + .factor_div = (ADC_READ_MAX + 1) * 5, + }, +}; +BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); + +/* Temperature sensor configuration */ +const struct temp_sensor_t temp_sensors[] = { + [TEMP_SENSOR_1_CPU] = { + .name = "CPU", + .type = TEMP_SENSOR_TYPE_BOARD, + .read = get_temp_3v3_30k9_47k_4050b, + .idx = ADC_TEMP_SENSOR_1_CPU + }, + [TEMP_SENSOR_2_CPU_VR] = { + .name = "CPU VR", + .type = TEMP_SENSOR_TYPE_BOARD, + .read = get_temp_3v3_30k9_47k_4050b, + .idx = ADC_TEMP_SENSOR_2_CPU_VR + }, + [TEMP_SENSOR_3_WIFI] = { + .name = "WIFI", + .type = TEMP_SENSOR_TYPE_BOARD, + .read = get_temp_3v3_30k9_47k_4050b, + .idx = ADC_TEMP_SENSOR_3_WIFI + }, + [TEMP_SENSOR_4_DIMM] = { + .name = "DIMM", + .type = TEMP_SENSOR_TYPE_BOARD, + .read = get_temp_3v3_30k9_47k_4050b, + .idx = ADC_TEMP_SENSOR_4_DIMM + }, +}; +BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); + +/* + * TODO(b/180681346): update for Alder Lake/brya + * + * 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 (85 C) + */ +/* + * TODO(b/202062363): Remove when clang is fixed. + */ +#define THERMAL_CPU \ + { \ + .temp_host = { \ + [EC_TEMP_THRESH_HIGH] = C_TO_K(70), \ + [EC_TEMP_THRESH_HALT] = C_TO_K(80), \ + }, \ + .temp_host_release = { \ + [EC_TEMP_THRESH_HIGH] = C_TO_K(65), \ + }, \ + .temp_fan_off = C_TO_K(35), \ + .temp_fan_max = C_TO_K(50), \ + } +__maybe_unused static const struct ec_thermal_config thermal_cpu = THERMAL_CPU; + +/* + * TODO(b/197478860): add the thermal sensor setting + */ +/* this should really be "const" */ +struct ec_thermal_config thermal_params[] = { + [TEMP_SENSOR_1_CPU] = THERMAL_CPU, + [TEMP_SENSOR_2_CPU_VR] = THERMAL_CPU, + [TEMP_SENSOR_3_WIFI] = THERMAL_CPU, + [TEMP_SENSOR_4_DIMM] = THERMAL_CPU, +}; +BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT); diff --git a/board/kinox/usbc_config.c b/board/kinox/usbc_config.c new file mode 100644 index 0000000000..a245fc96ae --- /dev/null +++ b/board/kinox/usbc_config.c @@ -0,0 +1,401 @@ +/* Copyright 2022 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 +#include + +#include "common.h" +#include "compile_time_macros.h" +#include "console.h" +#include "driver/bc12/pi3usb9201_public.h" +#include "driver/ppc/syv682x_public.h" +#include "driver/retimer/bb_retimer_public.h" +#include "driver/retimer/kb800x.h" +#include "driver/tcpm/nct38xx.h" +#include "driver/tcpm/rt1715.h" +#include "driver/tcpm/tcpci.h" +#include "ec_commands.h" +#include "gpio.h" +#include "gpio_signal.h" +#include "hooks.h" +#include "ioexpander.h" +#include "system.h" +#include "task.h" +#include "task_id.h" +#include "timer.h" +#include "usbc_config.h" +#include "usbc_ppc.h" +#include "usb_charge.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) + +/* USBC TCPC configuration */ +const struct tcpc_config_t tcpc_config[] = { + [USBC_PORT_C0] = { + .bus_type = EC_BUS_TYPE_I2C, + .i2c_info = { + .port = I2C_PORT_USB_C0_C2_TCPC, + .addr_flags = NCT38XX_I2C_ADDR1_1_FLAGS, + }, + .drv = &nct38xx_tcpm_drv, + .flags = TCPC_FLAGS_TCPCI_REV2_0 | + TCPC_FLAGS_NO_DEBUG_ACC_CONTROL, + }, + [USBC_PORT_C1] = { + .bus_type = EC_BUS_TYPE_I2C, + .i2c_info = { + .port = I2C_PORT_USB_C1_TCPC, + .addr_flags = RT1715_I2C_ADDR_FLAGS, + }, + .drv = &rt1715_tcpm_drv, + }, + [USBC_PORT_C2] = { + .bus_type = EC_BUS_TYPE_I2C, + .i2c_info = { + .port = I2C_PORT_USB_C0_C2_TCPC, + .addr_flags = NCT38XX_I2C_ADDR2_1_FLAGS, + }, + .drv = &nct38xx_tcpm_drv, + .flags = TCPC_FLAGS_TCPCI_REV2_0, + }, +}; +BUILD_ASSERT(ARRAY_SIZE(tcpc_config) == USBC_PORT_COUNT); +BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT == USBC_PORT_COUNT); + +/* USBC PPC configuration */ +struct ppc_config_t ppc_chips[] = { + [USBC_PORT_C0] = { + .i2c_port = I2C_PORT_USB_C0_C2_PPC, + .i2c_addr_flags = SYV682X_ADDR0_FLAGS, + .drv = &syv682x_drv, + }, + [USBC_PORT_C1] = { + .i2c_port = I2C_PORT_USB_C1_PPC, + .i2c_addr_flags = SYV682X_ADDR0_FLAGS, + .drv = &syv682x_drv, + }, + [USBC_PORT_C2] = { + .i2c_port = I2C_PORT_USB_C0_C2_PPC, + .i2c_addr_flags = SYV682X_ADDR2_FLAGS, + .drv = &syv682x_drv, + }, +}; +BUILD_ASSERT(ARRAY_SIZE(ppc_chips) == USBC_PORT_COUNT); + +unsigned int ppc_cnt = ARRAY_SIZE(ppc_chips); + +/* USBC mux configuration - Alder Lake includes internal mux */ +static const struct usb_mux usbc0_tcss_usb_mux = { + .usb_port = USBC_PORT_C0, + .driver = &virtual_usb_mux_driver, + .hpd_update = &virtual_hpd_update, +}; +static const struct usb_mux usbc1_tcss_usb_mux = { + .usb_port = USBC_PORT_C1, + .driver = &virtual_usb_mux_driver, + .hpd_update = &virtual_hpd_update, +}; +static const struct usb_mux usbc2_tcss_usb_mux = { + .usb_port = USBC_PORT_C2, + .driver = &virtual_usb_mux_driver, + .hpd_update = &virtual_hpd_update, +}; + +struct kb800x_control_t kb800x_control[] = { + [USBC_PORT_C0] = { + }, + [USBC_PORT_C1] = { + .retimer_rst_gpio = GPIO_USB_C1_RT_RST_R_L, + .ss_lanes = { + [KB800X_A0] = KB800X_TX0, [KB800X_A1] = KB800X_RX0, + [KB800X_B0] = KB800X_RX1, [KB800X_B1] = KB800X_TX1, + [KB800X_C0] = KB800X_RX0, [KB800X_C1] = KB800X_TX0, + [KB800X_D0] = KB800X_TX1, [KB800X_D1] = KB800X_RX1, + } + }, + [USBC_PORT_C2] = { + }, +}; +BUILD_ASSERT(ARRAY_SIZE(kb800x_control) == USBC_PORT_COUNT); + +const struct usb_mux usb_muxes[] = { + [USBC_PORT_C0] = { + .usb_port = USBC_PORT_C0, + .driver = &bb_usb_retimer, + .hpd_update = bb_retimer_hpd_update, + .i2c_port = I2C_PORT_USB_C0_C2_MUX, + .i2c_addr_flags = USBC_PORT_C0_BB_RETIMER_I2C_ADDR, + .next_mux = &usbc0_tcss_usb_mux, + }, + [USBC_PORT_C1] = { + .usb_port = USBC_PORT_C1, + .driver = &kb800x_usb_mux_driver, + .i2c_port = I2C_PORT_USB_C1_MUX, + .i2c_addr_flags = KB800X_I2C_ADDR0_FLAGS, + .next_mux = &usbc1_tcss_usb_mux, + }, + [USBC_PORT_C2] = { + .usb_port = USBC_PORT_C2, + .driver = &bb_usb_retimer, + .hpd_update = bb_retimer_hpd_update, + .i2c_port = I2C_PORT_USB_C0_C2_MUX, + .i2c_addr_flags = USBC_PORT_C2_BB_RETIMER_I2C_ADDR, + .next_mux = &usbc2_tcss_usb_mux, + }, +}; +BUILD_ASSERT(ARRAY_SIZE(usb_muxes) == USBC_PORT_COUNT); + +/* BC1.2 charger detect configuration */ +const struct pi3usb9201_config_t pi3usb9201_bc12_chips[] = { + [USBC_PORT_C0] = { + .i2c_port = I2C_PORT_USB_C0_C2_BC12, + .i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS, + }, + [USBC_PORT_C1] = { + .i2c_port = I2C_PORT_USB_C1_BC12, + .i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS, + }, + [USBC_PORT_C2] = { + .i2c_port = I2C_PORT_USB_C0_C2_BC12, + .i2c_addr_flags = PI3USB9201_I2C_ADDR_1_FLAGS, + }, +}; +BUILD_ASSERT(ARRAY_SIZE(pi3usb9201_bc12_chips) == USBC_PORT_COUNT); + +/* + * USB C0 and C2 uses burnside bridge chips and have their reset + * controlled by their respective TCPC chips acting as GPIO expanders. + * + * ioex_init() is normally called before we take the TCPCs out of + * reset, so we need to start in disabled mode, then explicitly + * call ioex_init(). + */ + +struct ioexpander_config_t ioex_config[] = { + [IOEX_C0_NCT38XX] = { + .i2c_host_port = I2C_PORT_USB_C0_C2_TCPC, + .i2c_addr_flags = NCT38XX_I2C_ADDR1_1_FLAGS, + .drv = &nct38xx_ioexpander_drv, + .flags = IOEX_FLAGS_DEFAULT_INIT_DISABLED, + }, + [IOEX_C2_NCT38XX] = { + .i2c_host_port = I2C_PORT_USB_C0_C2_TCPC, + .i2c_addr_flags = NCT38XX_I2C_ADDR2_1_FLAGS, + .drv = &nct38xx_ioexpander_drv, + .flags = IOEX_FLAGS_DEFAULT_INIT_DISABLED, + }, +}; +BUILD_ASSERT(ARRAY_SIZE(ioex_config) == CONFIG_IO_EXPANDER_PORT_COUNT); + +__override int bb_retimer_power_enable(const struct usb_mux *me, bool enable) +{ + enum ioex_signal rst_signal; + + if (me->usb_port == USBC_PORT_C0) { + rst_signal = IOEX_USB_C0_RT_RST_ODL; + } else if (me->usb_port == USBC_PORT_C2) { + rst_signal = IOEX_USB_C2_RT_RST_ODL; + } else { + return EC_ERROR_INVAL; + } + + /* + * We do not have a load switch for the burnside bridge chips, + * so we only need to sequence reset. + */ + + if (enable) { + /* + * Tpw, minimum time from VCC to RESET_N de-assertion is 100us. + * For boards that don't provide a load switch control, the + * retimer_init() function ensures power is up before calling + * this function. + */ + ioex_set_level(rst_signal, 1); + /* + * Allow 1ms time for the retimer to power up lc_domain + * which powers I2C controller within retimer + */ + msleep(1); + } else { + ioex_set_level(rst_signal, 0); + msleep(1); + } + return EC_SUCCESS; +} + +__override int bb_retimer_reset(const struct usb_mux *me) +{ + /* + * TODO(b/193402306, b/195375738): Remove this once transition to + * QS Silicon is complete + */ + bb_retimer_power_enable(me, false); + msleep(5); + bb_retimer_power_enable(me, true); + msleep(25); + + return EC_SUCCESS; +} + +void board_reset_pd_mcu(void) +{ + enum gpio_signal tcpc_rst; + + tcpc_rst = GPIO_USB_C0_C2_TCPC_RST_ODL; + + /* + * TODO(b/179648104): figure out correct timing + */ + + gpio_set_level(tcpc_rst, 0); + gpio_set_level(GPIO_USB_C1_RT_RST_R_L, 0); + + /* + * delay for power-on to reset-off and min. assertion time + */ + + msleep(20); + + gpio_set_level(tcpc_rst, 1); + gpio_set_level(GPIO_USB_C1_RT_RST_R_L, 1); + + /* wait for chips to come up */ + + msleep(50); +} + +static void enable_ioex(int ioex) +{ + ioex_init(ioex); +} + +static void board_tcpc_init(void) +{ + /* Don't reset TCPCs after initial reset */ + if (!system_jumped_late()) { + board_reset_pd_mcu(); + + /* + * These IO expander pins are implemented using the + * C0/C2 TCPC, so they must be set up after the TCPC has + * been taken out of reset. + */ + enable_ioex(IOEX_C0_NCT38XX); + enable_ioex(IOEX_C2_NCT38XX); + } + + /* Enable PPC interrupts. */ + gpio_enable_interrupt(GPIO_USB_C0_PPC_INT_ODL); + gpio_enable_interrupt(GPIO_USB_C2_PPC_INT_ODL); + + /* Enable TCPC interrupts. */ + gpio_enable_interrupt(GPIO_USB_C0_C2_TCPC_INT_ODL); + + gpio_enable_interrupt(GPIO_USB_C1_PPC_INT_ODL); + gpio_enable_interrupt(GPIO_USB_C1_TCPC_INT_ODL); +} +DECLARE_HOOK(HOOK_INIT, board_tcpc_init, HOOK_PRIO_INIT_CHIPSET); + +uint16_t tcpc_get_alert_status(void) +{ + uint16_t status = 0; + + if (gpio_get_level(GPIO_USB_C0_C2_TCPC_INT_ODL) == 0) + status |= PD_STATUS_TCPC_ALERT_0 | PD_STATUS_TCPC_ALERT_2; + + if (gpio_get_level(GPIO_USB_C1_TCPC_INT_ODL) == 0) + 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 if (port == USBC_PORT_C1) + return gpio_get_level(GPIO_USB_C1_PPC_INT_ODL) == 0; + else if (port == USBC_PORT_C2) + return gpio_get_level(GPIO_USB_C2_PPC_INT_ODL) == 0; + return 0; +} + +void tcpc_alert_event(enum gpio_signal signal) +{ + switch (signal) { + case GPIO_USB_C0_C2_TCPC_INT_ODL: + schedule_deferred_pd_interrupt(USBC_PORT_C0); + break; + case GPIO_USB_C1_TCPC_INT_ODL: + schedule_deferred_pd_interrupt(USBC_PORT_C1); + break; + default: + break; + } +} + +void bc12_interrupt(enum gpio_signal signal) +{ + switch (signal) { + case GPIO_USB_C0_BC12_INT_ODL: + task_set_event(TASK_ID_USB_CHG_P0, USB_CHG_EVENT_BC12); + break; + case GPIO_USB_C1_BC12_INT_ODL: + task_set_event(TASK_ID_USB_CHG_P1, USB_CHG_EVENT_BC12); + break; + case GPIO_USB_C2_BC12_INT_ODL: + task_set_event(TASK_ID_USB_CHG_P2, USB_CHG_EVENT_BC12); + break; + default: + break; + } +} + +void ppc_interrupt(enum gpio_signal signal) +{ + switch (signal) { + case GPIO_USB_C0_PPC_INT_ODL: + syv682x_interrupt(USBC_PORT_C0); + break; + case GPIO_USB_C1_PPC_INT_ODL: + syv682x_interrupt(USBC_PORT_C1); + break; + case GPIO_USB_C2_PPC_INT_ODL: + syv682x_interrupt(USBC_PORT_C2); + break; + default: + break; + } +} + +void retimer_interrupt(enum gpio_signal signal) +{ + /* + * TODO(b/179513527): add USB-C support + */ +} + +__override bool board_is_dts_port(int port) +{ + return port == USBC_PORT_C0; +} + +__override bool board_is_tbt_usb4_port(int port) +{ + return true; +} + +__override enum tbt_compat_cable_speed board_get_max_tbt_speed(int port) +{ + if (!board_is_tbt_usb4_port(port)) + return TBT_SS_RES_0; + + return TBT_SS_TBT_GEN3; +} diff --git a/board/kinox/usbc_config.h b/board/kinox/usbc_config.h new file mode 100644 index 0000000000..1ccb5f0a71 --- /dev/null +++ b/board/kinox/usbc_config.h @@ -0,0 +1,20 @@ +/* Copyright 2022 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. + */ + +/* Brya board-specific USB-C configuration */ + +#ifndef __CROS_EC_USBC_CONFIG_H +#define __CROS_EC_USBC_CONFIG_H + +#define CONFIG_USB_PD_PORT_MAX_COUNT 3 + +enum usbc_port { + USBC_PORT_C0 = 0, + USBC_PORT_C1, + USBC_PORT_C2, + USBC_PORT_COUNT +}; + +#endif /* __CROS_EC_USBC_CONFIG_H */ diff --git a/board/kinox/vif_override.xml b/board/kinox/vif_override.xml new file mode 100644 index 0000000000..32736caf64 --- /dev/null +++ b/board/kinox/vif_override.xml @@ -0,0 +1,3 @@ + -- cgit v1.2.1