diff options
Diffstat (limited to 'zephyr/projects/nissa/yaviks/src')
-rw-r--r-- | zephyr/projects/nissa/yaviks/src/charger.c | 56 | ||||
-rw-r--r-- | zephyr/projects/nissa/yaviks/src/hdmi.c | 28 | ||||
-rw-r--r-- | zephyr/projects/nissa/yaviks/src/keyboard.c | 50 | ||||
-rw-r--r-- | zephyr/projects/nissa/yaviks/src/led.c | 231 | ||||
-rw-r--r-- | zephyr/projects/nissa/yaviks/src/usbc.c | 393 |
5 files changed, 758 insertions, 0 deletions
diff --git a/zephyr/projects/nissa/yaviks/src/charger.c b/zephyr/projects/nissa/yaviks/src/charger.c new file mode 100644 index 0000000000..786bb1bd3c --- /dev/null +++ b/zephyr/projects/nissa/yaviks/src/charger.c @@ -0,0 +1,56 @@ +/* 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 <zephyr/logging/log.h> + +#include "battery.h" +#include "charger.h" +#include "console.h" +#include "driver/charger/sm5803.h" +#include "extpower.h" +#include "usb_pd.h" +#include "nissa_common.h" + +LOG_MODULE_DECLARE(nissa, CONFIG_NISSA_LOG_LEVEL); + +int extpower_is_present(void) +{ + int port; + int rv; + bool acok; + + for (port = 0; port < board_get_usb_pd_port_count(); port++) { + rv = sm5803_is_acok(port, &acok); + if ((rv == EC_SUCCESS) && acok) + return 1; + } + + return 0; +} + +/* + * Yaviks does not have a GPIO indicating whether extpower is present, + * so detect using the charger(s). + */ +__override void board_check_extpower(void) +{ + static int last_extpower_present; + int extpower_present = extpower_is_present(); + + if (last_extpower_present ^ extpower_present) + extpower_handle_update(extpower_present); + + last_extpower_present = extpower_present; +} + +__override void board_hibernate(void) +{ + /* Shut down the chargers */ + if (board_get_usb_pd_port_count() == 2) + sm5803_hibernate(CHARGER_SECONDARY); + sm5803_hibernate(CHARGER_PRIMARY); + LOG_INF("Charger(s) hibernated"); + cflush(); +} diff --git a/zephyr/projects/nissa/yaviks/src/hdmi.c b/zephyr/projects/nissa/yaviks/src/hdmi.c new file mode 100644 index 0000000000..d15e9fb034 --- /dev/null +++ b/zephyr/projects/nissa/yaviks/src/hdmi.c @@ -0,0 +1,28 @@ +/* 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 <cros_board_info.h> +#include "nissa_hdmi.h" + +__override void nissa_configure_hdmi_power_gpios(void) +{ + /* + * Yaviks versions before 2 need hdmi-en-odl to be + * pulled down to enable VCC on the HDMI port, but later + * versions (and other boards) disconnect this so + * the port's VCC directly follows en-rails-odl. Only + * configure the GPIO if needed, to save power. + */ + uint32_t board_version = 0; + + /* CBI errors ignored, will configure the pin */ + cbi_get_board_version(&board_version); + if (board_version < 2) { + nissa_configure_hdmi_vcc(); + } + + /* Still always need core rails controlled */ + nissa_configure_hdmi_rails(); +} diff --git a/zephyr/projects/nissa/yaviks/src/keyboard.c b/zephyr/projects/nissa/yaviks/src/keyboard.c new file mode 100644 index 0000000000..1e5ac7a953 --- /dev/null +++ b/zephyr/projects/nissa/yaviks/src/keyboard.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. + */ + +#include "ec_commands.h" +#include "keyboard_scan.h" +#include "timer.h" + +/* Keyboard scan setting */ +__override struct keyboard_scan_config keyscan_config = { + /* Increase from 50 us, because KSO_02 passes through the H1. */ + .output_settle_us = 80, + /* Other values should be the same as the default configuration. */ + .debounce_down_us = 9 * MSEC, + .debounce_up_us = 30 * MSEC, + .scan_period_us = 3 * MSEC, + .min_post_scan_delay_us = 1000, + .poll_timeout_us = 100 * MSEC, + .actual_key_mask = { + 0x1c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa4, 0xff, 0xf6, 0x55, 0xfe, 0xff, 0xff, 0xff, /* full set */ + }, +}; + +static const struct ec_response_keybd_config yaviks_kb_legacy = { + .num_top_row_keys = 13, + .action_keys = { + TK_BACK, /* T1 */ + TK_REFRESH, /* T2 */ + TK_FULLSCREEN, /* T3 */ + TK_OVERVIEW, /* T4 */ + TK_SNAPSHOT, /* T5 */ + TK_BRIGHTNESS_DOWN, /* T6 */ + TK_BRIGHTNESS_UP, /* T7 */ + TK_KBD_BKLIGHT_TOGGLE, /* T8 */ + TK_PLAY_PAUSE, /* T9 */ + TK_MICMUTE, /* T10 */ + TK_VOL_MUTE, /* T11 */ + TK_VOL_DOWN, /* T12 */ + TK_VOL_UP, /* T13 */ + }, + .capabilities = KEYBD_CAP_NUMERIC_KEYPAD, +}; + +__override const struct ec_response_keybd_config * +board_vivaldi_keybd_config(void) +{ + return &yaviks_kb_legacy; +} diff --git a/zephyr/projects/nissa/yaviks/src/led.c b/zephyr/projects/nissa/yaviks/src/led.c new file mode 100644 index 0000000000..e4f49dc9ad --- /dev/null +++ b/zephyr/projects/nissa/yaviks/src/led.c @@ -0,0 +1,231 @@ +/* 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 <stdint.h> + +#include "battery.h" +#include "charge_manager.h" +#include "charge_state.h" +#include "chipset.h" +#include "ec_commands.h" +#include "gpio.h" +#include "host_command.h" +#include "led_common.h" +#include "hooks.h" + +#define BAT_LED_ON 0 +#define BAT_LED_OFF 1 + +#define BATT_LOW_BCT 10 + +#define LED_TICKS_PER_CYCLE 4 +#define LED_TICKS_PER_CYCLE_S3 4 +#define LED_ON_TICKS 2 +#define POWER_LED_ON_S3_TICKS 2 + +const enum ec_led_id supported_led_ids[] = { EC_LED_ID_LEFT_LED, + EC_LED_ID_RIGHT_LED }; + +const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); + +enum led_color { + LED_OFF = 0, + LED_AMBER, + LED_WHITE, + LED_COLOR_COUNT /* Number of colors, not a color itself */ +}; + +enum led_port { LEFT_PORT = 0, RIGHT_PORT }; + +static void led_set_color_battery(int port, enum led_color color) +{ + const struct gpio_dt_spec *amber_led, *white_led; + + if (port == LEFT_PORT) { + amber_led = GPIO_DT_FROM_NODELABEL(gpio_c0_charger_led_amber_l); + white_led = GPIO_DT_FROM_NODELABEL(gpio_c0_charger_led_white_l); + } else if (port == RIGHT_PORT) { + amber_led = GPIO_DT_FROM_NODELABEL(gpio_c1_charger_led_amber_l); + white_led = GPIO_DT_FROM_NODELABEL(gpio_c1_charger_led_white_l); + } + + switch (color) { + case LED_WHITE: + gpio_pin_set_dt(white_led, BAT_LED_ON); + gpio_pin_set_dt(amber_led, BAT_LED_OFF); + break; + case LED_AMBER: + gpio_pin_set_dt(white_led, BAT_LED_OFF); + gpio_pin_set_dt(amber_led, BAT_LED_ON); + break; + case LED_OFF: + gpio_pin_set_dt(white_led, BAT_LED_OFF); + gpio_pin_set_dt(amber_led, BAT_LED_OFF); + break; + default: + break; + } +} + +void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) +{ + switch (led_id) { + case EC_LED_ID_LEFT_LED: + brightness_range[EC_LED_COLOR_WHITE] = 1; + brightness_range[EC_LED_COLOR_AMBER] = 1; + break; + case EC_LED_ID_RIGHT_LED: + brightness_range[EC_LED_COLOR_WHITE] = 1; + brightness_range[EC_LED_COLOR_AMBER] = 1; + break; + default: + break; + } +} + +int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness) +{ + switch (led_id) { + case EC_LED_ID_LEFT_LED: + if (brightness[EC_LED_COLOR_WHITE] != 0) + led_set_color_battery(LEFT_PORT, LED_WHITE); + else if (brightness[EC_LED_COLOR_AMBER] != 0) + led_set_color_battery(LEFT_PORT, LED_AMBER); + else + led_set_color_battery(LEFT_PORT, LED_OFF); + break; + case EC_LED_ID_RIGHT_LED: + if (brightness[EC_LED_COLOR_WHITE] != 0) + led_set_color_battery(RIGHT_PORT, LED_WHITE); + else if (brightness[EC_LED_COLOR_AMBER] != 0) + led_set_color_battery(RIGHT_PORT, LED_AMBER); + else + led_set_color_battery(RIGHT_PORT, LED_OFF); + break; + default: + return EC_ERROR_PARAM1; + } + + return EC_SUCCESS; +} + +/* + * Set active charge port color to the parameter, turn off all others. + * If no port is active (-1), turn off all LEDs. + */ +static void set_active_port_color(enum led_color color) +{ + int port = charge_manager_get_active_charge_port(); + + if (led_auto_control_is_enabled(EC_LED_ID_RIGHT_LED)) + led_set_color_battery(RIGHT_PORT, + (port == RIGHT_PORT) ? color : LED_OFF); + if (led_auto_control_is_enabled(EC_LED_ID_LEFT_LED)) + led_set_color_battery(LEFT_PORT, + (port == LEFT_PORT) ? color : LED_OFF); +} + +static void led_set_battery(void) +{ + static unsigned int battery_ticks; + static int suspend_ticks; + + battery_ticks++; + + /* + * Override battery LEDs for Yaviks, Yaviks is non-power LED + * design, blinking both two side battery white LEDs to indicate + * system suspend with non-charging state. + */ + if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND) && + charge_get_state() != PWR_STATE_CHARGE) { + suspend_ticks++; + + led_set_color_battery(RIGHT_PORT, + suspend_ticks % LED_TICKS_PER_CYCLE_S3 < + POWER_LED_ON_S3_TICKS ? + LED_WHITE : + LED_OFF); + led_set_color_battery(LEFT_PORT, + suspend_ticks % LED_TICKS_PER_CYCLE_S3 < + POWER_LED_ON_S3_TICKS ? + LED_WHITE : + LED_OFF); + return; + } + + suspend_ticks = 0; + + switch (charge_get_state()) { + case PWR_STATE_CHARGE: + /* Always indicate when charging, even in suspend. */ + set_active_port_color(LED_AMBER); + break; + case PWR_STATE_DISCHARGE: + /* + * Blinking amber LEDs slowly if battery is lower 10 + * percentage. + */ + if (led_auto_control_is_enabled(EC_LED_ID_RIGHT_LED)) { + if (charge_get_percent() < BATT_LOW_BCT) + led_set_color_battery( + RIGHT_PORT, + (battery_ticks % LED_TICKS_PER_CYCLE < + LED_ON_TICKS) ? + LED_AMBER : + LED_OFF); + else + led_set_color_battery(RIGHT_PORT, LED_OFF); + } + + if (led_auto_control_is_enabled(EC_LED_ID_LEFT_LED)) { + if (charge_get_percent() < BATT_LOW_BCT) + led_set_color_battery( + LEFT_PORT, + (battery_ticks % LED_TICKS_PER_CYCLE < + LED_ON_TICKS) ? + LED_AMBER : + LED_OFF); + else + led_set_color_battery(LEFT_PORT, LED_OFF); + } + break; + case PWR_STATE_ERROR: + if (led_auto_control_is_enabled(EC_LED_ID_RIGHT_LED)) { + led_set_color_battery( + RIGHT_PORT, + (battery_ticks & 0x1) ? LED_AMBER : LED_OFF); + } + + if (led_auto_control_is_enabled(EC_LED_ID_LEFT_LED)) { + led_set_color_battery(LEFT_PORT, (battery_ticks & 0x1) ? + LED_AMBER : + LED_OFF); + } + break; + case PWR_STATE_CHARGE_NEAR_FULL: + set_active_port_color(LED_WHITE); + break; + case PWR_STATE_IDLE: /* External power connected in IDLE */ + set_active_port_color(LED_WHITE); + break; + case PWR_STATE_FORCED_IDLE: + set_active_port_color( + (battery_ticks % LED_TICKS_PER_CYCLE < LED_ON_TICKS) ? + LED_AMBER : + LED_OFF); + break; + default: + /* Other states don't alter LED behavior */ + break; + } +} + +/* Called by hook task every TICK(IT83xx 500ms) */ +static void led_tick(void) +{ + led_set_battery(); +} +DECLARE_HOOK(HOOK_TICK, led_tick, HOOK_PRIO_DEFAULT); diff --git a/zephyr/projects/nissa/yaviks/src/usbc.c b/zephyr/projects/nissa/yaviks/src/usbc.c new file mode 100644 index 0000000000..48f7cfd9cb --- /dev/null +++ b/zephyr/projects/nissa/yaviks/src/usbc.c @@ -0,0 +1,393 @@ +/* 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 <zephyr/logging/log.h> +#include <ap_power/ap_power.h> + +#include "charge_state_v2.h" +#include "chipset.h" +#include "hooks.h" +#include "usb_mux.h" +#include "system.h" +#include "driver/charger/sm5803.h" +#include "driver/tcpm/it83xx_pd.h" +#include "driver/tcpm/ps8xxx_public.h" +#include "driver/tcpm/tcpci.h" + +#include "nissa_common.h" + +LOG_MODULE_DECLARE(nissa, CONFIG_NISSA_LOG_LEVEL); + +struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_MAX_COUNT] = { + { + .bus_type = EC_BUS_TYPE_EMBEDDED, + /* TCPC is embedded within EC so no i2c config needed */ + .drv = &it8xxx2_tcpm_drv, + /* Alert is active-low, push-pull */ + .flags = 0, + }, + { + /* + * Sub-board: optional PS8745 TCPC+redriver. Behaves the same + * as PS8815. + */ + .bus_type = EC_BUS_TYPE_I2C, + .i2c_info = { + .port = I2C_PORT_USB_C1_TCPC, + .addr_flags = PS8XXX_I2C_ADDR1_FLAGS, + }, + .drv = &ps8xxx_tcpm_drv, + /* PS8745 implements TCPCI 2.0 */ + .flags = TCPC_FLAGS_TCPCI_REV2_0, + }, +}; + +/* Vconn control for integrated ITE TCPC */ +void board_pd_vconn_ctrl(int port, enum usbpd_cc_pin cc_pin, int enabled) +{ + /* Vconn control is only for port 0 */ + if (port) + return; + + if (cc_pin == USBPD_CC_PIN_1) + gpio_pin_set_dt( + GPIO_DT_FROM_NODELABEL(gpio_en_usb_c0_cc1_vconn), + !!enabled); + else + gpio_pin_set_dt( + GPIO_DT_FROM_NODELABEL(gpio_en_usb_c0_cc2_vconn), + !!enabled); +} + +__override bool pd_check_vbus_level(int port, enum vbus_level level) +{ + return sm5803_check_vbus_level(port, level); +} + +/* + * Putting chargers into LPM when in suspend reduces power draw by about 8mW + * per charger, but also seems critical to correct operation in source mode: + * if chargers are not in LPM when a sink is first connected, VBUS sourcing + * works even if the partner is later removed (causing LPM entry) and + * reconnected (causing LPM exit). If in LPM initially, sourcing VBUS + * consistently causes the charger to report (apparently spurious) overcurrent + * failures. + * + * In short, this is important to making things work correctly but we don't + * understand why. + */ +static void board_chargers_suspend(struct ap_power_ev_callback *const cb, + const struct ap_power_ev_data data) +{ + void (*fn)(int chgnum); + + switch (data.event) { + case AP_POWER_SUSPEND: + fn = sm5803_enable_low_power_mode; + break; + case AP_POWER_RESUME: + fn = sm5803_disable_low_power_mode; + break; + default: + LOG_WRN("%s: power event %d is not recognized", __func__, + data.event); + return; + } + + fn(CHARGER_PRIMARY); + if (board_get_charger_chip_count() > 1) + fn(CHARGER_SECONDARY); +} + +static int board_chargers_suspend_init(const struct device *unused) +{ + static struct ap_power_ev_callback cb = { + .handler = board_chargers_suspend, + .events = AP_POWER_SUSPEND | AP_POWER_RESUME, + }; + ap_power_ev_add_callback(&cb); + return 0; +} +SYS_INIT(board_chargers_suspend_init, APPLICATION, 0); + +int board_set_active_charge_port(int port) +{ + int is_real_port = (port >= 0 && port < board_get_usb_pd_port_count()); + int i; + int old_port; + int rv; + + if (!is_real_port && port != CHARGE_PORT_NONE) + return EC_ERROR_INVAL; + + old_port = charge_manager_get_active_charge_port(); + LOG_INF("Charge update: p%d -> p%d", old_port, port); + + /* Check if port is sourcing VBUS. */ + if (port != CHARGE_PORT_NONE && charger_is_sourcing_otg_power(port)) { + LOG_WRN("Skip enable p%d: already sourcing", port); + return EC_ERROR_INVAL; + } + + /* Disable sinking on all ports except the desired one */ + for (i = 0; i < board_get_usb_pd_port_count(); i++) { + if (i == port) + continue; + + if (sm5803_vbus_sink_enable(i, 0)) + /* + * Do not early-return because this can fail during + * power-on which would put us into a loop. + */ + LOG_WRN("p%d: sink path disable failed.", i); + } + + /* Don't enable anything (stop here) if no ports were requested */ + if ((port == CHARGE_PORT_NONE) || (old_port == port)) + return EC_SUCCESS; + + /* + * Stop the charger IC from switching while changing ports. Otherwise, + * we can overcurrent the adapter we're switching to. (crbug.com/926056) + */ + if (old_port != CHARGE_PORT_NONE) + charger_discharge_on_ac(1); + + /* Enable requested charge port. */ + rv = sm5803_vbus_sink_enable(port, 1); + if (rv) + LOG_WRN("p%d: sink path enable failed: code %d", port, rv); + + /* Allow the charger IC to begin/continue switching. */ + charger_discharge_on_ac(0); + + return rv; +} + +uint16_t tcpc_get_alert_status(void) +{ + /* + * TCPC 0 is embedded in the EC and processes interrupts in the chip + * code (it83xx/intc.c). This function only needs to poll port C1 if + * present. + */ + uint16_t status = 0; + int regval; + + /* Is the C1 port present and its IRQ line asserted? */ + if (board_get_usb_pd_port_count() == 2 && + !gpio_pin_get_dt(GPIO_DT_FROM_ALIAS(gpio_usb_c1_int_odl))) { + /* + * C1 IRQ is shared between BC1.2 and TCPC; poll TCPC to see if + * it asserted the IRQ. + */ + if (!tcpc_read16(1, TCPC_REG_ALERT, ®val)) { + if (regval) + status = PD_STATUS_TCPC_ALERT_1; + } + } + + return status; +} + +void pd_power_supply_reset(int port) +{ + int prev_en; + + if (port < 0 || port >= board_get_usb_pd_port_count()) + return; + + prev_en = charger_is_sourcing_otg_power(port); + + /* Disable Vbus */ + charger_enable_otg_power(port, 0); + + /* Discharge Vbus if previously enabled */ + if (prev_en) + sm5803_set_vbus_disch(port, 1); + + /* Notify host of power info change. */ + pd_send_host_event(PD_EVENT_POWER_CHANGE); +} + +int pd_set_power_supply_ready(int port) +{ + enum ec_error_list rv; + + if (port < 0 || port > board_get_usb_pd_port_count()) { + LOG_WRN("Port C%d does not exist, cannot enable VBUS", port); + return EC_ERROR_INVAL; + } + + /* Disable sinking */ + rv = sm5803_vbus_sink_enable(port, 0); + if (rv) { + LOG_WRN("C%d failed to disable sinking: %d", port, rv); + return rv; + } + + /* Disable Vbus discharge */ + rv = sm5803_set_vbus_disch(port, 0); + if (rv) { + LOG_WRN("C%d failed to clear VBUS discharge: %d", port, rv); + return rv; + } + + /* Provide Vbus */ + rv = charger_enable_otg_power(port, 1); + if (rv) { + LOG_WRN("C%d failed to enable VBUS sourcing: %d", port, rv); + return rv; + } + + /* Notify host of power info change. */ + pd_send_host_event(PD_EVENT_POWER_CHANGE); + + return EC_SUCCESS; +} + +__override void typec_set_source_current_limit(int port, enum tcpc_rp_value rp) +{ + int rv; + const int current = rp == TYPEC_RP_3A0 ? 3000 : 1500; + + rv = charger_set_otg_current_voltage(port, current, 5000); + if (rv != EC_SUCCESS) { + LOG_WRN("Failed to set source ilimit on port %d to %d: %d", + port, current, rv); + } +} + +void board_reset_pd_mcu(void) +{ + /* + * Do nothing. The integrated TCPC for C0 lacks a dedicated reset + * command, and C1 (if present) doesn't have a reset pin connected + * to the EC. + */ +} + +#define INT_RECHECK_US 5000 + +/* C0 interrupt line shared by BC 1.2 and charger */ + +static void check_c0_line(void); +DECLARE_DEFERRED(check_c0_line); + +static void notify_c0_chips(void) +{ + usb_charger_task_set_event(0, USB_CHG_EVENT_BC12); + sm5803_interrupt(0); +} + +static void check_c0_line(void) +{ + /* + * If line is still being held low, see if there's more to process from + * one of the chips + */ + if (!gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(gpio_usb_c0_int_odl))) { + notify_c0_chips(); + hook_call_deferred(&check_c0_line_data, INT_RECHECK_US); + } +} + +void usb_c0_interrupt(enum gpio_signal s) +{ + /* Cancel any previous calls to check the interrupt line */ + hook_call_deferred(&check_c0_line_data, -1); + + /* Notify all chips using this line that an interrupt came in */ + notify_c0_chips(); + + /* Check the line again in 5ms */ + hook_call_deferred(&check_c0_line_data, INT_RECHECK_US); +} + +/* C1 interrupt line shared by BC 1.2, TCPC, and charger */ +static void check_c1_line(void); +DECLARE_DEFERRED(check_c1_line); + +static void notify_c1_chips(void) +{ + schedule_deferred_pd_interrupt(1); + usb_charger_task_set_event(1, USB_CHG_EVENT_BC12); + /* Charger is handled in board_process_pd_alert */ +} + +static void check_c1_line(void) +{ + /* + * If line is still being held low, see if there's more to process from + * one of the chips. + */ + if (!gpio_pin_get_dt(GPIO_DT_FROM_ALIAS(gpio_usb_c1_int_odl))) { + notify_c1_chips(); + hook_call_deferred(&check_c1_line_data, INT_RECHECK_US); + } +} + +void usb_c1_interrupt(enum gpio_signal s) +{ + /* Cancel any previous calls to check the interrupt line */ + hook_call_deferred(&check_c1_line_data, -1); + + /* Notify all chips using this line that an interrupt came in */ + notify_c1_chips(); + + /* Check the line again in 5ms */ + hook_call_deferred(&check_c1_line_data, INT_RECHECK_US); +} + +/* + * Check state of IRQ lines at startup, ensuring an IRQ that happened before + * the EC started up won't get lost (leaving the IRQ line asserted and blocking + * any further interrupts on the port). + * + * Although the PD task will check for pending TCPC interrupts on startup, + * the charger sharing the IRQ will not be polled automatically. + */ +void board_handle_initial_typec_irq(void) +{ + check_c0_line(); + if (board_get_usb_pd_port_count() == 2) + check_c1_line(); +} +/* + * This must run after sub-board detection (which happens in EC main()), + * but isn't depended on by anything else either. + */ +DECLARE_HOOK(HOOK_INIT, board_handle_initial_typec_irq, HOOK_PRIO_LAST); + +/* + * Handle charger interrupts in the PD task. Not doing so can lead to a priority + * inversion where we fail to respond to TCPC alerts quickly enough because we + * don't get another edge on a shared IRQ until the charger interrupt is cleared + * (or the IRQ is polled again), which happens in the low-priority charger task: + * the high-priority type-C handler is thus blocked on the lower-priority + * charger. + * + * To avoid that, we run charger interrupts at the same priority. + */ +void board_process_pd_alert(int port) +{ + /* + * Port 0 doesn't use an external TCPC, so its interrupts don't need + * this special handling. + */ + if (port == 1 && + !gpio_pin_get_dt(GPIO_DT_FROM_ALIAS(gpio_usb_c1_int_odl))) { + sm5803_handle_interrupt(port); + } +} + +int pd_snk_is_vbus_provided(int port) +{ + int chg_det = 0; + + sm5803_get_chg_det(port, &chg_det); + + return chg_det; +} |