diff options
author | Sam Hurst <shurst@google.com> | 2019-04-18 12:47:52 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-07-30 21:25:24 +0000 |
commit | 2e3109ec6b964776e7c5bd12f1fc058ca96d33f7 (patch) | |
tree | 23ea5eaf67e50094ae683f5d16f09a332a1303c4 /common/usbc/usbc_task.c | |
parent | b99b1b10c3f2070e3fbd8d5cb2d0927f04f642ea (diff) | |
download | chrome-ec-2e3109ec6b964776e7c5bd12f1fc058ca96d33f7.tar.gz |
type-c: USB Type-C State Machine based on Release 1.4 of the spec.
Implements DRP with Accessory, and Try.SRC as detailed in Release
1.4 of the USB Type-C specification.
BUG=b:130895206
BRANCH=none
TEST=manual
Used Atlas device to verify that it could be charged from PD and
none PD charges at 5V/3A. Attached USB dock and verifed access
to USB Thumb drive.
Performed same tests on Hatch
Port 0 on Hatch was used to run this CL, merged with PD functionality,
on the PD2.0 Compliance tester. All tests pass except for a few
physical layer tests. The test report has been added to the bug.
Change-Id: Ic4869e20e5b4c2ba6c827d92e40c70f3140f2518
Signed-off-by: Sam Hurst <shurst@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1574667
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
Tested-by: Sam Hurst <shurst@google.com>
Commit-Queue: Sam Hurst <shurst@google.com>
Diffstat (limited to 'common/usbc/usbc_task.c')
-rw-r--r-- | common/usbc/usbc_task.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/common/usbc/usbc_task.c b/common/usbc/usbc_task.c new file mode 100644 index 0000000000..7ff8bbf44f --- /dev/null +++ b/common/usbc/usbc_task.c @@ -0,0 +1,298 @@ +/* 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" + +/* Include USB Type-C State Machine Header File */ +#if defined(CONFIG_USB_TYPEC_CTVPD) +#include "usb_tc_ctvpd_sm.h" +#elif defined(CONFIG_USB_TYPEC_VPD) +#include "usb_tc_vpd_sm.h" +#elif defined(CONFIG_USB_TYPEC_DRP_ACC_TRYSRC) +#include "usb_tc_drp_acc_trysrc_sm.h" +#else +#error "A USB Type-C State Machine must be defined." +#endif + +#ifdef CONFIG_COMMON_RUNTIME +#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) +#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) +#else /* CONFIG_COMMON_RUNTIME */ +#define CPRINTF(format, args...) +#define CPRINTS(format, args...) +#endif + +#ifdef CONFIG_COMMON_RUNTIME +const char * const tc_state_names[] = { + "Disabled", + "Unattached.SNK", + "AttachWait.SNK", + "Attached.SNK", +#if !defined(CONFIG_USB_TYPEC_VPD) + "ErrorRecovery", + "Unattached.SRC", + "AttachWait.SRC", + "Attached.SRC", +#endif +#if !defined(CONFIG_USB_TYPEC_CTVPD) && !defined(CONFIG_USB_TYPEC_VPD) + "AudioAccessory", + "OrientedDebugAccessory.SRC", + "UnorientedDebugAccessory.SRC", + "DebugAccessory.SNK", + "Try.SRC", + "TryWait.SNK", + "CTUnattached.SNK", + "CTAttached.SNK", +#endif +#if defined(CONFIG_USB_TYPEC_CTVPD) + "CTTry.SNK", + "CTAttached.Unsupported", + "CTAttachWait.Unsupported", + "CTUnattached.Unsupported", + "CTUnattached.VPD", + "CTAttachWait.VPD", + "CTAttached.VPD", + "CTDisabled.VPD", + "Try.SNK", + "TryWait.SRC" +#endif +}; +BUILD_ASSERT(ARRAY_SIZE(tc_state_names) == TC_STATE_COUNT); +#endif + +/* Public Functions */ + +int tc_get_power_role(int port) +{ + return tc[port].power_role; +} + +int tc_get_data_role(int port) +{ + return tc[port].data_role; +} + +void tc_set_power_role(int port, int role) +{ + tc[port].power_role = role; +} + +void tc_set_timeout(int port, uint64_t timeout) +{ + tc[port].evt_timeout = timeout; +} + +enum typec_state_id get_typec_state_id(int port) +{ + return tc[port].state_id; +} + +/* + * CC values for regular sources and Debug sources (aka DTS) + * + * Source type Mode of Operation CC1 CC2 + * --------------------------------------------- + * Regular Default USB Power RpUSB Open + * Regular USB-C @ 1.5 A Rp1A5 Open + * Regular USB-C @ 3 A Rp3A0 Open + * DTS Default USB Power Rp3A0 Rp1A5 + * DTS USB-C @ 1.5 A Rp1A5 RpUSB + * DTS USB-C @ 3 A Rp3A0 RpUSB + */ + +inline enum pd_cc_polarity_type get_snk_polarity(int cc1, int cc2) +{ + /* the following assumes: + * TYPEC_CC_VOLT_RP_3_0 > TYPEC_CC_VOLT_RP_1_5 + * TYPEC_CC_VOLT_RP_1_5 > TYPEC_CC_VOLT_RP_DEF + * TYPEC_CC_VOLT_RP_DEF > TYPEC_CC_VOLT_OPEN + */ + return (cc2 > cc1) ? POLARITY_CC2 : POLARITY_CC1; +} + +int tc_restart_tcpc(int port) +{ + return tcpm_init(port); +} + +void set_polarity(int port, int polarity) +{ + tcpm_set_polarity(port, polarity); + + if (IS_ENABLED(CONFIG_USBC_PPC_POLARITY)) + ppc_set_polarity(port, polarity); +} + +void set_usb_mux_with_current_data_role(int port) +{ +#ifdef CONFIG_USBC_SS_MUX + /* + * If the SoC is down, then we disconnect the MUX to save power since + * no one cares about the data lines. + */ +#ifdef CONFIG_POWER_COMMON + if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) { + usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, + tc[port].polarity); + return; + } +#endif /* CONFIG_POWER_COMMON */ + + /* + * When PD stack is disconnected, then mux should be disconnected, which + * is also what happens in the set_state disconnection code. Once the + * PD state machine progresses out of disconnect, the MUX state will + * be set correctly again. + */ + if (!pd_is_connected(port)) + usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, + tc[port].polarity); + /* + * If new data role isn't DFP and we only support DFP, also disconnect. + */ + else if (IS_ENABLED(CONFIG_USBC_SS_MUX_DFP_ONLY) && + tc[port].data_role != PD_ROLE_DFP) + usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT, + tc[port].polarity); + /* + * Otherwise connect mux since we are in S3+ + */ + else + usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT, + tc[port].polarity); +#endif /* CONFIG_USBC_SS_MUX */ +} + +/* High-priority interrupt tasks implementations */ +#if defined(HAS_TASK_PD_INT_C0) || defined(HAS_TASK_PD_INT_C1) || \ + defined(HAS_TASK_PD_INT_C2) + +/* Used to conditionally compile code in main pd task. */ +#define HAS_DEFFERED_INTERRUPT_HANDLER + +/* Events for pd_interrupt_handler_task */ +#define PD_PROCESS_INTERRUPT (1<<0) + +static uint8_t pd_int_task_id[CONFIG_USB_PD_PORT_COUNT]; + +void schedule_deferred_pd_interrupt(const int port) +{ + task_set_event(pd_int_task_id[port], PD_PROCESS_INTERRUPT, 0); +} + +/* + * Main task entry point that handles PD interrupts for a single port + * + * @param p The PD port number for which to handle interrupts (pointer is + * reinterpreted as an integer directly). + */ +void pd_interrupt_handler_task(void *p) +{ + const int port = (int) p; + const int port_mask = (PD_STATUS_TCPC_ALERT_0 << port); + + ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_COUNT); + + pd_int_task_id[port] = task_get_current(); + + while (1) { + const int evt = task_wait_event(-1); + + if (evt & PD_PROCESS_INTERRUPT) { + /* + * While the interrupt signal is asserted; we have more + * work to do. This effectively makes the interrupt a + * level-interrupt instead of an edge-interrupt without + * having to enable/disable a real level-interrupt in + * multiple locations. + * + * Also, if the port is disabled do not process + * interrupts. Upon existing suspend, we schedule a + * PD_PROCESS_INTERRUPT to check if we missed anything. + */ + while ((tcpc_get_alert_status() & port_mask) && + pd_is_port_enabled(port)) + tcpc_alert(port); + } + } +} +#endif /* HAS_TASK_PD_INT_C0 || HAS_TASK_PD_INT_C1 || HAS_TASK_PD_INT_C2 */ + +void pd_task(void *u) +{ + int port = TASK_ID_TO_PD_PORT(task_get_current()); + + tc_state_init(port, TC_DEFAULT_STATE(port)); + + if (IS_ENABLED(CONFIG_USBC_PPC)) + ppc_init(port); + + /* + * 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(HAS_DEFFERED_INTERRUPT_HANDLER)) + schedule_deferred_pd_interrupt(port); + + + while (1) { + /* wait for next event/packet or timeout expiration */ + tc[port].evt = task_wait_event(tc[port].evt_timeout); + + /* handle events that affect the state machine as a whole */ + tc_event_check(port, tc[port].evt); + +#ifdef CONFIG_USB_PD_TCPC + /* + * run port controller task to check CC and/or read incoming + * messages + */ + tcpc_run(port, tc[port].evt); +#endif + +#ifdef CONFIG_USB_PE_SM + /* run policy engine state machine */ + usbc_policy_engine(port, tc[port].evt, tc[port].pd_enable); +#endif + +#ifdef CONFIG_USB_PRL_SM + /* run protocol state machine */ + usbc_protocol_layer(port, tc[port].evt, tc[port].pd_enable); +#endif + + /* run typec state machine */ + sm_run_state_machine(port, TC_OBJ(port), SM_RUN_SIG); + } +} |