From dd7da7238f68dd243fe6b8708e21aff904c33287 Mon Sep 17 00:00:00 2001 From: Tommy Chung Date: Tue, 4 Oct 2022 11:01:13 +0800 Subject: lisbon: Initial EC image Create the initial EC image for the lisbon 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:246657849 BRANCH=None TEST=make BOARD=lisbon Signed-off-by: Tommy Chung Change-Id: I1b10f39144a035a5877cbdf588c2e7485b8d3db2 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3935722 Commit-Queue: Ricky Chang Reviewed-by: Ricky Chang Reviewed-by: Devin Lu --- board/lisbon/board.c | 591 ++++++++++++++++++++++++++++++++++++++++++ board/lisbon/board.h | 204 +++++++++++++++ board/lisbon/build.mk | 22 ++ board/lisbon/ec.tasklist | 29 +++ board/lisbon/fans.c | 50 ++++ board/lisbon/fw_config.c | 65 +++++ board/lisbon/fw_config.h | 41 +++ board/lisbon/gpio.inc | 187 +++++++++++++ board/lisbon/i2c.c | 78 ++++++ board/lisbon/led.c | 260 +++++++++++++++++++ board/lisbon/pwm.c | 40 +++ board/lisbon/sensors.c | 114 ++++++++ board/lisbon/usbc_config.c | 416 +++++++++++++++++++++++++++++ board/lisbon/usbc_config.h | 20 ++ board/lisbon/vif_override.xml | 3 + 15 files changed, 2120 insertions(+) create mode 100644 board/lisbon/board.c create mode 100644 board/lisbon/board.h create mode 100644 board/lisbon/build.mk create mode 100644 board/lisbon/ec.tasklist create mode 100644 board/lisbon/fans.c create mode 100644 board/lisbon/fw_config.c create mode 100644 board/lisbon/fw_config.h create mode 100644 board/lisbon/gpio.inc create mode 100644 board/lisbon/i2c.c create mode 100644 board/lisbon/led.c create mode 100644 board/lisbon/pwm.c create mode 100644 board/lisbon/sensors.c create mode 100644 board/lisbon/usbc_config.c create mode 100644 board/lisbon/usbc_config.h create mode 100644 board/lisbon/vif_override.xml diff --git a/board/lisbon/board.c b/board/lisbon/board.c new file mode 100644 index 0000000000..aa9a2fb7b0 --- /dev/null +++ b/board/lisbon/board.c @@ -0,0 +1,591 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include + +#include "adc.h" +#include "builtin/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 "peripheral_charger.h" +#include "power.h" +#include "switch.h" +#include "throttle_ap.h" +#include "usbc_config.h" +#include "usbc_ppc.h" +#include "driver/tcpm/tcpci.h" +#include "fw_config.h" + +/* Console output macros */ +#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ##args) +#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ##args) + +static void power_monitor(void); +DECLARE_DEFERRED(power_monitor); + +/******************************************************************************/ +/* 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); + +extern struct pchg_drv cps8100_drv; +struct pchg pchgs[] = { + [0] = { + .cfg = &(const struct pchg_config) { + .drv = &cps8100_drv, + .i2c_port = I2C_PORT_QI, + .irq_pin = GPIO_QI_INT_ODL, + .full_percent = 96, + .block_size = 128, + }, + .events = QUEUE_NULL(PCHG_EVENT_QUEUE_SIZE, enum pchg_event), + }, +}; +const int pchg_count = ARRAY_SIZE(pchgs); + +__override void board_pchg_power_on(int port, bool on) +{ + if (port == 0) + gpio_set_level(GPIO_EC_QI_PWR, on); + else + CPRINTS("%s: Invalid port=%d", __func__, port); +} + +/******************************************************************************/ + +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; +} + +static uint8_t usbc_overcurrent; +static int32_t base_5v_power_s5; +static int32_t base_5v_power_z1; + +/* + * Power usage for each port as measured or estimated. + * Units are milliwatts (5v x ma current) + */ + +/* PP5000_S5 loads */ +#define PWR_S5_BASE_LOAD (5 * 1431) +#define PWR_S5_FRONT_HIGH (5 * 1737) +#define PWR_S5_FRONT_LOW (5 * 1055) +#define PWR_S5_REAR_HIGH (5 * 1737) +#define PWR_S5_REAR_LOW (5 * 1055) +#define PWR_S5_HDMI (5 * 580) +#define PWR_S5_MAX (5 * 10000) +#define FRONT_DELTA (PWR_S5_FRONT_HIGH - PWR_S5_FRONT_LOW) +#define REAR_DELTA (PWR_S5_REAR_HIGH - PWR_S5_REAR_LOW) + +/* PP5000_Z1 loads */ +#define PWR_Z1_BASE_LOAD (5 * 5) +#define PWR_Z1_C_HIGH (5 * 3600) +#define PWR_Z1_C_LOW (5 * 2000) +#define PWR_Z1_MAX (5 * 9000) +/* + * Update the 5V power usage, assuming no throttling, + * and invoke the power monitoring. + */ +static void update_5v_usage(void) +{ + int front_ports = 0; + int rear_ports = 0; + + /* + * Recalculate the 5V load, assuming no throttling. + */ + base_5v_power_s5 = PWR_S5_BASE_LOAD; + if (!gpio_get_level(GPIO_USB_A0_OC_ODL)) { + front_ports++; + base_5v_power_s5 += PWR_S5_FRONT_LOW; + } + if (!gpio_get_level(GPIO_USB_A1_OC_ODL)) { + front_ports++; + base_5v_power_s5 += PWR_S5_FRONT_LOW; + } + /* + * Only 1 front port can run higher power at a time. + */ + if (front_ports > 0) + base_5v_power_s5 += PWR_S5_FRONT_HIGH - PWR_S5_FRONT_LOW; + + if (!gpio_get_level(GPIO_USB_A2_OC_ODL)) { + rear_ports++; + base_5v_power_s5 += PWR_S5_REAR_LOW; + } + if (!gpio_get_level(GPIO_USB_A3_OC_ODL)) { + rear_ports++; + base_5v_power_s5 += PWR_S5_REAR_LOW; + } + /* + * Only 1 rear port can run higher power at a time. + */ + if (rear_ports > 0) + base_5v_power_s5 += PWR_S5_REAR_HIGH - PWR_S5_REAR_LOW; + if (!gpio_get_level(GPIO_HDMI_CONN_OC_ODL)) + base_5v_power_s5 += PWR_S5_HDMI; + base_5v_power_z1 = PWR_Z1_BASE_LOAD; + if (usbc_overcurrent) + base_5v_power_z1 += PWR_Z1_C_HIGH; + /* + * Invoke the power handler immediately. + */ + hook_call_deferred(&power_monitor_data, 0); +} +DECLARE_DEFERRED(update_5v_usage); +/* + * Start power monitoring after ADCs have been initialised. + */ +DECLARE_HOOK(HOOK_INIT, update_5v_usage, HOOK_PRIO_INIT_ADC + 1); + +static void port_ocp_interrupt(enum gpio_signal signal) +{ + hook_call_deferred(&update_5v_usage_data, 0); +} +#include "gpio_list.h" /* Must come after other header files. */ + +/******************************************************************************/ +/* + * 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. + */ + +#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) + ec_bj_power(&pi.voltage, &pi.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_INIT_CHARGE_MANAGER + 1); + +static void board_init(void) +{ + gpio_enable_interrupt(GPIO_BJ_ADP_PRESENT_ODL); + gpio_enable_interrupt(GPIO_HDMI_CONN_OC_ODL); + gpio_enable_interrupt(GPIO_USB_A0_OC_ODL); + gpio_enable_interrupt(GPIO_USB_A1_OC_ODL); + gpio_enable_interrupt(GPIO_USB_A2_OC_ODL); + gpio_enable_interrupt(GPIO_USB_A3_OC_ODL); +} +DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT); + +void board_overcurrent_event(int port, int is_overcurrented) +{ + /* Check that port number is valid. */ + if ((port < 0) || (port >= CONFIG_USB_PD_PORT_MAX_COUNT)) + return; + usbc_overcurrent = is_overcurrented; + update_5v_usage(); +} +/* + * Power monitoring and management. + * + * the power budgets are met without letting the system fall into + * power deficit (perhaps causing a brownout). + * + * There are 2 power budgets that need to be managed: + * The overall goal is to gracefully manage the power demand so that + * - overall system power as measured on the main power supply rail. + * - 5V power delivered to the USB and HDMI ports. + * + * The actual system power demand is calculated from the VBUS voltage and + * the input current (read from a shunt), averaged over 5 readings. + * The power budget limit is from the charge manager. + * + * The 5V power cannot be read directly. Instead, we rely on overcurrent + * inputs from the USB and HDMI ports to indicate that the port is in use + * (and drawing maximum power). + * + * There are 3 throttles that can be applied (in priority order): + * + * - Type A BC1.2 front port restriction (3W) + * - Type A BC1.2 rear port restriction (3W) + * - Type C PD (throttle to 1.5A if sourcing) + * - Turn on PROCHOT, which immediately throttles the CPU. + * + * The first 3 throttles affect both the system power and the 5V rails. + * The third is a last resort to force an immediate CPU throttle to + * reduce the overall power use. + * + * The strategy is to determine what the state of the throttles should be, + * and to then turn throttles off or on as needed to match this. + * + * This function runs on demand, or every 2 ms when the CPU is up, + * and continually monitors the power usage, applying the + * throttles when necessary. + * + * All measurements are in milliwatts. + */ +#define THROT_TYPE_A_FRONT BIT(0) +#define THROT_TYPE_A_REAR BIT(1) +#define THROT_TYPE_C0 BIT(2) +#define THROT_TYPE_C1 BIT(3) +#define THROT_TYPE_C2 BIT(4) +#define THROT_PROCHOT BIT(5) + +/* + * Power gain if front USB A ports are limited. + */ +#define POWER_GAIN_TYPE_A 3200 +/* + * Power gain if Type C port is limited. + */ +#define POWER_GAIN_TYPE_C 8800 +/* + * Power is averaged over 10 ms, with a reading every 2 ms. + */ +#define POWER_DELAY_MS 2 +#define POWER_READINGS (10 / POWER_DELAY_MS) + +static void power_monitor(void) +{ + static uint32_t current_state; + static uint32_t history[POWER_READINGS]; + static uint8_t index; + int32_t delay; + uint32_t new_state = 0, diff; + int32_t headroom_5v_s5 = PWR_S5_MAX - base_5v_power_s5; + int32_t headroom_5v_z1 = PWR_Z1_MAX - base_5v_power_z1; + + /* + * If CPU is off or suspended, no need to throttle + * or restrict power. + */ + if (chipset_in_state(CHIPSET_STATE_ANY_OFF | CHIPSET_STATE_SUSPEND)) { + /* + * Slow down monitoring, assume no throttling required. + */ + delay = 20 * MSEC; + /* + * Clear the first entry of the power table so that + * it is re-initilalised when the CPU starts. + */ + history[0] = 0; + } else { + int32_t charger_mw; + + delay = POWER_DELAY_MS * MSEC; + /* + * Get current charger limit (in mw). + * If not configured yet, skip. + */ + charger_mw = charge_manager_get_power_limit_uw() / 1000; + if (charger_mw != 0) { + int32_t gap, total, max, power; + int i; + + /* + * Read power usage. + */ + power = (adc_read_channel(ADC_VBUS) * + adc_read_channel(ADC_PPVAR_IMON)) / + 1000; + /* Init power table */ + if (history[0] == 0) { + for (i = 0; i < POWER_READINGS; i++) + history[i] = power; + } + /* + * Update the power readings and + * calculate the average and max. + */ + history[index] = power; + index = (index + 1) % POWER_READINGS; + total = 0; + max = history[0]; + for (i = 0; i < POWER_READINGS; i++) { + total += history[i]; + if (history[i] > max) + max = history[i]; + } + /* + * For Type-C power supplies, there is + * less tolerance for exceeding the rating, + * so use the max power that has been measured + * over the measuring period. + * For barrel-jack supplies, the rating can be + * exceeded briefly, so use the average. + */ + if (charge_manager_get_supplier() == CHARGE_SUPPLIER_PD) + power = max; + else + power = total / POWER_READINGS; + /* + * Calculate gap, and if negative, power + * demand is exceeding configured power budget, so + * throttling is required to reduce the demand. + */ + gap = charger_mw - power; + /* + * Limiting type-A power rear ports. + */ + if (gap <= 0) { + new_state |= THROT_TYPE_A_REAR; + headroom_5v_s5 += REAR_DELTA; + if (!(current_state & THROT_TYPE_A_REAR)) + gap += POWER_GAIN_TYPE_A; + } + /* + * Limiting type-A power front ports. + */ + if (gap <= 0) { + new_state |= THROT_TYPE_A_FRONT; + headroom_5v_s5 += FRONT_DELTA; + if (!(current_state & THROT_TYPE_A_REAR)) + gap += POWER_GAIN_TYPE_A; + } + /* + * If the type-C port is sourcing power, + * check whether it should be throttled. + */ + if (ppc_is_sourcing_vbus(0) && gap <= 0) { + new_state |= THROT_TYPE_C0; + headroom_5v_z1 += PWR_Z1_C_HIGH - PWR_Z1_C_LOW; + if (!(current_state & THROT_TYPE_C0)) + gap += POWER_GAIN_TYPE_C; + } + /* + * If the type-C port is sourcing power, + * check whether it should be throttled. + */ + if (ppc_is_sourcing_vbus(1) && gap <= 0) { + new_state |= THROT_TYPE_C1; + headroom_5v_z1 += PWR_Z1_C_HIGH - PWR_Z1_C_LOW; + if (!(current_state & THROT_TYPE_C1)) + gap += POWER_GAIN_TYPE_C; + } + /* + * If the type-C port is sourcing power, + * check whether it should be throttled. + */ + if (ppc_is_sourcing_vbus(2) && gap <= 0) { + new_state |= THROT_TYPE_C2; + headroom_5v_z1 += PWR_Z1_C_HIGH - PWR_Z1_C_LOW; + if (!(current_state & THROT_TYPE_C2)) + gap += POWER_GAIN_TYPE_C; + } + /* + * As a last resort, turn on PROCHOT to + * throttle the CPU. + */ + if (gap <= 0) + new_state |= THROT_PROCHOT; + } + } + /* + * Check the 5v power usage and if necessary, + * adjust the throttles in priority order. + * + * Either throttle may have already been activated by + * the overall power control. + * + * We rely on the overcurrent detection to inform us + * if the port is in use. + * + * - If type C not already throttled: + * * If not overcurrent, prefer to limit type C [1]. + * * If in overcurrentuse: + * - limit type A first [2] + * - If necessary, limit type C [3]. + * - If type A not throttled, if necessary limit it [2]. + */ + if (headroom_5v_z1 < 0) { + /* + * Check whether type C is not throttled, + * and is not overcurrent. + */ + if (!((new_state & THROT_TYPE_C0) || usbc_overcurrent)) { + /* + * [1] Type C not in overcurrent, throttle it. + */ + headroom_5v_z1 += PWR_Z1_C_HIGH - PWR_Z1_C_LOW; + new_state |= THROT_TYPE_C0; + } + /* + * [2] If still under-budget, limit type C. + * No need to check if it is already throttled or not. + */ + if (headroom_5v_z1 < 0) + new_state |= THROT_TYPE_C0; + } + if (headroom_5v_s5 < 0) { + /* + * [1] If type A rear not already throttled, and power still + * needed, limit type A rear. + */ + if (!(new_state & THROT_TYPE_A_REAR) && headroom_5v_s5 < 0) { + headroom_5v_s5 += PWR_S5_REAR_HIGH - PWR_S5_REAR_LOW; + new_state |= THROT_TYPE_A_REAR; + } + /* + * [2] If type A front not already throttled, and power still + * needed, limit type A front. + */ + if (!(new_state & THROT_TYPE_A_FRONT) && headroom_5v_s5 < 0) { + headroom_5v_s5 += PWR_S5_FRONT_HIGH - PWR_S5_FRONT_LOW; + new_state |= THROT_TYPE_A_FRONT; + } + } + /* + * Turn the throttles on or off if they have changed. + */ + diff = new_state ^ current_state; + current_state = new_state; + if (diff & THROT_PROCHOT) { + int prochot = (new_state & THROT_PROCHOT) ? 0 : 1; + + gpio_set_level(GPIO_EC_PROCHOT_ODL, prochot); + } + if (diff & THROT_TYPE_C0) { + enum tcpc_rp_value rp = (new_state & THROT_TYPE_C0) ? + TYPEC_RP_1A5 : + TYPEC_RP_3A0; + + ppc_set_vbus_source_current_limit(0, rp); + tcpm_select_rp_value(0, rp); + pd_update_contract(0); + } + if (diff & THROT_TYPE_C1) { + enum tcpc_rp_value rp = (new_state & THROT_TYPE_C1) ? + TYPEC_RP_1A5 : + TYPEC_RP_3A0; + + ppc_set_vbus_source_current_limit(1, rp); + tcpm_select_rp_value(1, rp); + pd_update_contract(1); + } + if (diff & THROT_TYPE_C2) { + enum tcpc_rp_value rp = (new_state & THROT_TYPE_C2) ? + TYPEC_RP_1A5 : + TYPEC_RP_3A0; + + ppc_set_vbus_source_current_limit(2, rp); + tcpm_select_rp_value(2, rp); + pd_update_contract(2); + } + if (diff & THROT_TYPE_A_REAR) { + int typea_bc = (new_state & THROT_TYPE_A_REAR) ? 1 : 0; + + gpio_set_level(GPIO_USB_A_LOW_PWR0_OD, typea_bc); + gpio_set_level(GPIO_USB_A_LOW_PWR1_OD, typea_bc); + } + if (diff & THROT_TYPE_A_FRONT) { + int typea_bc = (new_state & THROT_TYPE_A_FRONT) ? 1 : 0; + + gpio_set_level(GPIO_USB_A_LOW_PWR2_OD, typea_bc); + gpio_set_level(GPIO_USB_A_LOW_PWR3_OD, typea_bc); + } + hook_call_deferred(&power_monitor_data, delay); +} diff --git a/board/lisbon/board.h b/board/lisbon/board.h new file mode 100644 index 0000000000..7afa678c12 --- /dev/null +++ b/board/lisbon/board.h @@ -0,0 +1,204 @@ +/* Copyright 2022 The ChromiumOS Authors + * 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 +#define CONFIG_USB_PD_DATA_RESET_MSG + +/* 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 + +/* WPC/Qi charger */ +#ifdef SECTION_IS_RW +#define CONFIG_PERIPHERAL_CHARGER +#define CONFIG_CPS8100 +#endif + +#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_PPVAR_IMON, /* ADC3 */ + 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 }; + +extern void adp_connect_interrupt(enum gpio_signal signal); + +#endif /* !__ASSEMBLER__ */ + +#endif /* __CROS_EC_BOARD_H */ diff --git a/board/lisbon/build.mk b/board/lisbon/build.mk new file mode 100644 index 0000000000..3de758d1bd --- /dev/null +++ b/board/lisbon/build.mk @@ -0,0 +1,22 @@ +# -*- makefile -*- +# Copyright 2022 The ChromiumOS Authors +# 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+=fw_config.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/lisbon/ec.tasklist b/board/lisbon/ec.tasklist new file mode 100644 index 0000000000..d16fc35f52 --- /dev/null +++ b/board/lisbon/ec.tasklist @@ -0,0 +1,29 @@ +/* Copyright 2022 The ChromiumOS Authors + * 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_RW(PCHG, pchg_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/lisbon/fans.c b/board/lisbon/fans.c new file mode 100644 index 0000000000..6828438a10 --- /dev/null +++ b/board/lisbon/fans.c @@ -0,0 +1,50 @@ +/* Copyright 2022 The ChromiumOS Authors + * 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/lisbon/fw_config.c b/board/lisbon/fw_config.c new file mode 100644 index 0000000000..3ee71e6a0f --- /dev/null +++ b/board/lisbon/fw_config.c @@ -0,0 +1,65 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "cbi.h" +#include "common.h" +#include "compile_time_macros.h" +#include "console.h" +#include "cros_board_info.h" +#include "fw_config.h" + +#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ##args) + +static union brask_cbi_fw_config fw_config; +BUILD_ASSERT(sizeof(fw_config) == sizeof(uint32_t)); + +/* + * FW_CONFIG defaults for brask if the CBI.FW_CONFIG data is not + * initialized. + */ +static const union brask_cbi_fw_config fw_config_defaults = { + .audio = DB_NAU88L25B_I2S, + .bj_power = BJ_135W, +}; + +/* + * Barrel-jack power adapter ratings. + */ +static const struct { + int voltage; + int current; +} bj_power[] = { + [BJ_135W] = { /* 0 - 135W (also default) */ + .voltage = 19500, + .current = 6920 + }, + [BJ_230W] = { /* 1 - 230W */ + .voltage = 19500, + .current = 11800 + } +}; + +/**************************************************************************** + * Brask FW_CONFIG access + */ +void board_init_fw_config(void) +{ + if (cbi_get_fw_config(&fw_config.raw_value)) { + CPRINTS("CBI: Read FW_CONFIG failed, using board defaults"); + fw_config = fw_config_defaults; + } +} + +void ec_bj_power(uint32_t *voltage, uint32_t *current) +{ + unsigned int bj; + + bj = fw_config.bj_power; + /* Out of range value defaults to 0 */ + if (bj >= ARRAY_SIZE(bj_power)) + bj = 0; + *voltage = bj_power[bj].voltage; + *current = bj_power[bj].current; +} diff --git a/board/lisbon/fw_config.h b/board/lisbon/fw_config.h new file mode 100644 index 0000000000..95d81f1e05 --- /dev/null +++ b/board/lisbon/fw_config.h @@ -0,0 +1,41 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef __BOARD_BRASK_FW_CONFIG_H_ +#define __BOARD_BRASK_FW_CONFIG_H_ + +#include + +/**************************************************************************** + * CBI FW_CONFIG layout for Brask board. + * + * Source of truth is the project/brask/brask/config.star configuration file. + */ +enum ec_cfg_audio_type { DB_AUDIO_UNKNOWN = 0, DB_NAU88L25B_I2S = 1 }; + +enum ec_cfg_bj_power { BJ_135W = 0, BJ_230W = 1 }; + +union brask_cbi_fw_config { + struct { + uint32_t audio : 3; + uint32_t bj_power : 2; + uint32_t reserved_1 : 27; + }; + uint32_t raw_value; +}; + +/** + * Read the cached FW_CONFIG. Guaranteed to have valid values. + * + * @return the FW_CONFIG for the board. + */ +union brask_cbi_fw_config get_fw_config(void); + +/** + * Get the barrel-jack power from FW_CONFIG. + */ +void ec_bj_power(uint32_t *voltage, uint32_t *current); + +#endif /* __BOARD_BRASK_FW_CONFIG_H_ */ diff --git a/board/lisbon/gpio.inc b/board/lisbon/gpio.inc new file mode 100644 index 0000000000..51cf0010c6 --- /dev/null +++ b/board/lisbon/gpio.inc @@ -0,0 +1,187 @@ +/* -*- mode:c -*- + * + * Copyright 2022 The ChromiumOS Authors + * 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) +GPIO_INT(HDMI_CONN_OC_ODL, PIN(2, 4), GPIO_INPUT | GPIO_INT_BOTH, port_ocp_interrupt) +GPIO_INT(USB_A0_OC_ODL, PIN(3, 1), GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH, port_ocp_interrupt) +GPIO_INT(USB_A1_OC_ODL, PIN(3, 0), GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH, port_ocp_interrupt) +GPIO_INT(USB_A2_OC_ODL, PIN(2, 7), GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH, port_ocp_interrupt) +GPIO_INT(USB_A3_OC_ODL, PIN(2, 6), GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH, port_ocp_interrupt) +#ifdef SECTION_IS_RW +GPIO_INT(QI_INT_ODL, PIN(9, 6), GPIO_INT_FALLING, pchg_irq) +#else +UNIMPLEMENTED(QI_INT_ODL) +#endif + +/* 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) + + +/* 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 */ +GPIO(EC_QI_PWR, PIN(D, 2), GPIO_OUT_LOW) +GPIO(QI_RESET_L, PIN(9, 3), GPIO_OUT_HIGH) + +/* 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_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 */ diff --git a/board/lisbon/i2c.c b/board/lisbon/i2c.c new file mode 100644 index 0000000000..0a0b6b69a6 --- /dev/null +++ b/board/lisbon/i2c.c @@ -0,0 +1,78 @@ +/* Copyright 2022 The ChromiumOS Authors + * 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/lisbon/led.c b/board/lisbon/led.c new file mode 100644 index 0000000000..842cee0530 --- /dev/null +++ b/board/lisbon/led.c @@ -0,0 +1,260 @@ +/* Copyright 2022 The ChromiumOS Authors + * 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, const 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); +} +void board_set_charge_limit(int port, int supplier, int charge_ma, int max_ma, + int charge_mv) +{ + /* Blink alert if insufficient power per system_can_boot_ap(). */ + int insufficient_power = + (charge_ma * charge_mv) < + (CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON * 1000); + led_alert(insufficient_power); +} diff --git a/board/lisbon/pwm.c b/board/lisbon/pwm.c new file mode 100644 index 0000000000..3d4335f453 --- /dev/null +++ b/board/lisbon/pwm.c @@ -0,0 +1,40 @@ +/* Copyright 2022 The ChromiumOS Authors + * 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/lisbon/sensors.c b/board/lisbon/sensors.c new file mode 100644 index 0000000000..162140d37d --- /dev/null +++ b/board/lisbon/sensors.c @@ -0,0 +1,114 @@ +/* Copyright 2022 The ChromiumOS Authors + * 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, + }, + [ADC_PPVAR_IMON] = { /* 872.3 mV/A */ + .name = "PPVAR_IMON", + .input_ch = NPCX_ADC_CH3, + .factor_mul = ADC_MAX_VOLT * 1433, + .factor_div = (ADC_READ_MAX + 1) * 1250, + }, + +}; +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/lisbon/usbc_config.c b/board/lisbon/usbc_config.c new file mode 100644 index 0000000000..806ff2c4ee --- /dev/null +++ b/board/lisbon/usbc_config.c @@ -0,0 +1,416 @@ +/* Copyright 2022 The ChromiumOS Authors + * 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_chain usbc0_tcss_usb_mux = { + .mux = + &(const struct usb_mux){ + .usb_port = USBC_PORT_C0, + .driver = &virtual_usb_mux_driver, + .hpd_update = &virtual_hpd_update, + }, +}; +static const struct usb_mux_chain usbc1_tcss_usb_mux = { + .mux = + &(const struct usb_mux){ + .usb_port = USBC_PORT_C1, + .driver = &virtual_usb_mux_driver, + .hpd_update = &virtual_hpd_update, + }, +}; +static const struct usb_mux_chain usbc2_tcss_usb_mux = { + .mux = + &(const struct 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_chain usb_muxes[] = { + [USBC_PORT_C0] = { + .mux = &(const struct usb_mux) { + .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 = &usbc0_tcss_usb_mux, + }, + [USBC_PORT_C1] = { + .mux = &(const struct usb_mux) { + .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 = &usbc1_tcss_usb_mux, + }, + [USBC_PORT_C2] = { + .mux = &(const struct usb_mux) { + .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 = &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: + usb_charger_task_set_event(0, USB_CHG_EVENT_BC12); + break; + case GPIO_USB_C1_BC12_INT_ODL: + usb_charger_task_set_event(1, USB_CHG_EVENT_BC12); + break; + case GPIO_USB_C2_BC12_INT_ODL: + usb_charger_task_set_event(2, 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/lisbon/usbc_config.h b/board/lisbon/usbc_config.h new file mode 100644 index 0000000000..5e7beae21a --- /dev/null +++ b/board/lisbon/usbc_config.h @@ -0,0 +1,20 @@ +/* Copyright 2022 The ChromiumOS Authors + * 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/lisbon/vif_override.xml b/board/lisbon/vif_override.xml new file mode 100644 index 0000000000..32736caf64 --- /dev/null +++ b/board/lisbon/vif_override.xml @@ -0,0 +1,3 @@ + -- cgit v1.2.1