From: Peng Fan <[email protected]> Add a zero-overhead ring buffer trace facility for debugging USB PD negotiation timing issues. Trace points record timestamp and event to a RAM buffer without any serial output, avoiding interference with PD protocol timing constraints.
Trace points cover the critical PD sink negotiation path: - Source_Capabilities reception - Request construction and transmission - TX completion polling - Accept/PS_RDY reception - SNK_READY state entry - Post-ready polling The trace buffer can be dumped on demand via pd_trace_dump() for post-mortem analysis of PD negotiation failures. Signed-off-by: Peng Fan <[email protected]> --- drivers/usb/tcpm/tcpm-internal.h | 40 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/tcpm/tcpm.c | 17 +++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/drivers/usb/tcpm/tcpm-internal.h b/drivers/usb/tcpm/tcpm-internal.h index 56144209002..138b0dc089e 100644 --- a/drivers/usb/tcpm/tcpm-internal.h +++ b/drivers/usb/tcpm/tcpm-internal.h @@ -166,6 +166,46 @@ struct tcpm_port { unsigned long delay_target; }; +#define PD_TRACE_MAX 32 + +struct pd_trace_entry { + unsigned long ts_us; + const char *event; + u32 val; +}; + +struct pd_trace_buf { + struct pd_trace_entry entries[PD_TRACE_MAX]; + int count; +}; + +extern struct pd_trace_buf g_pd_trace; + +static inline void pd_trace(const char *event, u32 val) +{ + if (g_pd_trace.count < PD_TRACE_MAX) { + struct pd_trace_entry *e = &g_pd_trace.entries[g_pd_trace.count++]; + + e->ts_us = timer_get_us(); + e->event = event; + e->val = val; + } +} + +static inline void pd_trace_dump(void) +{ + int i; + unsigned long base = g_pd_trace.count > 0 ? g_pd_trace.entries[0].ts_us : 0; + + printf("=== PD TRACE (%d entries) ===\n", g_pd_trace.count); + for (i = 0; i < g_pd_trace.count; i++) { + struct pd_trace_entry *e = &g_pd_trace.entries[i]; + + printf("[+%7luus] %-30s 0x%08x\n", + e->ts_us - base, e->event, e->val); + } +} + extern const char * const tcpm_states[]; int tcpm_post_probe(struct udevice *dev); diff --git a/drivers/usb/tcpm/tcpm.c b/drivers/usb/tcpm/tcpm.c index d6af0fae7c1..1ce46118fea 100644 --- a/drivers/usb/tcpm/tcpm.c +++ b/drivers/usb/tcpm/tcpm.c @@ -23,6 +23,8 @@ const char * const tcpm_states[] = { FOREACH_TCPM_STATE(GENERATE_TCPM_STRING) }; +struct pd_trace_buf g_pd_trace; + const char * const typec_pd_rev_name[] = { [PD_REV10] = "rev1", [PD_REV20] = "rev2", @@ -193,7 +195,9 @@ static int tcpm_pd_transmit(struct udevice *dev, dev_dbg(dev, "TCPM: PD TX, type: %#x\n", type); port->tx_complete = false; + pd_trace("PD_TX_START", type); ret = drvops->pd_transmit(dev, type, msg, port->negotiated_rev); + pd_trace("PD_TX_DONE", ret); if (ret < 0) return ret; @@ -213,6 +217,7 @@ static int tcpm_pd_transmit(struct udevice *dev, return ret; } + pd_trace("TX_POLL_DONE", port->tx_status); switch (port->tx_status) { case TCPC_TX_SUCCESS: port->message_id = (port->message_id + 1) & PD_HEADER_ID_MASK; @@ -626,6 +631,7 @@ static void tcpm_pd_data_request(struct udevice *dev, switch (type) { case PD_DATA_SOURCE_CAP: + pd_trace("RX_SOURCE_CAP", cnt); for (i = 0; i < cnt; i++) port->source_caps[i] = le32_to_cpu(msg->payload[i]); @@ -668,6 +674,7 @@ static void tcpm_pd_data_request(struct udevice *dev, * but be prepared to keep waiting for VBUS after it was * handled. */ + pd_trace("SET_SNK_NEGOTIATE", 0); tcpm_set_state(dev, SNK_NEGOTIATE_CAPABILITIES, 0); break; case PD_DATA_REQUEST: @@ -736,6 +743,7 @@ static void tcpm_pd_ctrl_request(struct udevice *dev, case PD_CTRL_GOTO_MIN: break; case PD_CTRL_PS_RDY: + pd_trace("RX_PS_RDY", port->state); switch (port->state) { case SNK_TRANSITION_SINK: if (port->vbus_present) { @@ -775,6 +783,7 @@ static void tcpm_pd_ctrl_request(struct udevice *dev, } break; case PD_CTRL_ACCEPT: + pd_trace("RX_ACCEPT", port->state); switch (port->state) { case SNK_NEGOTIATE_CAPABILITIES: tcpm_set_state(dev, SNK_TRANSITION_SINK, 0); @@ -1655,9 +1664,13 @@ static void run_state_machine(struct udevice *dev) } break; case SNK_NEGOTIATE_CAPABILITIES: + pd_trace("SNK_NEGOTIATE_ENTER", 0); port->pd_capable = true; port->hard_reset_count = 0; ret = tcpm_pd_send_request(dev); + pd_trace("SNK_NEGOTIATE_SENT", ret); + pd_trace("REQ_MV", port->req_supply_voltage); + pd_trace("REQ_MA", port->req_current_limit); if (ret < 0) { /* Let the Source send capabilities again. */ tcpm_set_state(dev, SNK_WAIT_CAPABILITIES, 0); @@ -1672,8 +1685,10 @@ static void run_state_machine(struct udevice *dev) PD_T_PS_TRANSITION); break; case SNK_READY: + pd_trace("SNK_READY", 0); port->update_sink_caps = false; tcpm_typec_connect(port); + /* * Here poll_event_cnt is cleared, waiting for self-powered Type-C devices * to send DR_swap Messge until 1s (TCPM_POLL_EVENT_TIME_OUT * 500us)timeout @@ -2255,11 +2270,13 @@ static void tcpm_poll_event(struct udevice *dev) if (port->state == SNK_READY || port->state == SRC_READY) { int settle; + pd_trace("POST_READY_POLL_START", port->state); for (settle = 0; settle < 1000; settle++) { drvops->poll_event(dev); udelay(500); tcpm_check_and_run_delayed_work(dev); } + pd_trace("POST_READY_POLL_DONE", port->state); } if (port->state != SNK_READY && port->state != SRC_READY) -- 2.51.0

