189 lines
5.1 KiB
C
189 lines
5.1 KiB
C
/* 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"
|
|
|
|
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_get_polarity(port));
|
|
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_get_polarity(port));
|
|
/*
|
|
* 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_get_data_role(port) != PD_ROLE_DFP)
|
|
usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT,
|
|
tc_get_polarity(port));
|
|
/*
|
|
* Otherwise connect mux since we are in S3+
|
|
*/
|
|
else
|
|
usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT,
|
|
tc_get_polarity(port));
|
|
#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);
|
|
|
|
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 */
|
|
const uint32_t evt = task_wait_event(tc_get_timeout(port));
|
|
|
|
/* handle events that affect the state machine as a whole */
|
|
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);
|
|
|
|
if (IS_ENABLED(CONFIG_USB_PE_SM))
|
|
/* Run policy engine state machine */
|
|
pe_run(port, evt, tc_get_pd_enabled(port));
|
|
|
|
if (IS_ENABLED(CONFIG_USB_PRL_SM))
|
|
/* Run protocol state machine */
|
|
prl_run(port, evt, tc_get_pd_enabled(port));
|
|
|
|
/* Run TypeC state machine */
|
|
tc_run(port);
|
|
}
|
|
}
|