summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo-Tsai <leocx_tsai@compal.corp-partner.google.com>2022-08-24 11:40:16 +0800
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-08-26 02:11:38 +0000
commit59028c00d7cf8369c1f01373cb8011d1733ba071 (patch)
tree84af9f294a64e8763a82f7046cf00ae55b5e7105
parent7342c1b550b546293b62bcad3c0d335a9cb46a8c (diff)
downloadchrome-ec-59028c00d7cf8369c1f01373cb8011d1733ba071.tar.gz
winterhold: separate zephyr config for winterhold
separate EC Zephyr config for winterhold. BUG=b:240824572,b:243213383 TEST=zmake build winterhold and zmake build skyrim BRANCH=none Signed-off-by: Leo-Tsai <leocx_tsai@compal.corp-partner.google.com> Change-Id: Iedb5fc87a4365d29e4760e07c9bf337b10f69ecc Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3853770 Reviewed-by: Diana Z <dzigterman@chromium.org>
-rw-r--r--zephyr/projects/skyrim/BUILD.py45
-rw-r--r--zephyr/projects/skyrim/CMakeLists.txt27
-rw-r--r--zephyr/projects/skyrim/Kconfig14
-rw-r--r--zephyr/projects/skyrim/battery_skyrim.dts (renamed from zephyr/projects/skyrim/battery.dts)0
-rw-r--r--zephyr/projects/skyrim/battery_winterhold.dts15
-rw-r--r--zephyr/projects/skyrim/led_pins_winterhold.dts63
-rw-r--r--zephyr/projects/skyrim/led_policy_winterhold.dts103
-rw-r--r--zephyr/projects/skyrim/prj_winterhold.conf6
-rw-r--r--zephyr/projects/skyrim/usbc_config.c2
-rw-r--r--zephyr/projects/skyrim/winterhold.dts4
-rw-r--r--zephyr/projects/skyrim/winterhold/src/usbc_config.c631
11 files changed, 878 insertions, 32 deletions
diff --git a/zephyr/projects/skyrim/BUILD.py b/zephyr/projects/skyrim/BUILD.py
index 1651e5f68b..394afcc10d 100644
--- a/zephyr/projects/skyrim/BUILD.py
+++ b/zephyr/projects/skyrim/BUILD.py
@@ -1,11 +1,15 @@
-# Copyright 2021 The Chromium OS Authors. All rights reserved.
+# Copyright 2021 The ChromiumOS Authors.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Define zmake projects for skyrim."""
-def register_variant(project_name):
+def register_skyrim_project(
+ project_name,
+ extra_dts_overlays=(),
+ extra_kconfig_files=(),
+):
"""Register a variant of skyrim."""
register_npcx_project(
project_name=project_name,
@@ -13,24 +17,43 @@ def register_variant(project_name):
dts_overlays=[
# Common to all projects.
here / "adc.dts",
- here / "battery.dts",
here / "fan.dts",
here / "gpio.dts",
here / "i2c.dts",
here / "interrupts.dts",
here / "keyboard.dts",
- here / "led_pins_skyrim.dts",
- here / "led_policy_skyrim.dts",
here / "motionsense.dts",
here / "usbc.dts",
# Project-specific DTS customizations.
- here / f"{project_name}.dts",
- ],
- kconfig_files=[
- here / f"prj_{project_name}.conf",
+ *extra_dts_overlays,
],
+ kconfig_files=[here / "prj.conf", *extra_kconfig_files],
)
-register_variant(project_name="skyrim")
-register_variant(project_name="winterhold")
+register_skyrim_project(
+ project_name="skyrim",
+ extra_dts_overlays=[
+ here / "skyrim.dts",
+ here / "battery_skyrim.dts",
+ here / "led_pins_skyrim.dts",
+ here / "led_policy_skyrim.dts",
+ ],
+ extra_kconfig_files=[
+ here / "prj_skyrim.conf",
+ ],
+)
+
+
+register_skyrim_project(
+ project_name="winterhold",
+ extra_dts_overlays=[
+ here / "winterhold.dts",
+ here / "battery_winterhold.dts",
+ here / "led_pins_winterhold.dts",
+ here / "led_policy_winterhold.dts",
+ ],
+ extra_kconfig_files=[
+ here / "prj_winterhold.conf",
+ ],
+)
diff --git a/zephyr/projects/skyrim/CMakeLists.txt b/zephyr/projects/skyrim/CMakeLists.txt
index b63c8af5c5..d7a5495c32 100644
--- a/zephyr/projects/skyrim/CMakeLists.txt
+++ b/zephyr/projects/skyrim/CMakeLists.txt
@@ -5,23 +5,28 @@
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS "${ZEPHYR_BASE}")
-project(skyrim)
-cros_ec_library_include_directories_ifdef(CONFIG_BOARD_SKYRIM include)
-
-zephyr_library_sources_ifdef(CONFIG_BOARD_SKYRIM "common.c")
-
-zephyr_library_sources_ifdef(CONFIG_BOARD_SKYRIM "power_signals.c")
-
-zephyr_library_sources_ifdef(CONFIG_BOARD_SKYRIM "usbc_config.c")
+zephyr_library_sources("common.c")
+zephyr_library_sources("power_signals.c")
zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_USBC
"usb_pd_policy.c")
-
zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_LED_COMMON
"led.c")
-
zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_AMD_STT
"stt.c")
-
zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_FAN "fan.c")
+
+if(DEFINED CONFIG_BOARD_SKYRIM)
+ project(skyrim)
+ cros_ec_library_include_directories_ifdef(CONFIG_BOARD_SKYRIM include)
+ zephyr_library_sources_ifdef(CONFIG_BOARD_SKYRIM "usbc_config.c")
+endif()
+
+
+if(DEFINED CONFIG_BOARD_WINTERHOLD)
+ project(winterhold)
+ zephyr_library_sources(
+ "winterhold/src/usbc_config.c"
+ )
+endif()
diff --git a/zephyr/projects/skyrim/Kconfig b/zephyr/projects/skyrim/Kconfig
index 0573c3c73a..d5612d91d5 100644
--- a/zephyr/projects/skyrim/Kconfig
+++ b/zephyr/projects/skyrim/Kconfig
@@ -2,14 +2,20 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-module = SKYRIM
-module-str = Skyrim board-specific code
-source "subsys/logging/Kconfig.template.log_config"
-
config BOARD_SKYRIM
bool "Google Skyrim Board"
help
Build Google Skyrim reference board. This board uses an AMD SoC
and NPCX9 EC
+config BOARD_WINTERHOLD
+ bool "Google Winterhold Board"
+ help
+ Build Google Winterhold reference board. This board uses an AMD SoC
+ and NPCX9 EC
+
+module = SKYRIM
+module-str = Skyrim board-specific code
+source "subsys/logging/Kconfig.template.log_config"
+
source "Kconfig.zephyr"
diff --git a/zephyr/projects/skyrim/battery.dts b/zephyr/projects/skyrim/battery_skyrim.dts
index b9fc0d1090..b9fc0d1090 100644
--- a/zephyr/projects/skyrim/battery.dts
+++ b/zephyr/projects/skyrim/battery_skyrim.dts
diff --git a/zephyr/projects/skyrim/battery_winterhold.dts b/zephyr/projects/skyrim/battery_winterhold.dts
new file mode 100644
index 0000000000..b9fc0d1090
--- /dev/null
+++ b/zephyr/projects/skyrim/battery_winterhold.dts
@@ -0,0 +1,15 @@
+/* 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.
+ */
+
+/ {
+ batteries {
+ default_battery: aec_5477109 {
+ compatible = "aec,5477109", "battery-smart";
+ };
+ smp_l20m3pg1 {
+ compatible = "smp,l20m3pg1", "battery-smart";
+ };
+ };
+};
diff --git a/zephyr/projects/skyrim/led_pins_winterhold.dts b/zephyr/projects/skyrim/led_pins_winterhold.dts
new file mode 100644
index 0000000000..1c6bf60375
--- /dev/null
+++ b/zephyr/projects/skyrim/led_pins_winterhold.dts
@@ -0,0 +1,63 @@
+/* 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.
+ */
+
+/ {
+ pwm_pins {
+ compatible = "cros-ec,pwm-pin-config";
+
+ pwm_y: pwm_y {
+ #led-pin-cells = <1>;
+ pwms = <&pwm2 0 PWM_HZ(100) PWM_POLARITY_INVERTED>;
+ };
+
+ pwm_w: pwm_w {
+ #led-pin-cells = <1>;
+ pwms = <&pwm3 0 PWM_HZ(100) PWM_POLARITY_INVERTED>;
+ };
+ };
+
+ pwm-led-pins {
+ compatible = "cros-ec,pwm-led-pins";
+
+ color_off: color-off {
+ led-color = "LED_OFF";
+ led-id = "EC_LED_ID_BATTERY_LED";
+ led-pins = <&pwm_y 0>,
+ <&pwm_w 0>;
+ };
+
+ color_amber: color-amber {
+ led-color = "LED_AMBER";
+ led-id = "EC_LED_ID_BATTERY_LED";
+ br-color = "EC_LED_COLOR_AMBER";
+ led-pins = <&pwm_y 100>,
+ <&pwm_w 0>;
+ };
+
+ color_white: color-white {
+ led-color = "LED_WHITE";
+ led-id = "EC_LED_ID_BATTERY_LED";
+ br-color = "EC_LED_COLOR_BLUE";
+ led-pins = <&pwm_y 0>,
+ <&pwm_w 100>;
+ };
+ };
+};
+
+/* Amber "battery charging" LED */
+&pwm2 {
+ status = "okay";
+ clock-bus = "NPCX_CLOCK_BUS_LFCLK";
+ pinctrl-0 = <&pwm2_gpc4>;
+ pinctrl-names = "default";
+};
+
+/* White "battery full" LED */
+&pwm3 {
+ status = "okay";
+ clock-bus = "NPCX_CLOCK_BUS_LFCLK";
+ pinctrl-0 = <&pwm3_gp80>;
+ pinctrl-names = "default";
+};
diff --git a/zephyr/projects/skyrim/led_policy_winterhold.dts b/zephyr/projects/skyrim/led_policy_winterhold.dts
new file mode 100644
index 0000000000..a075c6b0d2
--- /dev/null
+++ b/zephyr/projects/skyrim/led_policy_winterhold.dts
@@ -0,0 +1,103 @@
+#include <dt-bindings/battery.h>
+
+/ {
+ led-colors {
+ compatible = "cros-ec,led-policy";
+
+ power-state-charge {
+ charge-state = "PWR_STATE_CHARGE";
+
+ color-0 {
+ led-color = <&color_amber>;
+ };
+ };
+
+ power-state-discharge-s0 {
+ charge-state = "PWR_STATE_DISCHARGE";
+ chipset-state = "POWER_S0";
+ /* Battery percent range (> Low, <= Full) */
+ batt-lvl = <(BATTERY_LEVEL_LOW + 1) BATTERY_LEVEL_FULL>;
+
+ color-0 {
+ led-color = <&color_white>;
+ };
+ };
+
+ power-state-discharge-s0-batt-low {
+ charge-state = "PWR_STATE_DISCHARGE";
+ chipset-state = "POWER_S0";
+ /* Battery percent range (>= Empty, <= Low) */
+ batt-lvl = <BATTERY_LEVEL_EMPTY BATTERY_LEVEL_LOW>;
+
+ /* White 2 sec, off 1 sec */
+ color-0 {
+ led-color = <&color_white>;
+ period-ms = <2000>;
+ };
+ color-1 {
+ led-color = <&color_off>;
+ period-ms = <1000>;
+ };
+ };
+
+ power-state-discharge-s3 {
+ charge-state = "PWR_STATE_DISCHARGE";
+ chipset-state = "POWER_S3";
+
+ /* White 1 sec, off 1 sec */
+ color-0 {
+ led-color = <&color_white>;
+ period-ms = <1000>;
+ };
+ color-1 {
+ led-color = <&color_off>;
+ period-ms = <1000>;
+ };
+ };
+
+ power-state-discharge-s5 {
+ charge-state = "PWR_STATE_DISCHARGE";
+ chipset-state = "POWER_S5";
+
+ color-0 {
+ led-color = <&color_off>;
+ };
+ };
+
+ power-state-error {
+ charge-state = "PWR_STATE_ERROR";
+
+ /* Amber 1 sec, off 1 sec */
+ color-0 {
+ led-color = <&color_amber>;
+ period-ms = <1000>;
+ };
+ color-1 {
+ led-color = <&color_off>;
+ period-ms = <1000>;
+ };
+ };
+
+ power-state-near-full {
+ charge-state = "PWR_STATE_CHARGE_NEAR_FULL";
+
+ color-0 {
+ led-color = <&color_white>;
+ };
+ };
+
+ power-state-forced-idle {
+ charge-state = "PWR_STATE_FORCED_IDLE";
+
+ /* Amber 2 sec, White 2 sec */
+ color-0 {
+ led-color = <&color_amber>;
+ period-ms = <2000>;
+ };
+ color-1 {
+ led-color = <&color_white>;
+ period-ms = <2000>;
+ };
+ };
+ };
+};
diff --git a/zephyr/projects/skyrim/prj_winterhold.conf b/zephyr/projects/skyrim/prj_winterhold.conf
index f10e7e7401..d0808a3767 100644
--- a/zephyr/projects/skyrim/prj_winterhold.conf
+++ b/zephyr/projects/skyrim/prj_winterhold.conf
@@ -2,8 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# Skyrim reference-board-specific Kconfig settings.
-CONFIG_BOARD_SKYRIM=y
+# Winterhold reference-board-specific Kconfig settings.
+CONFIG_BOARD_WINTERHOLD=y
# TODO(b/215404321): Remove later in board development
CONFIG_PLATFORM_EC_EEPROM_CBI_WP=y
@@ -12,7 +12,7 @@ CONFIG_PLATFORM_EC_SYSTEM_UNLOCKED=y
# LED
CONFIG_PLATFORM_EC_LED_DT=y
-# Only Skyrim has the PCT2075
+# Only Winterhold has the PCT2075
CONFIG_PLATFORM_EC_TEMP_SENSOR_PCT2075=y
CONFIG_PLATFORM_EC_USBC_RETIMER_ANX7483=y
diff --git a/zephyr/projects/skyrim/usbc_config.c b/zephyr/projects/skyrim/usbc_config.c
index 873cbdc258..b3b26f653d 100644
--- a/zephyr/projects/skyrim/usbc_config.c
+++ b/zephyr/projects/skyrim/usbc_config.c
@@ -1,4 +1,4 @@
-/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+/* Copyright 2021 The ChromiumOS Authors.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
diff --git a/zephyr/projects/skyrim/winterhold.dts b/zephyr/projects/skyrim/winterhold.dts
index fa5b1cc5a5..7998be6c22 100644
--- a/zephyr/projects/skyrim/winterhold.dts
+++ b/zephyr/projects/skyrim/winterhold.dts
@@ -11,7 +11,7 @@
};
named-gpios {
- /* Skyrim-specific GPIO customizations */
+ /* Winterhold-specific GPIO customizations */
gpio_usb_fault_odl: usb_fault_odl {
gpios = <&gpio5 0 (GPIO_ODR_HIGH | GPIO_VOLTAGE_1P8)>;
};
@@ -140,7 +140,7 @@
* Note this is expected to vary per-board, so we keep it in the board
* dts files.
*/
- skyrim-fw-config {
+ Winterhold-fw-config {
compatible = "cros-ec,cbi-fw-config";
form-factor {
diff --git a/zephyr/projects/skyrim/winterhold/src/usbc_config.c b/zephyr/projects/skyrim/winterhold/src/usbc_config.c
new file mode 100644
index 0000000000..e99fcf8aa2
--- /dev/null
+++ b/zephyr/projects/skyrim/winterhold/src/usbc_config.c
@@ -0,0 +1,631 @@
+/* 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.
+ */
+
+/* winterhold-specific USB-C configuration */
+
+#include <zephyr/drivers/gpio.h>
+
+#include "cros_board_info.h"
+#include "cros_cbi.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/anx7483_public.h"
+#include "driver/retimer/ps8811.h"
+#include "driver/retimer/ps8818.h"
+#include "driver/tcpm/nct38xx.h"
+#include "driver/usb_mux/amd_fp6.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));
+
+ /* Enable SBU fault interrupts */
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_c0_sbu_fault));
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_c1_sbu_fault));
+}
+DECLARE_HOOK(HOOK_INIT, usbc_interrupt_init, HOOK_PRIO_POST_I2C);
+
+static void usb_fault_interrupt_init(void)
+{
+ /* Enable USB fault interrupts when we hit S5 */
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_hub_fault));
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_a0_fault));
+ gpio_enable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_a1_fault));
+}
+DECLARE_HOOK(HOOK_CHIPSET_STARTUP, usb_fault_interrupt_init, HOOK_PRIO_DEFAULT);
+
+static void usb_fault_interrupt_disable(void)
+{
+ /* Disable USB fault interrupts leaving S5 */
+ gpio_disable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_hub_fault));
+ gpio_disable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_a0_fault));
+ gpio_disable_dt_interrupt(GPIO_INT_FROM_NODELABEL(int_usb_a1_fault));
+}
+DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, usb_fault_interrupt_disable,
+ HOOK_PRIO_DEFAULT);
+
+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 ioex_set_flip(const struct usb_mux *, mux_state_t, bool *);
+struct usb_mux_driver ioex_sbu_mux_driver = {
+ .set = ioex_set_flip,
+};
+
+/*
+ * Since NX3DV221GM 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 = &ioex_sbu_mux_driver,
+};
+
+struct usb_mux usbc1_sbu_mux = {
+ .usb_port = USBC_PORT_C1,
+ .driver = &ioex_sbu_mux_driver,
+};
+
+int baseboard_anx7483_c0_mux_set(const struct usb_mux *me,
+ mux_state_t mux_state)
+{
+ return anx7483_set_default_tuning(me, mux_state);
+}
+
+int baseboard_anx7483_c1_mux_set(const struct usb_mux *me,
+ mux_state_t mux_state)
+{
+ bool flipped = mux_state & USB_PD_MUX_POLARITY_INVERTED;
+
+ /* Remove flipped from the state for easier compraisons */
+ mux_state = mux_state & ~USB_PD_MUX_POLARITY_INVERTED;
+
+ RETURN_ERROR(anx7483_set_default_tuning(me, mux_state));
+
+ if (mux_state == USB_PD_MUX_USB_ENABLED) {
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_URX1,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_URX2,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_DRX1,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_DRX2,
+ ANX7483_EQ_SETTING_12_5DB));
+ } else if (mux_state == USB_PD_MUX_DP_ENABLED) {
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_URX1,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_URX2,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_UTX1,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_UTX2,
+ ANX7483_EQ_SETTING_12_5DB));
+ } else if (mux_state == USB_PD_MUX_DOCK && !flipped) {
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_URX1,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_URX2,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_DRX1,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_UTX2,
+ ANX7483_EQ_SETTING_12_5DB));
+ } else if (mux_state == USB_PD_MUX_DOCK && flipped) {
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_URX1,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_URX2,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_UTX1,
+ ANX7483_EQ_SETTING_12_5DB));
+ RETURN_ERROR(anx7483_set_eq(me, ANX7483_PIN_DRX2,
+ ANX7483_EQ_SETTING_12_5DB));
+ }
+
+ return EC_SUCCESS;
+}
+
+struct usb_mux usbc0_anx7483 = {
+ .usb_port = USBC_PORT_C0,
+ .i2c_port = I2C_PORT_TCPC0,
+ .i2c_addr_flags = ANX7483_I2C_ADDR0_FLAGS,
+ .driver = &anx7483_usb_retimer_driver,
+ .board_set = &baseboard_anx7483_c0_mux_set,
+ .next_mux = &usbc0_sbu_mux,
+};
+
+__overridable int board_c1_ps8818_mux_set(const struct usb_mux *me,
+ mux_state_t mux_state)
+{
+ CPRINTSUSB("C1: PS8818 mux using default tuning");
+
+ /* Once a DP connection is established, we need to set IN_HPD */
+ if (mux_state & USB_PD_MUX_DP_ENABLED)
+ ioex_set_level(IOEX_USB_C1_HPD_IN_DB, 1);
+ else
+ ioex_set_level(IOEX_USB_C1_HPD_IN_DB, 0);
+
+ 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,
+};
+
+struct usb_mux usbc1_anx7483 = {
+ .usb_port = USBC_PORT_C1,
+ .i2c_port = I2C_PORT_TCPC1,
+ .i2c_addr_flags = ANX7483_I2C_ADDR0_FLAGS,
+ .driver = &anx7483_usb_retimer_driver,
+ .board_set = &baseboard_anx7483_c1_mux_set,
+ .next_mux = &usbc1_sbu_mux,
+};
+
+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_anx7483,
+ },
+ [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 (general) and C1 (just ANX DB) use IOEX pins to
+ * indicate flipped polarity to a protection switch.
+ */
+static int ioex_set_flip(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 (me->usb_port == USBC_PORT_C0) {
+ 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);
+ } else {
+ if (mux_state & USB_PD_MUX_POLARITY_INVERTED)
+ ioex_set_level(IOEX_USB_C1_SBU_FLIP, 1);
+ else
+ ioex_set_level(IOEX_USB_C1_SBU_FLIP, 0);
+ }
+
+ return EC_SUCCESS;
+}
+
+static void setup_mux(void)
+{
+ uint32_t val;
+
+ if (cros_cbi_get_fw_config(FW_IO_DB, &val) != 0)
+ CPRINTSUSB("Error finding FW_DB_IO in CBI FW_CONFIG");
+ /* Val will have our dts default on error, so continue setup */
+
+ if (val == FW_IO_DB_PS8811_PS8818) {
+ CPRINTSUSB("C1: Setting PS8818 mux");
+ usb_muxes[USBC_PORT_C1].next_mux = &usbc1_ps8818;
+ } else if (val == FW_IO_DB_NONE_ANX7483) {
+ CPRINTSUSB("C1: Setting ANX7483 mux");
+ usb_muxes[USBC_PORT_C1].next_mux = &usbc1_anx7483;
+ } else {
+ CPRINTSUSB("Unexpected DB_IO board: %d", val);
+ }
+}
+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);
+}
+
+void sbu_fault_interrupt(enum gpio_signal signal)
+{
+ int port = signal == IOEX_USB_C1_FAULT_ODL ? 1 : 0;
+
+ CPRINTSUSB("C%d: SBU fault", port);
+ pd_handle_overcurrent(port);
+}
+
+void usb_fault_interrupt(enum gpio_signal signal)
+{
+ int out;
+
+ CPRINTSUSB("USB fault(%d), alerting the SoC", signal);
+ out = gpio_pin_get_dt(
+ GPIO_DT_FROM_NODELABEL(gpio_usb_hub_fault_q_odl)) &&
+ gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(ioex_usb_a0_fault_odl)) &&
+ gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(ioex_usb_a1_fault_db_odl));
+
+ gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(gpio_usb_fault_odl), out);
+}
+
+void usb_pd_soc_interrupt(enum gpio_signal signal)
+{
+ /*
+ * This interrupt is unexpected with our use of the SoC mux, so just log
+ * it as a point of interest.
+ */
+ CPRINTSUSB("SOC PD Interrupt");
+}
+
+/* Round up 3250 max current to multiple of 128mA for ISL9241 AC prochot. */
+#define WINTERHOLD_AC_PROCHOT_CURRENT_MA 3328
+static void set_ac_prochot(void)
+{
+ isl9241_set_ac_prochot(CHARGER_SOLO, WINTERHOLD_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;
+ const struct device *ioex_port0, *ioex_port1;
+
+ /* TODO(b/225189538): Save and restore ioex signals */
+ if (port == USBC_PORT_C0) {
+ reset_gpio_l = GPIO_DT_FROM_NODELABEL(gpio_usb_c0_tcpc_rst_l);
+ ioex_port0 = DEVICE_DT_GET(DT_NODELABEL(ioex_c0_port0));
+ ioex_port1 = DEVICE_DT_GET(DT_NODELABEL(ioex_c0_port1));
+ } else if (port == USBC_PORT_C1) {
+ reset_gpio_l = GPIO_DT_FROM_NODELABEL(gpio_usb_c1_tcpc_rst_l);
+ ioex_port0 = DEVICE_DT_GET(DT_NODELABEL(ioex_c1_port0));
+ ioex_port1 = DEVICE_DT_GET(DT_NODELABEL(ioex_c1_port1));
+ } 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);
+
+ /* Re-enable the IO expander pins */
+ gpio_reset_port(ioex_port0);
+ gpio_reset_port(ioex_port1);
+}
+
+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");
+}