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:
parent
a1e6e9dfbe
commit
e90f1b66d8
15 changed files with 4897 additions and 0 deletions
290
include/zephyr/usb_c/usbc.h
Normal file
290
include/zephyr/usb_c/usbc.h
Normal 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_ */
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
5
subsys/usb/usb_c/CMakeLists.txt
Normal file
5
subsys/usb/usb_c/CMakeLists.txt
Normal 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
40
subsys/usb/usb_c/Kconfig
Normal 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
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
221
subsys/usb/usb_c/usbc_pe.h
Normal 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
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
191
subsys/usb/usb_c/usbc_prl.h
Normal 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_ */
|
275
subsys/usb/usb_c/usbc_stack.c
Normal file
275
subsys/usb/usb_c/usbc_stack.c
Normal 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;
|
||||||
|
}
|
161
subsys/usb/usb_c/usbc_stack.h
Normal file
161
subsys/usb/usb_c/usbc_stack.h
Normal 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
522
subsys/usb/usb_c/usbc_tc.c
Normal 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]),
|
||||||
|
};
|
73
subsys/usb/usb_c/usbc_tc.h
Normal file
73
subsys/usb/usb_c/usbc_tc.h
Normal 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_ */
|
61
subsys/usb/usb_c/usbc_timer.c
Normal file
61
subsys/usb/usb_c/usbc_timer.c
Normal 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);
|
||||||
|
}
|
63
subsys/usb/usb_c/usbc_timer.h
Normal file
63
subsys/usb/usb_c/usbc_timer.h
Normal 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_ */
|
Loading…
Add table
Add a link
Reference in a new issue