usb-c: Add USB-C Subsystem Source PD Support
Add USB-C Power Delivery Source Support to USB-C Subsystem Signed-off-by: Sam Hurst <sbh1187@gmail.com>
This commit is contained in:
parent
cb9a250579
commit
0056249a13
|
@ -69,6 +69,16 @@ enum usbc_policy_request_t {
|
|||
* port partner
|
||||
*/
|
||||
REQUEST_PE_GET_SRC_CAPS,
|
||||
/**
|
||||
* Request Policy Engine to get Sink Capabilities from
|
||||
* port partner
|
||||
*/
|
||||
REQUEST_GET_SNK_CAPS,
|
||||
/**
|
||||
* Request Policy Engine to request the port partner to source
|
||||
* minimum power
|
||||
*/
|
||||
REQUEST_PE_GOTO_MIN,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -129,6 +139,8 @@ enum usbc_policy_check_t {
|
|||
CHECK_SNK_AT_DEFAULT_LEVEL,
|
||||
/** Check if should control VCONN */
|
||||
CHECK_VCONN_CONTROL,
|
||||
/** Check if Source Power Supply is at default level */
|
||||
CHECK_SRC_PS_AT_DEFAULT_LEVEL,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -145,7 +157,18 @@ enum usbc_policy_wait_t {
|
|||
WAIT_VCONN_SWAP,
|
||||
};
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
/**
|
||||
* @brief Device Policy Manager's response to a Sink Request
|
||||
*/
|
||||
enum usbc_snk_req_reply_t {
|
||||
/** The sink port partner's request can be met */
|
||||
SNK_REQUEST_VALID,
|
||||
/** The sink port partner's request can not be met */
|
||||
SNK_REQUEST_REJECT,
|
||||
/** The sink port partner's request can be met at a later time */
|
||||
SNK_REQUEST_WAIT,
|
||||
};
|
||||
|
||||
/** \addtogroup sink_callbacks
|
||||
* @{
|
||||
*/
|
||||
|
@ -157,9 +180,7 @@ enum usbc_policy_wait_t {
|
|||
* @param num_pdos pointer where number of pdos is stored
|
||||
* @return 0 on success
|
||||
*/
|
||||
typedef int (*policy_cb_get_snk_cap_t)(const struct device *dev,
|
||||
uint32_t **pdos,
|
||||
int *num_pdos);
|
||||
typedef int (*policy_cb_get_snk_cap_t)(const struct device *dev, uint32_t **pdos, int *num_pdos);
|
||||
/**
|
||||
* @brief Callback type used to report the received Port Partner's
|
||||
* Source Capabilities
|
||||
|
@ -168,8 +189,7 @@ typedef int (*policy_cb_get_snk_cap_t)(const struct device *dev,
|
|||
* @param pdos pointer to the partner's source pdos
|
||||
* @param num_pdos number of source pdos
|
||||
*/
|
||||
typedef void (*policy_cb_set_src_cap_t)(const struct device *dev,
|
||||
const uint32_t *pdos,
|
||||
typedef void (*policy_cb_set_src_cap_t)(const struct device *dev, const uint32_t *pdos,
|
||||
const int num_pdos);
|
||||
|
||||
/**
|
||||
|
@ -220,7 +240,105 @@ typedef uint32_t (*policy_cb_get_rdo_t)(const struct device *dev);
|
|||
*/
|
||||
typedef bool (*policy_cb_is_snk_at_default_t)(const struct device *dev);
|
||||
/** @}*/
|
||||
/** @endcond */
|
||||
/** \addtogroup source_callbacks
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Callback type used to get the Rp value that should be placed on
|
||||
* the CC lines
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param rp Rp value
|
||||
* @return 0 on success
|
||||
*/
|
||||
typedef int (*policy_cb_get_src_rp_t)(const struct device *dev, enum tc_rp_value *rp);
|
||||
|
||||
/**
|
||||
* @brief Callback type used to enable VBUS
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param en true to enable VBUS, else disable it
|
||||
* @return 0 on success
|
||||
*/
|
||||
typedef int (*policy_cb_src_en_t)(const struct device *dev, bool en);
|
||||
|
||||
/**
|
||||
* @brief Callback type used to get the Source Capabilities
|
||||
* from the Device Policy Manager
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param pdos pointer to source capability pdos
|
||||
* @param num_pdos pointer to number of source capability pdos
|
||||
* @return 0 on success
|
||||
*/
|
||||
typedef int (*policy_cb_get_src_caps_t)(const struct device *dev, const uint32_t **pdos,
|
||||
uint32_t *num_pdos);
|
||||
|
||||
/**
|
||||
* @brief Callback type used to check if Sink request is valid
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param request_msg request message to check
|
||||
* @return sink request reply
|
||||
*/
|
||||
typedef enum usbc_snk_req_reply_t (*policy_cb_check_sink_request_t)(const struct device *dev,
|
||||
const uint32_t request_msg);
|
||||
|
||||
/**
|
||||
* @brief Callback type used to check if Source Power Supply is ready
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @return true if power supply is ready, else false
|
||||
*/
|
||||
typedef bool (*policy_cb_is_ps_ready_t)(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Callback type used to check if present Contract is still valid
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param present_contract present contract
|
||||
* @return true if present contract is still valid
|
||||
*/
|
||||
typedef bool (*policy_cb_present_contract_is_valid_t)(const struct device *dev,
|
||||
const uint32_t present_contract);
|
||||
|
||||
/**
|
||||
* @brief Callback type used to request that a different set of Source Caps
|
||||
* be sent to the Sink
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @return true if a different set of Cource Caps is available
|
||||
*/
|
||||
typedef bool (*policy_cb_change_src_caps_t)(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Callback type used to report the Capabilities received from
|
||||
* a Sink Port Partner
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param pdos pointer to sink cap pdos
|
||||
* @param num_pdos number of sink cap pdos
|
||||
*/
|
||||
typedef void (*policy_cb_set_port_partner_snk_cap_t)(const struct device *dev, const uint32_t *pdos,
|
||||
const int num_pdos);
|
||||
/**
|
||||
* @brief Callback type used to get the Rp value that should be placed on
|
||||
* the CC lines
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param rp rp value
|
||||
* @return 0 on success
|
||||
*/
|
||||
typedef int (*policy_cb_get_src_rp_t)(const struct device *dev, enum tc_rp_value *rp);
|
||||
/**
|
||||
* @brief Callback type used to enable VBUS
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param en true if VBUS should be enabled, else false to disable it
|
||||
* @return 0 on success
|
||||
*/
|
||||
typedef int (*policy_cb_src_en_t)(const struct device *dev, bool en);
|
||||
/** @}*/
|
||||
|
||||
/**
|
||||
* @brief Start the USB-C Subsystem
|
||||
|
@ -248,8 +366,7 @@ int usbc_suspend(const struct device *dev);
|
|||
*
|
||||
* @retval 0 on success
|
||||
*/
|
||||
int usbc_request(const struct device *dev,
|
||||
const enum usbc_policy_request_t req);
|
||||
int usbc_request(const struct device *dev, const enum usbc_policy_request_t req);
|
||||
|
||||
/**
|
||||
* @brief Set pointer to Device Policy Manager (DPM) data
|
||||
|
@ -257,8 +374,7 @@ int usbc_request(const struct device *dev,
|
|||
* @param dev Runtime device structure
|
||||
* @param dpm_data pointer to dpm data
|
||||
*/
|
||||
void usbc_set_dpm_data(const struct device *dev,
|
||||
void *dpm_data);
|
||||
void usbc_set_dpm_data(const struct device *dev, void *dpm_data);
|
||||
|
||||
/**
|
||||
* @brief Get pointer to Device Policy Manager (DPM) data
|
||||
|
@ -276,8 +392,7 @@ void *usbc_get_dpm_data(const struct device *dev);
|
|||
* @param dev Runtime device structure
|
||||
* @param cb VCONN control callback
|
||||
*/
|
||||
void usbc_set_vconn_control_cb(const struct device *dev,
|
||||
const tcpc_vconn_control_cb_t cb);
|
||||
void usbc_set_vconn_control_cb(const struct device *dev, const tcpc_vconn_control_cb_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to discharge VCONN
|
||||
|
@ -285,8 +400,7 @@ void usbc_set_vconn_control_cb(const struct device *dev,
|
|||
* @param dev Runtime device structure
|
||||
* @param cb VCONN discharge callback
|
||||
*/
|
||||
void usbc_set_vconn_discharge_cb(const struct device *dev,
|
||||
const tcpc_vconn_discharge_cb_t cb);
|
||||
void usbc_set_vconn_discharge_cb(const struct device *dev, const tcpc_vconn_discharge_cb_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to check a policy
|
||||
|
@ -294,8 +408,7 @@ void usbc_set_vconn_discharge_cb(const struct device *dev,
|
|||
* @param dev Runtime device structure
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_check(const struct device *dev,
|
||||
const policy_cb_check_t cb);
|
||||
void usbc_set_policy_cb_check(const struct device *dev, const policy_cb_check_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to notify Device Policy Manager of a
|
||||
|
@ -304,8 +417,7 @@ void usbc_set_policy_cb_check(const struct device *dev,
|
|||
* @param dev Runtime device structure
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_notify(const struct device *dev,
|
||||
const policy_cb_notify_t cb);
|
||||
void usbc_set_policy_cb_notify(const struct device *dev, const policy_cb_notify_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to notify Device Policy Manager of WAIT
|
||||
|
@ -314,8 +426,7 @@ void usbc_set_policy_cb_notify(const struct device *dev,
|
|||
* @param dev Runtime device structure
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_wait_notify(const struct device *dev,
|
||||
const policy_cb_wait_notify_t cb);
|
||||
void usbc_set_policy_cb_wait_notify(const struct device *dev, const policy_cb_wait_notify_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to get the Sink Capabilities
|
||||
|
@ -323,8 +434,7 @@ void usbc_set_policy_cb_wait_notify(const struct device *dev,
|
|||
* @param dev Runtime device structure
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_get_snk_cap(const struct device *dev,
|
||||
const policy_cb_get_snk_cap_t cb);
|
||||
void usbc_set_policy_cb_get_snk_cap(const struct device *dev, const policy_cb_get_snk_cap_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to store the received Port Partner's
|
||||
|
@ -333,8 +443,7 @@ void usbc_set_policy_cb_get_snk_cap(const struct device *dev,
|
|||
* @param dev Runtime device structure
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_set_src_cap(const struct device *dev,
|
||||
const policy_cb_set_src_cap_t cb);
|
||||
void usbc_set_policy_cb_set_src_cap(const struct device *dev, const policy_cb_set_src_cap_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to get the Request Data Object (RDO)
|
||||
|
@ -342,8 +451,7 @@ void usbc_set_policy_cb_set_src_cap(const struct device *dev,
|
|||
* @param dev Runtime device structure
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_get_rdo(const struct device *dev,
|
||||
const policy_cb_get_rdo_t cb);
|
||||
void usbc_set_policy_cb_get_rdo(const struct device *dev, const policy_cb_get_rdo_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to check if the sink power supply is at
|
||||
|
@ -354,6 +462,79 @@ void usbc_set_policy_cb_get_rdo(const struct device *dev,
|
|||
*/
|
||||
void usbc_set_policy_cb_is_snk_at_default(const struct device *dev,
|
||||
const policy_cb_is_snk_at_default_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to get the Rp value that should be placed on
|
||||
* the CC lines
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_get_src_rp(const struct device *dev, const policy_cb_get_src_rp_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to enable VBUS
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_src_en(const struct device *dev, const policy_cb_src_en_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to get the Source Capabilities
|
||||
* from the Device Policy Manager
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_get_src_caps(const struct device *dev, const policy_cb_get_src_caps_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to check if Sink request is valid
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_check_sink_request(const struct device *dev,
|
||||
const policy_cb_check_sink_request_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to check if Source Power Supply is ready
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_is_ps_ready(const struct device *dev,
|
||||
const policy_cb_is_ps_ready_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback to check if present Contract is still valid
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_present_contract_is_valid(const struct device *dev,
|
||||
const policy_cb_present_contract_is_valid_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to request that a different set of Source Caps
|
||||
* be sent to the Sink
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_change_src_caps(const struct device *dev,
|
||||
const policy_cb_change_src_caps_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set the callback used to store the Capabilities received from a Sink Port Partner
|
||||
*
|
||||
* @param dev USB-C Connector Instance
|
||||
* @param cb callback
|
||||
*/
|
||||
void usbc_set_policy_cb_set_port_partner_snk_cap(const struct device *dev,
|
||||
const policy_cb_set_port_partner_snk_cap_t cb);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -18,3 +18,10 @@ zephyr_library_sources_ifdef(
|
|||
usbc_tc_snk_states.c
|
||||
usbc_pe_snk_states.c
|
||||
)
|
||||
|
||||
# Source USB-C Stack files
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
usbc_tc_src_states.c
|
||||
usbc_pe_src_states.c
|
||||
)
|
||||
|
|
|
@ -48,6 +48,11 @@ config USBC_CSM_SINK_ONLY
|
|||
help
|
||||
Allows the USB-C state machine to function as a Sink
|
||||
|
||||
config USBC_CSM_SOURCE_ONLY
|
||||
bool "Source USBC Connection State Machine"
|
||||
help
|
||||
Allow the USB-C state machine to function as a Source
|
||||
|
||||
endchoice
|
||||
|
||||
module = USBC_STACK
|
||||
|
|
|
@ -15,6 +15,7 @@ LOG_MODULE_DECLARE(usbc_stack, CONFIG_USBC_STACK_LOG_LEVEL);
|
|||
#include "usbc_stack.h"
|
||||
#include "usbc_pe_common_internal.h"
|
||||
#include "usbc_pe_snk_states_internal.h"
|
||||
#include "usbc_pe_src_states_internal.h"
|
||||
|
||||
static const struct smf_state pe_states[PE_STATE_COUNT];
|
||||
|
||||
|
@ -96,13 +97,17 @@ static void pe_init(const struct device *dev)
|
|||
atomic_clear(pe->flags);
|
||||
|
||||
/* Initialize common timers */
|
||||
usbc_timer_init(&pe->pd_t_sender_response, PD_T_SENDER_RESPONSE_NOM_MS);
|
||||
usbc_timer_init(&pe->pd_t_sender_response, PD_T_NO_RESPONSE_MAX_MS);
|
||||
usbc_timer_init(&pe->pd_t_chunking_not_supported, PD_T_CHUNKING_NOT_SUPPORTED_NOM_MS);
|
||||
|
||||
/* Initialize common counters */
|
||||
pe->hard_reset_counter = 0;
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
pe_snk_init(dev);
|
||||
#else
|
||||
pe_src_init(dev);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -229,6 +234,21 @@ void pe_message_sent(const struct device *dev)
|
|||
atomic_set_bit(pe->flags, PE_FLAGS_TX_COMPLETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State:
|
||||
*
|
||||
* The PE_Send_Soft_Reset state shall be entered from
|
||||
* any state when
|
||||
* * A Protocol Error is detected by Protocol Layer during a
|
||||
* Non-Interruptible AMS or
|
||||
* * A message has not been sent after retries or
|
||||
* * When not in an explicit contract and
|
||||
* * Protocol Errors occurred on SOP during an Interruptible AMS or
|
||||
* * Protocol Errors occurred on SOP during any AMS where the first
|
||||
* Message in the sequence has not yet been sent i.e. an unexpected
|
||||
* Message is received instead of the expected GoodCRC Message
|
||||
* response.
|
||||
*/
|
||||
static bool pe_soft_reset_is_required(const struct device *dev, const enum pd_packet_type type)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
@ -281,21 +301,6 @@ void pe_report_error(const struct device *dev, const enum pe_error e,
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State:
|
||||
*
|
||||
* The PE_Send_Soft_Reset state shall be entered from
|
||||
* any state when
|
||||
* * A Protocol Error is detected by Protocol Layer during a
|
||||
* Non-Interruptible AMS or
|
||||
* * A message has not been sent after retries or
|
||||
* * When not in an explicit contract and
|
||||
* * Protocol Errors occurred on SOP during an Interruptible AMS or
|
||||
* * Protocol Errors occurred on SOP during any AMS where the first
|
||||
* Message in the sequence has not yet been sent i.e. an unexpected
|
||||
* Message is received instead of the expected GoodCRC Message
|
||||
* response.
|
||||
*/
|
||||
/* Transmit error */
|
||||
if (e == ERR_XMIT) {
|
||||
atomic_set_bit(pe->flags, PE_FLAGS_MSG_XMIT_ERROR);
|
||||
|
@ -585,17 +590,7 @@ bool policy_wait_notify(const struct device *dev, const enum usbc_policy_wait_t
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the received source caps to the DPM
|
||||
*/
|
||||
void policy_set_src_cap(const struct device *dev, const uint32_t *pdos, const int num_pdos)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
if (data->policy_cb_set_src_cap) {
|
||||
data->policy_cb_set_src_cap(dev, pdos, num_pdos);
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
|
||||
/**
|
||||
* @brief Get a Request Data Object from the DPM
|
||||
|
@ -610,6 +605,18 @@ uint32_t policy_get_request_data_object(const struct device *dev)
|
|||
return data->policy_cb_get_rdo(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the received source caps to the DPM
|
||||
*/
|
||||
void policy_set_src_cap(const struct device *dev, const uint32_t *pdos, const int num_pdos)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
if (data->policy_cb_set_src_cap) {
|
||||
data->policy_cb_set_src_cap(dev, pdos, num_pdos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the sink is a default level
|
||||
*/
|
||||
|
@ -637,6 +644,83 @@ void policy_get_snk_cap(const struct device *dev, uint32_t **pdos, int *num_pdos
|
|||
data->policy_cb_get_snk_cap(dev, pdos, num_pdos);
|
||||
}
|
||||
|
||||
#else /* CONFIG_USBC_CSM_SOURCE_ONLY */
|
||||
|
||||
/**
|
||||
* @brief Send the received sink caps to the DPM
|
||||
*/
|
||||
void policy_set_port_partner_snk_cap(const struct device *dev,
|
||||
const uint32_t *pdos,
|
||||
const int num_pdos)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
if (data->policy_cb_set_port_partner_snk_cap) {
|
||||
data->policy_cb_set_port_partner_snk_cap(dev, pdos, num_pdos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if Sink Request can be met by DPM
|
||||
*/
|
||||
enum usbc_snk_req_reply_t policy_check_sink_request(const struct device *dev,
|
||||
const uint32_t request_msg)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
/* This callback must be implemented */
|
||||
__ASSERT(data->policy_cb_check_sink_request != NULL,
|
||||
"Callback pointer should not be NULL");
|
||||
|
||||
return data->policy_cb_check_sink_request(dev, request_msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the present contract is still valid
|
||||
*/
|
||||
bool policy_present_contract_is_valid(const struct device *dev,
|
||||
const uint32_t present_contract)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
/* This callback must be implemented */
|
||||
__ASSERT(data->policy_present_contract_is_valid != NULL,
|
||||
"Callback pointer should not be NULL");
|
||||
|
||||
return data->policy_present_contract_is_valid(dev, present_contract);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the power supply is ready
|
||||
*/
|
||||
bool policy_is_ps_ready(const struct device *dev)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
/* This callback must be implemented */
|
||||
__ASSERT(data->policy_is_ps_ready != NULL,
|
||||
"Callback pointer should not be NULL");
|
||||
|
||||
return data->policy_is_ps_ready(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ask the DPM to change the Source Caps.
|
||||
* Returns true if source caps have been updated, else false
|
||||
*/
|
||||
bool policy_change_src_caps(const struct device *dev)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
if (data->policy_change_src_caps == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return data->policy_change_src_caps(dev);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USBC_CSM_SINK_ONLY */
|
||||
|
||||
/**
|
||||
* @brief PE_DRS_Evaluate_Swap Entry state
|
||||
*/
|
||||
|
@ -698,6 +782,8 @@ static void pe_drs_evaluate_swap_run(void *obj)
|
|||
|
||||
/**
|
||||
* @brief PE_DRS_Send_Swap Entry state
|
||||
* NOTE: 8.3.3.18.1.5 PE_DRS_DFP_UFP_Send_Swap State
|
||||
* 8.3.3.18.2.5 PE_DRS_UFP_DFP_Send_Swap State
|
||||
*/
|
||||
static void pe_drs_send_swap_entry(void *obj)
|
||||
{
|
||||
|
@ -769,6 +855,89 @@ static void pe_drs_send_swap_run(void *obj)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PE_Get_Sink_Cap Entry state
|
||||
* @brief 8.3.3.18.7.1 PE_DR_SRC_Get_Source_Cap State
|
||||
* @brief 8.3.3.18.9.1 PE_DR_SNK_Get_Sink_Cap State
|
||||
*/
|
||||
void pe_get_sink_cap_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* On entry to the PE_DR_SNK_Get_Sink_Cap state the Policy Engine
|
||||
* Shall send a Get_Sink_Cap Message and initialize and run the
|
||||
* SenderResponseTimer.
|
||||
*/
|
||||
pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_GET_SINK_CAP);
|
||||
/* Initialize Submachine */
|
||||
pe->submachine = SM_WAIT_FOR_TX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PE_Get_Sink_Cap Run state
|
||||
* NOTE: Sender Response Timer is handled in super state.
|
||||
*/
|
||||
void pe_get_sink_cap_run(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
struct protocol_layer_rx_t *prl_rx = data->prl_rx;
|
||||
union pd_header header;
|
||||
|
||||
switch (pe->submachine) {
|
||||
case SM_WAIT_FOR_TX:
|
||||
if (!atomic_test_and_clear_bit(pe->flags, PE_FLAGS_TX_COMPLETE)) {
|
||||
break;
|
||||
}
|
||||
pe->submachine = SM_WAIT_FOR_RX;
|
||||
/* fall through */
|
||||
case SM_WAIT_FOR_RX:
|
||||
if (atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_RECEIVED)) {
|
||||
header = prl_rx->emsg.header;
|
||||
|
||||
if (prl_rx->emsg.type == PD_PACKET_SOP) {
|
||||
if (received_data_message(dev, header, PD_DATA_SINK_CAP)) {
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
uint32_t *pdos = (uint32_t *)prl_rx->emsg.data;
|
||||
uint32_t num_pdos =
|
||||
PD_CONVERT_BYTES_TO_PD_HEADER_COUNT(prl_rx->emsg.len);
|
||||
|
||||
policy_set_port_partner_snk_cap(dev, pdos, num_pdos);
|
||||
pe_set_state(dev, PE_SRC_READY);
|
||||
#else
|
||||
pe_set_state(dev, PE_SNK_READY);
|
||||
#endif
|
||||
return;
|
||||
} else if (received_control_message(dev, header, PD_CTRL_REJECT) ||
|
||||
received_control_message(dev,
|
||||
header, PD_CTRL_NOT_SUPPORTED)) {
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
pe_set_state(dev, PE_SRC_READY);
|
||||
#else
|
||||
pe_set_state(dev, PE_SNK_READY);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
/* Unexpected messages fall through to soft reset */
|
||||
}
|
||||
pe_send_soft_reset(dev, PD_PACKET_SOP);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Inform Device Policy Manager that the message
|
||||
* was discarded
|
||||
*/
|
||||
else if (atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_DISCARDED)) {
|
||||
policy_notify(dev, MSG_DISCARDED);
|
||||
pe_set_state(dev, PE_SNK_READY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pe_suspend_entry(void *obj)
|
||||
{
|
||||
LOG_INF("PE_SUSPEND");
|
||||
|
@ -994,6 +1163,7 @@ static void pe_sender_response_run(void *obj)
|
|||
* Handle Sender Response Timeouts
|
||||
*/
|
||||
switch (current_state) {
|
||||
#if CONFIG_USBC_CSM_SINK_ONLY
|
||||
/* Sink states */
|
||||
case PE_SNK_SELECT_CAPABILITY:
|
||||
pe_set_state(dev, PE_SNK_HARD_RESET);
|
||||
|
@ -1001,7 +1171,44 @@ static void pe_sender_response_run(void *obj)
|
|||
case PE_SNK_GET_SOURCE_CAP:
|
||||
pe_set_state(dev, PE_SNK_READY);
|
||||
break;
|
||||
|
||||
#else
|
||||
/* Source states */
|
||||
case PE_SRC_DISCOVERY:
|
||||
/*
|
||||
* The Policy Engine Shall go to the PE_SRC_Disabled state when:
|
||||
* 1) The Port Partners have not been PD Connected
|
||||
* 2) And the NoResponseTimer times out
|
||||
* 3) And the HardResetCounter > nHardResetCount.
|
||||
*/
|
||||
if ((atomic_test_bit(pe->flags, PE_FLAGS_HAS_BEEN_PD_CONNECTED) == false)
|
||||
&& pe->hard_reset_counter > PD_N_HARD_RESET_COUNT) {
|
||||
pe_set_state(dev, PE_SUSPEND);
|
||||
}
|
||||
break;
|
||||
case PE_SRC_SEND_CAPABILITIES:
|
||||
/*
|
||||
* The Policy Engine Shall go to the ErrorRecovery state when:
|
||||
* 1) The Port Partners have previously been PD Connected
|
||||
* 2) And the NoResponseTimer times out
|
||||
* 3) And the HardResetCounter > nHardResetCount
|
||||
*/
|
||||
if (atomic_test_bit(pe->flags, PE_FLAGS_HAS_BEEN_PD_CONNECTED)
|
||||
&& pe->hard_reset_counter > PD_N_HARD_RESET_COUNT) {
|
||||
usbc_request(dev, REQUEST_TC_ERROR_RECOVERY);
|
||||
}
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Hard_Reset
|
||||
* state when:
|
||||
* 1) The SenderResponseTimer times out
|
||||
*/
|
||||
else {
|
||||
pe_set_state(dev, PE_SRC_HARD_RESET);
|
||||
}
|
||||
break;
|
||||
case PE_GET_SINK_CAP:
|
||||
pe_send_soft_reset(dev, PD_PACKET_SOP);
|
||||
break;
|
||||
#endif
|
||||
/*
|
||||
* Common states:
|
||||
* Could transition to a Sink or Source states,
|
||||
|
@ -1039,6 +1246,14 @@ static const struct smf_state pe_states[PE_STATE_COUNT] = {
|
|||
pe_sender_response_run,
|
||||
pe_sender_response_exit,
|
||||
NULL),
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
[PE_SRC_HARD_RESET_PARENT] = SMF_CREATE_STATE(
|
||||
pe_src_hard_reset_parent_entry,
|
||||
pe_src_hard_reset_parent_run,
|
||||
pe_src_hard_reset_parent_exit,
|
||||
NULL),
|
||||
#endif
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
[PE_SNK_STARTUP] = SMF_CREATE_STATE(
|
||||
pe_snk_startup_entry,
|
||||
pe_snk_startup_run,
|
||||
|
@ -1067,7 +1282,7 @@ static const struct smf_state pe_states[PE_STATE_COUNT] = {
|
|||
[PE_SNK_READY] = SMF_CREATE_STATE(
|
||||
pe_snk_ready_entry,
|
||||
pe_snk_ready_run,
|
||||
NULL,
|
||||
pe_snk_ready_exit,
|
||||
NULL),
|
||||
[PE_SNK_HARD_RESET] = SMF_CREATE_STATE(
|
||||
pe_snk_hard_reset_entry,
|
||||
|
@ -1094,6 +1309,63 @@ static const struct smf_state pe_states[PE_STATE_COUNT] = {
|
|||
pe_snk_transition_sink_run,
|
||||
pe_snk_transition_sink_exit,
|
||||
NULL),
|
||||
#else
|
||||
[PE_SRC_STARTUP] = SMF_CREATE_STATE(
|
||||
pe_src_startup_entry,
|
||||
pe_src_startup_run,
|
||||
NULL,
|
||||
NULL),
|
||||
[PE_SRC_DISCOVERY] = SMF_CREATE_STATE(
|
||||
pe_src_discovery_entry,
|
||||
pe_src_discovery_run,
|
||||
pe_src_discovery_exit,
|
||||
&pe_states[PE_SENDER_RESPONSE_PARENT]),
|
||||
[PE_SRC_SEND_CAPABILITIES] = SMF_CREATE_STATE(
|
||||
pe_src_send_capabilities_entry,
|
||||
pe_src_send_capabilities_run,
|
||||
NULL,
|
||||
&pe_states[PE_SENDER_RESPONSE_PARENT]),
|
||||
[PE_SRC_NEGOTIATE_CAPABILITY] = SMF_CREATE_STATE(
|
||||
pe_src_negotiate_capability_entry,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL),
|
||||
[PE_SRC_CAPABILITY_RESPONSE] = SMF_CREATE_STATE(
|
||||
pe_src_capability_response_entry,
|
||||
pe_src_capability_response_run,
|
||||
NULL,
|
||||
NULL),
|
||||
[PE_SRC_TRANSITION_SUPPLY] = SMF_CREATE_STATE(
|
||||
pe_src_transition_supply_entry,
|
||||
pe_src_transition_supply_run,
|
||||
pe_src_transition_supply_exit,
|
||||
NULL),
|
||||
[PE_SRC_READY] = SMF_CREATE_STATE(
|
||||
pe_src_ready_entry,
|
||||
pe_src_ready_run,
|
||||
pe_src_ready_exit,
|
||||
NULL),
|
||||
[PE_SRC_TRANSITION_TO_DEFAULT] = SMF_CREATE_STATE(
|
||||
pe_src_transition_to_default_entry,
|
||||
pe_src_transition_to_default_run,
|
||||
pe_src_transition_to_default_exit,
|
||||
NULL),
|
||||
[PE_SRC_HARD_RESET_RECEIVED] = SMF_CREATE_STATE(
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&pe_states[PE_SRC_HARD_RESET_PARENT]),
|
||||
[PE_SRC_HARD_RESET] = SMF_CREATE_STATE(
|
||||
pe_src_hard_reset_entry,
|
||||
NULL,
|
||||
NULL,
|
||||
&pe_states[PE_SRC_HARD_RESET_PARENT]),
|
||||
#endif
|
||||
[PE_GET_SINK_CAP] = SMF_CREATE_STATE(
|
||||
pe_get_sink_cap_entry,
|
||||
pe_get_sink_cap_run,
|
||||
NULL,
|
||||
&pe_states[PE_SENDER_RESPONSE_PARENT]),
|
||||
[PE_SEND_SOFT_RESET] = SMF_CREATE_STATE(
|
||||
pe_send_soft_reset_entry,
|
||||
pe_send_soft_reset_run,
|
||||
|
|
|
@ -14,6 +14,26 @@
|
|||
#include <zephyr/smf.h>
|
||||
#include "usbc_timer.h"
|
||||
|
||||
/**
|
||||
* @brief Used in sub-machines for message transmit and receive operation
|
||||
*/
|
||||
enum sm_msg_xmit {
|
||||
/* Wait for a message transmission sub-machine state */
|
||||
SM_WAIT_FOR_TX,
|
||||
/* Wait for a message reception sub-machine state */
|
||||
SM_WAIT_FOR_RX,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Used in sub-machines for message source hard reset operation
|
||||
*/
|
||||
enum sm_hard_reset {
|
||||
/* Start the hard-reset sub-machine state */
|
||||
SM_HARD_RESET_START,
|
||||
/* Wait for hard-reset to complete sub-machine state */
|
||||
SM_HARD_RESET_WAIT,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Policy Engine Errors
|
||||
*/
|
||||
|
@ -48,18 +68,43 @@ enum usbc_pe_state {
|
|||
PE_SNK_GIVE_SINK_CAP,
|
||||
/** PE_SNK_Get_Source_Cap */
|
||||
PE_SNK_GET_SOURCE_CAP,
|
||||
|
||||
/** PE_SRC_Startup */
|
||||
PE_SRC_STARTUP,
|
||||
/** PE_SRC_Discovery */
|
||||
PE_SRC_DISCOVERY,
|
||||
/** PE_SRC_Send_Capabilities */
|
||||
PE_SRC_SEND_CAPABILITIES,
|
||||
/** PE_SRC_Negotiate_capability */
|
||||
PE_SRC_NEGOTIATE_CAPABILITY,
|
||||
/** PE_SRC_Capability_Response */
|
||||
PE_SRC_CAPABILITY_RESPONSE,
|
||||
/** PE_SRC_Transition_Supply */
|
||||
PE_SRC_TRANSITION_SUPPLY,
|
||||
/** PE_SRC_Ready */
|
||||
PE_SRC_READY,
|
||||
/** PE_SRC_Hard_Reset */
|
||||
PE_SRC_HARD_RESET,
|
||||
/** PE_SRC_Hard_Reset_Received */
|
||||
PE_SRC_HARD_RESET_RECEIVED,
|
||||
/** PE_SRC_Transition_To_Default */
|
||||
PE_SRC_TRANSITION_TO_DEFAULT,
|
||||
|
||||
/** PE_SNK_Soft_Reset and PE_SRC_Soft_Reset */
|
||||
PE_SOFT_RESET,
|
||||
/** PE_SNK_Chunk_Received or PE_SRC_Chunk_Received */
|
||||
PE_CHUNK_RECEIVED,
|
||||
/**PE_Send_Soft_Reset */
|
||||
PE_SEND_SOFT_RESET,
|
||||
/** PE_Soft_Reset */
|
||||
PE_SOFT_RESET,
|
||||
|
||||
/** PE_Send_Not_Supported */
|
||||
PE_SEND_NOT_SUPPORTED,
|
||||
/** PE_DRS_Evaluate_Swap */
|
||||
PE_DRS_EVALUATE_SWAP,
|
||||
/** PE_DRS_Send_Swap */
|
||||
PE_DRS_SEND_SWAP,
|
||||
/** PE_SNK_Chunk_Received */
|
||||
PE_CHUNK_RECEIVED,
|
||||
/** PE_Get_Sink_Cap */
|
||||
PE_GET_SINK_CAP,
|
||||
|
||||
/** PE_Suspend. Not part of the PD specification. */
|
||||
PE_SUSPEND,
|
||||
|
@ -71,7 +116,8 @@ enum usbc_pe_state {
|
|||
|
||||
/** PE_SENDER_RESPONSE_PARENT. Not part of the PD specification. */
|
||||
PE_SENDER_RESPONSE_PARENT,
|
||||
|
||||
/** PE_SRC_HARD_RESET_PARENT. Not part of the PD specification. */
|
||||
PE_SRC_HARD_RESET_PARENT,
|
||||
/** Number of PE States */
|
||||
PE_STATE_COUNT
|
||||
};
|
||||
|
@ -129,6 +175,14 @@ enum pe_flags {
|
|||
/** This flag is set when a transmit error occurs. */
|
||||
PE_FLAGS_MSG_XMIT_ERROR = 14,
|
||||
|
||||
/* The Port Partner is PD connected */
|
||||
PE_FLAGS_PD_CONNECTED = 15,
|
||||
/* The Port partner has been PD connected at least once */
|
||||
PE_FLAGS_HAS_BEEN_PD_CONNECTED = 16,
|
||||
/* A Protocol Error didn't generate a Soft Reset */
|
||||
PE_FLAGS_PROTOCOL_ERROR_NO_SOFT_RESET = 17,
|
||||
/* This flag is set when the first AMS message is sent */
|
||||
PE_FLAGS_FIRST_MSG_SENT = 18,
|
||||
/** Number of PE Flags */
|
||||
PE_FLAGS_COUNT
|
||||
};
|
||||
|
@ -153,7 +207,16 @@ struct policy_engine {
|
|||
enum usbc_policy_request_t dpm_request;
|
||||
/** generic variable used for simple in state statemachines */
|
||||
uint32_t submachine;
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
/** The Sink made a valid request of the Source if true */
|
||||
bool snk_request_can_be_met;
|
||||
/** Outcome of the Sink request */
|
||||
enum usbc_snk_req_reply_t snk_request_reply;
|
||||
/** Save Sink Request Object */
|
||||
uint32_t snk_request;
|
||||
/** Present Contract stores the current Sink Request */
|
||||
uint32_t present_contract;
|
||||
#endif
|
||||
/* Counters */
|
||||
|
||||
/**
|
||||
|
@ -162,20 +225,38 @@ struct policy_engine {
|
|||
*/
|
||||
uint32_t hard_reset_counter;
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
/**
|
||||
* This counter tracks the number of times a Source Caps message was
|
||||
* sent.
|
||||
*/
|
||||
uint32_t caps_counter;
|
||||
#endif
|
||||
|
||||
/* Timers */
|
||||
|
||||
/** tTypeCSinkWaitCap timer */
|
||||
struct usbc_timer_t pd_t_typec_sink_wait_cap;
|
||||
/** tSenderResponse timer */
|
||||
struct usbc_timer_t pd_t_sender_response;
|
||||
/** tPSTransition timer */
|
||||
struct usbc_timer_t pd_t_ps_transition;
|
||||
/** tSinkRequest timer */
|
||||
struct usbc_timer_t pd_t_sink_request;
|
||||
/** tChunkingNotSupported timer */
|
||||
struct usbc_timer_t pd_t_chunking_not_supported;
|
||||
/** Time to wait before resending message after WAIT reception */
|
||||
struct usbc_timer_t pd_t_wait_to_resend;
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
/** tTypeCSinkWaitCap timer */
|
||||
struct usbc_timer_t pd_t_typec_sink_wait_cap;
|
||||
/** tPSTransition timer */
|
||||
struct usbc_timer_t pd_t_ps_transition;
|
||||
/** tSinkRequest timer */
|
||||
struct usbc_timer_t pd_t_sink_request;
|
||||
#else
|
||||
/** tTypeCSendSourceCap timer */
|
||||
struct usbc_timer_t pd_t_typec_send_source_cap;
|
||||
/** tNoResponse timer */
|
||||
struct usbc_timer_t pd_t_no_response;
|
||||
/** tPSHardReset timer */
|
||||
struct usbc_timer_t pd_t_ps_hard_reset;
|
||||
#endif /* CONFIG_USBC_CSM_SINK_ONLY */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -311,6 +392,20 @@ bool policy_wait_notify(const struct device *dev, const enum usbc_policy_wait_t
|
|||
*/
|
||||
void policy_set_src_cap(const struct device *dev, const uint32_t *pdos, const int num_pdos);
|
||||
|
||||
/**
|
||||
* @brief Check if the sink request can be met by the DPM
|
||||
*/
|
||||
enum usbc_snk_req_reply_t policy_check_sink_request(const struct device *dev,
|
||||
const uint32_t request_msg);
|
||||
|
||||
/**
|
||||
* @brief Check if the Present Contract is still valid.
|
||||
*
|
||||
* @note The contract is considered "invalid" if the previous current/voltage
|
||||
* are no longer available AND the sink fails to make a valid request.
|
||||
*/
|
||||
bool policy_present_contract_is_valid(const struct device *dev, const uint32_t present_contract);
|
||||
|
||||
/**
|
||||
* @brief Get a Request Data Object from the DPM
|
||||
*
|
||||
|
@ -336,6 +431,25 @@ bool policy_is_snk_at_default(const struct device *dev);
|
|||
*/
|
||||
void policy_get_snk_cap(const struct device *dev, uint32_t **pdos, int *num_pdos);
|
||||
|
||||
/**
|
||||
* @brief Check if Source Power Supply is ready
|
||||
*/
|
||||
bool policy_is_ps_ready(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Informs the Device Policy Manager that the Sink
|
||||
* is unable to use the current Source Caps and should
|
||||
* should enable a different set of Source Caps. True
|
||||
* is returned if new Source Caps are available, else
|
||||
* false.
|
||||
*/
|
||||
bool policy_change_src_caps(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief End and atomic messaging sequence
|
||||
*/
|
||||
void pe_dpm_end_ams(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Handle common DPM requests
|
||||
*
|
||||
|
|
|
@ -495,6 +495,20 @@ void pe_snk_ready_run(void *obj)
|
|||
sink_dpm_requests(dev);
|
||||
}
|
||||
|
||||
void pe_snk_ready_exit(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* If the Source is initiating an AMS, then notify the
|
||||
* PRL that the first message in an AMS will follow.
|
||||
*/
|
||||
if (pe_dpm_initiated_ams(dev)) {
|
||||
prl_first_msg_notificaiton(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PE_SNK_Hard_Reset Entry State
|
||||
*/
|
||||
|
|
|
@ -60,6 +60,7 @@ void pe_snk_transition_sink_exit(void *obj);
|
|||
*/
|
||||
void pe_snk_ready_entry(void *obj);
|
||||
void pe_snk_ready_run(void *obj);
|
||||
void pe_snk_ready_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SNK_Hard_Reset
|
||||
|
|
697
subsys/usb/usb_c/usbc_pe_src_states.c
Normal file
697
subsys/usb/usb_c/usbc_pe_src_states.c
Normal file
|
@ -0,0 +1,697 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The Chromium OS Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief USB-C Power Policy Engine (PE)
|
||||
*
|
||||
* The information in this file was taken from the USB PD
|
||||
* Specification Revision 3.0, Version 2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/smf.h>
|
||||
#include <zephyr/usb_c/usbc.h>
|
||||
#include <zephyr/drivers/usb_c/usbc_pd.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(usbc_stack, CONFIG_USBC_STACK_LOG_LEVEL);
|
||||
|
||||
#include "usbc_stack.h"
|
||||
|
||||
/**
|
||||
* @brief Initialize the Source Policy Engine layer
|
||||
*/
|
||||
void pe_src_init(const struct device *dev)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
struct policy_engine *pe = data->pe;
|
||||
|
||||
/* Initial role of source is DFP */
|
||||
pe_set_data_role(dev, TC_ROLE_DFP);
|
||||
|
||||
/* Reject Sink Request by default */
|
||||
pe->snk_request_reply = SNK_REQUEST_REJECT;
|
||||
|
||||
/* Initialize timers */
|
||||
usbc_timer_init(&pe->pd_t_typec_send_source_cap, PD_T_TYPEC_SEND_SOURCE_CAP_MIN_MS);
|
||||
usbc_timer_init(&pe->pd_t_ps_hard_reset, PD_T_PS_HARD_RESET_MAX_MS);
|
||||
|
||||
/* Goto startup state */
|
||||
pe_set_state(dev, PE_SRC_STARTUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle source-specific DPM requests
|
||||
*/
|
||||
bool source_dpm_requests(const struct device *dev)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
struct policy_engine *pe = data->pe;
|
||||
|
||||
if (pe->dpm_request == REQUEST_GET_SNK_CAPS) {
|
||||
atomic_set_bit(pe->flags, PE_FLAGS_DPM_INITIATED_AMS);
|
||||
pe_set_state(dev, PE_GET_SINK_CAP);
|
||||
return true;
|
||||
} else if (pe->dpm_request == REQUEST_PE_GOTO_MIN) {
|
||||
atomic_set_bit(pe->flags, PE_FLAGS_DPM_INITIATED_AMS);
|
||||
pe_set_state(dev, PE_SRC_TRANSITION_SUPPLY);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send Source Caps to Sink
|
||||
*/
|
||||
static void send_src_caps(struct policy_engine *pe)
|
||||
{
|
||||
const struct device *dev = pe->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
struct protocol_layer_tx_t *prl_tx = data->prl_tx;
|
||||
struct pd_msg *msg = &prl_tx->emsg;
|
||||
const uint32_t *pdos;
|
||||
uint32_t num_pdos = 0;
|
||||
|
||||
/* This callback must be implemented */
|
||||
__ASSERT(data->policy_cb_get_src_caps != NULL,
|
||||
"Callback pointer should not be NULL");
|
||||
|
||||
data->policy_cb_get_src_caps(dev, &pdos, &num_pdos);
|
||||
|
||||
msg->len = PD_CONVERT_PD_HEADER_COUNT_TO_BYTES(num_pdos);
|
||||
memcpy(msg->data, pdos, msg->len);
|
||||
pe_send_data_msg(dev, PD_PACKET_SOP, PD_DATA_SOURCE_CAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 8.3.3.2.1 PE_SRC_Startup State
|
||||
*/
|
||||
void pe_src_startup_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
LOG_INF("PE_SRC_Startup");
|
||||
|
||||
/* Reset CapsCounter */
|
||||
pe->caps_counter = 0;
|
||||
|
||||
/* Reset the protocol layer */
|
||||
prl_reset(dev);
|
||||
|
||||
/* Set power role to Source */
|
||||
pe->power_role = TC_ROLE_SOURCE;
|
||||
|
||||
/* Invalidate explicit contract */
|
||||
atomic_clear_bit(pe->flags, PE_FLAGS_EXPLICIT_CONTRACT);
|
||||
|
||||
policy_notify(dev, NOT_PD_CONNECTED);
|
||||
}
|
||||
|
||||
void pe_src_startup_run(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* Once the reset process completes, the Policy Engine Shall
|
||||
* transition to the PE_SRC_Send_Capabilities state
|
||||
*/
|
||||
if (prl_is_running(dev)) {
|
||||
pe_set_state(dev, PE_SRC_SEND_CAPABILITIES);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 8.3.3.2.2 PE_SRC_Discovery State
|
||||
*/
|
||||
void pe_src_discovery_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
|
||||
LOG_INF("PE_SRC_Discovery");
|
||||
|
||||
/*
|
||||
* Start the SourceCapabilityTimer in order to trigger sending a
|
||||
* Source_Capabilities message
|
||||
*/
|
||||
usbc_timer_start(&pe->pd_t_typec_send_source_cap);
|
||||
}
|
||||
|
||||
void pe_src_discovery_run(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Send_Capabilities state when:
|
||||
* 1) The SourceCapabilityTimer times out
|
||||
* 2) And CapsCounter ≤ nCapsCount
|
||||
*/
|
||||
if (usbc_timer_expired(&pe->pd_t_typec_send_source_cap)
|
||||
&& pe->caps_counter <= PD_N_CAPS_COUNT) {
|
||||
pe_set_state(dev, PE_SRC_SEND_CAPABILITIES);
|
||||
}
|
||||
}
|
||||
|
||||
void pe_src_discovery_exit(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
|
||||
usbc_timer_stop(&pe->pd_t_typec_send_source_cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 8.3.3.2.3 PE_SRC_Send_Capabilities State
|
||||
*/
|
||||
void pe_src_send_capabilities_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
|
||||
/* Request present source capabilities from Device Policy Manager */
|
||||
send_src_caps(pe);
|
||||
/* Increment CapsCounter */
|
||||
pe->caps_counter++;
|
||||
/* Init submachine */
|
||||
pe->submachine = SM_WAIT_FOR_TX;
|
||||
|
||||
LOG_INF("PE_SRC_Send_Capabilities");
|
||||
}
|
||||
|
||||
void pe_src_send_capabilities_run(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
struct protocol_layer_rx_t *prl_rx = data->prl_rx;
|
||||
|
||||
switch (pe->submachine) {
|
||||
case SM_WAIT_FOR_TX:
|
||||
/*
|
||||
* When message is sent, the Policy Engine Shall:
|
||||
* 1) Stop the NoResponseTimer .
|
||||
* 2) Reset the HardResetCounter and CapsCounter to zero.
|
||||
* 3) Initialize and run the SenderResponseTimer
|
||||
*/
|
||||
if (atomic_test_and_clear_bit(pe->flags, PE_FLAGS_TX_COMPLETE)) {
|
||||
usbc_timer_stop(&pe->pd_t_no_response);
|
||||
pe->hard_reset_counter = 0;
|
||||
pe->caps_counter = 0;
|
||||
pe->submachine = SM_WAIT_FOR_RX;
|
||||
}
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Discovery
|
||||
* state when:
|
||||
* 1) The Protocol Layer indicates that the Message has
|
||||
* not been sent
|
||||
* 2) And we are presently not Connected.
|
||||
*/
|
||||
else if ((atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_XMIT_ERROR) ||
|
||||
atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_DISCARDED))
|
||||
&& (atomic_test_bit(pe->flags, PE_FLAGS_PD_CONNECTED) == false)) {
|
||||
pe_set_state(dev, PE_SRC_DISCOVERY);
|
||||
}
|
||||
break;
|
||||
case SM_WAIT_FOR_RX:
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Negotiate_Capability state when:
|
||||
* 1) A Request Message is received from the Sink.
|
||||
*/
|
||||
if (atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_RECEIVED)) {
|
||||
union pd_header header = prl_rx->emsg.header;
|
||||
|
||||
if (received_data_message(dev, header, PD_DATA_REQUEST)) {
|
||||
/* Set to highest revision supported by both ports */
|
||||
prl_set_rev(dev, PD_PACKET_SOP,
|
||||
MIN(PD_REV30, header.specification_revision));
|
||||
pe_set_state(dev, PE_SRC_NEGOTIATE_CAPABILITY);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Hard_Reset
|
||||
* state when:
|
||||
* 1) The SenderResponseTimer times out
|
||||
*/
|
||||
else if (usbc_timer_expired(&pe->pd_t_sender_response)) {
|
||||
pe_set_state(dev, PE_SRC_HARD_RESET);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 8.3.3.2.4 PE_SRC_Negotiate_Capability State
|
||||
*/
|
||||
void pe_src_negotiate_capability_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
struct protocol_layer_rx_t *prl_rx = data->prl_rx;
|
||||
|
||||
LOG_INF("PE_SRC_Negotiate_Capability");
|
||||
|
||||
/* Get sink request */
|
||||
pe->snk_request = *(uint32_t *)prl_rx->emsg.data;
|
||||
|
||||
/*
|
||||
* Ask the Device Policy Manager to evaluate the Request
|
||||
* from the Attached Sink.
|
||||
*/
|
||||
pe->snk_request_reply =
|
||||
policy_check_sink_request(dev, pe->snk_request);
|
||||
|
||||
/*
|
||||
* The Policy Engine Shall transition to the
|
||||
* PE_SRC_Transition_Supply state when:
|
||||
* 1) The Request can be met.
|
||||
*/
|
||||
if (pe->snk_request_reply == SNK_REQUEST_VALID) {
|
||||
pe_set_state(dev, PE_SRC_TRANSITION_SUPPLY);
|
||||
}
|
||||
/*
|
||||
* The Policy Engine Shall transition to the
|
||||
* PE_SRC_Capability_Response state when:
|
||||
* 1) The Request cannot be met.
|
||||
* 2) Or the Request can be met later from the Power Reserve.
|
||||
*/
|
||||
else {
|
||||
pe_set_state(dev, PE_SRC_CAPABILITY_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 8.3.3.2.5 PE_SRC_Transition_Supply State
|
||||
*/
|
||||
void pe_src_transition_supply_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
LOG_INF("PE_SRC_Transition_Supply");
|
||||
|
||||
/*
|
||||
* If snk_request_reply is set, this state was entered
|
||||
* from PE_SRC_Negotiate_Capability. So send Accept Message
|
||||
* and inform the Device Policy Manager that it Shall transition
|
||||
* the power supply to the Requested power level.
|
||||
*/
|
||||
if (pe->snk_request_reply == SNK_REQUEST_VALID) {
|
||||
pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_ACCEPT);
|
||||
policy_notify(dev, TRANSITION_PS);
|
||||
}
|
||||
/*
|
||||
* If snk_request_reply is not valid, this state was entered
|
||||
* from PE_SRC_Ready. So send GotoMin Message.
|
||||
*/
|
||||
else {
|
||||
pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_GOTO_MIN);
|
||||
}
|
||||
}
|
||||
|
||||
void pe_src_transition_supply_run(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Ready state when:
|
||||
* 1) The Device Policy Manager informs the Policy Engine that
|
||||
* the power supply is ready.
|
||||
*/
|
||||
if (atomic_test_bit(pe->flags, PE_FLAGS_TX_COMPLETE)) {
|
||||
if (policy_is_ps_ready(dev)) {
|
||||
pe_set_state(dev, PE_SRC_READY);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Hard_Reset
|
||||
* state when:
|
||||
* 1) A Protocol Error occurs.
|
||||
*/
|
||||
else if (atomic_test_bit(pe->flags, PE_FLAGS_PROTOCOL_ERROR)) {
|
||||
pe_set_state(dev, PE_SRC_HARD_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
void pe_src_transition_supply_exit(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/* Send PS_RDY message */
|
||||
if (pe->snk_request_reply == SNK_REQUEST_VALID) {
|
||||
/* Clear request reply and reject by default */
|
||||
pe->snk_request_reply = SNK_REQUEST_REJECT;
|
||||
/* Send PS Ready */
|
||||
pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_PS_RDY);
|
||||
/* Explicit Contract is now in place */
|
||||
atomic_set_bit(pe->flags, PE_FLAGS_EXPLICIT_CONTRACT);
|
||||
/* Update present contract */
|
||||
pe->present_contract = pe->snk_request;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 8.3.3.2.6 PE_SRC_Ready State
|
||||
*/
|
||||
void pe_src_ready_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
LOG_INF("PE_SRC_Ready");
|
||||
|
||||
/*
|
||||
* If the transition into PE_SRC_Ready is the result of Protocol Error
|
||||
* that has not caused a Soft Reset then the notification to the
|
||||
* Protocol Layer of the end of the AMS Shall Not be sent since there
|
||||
* is a Message to be processed.
|
||||
*
|
||||
* Else on entry to the PE_SRC_Ready state the Source Shall notify the
|
||||
* Protocol Layer of the end of the Atomic Message Sequence (AMS).
|
||||
*/
|
||||
if (atomic_test_and_clear_bit(pe->flags,
|
||||
PE_FLAGS_PROTOCOL_ERROR_NO_SOFT_RESET)) {
|
||||
pe_dpm_end_ams(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void pe_src_ready_run(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
struct protocol_layer_rx_t *prl_rx = data->prl_rx;
|
||||
|
||||
/* Handle incoming messages */
|
||||
if (atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_RECEIVED)) {
|
||||
union pd_header header = prl_rx->emsg.header;
|
||||
|
||||
/*
|
||||
* Extended Message Requests
|
||||
*/
|
||||
if (header.extended) {
|
||||
extended_message_not_supported(dev);
|
||||
}
|
||||
/*
|
||||
* Data Message Requests
|
||||
*/
|
||||
else if (header.number_of_data_objects > 0) {
|
||||
switch (header.message_type) {
|
||||
case PD_DATA_REQUEST:
|
||||
pe_set_state(dev, PE_SRC_NEGOTIATE_CAPABILITY);
|
||||
break;
|
||||
default:
|
||||
pe_set_state(dev, PE_SEND_NOT_SUPPORTED);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Control Message Requests
|
||||
*/
|
||||
else {
|
||||
switch (header.message_type) {
|
||||
case PD_CTRL_GOOD_CRC:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case PD_CTRL_NOT_SUPPORTED:
|
||||
/* Notify DPM */
|
||||
policy_notify(dev, MSG_NOT_SUPPORTED_RECEIVED);
|
||||
break;
|
||||
case PD_CTRL_PING:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case PD_CTRL_GET_SOURCE_CAP:
|
||||
pe_set_state(dev, PE_SRC_SEND_CAPABILITIES);
|
||||
break;
|
||||
case PD_CTRL_DR_SWAP:
|
||||
pe_set_state(dev, PE_DRS_EVALUATE_SWAP);
|
||||
break;
|
||||
/*
|
||||
* USB PD 3.0 6.8.1:
|
||||
* Receiving an unexpected message shall be responded
|
||||
* to with a soft reset message.
|
||||
*/
|
||||
case PD_CTRL_ACCEPT:
|
||||
case PD_CTRL_REJECT:
|
||||
case PD_CTRL_WAIT:
|
||||
case PD_CTRL_PS_RDY:
|
||||
pe_send_soft_reset(dev, prl_rx->emsg.type);
|
||||
break;
|
||||
/*
|
||||
* Receiving an unknown or unsupported message
|
||||
* shall be responded to with a not supported
|
||||
* message.
|
||||
*/
|
||||
default:
|
||||
pe_set_state(dev, PE_SEND_NOT_SUPPORTED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Handle Source DPManager Requests */
|
||||
source_dpm_requests(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void pe_src_ready_exit(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* If the Source is initiating an AMS, then notify the
|
||||
* PRL that the first message in an AMS will follow.
|
||||
*/
|
||||
if (pe_dpm_initiated_ams(dev)) {
|
||||
prl_first_msg_notificaiton(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 8.3.3.2.11 PE_SRC_Transition_to_default State
|
||||
*/
|
||||
void pe_src_transition_to_default_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* On entry to the PE_SRC_Transition_to_default state the
|
||||
* Policy Engine Shall:
|
||||
* 1: indicate to the Device Policy Manager that the power
|
||||
* supply Shall Hard Reset
|
||||
* 2: request a reset of the local hardware
|
||||
* 3: request the Device Policy Manager to set the Port
|
||||
* Data Role to DFP and turn off VCONN.
|
||||
*
|
||||
* NOTE: 1, 2 and VCONN off are done by Device Policy Manager when
|
||||
* it receives the HARD_RESET_RECEIVED notification.
|
||||
*/
|
||||
policy_notify(dev, HARD_RESET_RECEIVED);
|
||||
pe->data_role = TC_ROLE_DFP;
|
||||
policy_notify(dev, DATA_ROLE_IS_DFP);
|
||||
}
|
||||
|
||||
void pe_src_transition_to_default_run(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Startup
|
||||
* state when:
|
||||
* 1: The Device Policy Manager indicates that the power
|
||||
* supply has reached the default level.
|
||||
*/
|
||||
if (policy_check(dev, CHECK_SRC_PS_AT_DEFAULT_LEVEL)) {
|
||||
pe_set_state(dev, PE_SRC_STARTUP);
|
||||
}
|
||||
}
|
||||
|
||||
void pe_src_transition_to_default_exit(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* On exit from the PE_SRC_Transition_to_default state the
|
||||
* Policy Engine Shall:
|
||||
* 1: request the Device Policy Manager to turn on VCONN
|
||||
* 2: inform the Protocol Layer that the Hard Reset is complete.
|
||||
*
|
||||
* NOTE: The Device Policy Manager turns on VCONN when it notifies the
|
||||
* PE that the Power Supply is at the default level.
|
||||
*/
|
||||
prl_hard_reset_complete(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* 8.3.3.2.8 PE_SRC_Capability_Response State
|
||||
*/
|
||||
void pe_src_capability_response_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* On entry to the PE_SRC_Capability_Response state the Policy Engine
|
||||
* Shall request the Protocol Layer to send one of the following:
|
||||
*/
|
||||
|
||||
/*
|
||||
* 1: Reject Message – if the request cannot be met or the present
|
||||
* Contract is Invalid.
|
||||
*/
|
||||
if (pe->snk_request_reply == SNK_REQUEST_REJECT) {
|
||||
pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_REJECT);
|
||||
}
|
||||
/*
|
||||
* 2: Wait Message – if the request could be met later from the Power
|
||||
* Reserve. A Wait Message Shall Not be sent if the present Contract
|
||||
* is Invalid.
|
||||
*/
|
||||
else {
|
||||
pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
void pe_src_capability_response_run(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/* Wait until message has been sent */
|
||||
if (!atomic_test_and_clear_bit(pe->flags, PE_FLAGS_TX_COMPLETE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Ready state when:
|
||||
* 1: There is an Explicit Contract AND
|
||||
* 2: A Reject Message has been sent and the present Contract
|
||||
* is still Valid OR
|
||||
* 3: A Wait Message has been sent.
|
||||
*/
|
||||
if (atomic_test_bit(pe->flags, PE_FLAGS_EXPLICIT_CONTRACT) &&
|
||||
((pe->snk_request_reply == SNK_REQUEST_REJECT &&
|
||||
policy_present_contract_is_valid(dev, pe->present_contract)) ||
|
||||
(pe->snk_request_reply == SNK_REQUEST_WAIT))) {
|
||||
pe_set_state(dev, PE_SRC_READY);
|
||||
}
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Hard_Reset state
|
||||
* when:
|
||||
* 1: There is an Explicit Contract and
|
||||
* 2: The Reject Message has been sent and the present Contract
|
||||
* is Invalid
|
||||
*/
|
||||
else if (atomic_test_bit(pe->flags, PE_FLAGS_EXPLICIT_CONTRACT) &&
|
||||
policy_present_contract_is_valid(dev, pe->present_contract) == false) {
|
||||
pe_set_state(dev, PE_SRC_HARD_RESET);
|
||||
}
|
||||
/*
|
||||
* The Policy Engine Shall transition to the PE_SRC_Wait_New_Capabilities
|
||||
* state when:
|
||||
* 1: There is no Explicit Contract and
|
||||
* 2: A Reject Message has been sent or
|
||||
* 3: A Wait Message has been sent.
|
||||
*/
|
||||
else {
|
||||
/* 8.3.3.2.13 PE_SRC_Wait_New_Capabilities embedded here */
|
||||
|
||||
/*
|
||||
* In the PE_SRC_Wait_New_Capabilities State the Device Policy Manager
|
||||
* Should either decide to send no further Source Capabilities or
|
||||
* Should send a different set of Source Capabilities. Continuing
|
||||
* to send the same set of Source Capabilities could result in a live
|
||||
* lock situation.
|
||||
*/
|
||||
|
||||
/* Notify DPM to send a different set of Source Capabilities */
|
||||
if (policy_change_src_caps(dev)) {
|
||||
/* DPM will send different set of Source Capabilities */
|
||||
pe_set_state(dev, PE_SRC_SEND_CAPABILITIES);
|
||||
} else {
|
||||
/*
|
||||
* DPM can not send a different set of Source
|
||||
* Capabilities, so disable port.
|
||||
*/
|
||||
pe_set_state(dev, PE_SUSPEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pe_src_hard_reset_parent_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
|
||||
pe->submachine = SM_HARD_RESET_START;
|
||||
}
|
||||
|
||||
void pe_src_hard_reset_parent_run(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
switch (pe->submachine) {
|
||||
case SM_HARD_RESET_START:
|
||||
/*
|
||||
* Initialize and run the NoResponseTimer.
|
||||
* Note that the NoResponseTimer Shall continue to run
|
||||
* in every state until it is stopped or times out.
|
||||
*/
|
||||
usbc_timer_start(&pe->pd_t_no_response);
|
||||
|
||||
/* Initialize and run the PSHardResetTimer */
|
||||
usbc_timer_start(&pe->pd_t_ps_hard_reset);
|
||||
|
||||
pe->submachine = SM_HARD_RESET_WAIT;
|
||||
break;
|
||||
case SM_HARD_RESET_WAIT:
|
||||
/*
|
||||
* The Policy Engine Shall transition to the
|
||||
* PE_SRC_Transition_to_default state when:
|
||||
* The PSHardResetTimer times out.
|
||||
*/
|
||||
if (usbc_timer_expired(&pe->pd_t_ps_hard_reset)) {
|
||||
pe_set_state(dev, PE_SRC_TRANSITION_TO_DEFAULT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pe_src_hard_reset_parent_exit(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
|
||||
/* Stop the Hard Reset Timer */
|
||||
usbc_timer_stop(&pe->pd_t_ps_hard_reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 8.3.3.2.9 PE_SRC_Hard_Reset State
|
||||
*/
|
||||
void pe_src_hard_reset_entry(void *obj)
|
||||
{
|
||||
struct policy_engine *pe = (struct policy_engine *)obj;
|
||||
const struct device *dev = pe->dev;
|
||||
|
||||
/*
|
||||
* On entry to the PE_SRC_Hard_Reset state the
|
||||
* Policy Engine Shall:
|
||||
*/
|
||||
|
||||
/*
|
||||
* Request the generation of Hard Reset Signaling by
|
||||
* the PHY Layer
|
||||
*/
|
||||
prl_execute_hard_reset(dev);
|
||||
|
||||
}
|
98
subsys/usb/usb_c/usbc_pe_src_states_internal.h
Normal file
98
subsys/usb/usb_c/usbc_pe_src_states_internal.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The Chromium OS Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_SUBSYS_USBC_PE_SRC_STATES_INTERNAL_H_
|
||||
#define ZEPHYR_SUBSYS_USBC_PE_SRC_STATES_INTERNAL_H_
|
||||
|
||||
/**
|
||||
* @brief Init the PE Source State machine
|
||||
*/
|
||||
void pe_src_init(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Handle Source-specific DPM requests
|
||||
*/
|
||||
bool source_dpm_requests(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Startup State
|
||||
*/
|
||||
void pe_src_startup_entry(void *obj);
|
||||
void pe_src_startup_run(void *obj);
|
||||
void pe_src_startup_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Discovery State
|
||||
*/
|
||||
void pe_src_discovery_entry(void *obj);
|
||||
void pe_src_discovery_run(void *obj);
|
||||
void pe_src_discovery_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Send_Capabilities State
|
||||
*/
|
||||
void pe_src_send_capabilities_entry(void *obj);
|
||||
void pe_src_send_capabilities_run(void *obj);
|
||||
void pe_src_send_capabilities_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Negotiate_Capability State
|
||||
*/
|
||||
void pe_src_negotiate_capability_entry(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Transition_Supply State
|
||||
*/
|
||||
void pe_src_transition_supply_entry(void *obj);
|
||||
void pe_src_transition_supply_run(void *obj);
|
||||
void pe_src_transition_supply_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Ready State
|
||||
*/
|
||||
void pe_src_ready_entry(void *obj);
|
||||
void pe_src_ready_run(void *obj);
|
||||
void pe_src_ready_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Transition_To_Default State
|
||||
*/
|
||||
void pe_src_transition_to_default_entry(void *obj);
|
||||
void pe_src_transition_to_default_run(void *obj);
|
||||
void pe_src_transition_to_default_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Hard_Reset State
|
||||
*/
|
||||
void pe_src_hard_reset_entry(void *obj);
|
||||
void pe_src_hard_reset_run(void *obj);
|
||||
void pe_src_hard_reset_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Capability_Response State
|
||||
*/
|
||||
void pe_src_capability_response_entry(void *obj);
|
||||
void pe_src_capability_response_run(void *obj);
|
||||
void pe_src_capability_response_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Wait_New_Capabilities State
|
||||
*/
|
||||
void pe_src_wait_new_capabilities_entry(void *obj);
|
||||
void pe_src_wait_new_capabilities_run(void *obj);
|
||||
void pe_src_wait_new_capabilities_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief PE_SRC_Hard_Reset_Parent State
|
||||
*
|
||||
* Parent state of PE_SRC_Hard_Reset and
|
||||
* PE_SRC_Hard_Reset_Received States
|
||||
*/
|
||||
void pe_src_hard_reset_parent_entry(void *obj);
|
||||
void pe_src_hard_reset_parent_run(void *obj);
|
||||
void pe_src_hard_reset_parent_exit(void *obj);
|
||||
|
||||
#endif /* ZEPHYR_SUBSYS_USBC_PE_SRC_STATES_INTERNAL_H_ */
|
|
@ -49,6 +49,8 @@ enum prl_flags {
|
|||
PRL_FLAGS_MSG_XMIT = 7,
|
||||
/** Flag to track if first message in AMS is pending */
|
||||
PRL_FLAGS_FIRST_MSG_PENDING = 8,
|
||||
/* Flag to note that PRL requested to set SINK_NG CC state */
|
||||
PRL_FLAGS_SINK_NG = 9,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -69,6 +71,10 @@ enum usbc_prl_tx_state_t {
|
|||
PRL_TX_SNK_PENDING,
|
||||
/** PRL_Tx_Discard_Message */
|
||||
PRL_TX_DISCARD_MESSAGE,
|
||||
/** PRL_TX_SRC_Source_Tx */
|
||||
PRL_TX_SRC_SOURCE_TX,
|
||||
/** PRL_TX_SRC_Pending */
|
||||
PRL_TX_SRC_PENDING,
|
||||
|
||||
/** PRL_Tx_Suspend. Not part of the PD specification. */
|
||||
PRL_TX_SUSPEND,
|
||||
|
@ -550,6 +556,7 @@ static void prl_init(const struct device *dev)
|
|||
prl_tx->msg_id_counter[i] = 0;
|
||||
}
|
||||
usbc_timer_init(&prl_tx->pd_t_tx_timeout, PD_T_TX_TIMEOUT_MS);
|
||||
usbc_timer_init(&prl_tx->pd_t_sink_tx, PD_T_SINK_TX_MAX_MS);
|
||||
prl_tx_set_state(dev, PRL_TX_PHY_LAYER_RESET);
|
||||
|
||||
/* Initialize the PRL_RX state machine */
|
||||
|
@ -602,6 +609,12 @@ static void prl_tx_wait_for_message_request_run(void *obj)
|
|||
|
||||
/* Clear any AMS flags and state if we are no longer in an AMS */
|
||||
if (pe_dpm_initiated_ams(dev) == false) {
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
/* Note PRL_Tx_Src_Sink_Tx is embedded here. */
|
||||
if (atomic_test_and_clear_bit(&prl_tx->flags, PRL_FLAGS_SINK_NG)) {
|
||||
tc_select_src_collision_rp(dev, SINK_TX_OK);
|
||||
}
|
||||
#endif
|
||||
atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_WAIT_SINK_OK);
|
||||
}
|
||||
|
||||
|
@ -610,24 +623,33 @@ static void prl_tx_wait_for_message_request_run(void *obj)
|
|||
* lines appropriately.
|
||||
*/
|
||||
if (data->rev[PD_PACKET_SOP] == PD_REV30 && pe_dpm_initiated_ams(dev)) {
|
||||
if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_WAIT_SINK_OK)) {
|
||||
if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_WAIT_SINK_OK) ||
|
||||
atomic_test_bit(&prl_tx->flags, PRL_FLAGS_SINK_NG)) {
|
||||
/*
|
||||
* If we are already in an AMS then allow the
|
||||
* multi-message AMS to continue.
|
||||
*
|
||||
* Fall Through using the current AMS
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
* Start of SNK AMS notification received from
|
||||
* Start of AMS notification received from
|
||||
* Policy Engine
|
||||
*/
|
||||
atomic_set_bit(&prl_tx->flags, PRL_FLAGS_WAIT_SINK_OK);
|
||||
prl_tx_set_state(dev, PRL_TX_SNK_START_AMS);
|
||||
if (IS_ENABLED(CONFIG_USBC_CSM_SOURCE_ONLY) &&
|
||||
pe_get_power_role(dev) == TC_ROLE_SOURCE) {
|
||||
atomic_set_bit(&prl_tx->flags, PRL_FLAGS_SINK_NG);
|
||||
prl_tx_set_state(dev, PRL_TX_SRC_SOURCE_TX);
|
||||
} else {
|
||||
atomic_set_bit(&prl_tx->flags, PRL_FLAGS_WAIT_SINK_OK);
|
||||
prl_tx_set_state(dev, PRL_TX_SNK_START_AMS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle non Rev 3.0 or subsequent messages in AMS sequence */
|
||||
if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT)) {
|
||||
if (atomic_test_and_clear_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT)) {
|
||||
/*
|
||||
* Soft Reset Message pending
|
||||
*/
|
||||
|
@ -744,6 +766,36 @@ static void prl_tx_wait_for_phy_response_exit(void *obj)
|
|||
increment_msgid_counter(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
/**
|
||||
* @brief 6.11.2.2.2.1 PRL_Tx_Src_Source_Tx
|
||||
*/
|
||||
static void prl_tx_src_source_tx_entry(void *obj)
|
||||
{
|
||||
struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj;
|
||||
const struct device *dev = prl_tx->dev;
|
||||
|
||||
LOG_INF("PRL_Tx_Src_Tx");
|
||||
|
||||
/* Set Rp = SinkTxNG */
|
||||
tc_select_src_collision_rp(dev, SINK_TX_NG);
|
||||
}
|
||||
|
||||
static void prl_tx_src_source_tx_run(void *obj)
|
||||
{
|
||||
struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj;
|
||||
const struct device *dev = prl_tx->dev;
|
||||
|
||||
if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT)) {
|
||||
/*
|
||||
* Don't clear pending XMIT flag here. Wait until we send so
|
||||
* we can detect if we dropped this message or not.
|
||||
*/
|
||||
prl_tx_set_state(dev, PRL_TX_SRC_PENDING);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_USBC_CSM_SINK_ONLY
|
||||
/**
|
||||
* @brief PRL_Tx_Snk_Start_of_AMS Entry State
|
||||
*/
|
||||
|
@ -768,7 +820,67 @@ static void prl_tx_snk_start_ams_run(void *obj)
|
|||
prl_tx_set_state(dev, PRL_TX_SNK_PENDING);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
/**
|
||||
* @brief PRL_Tx_Src_Pending Entry State
|
||||
*/
|
||||
static void prl_tx_src_pending_entry(void *obj)
|
||||
{
|
||||
struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj;
|
||||
|
||||
LOG_INF("PRL_Tx_Src_Pending");
|
||||
|
||||
/* Start SinkTxTimer */
|
||||
usbc_timer_start(&prl_tx->pd_t_sink_tx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PRL_Tx_Src_Pending Run State
|
||||
*/
|
||||
static void prl_tx_src_pending_run(void *obj)
|
||||
{
|
||||
struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj;
|
||||
const struct device *dev = prl_tx->dev;
|
||||
|
||||
if (usbc_timer_expired(&prl_tx->pd_t_sink_tx)) {
|
||||
/*
|
||||
* We clear the pending XMIT flag here right before we send so
|
||||
* we can detect if we discarded this message or not
|
||||
*/
|
||||
atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT);
|
||||
|
||||
/* Soft Reset Message pending & SinkTxTimer timeout */
|
||||
if ((prl_tx->msg_type == PD_CTRL_SOFT_RESET) && (prl_tx->emsg.len == 0)) {
|
||||
prl_tx_set_state(dev, PRL_TX_LAYER_RESET_FOR_TRANSMIT);
|
||||
}
|
||||
/* Message pending (except Soft Reset) & SinkTxTimer timeout */
|
||||
else {
|
||||
/* If this is the first AMS message, inform the PE that it's been sent */
|
||||
if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_FIRST_MSG_PENDING)) {
|
||||
atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_FIRST_MSG_PENDING);
|
||||
pe_first_msg_sent(dev);
|
||||
}
|
||||
|
||||
prl_tx_construct_message(dev);
|
||||
prl_tx_set_state(dev, PRL_TX_WAIT_FOR_PHY_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PRL_Tx_Src_Pending Exit State
|
||||
*/
|
||||
static void prl_tx_src_pending_exit(void *obj)
|
||||
{
|
||||
struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj;
|
||||
|
||||
/* Stop SinkTxTimer */
|
||||
usbc_timer_stop(&prl_tx->pd_t_sink_tx);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
/**
|
||||
* @brief PRL_Tx_Snk_Pending Entry State
|
||||
*/
|
||||
|
@ -808,6 +920,12 @@ static void prl_tx_snk_pending_run(void *obj)
|
|||
if ((prl_tx->msg_type == PD_CTRL_SOFT_RESET) && (prl_tx->emsg.len == 0)) {
|
||||
prl_tx_set_state(dev, PRL_TX_LAYER_RESET_FOR_TRANSMIT);
|
||||
} else if (cc1 == TC_CC_VOLT_RP_3A0 || cc2 == TC_CC_VOLT_RP_3A0) {
|
||||
/* If this is the first AMS message, inform the PE that it's been sent */
|
||||
if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_FIRST_MSG_PENDING)) {
|
||||
atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_FIRST_MSG_PENDING);
|
||||
pe_first_msg_sent(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* The Protocol Layer Shall transition to the PRL_Tx_Construct_Message
|
||||
* state when Rp is set to SinkTxOk and a Soft_Reset Message is not
|
||||
|
@ -822,6 +940,7 @@ static void prl_tx_snk_pending_run(void *obj)
|
|||
prl_tx_set_state(dev, PRL_TX_WAIT_FOR_PHY_RESPONSE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void prl_tx_suspend_entry(void *obj)
|
||||
{
|
||||
|
@ -1162,6 +1281,12 @@ static const struct smf_state prl_tx_states[PRL_TX_STATE_COUNT] = {
|
|||
prl_tx_wait_for_phy_response_run,
|
||||
prl_tx_wait_for_phy_response_exit,
|
||||
NULL),
|
||||
[PRL_TX_SUSPEND] = SMF_CREATE_STATE(
|
||||
prl_tx_suspend_entry,
|
||||
prl_tx_suspend_run,
|
||||
NULL,
|
||||
NULL),
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
[PRL_TX_SNK_START_AMS] = SMF_CREATE_STATE(
|
||||
prl_tx_snk_start_ams_entry,
|
||||
prl_tx_snk_start_ams_run,
|
||||
|
@ -1172,11 +1297,19 @@ static const struct smf_state prl_tx_states[PRL_TX_STATE_COUNT] = {
|
|||
prl_tx_snk_pending_run,
|
||||
NULL,
|
||||
NULL),
|
||||
[PRL_TX_SUSPEND] = SMF_CREATE_STATE(
|
||||
prl_tx_suspend_entry,
|
||||
prl_tx_suspend_run,
|
||||
#endif
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
[PRL_TX_SRC_SOURCE_TX] = SMF_CREATE_STATE(
|
||||
prl_tx_src_source_tx_entry,
|
||||
prl_tx_src_source_tx_run,
|
||||
NULL,
|
||||
NULL),
|
||||
[PRL_TX_SRC_PENDING] = SMF_CREATE_STATE(
|
||||
prl_tx_src_pending_entry,
|
||||
prl_tx_src_pending_run,
|
||||
prl_tx_src_pending_exit,
|
||||
NULL),
|
||||
#endif
|
||||
};
|
||||
BUILD_ASSERT(ARRAY_SIZE(prl_tx_states) == PRL_TX_STATE_COUNT);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The Chromium OS Authors
|
||||
* Copyright (c) 2023 The Chromium OS Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -63,6 +63,8 @@ struct protocol_layer_tx_t {
|
|||
|
||||
/** tTxTimeout timer */
|
||||
struct usbc_timer_t pd_t_tx_timeout;
|
||||
/** tSinkTx timer */
|
||||
struct usbc_timer_t pd_t_sink_tx;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -153,6 +153,7 @@ void *usbc_get_dpm_data(const struct device *dev)
|
|||
return data->dpm_data;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
/**
|
||||
* @brief Set the callback that gets the Sink Capabilities from the
|
||||
* Device Policy Manager
|
||||
|
@ -177,6 +178,151 @@ void usbc_set_policy_cb_set_src_cap(const struct device *dev,
|
|||
data->policy_cb_set_src_cap = policy_cb_set_src_cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback for requesting the data object (RDO)
|
||||
*/
|
||||
void usbc_set_policy_cb_get_rdo(const struct device *dev,
|
||||
const policy_cb_get_rdo_t policy_cb_get_rdo)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_cb_get_rdo = policy_cb_get_rdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback for checking if Sink Power Supply is at
|
||||
* default level
|
||||
*/
|
||||
void usbc_set_policy_cb_is_snk_at_default(const struct device *dev,
|
||||
const policy_cb_is_snk_at_default_t policy_cb_is_snk_at_default)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_cb_is_snk_at_default = policy_cb_is_snk_at_default;
|
||||
}
|
||||
|
||||
#else /* CONFIG_USBC_CSM_SOURCE_ONLY */
|
||||
|
||||
/**
|
||||
* @brief Set the callback for sending the Sink Caps to the DPM
|
||||
*/
|
||||
void usbc_set_policy_cb_set_port_partner_snk_cap(const struct device *dev,
|
||||
const policy_cb_set_port_partner_snk_cap_t cb)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_cb_set_port_partner_snk_cap = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback that gets the Source Capabilities from the
|
||||
* Device Policy Manager
|
||||
*/
|
||||
void usbc_set_policy_cb_get_src_caps(const struct device *dev,
|
||||
const policy_cb_get_src_caps_t cb)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_cb_get_src_caps = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback that gets the Source Rp value from the
|
||||
* Device Policy Manager
|
||||
*/
|
||||
void usbc_set_policy_cb_get_src_rp(const struct device *dev,
|
||||
const policy_cb_get_src_rp_t policy_cb_get_src_rp)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_cb_get_src_rp = policy_cb_get_src_rp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback that controls the sourcing of VBUS from the
|
||||
* Device Policy Manager
|
||||
*/
|
||||
void usbc_set_policy_cb_src_en(const struct device *dev,
|
||||
const policy_cb_src_en_t policy_cb_src_en)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_cb_src_en = policy_cb_src_en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback for checking if a Sink Request is valid
|
||||
*/
|
||||
void usbc_set_policy_cb_check_sink_request(const struct device *dev,
|
||||
const policy_cb_check_sink_request_t cb)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_cb_check_sink_request = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback for checking if the Source Power Supply is ready
|
||||
*/
|
||||
void usbc_set_policy_cb_is_ps_ready(const struct device *dev,
|
||||
const policy_cb_is_ps_ready_t cb)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_is_ps_ready = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback for checking if the Present Contract is still valid
|
||||
*/
|
||||
void usbc_set_policy_cb_present_contract_is_valid(const struct device *dev,
|
||||
const policy_cb_present_contract_is_valid_t cb)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_present_contract_is_valid = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback that requests the use of a new set of Sources Caps if
|
||||
* they're available
|
||||
*/
|
||||
void usbc_set_policy_cb_change_src_caps(const struct device *dev,
|
||||
const policy_cb_change_src_caps_t cb)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_change_src_caps = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback that controls the sourcing of VCONN from the
|
||||
* Device Policy Manager
|
||||
*/
|
||||
void usbc_set_vconn_control_cb(const struct device *dev,
|
||||
const tcpc_vconn_control_cb_t cb)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
const struct device *tcpc = data->tcpc;
|
||||
|
||||
tcpc_set_vconn_cb(tcpc, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback that discharges VCONN from the
|
||||
* Device Policy Manager
|
||||
*/
|
||||
void usbc_set_vconn_discharge(const struct device *dev,
|
||||
const tcpc_vconn_discharge_cb_t cb)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
const struct device *tcpc = data->tcpc;
|
||||
|
||||
tcpc_set_vconn_discharge_cb(tcpc, cb);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USBC_CSM_SINK_ONLY */
|
||||
|
||||
/**
|
||||
* @brief Set the callback for the Device Policy Manager policy check
|
||||
*/
|
||||
|
@ -208,29 +354,6 @@ void usbc_set_policy_cb_wait_notify(const struct device *dev,
|
|||
data->policy_cb_wait_notify = policy_cb_wait_notify;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback for requesting the data object (RDO)
|
||||
*/
|
||||
void usbc_set_policy_cb_get_rdo(const struct device *dev,
|
||||
const policy_cb_get_rdo_t policy_cb_get_rdo)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_cb_get_rdo = policy_cb_get_rdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback for checking if Sink Power Supply is at
|
||||
* default level
|
||||
*/
|
||||
void usbc_set_policy_cb_is_snk_at_default(
|
||||
const struct device *dev, const policy_cb_is_snk_at_default_t policy_cb_is_snk_at_default)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
|
||||
data->policy_cb_is_snk_at_default = policy_cb_is_snk_at_default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the USB-C Subsystem
|
||||
*/
|
||||
|
|
|
@ -127,6 +127,8 @@ struct usbc_port_data {
|
|||
*/
|
||||
bool (*policy_cb_wait_notify)(const struct device *dev,
|
||||
const enum usbc_policy_wait_t policy_notify);
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
/**
|
||||
* Callback used by the Policy Engine to get the Sink Capabilities
|
||||
* from the Device Policy Manager
|
||||
|
@ -150,7 +152,61 @@ struct usbc_port_data {
|
|||
* is at default level
|
||||
*/
|
||||
bool (*policy_cb_is_snk_at_default)(const struct device *dev);
|
||||
#else /* CONFIG_USBC_CSM_SOURCE_ONLY */
|
||||
/**
|
||||
* Callback used by the Policy Engine get the Rp pull-up that should
|
||||
* be placed on the CC lines
|
||||
*/
|
||||
int (*policy_cb_get_src_rp)(const struct device *dev,
|
||||
enum tc_rp_value *rp);
|
||||
|
||||
/**
|
||||
* Callback used by the Policy Engine to enable and disable the
|
||||
* Source Power Supply
|
||||
*/
|
||||
int (*policy_cb_src_en)(const struct device *dev, bool en);
|
||||
|
||||
/**
|
||||
* Callback used by the Policy Engine to get the Source Caps that
|
||||
* will be sent to the Sink
|
||||
*/
|
||||
int (*policy_cb_get_src_caps)(const struct device *dev,
|
||||
const uint32_t **pdos,
|
||||
uint32_t *num_pdos);
|
||||
|
||||
/**
|
||||
* Callback used by the Policy Engine to check if the Sink's request
|
||||
* is valid
|
||||
*/
|
||||
enum usbc_snk_req_reply_t (*policy_cb_check_sink_request)(const struct device *dev,
|
||||
const uint32_t request_msg);
|
||||
|
||||
/**
|
||||
* Callback used by the Policy Engine to check if the Present Contract
|
||||
* is still valid
|
||||
*/
|
||||
bool (*policy_present_contract_is_valid)(const struct device *dev,
|
||||
const uint32_t present_contract);
|
||||
|
||||
/**
|
||||
* Callback used by the Policy Engine to check if the Source Power Supply
|
||||
* is ready
|
||||
*/
|
||||
bool (*policy_is_ps_ready)(const struct device *dev);
|
||||
|
||||
/**
|
||||
* Callback used by the Policy Engine to request that a different set of
|
||||
* Source Caps be used
|
||||
*/
|
||||
bool (*policy_change_src_caps)(const struct device *dev);
|
||||
|
||||
/**
|
||||
* Callback used by the Policy Engine to store the Sink's Capabilities
|
||||
*/
|
||||
void (*policy_cb_set_port_partner_snk_cap)(const struct device *dev,
|
||||
const uint32_t *pdos,
|
||||
const int num_pdos);
|
||||
#endif /* CONFIG_USBC_CSM_SINK_ONLY */
|
||||
/** Device Policy Manager data */
|
||||
void *dpm_data;
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ LOG_MODULE_DECLARE(usbc_stack, CONFIG_USBC_STACK_LOG_LEVEL);
|
|||
|
||||
#include "usbc_stack.h"
|
||||
#include "usbc_tc_snk_states_internal.h"
|
||||
#include "usbc_tc_src_states_internal.h"
|
||||
#include "usbc_tc_common_internal.h"
|
||||
|
||||
static const struct smf_state tc_states[TC_STATE_COUNT];
|
||||
|
@ -89,7 +90,11 @@ void tc_run(const struct device *dev, const int32_t dpm_request)
|
|||
*/
|
||||
bool tc_is_in_attached_state(const struct device *dev)
|
||||
{
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
return (tc_get_state(dev) == TC_ATTACHED_SNK_STATE);
|
||||
#else
|
||||
return (tc_get_state(dev) == TC_ATTACHED_SRC_STATE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -99,17 +104,29 @@ static void tc_init(const struct device *dev)
|
|||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
struct tc_sm_t *tc = data->tc;
|
||||
const struct device *tcpc = data->tcpc;
|
||||
|
||||
/* Initialize the timers */
|
||||
usbc_timer_init(&tc->tc_t_error_recovery, TC_T_ERROR_RECOVERY_SOURCE_MIN_MS);
|
||||
usbc_timer_init(&tc->tc_t_cc_debounce, TC_T_CC_DEBOUNCE_MAX_MS);
|
||||
usbc_timer_init(&tc->tc_t_rp_value_change, TC_T_RP_VALUE_CHANGE_MAX_MS);
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
usbc_timer_init(&tc->tc_t_vconn_off, TC_T_VCONN_OFF_MAX_MS);
|
||||
#endif
|
||||
|
||||
/* Clear the flags */
|
||||
tc->flags = ATOMIC_INIT(0);
|
||||
|
||||
/* Initialize the TCPC */
|
||||
tcpc_init(data->tcpc);
|
||||
tcpc_init(tcpc);
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
/* Stop sourcing VBUS */
|
||||
data->policy_cb_src_en(dev, false);
|
||||
|
||||
/* Stop sourcing VCONN */
|
||||
tcpc_set_vconn(tcpc, false);
|
||||
#endif
|
||||
|
||||
/* Initialize the state machine */
|
||||
/*
|
||||
|
@ -155,6 +172,21 @@ void tc_pd_enable(const struct device *dev, const bool enable)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TCPC CC/Rp management
|
||||
*/
|
||||
void tc_select_src_collision_rp(const struct device *dev, enum tc_rp_value rp)
|
||||
{
|
||||
struct usbc_port_data *data = dev->data;
|
||||
const struct device *tcpc = data->tcpc;
|
||||
|
||||
/* Select Rp value */
|
||||
tcpc_select_rp_value(tcpc, rp);
|
||||
|
||||
/* Place Rp on CC lines */
|
||||
tcpc_set_cc(tcpc, TC_CC_RP);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CC Open Entry
|
||||
*/
|
||||
|
@ -216,8 +248,13 @@ static void tc_error_recovery_run(void *obj)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
/* Transition to Unattached.SNK */
|
||||
tc_set_state(dev, TC_UNATTACHED_SNK_STATE);
|
||||
#else
|
||||
/* Transition to Unattached.SRC */
|
||||
tc_set_state(dev, TC_UNATTACHED_SRC_STATE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,12 +267,21 @@ static const struct smf_state tc_states[TC_STATE_COUNT] = {
|
|||
NULL,
|
||||
NULL,
|
||||
NULL),
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
[TC_CC_RD_SUPER_STATE] = SMF_CREATE_STATE(
|
||||
tc_cc_rd_entry,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL),
|
||||
#else
|
||||
[TC_CC_RP_SUPER_STATE] = SMF_CREATE_STATE(
|
||||
tc_cc_rp_entry,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL),
|
||||
#endif
|
||||
/* Normal States */
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
[TC_UNATTACHED_SNK_STATE] = SMF_CREATE_STATE(
|
||||
tc_unattached_snk_entry,
|
||||
tc_unattached_snk_run,
|
||||
|
@ -251,6 +297,28 @@ static const struct smf_state tc_states[TC_STATE_COUNT] = {
|
|||
tc_attached_snk_run,
|
||||
tc_attached_snk_exit,
|
||||
NULL),
|
||||
#else
|
||||
[TC_UNATTACHED_SRC_STATE] = SMF_CREATE_STATE(
|
||||
tc_unattached_src_entry,
|
||||
tc_unattached_src_run,
|
||||
NULL,
|
||||
&tc_states[TC_CC_RP_SUPER_STATE]),
|
||||
[TC_UNATTACHED_WAIT_SRC_STATE] = SMF_CREATE_STATE(
|
||||
tc_unattached_wait_src_entry,
|
||||
tc_unattached_wait_src_run,
|
||||
tc_unattached_wait_src_exit,
|
||||
NULL),
|
||||
[TC_ATTACH_WAIT_SRC_STATE] = SMF_CREATE_STATE(
|
||||
tc_attach_wait_src_entry,
|
||||
tc_attach_wait_src_run,
|
||||
tc_attach_wait_src_exit,
|
||||
&tc_states[TC_CC_RP_SUPER_STATE]),
|
||||
[TC_ATTACHED_SRC_STATE] = SMF_CREATE_STATE(
|
||||
tc_attached_src_entry,
|
||||
tc_attached_src_run,
|
||||
tc_attached_src_exit,
|
||||
NULL),
|
||||
#endif
|
||||
[TC_DISABLED_STATE] = SMF_CREATE_STATE(
|
||||
tc_disabled_entry,
|
||||
tc_disabled_run,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The Chromium OS Authors
|
||||
* Copyright (c) 2023 The Chromium OS Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -14,12 +14,23 @@
|
|||
#include "usbc_timer.h"
|
||||
#include "usbc_stack.h"
|
||||
|
||||
enum tc_flags {
|
||||
/**
|
||||
* Flag to track Rp resistor change when the sink attached
|
||||
* sub-state runs
|
||||
*/
|
||||
TC_FLAGS_RP_SUBSTATE_CHANGE = 0,
|
||||
/** Tracks if VCONN is ON or OFF */
|
||||
TC_FLAGS_VCONN_ON,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type-C States
|
||||
*/
|
||||
enum tc_state_t {
|
||||
/** Super state that opens the CC lines */
|
||||
TC_CC_OPEN_SUPER_STATE,
|
||||
#ifdef CONFIG_USBC_CSM_SINK_ONLY
|
||||
/** Super state that applies Rd to the CC lines */
|
||||
TC_CC_RD_SUPER_STATE,
|
||||
/** Unattached Sink State */
|
||||
|
@ -28,6 +39,18 @@ enum tc_state_t {
|
|||
TC_ATTACH_WAIT_SNK_STATE,
|
||||
/** Attached Sink State */
|
||||
TC_ATTACHED_SNK_STATE,
|
||||
#else
|
||||
/** Super state that applies Rp to the CC lines */
|
||||
TC_CC_RP_SUPER_STATE,
|
||||
/** Unattached Source State */
|
||||
TC_UNATTACHED_SRC_STATE,
|
||||
/** Unattached Wait Source State */
|
||||
TC_UNATTACHED_WAIT_SRC_STATE,
|
||||
/** Attach Wait Source State */
|
||||
TC_ATTACH_WAIT_SRC_STATE,
|
||||
/** Attached Source State */
|
||||
TC_ATTACHED_SRC_STATE,
|
||||
#endif
|
||||
/** Disabled State */
|
||||
TC_DISABLED_STATE,
|
||||
/** Error Recovery State */
|
||||
|
@ -37,17 +60,6 @@ enum tc_state_t {
|
|||
TC_STATE_COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type-C Layer flags
|
||||
*/
|
||||
enum tc_flags {
|
||||
/**
|
||||
* Flag to track Rp resistor change when the sink attached
|
||||
* sub-state runs
|
||||
*/
|
||||
TC_FLAGS_RP_SUBSTATE_CHANGE = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief TC Layer State Machine Object
|
||||
*/
|
||||
|
@ -79,6 +91,10 @@ struct tc_sm_t {
|
|||
struct usbc_timer_t tc_t_rp_value_change;
|
||||
/** tErrorRecovery timer */
|
||||
struct usbc_timer_t tc_t_error_recovery;
|
||||
#ifdef CONFIG_USBC_CSM_SOURCE_ONLY
|
||||
/** tVconnOff timer */
|
||||
struct usbc_timer_t tc_t_vconn_off;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -130,4 +146,12 @@ void tc_run(const struct device *dev, int32_t dpm_request);
|
|||
*/
|
||||
bool tc_is_in_attached_state(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Sets the Collision Avoidance Rp value
|
||||
*
|
||||
* @param dev Pointer to the device structure for the driver instance.
|
||||
* @param rp Collision Avoidance Rp to set
|
||||
*/
|
||||
void tc_select_src_collision_rp(const struct device *dev, enum tc_rp_value rp);
|
||||
|
||||
#endif /* ZEPHYR_SUBSYS_USBC_TC_COMMON_INTERNAL_H_ */
|
||||
|
|
329
subsys/usb/usb_c/usbc_tc_src_states.c
Normal file
329
subsys/usb/usb_c/usbc_tc_src_states.c
Normal file
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The Chromium OS Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(usbc_stack, CONFIG_USBC_STACK_LOG_LEVEL);
|
||||
|
||||
#include "usbc_stack.h"
|
||||
#include "usbc_tc_src_states_internal.h"
|
||||
|
||||
/**
|
||||
* @brief Spec. Release 1.3, section 4.5.2.2.7 Unattached.SRC State
|
||||
*
|
||||
* When in the Unattached.SRC state, the port is waiting to detect the
|
||||
* presence of a Sink or an Accessory.
|
||||
*
|
||||
* Requirements:
|
||||
* 1: The port shall not drive VBUS or VCONN.
|
||||
* NOTE: Implemented in the tc_attached_src_exit
|
||||
* function and initially set the tc_init function.
|
||||
*
|
||||
* 2: The port shall provide a separate Rp termination on the CC1 and
|
||||
* CC2 pins.
|
||||
* NOTE: Implemented in the tc_cc_rp super state.
|
||||
*/
|
||||
|
||||
void tc_unattached_src_entry(void *obj)
|
||||
{
|
||||
LOG_INF("Unattached.SRC");
|
||||
}
|
||||
|
||||
void tc_unattached_src_run(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
const struct device *dev = tc->dev;
|
||||
|
||||
/*
|
||||
* Transition to AttachWait.SRC when:
|
||||
* The SRC.Rd is detected on either CC1 or CC2 pin or
|
||||
* SRC.Ra is detected on both CC1 and CC2 pins.
|
||||
* NOTE: Audio Adapter Accessory Mode is not supported, so
|
||||
* SRC.Ra will not be checked.
|
||||
*/
|
||||
if (tcpc_is_cc_at_least_one_rd(tc->cc1, tc->cc2)) {
|
||||
tc_set_state(dev, TC_ATTACH_WAIT_SRC_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Spec. Release 1.3, section 4.5.2.2.6 UnattachedWait.SRC State
|
||||
*
|
||||
* When in the UnattachedWait.SRC state, the port is discharging the CC pin
|
||||
* that was providing VCONN in the previous Attached.SRC state.
|
||||
*
|
||||
* Requirements:
|
||||
* 1: The port shall not enable VBUS or VCONN.
|
||||
* NOTE: Implemented in tc_attached_src_exit
|
||||
*
|
||||
* 2: The port shall continue to provide an Rp termination on the CC pin not
|
||||
* being discharged.
|
||||
* NOTE: Implemented in TC_CC_RP_SUPER_STATE super state.
|
||||
*
|
||||
* 3: The port shall provide an Rdch termination on the CC pin being
|
||||
* discharged.
|
||||
* NOTE: Implemented in tc_unattached_wait_src_entry
|
||||
*/
|
||||
|
||||
void tc_unattached_wait_src_entry(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
const struct device *dev = tc->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
const struct device *tcpc = data->tcpc;
|
||||
|
||||
LOG_INF("UnattachedWait.SRC");
|
||||
|
||||
/* Start discharging VCONN */
|
||||
tcpc_vconn_discharge(tcpc, true);
|
||||
|
||||
/* Start VCONN off timer */
|
||||
usbc_timer_start(&tc->tc_t_vconn_off);
|
||||
}
|
||||
|
||||
void tc_unattached_wait_src_run(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
const struct device *dev = tc->dev;
|
||||
|
||||
/* CC Debounce time should be enough time for VCONN to discharge */
|
||||
if (usbc_timer_expired(&tc->tc_t_vconn_off)) {
|
||||
tc_set_state(dev, TC_UNATTACHED_SRC_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
void tc_unattached_wait_src_exit(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
const struct device *dev = tc->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
const struct device *tcpc = data->tcpc;
|
||||
|
||||
/* Stop discharging VCONN */
|
||||
tcpc_vconn_discharge(tcpc, false);
|
||||
|
||||
/* Stop timer */
|
||||
usbc_timer_stop(&tc->tc_t_vconn_off);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Spec. Release 1.3, section 4.5.2.2.8 AttachWait.SRC State
|
||||
*
|
||||
* The AttachWait.SRC state is used to ensure that the state of both of
|
||||
* the CC1 and CC2 pins is stable after a Sink is connected.
|
||||
*
|
||||
* Requirements:
|
||||
* The requirements for this state are identical to Unattached.SRC.
|
||||
*/
|
||||
|
||||
void tc_attach_wait_src_entry(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
|
||||
LOG_INF("AttachWait.SRC");
|
||||
|
||||
/* Initialize the cc state to open */
|
||||
tc->cc_state = TC_CC_NONE;
|
||||
}
|
||||
|
||||
void tc_attach_wait_src_run(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
const struct device *dev = tc->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
const struct device *vbus = data->vbus;
|
||||
enum tc_cc_states new_cc_state;
|
||||
|
||||
/* Is a connection detected? */
|
||||
if (tcpc_is_cc_at_least_one_rd(tc->cc1, tc->cc2)) {
|
||||
/* UFP attached */
|
||||
new_cc_state = TC_CC_UFP_ATTACHED;
|
||||
} else {
|
||||
/* No UFP */
|
||||
tc_set_state(dev, TC_UNATTACHED_SRC_STATE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Debounce the cc state */
|
||||
if (new_cc_state != tc->cc_state) {
|
||||
/* Start debouce timer */
|
||||
usbc_timer_start(&tc->tc_t_cc_debounce);
|
||||
tc->cc_state = new_cc_state;
|
||||
}
|
||||
|
||||
/* Wait for CC debounce */
|
||||
if (usbc_timer_running(&tc->tc_t_cc_debounce) &&
|
||||
!usbc_timer_expired(&tc->tc_t_cc_debounce)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The port shall transition to Attached.SRC when VBUS is at vSafe0V
|
||||
* and the SRC.Rd state is detected on exactly one of the CC1 or CC2
|
||||
* pins for at least tCCDebounce.
|
||||
*/
|
||||
if (usbc_vbus_check_level(vbus, TC_VBUS_SAFE0V)) {
|
||||
if (new_cc_state == TC_CC_UFP_ATTACHED) {
|
||||
tc_set_state(dev, TC_ATTACHED_SRC_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tc_attach_wait_src_exit(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
|
||||
/* Stop debounce timer */
|
||||
usbc_timer_stop(&tc->tc_t_cc_debounce);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Spec. Release 1.3, section 4.5.2.2.9 Attached.SRC State
|
||||
*
|
||||
* When in the Attached.SRC state, the port is attached and operating as a
|
||||
* Source. When the port initially enters this state it is also operating
|
||||
* as a DFP. Subsequently, the initial power and data roles can be changed
|
||||
* using USB PD commands.
|
||||
*
|
||||
* Requirements:
|
||||
* 1: If the port needs to determine the orientation of the connector, it
|
||||
* shall do so only upon entry to the Attached.SRC state by detecting
|
||||
* which of the CC1 or CC2 pins is connected through the
|
||||
* cable, i.e., which CC pin is in the SRC.Rd state.
|
||||
* NOTE: Implemented in tc_attached_src_entry.
|
||||
*
|
||||
* 2: If the port has entered this state from the AttachWait.SRC state,
|
||||
* the SRC.Rd state will be on only one of the CC1 or CC2 pins. The
|
||||
* port shall source current on this CC pin and monitor its state.
|
||||
* NOTE: Implemented in the super state of AttachWait.SRC.
|
||||
*
|
||||
* 3: The port shall provide an Rp
|
||||
* NOTE: Implemented in the super state of AttachWait.SRC.
|
||||
*
|
||||
* 5: The port shall supply VBUS current at the level it advertises on Rp.
|
||||
* NOTE: Implemented in tc_attached_src_entry.
|
||||
*
|
||||
* 7: The port shall not initiate any USB PD communications until VBUS
|
||||
* reaches vSafe5V.
|
||||
* NOTE: Implemented in tc_attached_src_run.
|
||||
*
|
||||
* 8: The port may negotiate a USB PD PR_Swap, DR_Swap or VCONN_Swap.
|
||||
* NOTE: Implemented in tc_attached_src_run.
|
||||
*
|
||||
* 9: If the port supplies VCONN, it shall do so within t_VCONN_ON.
|
||||
* NOTE: Implemented in tc_attached_src_entry.
|
||||
*/
|
||||
|
||||
void tc_attached_src_entry(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
const struct device *dev = tc->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
const struct device *tcpc = data->tcpc;
|
||||
|
||||
LOG_INF("Attached.SRC");
|
||||
|
||||
/* Initial data role for source is DFP */
|
||||
tcpc_set_roles(tcpc, TC_ROLE_SOURCE, TC_ROLE_DFP);
|
||||
|
||||
/* Set cc polarity */
|
||||
tcpc_set_cc_polarity(tcpc, tc->cc_polarity);
|
||||
|
||||
/* Start sourcing VBUS */
|
||||
if (data->policy_cb_src_en(dev, true) == 0) {
|
||||
/* Start sourcing VCONN */
|
||||
if (policy_check(dev, CHECK_VCONN_CONTROL)) {
|
||||
if (tcpc_set_vconn(tcpc, true) == 0) {
|
||||
atomic_set_bit(&tc->flags, TC_FLAGS_VCONN_ON);
|
||||
} else {
|
||||
LOG_ERR("VCONN can't be enabled\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_ERR("Power Supply can't be enabled\n");
|
||||
}
|
||||
|
||||
/* Enable PD */
|
||||
tc_pd_enable(dev, true);
|
||||
}
|
||||
|
||||
void tc_attached_src_run(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
const struct device *dev = tc->dev;
|
||||
|
||||
/* Monitor for CC disconnection */
|
||||
if (tcpc_is_cc_open(tc->cc1, tc->cc2)) {
|
||||
/*
|
||||
* A Source that is supplying VCONN or has yielded VCONN source
|
||||
* responsibility to the Sink through USBPD VCONN_Swap messaging
|
||||
* shall transition to UnattachedWait.SRC when the SRC.Open state
|
||||
* is detected on the monitored CC pin. The Source shall detect
|
||||
* the SRC.Open state within tSRCDisconnect, but should detect
|
||||
* it as quickly as possible.
|
||||
*/
|
||||
if (atomic_test_and_clear_bit(&tc->flags, TC_FLAGS_VCONN_ON)) {
|
||||
tc_set_state(dev, TC_UNATTACHED_WAIT_SRC_STATE);
|
||||
}
|
||||
/*
|
||||
* A Source that is not supplying VCONN and has not yielded
|
||||
* VCONN responsibility to the Sink through USBPD VCONN_Swap
|
||||
* messaging shall transition to Unattached.SRC when the
|
||||
* SRC.Open state is detected on the monitored CC pin. The
|
||||
* Source shall detect the SRC.Open state within tSRCDisconnect,
|
||||
* but should detect it as quickly as possible.
|
||||
*/
|
||||
else {
|
||||
tc_set_state(dev, TC_UNATTACHED_SRC_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tc_attached_src_exit(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
const struct device *dev = tc->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
const struct device *tcpc = data->tcpc;
|
||||
|
||||
__ASSERT(data->policy_cb_src_en != NULL,
|
||||
"policy_cb_src_en must not be NULL");
|
||||
|
||||
/* Disable PD */
|
||||
tc_pd_enable(dev, false);
|
||||
|
||||
/* Stop sourcing VBUS */
|
||||
data->policy_cb_src_en(dev, false);
|
||||
|
||||
/* Stop sourcing VCONN */
|
||||
tcpc_set_vconn(tcpc, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This is a super state for Source States that
|
||||
* requirement the Rp value placed on the CC lines.
|
||||
*/
|
||||
void tc_cc_rp_entry(void *obj)
|
||||
{
|
||||
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
|
||||
const struct device *dev = tc->dev;
|
||||
struct usbc_port_data *data = dev->data;
|
||||
const struct device *tcpc = data->tcpc;
|
||||
enum tc_rp_value rp = TC_RP_USB;
|
||||
|
||||
/*
|
||||
* Get initial Rp value from Device Policy Manager or use
|
||||
* default TC_RP_USB.
|
||||
*/
|
||||
if (data->policy_cb_get_src_rp) {
|
||||
data->policy_cb_get_src_rp(dev, &rp);
|
||||
}
|
||||
|
||||
/* Select Rp value */
|
||||
tcpc_select_rp_value(tcpc, rp);
|
||||
|
||||
/* Place Rp on CC lines */
|
||||
tcpc_set_cc(tcpc, TC_CC_RP);
|
||||
}
|
41
subsys/usb/usb_c/usbc_tc_src_states_internal.h
Normal file
41
subsys/usb/usb_c/usbc_tc_src_states_internal.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The Chromium OS Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_SUBSYS_USBC_TC_SRC_STATES_INTERNAL_H_
|
||||
#define ZEPHYR_SUBSYS_USBC_TC_SRC_STATES_INTERNAL_H_
|
||||
|
||||
/**
|
||||
* @brief Unattached.SRC
|
||||
*/
|
||||
void tc_unattached_src_entry(void *obj);
|
||||
void tc_unattached_src_run(void *obj);
|
||||
|
||||
/**
|
||||
* @brief UnattachedWait.SRC
|
||||
*/
|
||||
void tc_unattached_wait_src_entry(void *obj);
|
||||
void tc_unattached_wait_src_run(void *obj);
|
||||
void tc_unattached_wait_src_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief AttachWait.SRC
|
||||
*/
|
||||
void tc_attach_wait_src_entry(void *obj);
|
||||
void tc_attach_wait_src_run(void *obj);
|
||||
void tc_attach_wait_src_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief Attached.SRC
|
||||
*/
|
||||
void tc_attached_src_entry(void *obj);
|
||||
void tc_attached_src_run(void *obj);
|
||||
void tc_attached_src_exit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief Super State to set RP
|
||||
*/
|
||||
void tc_cc_rp_entry(void *obj);
|
||||
#endif /* ZEPHYR_SUBSYS_USBC_TC_SRC_STATES_INTERNAL_H_ */
|
Loading…
Reference in a new issue