mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-09 00:51:29 +00:00
tcpc: re-initialize tcpc if it reboots while tcpm is running
On TCPC startup, set an alert to notify TCPM that we have been reset. When TCPM gets this notification, it should re-send initial TCPC parameters. If we were in a stable contract as a sink, make sure we don't reset connection. If not, then reset PD protocol state machine to the default state. This fixes a bug where if the TCPC reboots while the TCPM is still running, then the TCPC would not get re-initialized and therefore no PD communication would not work. This also fixes it such that if we are in a stable contract as a sink and the TCPC reboots, then we don't lose power. BUG=chrome-os-partner:46676 BRANCH=none TEST=tested on glados. reboot PD MCU with and without a charger plugged in and verify that PD communication works after the reboot. verify that with a charger, we don't lose power. also tested with a hoho plugged in during reboot. Change-Id: I84fec4577b0daf5891bd8461d3f3d925014a5ecf Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/307187 Reviewed-by: Shawn N <shawnn@chromium.org>
This commit is contained in:
@@ -285,6 +285,10 @@ static inline void set_state(int port, enum pd_states next_state)
|
||||
#else /* CONFIG_USB_PD_DUAL_ROLE */
|
||||
if (next_state == PD_STATE_SRC_DISCONNECTED) {
|
||||
#endif
|
||||
/* If we are source, make sure VBUS is off */
|
||||
if (pd[port].power_role == PD_ROLE_SOURCE)
|
||||
pd_power_supply_reset(port);
|
||||
|
||||
pd[port].dev_id = 0;
|
||||
pd[port].flags &= ~PD_FLAGS_RESET_ON_DISCONNECT_MASK;
|
||||
#ifdef CONFIG_CHARGE_MANAGER
|
||||
@@ -1406,9 +1410,11 @@ void pd_task(void)
|
||||
/* Ensure the power supply is in the default state */
|
||||
pd_power_supply_reset(port);
|
||||
|
||||
#ifdef CONFIG_USB_PD_TCPC
|
||||
/* Initialize TCPM driver and wait for TCPC to be ready */
|
||||
tcpm_init(port);
|
||||
CPRINTF("[%T TCPC p%d ready]\n", port);
|
||||
#endif
|
||||
|
||||
/* Disable TCPC RX until connection is established */
|
||||
tcpm_set_rx_enable(port, 0);
|
||||
@@ -1460,6 +1466,36 @@ void pd_task(void)
|
||||
* messages
|
||||
*/
|
||||
tcpc_run(port, evt);
|
||||
#else
|
||||
/* if TCPC has reset, then need to initialize it again */
|
||||
if (evt & PD_EVENT_TCPC_RESET) {
|
||||
CPRINTF("[%T TCPC p%d reset!]\n", port);
|
||||
tcpm_init(port);
|
||||
|
||||
/* Ensure CC termination is default */
|
||||
tcpm_set_cc(port, PD_ROLE_DEFAULT == PD_ROLE_SOURCE ?
|
||||
TYPEC_CC_RP :
|
||||
TYPEC_CC_RD);
|
||||
|
||||
/*
|
||||
* If we have a stable contract in the default role,
|
||||
* then simply update TCPC with some missing info
|
||||
* so that we can continue without resetting PD comms.
|
||||
* Otherwise, go to the default disconnected state
|
||||
* and force renegotiation.
|
||||
*/
|
||||
if ((PD_ROLE_DEFAULT == PD_ROLE_SINK &&
|
||||
pd[port].task_state == PD_STATE_SNK_READY) ||
|
||||
(PD_ROLE_DEFAULT == PD_ROLE_SOURCE &&
|
||||
pd[port].task_state == PD_STATE_SRC_READY)) {
|
||||
tcpm_set_polarity(port, pd[port].polarity);
|
||||
tcpm_set_msg_header(port, pd[port].power_role,
|
||||
pd[port].data_role);
|
||||
tcpm_set_rx_enable(port, 1);
|
||||
} else {
|
||||
set_state(port, PD_DEFAULT_STATE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* process any potential incoming message */
|
||||
@@ -1538,10 +1574,6 @@ void pd_task(void)
|
||||
new_cc_state = PD_CC_AUDIO_ACC;
|
||||
} else {
|
||||
/* No UFP */
|
||||
#ifdef CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP
|
||||
/* No connection any more, remove VBUS */
|
||||
pd_power_supply_reset(port);
|
||||
#endif
|
||||
set_state(port, PD_STATE_SRC_DISCONNECTED);
|
||||
timeout = 5*MSEC;
|
||||
break;
|
||||
@@ -1761,7 +1793,6 @@ void pd_task(void)
|
||||
set_state(port, PD_STATE_SRC_READY);
|
||||
} else {
|
||||
/* The sink did not ack, cut the power... */
|
||||
pd_power_supply_reset(port);
|
||||
set_state(port, PD_STATE_SRC_DISCONNECTED);
|
||||
}
|
||||
break;
|
||||
@@ -2510,7 +2541,6 @@ void pd_task(void)
|
||||
if (pd[port].polarity)
|
||||
cc1 = cc2;
|
||||
if (cc1 == TYPEC_CC_VOLT_OPEN) {
|
||||
pd_power_supply_reset(port);
|
||||
set_state(port, PD_STATE_SRC_DISCONNECTED);
|
||||
/* Debouncing */
|
||||
timeout = 10*MSEC;
|
||||
|
||||
@@ -1069,6 +1069,13 @@ void tcpc_init(int port)
|
||||
tcpc_set_power_status(port, !gpio_get_level(GPIO_USB_C0_VBUS_WAKE_L));
|
||||
#endif /* CONFIG_USB_PD_PORT_COUNT >= 2 */
|
||||
#endif /* CONFIG_USB_PD_TCPM_VBUS */
|
||||
|
||||
/* set default alert and power mask register values */
|
||||
pd[port].alert_mask = TCPC_REG_ALERT_MASK_ALL;
|
||||
pd[port].power_status_mask = TCPC_REG_POWER_STATUS_MASK_ALL;
|
||||
|
||||
/* set power status alert since the UNINIT bit has been set */
|
||||
alert(port, TCPC_REG_ALERT_POWER_STATUS);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_TCPM_VBUS
|
||||
@@ -1201,6 +1208,9 @@ static int tcpc_i2c_read(int port, int reg, uint8_t *payload)
|
||||
case TCPC_REG_POWER_STATUS:
|
||||
payload[0] = pd[port].power_status;
|
||||
return 1;
|
||||
case TCPC_REG_POWER_STATUS_MASK:
|
||||
payload[0] = pd[port].power_status_mask;
|
||||
return 1;
|
||||
case TCPC_REG_TX_BYTE_CNT:
|
||||
payload[0] = PD_HEADER_CNT(pd[port].tx_head);
|
||||
return 1;
|
||||
@@ -1299,12 +1309,14 @@ static int command_tcpc(int argc, char **argv)
|
||||
ccprintf("set TX frequency to %d Hz\n", freq);
|
||||
return EC_SUCCESS;
|
||||
} else if (!strncasecmp(argv[2], "state", 5)) {
|
||||
ccprintf("Port C%d, %s - CC:%d, CC0:%d, CC1:%d, "
|
||||
"Alert: 0x%02x\n", port,
|
||||
ccprintf("Port C%d, %s - CC:%d, CC0:%d, CC1:%d\n"
|
||||
"Alert: 0x%02x Mask: 0x%04x\n"
|
||||
"Power Status: 0x%02x Mask: 0x%02x\n", port,
|
||||
pd[port].rx_enabled ? "Ena" : "Dis",
|
||||
pd[port].cc_pull,
|
||||
pd[port].cc_status[0], pd[port].cc_status[1],
|
||||
pd[port].alert);
|
||||
pd[port].alert, pd[port].alert_mask,
|
||||
pd[port].power_status, pd[port].power_status_mask);
|
||||
}
|
||||
|
||||
return EC_SUCCESS;
|
||||
|
||||
@@ -43,10 +43,21 @@ static int init_alert_mask(int port)
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int init_power_status_mask(int port)
|
||||
{
|
||||
return tcpm_set_power_status_mask(port, 0);
|
||||
}
|
||||
|
||||
int tcpm_init(int port)
|
||||
{
|
||||
int rv;
|
||||
|
||||
tcpc_init(port);
|
||||
return init_alert_mask(port);
|
||||
rv = init_alert_mask(port);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
return init_power_status_mask(port);
|
||||
}
|
||||
|
||||
int tcpm_get_cc(int port, int *cc1, int *cc2)
|
||||
|
||||
@@ -42,18 +42,20 @@ static int init_alert_mask(int port)
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_PD_TCPM_VBUS
|
||||
static int init_power_status_mask(int port)
|
||||
{
|
||||
uint8_t mask;
|
||||
int rv;
|
||||
|
||||
#ifdef CONFIG_USB_PD_TCPM_VBUS
|
||||
mask = TCPC_REG_POWER_STATUS_VBUS_PRES;
|
||||
#else
|
||||
mask = 0;
|
||||
#endif
|
||||
rv = tcpm_set_power_status_mask(port, mask);
|
||||
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
int tcpm_get_cc(int port, int *cc1, int *cc2)
|
||||
{
|
||||
@@ -220,7 +222,6 @@ int tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header,
|
||||
void tcpc_alert(int port)
|
||||
{
|
||||
int status;
|
||||
int power_status;
|
||||
|
||||
/* Read the Alert register from the TCPC */
|
||||
tcpm_alert_status(port, &status);
|
||||
@@ -238,16 +239,30 @@ void tcpc_alert(int port)
|
||||
task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0);
|
||||
}
|
||||
if (status & TCPC_REG_ALERT_POWER_STATUS) {
|
||||
/* Read Power Status register */
|
||||
tcpm_get_power_status(port, &power_status);
|
||||
/* Update VBUS status */
|
||||
tcpc_vbus[port] = power_status &
|
||||
TCPC_REG_POWER_STATUS_VBUS_PRES ? 1 : 0;
|
||||
int reg = 0;
|
||||
|
||||
i2c_read8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port),
|
||||
TCPC_REG_POWER_STATUS_MASK, ®);
|
||||
|
||||
if (reg == TCPC_REG_POWER_STATUS_MASK_ALL) {
|
||||
/*
|
||||
* If power status mask has been reset, then the TCPC
|
||||
* has reset.
|
||||
*/
|
||||
task_set_event(PD_PORT_TO_TASK_ID(port),
|
||||
PD_EVENT_TCPC_RESET, 0);
|
||||
} else {
|
||||
/* Read Power Status register */
|
||||
tcpm_get_power_status(port, ®);
|
||||
/* Update VBUS status */
|
||||
tcpc_vbus[port] = reg &
|
||||
TCPC_REG_POWER_STATUS_VBUS_PRES ? 1 : 0;
|
||||
#if defined(CONFIG_USB_PD_TCPM_VBUS) && defined(CONFIG_USB_CHARGER)
|
||||
/* Update charge manager with new VBUS state */
|
||||
usb_charger_vbus_change(port, tcpc_vbus[port]);
|
||||
/* Update charge manager with new VBUS state */
|
||||
usb_charger_vbus_change(port, tcpc_vbus[port]);
|
||||
#endif /* CONFIG_USB_PD_TCPM_VBUS && CONFIG_USB_CHARGER */
|
||||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||||
task_wake(PD_PORT_TO_TASK_ID(port));
|
||||
}
|
||||
}
|
||||
if (status & TCPC_REG_ALERT_RX_STATUS) {
|
||||
/* message received */
|
||||
@@ -283,13 +298,11 @@ int tcpm_init(int port)
|
||||
!(power_status & TCPC_REG_POWER_STATUS_UNINIT)) {
|
||||
i2c_write16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port),
|
||||
TCPC_REG_ALERT, 0xffff);
|
||||
#ifdef CONFIG_USB_PD_TCPM_VBUS
|
||||
/* Initialize power_status_mask */
|
||||
init_power_status_mask(port);
|
||||
/* Update VBUS status */
|
||||
tcpc_vbus[port] = power_status &
|
||||
TCPC_REG_POWER_STATUS_VBUS_PRES ? 1 : 0;
|
||||
#endif
|
||||
return init_alert_mask(port);
|
||||
}
|
||||
msleep(10);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define TCPC_REG_PD_INT_REV 0xa
|
||||
#define TCPC_REG_ALERT 0x10
|
||||
|
||||
#define TCPC_REG_ALERT_MASK_ALL 0xfff
|
||||
#define TCPC_REG_ALERT_VBUS_DISCNCT (1<<11)
|
||||
#define TCPC_REG_ALERT_RX_BUF_OVF (1<<10)
|
||||
#define TCPC_REG_ALERT_FAULT (1<<9)
|
||||
@@ -59,6 +60,7 @@
|
||||
#define TCPC_REG_CC_STATUS_CC1(reg) ((reg) & 0x3)
|
||||
|
||||
#define TCPC_REG_POWER_STATUS 0x1e
|
||||
#define TCPC_REG_POWER_STATUS_MASK_ALL 0xff
|
||||
#define TCPC_REG_POWER_STATUS_VBUS_PRES (1<<2)
|
||||
#define TCPC_REG_POWER_STATUS_VBUS_DET (1<<3)
|
||||
#define TCPC_REG_POWER_STATUS_UNINIT (1<<6)
|
||||
|
||||
@@ -42,11 +42,11 @@ enum pd_rx_errors {
|
||||
PD_RX_ERR_CABLE_RESET = -6 /* Got a Cable-Reset packet */
|
||||
};
|
||||
|
||||
/* incoming/outgoing packet event (for the USB PD task) */
|
||||
#define PD_EVENT_RX (1<<2)
|
||||
#define PD_EVENT_TX (1<<3)
|
||||
/* CC line change event */
|
||||
#define PD_EVENT_CC (1<<4)
|
||||
/* Events for USB PD task */
|
||||
#define PD_EVENT_RX (1<<2) /* Incoming packet event */
|
||||
#define PD_EVENT_TX (1<<3) /* Outgoing packet event */
|
||||
#define PD_EVENT_CC (1<<4) /* CC line change event */
|
||||
#define PD_EVENT_TCPC_RESET (1<<5) /* TCPC has reset */
|
||||
|
||||
/* --- PD data message helpers --- */
|
||||
#define PDO_MAX_OBJECTS 7
|
||||
|
||||
Reference in New Issue
Block a user