summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Van Patten <timvp@google.com>2022-07-15 16:06:07 +0000
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-07-15 19:23:56 +0000
commit053549018ff686d1763b9d86992bee5d49a34593 (patch)
tree8b6688ea3b030869c164642d16d62d027e6f0d05
parentce12b52b10b973cfe75c8999c45dc6593f1f139b (diff)
downloadchrome-ec-053549018ff686d1763b9d86992bee5d49a34593.tar.gz
Revert "Skyrim: Remove guybrush support"
This reverts commit f9bbc1f59586878d9bbb9eac25e959e306661e67. Reason for revert: Breaks gitlab: https://gitlab.com/zephyr-ec/ec/-/jobs/2724604404 Original change's description: > Skyrim: Remove guybrush support > > Skyrim hardware is available now, so remove guybrush as a skyrim > "variant". > > BUG=b:231996904 > TEST=emerge-skyrim chromeos-zephyr > BRANCH=None > > Change-Id: Id8084000b112fe38a8f9556688e4d9c8edd68b10 > Signed-off-by: Tim Van Patten <timvp@google.com> > Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3763782 > Reviewed-by: Diana Z <dzigterman@chromium.org> Bug: b:231996904 Change-Id: I046926c82ad9fdcd4c8835bf22227dbf70afed95 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3765443 Reviewed-by: Tim Van Patten <timvp@google.com> Tested-by: Tim Van Patten <timvp@google.com> Reviewed-by: Jeremy Bettis <jbettis@chromium.org> Commit-Queue: Tim Van Patten <timvp@google.com>
-rw-r--r--zephyr/projects/skyrim/BUILD.py3
-rw-r--r--zephyr/projects/skyrim/CMakeLists.txt4
-rw-r--r--zephyr/projects/skyrim/Kconfig6
-rw-r--r--zephyr/projects/skyrim/guybrush.dts198
-rw-r--r--zephyr/projects/skyrim/power_signals_guybrush.c127
-rw-r--r--zephyr/projects/skyrim/prj_guybrush.conf9
-rw-r--r--zephyr/projects/skyrim/usbc_config_guybrush.c599
7 files changed, 946 insertions, 0 deletions
diff --git a/zephyr/projects/skyrim/BUILD.py b/zephyr/projects/skyrim/BUILD.py
index a8b2da3716..3d43b3676b 100644
--- a/zephyr/projects/skyrim/BUILD.py
+++ b/zephyr/projects/skyrim/BUILD.py
@@ -33,3 +33,6 @@ def register_variant(project_name):
register_variant(project_name="skyrim")
+
+# TODO: Deprecate guybrush build after skyrim hardware is readily available.
+# register_variant(project_name="guybrush")
diff --git a/zephyr/projects/skyrim/CMakeLists.txt b/zephyr/projects/skyrim/CMakeLists.txt
index bc4c51b40a..54039ecab8 100644
--- a/zephyr/projects/skyrim/CMakeLists.txt
+++ b/zephyr/projects/skyrim/CMakeLists.txt
@@ -5,12 +5,16 @@
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
+project(guybrush)
cros_ec_library_include_directories_ifdef(CONFIG_BOARD_SKYRIM include)
+cros_ec_library_include_directories_ifdef(CONFIG_BOARD_GUYBRUSH include_guybrush)
zephyr_library_sources_ifdef(CONFIG_BOARD_SKYRIM "power_signals.c")
+zephyr_library_sources_ifdef(CONFIG_BOARD_GUYBRUSH "power_signals_guybrush.c")
zephyr_library_sources_ifdef(CONFIG_BOARD_SKYRIM "usbc_config.c")
+zephyr_library_sources_ifdef(CONFIG_BOARD_GUYBRUSH "usbc_config_guybrush.c")
zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_USBC
"usb_pd_policy.c")
diff --git a/zephyr/projects/skyrim/Kconfig b/zephyr/projects/skyrim/Kconfig
index 6da27ee2d0..ea68baf71b 100644
--- a/zephyr/projects/skyrim/Kconfig
+++ b/zephyr/projects/skyrim/Kconfig
@@ -2,6 +2,12 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+config BOARD_GUYBRUSH
+ bool "Google Guybrush Board"
+ help
+ Build Google Guybrush reference board. This board build is a
+ prototype rather than a releasing product.
+
config BOARD_SKYRIM
bool "Google Skyrim Board"
help
diff --git a/zephyr/projects/skyrim/guybrush.dts b/zephyr/projects/skyrim/guybrush.dts
new file mode 100644
index 0000000000..6c5c72d061
--- /dev/null
+++ b/zephyr/projects/skyrim/guybrush.dts
@@ -0,0 +1,198 @@
+/* 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.
+ */
+
+/ {
+ aliases {
+ gpio-wp = &gpio_wp;
+ gpio-kbd-kso2 = &gpio_ec_kso_02_inv;
+ };
+
+ named-gpios {
+ /* Guybrush-specific GPIO customizations */
+ gpio_wp: ec_wp_l {
+ gpios = <&gpio5 0 (GPIO_INPUT | GPIO_ACTIVE_LOW)>;
+ };
+ gpio_ec_pwr_btn_odl: ec_pwr_btn_odl {
+ gpios = <&gpio0 1 GPIO_INPUT>;
+ };
+ gpio_slp_s3_s0i3_l: slp_s3_s0i3_l {
+ gpios = <&gpio7 4 GPIO_INPUT>;
+ enum-name = "GPIO_PCH_SLP_S0_L";
+ };
+ gpio_ec_pcore_int_odl: ec_pcore_int_odl {
+ gpios = <&gpiof 0 GPIO_INPUT_PULL_UP>;
+ };
+ gpio_pg_groupc_s0_od: pg_groupc_s0_od {
+ gpios = <&gpioa 3 GPIO_INPUT>;
+ };
+ gpio_pg_lpddr4x_s3_od: pg_lpddr4x_s3_od {
+ gpios = <&gpio9 5 GPIO_INPUT>;
+ };
+ ec_soc_pwr_good {
+ gpios = <&gpiod 3 GPIO_OUTPUT_LOW>;
+ enum-name = "GPIO_PCH_SYS_PWROK";
+ };
+ ec_entering_rw {
+ gpios = <&gpio6 6 GPIO_OUTPUT_LOW>;
+ enum-name = "GPIO_ENTERING_RW";
+ };
+ ec_clr_cmos {
+ gpios = <&gpioa 1 GPIO_OUTPUT_LOW>;
+ };
+ ec_mem_event {
+ gpios = <&gpioa 5 GPIO_OUTPUT_LOW>;
+ };
+ gpio_ec_soc_pwr_btn_l: ec_soc_pwr_btn_l {
+ gpios = <&gpio6 3 GPIO_OUTPUT_HIGH>;
+ enum-name = "GPIO_PCH_PWRBTN_L";
+ };
+ ec_soc_int_l {
+ gpios = <&gpio8 3 GPIO_OUTPUT_HIGH>;
+ enum-name = "GPIO_EC_INT_L";
+ };
+ soc_thermtrip_odl {
+ gpios = <&gpioe 5 GPIO_INPUT>;
+ };
+ gpio_usb_c0_c1_fault_odl: usb_c0_c1_fault_odl {
+ gpios = <&gpio7 3 GPIO_ODR_HIGH>;
+ };
+ 3axis_int_l {
+ gpios = <&gpioa 2 GPIO_INPUT_PULL_DOWN>;
+ };
+ gpio_voldn_btn_odl: voldn_btn_odl {
+ gpios = <&gpioa 7 GPIO_INPUT_PULL_UP>;
+ enum-name = "GPIO_VOLUME_DOWN_L";
+ };
+ gpio_volup_btn_odl: volup_btn_odl {
+ gpios = <&gpio9 3 GPIO_INPUT_PULL_UP>;
+ enum-name = "GPIO_VOLUME_UP_L";
+ };
+ ec_ps2_clk {
+ gpios = <&gpio6 7 GPIO_INPUT_PULL_UP>;
+ };
+ ec_ps2_dat {
+ gpios = <&gpio7 0 GPIO_INPUT_PULL_UP>;
+ };
+ ec_ps2_rst {
+ gpios = <&gpio6 2 GPIO_INPUT_PULL_UP>;
+ };
+ ec_gpiob0 {
+ gpios = <&gpiob 0 GPIO_INPUT_PULL_UP>;
+ };
+ ec_gpio81 {
+ gpios = <&gpio8 1 GPIO_INPUT_PULL_UP>;
+ };
+ ec_psl_gpo {
+ gpios = <&gpiod 7 GPIO_INPUT_PULL_UP>;
+ };
+ ec_pwm7 {
+ gpios = <&gpio6 0 GPIO_INPUT_PULL_UP>;
+ };
+ gpio_accel_gyro_int_l: accel_gyro_int_l {
+ gpios = <&gpioa 0 GPIO_INPUT_PULL_UP>;
+ };
+ };
+
+ def-lvol-io-list {
+ compatible = "nuvoton,npcx-lvolctrl-def";
+
+ /* Low voltage on I2C6_1 */
+ lvol-io-pads = <&lvol_ioe4 &lvol_ioe3>;
+ };
+
+ named-adc-channels {
+ compatible = "named-adc-channels";
+
+ adc_temp_soc: temp-soc {
+ label = "SOC";
+ enum-name = "ADC_TEMP_SENSOR_SOC";
+ io-channels = <&adc0 0>;
+ };
+ };
+
+ named-temp-sensors {
+ soc-tmp112 {
+ compatible = "cros-ec,temp-sensor-tmp112",
+ "cros-ec,temp-sensor";
+ label = "SOC";
+ enum-name = "TEMP_SENSOR_SOC";
+ tmp112-name = "TMP112_SOC";
+ port = <&i2c_sensor>;
+ i2c-addr-flags = "TMP112_I2C_ADDR_FLAGS0";
+ temp_host_high = <100>;
+ temp_host_halt = <105>;
+ temp_host_release_high = <80>;
+ temp_fan_off = <0>;
+ temp_fan_max = <70>;
+ };
+ amb-tmp112 {
+ compatible = "cros-ec,temp-sensor-tmp112",
+ "cros-ec,temp-sensor";
+ label = "Ambient";
+ enum-name = "TEMP_SENSOR_AMB";
+ tmp112-name = "TMP112_AMB";
+ port = <&i2c_sensor>;
+ i2c-addr-flags = "TMP112_I2C_ADDR_FLAGS1";
+ };
+ };
+
+ gpio-interrupts {
+ compatible = "cros-ec,gpio-interrupts";
+
+ int_pg_lpddr4x_s3: pg_lpddr4x_s3 {
+ irq-pin = <&gpio_pg_lpddr4x_s3_od>;
+ flags = <GPIO_INT_EDGE_BOTH>;
+ handler = "baseboard_en_pwr_pcore_s0";
+ };
+ int_slp_s3_s0i3: slp_s3_s0i3 {
+ irq-pin = <&gpio_slp_s3_s0i3_l>;
+ flags = <GPIO_INT_EDGE_BOTH>;
+ handler = "power_signal_interrupt";
+ };
+ int_ec_pwr_btn: ec_pwr_btn {
+ irq-pin = <&gpio_ec_pwr_btn_odl>;
+ flags = <GPIO_INT_EDGE_BOTH>;
+ handler = "power_signal_interrupt";
+ };
+ int_ec_pcore: ec_pcore {
+ irq-pin = <&gpio_ec_pcore_int_odl>;
+ flags = <GPIO_INT_EDGE_BOTH>;
+ handler = "power_signal_interrupt";
+ };
+ int_pg_groupc_s0: pg_groupc_s0 {
+ irq-pin = <&gpio_pg_groupc_s0_od>;
+ flags = <GPIO_INT_EDGE_BOTH>;
+ handler = "baseboard_en_pwr_pcore_s0";
+ };
+ int_s0_pgood: s0_pgood {
+ irq-pin = <&gpio_s0_pgood>;
+ flags = <GPIO_INT_EDGE_BOTH>;
+ handler = "power_signal_interrupt";
+ };
+ };
+
+ /* Rotation matrices for motion sensors. */
+ motionsense-rotation-ref {
+ compatible = "cros-ec,motionsense-rotation-ref";
+ lid_rot_ref: lid-rotation-ref {
+ mat33 = <0 (-1) 0
+ (-1) 0 0
+ 0 0 (-1)>;
+ };
+
+ base_rot_ref: base-rotation-ref {
+ mat33 = <(-1) 0 0
+ 0 1 0
+ 0 0 (-1)>;
+ };
+ };
+};
+
+/* host interface */
+&espi0 {
+ status = "okay";
+ pinctrl-0 = <&espi_lpc_gp46_47_51_52_53_54_55_57>;
+ pinctrl-names = "default";
+};
diff --git a/zephyr/projects/skyrim/power_signals_guybrush.c b/zephyr/projects/skyrim/power_signals_guybrush.c
new file mode 100644
index 0000000000..ba9eb1a92f
--- /dev/null
+++ b/zephyr/projects/skyrim/power_signals_guybrush.c
@@ -0,0 +1,127 @@
+/* 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.
+ */
+
+#include <zephyr/drivers/gpio.h>
+
+#include "chipset.h"
+#include "config.h"
+#include "gpio_signal.h"
+#include "gpio/gpio_int.h"
+#include "hooks.h"
+#include "power.h"
+#include "timer.h"
+
+/* Wake Sources */
+/* TODO: b/218904113: Convert to using Zephyr GPIOs */
+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 */
+/* TODO: b/218904113: Convert to using Zephyr GPIOs */
+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);
+
+static void baseboard_interrupt_init(void)
+{
+ /* Enable Power Group interrupts. */
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_pg_groupc_s0));
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_pg_lpddr4x_s3));
+}
+DECLARE_HOOK(HOOK_INIT, baseboard_interrupt_init, HOOK_PRIO_POST_I2C);
+
+/**
+ * b/175324615: On G3->S5, wait for RSMRST_L to be deasserted before asserting
+ * PCH_PWRBTN_L.
+ */
+void board_pwrbtn_to_pch(int level)
+{
+ timestamp_t start;
+ const uint32_t timeout_rsmrst_rise_us = 30 * MSEC;
+
+ /* Add delay for G3 exit if asserting PWRBTN_L and RSMRST_L is low. */
+ if (!level &&
+ !gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(gpio_ec_soc_rsmrst_l))) {
+ start = get_time();
+ do {
+ usleep(200);
+ if (gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(
+ gpio_ec_soc_rsmrst_l)))
+ break;
+ } while (time_since32(start) < timeout_rsmrst_rise_us);
+
+ if (!gpio_pin_get_dt(
+ GPIO_DT_FROM_NODELABEL(gpio_ec_soc_rsmrst_l)))
+ ccprints("Error pwrbtn: RSMRST_L still low");
+
+ msleep(16);
+ }
+ gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_ec_soc_pwr_btn_l), level);
+}
+
+void baseboard_en_pwr_pcore_s0(enum gpio_signal signal)
+{
+ /* EC must AND signals PG_LPDDR4X_S3_OD and PG_GROUPC_S0_OD */
+ gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_en_pwr_pcore_s0_r),
+ gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(
+ gpio_pg_lpddr4x_s3_od)) &&
+ gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(
+ 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_pin_set_dt(
+ GPIO_DT_FROM_NODELABEL(gpio_en_pwr_s0_r),
+ gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(gpio_slp_s3_l)) &&
+ gpio_pin_get_dt(
+ GPIO_DT_FROM_NODELABEL(gpio_pg_pwr_s5)));
+
+ /* Now chain off to the normal power signal interrupt handler. */
+ power_signal_interrupt(signal);
+}
+
+void baseboard_s5_pgood(enum gpio_signal signal)
+{
+ baseboard_en_pwr_s0(signal);
+}
+
+void baseboard_set_en_pwr_s3(enum gpio_signal signal)
+{
+ /* EC has no EN_PWR_S3 on this board */
+
+ /* Chain off the normal power signal interrupt handler */
+ power_signal_interrupt(signal);
+}
diff --git a/zephyr/projects/skyrim/prj_guybrush.conf b/zephyr/projects/skyrim/prj_guybrush.conf
new file mode 100644
index 0000000000..0ca57174a4
--- /dev/null
+++ b/zephyr/projects/skyrim/prj_guybrush.conf
@@ -0,0 +1,9 @@
+# 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.
+
+# Guybrush board-specific Kconfig settings.
+CONFIG_BOARD_GUYBRUSH=y
+
+# Only Guybrush has TMP112
+CONFIG_PLATFORM_EC_TEMP_SENSOR_TMP112=y \ No newline at end of file
diff --git a/zephyr/projects/skyrim/usbc_config_guybrush.c b/zephyr/projects/skyrim/usbc_config_guybrush.c
new file mode 100644
index 0000000000..ef9a47e52c
--- /dev/null
+++ b/zephyr/projects/skyrim/usbc_config_guybrush.c
@@ -0,0 +1,599 @@
+/* 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.
+ */
+
+/* Guybrush family-specific USB-C configuration */
+
+#include "cros_board_info.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 "driver/bc12/pi3usb9201.h"
+#include "driver/charger/isl9241.h"
+#include "driver/ppc/aoz1380.h"
+#include "driver/ppc/nx20p348x.h"
+#include "driver/retimer/anx7491.h"
+#include "driver/retimer/ps8811.h"
+#include "driver/retimer/ps8818.h"
+#include "driver/tcpm/nct38xx.h"
+#include "driver/usb_mux/anx7451.h"
+#include "driver/usb_mux/amd_fp6.h"
+#include "gpio.h"
+#include "gpio/gpio_int.h"
+#include "hooks.h"
+#include "ioexpander.h"
+#include "power.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)
+
+/* USB-A ports */
+enum usba_port { USBA_PORT_A0 = 0, USBA_PORT_A1, USBA_PORT_COUNT };
+
+/* USB-C ports */
+enum usbc_port { USBC_PORT_C0 = 0, USBC_PORT_C1, USBC_PORT_COUNT };
+BUILD_ASSERT(USBC_PORT_COUNT == CONFIG_USB_PD_PORT_MAX_COUNT);
+
+static void reset_nct38xx_port(int port);
+
+static void usbc_interrupt_init(void)
+{
+ /* Enable PPC interrupts. */
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_c0_ppc));
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_c1_ppc));
+
+ /* Enable TCPC interrupts. */
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_c0_tcpc));
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_c1_tcpc));
+
+ /* Enable BC 1.2 interrupts */
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_c0_bc12));
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_c1_bc12));
+
+ /* TODO: Enable SBU fault interrupts (io expander )*/
+}
+DECLARE_HOOK(HOOK_INIT, usbc_interrupt_init, HOOK_PRIO_POST_I2C);
+
+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) == CONFIG_USB_PD_PORT_MAX_COUNT);
+unsigned int ppc_cnt = ARRAY_SIZE(ppc_chips);
+
+/*
+ * .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, bool *);
+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.
+ */
+struct usb_mux usbc0_sbu_mux = {
+ .usb_port = USBC_PORT_C0,
+ .driver = &usbc0_sbu_mux_driver,
+};
+
+__overridable int board_c1_ps8818_mux_set(const struct usb_mux *me,
+ mux_state_t mux_state)
+{
+ CPRINTSUSB("C1: PS8818 mux using default tuning");
+ return 0;
+}
+
+struct usb_mux usbc1_ps8818 = {
+ .usb_port = USBC_PORT_C1,
+ .i2c_port = I2C_PORT_TCPC1,
+ .flags = USB_MUX_FLAG_RESETS_IN_G3,
+ .i2c_addr_flags = PS8818_I2C_ADDR_FLAGS,
+ .driver = &ps8818_usb_retimer_driver,
+ .board_set = &board_c1_ps8818_mux_set,
+};
+
+/*
+ * ANX7491(A1) and ANX7451(C1) are on the same i2c bus. Both default
+ * to 0x29 for the USB i2c address. This moves ANX7451(C1) USB i2c
+ * address to 0x2A. ANX7491(A1) will stay at the default 0x29.
+ */
+uint16_t board_anx7451_get_usb_i2c_addr(const struct usb_mux *me)
+{
+ ASSERT(me->usb_port == USBC_PORT_C1);
+ return 0x2a;
+}
+
+__overridable int board_c1_anx7451_mux_set(const struct usb_mux *me,
+ mux_state_t mux_state)
+{
+ CPRINTSUSB("C1: ANX7451 mux using default tuning");
+ return 0;
+}
+
+struct usb_mux usbc1_anx7451 = {
+ .usb_port = USBC_PORT_C1,
+ .i2c_port = I2C_PORT_TCPC1,
+ .flags = USB_MUX_FLAG_RESETS_IN_G3,
+ .i2c_addr_flags = ANX7491_I2C_ADDR3_FLAGS,
+ .driver = &anx7451_usb_mux_driver,
+ .board_set = &board_c1_anx7451_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 = filled in by setup_mux based on fw_config */
+ }
+};
+BUILD_ASSERT(ARRAY_SIZE(usb_muxes) == CONFIG_USB_PD_PORT_MAX_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,
+ bool *ack_required)
+{
+ /* This driver does not use host command ACKs */
+ *ack_required = false;
+
+ 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;
+}
+
+static void setup_mux(void)
+{
+ /* TODO: Fill in C1 mux based on CBI */
+ CPRINTSUSB("C1: Setting ANX7451 mux");
+ usb_muxes[USBC_PORT_C1].next_mux = &usbc1_anx7451;
+}
+DECLARE_HOOK(HOOK_INIT, setup_mux, HOOK_PRIO_INIT_I2C);
+
+int board_set_active_charge_port(int port)
+{
+ int is_valid_port = (port >= 0 && port < CONFIG_USB_PD_PORT_MAX_COUNT);
+ int i;
+ int rv;
+
+ if (port == CHARGE_PORT_NONE) {
+ CPRINTSUSB("Disabling all charger ports");
+
+ /* Disable all ports. */
+ for (i = 0; i < ppc_cnt; i++) {
+ /*
+ * If this port had booted in dead battery mode, go
+ * ahead and reset it so EN_SNK responds properly.
+ */
+ if (nct38xx_get_boot_type(i) ==
+ NCT38XX_BOOT_DEAD_BATTERY) {
+ reset_nct38xx_port(i);
+ pd_set_error_recovery(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 we can reset any ports in dead battery mode
+ *
+ * The NCT3807 may continue to keep EN_SNK low on the dead battery port
+ * and allow a dangerous level of voltage to pass through to the initial
+ * charge port (see b/183660105). We must reset the ports if we have
+ * sufficient battery to do so, which will bring EN_SNK back under
+ * normal control.
+ */
+ rv = EC_SUCCESS;
+ for (i = 0; i < board_get_usb_pd_port_count(); i++) {
+ if (nct38xx_get_boot_type(i) == NCT38XX_BOOT_DEAD_BATTERY) {
+ CPRINTSUSB("Found dead battery on %d", i);
+ /*
+ * If we have battery, get this port reset ASAP.
+ * This means temporarily rejecting charge manager
+ * sets to it.
+ */
+ if (pd_is_battery_capable()) {
+ reset_nct38xx_port(i);
+ pd_set_error_recovery(i);
+
+ if (port == i)
+ rv = EC_ERROR_INVAL;
+ } else if (port != i) {
+ /*
+ * If other port is selected and in dead battery
+ * mode, reset this port. Otherwise, reject
+ * change because we'll brown out.
+ */
+ if (nct38xx_get_boot_type(port) ==
+ NCT38XX_BOOT_DEAD_BATTERY) {
+ reset_nct38xx_port(i);
+ pd_set_error_recovery(i);
+ } else {
+ rv = EC_ERROR_INVAL;
+ }
+ }
+ }
+ }
+
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /* Check if the port is sourcing VBUS. */
+ if (tcpm_get_src_ctrl(port)) {
+ CPRINTSUSB("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;
+}
+
+/*
+ * 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 = EC_SUCCESS;
+
+ 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);
+}
+
+/* TODO: sbu_fault_interrupt from io expander */
+
+/* Round up 3250 max current to multiple of 128mA for ISL9241 AC prochot. */
+#define GUYBRUSH_AC_PROCHOT_CURRENT_MA 3328
+static void set_ac_prochot(void)
+{
+ isl9241_set_ac_prochot(CHARGER_SOLO, GUYBRUSH_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_nct38xx_port(int port)
+{
+ const struct gpio_dt_spec *reset_gpio_l;
+
+ /* TODO: Save and restore ioex signals */
+ if (port == USBC_PORT_C0)
+ reset_gpio_l = GPIO_DT_FROM_NODELABEL(gpio_usb_c0_tcpc_rst_l);
+ else if (port == USBC_PORT_C1)
+ reset_gpio_l = GPIO_DT_FROM_NODELABEL(gpio_usb_c1_tcpc_rst_l);
+ else
+ /* Invalid port: do nothing */
+ return;
+
+ gpio_pin_set_dt(reset_gpio_l, 0);
+ msleep(NCT38XX_RESET_HOLD_DELAY_MS);
+ gpio_pin_set_dt(reset_gpio_l, 1);
+ nct38xx_reset_notify(port);
+ if (NCT3807_RESET_POST_DELAY_MS != 0)
+ msleep(NCT3807_RESET_POST_DELAY_MS);
+}
+
+void board_reset_pd_mcu(void)
+{
+ /* Reset TCPC0 */
+ reset_nct38xx_port(USBC_PORT_C0);
+
+ /* Reset TCPC1 */
+ reset_nct38xx_port(USBC_PORT_C1);
+}
+
+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_pin_get_dt(
+ GPIO_DT_FROM_NODELABEL(gpio_usb_c0_tcpc_int_odl))) {
+ if (gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(
+ gpio_usb_c0_tcpc_rst_l)) != 0)
+ status |= PD_STATUS_TCPC_ALERT_0;
+ }
+
+ if (!gpio_pin_get_dt(
+ GPIO_DT_FROM_NODELABEL(gpio_usb_c1_tcpc_int_odl))) {
+ if (gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(
+ 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:
+ 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;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * Return if VBUS is sagging too low
+ *
+ * For legacy BC1.2 charging with CONFIG_CHARGE_RAMP_SW, ramp up input current
+ * until voltage drops to 4.5V. Don't go lower than this to be kind to the
+ * charger (see b/67964166).
+ */
+#define BC12_MIN_VOLTAGE 4500
+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;
+}
+
+#define SAFE_RESET_VBUS_DELAY_MS 900
+#define SAFE_RESET_VBUS_MV 5000
+void board_hibernate(void)
+{
+ int port;
+ enum ec_error_list ret;
+
+ /*
+ * 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);
+ }
+
+ /* Try to put our battery fuel gauge into sleep mode */
+ ret = battery_sleep_fuel_gauge();
+ if ((ret != EC_SUCCESS) && (ret != EC_ERROR_UNIMPLEMENTED))
+ cprints(CC_SYSTEM, "Failed to send battery sleep command");
+}
+
+__overridable enum ec_error_list
+board_a1_ps8811_retimer_init(const struct usb_mux *me)
+{
+ return EC_SUCCESS;
+}
+
+static int baseboard_a1_ps8811_retimer_init(const struct usb_mux *me)
+{
+ int rv;
+ int tries = 2;
+
+ do {
+ int val;
+
+ rv = ps8811_i2c_read(me, PS8811_REG_PAGE1,
+ PS8811_REG1_USB_BEQ_LEVEL, &val);
+ } while (rv && --tries);
+
+ if (rv) {
+ CPRINTSUSB("A1: PS8811 retimer not detected!");
+ return rv;
+ }
+ CPRINTSUSB("A1: PS8811 retimer detected");
+ rv = board_a1_ps8811_retimer_init(me);
+ if (rv)
+ CPRINTSUSB("A1: Error during PS8811 setup rv:%d", rv);
+ return rv;
+}
+
+/*
+ * PS8811 is just a type-A USB retimer, reusing mux structure for
+ * convenience.
+ */
+const struct usb_mux usba1_ps8811 = {
+ .usb_port = USBA_PORT_A1,
+ .i2c_port = I2C_PORT_TCPC1,
+ .i2c_addr_flags = PS8811_I2C_ADDR_FLAGS3,
+ .board_init = &baseboard_a1_ps8811_retimer_init,
+};
+
+__overridable enum ec_error_list
+board_a1_anx7491_retimer_init(const struct usb_mux *me)
+{
+ return EC_SUCCESS;
+}
+
+static int baseboard_a1_anx7491_retimer_init(const struct usb_mux *me)
+{
+ int rv;
+ int tries = 2;
+
+ do {
+ int val;
+
+ rv = i2c_read8(me->i2c_port, me->i2c_addr_flags, 0, &val);
+ } while (rv && --tries);
+ if (rv) {
+ CPRINTSUSB("A1: ANX7491 retimer not detected!");
+ return rv;
+ }
+ CPRINTSUSB("A1: ANX7491 retimer detected");
+ rv = board_a1_anx7491_retimer_init(me);
+ if (rv)
+ CPRINTSUSB("A1: Error during ANX7491 setup rv:%d", rv);
+ return rv;
+}
+
+/*
+ * ANX7491 is just a type-A USB retimer, reusing mux structure for
+ * convenience.
+ */
+const struct usb_mux usba1_anx7491 = {
+ .usb_port = USBA_PORT_A1,
+ .i2c_port = I2C_PORT_TCPC1,
+ .i2c_addr_flags = ANX7491_I2C_ADDR0_FLAGS,
+ .board_init = &baseboard_a1_anx7491_retimer_init,
+};
+
+void baseboard_a1_retimer_setup(void)
+{
+ struct usb_mux a1_retimer;
+
+ /* TODO: Support PS8811 retimer through CBI */
+ a1_retimer = usba1_anx7491;
+ a1_retimer.board_init(&a1_retimer);
+}
+DECLARE_DEFERRED(baseboard_a1_retimer_setup);
+
+/* TODO: Remove when guybrush is no longer supported */
+#ifdef CONFIG_BOARD_GUYBRUSH
+void board_overcurrent_event(int port, int is_overcurrented)
+{
+ switch (port) {
+ case USBC_PORT_C0:
+ case USBC_PORT_C1:
+ gpio_pin_set_dt(
+ GPIO_DT_FROM_NODELABEL(gpio_usb_c0_c1_fault_odl),
+ !is_overcurrented);
+ break;
+
+ default:
+ break;
+ }
+}
+#endif