/* Copyright 2019 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 "battery.h" #include "battery_smart.h" #include "board.h" #include "charge_manager.h" #include "charge_state.h" #include "chipset.h" #include "common.h" #include "console.h" #include "ec_commands.h" #include "gpio.h" #include "hooks.h" #include "host_command.h" #include "registers.h" #include "system.h" #include "task.h" #include "timer.h" #include "util.h" #include "usb_charge.h" #include "usb_mux.h" #include "usb_pd.h" #include "usb_prl_sm.h" #include "tcpm.h" #include "usb_pe_sm.h" #include "usb_prl_sm.h" #include "usb_sm.h" #include "usb_tc_sm.h" #include "usbc_ppc.h" #include "version.h" #define USBC_EVENT_TIMEOUT (5 * MSEC) #define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) #define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) static uint8_t paused[CONFIG_USB_PD_PORT_MAX_COUNT]; void tc_pause_event_loop(int port) { paused[port] = 1; } void tc_start_event_loop(int port) { /* * Only generate TASK_EVENT_WAKE event if state * machine is transitioning to un-paused */ if (paused[port]) { paused[port] = 0; task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE); } } static void pd_task_init(int port) { if (IS_ENABLED(CONFIG_USB_TYPEC_SM)) tc_state_init(port); paused[port] = 0; /* * Since most boards configure the TCPC interrupt as edge * and it is possible that the interrupt line was asserted between init * and calling set_state, we need to process any pending interrupts now. * Otherwise future interrupts will never fire because another edge * never happens. Note this needs to happen after set_state() is called. */ if (IS_ENABLED(CONFIG_HAS_TASK_PD_INT)) schedule_deferred_pd_interrupt(port); } static bool pd_task_loop(int port) { /* wait for next event/packet or timeout expiration */ const uint32_t evt = task_wait_event(paused[port] ? -1 : USBC_EVENT_TIMEOUT); /* * Re-use TASK_EVENT_RESET_DONE in tests to restart the USB task * if this code is running in a unit test. */ if (IS_ENABLED(TEST_BUILD) && (evt & TASK_EVENT_RESET_DONE)) return false; /* handle events that affect the state machine as a whole */ if (IS_ENABLED(CONFIG_USB_TYPEC_SM)) tc_event_check(port, evt); /* * run port controller task to check CC and/or read incoming * messages */ if (IS_ENABLED(CONFIG_USB_PD_TCPC)) tcpc_run(port, evt); /* Run policy engine state machine */ if (IS_ENABLED(CONFIG_USB_PE_SM)) pe_run(port, evt, tc_get_pd_enabled(port)); /* Run protocol state machine */ if (IS_ENABLED(CONFIG_USB_PRL_SM) || IS_ENABLED(CONFIG_TEST_USB_PE_SM)) prl_run(port, evt, tc_get_pd_enabled(port)); /* Run TypeC state machine */ if (IS_ENABLED(CONFIG_USB_TYPEC_SM)) tc_run(port); return true; } void pd_task(void *u) { int port = TASK_ID_TO_PD_PORT(task_get_current()); /* * If port does not exist, return */ if (port >= board_get_usb_pd_port_count()) return; while (1) { pd_task_init(port); /* As long as pd_task_loop returns true, keep running the loop. * pd_task_loop returns false when the code needs to re-init * the task, so once the code breaks out of the inner while * loop, the re-init code at the top of the outer while loop * will run. */ while (pd_task_loop(port)) continue; } }