diff options
-rw-r--r-- | common/usbc/usb_pd_dp_ufp.c | 43 | ||||
-rw-r--r-- | common/usbc/usb_pd_dpm.c | 82 | ||||
-rw-r--r-- | common/usbc/usb_tc_drp_acc_trysrc_sm.c | 22 | ||||
-rw-r--r-- | include/usb_pd.h | 31 |
4 files changed, 151 insertions, 27 deletions
diff --git a/common/usbc/usb_pd_dp_ufp.c b/common/usbc/usb_pd_dp_ufp.c index e9da5b55e0..2c651052e5 100644 --- a/common/usbc/usb_pd_dp_ufp.c +++ b/common/usbc/usb_pd_dp_ufp.c @@ -59,13 +59,54 @@ struct hpd_info { static struct hpd_info hpd; static struct mutex hpd_mutex; +static int alt_dp_mode_opos[CONFIG_USB_PD_PORT_MAX_COUNT]; + +void pd_ufp_set_dp_opos(int port, int opos) +{ + alt_dp_mode_opos[port] = opos; +} + +int pd_ufp_get_dp_opos(int port) +{ + return alt_dp_mode_opos[port]; +} static void hpd_to_dp_attention(void) { + int port = hpd_config.port; int evt_index = hpd.count - 1; + uint32_t vdm[2]; + uint32_t svdm_header; + enum hpd_event evt; + int opos = pd_ufp_get_dp_opos(port); + + if (!opos) + return; + /* Get the next hpd event from the queue */ + evt = hpd.queue[evt_index]; + /* Save timestamp of when most recent DP attention message was sent */ hpd.last_send_ts = get_time().val; - pd_send_hpd(hpd_config.port, hpd.queue[evt_index]); + + /* + * Construct DP Attention message. This consists of the VDM header and + * the DP_STATUS VDO. + */ + svdm_header = VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPC_TX_SOP)) | + VDO_OPOS(opos) | CMD_ATTENTION; + vdm[0] = VDO(USB_SID_DISPLAYPORT, 1, svdm_header); + + vdm[1] = VDO_DP_STATUS((evt == hpd_irq), /* IRQ_HPD */ + (evt != hpd_low), /* HPD_HI|LOW */ + 0, /* request exit DP */ + 0, /* request exit USB */ + 0, /* MF pref */ + 1, /* enabled */ + 0, /* power low */ + 0x2); + + /* Send request to DPM to send an attention VDM */ + pd_request_vdm_attention(port, vdm, ARRAY_SIZE(vdm)); /* If there are still events, need to shift the buffer */ if (--hpd.count) { diff --git a/common/usbc/usb_pd_dpm.c b/common/usbc/usb_pd_dpm.c index 1c579d7630..9aefa97649 100644 --- a/common/usbc/usb_pd_dpm.c +++ b/common/usbc/usb_pd_dpm.c @@ -30,8 +30,14 @@ #define CPRINTS(format, args...) #endif +/* Max Attention length is header + 1 VDO */ +#define DPM_ATTENION_MAX_VDO 2 + static struct { uint32_t flags; + uint32_t vdm_attention[DPM_ATTENION_MAX_VDO]; + int vdm_cnt; + mutex_t vdm_attention_mutex; } dpm[CONFIG_USB_PD_PORT_MAX_COUNT]; #define DPM_SET_FLAG(port, flag) atomic_or(&dpm[(port)].flags, (flag)) @@ -44,6 +50,55 @@ static struct { #define DPM_FLAG_ENTER_DP BIT(2) #define DPM_FLAG_ENTER_TBT BIT(3) #define DPM_FLAG_ENTER_USB4 BIT(4) +#define DPM_FLAG_SEND_ATTENTION BIT(5) + +#ifdef CONFIG_ZEPHYR +static int init_vdm_attention_mutex(const struct device *dev) +{ + int port; + + ARG_UNUSED(dev); + + for (port = 0; port < CONFIG_USB_PD_PORT_MAX_COUNT; port++) + k_mutex_init(&dpm[port].vdm_attention_mutex); + + return 0; +} +SYS_INIT(init_vdm_attention_mutex, POST_KERNEL, 50); +#endif /* CONFIG_ZEPHYR */ + +enum ec_status pd_request_vdm_attention(int port, const uint32_t *data, + int vdo_count) +{ + mutex_lock(&dpm[port].vdm_attention_mutex); + + /* Only one Attention message may be pending */ + if (DPM_CHK_FLAG(port, DPM_FLAG_SEND_ATTENTION)) { + mutex_unlock(&dpm[port].vdm_attention_mutex); + return EC_RES_UNAVAILABLE; + } + + /* SVDM Attention message must be 1 or 2 VDOs in length */ + if (!vdo_count || (vdo_count > DPM_ATTENION_MAX_VDO)) { + mutex_unlock(&dpm[port].vdm_attention_mutex); + return EC_RES_INVALID_PARAM; + } + + /* Save contents of Attention message */ + memcpy(dpm[port].vdm_attention, data, vdo_count * sizeof(uint32_t)); + dpm[port].vdm_cnt = vdo_count; + + /* + * Indicate to DPM that an Attention message needs to be sent. This flag + * will be cleared when the Attention message is sent to the policy + * engine. + */ + DPM_SET_FLAG(port, DPM_FLAG_SEND_ATTENTION); + + mutex_unlock(&dpm[port].vdm_attention_mutex); + + return EC_RES_SUCCESS; +} enum ec_status pd_request_enter_mode(int port, enum typec_mode mode) { @@ -333,12 +388,31 @@ static void dpm_attempt_mode_exit(int port) pd_dpm_request(port, DPM_REQUEST_VDM); } +static void dpm_send_attention_vdm(int port) +{ + /* Set up VDM ATTEN msg that was passed in previously */ + if (pd_setup_vdm_request(port, TCPC_TX_SOP, dpm[port].vdm_attention, + dpm[port].vdm_cnt) == true) + /* Trigger PE to start a VDM command run */ + pd_dpm_request(port, DPM_REQUEST_VDM); + + /* Clear flag after message is sent to PE layer */ + DPM_CLR_FLAG(port, DPM_FLAG_SEND_ATTENTION); +} + void dpm_run(int port) { - if (DPM_CHK_FLAG(port, DPM_FLAG_EXIT_REQUEST)) - dpm_attempt_mode_exit(port); - else if (!DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) - dpm_attempt_mode_entry(port); + if (pd_get_data_role(port) == PD_ROLE_DFP) { + /* Run DFP related DPM requests */ + if (DPM_CHK_FLAG(port, DPM_FLAG_EXIT_REQUEST)) + dpm_attempt_mode_exit(port); + else if (!DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) + dpm_attempt_mode_entry(port); + } else { + /* Run UFP related DPM requests */ + if (DPM_CHK_FLAG(port, DPM_FLAG_SEND_ATTENTION)) + dpm_send_attention_vdm(port); + } } /* diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c index 269ded1159..fad4c0cb5d 100644 --- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c +++ b/common/usbc/usb_tc_drp_acc_trysrc_sm.c @@ -1860,28 +1860,6 @@ __maybe_unused static void handle_new_power_state(int port) } } -#if defined(CONFIG_USB_PD_ALT_MODE) && !defined(CONFIG_USB_PD_ALT_MODE_DFP) -void pd_send_hpd(int port, enum hpd_event hpd) -{ - uint32_t data[1]; - int opos = pd_alt_mode(port, TCPC_TX_SOP, USB_SID_DISPLAYPORT); - - if (!opos) - return; - - data[0] = VDO_DP_STATUS((hpd == hpd_irq), /* IRQ_HPD */ - (hpd != hpd_low), /* HPD_HI|LOW */ - 0, /* request exit DP */ - 0, /* request exit USB */ - 0, /* MF pref */ - 1, /* enabled */ - 0, /* power low */ - 0x2); - pd_send_vdm(port, USB_SID_DISPLAYPORT, VDO_OPOS(opos) | CMD_ATTENTION, - data, 1); -} -#endif - #ifdef CONFIG_USBC_VCONN_SWAP void pd_request_vconn_swap_off(int port) { diff --git a/include/usb_pd.h b/include/usb_pd.h index 7e4b2fadef..1f91af14c7 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -2163,6 +2163,23 @@ struct partner_active_modes *pd_get_partner_active_modes(int port, enum tcpm_transmit_type type); /* + * Sets the current object position for DP alt-mode + * Note: opos == 0 means the mode is not active + * + * @param port USB-C port number + * @param opos Object position for DP alternate mode + */ +void pd_ufp_set_dp_opos(int port, int opos); + +/* + * Gets the current object position for DP alt-mode + * + * @param port USB-C port number + * @return Alt-DP object position value for the given port + */ +int pd_ufp_get_dp_opos(int port); + +/* * Returns True if cable supports USB2 connection * * @param port USB-C port number @@ -2783,6 +2800,20 @@ void pd_notify_event(int port, uint32_t event_mask); void pd_clear_events(int port, uint32_t clear_mask); /* + * Requests a VDM Attention message be sent. Attention is the only SVDM message + * that does not result in a response from the port partner. In addition, if + * it's a DP Attention message, then it will be requested from outside of the + * port's PD task. + * + * @param port USB-C port number + * @param *data pointer to the VDM Attention message + * @param vdo_count number of VDOs (must be 1 or 2) + * @return EC_RES_SUCCESS if a VDM message is scheduled. + */ +enum ec_status pd_request_vdm_attention(int port, const uint32_t *data, + int vdo_count); + +/* * Requests that the port enter the specified mode. A successful result just * means that the request was received, not that the mode has been entered yet. * |