usb-c: Add USB-C Subsystem with Sink PD Support

This USB-C Subsystem enables an application to include
USB-C Power Delivery Sink functionality.

Signed-off-by: Sam Hurst <sbh1187@gmail.com>
This commit is contained in:
Sam Hurst 2022-05-02 15:36:29 -07:00 committed by Anas Nashif
commit e90f1b66d8
15 changed files with 4897 additions and 0 deletions

290
include/zephyr/usb_c/usbc.h Normal file
View file

@ -0,0 +1,290 @@
/*
* Copyright 2022 The Chromium OS Authors
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief USB-C Device APIs
*
* This file contains the USB-C Device APIs.
*/
#ifndef ZEPHYR_INCLUDE_USBC_H_
#define ZEPHYR_INCLUDE_USBC_H_
#include <zephyr/types.h>
#include <zephyr/device.h>
#include <zephyr/drivers/usb_c/usbc_tcpc.h>
#include <zephyr/drivers/usb_c/usbc_vbus.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief USB-C Device APIs
* @defgroup _usbc_device_api USB-C Device API
* @{
*/
/**
* @brief This Request Data Object (RDO) value can be returned from the
* policy_cb_get_rdo if 5V@100mA with the following
* options are sufficient for the Sink to operate.
*
* The RDO is configured as follows:
* Maximum operating current 100mA
* Operating current 100mA
* Unchunked Extended Messages Not Supported
* No USB Suspend
* Not USB Communications Capable
* No capability mismatch
* Don't giveback
* Object position 1 (5V PDO)
*/
#define FIXED_5V_100MA_RDO 0x1100280a
/**
* @brief Device Policy Manager requests
*/
enum usbc_policy_request_t {
/** No request */
REQUEST_NOP,
/** Request Type-C layer to transition to Disabled State */
REQUEST_TC_DISABLED,
/** Request Type-C layer to transition to Error Recovery State */
REQUEST_TC_ERROR_RECOVERY,
/** End of Type-C requests */
REQUEST_TC_END,
/** Request Policy Engine layer to perform a Data Role Swap */
REQUEST_PE_DR_SWAP,
/** Request Policy Engine layer to send a hard reset */
REQUEST_PE_HARD_RESET_SEND,
/** Request Policy Engine layer to send a soft reset */
REQUEST_PE_SOFT_RESET_SEND,
/**
* Request Policy Engine layer to get Source Capabilities from
* port partner
*/
REQUEST_PE_GET_SRC_CAPS,
};
/**
* @brief Device Policy Manager notifications
*/
enum usbc_policy_notify_t {
/** Power Delivery Accept message was received */
MSG_ACCEPT_RECEIVED,
/** Power Delivery Reject message was received */
MSG_REJECTED_RECEIVED,
/** Power Delivery discarded the message being transmited */
MSG_DISCARDED,
/** Power Delivery Not Supported message was received */
MSG_NOT_SUPPORTED_RECEIVED,
/** Data Role has been set to Upstream Facing Port (UFP) */
DATA_ROLE_IS_UFP,
/** Data Role has been set to Downstream Facing Port (DFP) */
DATA_ROLE_IS_DFP,
/** A PD Explicit Contract is in place */
PD_CONNECTED,
/** No PD Explicit Contract is in place */
NOT_PD_CONNECTED,
/** Transition the Power Supply */
TRANSITION_PS,
/** Port partner is not responsive */
PORT_PARTNER_NOT_RESPONSIVE,
/** Protocol Error occurred */
PROTOCOL_ERROR,
/** Transition the Sink to default */
SNK_TRANSITION_TO_DEFAULT,
/** Hard Reset Received */
HARD_RESET_RECEIVED,
/** Sink SubPower state at 0V */
POWER_CHANGE_0A0,
/** Sink SubPower state a 5V / 500mA */
POWER_CHANGE_DEF,
/** Sink SubPower state a 5V / 1.5A */
POWER_CHANGE_1A5,
/** Sink SubPower state a 5V / 3A */
POWER_CHANGE_3A0,
};
/**
* @brief Device Policy Manager checks
*/
enum usbc_policy_check_t {
/** Check if Power Role Swap is allowed */
CHECK_POWER_ROLE_SWAP,
/** Check if Data Role Swap to DFP is allowed */
CHECK_DATA_ROLE_SWAP_TO_DFP,
/** Check if Data Role Swap to UFP is allowed */
CHECK_DATA_ROLE_SWAP_TO_UFP,
/** Check if Sink is at default level */
CHECK_SNK_AT_DEFAULT_LEVEL,
};
/**
* @brief Device Policy Manager Wait message notifications
*/
enum usbc_policy_wait_t {
/** The port partner is unable to meet the sink request at this time */
WAIT_SINK_REQUEST,
/** The port partner is unable to do a Power Role Swap at this time */
WAIT_POWER_ROLE_SWAP,
/** The port partner is unable to do a Data Role Swap at this time */
WAIT_DATA_ROLE_SWAP,
/** The port partner is unable to do a VCONN Swap at this time */
WAIT_VCONN_SWAP,
};
/** @cond INTERNAL_HIDDEN */
typedef int (*policy_cb_get_snk_cap_t)(const struct device *dev,
uint32_t **pdos,
int *num_pdos);
typedef void (*policy_cb_set_src_cap_t)(const struct device *dev,
const uint32_t *pdos,
const int num_pdos);
typedef bool (*policy_cb_check_t)(const struct device *dev,
const enum usbc_policy_check_t policy_check);
typedef bool (*policy_cb_wait_notify_t)(const struct device *dev,
const enum usbc_policy_wait_t wait_notify);
typedef void (*policy_cb_notify_t)(const struct device *dev,
const enum usbc_policy_notify_t policy_notify);
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 */
/**
* @brief Start the USB-C Subsystem
*
* @param dev Runtime device structure
*
* @retval 0 on success
*/
int usbc_start(const struct device *dev);
/**
* @brief Suspend the USB-C Subsystem
*
* @param dev Runtime device structure
*
* @retval 0 on success
*/
int usbc_suspend(const struct device *dev);
/**
* @brief Make a request of the USB-C Subsystem
*
* @param dev Runtime device structure
* @param req request
*
* @retval 0 on success
*/
int usbc_request(const struct device *dev,
const enum usbc_policy_request_t req);
/**
* @brief Set pointer to Device Policy Manager (DPM) data
*
* @param dev Runtime device structure
* @param dpm_data pointer to dpm data
*/
void usbc_set_dpm_data(const struct device *dev,
void *dpm_data);
/**
* @brief Get pointer to Device Policy Manager (DPM) data
*
* @param dev Runtime device structure
*
* @retval pointer to dpm data that was set with usbc_set_dpm_data
* @retval NULL if dpm data was not set
*/
void *usbc_get_dpm_data(const struct device *dev);
/**
* @brief Set the callback used to set VCONN control
*
* @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);
/**
* @brief Set the callback used to check a policy
*
* @param dev Runtime device structure
* @param cb callback
*/
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
* policy change
*
* @param dev Runtime device structure
* @param cb callback
*/
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
* message reception
*
* @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);
/**
* @brief Set the callback used to get the Sink Capabilities
*
* @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);
/**
* @brief Set the callback used to store the received Port Partner's
* Source Capabilities
*
* @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);
/**
* @brief Set the callback used to get the Request Data Object (RDO)
*
* @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);
/**
* @brief Set the callback used to check if the sink power supply is at
* the default level
*
* @param dev Runtime device structure
* @param cb callback
*/
void usbc_set_policy_cb_is_snk_at_default(const struct device *dev,
const policy_cb_is_snk_at_default_t cb);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_USBC_H_ */

View file

@ -48,6 +48,8 @@ source "subsys/stats/Kconfig"
source "subsys/usb/device/Kconfig" source "subsys/usb/device/Kconfig"
source "subsys/usb/usb_c/Kconfig"
source "subsys/sd/Kconfig" source "subsys/sd/Kconfig"
source "subsys/dfu/Kconfig" source "subsys/dfu/Kconfig"

View file

@ -2,3 +2,4 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK device) add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK device)
add_subdirectory_ifdef(CONFIG_USBC_STACK usb_c)

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_USBC_STACK usbc_timer.c usbc_stack.c usbc_tc.c usbc_prl.c usbc_pe.c)

40
subsys/usb/usb_c/Kconfig Normal file
View file

@ -0,0 +1,40 @@
# USB-C stack configuration options
# Copyright (c) 2022 The Chromium OS Authors
# SPDX-License-Identifier: Apache-2.0
menuconfig USBC_STACK
bool "USB-C Stack Support"
select SMF
select SMF_ANCESTOR_SUPPORT
select USBC_TCPC_DRIVER
select USBC_VBUS_DRIVER
help
Enable the USB-C Stack. Note that each USB-C port gets its own thread.
if USBC_STACK
config USBC_THREAD_PRIORITY
int "USB-C thread priority"
default 0
help
Set thread priority of the USB-C
config USBC_STACK_SIZE
int "USB-C thread stack size"
default 1024
help
Stack size of thread created for each instance.
config USBC_STATE_MACHINE_CYCLE_TIME
int "USB-C state machine cycle time in milliseconds"
default 5
help
The USB-C state machine is run in a loop and the cycle time is the
delay before running the loop again.
module = USBC_STACK
module-str = usbc stack
source "subsys/logging/Kconfig.template.log_config"
endif # USBC_STACK

1783
subsys/usb/usb_c/usbc_pe.c Normal file

File diff suppressed because it is too large Load diff

221
subsys/usb/usb_c/usbc_pe.h Normal file
View file

@ -0,0 +1,221 @@
/*
* Copyright (c) 2022 The Chromium OS Authors
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_USBC_PE_H_
#define ZEPHYR_SUBSYS_USBC_PE_H_
#include <zephyr/kernel.h>
#include <zephyr/usb_c/usbc.h>
#include <zephyr/drivers/usb_c/usbc_pd.h>
#include <zephyr/drivers/usb_c/usbc_tc.h>
#include <zephyr/smf.h>
#include "usbc_timer.h"
/**
* @brief Policy Engine Errors
*/
enum pe_error {
/** Transmit error */
ERR_XMIT,
};
/**
* @brief Policy Engine State Machine Object
*/
struct policy_engine {
/** state machine context */
struct smf_ctx ctx;
/** Port device */
const struct device *dev;
/** state machine flags */
atomic_t flags;
/** current port power role (SOURCE or SINK) */
enum tc_power_role power_role;
/** current port data role (DFP or UFP) */
enum tc_data_role data_role;
/** port address where soft resets are sent */
enum pd_packet_type soft_reset_sop;
/** DPM request */
enum usbc_policy_request_t dpm_request;
/* Counters */
/**
* This counter is used to retry the Hard Reset whenever there is no
* response from the remote device.
*/
uint32_t hard_reset_counter;
/* 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;
};
/**
* @brief This function must only be called in the subsystem init function.
*
* @param dev Pointer to the device structure for the driver instance.
*/
void pe_subsys_init(const struct device *dev);
/**
* @brief Start the Policy Engine Layer state machine. This is only called
* from the Type-C state machine.
*
* @param dev Pointer to the device structure for the driver instance
*/
void pe_start(const struct device *dev);
/**
* @brief Suspend the Policy Engine Layer state machine. This is only called
* from the Type-C state machine.
*
* @param dev Pointer to the device structure for the driver instance
*/
void pe_suspend(const struct device *dev);
/**
* @brief Run the Policy Engine Layer state machine. This is called from the
* subsystems port stack thread
*
* @param dev Pointer to the device structure for the driver instance
* @param dpm_request Device Policy Manager request
*/
void pe_run(const struct device *dev,
const int32_t dpm_request);
/**
* @brief Query if the Policy Engine is running
*
* @param dev Pointer to the device structure for the driver instance
*
* @retval TRUE if the Policy Engine is running
* @retval FALSE if the Policy Engine is not running
*/
bool pe_is_running(const struct device *dev);
/**
* @brief Informs the Policy Engine that a message was successfully sent
*
* @param dev Pointer to the device structure for the driver instance
*/
void pe_message_sent(const struct device *dev);
/**
* @brief Informs the Policy Engine of an error.
*
* @param dev Pointer to the device structure for the driver instance
* @param e policy error
* @param type port partner address where error was generated
*/
void pe_report_error(const struct device *dev,
const enum pe_error e,
const enum pd_packet_type type);
/**
* @brief Informs the Policy Engine that a transmit message was discarded
* because of an incoming message.
*
* @param dev Pointer to the device structure for the driver instance
*/
void pe_report_discard(const struct device *dev);
/**
* @brief Called by the Protocol Layer to informs the Policy Engine
* that a message has been received.
*
* @param dev Pointer to the device structure for the driver instance
*/
void pe_message_received(const struct device *dev);
/**
* @brief Informs the Policy Engine that a hard reset was received.
*
* @param dev Pointer to the device structure for the driver instance
*/
void pe_got_hard_reset(const struct device *dev);
/**
* @brief Informs the Policy Engine that a soft reset was received.
*
* @param dev Pointer to the device structure for the driver instance
*/
void pe_got_soft_reset(const struct device *dev);
/**
* @brief Informs the Policy Engine that a hard reset was sent.
*
* @param dev Pointer to the device structure for the driver instance
*/
void pe_hard_reset_sent(const struct device *dev);
/**
* @brief Indicates if an explicit contract is in place
*
* @param dev Pointer to the device structure for the driver instance
*
* @retval true if an explicit contract is in place, else false
*/
bool pe_is_explicit_contract(const struct device *dev);
/*
* @brief Informs the Policy Engine that it should invalidate the
* explicit contract.
*
* @param dev Pointer to the device structure for the driver instance
*/
void pe_invalidate_explicit_contract(const struct device *dev);
/**
* @brief Return true if the PE is is within an atomic messaging sequence
* that it initiated with a SOP* port partner.
*
* @note The PRL layer polls this instead of using AMS_START and AMS_END
* notification from the PE that is called out by the spec
*
* @param dev Pointer to the device structure for the driver instance
*/
bool pe_dpm_initiated_ams(const struct device *dev);
/**
* @brief Get the current data role
*
* @param dev Pointer to the device structure for the driver instance
*
* @retval data role
*/
enum tc_data_role pe_get_data_role(const struct device *dev);
/**
* @brief Get the current power role
*
* @param dev Pointer to the device structure for the driver instance
*
* @retval power role
*/
enum tc_power_role pe_get_power_role(const struct device *dev);
/**
* @brief Get cable plug role
*
* @param dev Pointer to the device structure for the driver instance
*
* @retval cable plug role
*/
enum tc_cable_plug pe_get_cable_plug(const struct device *dev);
#endif /* ZEPHYR_SUBSYS_USBC_PE_H_ */

1209
subsys/usb/usb_c/usbc_prl.c Normal file

File diff suppressed because it is too large Load diff

191
subsys/usb/usb_c/usbc_prl.h Normal file
View file

@ -0,0 +1,191 @@
/*
* Copyright (c) 2022 The Chromium OS Authors
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_USBC_PRL_H_
#define ZEPHYR_SUBSYS_USBC_PRL_H_
#include <zephyr/kernel.h>
#include <zephyr/usb_c/usbc.h>
#include <zephyr/drivers/usb_c/usbc_tcpc.h>
#include <zephyr/smf.h>
#include "usbc_pe.h"
#include "usbc_timer.h"
/**
* @brief PD counter definitions
* See Table 6-63 Counter parameters
* Parameter Name: nMessageIDCount
*/
#define PD_MESSAGE_ID_COUNT 7
/**
* @brief Message Reception State Machine Object
*/
struct protocol_layer_rx_t {
/** state machine flags */
atomic_t flags;
/** message ids for all valid port partners */
int msg_id[NUM_SOP_STAR_TYPES];
/** Received Power Delivery Messages are stored in emsg */
struct pd_msg emsg;
};
/**
* @brief Message Transmission State Machine Object
*/
struct protocol_layer_tx_t {
/** state machine context */
struct smf_ctx ctx;
/** Port device */
const struct device *dev;
/** state machine flags */
atomic_t flags;
/** last packet type we transmitted */
enum pd_packet_type last_xmit_type;
/** Current message type to transmit */
uint8_t msg_type;
/**
* Power Delivery Messages meant for transmission are stored
* in emsg
*/
struct pd_msg emsg;
/* Counters */
/** message id counters for all 6 port partners */
uint32_t msg_id_counter[NUM_SOP_STAR_TYPES];
/* Timers */
/** tTxTimeout timer */
struct usbc_timer_t pd_t_tx_timeout;
};
/**
* @brief Hard Reset State Machine Object
*/
struct protocol_hard_reset_t {
/** state machine context */
struct smf_ctx ctx;
/** Port device */
const struct device *dev;
/** state machine flags */
atomic_t flags;
/* Timers */
/** tHardResetComplete timer */
struct usbc_timer_t pd_t_hard_reset_complete;
};
/**
* @brief This function must only be called in the subsystem init function.
*
* @param dev Pointer to the device structure for the driver instance.
*/
void prl_subsys_init(const struct device *dev);
/**
* @brief Start the PRL Layer state machine. This is only called from the
* Type-C state machine.
*
* @param dev Pointer to the device structure for the driver instance
*/
void prl_start(const struct device *dev);
/**
* @brief Suspends the PRL Layer state machine. This is only called from the
* Type-C state machine.
*
* @param dev Pointer to the device structure for the driver instance
*/
void prl_suspend(const struct device *dev);
/**
* @brief Reset the PRL Layer state machine
*
* @param dev Pointer to the device structure for the driver instance
*/
void prl_reset(const struct device *dev);
/**
* @brief Run the PRL Layer state machine. This is called from the subsystems
* port stack thread
*
* @param dev Pointer to the device structure for the driver instance
*/
void prl_run(const struct device *dev);
/**
* @brief Called from the Policy Engine to signal that a hard reset is complete
*
* @param dev Pointer to the device structure for the driver instance
*/
void prl_hard_reset_complete(const struct device *dev);
/**
* @brief Sets the revision received from the port partner
*
* @param dev Pointer to the device structure for the driver instance
* @param type SOP* packet sent from port partner
* @param rev Revision sent from the port partner
*/
void prl_set_rev(const struct device *dev,
const enum pd_packet_type type,
const enum pd_rev_type rev);
/**
* @brief Gets the revision received assciated with a packet type
*
* @param dev Pointer to the device structure for the driver instance
* @param type SOP* packet type to get the revision for
*
* @retval revsion associated with the packet type
*/
enum pd_rev_type prl_get_rev(const struct device *dev,
const enum pd_packet_type type);
/**
* @brief Instructs the Protocol Layer to send a Power Delivery control message
*
* @param dev Pointer to the device structure for the driver instance
* @param type The port partner to send this message to
* @param msg The control message to send
*/
void prl_send_ctrl_msg(const struct device *dev,
const enum pd_packet_type type,
const enum pd_ctrl_msg_type msg);
/**
* @brief Instructs the Protocol Layer to send a Power Delivery data message
*
* @param dev Pointer to the device structure for the driver instance
* @param type The port partner to send this message to
* @param msg The data message to send
*/
void prl_send_data_msg(const struct device *dev,
const enum pd_packet_type type,
const enum pd_data_msg_type msg);
/**
* @brief Instructs the Protocol Layer to execute a hard reset
*
* @param dev Pointer to the device structure for the driver instance
*/
void prl_execute_hard_reset(const struct device *dev);
/**
* @brief Query if the Protocol Layer is running
*
* @param dev Pointer to the device structure for the driver instance
*
* @retval TRUE if the Protocol Layer is running
* @retval FALSE if the Protocol Layer is not running
*/
bool prl_is_running(const struct device *dev);
#endif /* ZEPHYR_SUBSYS_USBC_PRL_H_ */

View file

@ -0,0 +1,275 @@
/*
* Copyright (c) 2022 The Chromium OS Authors
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT usb_c_connector
#include <zephyr/devicetree.h>
#include <zephyr/init.h>
#include <zephyr/smf.h>
#include <zephyr/usb_c/usbc.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(usbc_stack, CONFIG_USBC_STACK_LOG_LEVEL);
#include "usbc_stack.h"
static int usbc_subsys_init(const struct device *dev);
static ALWAYS_INLINE void usbc_handler(void *port_dev)
{
const struct device *dev = (const struct device *)port_dev;
struct usbc_port_data *port = dev->data;
struct request_value *req;
int32_t request;
req = k_fifo_get(&port->request_fifo, K_NO_WAIT);
request = (req != NULL) ? req->val : REQUEST_NOP;
pe_run(dev, request);
prl_run(dev);
tc_run(dev, request);
if (request == PRIV_PORT_REQUEST_SUSPEND) {
k_thread_suspend(port->port_thread);
}
k_msleep(CONFIG_USBC_STATE_MACHINE_CYCLE_TIME);
}
#define USBC_SUBSYS_INIT(inst) \
K_THREAD_STACK_DEFINE(my_stack_area_##inst, \
CONFIG_USBC_STACK_SIZE); \
\
static struct tc_sm_t tc_##inst; \
static struct policy_engine pe_##inst; \
static struct protocol_layer_rx_t prl_rx_##inst; \
static struct protocol_layer_tx_t prl_tx_##inst; \
static struct protocol_hard_reset_t prl_hr_##inst; \
\
static void run_usbc_##inst(void *port_dev, \
void *unused1, \
void *unused2) \
{ \
while (1) { \
usbc_handler(port_dev); \
} \
} \
\
static void create_thread_##inst(const struct device *dev) \
{ \
struct usbc_port_data *port = dev->data; \
\
port->port_thread = k_thread_create(&port->thread_data, \
my_stack_area_##inst, \
K_THREAD_STACK_SIZEOF(my_stack_area_##inst), \
run_usbc_##inst, \
(void *)dev, 0, 0, \
CONFIG_USBC_THREAD_PRIORITY, \
K_ESSENTIAL, \
K_NO_WAIT); \
k_thread_suspend(port->port_thread); \
} \
\
static struct usbc_port_data usbc_port_data_##inst = { \
.tc = &tc_##inst, \
.pe = &pe_##inst, \
.prl_rx = &prl_rx_##inst, \
.prl_tx = &prl_tx_##inst, \
.prl_hr = &prl_hr_##inst, \
.tcpc = DEVICE_DT_GET(DT_INST_PROP(inst, tcpc)), \
.vbus = DEVICE_DT_GET(DT_INST_PROP(inst, vbus)), \
}; \
\
static const struct usbc_port_config usbc_port_config_##inst = { \
.create_thread = create_thread_##inst, \
}; \
\
DEVICE_DT_INST_DEFINE(inst, \
&usbc_subsys_init, \
NULL, \
&usbc_port_data_##inst, \
&usbc_port_config_##inst, \
APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
NULL);
DT_INST_FOREACH_STATUS_OKAY(USBC_SUBSYS_INIT)
/**
* @brief Called by the Device Policy Manager to start the USB-C Subsystem
*/
int usbc_start(const struct device *dev)
{
struct usbc_port_data *data = dev->data;
/* Add private start request to fifo */
data->request.val = PRIV_PORT_REQUEST_START;
k_fifo_put(&data->request_fifo, &data->request);
/* Start the port thread */
k_thread_resume(data->port_thread);
return 0;
}
/**
* @brief Called by the Device Policy Manager to suspend the USB-C Subsystem
*/
int usbc_suspend(const struct device *dev)
{
struct usbc_port_data *data = dev->data;
/* Add private suspend request to fifo */
data->request.val = PRIV_PORT_REQUEST_SUSPEND;
k_fifo_put(&data->request_fifo, &data->request);
return 0;
}
/**
* @brief Called by the Device Policy Manager to make a request of the
* USB-C Subsystem
*/
int usbc_request(const struct device *dev,
const enum usbc_policy_request_t req)
{
struct usbc_port_data *data = dev->data;
/* Add public request to fifo */
data->request.val = req;
k_fifo_put(&data->request_fifo, &data->request);
return 0;
}
/**
* @brief Sets the Device Policy Manager's data
*/
void usbc_set_dpm_data(const struct device *dev,
void *dpm_data)
{
struct usbc_port_data *data = dev->data;
data->dpm_data = dpm_data;
}
/**
* @brief Gets the Device Policy Manager's data
*/
void *usbc_get_dpm_data(const struct device *dev)
{
struct usbc_port_data *data = dev->data;
return data->dpm_data;
}
/**
* @brief Set the callback that gets the Sink Capabilities from the
* Device Policy Manager
*/
void usbc_set_policy_cb_get_snk_cap(const struct device *dev,
const policy_cb_get_snk_cap_t policy_cb_get_snk_cap)
{
struct usbc_port_data *data = dev->data;
data->policy_cb_get_snk_cap = policy_cb_get_snk_cap;
}
/**
* @brief Set the callback that sends the received Source Capabilities to the
* Device Policy Manager
*/
void usbc_set_policy_cb_set_src_cap(const struct device *dev,
const policy_cb_set_src_cap_t policy_cb_set_src_cap)
{
struct usbc_port_data *data = dev->data;
data->policy_cb_set_src_cap = policy_cb_set_src_cap;
}
/**
* @brief Set the callback for the Device Policy Manager policy check
*/
void usbc_set_policy_cb_check(const struct device *dev,
const policy_cb_check_t policy_cb_check)
{
struct usbc_port_data *data = dev->data;
data->policy_cb_check = policy_cb_check;
}
/**
* @brief Set the callback for the Device Policy Manager policy change notify
*/
void usbc_set_policy_cb_notify(const struct device *dev,
const policy_cb_notify_t policy_cb_notify)
{
struct usbc_port_data *data = dev->data;
data->policy_cb_notify = policy_cb_notify;
}
/**
* @brief Set the callback for the Device Policy Manager policy change notify
*/
void usbc_set_policy_cb_wait_notify(const struct device *dev,
const policy_cb_wait_notify_t policy_cb_wait_notify)
{
struct usbc_port_data *data = dev->data;
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
*/
static int usbc_subsys_init(const struct device *dev)
{
struct usbc_port_data *data = dev->data;
const struct usbc_port_config *const config = dev->config;
const struct device *tcpc = data->tcpc;
/* Make sure TCPC is ready */
if (!device_is_ready(tcpc)) {
LOG_ERR("TCPC NOT READY");
return -ENODEV;
}
/* Initialize the state machines */
tc_subsys_init(dev);
pe_subsys_init(dev);
prl_subsys_init(dev);
/* Initialize the request fifo */
k_fifo_init(&data->request_fifo);
/* Create the thread for this port */
config->create_thread(dev);
return 0;
}

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2022 The Chromium OS Authors
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_USBC_STACK_PRIV_H_
#define ZEPHYR_SUBSYS_USBC_STACK_PRIV_H_
#include <zephyr/kernel.h>
#include <zephyr/usb_c/usbc.h>
#include "usbc_tc.h"
#include "usbc_pe.h"
#include "usbc_prl.h"
#define PRIV_PORT_REQUEST_SUSPEND -1
#define PRIV_PORT_REQUEST_START -2
/**
* @brief Each layer of the stack is composed of state machines that can be
* in one of the following states.
*/
enum usbc_sm_state {
/** The state machine is paused */
SM_PAUSED,
/** The state machine is initializing */
SM_INIT,
/** The state machine is running */
SM_RUN
};
/**
* @brief Port config
*/
struct usbc_port_config {
/**
* The usbc stack initializes this pointer that creates the
* main thread for this port
*/
void (*create_thread)(const struct device *dev);
/** The thread stack for this port's thread */
k_thread_stack_t *stack;
};
/**
* @brief Request FIFO
*/
struct request_value {
/** First word is reserved for use by FIFO */
void *fifo_reserved;
/** Request value */
int32_t val;
};
/**
* @brief Port data
*/
struct usbc_port_data {
/** This port's thread */
k_tid_t port_thread;
/** This port thread's data */
struct k_thread thread_data;
/* Type-C layer data */
/** Type-C state machine object */
struct tc_sm_t *tc;
/** Enables or Disables the Type-C state machine */
bool tc_enabled;
/** The state of the Type-C state machine */
enum usbc_sm_state tc_sm_state;
/* Policy Engine layer data */
/** Policy Engine state machine object */
struct policy_engine *pe;
/** Enables or Disables the Policy Engine state machine */
bool pe_enabled;
/** The state of the Policy Engine state machine */
enum usbc_sm_state pe_sm_state;
/* Protocol Layer data */
/** Protocol Receive Layer state machine object */
struct protocol_layer_rx_t *prl_rx;
/** Protocol Transmit Layer state machine object */
struct protocol_layer_tx_t *prl_tx;
/** Protocol Hard Reset Layer state machine object */
struct protocol_hard_reset_t *prl_hr;
/** Enables or Disables the Protocol Layer state machine */
bool prl_enabled;
/** The state of the Protocol Layer state machine */
enum usbc_sm_state prl_sm_state;
/* Common data for all layers */
/** Power Delivery revisions for each packet type */
enum pd_rev_type rev[NUM_SOP_STAR_TYPES];
/** The Type-C Port Controller on this port */
const struct device *tcpc;
/** VBUS Measurement and control device on this port */
const struct device *vbus;
/** Device Policy Manager Request FIFO */
struct k_fifo request_fifo;
/** Device Policy manager Request */
struct request_value request;
/* USB-C Callbacks */
/**
* Callback used by the Policy Engine to ask the Device Policy Manager
* if a particular policy should be allowed
*/
bool (*policy_cb_check)(const struct device *dev,
const enum usbc_policy_check_t policy_check);
/**
* Callback used by the Policy Engine to notify the Device Policy
* Manager of a policy change
*/
void (*policy_cb_notify)(const struct device *dev,
const enum usbc_policy_notify_t policy_notify);
/**
* Callback used by the Policy Engine to notify the Device Policy
* Manager of WAIT message reception
*/
bool (*policy_cb_wait_notify)(const struct device *dev,
const enum usbc_policy_wait_t policy_notify);
/**
* Callback used by the Policy Engine to get the Sink Capabilities
* from the Device Policy Manager
*/
int (*policy_cb_get_snk_cap)(const struct device *dev,
uint32_t **pdos,
int *num_pdos);
/**
* Callback used by the Policy Engine to send the received Source
* Capabilities to the Device Policy Manager
*/
void (*policy_cb_set_src_cap)(const struct device *dev,
const uint32_t *pdos,
const int num_pdos);
/**
* Callback used by the Policy Engine to get the Request Data Object
* (RDO) from the Device Policy Manager
*/
uint32_t (*policy_cb_get_rdo)(const struct device *dev);
/**
* Callback used by the Policy Engine to check if Sink Power Supply
* is at default level
*/
bool (*policy_cb_is_snk_at_default)(const struct device *dev);
/** Device Policy Manager data */
void *dpm_data;
};
#endif /* ZEPHYR_SUBSYS_USBC_STACK_PRIV_H_ */

522
subsys/usb/usb_c/usbc_tc.c Normal file
View file

@ -0,0 +1,522 @@
/*
* Copyright (c) 2022 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"
/**
* @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 Type-C States
*/
enum tc_state_t {
/** Super state that opens the CC lines */
TC_CC_OPEN_SUPER_STATE,
/** Super state that applies Rd to the CC lines */
TC_CC_RD_SUPER_STATE,
/** Disabled State */
TC_DISABLED_STATE,
/** Error Recovery State */
TC_ERROR_RECOVERY_STATE,
/** Unnattached Sink State */
TC_UNATTACHED_SNK_STATE,
/** Attach Wait Sink State */
TC_ATTACH_WAIT_SNK_STATE,
/** Attached Sink State */
TC_ATTACHED_SNK_STATE,
};
static const struct smf_state tc_snk_states[];
static void tc_init(const struct device *dev);
static void tc_set_state(const struct device *dev,
const enum tc_state_t state);
static enum tc_state_t tc_get_state(const struct device *dev);
static void pd_enable(const struct device *dev,
const bool enable);
/**
* @brief Initializes the state machine and enters the Disabled state
*/
void tc_subsys_init(const struct device *dev)
{
struct usbc_port_data *data = dev->data;
struct tc_sm_t *tc = data->tc;
/* Save the port device object so states can access it */
tc->dev = dev;
/* Initialize the state machine */
smf_set_initial(SMF_CTX(tc), &tc_snk_states[TC_DISABLED_STATE]);
}
/**
* @brief Runs the Type-C layer
*/
void tc_run(const struct device *dev,
const int32_t dpm_request)
{
struct usbc_port_data *data = dev->data;
const struct device *tcpc = data->tcpc;
struct tc_sm_t *tc = data->tc;
/* These requests are implicitly set by the Device Policy Manager */
if (dpm_request == PRIV_PORT_REQUEST_START) {
data->tc_enabled = true;
} else if (dpm_request == PRIV_PORT_REQUEST_SUSPEND) {
data->tc_enabled = false;
tc_set_state(dev, TC_DISABLED_STATE);
}
switch (data->tc_sm_state) {
case SM_PAUSED:
if (data->tc_enabled == false) {
break;
}
/* fall through */
case SM_INIT:
/* Initialize the Type-C layer */
tc_init(dev);
data->tc_sm_state = SM_RUN;
/* fall through */
case SM_RUN:
if (data->tc_enabled == false) {
pd_enable(dev, false);
data->tc_sm_state = SM_PAUSED;
break;
}
/* Sample CC lines */
tcpc_get_cc(tcpc, &tc->cc1, &tc->cc2);
/* Detect polarity */
tc->cc_polarity = (tc->cc1 > tc->cc2) ?
TC_POLARITY_CC1 : TC_POLARITY_CC2;
/* Execute any asyncronous Device Policy Manager Requests */
if (dpm_request == REQUEST_TC_ERROR_RECOVERY) {
/* Transition to Error Recovery State */
tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
} else if (dpm_request == REQUEST_TC_DISABLED) {
/* Transition to Disabled State */
tc_set_state(dev, TC_DISABLED_STATE);
}
/* Run state machine */
smf_run_state(SMF_CTX(tc));
}
}
/**
* @brief Checks if the TC Layer is in an Attached state
*/
bool tc_is_in_attached_state(const struct device *dev)
{
return (tc_get_state(dev) == TC_ATTACHED_SNK_STATE);
}
/**
* @brief Initializes the Type-C layer
*/
static void tc_init(const struct device *dev)
{
struct usbc_port_data *data = dev->data;
struct tc_sm_t *tc = data->tc;
/* 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);
/* Clear the flags */
tc->flags = ATOMIC_INIT(0);
/* Initialize the TCPC */
tcpc_init(data->tcpc);
/* Initialize the state machine */
/*
* Start out in error recovery state so the CC lines are opened for a
* short while if this is a system reset.
*/
tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
}
/**
* @brief Sets a Type-C state
*/
static void tc_set_state(const struct device *dev,
const enum tc_state_t state)
{
struct usbc_port_data *data = dev->data;
struct tc_sm_t *tc = data->tc;
smf_set_state(SMF_CTX(tc), &tc_snk_states[state]);
}
/**
* @brief Get the Type-C current state
*/
static enum tc_state_t tc_get_state(const struct device *dev)
{
struct usbc_port_data *data = dev->data;
return data->tc->ctx.current - &tc_snk_states[0];
}
/**
* @brief Enable Power Delivery
*/
static void pd_enable(const struct device *dev,
const bool enable)
{
if (enable) {
prl_start(dev);
pe_start(dev);
} else {
prl_suspend(dev);
pe_suspend(dev);
}
}
/**
* @brief Sink power sub states. Only called if a PD contract is not in place
*/
static void sink_power_sub_states(const struct device *dev)
{
struct usbc_port_data *data = dev->data;
enum tc_cc_voltage_state cc;
enum tc_cc_voltage_state new_cc_voltage;
enum usbc_policy_check_t dpm_pwr_change_notify;
struct tc_sm_t *tc = data->tc;
/* Get the active CC line */
cc = tc->cc_polarity ? tc->cc2 : tc->cc1;
if (cc == TC_CC_VOLT_RP_DEF) {
/*
* This sub-state supports Sinks consuming current within the
* lowest range (default) of Source-supplied current.
*/
new_cc_voltage = TC_CC_VOLT_RP_DEF;
dpm_pwr_change_notify = POWER_CHANGE_DEF;
} else if (cc == TC_CC_VOLT_RP_1A5) {
/*
* This sub-state supports Sinks consuming current within the
* two lower ranges (default and 1.5 A) of Source-supplied
* current.
*/
new_cc_voltage = TC_CC_VOLT_RP_1A5;
dpm_pwr_change_notify = POWER_CHANGE_1A5;
} else if (cc == TC_CC_VOLT_RP_3A0) {
/*
* This sub-state supports Sinks consuming current within all
* three ranges (default, 1.5 A and 3.0 A) of Source-supplied
* current.
*/
new_cc_voltage = TC_CC_VOLT_RP_3A0;
dpm_pwr_change_notify = POWER_CHANGE_3A0;
} else {
/* Disconnect detected */
new_cc_voltage = TC_CC_VOLT_OPEN;
dpm_pwr_change_notify = POWER_CHANGE_0A0;
}
/* Debounce the Rp state */
if (new_cc_voltage != tc->cc_voltage) {
tc->cc_voltage = new_cc_voltage;
atomic_set_bit(&tc->flags, TC_FLAGS_RP_SUBSTATE_CHANGE);
usbc_timer_start(&tc->tc_t_rp_value_change);
}
/* Wait for Rp debounce */
if (usbc_timer_expired(&tc->tc_t_rp_value_change) == false) {
return;
}
/* Notify DPM of sink sub-state power change */
if (atomic_test_and_clear_bit(&tc->flags,
TC_FLAGS_RP_SUBSTATE_CHANGE)) {
if (data->policy_cb_notify) {
data->policy_cb_notify(dev, dpm_pwr_change_notify);
}
}
}
/**
* @brief Unattached.SNK Entry
*/
static void tc_unattached_snk_entry(void *obj)
{
LOG_INF("Unattached.SNK");
}
/**
* @brief Unattached.SNK Run
*/
static void tc_unattached_snk_run(void *obj)
{
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
const struct device *dev = tc->dev;
/*
* Transition to AttachWait.SNK when the SNK.Rp state is present
* on at least one of its CC pins.
*/
if (tcpc_is_cc_rp(tc->cc1) || tcpc_is_cc_rp(tc->cc2)) {
tc_set_state(dev, TC_ATTACH_WAIT_SNK_STATE);
}
}
/**
* @brief AttachWait.SNK Entry
*/
static void tc_attach_wait_snk_entry(void *obj)
{
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
LOG_INF("AttachWait.SNK");
tc->cc_state = TC_CC_NONE;
}
/**
* @brief AttachWait.SNK Run
*/
static void tc_attach_wait_snk_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;
bool vbus_present;
if (tcpc_is_cc_rp(tc->cc1) || tcpc_is_cc_rp(tc->cc2)) {
new_cc_state = TC_CC_DFP_ATTACHED;
} else {
new_cc_state = TC_CC_NONE;
}
/* Debounce the cc state */
if (new_cc_state != tc->cc_state) {
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) == false) {
return;
}
/* Transition to UnAttached.SNK if CC lines are open */
if (new_cc_state == TC_CC_NONE) {
tc_set_state(dev, TC_UNATTACHED_SNK_STATE);
}
/*
* The port shall transition to Attached.SNK after the state of only
* one of the CC1 or CC2 pins is SNK.Rp for at least tCCDebounce and
* VBUS is detected.
*/
vbus_present = usbc_vbus_check_level(vbus, TC_VBUS_PRESENT);
if (vbus_present) {
tc_set_state(dev, TC_ATTACHED_SNK_STATE);
}
}
static void tc_attach_wait_snk_exit(void *obj)
{
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
usbc_timer_stop(&tc->tc_t_cc_debounce);
}
/**
* @brief Attached.SNK Entry
*/
static void tc_attached_snk_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.SNK");
/* Set CC polarity */
tcpc_set_cc_polarity(tcpc, tc->cc_polarity);
/* Enable PD */
pd_enable(dev, true);
}
/**
* @brief Attached.SNK and DebugAccessory.SNK Run
*/
static void tc_attached_snk_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;
/* Detach detection */
if (usbc_vbus_check_level(vbus, TC_VBUS_PRESENT) == false) {
tc_set_state(dev, TC_UNATTACHED_SNK_STATE);
return;
}
/* Run Sink Power Sub-State if not in an explicit contract */
if (pe_is_explicit_contract(dev) == false) {
sink_power_sub_states(dev);
}
}
/**
* @brief Attached.SNK and DebugAccessory.SNK Exit
*/
static void tc_attached_snk_exit(void *obj)
{
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
const struct device *dev = tc->dev;
/* Disable PD */
pd_enable(dev, false);
}
/**
* @brief CC Open Entry
*/
static void tc_cc_open_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;
tc->cc_voltage = TC_CC_VOLT_OPEN;
/* Disable VCONN */
tcpc_set_vconn(tcpc, false);
/* Open CC lines */
tcpc_set_cc(tcpc, TC_CC_OPEN);
}
/**
* @brief Rd on CC lines Entry
*/
static void tc_cc_rd_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;
tcpc_set_cc(tcpc, TC_CC_RD);
}
/**
* @brief Disabled Entry
*/
static void tc_disabled_entry(void *obj)
{
LOG_INF("Disabled");
}
/**
* @brief Disabled Run
*/
static void tc_disabled_run(void *obj)
{
/* Do nothing */
}
/**
* @brief ErrorRecovery Entry
*/
static void tc_error_recovery_entry(void *obj)
{
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
LOG_INF("ErrorRecovery");
/* Start tErrorRecovery timer */
usbc_timer_start(&tc->tc_t_error_recovery);
}
/**
* @brief ErrorRecovery Run
*/
static void tc_error_recovery_run(void *obj)
{
struct tc_sm_t *tc = (struct tc_sm_t *)obj;
const struct device *dev = tc->dev;
/* Wait for expiry */
if (usbc_timer_expired(&tc->tc_t_error_recovery) == false) {
return;
}
/* Transition to Unattached.SNK */
tc_set_state(dev, TC_UNATTACHED_SNK_STATE);
}
/**
* @brief Type-C State Table
*/
static const struct smf_state tc_snk_states[] = {
/* Super States */
[TC_CC_OPEN_SUPER_STATE] = SMF_CREATE_STATE(
tc_cc_open_entry,
NULL,
NULL,
NULL),
[TC_CC_RD_SUPER_STATE] = SMF_CREATE_STATE(
tc_cc_rd_entry,
NULL,
NULL,
NULL),
/* Normal States */
[TC_UNATTACHED_SNK_STATE] = SMF_CREATE_STATE(
tc_unattached_snk_entry,
tc_unattached_snk_run,
NULL,
&tc_snk_states[TC_CC_RD_SUPER_STATE]),
[TC_ATTACH_WAIT_SNK_STATE] = SMF_CREATE_STATE(
tc_attach_wait_snk_entry,
tc_attach_wait_snk_run,
tc_attach_wait_snk_exit,
&tc_snk_states[TC_CC_RD_SUPER_STATE]),
[TC_ATTACHED_SNK_STATE] = SMF_CREATE_STATE(
tc_attached_snk_entry,
tc_attached_snk_run,
tc_attached_snk_exit,
NULL),
[TC_DISABLED_STATE] = SMF_CREATE_STATE(
tc_disabled_entry,
tc_disabled_run,
NULL,
&tc_snk_states[TC_CC_OPEN_SUPER_STATE]),
[TC_ERROR_RECOVERY_STATE] = SMF_CREATE_STATE(
tc_error_recovery_entry,
tc_error_recovery_run,
NULL,
&tc_snk_states[TC_CC_OPEN_SUPER_STATE]),
};

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2022 The Chromium OS Authors
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_USBC_H_
#define ZEPHYR_SUBSYS_USBC_H_
#include <zephyr/kernel.h>
#include <zephyr/usb_c/usbc.h>
#include <zephyr/smf.h>
#include "usbc_timer.h"
/**
* @brief TC Layer State Machine Object
*/
struct tc_sm_t {
/** TC layer state machine context */
struct smf_ctx ctx;
/** Port device */
const struct device *dev;
/** TC layer flags */
atomic_t flags;
/** VBUS measurement device */
const struct device *vbus_dev;
/** Port polarity */
enum tc_cc_polarity cc_polarity;
/** The cc state */
enum tc_cc_states cc_state;
/** Voltage on CC pin */
enum tc_cc_voltage_state cc_voltage;
/** Current CC1 value */
enum tc_cc_voltage_state cc1;
/** Current CC2 value */
enum tc_cc_voltage_state cc2;
/* Timers */
/** tCCDebounce timer */
struct usbc_timer_t tc_t_cc_debounce;
/** tRpValueChange timer */
struct usbc_timer_t tc_t_rp_value_change;
/** tErrorRecovery timer */
struct usbc_timer_t tc_t_error_recovery;
};
/**
* @brief This function must only be called in the subsystem init function.
*
* @param dev Pointer to the device structure for the driver instance.
*/
void tc_subsys_init(const struct device *dev);
/**
* @brief Run the TC Layer state machine. This is called from the subsystems
* port stack thread.
*
* @param dev Pointer to the device structure for the driver instance.
* @param dpm_request Device Policy Manager request
*/
void tc_run(const struct device *dev, int32_t dpm_request);
/**
* @brief Checks if the TC Layer is in an Attached state
*
* @param dev Pointer to the device structure for the driver instance.
*
* @retval true if TC Layer is in an Attached state, else false
*/
bool tc_is_in_attached_state(const struct device *dev);
#endif /* ZEPHYR_SUBSYS_USBC_TC_H_ */

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2022 The Chromium OS Authors
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbc_timer.h"
/** Timer flag to track if timer was started */
#define TIMER_STARTED 0
/** Timer flag to track if timer has expired */
#define TIMER_EXPIRED 1
/**
* @brief The timer function that's executed when the timer expires
*/
static void usbc_timer_handler(struct k_timer *timer)
{
struct usbc_timer_t *usbc_timer = k_timer_user_data_get(timer);
atomic_set_bit(&usbc_timer->flags, TIMER_EXPIRED);
}
void usbc_timer_init(struct usbc_timer_t *usbc_timer,
uint32_t timeout_ms)
{
k_timer_init(&usbc_timer->timer, usbc_timer_handler, NULL);
k_timer_user_data_set(&usbc_timer->timer, usbc_timer);
usbc_timer->timeout_ms = timeout_ms;
}
void usbc_timer_start(struct usbc_timer_t *usbc_timer)
{
atomic_clear_bit(&usbc_timer->flags, TIMER_EXPIRED);
atomic_set_bit(&usbc_timer->flags, TIMER_STARTED);
k_timer_start(&usbc_timer->timer, K_MSEC(usbc_timer->timeout_ms), K_NO_WAIT);
}
bool usbc_timer_expired(struct usbc_timer_t *usbc_timer)
{
bool started = atomic_test_bit(&usbc_timer->flags, TIMER_STARTED);
bool expired = atomic_test_bit(&usbc_timer->flags, TIMER_EXPIRED);
if (started & expired) {
atomic_clear_bit(&usbc_timer->flags, TIMER_STARTED);
return true;
}
return false;
}
bool usbc_timer_running(struct usbc_timer_t *usbc_timer)
{
return atomic_test_bit(&usbc_timer->flags, TIMER_STARTED);
}
void usbc_timer_stop(struct usbc_timer_t *usbc_timer)
{
atomic_clear_bit(&usbc_timer->flags, TIMER_STARTED);
k_timer_stop(&usbc_timer->timer);
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2022 The Chromium OS Authors
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_USBC_TIMER_H_
#define ZEPHYR_SUBSYS_USBC_TIMER_H_
#include <zephyr/kernel.h>
/**
* @brief USB-C Timer Object
*/
struct usbc_timer_t {
/** kernel timer */
struct k_timer timer;
/** timeout value in ms */
uint32_t timeout_ms;
/** flags to track timer status */
atomic_t flags;
};
/**
* @brief Initialize a timer
*
* @param usbc_timer timer object
* @param timeout_ms timer timeout in ms
*/
void usbc_timer_init(struct usbc_timer_t *usbc_timer,
uint32_t timeout_ms);
/**
* @brief Start a timer
*
* @param usbc_timer timer object
*/
void usbc_timer_start(struct usbc_timer_t *usbc_timer);
/**
* @brief Check if a timer has expired
*
* @param usbc_timer timer object
* @retval true if the timer has expired
*/
bool usbc_timer_expired(struct usbc_timer_t *usbc_timer);
/**
* @brief Check if a timer has been started
*
* @param usbc_timer timer object
* @retval true if the timer is running
*/
bool usbc_timer_running(struct usbc_timer_t *usbc_timer);
/**
* @brief Stop a timer
*
* @param usbc_timer timer object
*/
void usbc_timer_stop(struct usbc_timer_t *usbc_timer);
#endif /* ZEPHYR_SUBSYS_USBC_TIMER_H_ */