summaryrefslogtreecommitdiff
path: root/baseboard/mancomb/baseboard.c
diff options
context:
space:
mode:
Diffstat (limited to 'baseboard/mancomb/baseboard.c')
-rw-r--r--baseboard/mancomb/baseboard.c921
1 files changed, 921 insertions, 0 deletions
diff --git a/baseboard/mancomb/baseboard.c b/baseboard/mancomb/baseboard.c
new file mode 100644
index 0000000000..7151157ae4
--- /dev/null
+++ b/baseboard/mancomb/baseboard.c
@@ -0,0 +1,921 @@
+/* Copyright 2021 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.
+ */
+
+/* Mancomb family-specific configuration */
+
+#include "adc.h"
+#include "adc_chip.h"
+#include "battery_fuel_gauge.h"
+#include "charge_manager.h"
+#include "charge_ramp.h"
+#include "charge_state_v2.h"
+#include "charge_state.h"
+#include "charger.h"
+#include "chip/npcx/ps2_chip.h"
+#include "chip/npcx/pwm_chip.h"
+#include "chipset.h"
+#include "driver/ppc/aoz1380.h"
+#include "driver/ppc/nx20p348x.h"
+#include "driver/retimer/ps8818.h"
+#include "driver/tcpm/nct38xx.h"
+#include "driver/temp_sensor/sb_tsi.h"
+#include "driver/usb_mux/amd_fp6.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "i2c.h"
+#include "ioexpander.h"
+#include "isl9241.h"
+#include "keyboard_scan.h"
+#include "nct38xx.h"
+#include "pi3usb9201.h"
+#include "power.h"
+#include "pwm.h"
+#include "temp_sensor.h"
+#include "thermal.h"
+#include "thermistor.h"
+#include "usb_mux.h"
+#include "usb_pd_tcpm.h"
+#include "usbc_ppc.h"
+
+#define CPRINTSUSB(format, args...) cprints(CC_USBCHARGE, format, ## args)
+#define CPRINTFUSB(format, args...) cprintf(CC_USBCHARGE, format, ## args)
+
+/* Wake Sources */
+const enum gpio_signal hibernate_wake_pins[] = {
+ GPIO_LID_OPEN,
+ GPIO_AC_PRESENT,
+ GPIO_POWER_BUTTON_L,
+};
+const int hibernate_wake_pins_used = ARRAY_SIZE(hibernate_wake_pins);
+
+/* Power Signal Input List */
+const struct power_signal_info power_signal_list[] = {
+ [X86_SLP_S0_N] = {
+ .gpio = GPIO_PCH_SLP_S0_L,
+ .flags = POWER_SIGNAL_ACTIVE_HIGH,
+ .name = "SLP_S0_DEASSERTED",
+ },
+ [X86_SLP_S3_N] = {
+ .gpio = GPIO_PCH_SLP_S3_L,
+ .flags = POWER_SIGNAL_ACTIVE_HIGH,
+ .name = "SLP_S3_DEASSERTED",
+ },
+ [X86_SLP_S5_N] = {
+ .gpio = GPIO_PCH_SLP_S5_L,
+ .flags = POWER_SIGNAL_ACTIVE_HIGH,
+ .name = "SLP_S5_DEASSERTED",
+ },
+ [X86_S0_PGOOD] = {
+ .gpio = GPIO_S0_PGOOD,
+ .flags = POWER_SIGNAL_ACTIVE_HIGH,
+ .name = "S0_PGOOD",
+ },
+ [X86_S5_PGOOD] = {
+ .gpio = GPIO_S5_PGOOD,
+ .flags = POWER_SIGNAL_ACTIVE_HIGH,
+ .name = "S5_PGOOD",
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT);
+
+const struct i2c_port_t i2c_ports[] = {
+ {
+ .name = "tcpc0",
+ .port = I2C_PORT_TCPC0,
+ .kbps = 400,
+ .scl = GPIO_EC_I2C_USB_A0_C0_SCL,
+ .sda = GPIO_EC_I2C_USB_A0_C0_SDA,
+ },
+ {
+ .name = "tcpc1",
+ .port = I2C_PORT_TCPC1,
+ .kbps = 400,
+ .scl = GPIO_EC_I2C_USB_A1_C1_SCL,
+ .sda = GPIO_EC_I2C_USB_A1_C1_SDA,
+ },
+ {
+ .name = "battery",
+ .port = I2C_PORT_BATTERY,
+ .kbps = 100,
+ .scl = GPIO_EC_I2C_BATT_SCL,
+ .sda = GPIO_EC_I2C_BATT_SDA,
+ },
+ {
+ .name = "usb_mux",
+ .port = I2C_PORT_USB_MUX,
+ .kbps = 400,
+ .scl = GPIO_EC_I2C_USBC_MUX_SCL,
+ .sda = GPIO_EC_I2C_USBC_MUX_SDA,
+ },
+ {
+ .name = "charger",
+ .port = I2C_PORT_CHARGER,
+ .kbps = 400,
+ .scl = GPIO_EC_I2C_POWER_SCL,
+ .sda = GPIO_EC_I2C_POWER_SDA,
+ },
+ {
+ .name = "eeprom",
+ .port = I2C_PORT_EEPROM,
+ .kbps = 400,
+ .scl = GPIO_EC_I2C_CBI_SCL,
+ .sda = GPIO_EC_I2C_CBI_SDA,
+ },
+ {
+ .name = "sensor",
+ .port = I2C_PORT_SENSOR,
+ .kbps = 400,
+ .scl = GPIO_EC_I2C_SENSOR_SCL,
+ .sda = GPIO_EC_I2C_SENSOR_SDA,
+ },
+ {
+ .name = "soc_thermal",
+ .port = I2C_PORT_THERMAL_AP,
+ .kbps = 400,
+ .scl = GPIO_EC_I2C_SOC_SIC,
+ .sda = GPIO_EC_I2C_SOC_SID,
+ },
+};
+const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
+
+/* ADC Channels */
+const struct adc_t adc_channels[] = {
+ [ADC_TEMP_SENSOR_SOC] = {
+ .name = "SOC",
+ .input_ch = NPCX_ADC_CH0,
+ .factor_mul = ADC_MAX_VOLT,
+ .factor_div = ADC_READ_MAX + 1,
+ .shift = 0,
+ },
+ [ADC_TEMP_SENSOR_CHARGER] = {
+ .name = "CHARGER",
+ .input_ch = NPCX_ADC_CH1,
+ .factor_mul = ADC_MAX_VOLT,
+ .factor_div = ADC_READ_MAX + 1,
+ .shift = 0,
+ },
+ [ADC_TEMP_SENSOR_MEMORY] = {
+ .name = "MEMORY",
+ .input_ch = NPCX_ADC_CH2,
+ .factor_mul = ADC_MAX_VOLT,
+ .factor_div = ADC_READ_MAX + 1,
+ .shift = 0,
+ },
+
+};
+BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
+
+/* Temp Sensors */
+static int board_get_memory_temp(int, int *);
+static int board_get_soc_temp(int, int *);
+const struct temp_sensor_t temp_sensors[] = {
+ [TEMP_SENSOR_SOC] = {
+ .name = "SOC",
+ .type = TEMP_SENSOR_TYPE_BOARD,
+ .read = board_get_soc_temp,
+ .idx = TEMP_SENSOR_SOC,
+ },
+ [TEMP_SENSOR_CHARGER] = {
+ .name = "Charger",
+ .type = TEMP_SENSOR_TYPE_BOARD,
+ .read = get_temp_3v3_30k9_47k_4050b,
+ .idx = TEMP_SENSOR_CHARGER,
+ },
+ [TEMP_SENSOR_MEMORY] = {
+ .name = "Memory",
+ .type = TEMP_SENSOR_TYPE_BOARD,
+ .read = board_get_memory_temp,
+ .idx = TEMP_SENSOR_MEMORY,
+ },
+ [TEMP_SENSOR_CPU] = {
+ .name = "CPU",
+ .type = TEMP_SENSOR_TYPE_CPU,
+ .read = sb_tsi_get_val,
+ .idx = 0,
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT);
+
+struct ec_thermal_config thermal_params[TEMP_SENSOR_COUNT] = {
+ [TEMP_SENSOR_SOC] = {
+ .temp_host = {
+ [EC_TEMP_THRESH_HIGH] = C_TO_K(90),
+ [EC_TEMP_THRESH_HALT] = C_TO_K(92),
+ },
+ .temp_host_release = {
+ [EC_TEMP_THRESH_HIGH] = C_TO_K(80),
+ },
+ .temp_fan_off = C_TO_K(32),
+ .temp_fan_max = C_TO_K(75),
+ },
+ [TEMP_SENSOR_CHARGER] = {
+ .temp_host = {
+ [EC_TEMP_THRESH_HIGH] = C_TO_K(90),
+ [EC_TEMP_THRESH_HALT] = C_TO_K(92),
+ },
+ .temp_host_release = {
+ [EC_TEMP_THRESH_HIGH] = C_TO_K(80),
+ },
+ .temp_fan_off = 0,
+ .temp_fan_max = 0,
+ },
+ [TEMP_SENSOR_MEMORY] = {
+ .temp_host = {
+ [EC_TEMP_THRESH_HIGH] = C_TO_K(90),
+ [EC_TEMP_THRESH_HALT] = C_TO_K(92),
+ },
+ .temp_host_release = {
+ [EC_TEMP_THRESH_HIGH] = C_TO_K(80),
+ },
+ .temp_fan_off = 0,
+ .temp_fan_max = 0,
+ },
+ [TEMP_SENSOR_CPU] = {
+ .temp_host = {
+ [EC_TEMP_THRESH_HIGH] = C_TO_K(90),
+ [EC_TEMP_THRESH_HALT] = C_TO_K(92),
+ },
+ .temp_host_release = {
+ [EC_TEMP_THRESH_HIGH] = C_TO_K(80),
+ },
+ .temp_fan_off = 0,
+ .temp_fan_max = 0,
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT);
+
+/*
+ * Battery info for all Mancomb battery types. Note that the fields
+ * start_charging_min/max and charging_min/max are not used for the charger.
+ * The effective temperature limits are given by discharging_min/max_c.
+ *
+ * Fuel Gauge (FG) parameters which are used for determining if the battery
+ * is connected, the appropriate ship mode (battery cutoff) command, and the
+ * charge/discharge FETs status.
+ *
+ * Ship mode (battery cutoff) requires 2 writes to the appropriate smart battery
+ * register. For some batteries, the charge/discharge FET bits are set when
+ * charging/discharging is active, in other types, these bits set mean that
+ * charging/discharging is disabled. Therefore, in addition to the mask for
+ * these bits, a disconnect value must be specified. Note that for TI fuel
+ * gauge, the charge/discharge FET status is found in Operation Status (0x54),
+ * but a read of Manufacturer Access (0x00) will return the lower 16 bits of
+ * Operation status which contains the FET status bits.
+ *
+ * The assumption for battery types supported is that the charge/discharge FET
+ * status can be read with a sb_read() command and therefore, only the register
+ * address, mask, and disconnect value need to be provided.
+ */
+const struct board_batt_params board_battery_info[] = {
+ /* AP18F4M / LIS4163ACPC */
+ [BATTERY_AP18F4M] = {
+ .fuel_gauge = {
+ .manuf_name = "Murata KT00404001",
+ .ship_mode = {
+ .reg_addr = 0x3A,
+ .reg_data = { 0xC574, 0xC574 },
+ },
+ .fet = {
+ .reg_addr = 0x0,
+ .reg_mask = 0x2000,
+ .disconnect_val = 0x2000,
+ }
+ },
+ .batt_info = {
+ .voltage_max = 8700,
+ .voltage_normal = 7600,
+ .voltage_min = 5500,
+ .precharge_current = 256,
+ .start_charging_min_c = 0,
+ .start_charging_max_c = 50,
+ .charging_min_c = 0,
+ .charging_max_c = 60,
+ .discharging_min_c = -20,
+ .discharging_max_c = 75,
+ },
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(board_battery_info) == BATTERY_TYPE_COUNT);
+const enum battery_type DEFAULT_BATTERY_TYPE = BATTERY_AP18F4M;
+
+const struct charger_config_t chg_chips[] = {
+ {
+ .i2c_port = I2C_PORT_CHARGER,
+ .i2c_addr_flags = ISL9241_ADDR_FLAGS,
+ .drv = &isl9241_drv,
+ },
+};
+
+const struct tcpc_config_t tcpc_config[] = {
+ [USBC_PORT_C0] = {
+ .bus_type = EC_BUS_TYPE_I2C,
+ .i2c_info = {
+ .port = I2C_PORT_TCPC0,
+ .addr_flags = NCT38XX_I2C_ADDR1_1_FLAGS,
+ },
+ .drv = &nct38xx_tcpm_drv,
+ .flags = TCPC_FLAGS_TCPCI_REV2_0,
+ },
+ [USBC_PORT_C1] = {
+ .bus_type = EC_BUS_TYPE_I2C,
+ .i2c_info = {
+ .port = I2C_PORT_TCPC1,
+ .addr_flags = NCT38XX_I2C_ADDR1_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);
+
+const int usb_port_enable[USBA_PORT_COUNT] = {
+ IOEX_EN_PP5000_USB_A0_VBUS,
+ IOEX_EN_PP5000_USB_A1_VBUS_DB,
+};
+
+static void baseboard_interrupt_init(void)
+{
+ /* Enable PPC interrupts. */
+ gpio_enable_interrupt(GPIO_USB_C0_PPC_INT_ODL);
+ gpio_enable_interrupt(GPIO_USB_C1_PPC_INT_ODL);
+
+ /* Enable TCPC interrupts. */
+ gpio_enable_interrupt(GPIO_USB_C0_TCPC_INT_ODL);
+ gpio_enable_interrupt(GPIO_USB_C1_TCPC_INT_ODL);
+
+ /* Enable BC 1.2 interrupts */
+ gpio_enable_interrupt(GPIO_USB_C0_BC12_INT_ODL);
+ gpio_enable_interrupt(GPIO_USB_C1_BC12_INT_ODL);
+
+ /* Enable SBU fault interrupts */
+ ioex_enable_interrupt(IOEX_USB_C0_SBU_FAULT_ODL);
+ ioex_enable_interrupt(IOEX_USB_C1_SBU_FAULT_ODL);
+}
+DECLARE_HOOK(HOOK_INIT, baseboard_interrupt_init, HOOK_PRIO_INIT_I2C + 1);
+
+struct ppc_config_t ppc_chips[] = {
+ [USBC_PORT_C0] = {
+ /* Device does not talk I2C */
+ .drv = &aoz1380_drv
+ },
+
+ [USBC_PORT_C1] = {
+ .i2c_port = I2C_PORT_TCPC1,
+ .i2c_addr_flags = NX20P3483_ADDR1_FLAGS,
+ .drv = &nx20p348x_drv
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(ppc_chips) == USBC_PORT_COUNT);
+unsigned int ppc_cnt = ARRAY_SIZE(ppc_chips);
+
+const struct pi3usb9201_config_t pi3usb9201_bc12_chips[] = {
+ [USBC_PORT_C0] = {
+ .i2c_port = I2C_PORT_TCPC0,
+ .i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS,
+ },
+
+ [USBC_PORT_C1] = {
+ .i2c_port = I2C_PORT_TCPC1,
+ .i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS,
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(pi3usb9201_bc12_chips) == USBC_PORT_COUNT);
+
+/*
+ * .init is not necessary here because it has nothing
+ * to do. Primary mux will handle mux state so .get is
+ * not needed as well. usb_mux.c can handle the situation
+ * properly.
+ */
+static int fsusb42umx_set_mux(const struct usb_mux*, mux_state_t);
+const struct usb_mux_driver usbc0_sbu_mux_driver = {
+ .set = fsusb42umx_set_mux,
+};
+
+/*
+ * Since FSUSB42UMX is not a i2c device, .i2c_port and
+ * .i2c_addr_flags are not required here.
+ */
+const struct usb_mux usbc0_sbu_mux = {
+ .usb_port = USBC_PORT_C0,
+ .driver = &usbc0_sbu_mux_driver,
+};
+
+static int board_ps8818_mux_set(const struct usb_mux*, mux_state_t);
+const struct usb_mux usbc1_ps8818 = {
+ .usb_port = USBC_PORT_C1,
+ .i2c_port = I2C_PORT_TCPC1,
+ .i2c_addr_flags = PS8818_I2C_ADDR_FLAGS,
+ .driver = &ps8818_usb_retimer_driver,
+ .board_set = &board_ps8818_mux_set,
+};
+
+struct usb_mux usb_muxes[] = {
+ [USBC_PORT_C0] = {
+ .usb_port = USBC_PORT_C0,
+ .i2c_port = I2C_PORT_USB_MUX,
+ .i2c_addr_flags = AMD_FP6_C0_MUX_I2C_ADDR,
+ .driver = &amd_fp6_usb_mux_driver,
+ .next_mux = &usbc0_sbu_mux,
+ },
+ [USBC_PORT_C1] = {
+ .usb_port = USBC_PORT_C1,
+ .i2c_port = I2C_PORT_USB_MUX,
+ .i2c_addr_flags = AMD_FP6_C4_MUX_I2C_ADDR,
+ .driver = &amd_fp6_usb_mux_driver,
+ .next_mux = &usbc1_ps8818,
+ }
+};
+BUILD_ASSERT(ARRAY_SIZE(usb_muxes) == USBC_PORT_COUNT);
+
+struct ioexpander_config_t ioex_config[] = {
+ [USBC_PORT_C0] = {
+ .i2c_host_port = I2C_PORT_TCPC0,
+ .i2c_addr_flags = NCT38XX_I2C_ADDR1_1_FLAGS,
+ .drv = &nct38xx_ioexpander_drv,
+ },
+ [USBC_PORT_C1] = {
+ .i2c_host_port = I2C_PORT_TCPC1,
+ .i2c_addr_flags = NCT38XX_I2C_ADDR1_1_FLAGS,
+ .drv = &nct38xx_ioexpander_drv,
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(ioex_config) == USBC_PORT_COUNT);
+BUILD_ASSERT(CONFIG_IO_EXPANDER_PORT_COUNT == USBC_PORT_COUNT);
+
+/* Keyboard scan setting */
+struct keyboard_scan_config keyscan_config = {
+ /*
+ * F3 key scan cycle completed but scan input is not
+ * charging to logic high when EC start scan next
+ * column for "T" key, so we set .output_settle_us
+ * to 80us
+ */
+ .output_settle_us = 80,
+ .debounce_down_us = 6 * MSEC,
+ .debounce_up_us = 30 * MSEC,
+ .scan_period_us = 1500,
+ .min_post_scan_delay_us = 1000,
+ .poll_timeout_us = SECOND,
+ .actual_key_mask = {
+ 0x3c, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff,
+ 0xa4, 0xff, 0xfe, 0x55, 0xfa, 0xca /* full set */
+ },
+};
+
+const struct pwm_t pwm_channels[] = {
+ [PWM_CH_FAN] = {
+ .channel = 0,
+ .flags = PWM_CONFIG_OPEN_DRAIN,
+ .freq = 25000,
+ },
+ [PWM_CH_KBLIGHT] = {
+ .channel = 1,
+ .flags = PWM_CONFIG_DSLEEP,
+ .freq = 100,
+ },
+ [PWM_CH_LED_CHRG] = {
+ .channel = 2,
+ .flags = PWM_CONFIG_DSLEEP,
+ .freq = 100,
+ },
+ [PWM_CH_LED_FULL] = {
+ .channel = 3,
+ .flags = PWM_CONFIG_DSLEEP,
+ .freq = 100,
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
+
+/*
+ * USB C0 port SBU mux use standalone FSUSB42UMX
+ * chip and it needs a board specific driver.
+ * Overall, it will use chained mux framework.
+ */
+static int fsusb42umx_set_mux(const struct usb_mux *me, mux_state_t mux_state)
+{
+ if (mux_state & USB_PD_MUX_POLARITY_INVERTED)
+ ioex_set_level(IOEX_USB_C0_SBU_FLIP, 1);
+ else
+ ioex_set_level(IOEX_USB_C0_SBU_FLIP, 0);
+
+ return EC_SUCCESS;
+}
+
+/*
+ * PS8818 set mux board tuning.
+ * Adds in board specific gain and DP lane count configuration
+ * TODO(b/179036200): Adjust PS8818 tuning for mancomb and variants
+ */
+static int board_ps8818_mux_set(const struct usb_mux *me,
+ mux_state_t mux_state)
+{
+ int rv = EC_SUCCESS;
+
+ /* USB specific config */
+ if (mux_state & USB_PD_MUX_USB_ENABLED) {
+ /* Boost the USB gain */
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_APTX1EQ_10G_LEVEL,
+ PS8818_EQ_LEVEL_UP_MASK,
+ PS8818_EQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_APTX2EQ_10G_LEVEL,
+ PS8818_EQ_LEVEL_UP_MASK,
+ PS8818_EQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_APTX1EQ_5G_LEVEL,
+ PS8818_EQ_LEVEL_UP_MASK,
+ PS8818_EQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_APTX2EQ_5G_LEVEL,
+ PS8818_EQ_LEVEL_UP_MASK,
+ PS8818_EQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ /* Set the RX input termination */
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_RX_PHY,
+ PS8818_RX_INPUT_TERM_MASK,
+ PS8818_RX_INPUT_TERM_112_OHM);
+ if (rv)
+ return rv;
+ }
+
+ /* DP specific config */
+ if (mux_state & USB_PD_MUX_DP_ENABLED) {
+ /* Boost the DP gain */
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_DPEQ_LEVEL,
+ PS8818_DPEQ_LEVEL_UP_MASK,
+ PS8818_DPEQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ /* Enable HPD on the DB */
+ gpio_set_level(GPIO_USB_C1_HPD, 1);
+ } else {
+ /* Disable HPD on the DB */
+ gpio_set_level(GPIO_USB_C1_HPD, 0);
+ }
+
+ return rv;
+}
+
+int board_set_active_charge_port(int port)
+{
+ int is_valid_port = (port >= 0 &&
+ port < CONFIG_USB_PD_PORT_MAX_COUNT);
+ int i;
+
+ if (port == CHARGE_PORT_NONE) {
+ CPRINTSUSB("Disabling all charger ports");
+
+ /* Disable all ports. */
+ for (i = 0; i < ppc_cnt; i++) {
+ /*
+ * Do not return early if one fails otherwise we can
+ * get into a boot loop assertion failure.
+ */
+ if (ppc_vbus_sink_enable(i, 0))
+ CPRINTSUSB("Disabling C%d as sink failed.", i);
+ }
+
+ return EC_SUCCESS;
+ } else if (!is_valid_port) {
+ return EC_ERROR_INVAL;
+ }
+
+
+ /* Check if the port is sourcing VBUS. */
+ if (ppc_is_sourcing_vbus(port)) {
+ CPRINTFUSB("Skip enable C%d", port);
+ return EC_ERROR_INVAL;
+ }
+
+ CPRINTSUSB("New charge port: C%d", port);
+
+ /*
+ * Turn off the other ports' sink path FETs, before enabling the
+ * requested charge port.
+ */
+ for (i = 0; i < ppc_cnt; i++) {
+ if (i == port)
+ continue;
+
+ if (ppc_vbus_sink_enable(i, 0))
+ CPRINTSUSB("C%d: sink path disable failed.", i);
+ }
+
+ /* Enable requested charge port. */
+ if (ppc_vbus_sink_enable(port, 1)) {
+ CPRINTSUSB("C%d: sink path enable failed.", port);
+ return EC_ERROR_UNKNOWN;
+ }
+
+ return EC_SUCCESS;
+}
+
+int board_is_i2c_port_powered(int port)
+{
+ switch (port) {
+ case I2C_PORT_USB_MUX:
+ case I2C_PORT_SENSOR:
+ /* USB mux and sensor i2c bus is unpowered in Z1 */
+ return chipset_in_state(CHIPSET_STATE_HARD_OFF) ? 0 : 1;
+ case I2C_PORT_THERMAL_AP:
+ /* SOC thermal i2c bus is unpowered in S0i3/S3/S5/Z1 */
+ return chipset_in_state(CHIPSET_STATE_ANY_OFF |
+ CHIPSET_STATE_ANY_SUSPEND) ? 0 : 1;
+ default:
+ return 1;
+ }
+}
+
+/*
+ * In the AOZ1380 PPC, there are no programmable features. We use
+ * the attached NCT3807 to control a GPIO to indicate 1A5 or 3A0
+ * current limits.
+ */
+int board_aoz1380_set_vbus_source_current_limit(int port,
+ enum tcpc_rp_value rp)
+{
+ int rv;
+
+ /* Use the TCPC to set the current limit */
+ rv = ioex_set_level(IOEX_USB_C0_PPC_ILIM_3A_EN,
+ (rp == TYPEC_RP_3A0) ? 1 : 0);
+
+ return rv;
+}
+
+void board_set_charge_limit(int port, int supplier, int charge_ma,
+ int max_ma, int charge_mv)
+{
+ charge_set_input_current_limit(MAX(charge_ma,
+ CONFIG_CHARGER_INPUT_CURRENT),
+ charge_mv);
+}
+
+void sbu_fault_interrupt(enum ioex_signal signal)
+{
+ int port = (signal == IOEX_USB_C0_SBU_FAULT_ODL) ? 0 : 1;
+
+ pd_handle_overcurrent(port);
+}
+
+static void set_ac_prochot(void)
+{
+ isl9241_set_ac_prochot(CHARGER_SOLO, MANCOMB_AC_PROCHOT_CURRENT_MA);
+}
+DECLARE_HOOK(HOOK_INIT, set_ac_prochot, HOOK_PRIO_DEFAULT);
+
+void tcpc_alert_event(enum gpio_signal signal)
+{
+ int port;
+
+ switch (signal) {
+ case GPIO_USB_C0_TCPC_INT_ODL:
+ port = 0;
+ break;
+ case GPIO_USB_C1_TCPC_INT_ODL:
+ port = 1;
+ break;
+ default:
+ return;
+ }
+
+ schedule_deferred_pd_interrupt(port);
+}
+
+static void reset_pd_port(int port, enum gpio_signal reset_gpio_l,
+ int hold_delay, int post_delay)
+{
+ gpio_set_level(reset_gpio_l, 0);
+ msleep(hold_delay);
+ gpio_set_level(reset_gpio_l, 1);
+ if (post_delay)
+ msleep(post_delay);
+}
+
+
+void board_reset_pd_mcu(void)
+{
+ /* Reset TCPC0 */
+ reset_pd_port(USBC_PORT_C0, GPIO_USB_C0_TCPC_RST_L,
+ NCT38XX_RESET_HOLD_DELAY_MS,
+ NCT38XX_RESET_POST_DELAY_MS);
+
+ /* Reset TCPC1 */
+ reset_pd_port(USBC_PORT_C1, GPIO_USB_C1_TCPC_RST_L,
+ NCT38XX_RESET_HOLD_DELAY_MS,
+ NCT38XX_RESET_POST_DELAY_MS);
+}
+
+uint16_t tcpc_get_alert_status(void)
+{
+ uint16_t status = 0;
+
+ /*
+ * Check which port has the ALERT line set and ignore if that TCPC has
+ * its reset line active.
+ */
+ if (!gpio_get_level(GPIO_USB_C0_TCPC_INT_ODL)) {
+ if (gpio_get_level(GPIO_USB_C0_TCPC_RST_L) != 0)
+ status |= PD_STATUS_TCPC_ALERT_0;
+ }
+
+ if (!gpio_get_level(GPIO_USB_C1_TCPC_INT_ODL)) {
+ if (gpio_get_level(GPIO_USB_C1_TCPC_RST_L) != 0)
+ status |= PD_STATUS_TCPC_ALERT_1;
+ }
+
+ return status;
+}
+
+void ppc_interrupt(enum gpio_signal signal)
+{
+ switch (signal) {
+ case GPIO_USB_C0_PPC_INT_ODL:
+ aoz1380_interrupt(USBC_PORT_C0);
+ break;
+
+ case GPIO_USB_C1_PPC_INT_ODL:
+ nx20p348x_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;
+
+ default:
+ break;
+ }
+}
+
+static int board_get_memory_temp(int idx, int *temp_k)
+{
+ if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
+ return EC_ERROR_NOT_POWERED;
+ return get_temp_3v3_30k9_47k_4050b(idx, temp_k);
+}
+
+static int board_get_soc_temp(int idx, int *temp_k)
+{
+ if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
+ return EC_ERROR_NOT_POWERED;
+ return get_temp_3v3_30k9_47k_4050b(idx, temp_k);
+}
+
+/**
+ * Return if VBUS is sagging too low
+ */
+int board_is_vbus_too_low(int port, enum chg_ramp_vbus_state ramp_state)
+{
+ int voltage = 0;
+ int rv;
+
+ rv = charger_get_vbus_voltage(port, &voltage);
+
+ if (rv) {
+ CPRINTSUSB("%s rv=%d", __func__, rv);
+ return 0;
+ }
+
+ /*
+ * b/168569046: The ISL9241 sometimes incorrectly reports 0 for unknown
+ * reason, causing ramp to stop at 0.5A. Workaround this by ignoring 0.
+ * This partly defeats the point of ramping, but will still catch
+ * VBUS below 4.5V and above 0V.
+ */
+ if (voltage == 0) {
+ CPRINTSUSB("%s vbus=0", __func__);
+ return 0;
+ }
+
+ if (voltage < BC12_MIN_VOLTAGE)
+ CPRINTSUSB("%s vbus=%d", __func__, voltage);
+
+ return voltage < BC12_MIN_VOLTAGE;
+}
+
+/**
+ * b/175324615: On G3->S5, wait for RSMRST_L to be deasserted before asserting
+ * PCH_PWRBTN_L.
+ */
+void board_pwrbtn_to_pch(int level)
+{
+ /* Add delay for G3 exit if asserting PWRBTN_L and S5_PGOOD is low. */
+ if (!level && !gpio_get_level(GPIO_S5_PGOOD)) {
+ /*
+ * From measurement, wait 80 ms for RSMRST_L to rise after
+ * S5_PGOOD.
+ */
+ msleep(G3_TO_PWRBTN_DELAY_MS);
+
+ if (!gpio_get_level(GPIO_S5_PGOOD))
+ ccprints("Error: pwrbtn S5_PGOOD low");
+ }
+ gpio_set_level(GPIO_PCH_PWRBTN_L, level);
+}
+
+void board_hibernate(void)
+{
+ int port;
+
+ /*
+ * If we are charging, then drop the Vbus level down to 5V to ensure
+ * that we don't get locked out of the 6.8V OVLO for our PPCs in
+ * dead-battery mode. This is needed when the TCPC/PPC rails go away.
+ * (b/79218851, b/143778351, b/147007265)
+ */
+ port = charge_manager_get_active_charge_port();
+ if (port != CHARGE_PORT_NONE) {
+ pd_request_source_voltage(port, SAFE_RESET_VBUS_MV);
+
+ /* Give PD task and PPC chip time to get to 5V */
+ msleep(SAFE_RESET_VBUS_DELAY_MS);
+ }
+}
+
+static void baseboard_chipset_suspend(void)
+{
+ /* Disable display and keyboard backlights. */
+ gpio_set_level(GPIO_EC_DISABLE_DISP_BL, 1);
+ ioex_set_level(GPIO_EN_KB_BL, 0);
+}
+DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, baseboard_chipset_suspend,
+ HOOK_PRIO_DEFAULT);
+
+static void baseboard_chipset_resume(void)
+{
+ /* Enable display and keyboard backlights. */
+ gpio_set_level(GPIO_EC_DISABLE_DISP_BL, 0);
+ ioex_set_level(GPIO_EN_KB_BL, 1);
+}
+DECLARE_HOOK(HOOK_CHIPSET_RESUME, baseboard_chipset_resume, HOOK_PRIO_DEFAULT);
+
+void board_overcurrent_event(int port, int is_overcurrented)
+{
+ switch (port) {
+ case USBC_PORT_C0:
+ case USBC_PORT_C1:
+ gpio_set_level(GPIO_USB_C0_C1_FAULT_ODL, !is_overcurrented);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void baseboard_en_pwr_pcore_s0(enum gpio_signal signal)
+{
+
+ /* EC must AND signals PG_LPDDR4X_S3_OD and PG_GROUPC_S0_OD */
+ gpio_set_level(GPIO_EN_PWR_PCORE_S0_R,
+ gpio_get_level(GPIO_PG_LPDDR4X_S3_OD) &&
+ gpio_get_level(GPIO_PG_GROUPC_S0_OD));
+}
+
+void baseboard_en_pwr_s0(enum gpio_signal signal)
+{
+
+ /* EC must AND signals SLP_S3_L and PG_PWR_S5 */
+ gpio_set_level(GPIO_EN_PWR_S0_R,
+ gpio_get_level(GPIO_SLP_S3_L) &&
+ gpio_get_level(GPIO_PG_PWR_S5));
+
+ /* Now chain off to the normal power signal interrupt handler. */
+ power_signal_interrupt(signal);
+}