pd: Add rt1715 driver initial version

PD/rt1715 initial sample code from Richtek  

Change-Id: Ide34829746b768c5ed8e7bdd5efd3567d7dd7886
Reviewed-on: https://gerrit.mot.com/2038928
Reviewed-by: Huosheng Liao <liaohs@motorola.com>
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Submit-Approved: Jira Key
This commit is contained in:
chailu1 2021-08-12 21:09:10 +08:00 • committed by Lu Chai
commit 94c57f4137
61 changed files with 38217 additions and 0 deletions

View file

@ -0,0 +1,34 @@
DLKM_DIR := motorola/kernel/modules
LOCAL_PATH := $(call my-dir)
#ifeq ($(TCPC_RT1711H),true)
KERNEL_CFLAGS += CONFIG_TCPC_RT1711H=y
#endif
#ifeq ($(TCPC_CLASS),true)
KERNEL_CFLAGS += CONFIG_TCPC_CLASS=y
#endif
#ifeq ($(USB_POWER_DELIVERY),true)
KERNEL_CFLAGS += CONFIG_USB_POWER_DELIVERY=y
#endif
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := tcpc_rt1711h.ko
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
include $(DLKM_DIR)/AndroidKernelModule.mk
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := tcpc_class.ko
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
include $(DLKM_DIR)/AndroidKernelModule.mk
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := rt_pd_manager.ko
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
include $(DLKM_DIR)/AndroidKernelModule.mk

View file

@ -0,0 +1,37 @@
# add -Wall to try to catch everything we can.
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \
-I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/usb/typec/mmi_tcpc
subdir-ccflags-y += -Wall -Werror -DCONFIG_RT_REGMAP
#ifneq ($(filter m y,$(CONFIG_TCPC_CLASS)),)
EXTRA_CFLAGS += -DCONFIG_TCPC_CLASS
#endif
#ifneq ($(filter m y,$(CONFIG_USB_POWER_DELIVERY)),)
EXTRA_CFLAGS += -DCONFIG_USB_POWER_DELIVERY
#endif
#ifneq ($(filter m y,$(CONFIG_TCPC_RT1711H)),)
obj-m += tcpc_rt1711h.o
#endif
tcpc_class-objs += tcpci_core.o tcpci_typec.o tcpci_timer.o \
tcpm.o tcpci.o pd_dbg_info.o tcpci_alert.o rt-regmap.o tcpci_dual_role.o
#ifneq ($(filter m y,$(CONFIG_USB_POWER_DELIVERY)),)
tcpc_class-objs += tcpci_event.o \
pd_core.o pd_policy_engine.o pd_process_evt.o \
pd_dpm_core.o pd_dpm_uvdm.o pd_dpm_alt_mode_dp.o pd_dpm_pdo_select.o\
pd_dpm_reaction.o \
pd_process_evt_snk.o pd_process_evt_src.o pd_process_evt_vdm.o \
pd_process_evt_drs.o pd_process_evt_prs.o pd_process_evt_vcs.o \
pd_process_evt_dbg.o pd_process_evt_tcp.o pd_process_evt_com.o \
pd_policy_engine_src.o pd_policy_engine_snk.o pd_policy_engine_ufp.o pd_policy_engine_vcs.o \
pd_policy_engine_dfp.o pd_policy_engine_dr.o pd_policy_engine_drs.o pd_policy_engine_prs.o \
pd_policy_engine_dbg.o pd_policy_engine_com.o pd_dpm_alt_mode_dc.o
#endif
obj-m += tcpc_class.o
obj-m += rt_pd_manager.o

View file

@ -0,0 +1,11 @@
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
modules_install:
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
clean:
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef PD_DBG_INFO_H_INCLUDED
#define PD_DBG_INFO_H_INCLUDED
#include <linux/kernel.h>
#include <linux/module.h>
#include "tcpci_config.h"
#ifdef CONFIG_PD_DBG_INFO
extern int pd_dbg_info(const char *fmt, ...);
extern void pd_dbg_info_lock(void);
extern void pd_dbg_info_unlock(void);
extern int pd_dbg_info_init(void);
extern void pd_dbg_info_exit(void);
#else
static inline int pd_dbg_info(const char *fmt, ...)
{
return 0;
}
static inline void pd_dbg_info_lock(void) {}
static inline void pd_dbg_info_unlock(void) {}
static inline int pd_dbg_info_init(void)
{
return 0;
}
static inline void pd_dbg_info_exit(void) {}
#endif /* CONFIG_PD_DBG_INFO */
#endif /* PD_DBG_INFO_H_INCLUDED */

View file

@ -0,0 +1,405 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef PD_DPM_CORE_H
#define PD_DPM_CORE_H
#include "tcpci.h"
#include "pd_core.h"
#include "pd_process_evt.h"
/* ---- MISC ---- */
int pd_dpm_core_init(struct pd_port *pd_port);
int pd_dpm_enable_vconn(struct pd_port *pd_port, bool en);
int pd_dpm_send_sink_caps(struct pd_port *pd_port);
int pd_dpm_send_source_caps(struct pd_port *pd_port);
void pd_dpm_inform_cable_id(struct pd_port *pd_port, bool src_startup);
void pd_dpm_dynamic_enable_vconn(struct pd_port *pd_port);
void pd_dpm_dynamic_disable_vconn(struct pd_port *pd_port);
/* ---- SNK ---- */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
void pd_dpm_start_pps_request_thread(struct pd_port *pd_port, bool en);
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
int pd_dpm_update_tcp_request(struct pd_port *pd_port,
struct tcp_dpm_pd_request *pd_req);
int pd_dpm_update_tcp_request_ex(struct pd_port *pd_port,
struct tcp_dpm_pd_request_ex *pd_req);
int pd_dpm_update_tcp_request_again(struct pd_port *pd_port);
void pd_dpm_snk_evaluate_caps(struct pd_port *pd_port);
void pd_dpm_snk_standby_power(struct pd_port *pd_port);
void pd_dpm_snk_transition_power(struct pd_port *pd_port);
void pd_dpm_snk_hard_reset(struct pd_port *pd_port);
/* ---- SRC ---- */
void pd_dpm_src_evaluate_request(struct pd_port *pd_port);
void pd_dpm_src_transition_power(struct pd_port *pd_port);
void pd_dpm_src_hard_reset(struct pd_port *pd_port);
/* ---- UFP : Handle VDM Request ---- */
void pd_dpm_ufp_request_id_info(struct pd_port *pd_port);
void pd_dpm_ufp_request_svid_info(struct pd_port *pd_port);
void pd_dpm_ufp_request_mode_info(struct pd_port *pd_port);
void pd_dpm_ufp_request_enter_mode(struct pd_port *pd_port);
void pd_dpm_ufp_request_exit_mode(struct pd_port *pd_port);
/* ---- UFP : DP Only ---- */
#ifdef CONFIG_USB_PD_ALT_MODE
int pd_dpm_ufp_request_dp_status(struct pd_port *pd_port);
int pd_dpm_ufp_request_dp_config(struct pd_port *pd_port);
void pd_dpm_ufp_send_dp_attention(struct pd_port *pd_port);
#endif
/* ---- DFP : Inform VDM Result ---- */
void pd_dpm_dfp_inform_id(struct pd_port *pd_port, bool ack);
void pd_dpm_dfp_inform_svids(struct pd_port *pd_port, bool ack);
void pd_dpm_dfp_inform_modes(struct pd_port *pd_port, bool ack);
void pd_dpm_dfp_inform_enter_mode(struct pd_port *pd_port, bool ack);
void pd_dpm_dfp_inform_exit_mode(struct pd_port *pd_port);
void pd_dpm_dfp_inform_attention(struct pd_port *pd_port);
/* ---- DFP : DP Only ---- */
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
void pd_dpm_dfp_send_dp_status_update(struct pd_port *pd_port);
void pd_dpm_dfp_inform_dp_status_update(struct pd_port *pd_port, bool ack);
void pd_dpm_dfp_send_dp_configuration(struct pd_port *pd_port);
void pd_dpm_dfp_inform_dp_configuration(struct pd_port *pd_port, bool ack);
#endif
/* ---- SVDM/UVDM ---- */
#ifdef CONFIG_USB_PD_CUSTOM_VDM
void pd_dpm_ufp_recv_uvdm(struct pd_port *pd_port);
void pd_dpm_dfp_send_uvdm(struct pd_port *pd_port);
void pd_dpm_dfp_inform_uvdm(struct pd_port *pd_port, bool ack);
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
/* ---- DRP : Inform PowerCap ---- */
void pd_dpm_dr_inform_sink_cap(struct pd_port *pd_port);
void pd_dpm_dr_inform_source_cap(struct pd_port *pd_port);
/* ---- DRP : Data Role Swap ---- */
void pd_dpm_drs_evaluate_swap(struct pd_port *pd_port, uint8_t role);
void pd_dpm_drs_change_role(struct pd_port *pd_port, uint8_t role);
/* ---- DRP : Power Role Swap ---- */
void pd_dpm_prs_evaluate_swap(struct pd_port *pd_port, uint8_t role);
void pd_dpm_prs_turn_off_power_sink(struct pd_port *pd_port);
void pd_dpm_prs_enable_power_source(struct pd_port *pd_port, bool en);
void pd_dpm_prs_change_role(struct pd_port *pd_port, uint8_t role);
/* ---- DRP : Vconn Swap ---- */
void pd_dpm_vcs_evaluate_swap(struct pd_port *pd_port);
void pd_dpm_vcs_enable_vconn(struct pd_port *pd_port, uint8_t role);
/*
* PE : PD3.0
*/
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
int pd_dpm_send_source_cap_ext(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
void pd_dpm_inform_source_cap_ext(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
int pd_dpm_send_battery_cap(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
void pd_dpm_inform_battery_cap(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
int pd_dpm_send_battery_status(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
void pd_dpm_inform_battery_status(struct pd_port *pd_port);
#endif /* #ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
int pd_dpm_send_mfrs_info(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
void pd_dpm_inform_mfrs_info(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
int pd_dpm_send_country_codes(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
void pd_dpm_inform_country_codes(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL
int pd_dpm_send_country_info(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
void pd_dpm_inform_country_info(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
int pd_dpm_send_alert(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
void pd_dpm_inform_alert(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
int pd_dpm_send_status(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
void pd_dpm_inform_status(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
void pd_dpm_inform_pps_status(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
void pd_dpm_inform_not_support(struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30 */
/* PE : Notify DPM */
int pd_dpm_notify_pe_startup(struct pd_port *pd_port);
int pd_dpm_notify_pe_hardreset(struct pd_port *pd_port);
/* TCPCI - VBUS Control */
static inline int pd_dpm_check_vbus_valid(struct pd_port *pd_port)
{
return tcpci_check_vbus_valid(pd_port->tcpc);
}
static inline int pd_dpm_sink_vbus(struct pd_port *pd_port, bool en)
{
int mv = en ? TCPC_VBUS_SINK_5V : TCPC_VBUS_SINK_0V;
return tcpci_sink_vbus(pd_port->tcpc,
TCP_VBUS_CTRL_REQUEST, mv, -1);
}
static inline int pd_dpm_source_vbus(struct pd_port *pd_port, bool en)
{
int mv = en ? TCPC_VBUS_SOURCE_5V : TCPC_VBUS_SOURCE_0V;
return tcpci_source_vbus(pd_port->tcpc,
TCP_VBUS_CTRL_REQUEST, mv, -1);
}
/* Mode Operations */
#ifdef CONFIG_USB_PD_ALT_MODE
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
extern bool dp_dfp_u_notify_discover_id(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
extern bool dp_dfp_u_notify_discover_svid(
struct pd_port *pd_port, struct svdm_svid_data *svid_data, bool ack);
extern bool dp_dfp_u_notify_discover_modes(
struct pd_port *pd_port, struct svdm_svid_data *svid_data, bool ack);
extern bool dp_dfp_u_notify_enter_mode(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, uint8_t ops, bool ack);
extern bool dp_dfp_u_notify_exit_mode(
struct pd_port *pd_port, struct svdm_svid_data *svid_data, uint8_t ops);
extern bool dp_dfp_u_notify_attention(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
extern void dp_ufp_u_request_enter_mode(
struct pd_port *pd_port, struct svdm_svid_data *svid_data, uint8_t ops);
extern void dp_ufp_u_request_exit_mode(
struct pd_port *pd_port, struct svdm_svid_data *svid_data, uint8_t ops);
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
extern bool dp_dfp_u_notify_pe_startup(
struct pd_port *pd_port, struct svdm_svid_data *svid_data);
extern int dp_dfp_u_notify_pe_ready(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
extern bool dp_reset_state(
struct pd_port *pd_port, struct svdm_svid_data *svid_data);
extern bool dp_parse_svid_data(
struct pd_port *pd_port, struct svdm_svid_data *svid_data);
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifdef CONFIG_USB_PD_RICHTEK_UVDM
extern bool richtek_dfp_notify_pe_startup(
struct pd_port *pd_port, struct svdm_svid_data *svid_data);
extern int richtek_dfp_notify_pe_ready(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
extern bool richtek_dfp_notify_uvdm(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
extern bool richtek_ufp_notify_uvdm(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
#endif /* CONFIG_USB_PD_RICHTEK_UVDM */
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
extern bool dc_dfp_notify_discover_id(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
extern bool dc_dfp_notify_discover_svid(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
extern bool dc_dfp_notify_discover_modes(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
extern bool dc_dfp_notify_enter_mode(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, uint8_t ops, bool ack);
extern bool dc_dfp_notify_exit_mode(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, uint8_t ops);
extern bool dc_dfp_notify_pe_startup(
struct pd_port *pd_port, struct svdm_svid_data *svid_data);
extern int dc_dfp_notify_pe_ready(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
extern bool dc_dfp_notify_uvdm(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
extern bool dc_ufp_notify_uvdm(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
extern bool dc_reset_state(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
extern bool dc_parse_svid_data(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
/**
* pd_dpm_get_ready_reaction
*
* Get a pending event from the reaction's table definied by DPM.
*
* Returns TCP_DPM_EVT_ID if succeeded;
* Returns Zero to indicate there is no pending event.
* Returns DPM_READY_REACTION_BUSY to indicate
* waiting for previous reaction is finished.
*/
#define DPM_READY_REACTION_BUSY (0xff)
extern uint8_t pd_dpm_get_ready_reaction(struct pd_port *pd_port);
/* ---- DPM reactions ---- */
/* If receive reject/wait, cancel reaction */
#define DPM_REACTION_REQUEST_PR_SWAP (1<<0)
#define DPM_REACTION_REQUEST_DR_SWAP (1<<1)
#define DPM_REACTION_GET_SINK_CAP (1<<2)
#define DPM_REACTION_GET_SOURCE_CAP (1<<3)
#define DPM_REACTION_ATTEMPT_GET_FLAG (1<<4)
#define DPM_REACTION_REQUEST_VCONN_SRC (1<<5)
#define DPM_REACTION_RETURN_VCONN_SRC (1<<6)
#define DPM_REACTION_REJECT_CANCEL (1<<7) /* FLAG ONLY */
#define DPM_REACTION_DFP_FLOW_DELAY (1<<10)
#define DPM_REACTION_UFP_FLOW_DELAY (1<<11)
#define DPM_REACTION_VCONN_STABLE_DELAY (1<<12)
#define DPM_REACTION_DISCOVER_ID (1<<16)
#define DPM_REACTION_DISCOVER_SVID (1<<17)
#define DPM_REACTION_DISCOVER_CABLE (1<<18)
#define DPM_REACTION_DYNAMIC_VCONN (1<<19)
#define DPM_REACTION_DISCOVER_CABLE_FLOW (\
DPM_REACTION_DISCOVER_CABLE | \
DPM_REACTION_REQUEST_VCONN_SRC |\
DPM_REACTION_VCONN_STABLE_DELAY |\
DPM_REACTION_DYNAMIC_VCONN)
#define DPM_REACTION_GET_SOURCE_CAP_EXT (1<<24)
#define DPM_REACTION_CAP_RESET_CABLE (1<<28)
#define DPM_REACTION_CAP_READY_ONCE (1<<29)
#define DPM_REACTION_CAP_DISCOVER_CABLE (1<<30)
#define DPM_REACTION_CAP_ALWAYS (1<<31)
static inline void dpm_reaction_clear(struct pd_port *pd_port, uint32_t mask)
{
pd_port->pe_data.dpm_ready_reactions &= ~mask;
}
static inline void dpm_reaction_set(struct pd_port *pd_port, uint32_t mask)
{
pd_port->pe_data.dpm_ready_reactions |= mask;
}
static inline void dpm_reaction_set_ready_once(struct pd_port *pd_port)
{
if (pd_check_pe_state_ready(pd_port))
dpm_reaction_set(pd_port, DPM_REACTION_CAP_READY_ONCE);
}
static inline void dpm_reaction_set_clear(
struct pd_port *pd_port, uint32_t set, uint32_t clear)
{
uint32_t val = pd_port->pe_data.dpm_ready_reactions | set;
pd_port->pe_data.dpm_ready_reactions = val & (~clear);
}
static inline uint32_t dpm_reaction_check(
struct pd_port *pd_port, uint32_t mask)
{
return pd_port->pe_data.dpm_ready_reactions & mask;
}
#endif /* PD_DPM_CORE_H */

View file

@ -0,0 +1,63 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef PD_DPM_PDO_SELECT_H
#define PD_DPM_PDO_SELECT_H
#include "inc/tcpci.h"
struct dpm_pdo_info_t {
uint8_t type;
uint8_t apdo_type;
uint8_t pwr_limit;
int vmin;
int vmax;
int uw;
int ma;
};
struct dpm_rdo_info_t {
uint8_t pos;
uint8_t type;
bool mismatch;
int vmin;
int vmax;
union {
uint32_t max_uw;
uint32_t max_ma;
};
union {
uint32_t oper_uw;
uint32_t oper_ma;
};
};
#define DPM_PDO_TYPE_FIXED TCPM_POWER_CAP_VAL_TYPE_FIXED
#define DPM_PDO_TYPE_VAR TCPM_POWER_CAP_VAL_TYPE_VARIABLE
#define DPM_PDO_TYPE_BAT TCPM_POWER_CAP_VAL_TYPE_BATTERY
#define DPM_PDO_TYPE_APDO TCPM_POWER_CAP_VAL_TYPE_AUGMENT
#define DPM_APDO_TYPE_PPS (TCPM_POWER_CAP_APDO_TYPE_PPS)
#define DPM_APDO_TYPE_PPS_CF (TCPM_POWER_CAP_APDO_TYPE_PPS_CF)
extern void dpm_extract_pdo_info(
uint32_t pdo, struct dpm_pdo_info_t *info);
extern bool dpm_find_match_req_info(struct dpm_rdo_info_t *req_info,
struct dpm_pdo_info_t *sink, int cnt, uint32_t *src_pdos,
int min_uw, uint32_t select_rule);
#endif /* PD_DPM_PDO_SELECT_H */

View file

@ -0,0 +1,933 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef PD_POLICY_ENGINE_H_
#define PD_POLICY_ENGINE_H_
#include "pd_core.h"
/* ---- Policy Engine State ---- */
enum pd_pe_state_machine {
PE_STATE_MACHINE_IDLE = 0,
PE_STATE_MACHINE_NORMAL,
PE_STATE_MACHINE_DR_SWAP,
PE_STATE_MACHINE_PR_SWAP,
PE_STATE_MACHINE_VCONN_SWAP,
};
/* ---- Policy Engine Runtime Flags ---- */
#define PE_STATE_FLAG_BACK_READY_IF_RECV_WAIT (1<<0)
#define PE_STATE_FLAG_BACK_READY_IF_RECV_REJECT (1<<1)
#define PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT (1<<2)
#define PE_STATE_FLAG_BACK_READY_IF_TX_FAILED (1<<3)
#define PE_STATE_FLAG_HRESET_IF_SR_TIMEOUT (1<<4)
#define PE_STATE_FLAG_HRESET_IF_TX_FAILED (1<<5)
#define PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT (1<<6)
#define PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER (1<<7)
#define PE_STATE_WAIT_RESPONSE(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; }
#define PE_STATE_WAIT_MSG(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT |\
PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; }
#define PE_STATE_WAIT_MSG_HRESET_IF_TOUT(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_HRESET_IF_SR_TIMEOUT |\
PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; }
#define PE_STATE_WAIT_MSG_OR_TX_FAILED(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_BACK_READY_IF_TX_FAILED |\
PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT |\
PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; }
#define PE_STATE_WAIT_MSG_OR_RJ(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_BACK_READY_IF_RECV_REJECT |\
PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT |\
PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; }
#define PE_STATE_WAIT_ANSWER_MSG(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_BACK_READY_IF_RECV_WAIT |\
PE_STATE_FLAG_BACK_READY_IF_RECV_REJECT |\
PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT |\
PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; }
#define PE_STATE_HRESET_IF_TX_FAILED(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_HRESET_IF_TX_FAILED; }
#define PE_STATE_IGNORE_UNKNOWN_EVENT(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT; }
#define PE_STATE_RECV_SOFT_RESET(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_HRESET_IF_TX_FAILED | \
PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT; }
#define PE_STATE_SEND_SOFT_RESET(pd_port) {\
pd_port->pe_data.pe_state_flags = \
PE_STATE_FLAG_HRESET_IF_TX_FAILED |\
PE_STATE_FLAG_HRESET_IF_SR_TIMEOUT |\
PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT |\
PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; }
#define PE_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC (1<<0)
#define PE_STATE_FLAG_BACK_READY_IF_DPM_ACK (1<<1)
#define PE_STATE_FLAG_DPM_ACK_IMMEDIATELY (1<<7)
#define PE_STATE_WAIT_TX_SUCCESS(pd_port) {\
pd_port->pe_data.pe_state_flags2 = \
PE_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC; }
#define PE_STATE_DPM_INFORMED(pd_port) {\
pd_port->pe_data.pe_state_flags2 = \
PE_STATE_FLAG_BACK_READY_IF_DPM_ACK |\
PE_STATE_FLAG_DPM_ACK_IMMEDIATELY; }
#define PE_STATE_WAIT_DPM_ACK(pd_port) {\
pd_port->pe_data.pe_state_flags2 = \
PE_STATE_FLAG_BACK_READY_IF_DPM_ACK; }
#define PE_STATE_DPM_ACK_IMMEDIATELY(pd_port) {\
pd_port->pe_data.pe_state_flags2 |= \
PE_STATE_FLAG_DPM_ACK_IMMEDIATELY; }
#define VDM_STATE_FLAG_ENABLE_VDM_RESPONSE_TIMER (1<<0)
#define VDM_STATE_FLAG_DPM_ACK_IMMEDIATELY (1<<4)
#define VDM_STATE_FLAG_BACK_READY_IF_DPM_ACK (1<<6)
#define VDM_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC (1<<7)
#define VDM_STATE_DPM_INFORMED(pd_port) {\
pd_port->pe_data.vdm_state_flags = \
VDM_STATE_FLAG_BACK_READY_IF_DPM_ACK |\
VDM_STATE_FLAG_DPM_ACK_IMMEDIATELY; }
#define VDM_STATE_REPLY_SVDM_REQUEST(pd_port) {\
pd_port->pe_data.vdm_state_flags = \
VDM_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC; }
#define VDM_STATE_NORESP_CMD(pd_port) {\
pd_port->pe_data.vdm_state_flags = \
VDM_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC; }
#define VDM_STATE_RESPONSE_CMD(pd_port, timer_id) {\
pd_port->pe_data.vdm_state_flags = \
VDM_STATE_FLAG_ENABLE_VDM_RESPONSE_TIMER; \
pd_port->pe_data.vdm_state_timer = timer_id; }
static inline bool pd_check_pe_during_hard_reset(struct pd_port *pd_port)
{
return pd_port->tcpc->pd_wait_hard_reset_complete;
}
enum pd_pe_state {
PE_STATE_START_ID = -1,
/******************* Source *******************/
#ifdef CONFIG_USB_PD_PE_SOURCE
PE_SRC_STARTUP,
PE_SRC_DISCOVERY,
PE_SRC_SEND_CAPABILITIES,
PE_SRC_NEGOTIATE_CAPABILITIES,
PE_SRC_TRANSITION_SUPPLY,
PE_SRC_TRANSITION_SUPPLY2,
PE_SRC_READY,
PE_SRC_DISABLED,
PE_SRC_CAPABILITY_RESPONSE,
PE_SRC_HARD_RESET,
PE_SRC_HARD_RESET_RECEIVED,
PE_SRC_TRANSITION_TO_DEFAULT,
PE_SRC_GET_SINK_CAP,
PE_SRC_WAIT_NEW_CAPABILITIES,
PE_SRC_SEND_SOFT_RESET,
PE_SRC_SOFT_RESET,
/* Source Startup Discover Cable */
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
#ifdef CONFIG_PD_SRC_RESET_CABLE
PE_SRC_CBL_SEND_SOFT_RESET,
#endif /* CONFIG_PD_SRC_RESET_CABLE */
PE_SRC_VDM_IDENTITY_REQUEST,
PE_SRC_VDM_IDENTITY_ACKED,
PE_SRC_VDM_IDENTITY_NAKED,
#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */
/* Source for PD30 */
#ifdef CONFIG_USB_PD_REV30
PE_SRC_SEND_NOT_SUPPORTED,
PE_SRC_NOT_SUPPORTED_RECEIVED,
PE_SRC_CHUNK_RECEIVED,
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
PE_SRC_SEND_SOURCE_ALERT,
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
PE_SRC_SINK_ALERT_RECEIVED,
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
PE_SRC_GIVE_SOURCE_CAP_EXT,
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
PE_SRC_GIVE_SOURCE_STATUS,
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
PE_SRC_GET_SINK_STATUS,
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE
PE_SRC_GIVE_PPS_STATUS,
#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */
#endif /* CONFIG_USB_PD_REV30 */
#endif /* CONFIG_USB_PD_PE_SOURCE */
/******************* Sink *******************/
#ifdef CONFIG_USB_PD_PE_SINK
/* Sink Init */
PE_SNK_STARTUP,
PE_SNK_DISCOVERY,
PE_SNK_WAIT_FOR_CAPABILITIES,
PE_SNK_EVALUATE_CAPABILITY,
PE_SNK_SELECT_CAPABILITY,
PE_SNK_TRANSITION_SINK,
PE_SNK_READY,
PE_SNK_HARD_RESET,
PE_SNK_TRANSITION_TO_DEFAULT,
PE_SNK_GIVE_SINK_CAP,
PE_SNK_GET_SOURCE_CAP,
PE_SNK_SEND_SOFT_RESET,
PE_SNK_SOFT_RESET,
/* Sink for PD30 */
#ifdef CONFIG_USB_PD_REV30
PE_SNK_SEND_NOT_SUPPORTED,
PE_SNK_NOT_SUPPORTED_RECEIVED,
PE_SNK_CHUNK_RECEIVED,
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
PE_SNK_SOURCE_ALERT_RECEIVED,
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
PE_SNK_SEND_SINK_ALERT,
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
PE_SNK_GET_SOURCE_CAP_EXT,
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
PE_SNK_GET_SOURCE_STATUS,
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
PE_SNK_GIVE_SINK_STATUS,
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
PE_SNK_GET_PPS_STATUS,
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
#endif /* CONFIG_USB_PD_REV30 */
#endif /* CONFIG_USB_PD_PE_SINK */
/******************* DR_SWAP *******************/
#ifdef CONFIG_USB_PD_DR_SWAP
/* DR_SWAP_DFP */
PE_DRS_DFP_UFP_EVALUATE_DR_SWAP,
PE_DRS_DFP_UFP_ACCEPT_DR_SWAP,
PE_DRS_DFP_UFP_CHANGE_TO_UFP,
PE_DRS_DFP_UFP_SEND_DR_SWAP,
PE_DRS_DFP_UFP_REJECT_DR_SWAP,
/* DR_SWAP_UFP */
PE_DRS_UFP_DFP_EVALUATE_DR_SWAP,
PE_DRS_UFP_DFP_ACCEPT_DR_SWAP,
PE_DRS_UFP_DFP_CHANGE_TO_DFP,
PE_DRS_UFP_DFP_SEND_DR_SWAP,
PE_DRS_UFP_DFP_REJECT_DR_SWAP,
#endif /* CONFIG_USB_PD_DR_SWAP */
/******************* PR_SWAP *******************/
#ifdef CONFIG_USB_PD_PR_SWAP
/* PR_SWAP_SRC */
PE_PRS_SRC_SNK_EVALUATE_PR_SWAP,
PE_PRS_SRC_SNK_ACCEPT_PR_SWAP,
PE_PRS_SRC_SNK_TRANSITION_TO_OFF,
PE_PRS_SRC_SNK_ASSERT_RD,
PE_PRS_SRC_SNK_WAIT_SOURCE_ON,
PE_PRS_SRC_SNK_SEND_SWAP,
PE_PRS_SRC_SNK_REJECT_PR_SWAP,
/* PR_SWAP_SNK */
PE_PRS_SNK_SRC_EVALUATE_PR_SWAP,
PE_PRS_SNK_SRC_ACCEPT_PR_SWAP,
PE_PRS_SNK_SRC_TRANSITION_TO_OFF,
PE_PRS_SNK_SRC_ASSERT_RP,
PE_PRS_SNK_SRC_SOURCE_ON,
PE_PRS_SNK_SRC_SEND_SWAP,
PE_PRS_SNK_SRC_REJECT_SWAP,
/* get same role cap */
PE_DR_SRC_GET_SOURCE_CAP,
PE_DR_SRC_GIVE_SINK_CAP,
PE_DR_SNK_GET_SINK_CAP,
PE_DR_SNK_GIVE_SOURCE_CAP,
/* get same role cap for PD30 */
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
PE_DR_SNK_GIVE_SOURCE_CAP_EXT,
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
PE_DR_SRC_GET_SOURCE_CAP_EXT,
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
#endif /* CONFIG_USB_PD_REV30 */
#endif /* CONFIG_USB_PD_PR_SWAP */
/******************* VCONN_SWAP *******************/
#ifdef CONFIG_USB_PD_VCONN_SWAP
PE_VCS_SEND_SWAP,
PE_VCS_EVALUATE_SWAP,
PE_VCS_ACCEPT_SWAP,
PE_VCS_REJECT_VCONN_SWAP,
PE_VCS_WAIT_FOR_VCONN,
PE_VCS_TURN_OFF_VCONN,
PE_VCS_TURN_ON_VCONN,
PE_VCS_SEND_PS_RDY,
#endif /* CONFIG_USB_PD_VCONN_SWAP */
/******************* UFP_VDM *******************/
PE_UFP_VDM_GET_IDENTITY,
PE_UFP_VDM_GET_SVIDS,
PE_UFP_VDM_GET_MODES,
PE_UFP_VDM_EVALUATE_MODE_ENTRY,
PE_UFP_VDM_MODE_EXIT,
PE_UFP_VDM_ATTENTION_REQUEST,
#ifdef CONFIG_USB_PD_ALT_MODE
PE_UFP_VDM_DP_STATUS_UPDATE,
PE_UFP_VDM_DP_CONFIGURE,
#endif/* CONFIG_USB_PD_ALT_MODE */
/******************* DFP_VDM *******************/
PE_DFP_UFP_VDM_IDENTITY_REQUEST,
PE_DFP_UFP_VDM_IDENTITY_ACKED,
PE_DFP_UFP_VDM_IDENTITY_NAKED,
PE_DFP_CBL_VDM_IDENTITY_REQUEST,
PE_DFP_CBL_VDM_IDENTITY_ACKED,
PE_DFP_CBL_VDM_IDENTITY_NAKED,
PE_DFP_VDM_SVIDS_REQUEST,
PE_DFP_VDM_SVIDS_ACKED,
PE_DFP_VDM_SVIDS_NAKED,
PE_DFP_VDM_MODES_REQUEST,
PE_DFP_VDM_MODES_ACKED,
PE_DFP_VDM_MODES_NAKED,
PE_DFP_VDM_MODE_ENTRY_REQUEST,
PE_DFP_VDM_MODE_ENTRY_ACKED,
PE_DFP_VDM_MODE_ENTRY_NAKED,
PE_DFP_VDM_MODE_EXIT_REQUEST,
PE_DFP_VDM_MODE_EXIT_ACKED,
PE_DFP_VDM_ATTENTION_REQUEST,
#ifdef CONFIG_PD_DFP_RESET_CABLE
PE_DFP_CBL_SEND_SOFT_RESET,
PE_DFP_CBL_SEND_CABLE_RESET,
#endif /* CONFIG_PD_DFP_RESET_CABLE */
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
PE_DFP_VDM_DP_STATUS_UPDATE_REQUEST,
PE_DFP_VDM_DP_STATUS_UPDATE_ACKED,
PE_DFP_VDM_DP_STATUS_UPDATE_NAKED,
PE_DFP_VDM_DP_CONFIGURATION_REQUEST,
PE_DFP_VDM_DP_CONFIGURATION_ACKED,
PE_DFP_VDM_DP_CONFIGURATION_NAKED,
#endif/* CONFIG_USB_PD_ALT_MODE_DFP */
/******************* UVDM & SVDM *******************/
#ifdef CONFIG_USB_PD_CUSTOM_VDM
PE_UFP_UVDM_RECV,
PE_DFP_UVDM_SEND,
PE_DFP_UVDM_ACKED,
PE_DFP_UVDM_NAKED,
#endif/* CONFIG_USB_PD_CUSTOM_VDM */
/******************* PD30 Common *******************/
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
PE_GET_BATTERY_CAP,
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
PE_GIVE_BATTERY_CAP,
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
PE_GET_BATTERY_STATUS,
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
PE_GIVE_BATTERY_STATUS,
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
PE_GET_MANUFACTURER_INFO,
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
PE_GIVE_MANUFACTURER_INFO,
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
PE_GET_COUNTRY_CODES,
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
PE_GIVE_COUNTRY_CODES,
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
PE_GET_COUNTRY_INFO,
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL
PE_GIVE_COUNTRY_INFO,
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */
PE_VDM_NOT_SUPPORTED,
#endif /* CONFIG_USB_PD_REV30 */
/******************* Others *******************/
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
PE_DBG_READY,
#endif/* CONFIG_USB_PD_CUSTOM_DBGACC */
#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER
PE_OVER_RECV_HRESET_LIMIT,
#endif/* CONFIG_USB_PD_RECV_HRESET_COUNTER */
PE_REJECT,
PE_ERROR_RECOVERY,
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
PE_ERROR_RECOVERY_ONCE,
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
PE_BIST_TEST_DATA,
PE_BIST_CARRIER_MODE_2,
/* Wait tx finished */
PE_IDLE1,
PE_IDLE2,
PD_NR_PE_STATES,
};
/**
* pd_policy_engine_run
*
* Driving the PE to get next event, and take the corresponding actions.
*
*
* Returns True if a pending event is processed;
* Returns Zero to indicate there is no pending event.
* Returns Negative Value if an error occurs.
*/
int pd_policy_engine_run(struct tcpc_device *tcpc);
/* ---- Policy Engine (General) ---- */
void pe_power_ready_entry(struct pd_port *pd_port);
static inline void pe_send_swap_request_entry(
struct pd_port *pd_port, uint8_t msg)
{
PE_STATE_WAIT_ANSWER_MSG(pd_port);
pd_send_sop_ctrl_msg(pd_port, msg);
}
/******************* Source *******************/
#ifdef CONFIG_USB_PD_PE_SOURCE
void pe_src_startup_entry(
struct pd_port *pd_port);
void pe_src_discovery_entry(
struct pd_port *pd_port);
void pe_src_send_capabilities_entry(
struct pd_port *pd_port);
void pe_src_negotiate_capabilities_entry(
struct pd_port *pd_port);
void pe_src_transition_supply_entry(
struct pd_port *pd_port);
void pe_src_transition_supply_exit(
struct pd_port *pd_port);
void pe_src_transition_supply2_entry(
struct pd_port *pd_port);
void pe_src_ready_entry(
struct pd_port *pd_port);
void pe_src_disabled_entry(
struct pd_port *pd_port);
void pe_src_capability_response_entry(
struct pd_port *pd_port);
void pe_src_hard_reset_entry(
struct pd_port *pd_port);
void pe_src_hard_reset_received_entry(
struct pd_port *pd_port);
void pe_src_transition_to_default_entry(
struct pd_port *pd_port);
void pe_src_transition_to_default_exit(
struct pd_port *pd_port);
void pe_src_get_sink_cap_entry(
struct pd_port *pd_port);
void pe_src_get_sink_cap_exit(
struct pd_port *pd_port);
void pe_src_wait_new_capabilities_entry(
struct pd_port *pd_port);
void pe_src_send_soft_reset_entry(
struct pd_port *pd_port);
void pe_src_soft_reset_entry(
struct pd_port *pd_port);
/* Source Startup Discover Cable */
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
#ifdef CONFIG_PD_SRC_RESET_CABLE
void pe_src_cbl_send_soft_reset_entry(
struct pd_port *pd_port);
#endif /* CONFIG_PD_SRC_RESET_CABLE */
void pe_src_vdm_identity_request_entry(
struct pd_port *pd_port);
void pe_src_vdm_identity_acked_entry(
struct pd_port *pd_port);
void pe_src_vdm_identity_naked_entry(
struct pd_port *pd_port);
#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */
/* Source for PD30 */
#ifdef CONFIG_USB_PD_REV30
void pe_src_send_not_supported_entry(
struct pd_port *pd_port);
void pe_src_not_supported_received_entry(
struct pd_port *pd_port);
void pe_src_chunk_received_entry(
struct pd_port *pd_port);
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
void pe_src_send_source_alert_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
void pe_src_sink_alert_received_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
void pe_src_give_source_cap_ext_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
void pe_src_give_source_status_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
void pe_src_get_sink_status_entry(
struct pd_port *pd_port);
void pe_src_get_sink_status_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE
void pe_src_give_pps_status_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */
#endif /* CONFIG_USB_PD_REV30 */
#endif /* CONFIG_USB_PD_PE_SOURCE */
/******************* Sink *******************/
#ifdef CONFIG_USB_PD_PE_SINK
/* Sink Init */
void pe_snk_startup_entry(
struct pd_port *pd_port);
void pe_snk_discovery_entry(
struct pd_port *pd_port);
void pe_snk_wait_for_capabilities_entry(
struct pd_port *pd_port);
void pe_snk_evaluate_capability_entry(
struct pd_port *pd_port);
void pe_snk_select_capability_entry(
struct pd_port *pd_port);
void pe_snk_select_capability_exit(
struct pd_port *pd_port);
void pe_snk_transition_sink_entry(
struct pd_port *pd_port);
void pe_snk_transition_sink_exit(
struct pd_port *pd_port);
void pe_snk_ready_entry(
struct pd_port *pd_port);
void pe_snk_hard_reset_entry(
struct pd_port *pd_port);
void pe_snk_transition_to_default_entry(
struct pd_port *pd_port);
void pe_snk_give_sink_cap_entry(
struct pd_port *pd_port);
void pe_snk_get_source_cap_entry(
struct pd_port *pd_port);
void pe_snk_send_soft_reset_entry(
struct pd_port *pd_port);
void pe_snk_soft_reset_entry(
struct pd_port *pd_port);
/* Sink for PD30 */
#ifdef CONFIG_USB_PD_REV30
void pe_snk_send_not_supported_entry(
struct pd_port *pd_port);
void pe_snk_not_supported_received_entry(
struct pd_port *pd_port);
void pe_snk_chunk_received_entry(
struct pd_port *pd_port);
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
void pe_snk_source_alert_received_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
void pe_snk_send_sink_alert_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
void pe_snk_get_source_cap_ext_entry(
struct pd_port *pd_port);
void pe_snk_get_source_cap_ext_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
void pe_snk_get_source_status_entry(
struct pd_port *pd_port);
void pe_snk_get_source_status_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
void pe_snk_give_sink_status_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
void pe_snk_get_pps_status_entry(
struct pd_port *pd_port);
void pe_snk_get_pps_status_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
#endif /* CONFIG_USB_PD_REV30 */
#endif /* CONFIG_USB_PD_PE_SINK */
/******************* DR_SWAP *******************/
#ifdef CONFIG_USB_PD_DR_SWAP
/* DR_SWAP_DFP */
void pe_drs_dfp_ufp_evaluate_dr_swap_entry(
struct pd_port *pd_port);
void pe_drs_dfp_ufp_accept_dr_swap_entry(
struct pd_port *pd_port);
void pe_drs_dfp_ufp_change_to_ufp_entry(
struct pd_port *pd_port);
void pe_drs_dfp_ufp_send_dr_swap_entry(
struct pd_port *pd_port);
void pe_drs_dfp_ufp_reject_dr_swap_entry(
struct pd_port *pd_port);
/* DR_SWAP_UFP */
void pe_drs_ufp_dfp_evaluate_dr_swap_entry(
struct pd_port *pd_port);
void pe_drs_ufp_dfp_accept_dr_swap_entry(
struct pd_port *pd_port);
void pe_drs_ufp_dfp_change_to_dfp_entry(
struct pd_port *pd_port);
void pe_drs_ufp_dfp_send_dr_swap_entry(
struct pd_port *pd_port);
void pe_drs_ufp_dfp_reject_dr_swap_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_DR_SWAP */
/******************* PR_SWAP *******************/
#ifdef CONFIG_USB_PD_PR_SWAP
/* PR_SWAP_SRC */
void pe_prs_src_snk_evaluate_pr_swap_entry(
struct pd_port *pd_port);
void pe_prs_src_snk_accept_pr_swap_entry(
struct pd_port *pd_port);
void pe_prs_src_snk_transition_to_off_entry(
struct pd_port *pd_port);
void pe_prs_src_snk_assert_rd_entry(
struct pd_port *pd_port);
void pe_prs_src_snk_wait_source_on_entry(
struct pd_port *pd_port);
void pe_prs_src_snk_wait_source_on_exit(
struct pd_port *pd_port);
void pe_prs_src_snk_send_swap_entry(
struct pd_port *pd_port);
void pe_prs_src_snk_reject_pr_swap_entry(
struct pd_port *pd_port);
/* PR_SWAP_SNK */
void pe_prs_snk_src_evaluate_pr_swap_entry(
struct pd_port *pd_port);
void pe_prs_snk_src_accept_pr_swap_entry(
struct pd_port *pd_port);
void pe_prs_snk_src_transition_to_off_entry(
struct pd_port *pd_port);
void pe_prs_snk_src_transition_to_off_exit(
struct pd_port *pd_port);
void pe_prs_snk_src_assert_rp_entry(
struct pd_port *pd_port);
void pe_prs_snk_src_source_on_entry(
struct pd_port *pd_port);
void pe_prs_snk_src_source_on_exit(
struct pd_port *pd_port);
void pe_prs_snk_src_send_swap_entry(
struct pd_port *pd_port);
void pe_prs_snk_src_reject_swap_entry(
struct pd_port *pd_port);
/* get same role cap */
void pe_dr_src_get_source_cap_entry(
struct pd_port *pd_port);
void pe_dr_src_get_source_cap_exit(
struct pd_port *pd_port);
void pe_dr_src_give_sink_cap_entry(
struct pd_port *pd_port);
void pe_dr_snk_get_sink_cap_entry(
struct pd_port *pd_port);
void pe_dr_snk_get_sink_cap_exit(
struct pd_port *pd_port);
void pe_dr_snk_give_source_cap_entry(
struct pd_port *pd_port);
/* get same role cap for PD30 */
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
void pe_dr_snk_give_source_cap_ext_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
void pe_dr_src_get_source_cap_ext_entry(
struct pd_port *pd_port);
void pe_dr_src_get_source_cap_ext_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
#endif /* CONFIG_USB_PD_REV30 */
#endif /* CONFIG_USB_PD_PR_SWAP */
/******************* VCONN_SWAP *******************/
#ifdef CONFIG_USB_PD_VCONN_SWAP
void pe_vcs_send_swap_entry(
struct pd_port *pd_port);
void pe_vcs_evaluate_swap_entry(
struct pd_port *pd_port);
void pe_vcs_accept_swap_entry(
struct pd_port *pd_port);
void pe_vcs_reject_vconn_swap_entry(
struct pd_port *pd_port);
void pe_vcs_wait_for_vconn_entry(
struct pd_port *pd_port);
void pe_vcs_wait_for_vconn_exit(
struct pd_port *pd_port);
void pe_vcs_turn_off_vconn_entry(
struct pd_port *pd_port);
void pe_vcs_turn_on_vconn_entry(
struct pd_port *pd_port);
void pe_vcs_send_ps_rdy_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_VCONN_SWAP */
/******************* UFP_VDM *******************/
void pe_ufp_vdm_get_identity_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_send_identity_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_get_identity_nak_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_get_svids_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_send_svids_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_get_svids_nak_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_get_modes_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_send_modes_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_get_modes_nak_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_evaluate_mode_entry_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_mode_entry_ack_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_mode_entry_nak_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_mode_exit_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_mode_exit_ack_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_mode_exit_nak_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_attention_request_entry(
struct pd_port *pd_port);
#ifdef CONFIG_USB_PD_ALT_MODE
void pe_ufp_vdm_dp_status_update_entry(
struct pd_port *pd_port);
void pe_ufp_vdm_dp_configure_entry(
struct pd_port *pd_port);
#endif/* CONFIG_USB_PD_ALT_MODE */
/******************* DFP_VDM *******************/
void pe_dfp_ufp_vdm_identity_request_entry(
struct pd_port *pd_port);
void pe_dfp_ufp_vdm_identity_acked_entry(
struct pd_port *pd_port);
void pe_dfp_ufp_vdm_identity_naked_entry(
struct pd_port *pd_port);
void pe_dfp_cbl_vdm_identity_request_entry(
struct pd_port *pd_port);
void pe_dfp_cbl_vdm_identity_acked_entry(
struct pd_port *pd_port);
void pe_dfp_cbl_vdm_identity_naked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_svids_request_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_svids_acked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_svids_naked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_modes_request_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_modes_acked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_modes_naked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_mode_entry_request_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_mode_entry_acked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_mode_entry_naked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_mode_exit_request_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_mode_exit_acked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_attention_request_entry(
struct pd_port *pd_port);
#ifdef CONFIG_PD_DFP_RESET_CABLE
void pe_dfp_cbl_send_soft_reset_entry(
struct pd_port *pd_port);
void pe_dfp_cbl_send_cable_reset_entry(
struct pd_port *pd_port);
#endif /* CONFIG_PD_DFP_RESET_CABLE */
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
void pe_dfp_vdm_dp_status_update_request_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_dp_status_update_acked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_dp_status_update_naked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_dp_configuration_request_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_dp_configuration_acked_entry(
struct pd_port *pd_port);
void pe_dfp_vdm_dp_configuration_naked_entry(
struct pd_port *pd_port);
#endif/* CONFIG_USB_PD_ALT_MODE_DFP */
/******************* UVDM & SVDM *******************/
#ifdef CONFIG_USB_PD_CUSTOM_VDM
void pe_ufp_uvdm_recv_entry(
struct pd_port *pd_port);
void pe_dfp_uvdm_send_entry(
struct pd_port *pd_port);
void pe_dfp_uvdm_acked_entry(
struct pd_port *pd_port);
void pe_dfp_uvdm_naked_entry(
struct pd_port *pd_port);
#endif/* CONFIG_USB_PD_CUSTOM_VDM */
/******************* PD30 Common *******************/
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
void pe_get_battery_cap_entry(
struct pd_port *pd_port);
void pe_get_battery_cap_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
void pe_give_battery_cap_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
void pe_get_battery_status_entry(
struct pd_port *pd_port);
void pe_get_battery_status_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
void pe_give_battery_status_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
void pe_get_manufacturer_info_entry(
struct pd_port *pd_port);
void pe_get_manufacturer_info_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
void pe_give_manufacturer_info_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
void pe_get_country_codes_entry(
struct pd_port *pd_port);
void pe_get_country_codes_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
void pe_give_country_codes_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
void pe_get_country_info_entry(
struct pd_port *pd_port);
void pe_get_country_info_exit(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL
void pe_give_country_info_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */
void pe_vdm_not_supported_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_REV30 */
/******************* Others *******************/
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
void pe_dbg_ready_entry(
struct pd_port *pd_port);
#endif/* CONFIG_USB_PD_CUSTOM_DBGACC */
#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER
void pe_over_recv_hreset_limit_entry(
struct pd_port *pd_port);
#endif/* CONFIG_USB_PD_RECV_HRESET_COUNTER */
void pe_reject_entry(
struct pd_port *pd_port);
void pe_error_recovery_entry(
struct pd_port *pd_port);
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
void pe_error_recovery_once_entry(
struct pd_port *pd_port);
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
void pe_bist_test_data_entry(
struct pd_port *pd_port);
void pe_bist_test_data_exit(
struct pd_port *pd_port);
void pe_bist_carrier_mode_2_entry(
struct pd_port *pd_port);
void pe_bist_carrier_mode_2_exit(
struct pd_port *pd_port);
/* Wait tx finished */
void pe_idle1_entry(
struct pd_port *pd_port);
void pe_idle2_entry(
struct pd_port *pd_port);
#endif /* PD_POLICY_ENGINE_H_ */

View file

@ -0,0 +1,205 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef PD_PROCESS_EVT_H_
#define PD_PROCESS_EVT_H_
#include "tcpci.h"
#include "pd_policy_engine.h"
/*---------------------------------------------------------------------------*/
struct pe_state_transition {
uint8_t curr_state; /*state, msg, or cmd */
uint8_t next_state;
};
struct pe_state_reaction {
uint16_t nr_transition;
const struct pe_state_transition *state_transition;
};
#define DECL_PE_STATE_TRANSITION(state) \
static const struct pe_state_transition state##_state_transition[]
#define DECL_PE_STATE_REACTION(state) \
static const struct pe_state_reaction state##_reactions = {\
.nr_transition = ARRAY_SIZE(state##_state_transition),\
.state_transition = state##_state_transition,\
}
/*-----------------------------------------------------------------------------
* Sink & Source Common Event
*---------------------------------------------------------------------------
*/
bool pd_process_protocol_error(
struct pd_port *pd_port, struct pd_event *pd_event);
bool pd_process_tx_failed(struct pd_port *pd_port);
/*---------------------------------------------------------------------------*/
#define PE_TRANSIT_STATE(pd_port, state) \
(pd_port->pe_state_next = state)
#define PE_TRANSIT_DATA_STATE(pd_port, ufp, dfp) \
(pd_port->pe_state_next =\
((pd_port->data_role == PD_ROLE_UFP) ? ufp : dfp))
static inline uint8_t pe_get_curr_ready_state(struct pd_port *pd_port)
{
return pd_port->curr_ready_state;
}
static inline uint8_t pe_get_curr_hard_reset_state(struct pd_port *pd_port)
{
return pd_port->curr_hreset_state;
}
static inline uint8_t pe_get_curr_soft_reset_state(struct pd_port *pd_port)
{
return pd_port->curr_sreset_state;
}
static inline uint8_t pe_get_curr_evaluate_pr_swap_state(
struct pd_port *pd_port)
{
if (pd_port->power_role == PD_ROLE_SINK)
return PE_PRS_SNK_SRC_EVALUATE_PR_SWAP;
return PE_PRS_SRC_SNK_EVALUATE_PR_SWAP;
}
static inline uint8_t pe_get_curr_send_pr_swap_state(
struct pd_port *pd_port)
{
if (pd_port->power_role == PD_ROLE_SINK)
return PE_PRS_SNK_SRC_SEND_SWAP;
return PE_PRS_SRC_SNK_SEND_SWAP;
}
static inline uint8_t pd_get_curr_hard_reset_recv_state(
struct pd_port *pd_port)
{
if (pd_port->power_role == PD_ROLE_SINK)
return PE_SNK_TRANSITION_TO_DEFAULT;
return PE_SRC_HARD_RESET_RECEIVED;
}
static inline uint8_t pd_get_curr_soft_reset_recv_state(
struct pd_port *pd_port)
{
if (pd_port->power_role == PD_ROLE_SINK)
return PE_SNK_SOFT_RESET;
return PE_SRC_SOFT_RESET;
}
static inline void pe_transit_ready_state(struct pd_port *pd_port)
{
PE_TRANSIT_STATE(pd_port, pe_get_curr_ready_state(pd_port));
}
static inline void pe_transit_hard_reset_state(struct pd_port *pd_port)
{
PE_TRANSIT_STATE(pd_port, pe_get_curr_hard_reset_state(pd_port));
}
static inline void pe_transit_soft_reset_state(struct pd_port *pd_port)
{
PE_TRANSIT_STATE(pd_port, pe_get_curr_soft_reset_state(pd_port));
}
static inline void pe_transit_soft_reset_recv_state(struct pd_port *pd_port)
{
PE_TRANSIT_STATE(pd_port, pd_get_curr_soft_reset_recv_state(pd_port));
}
static inline void pe_transit_evaluate_pr_swap_state(struct pd_port *pd_port)
{
PE_TRANSIT_STATE(pd_port,
pe_get_curr_evaluate_pr_swap_state(pd_port));
}
static inline void pe_transit_send_pr_swap_state(struct pd_port *pd_port)
{
PE_TRANSIT_STATE(pd_port,
pe_get_curr_send_pr_swap_state(pd_port));
}
static inline void pe_transit_hard_reset_recv_state(struct pd_port *pd_port)
{
PE_TRANSIT_STATE(pd_port,
pd_get_curr_hard_reset_recv_state(pd_port));
}
/*---------------------------------------------------------------------------*/
static inline bool pd_check_pe_state_ready(struct pd_port *pd_port)
{
uint8_t ready_state = pe_get_curr_ready_state(pd_port);
return pd_port->pe_state_curr == ready_state;
}
/*---------------------------------------------------------------------------*/
#define PE_MAKE_STATE_TRANSIT_SINGLE(reaction, next) \
pd_make_pe_state_transit_single(\
pd_port, pd_port->pe_state_curr, reaction, next)
/* PE_MAKE_STATE_TRANSIT_SINGLE */
#define PE_MAKE_STATE_TRANSIT_TO_HRESET(reaction) \
PE_MAKE_STATE_TRANSIT_SINGLE(reaction, \
pe_get_curr_hard_reset_state(pd_port))
/* PE_MAKE_STATE_TRANSIT_TO_HRESET */
#define PE_MAKE_STATE_TRANSIT(state) \
pd_make_pe_state_transit(\
pd_port, pd_port->pe_state_curr, &state##_reactions)
/* PE_MAKE_STATE_TRANSIT */
static inline bool pd_make_pe_state_transit_single(struct pd_port *pd_port,
uint8_t curr_state, uint8_t reaction_state, uint8_t next_state)
{
if (curr_state == reaction_state) {
PE_TRANSIT_STATE(pd_port, next_state);
return true;
}
return false;
}
bool pd_make_pe_state_transit(struct pd_port *pd_port, uint8_t curr_state,
const struct pe_state_reaction *state_reaction);
bool pd_process_event(struct pd_port *pd_port, struct pd_event *pd_event);
extern bool pd_process_event_snk(struct pd_port *pd_port, struct pd_event *evt);
extern bool pd_process_event_src(struct pd_port *pd_port, struct pd_event *evt);
extern bool pd_process_event_drs(struct pd_port *pd_port, struct pd_event *evt);
extern bool pd_process_event_prs(struct pd_port *pd_port, struct pd_event *evt);
extern bool pd_process_event_vdm(struct pd_port *pd_port, struct pd_event *evt);
extern bool pd_process_event_vcs(struct pd_port *pd_port, struct pd_event *evt);
extern bool pd_process_event_com(struct pd_port *pd_port, struct pd_event *evt);
extern bool pd_process_event_tcp(struct pd_port *pd_port, struct pd_event *evt);
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
extern bool pd_process_event_dbg(struct pd_port *pd_port, struct pd_event *evt);
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
#endif /* PD_PROCESS_EVT_H_ */

View file

@ -0,0 +1,218 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020 MediaTek Inc.
* Author: cy_huang <cy_huang@richtek.com>
*/
#ifndef LINUX_MISC_RT_REGMAP_H
#define LINUX_MISC_RT_REGMAP_H
#include <linux/i2c.h>
enum rt_access_mode {
RT_1BYTE_MODE = 1,
RT_2BYTE_MODE = 2,
RT_4BYTE_MODE = 4,
RT_DUMMY_MODE = -1,
};
/* start: the start address of group
* end: the end address of group
* mode: access mode (1,2,4 bytes)
*/
struct rt_access_group {
u32 start;
u32 end;
enum rt_access_mode mode;
};
/* rt_reg_type
* RT_NORMAL : Write without mask, Read from cache
* RT_WBITS : Write with mask, Read from cache
* RT_VOLATILE : Write to chip directly, Read from chip
* RT_RESERVE : Reserve registers (Write/Read as RT_NORMAL)
*/
#define RT_REG_TYPE_MASK (0x03)
#define RT_NORMAL (0x00)
#define RT_WBITS (0x01)
#define RT_VOLATILE (0x02)
#define RT_RESERVE (0x03)
/* RT_WR_ONCE: if write data = cache data, data will not be written.
*/
#define RT_WR_ONCE (0x08)
#define RT_NORMAL_WR_ONCE (RT_NORMAL | RT_WR_ONCE)
#define RT_WBITS_WR_ONCE (RT_WBITS | RT_WR_ONCE)
enum rt_data_format {
RT_LITTLE_ENDIAN,
RT_BIG_ENDIAN,
};
/* rt_regmap_mode
* 0 0 0 0 0 0 0 0
* | | | | | |
* | | | |__| byte_mode
* | |__| ||
* | || Cache_mode
* | Block_mode
* Debug_mode
*/
#define RT_BYTE_MODE_MASK (0x01)
/* 1 byte for each register */
#define RT_SINGLE_BYTE (0 << 0)
/* multi bytes for each regiseter */
#define RT_MULTI_BYTE (1 << 0)
#define RT_CACHE_MODE_MASK (0x06)
/* write to cache and chip synchronously */
#define RT_CACHE_WR_THROUGH (0 << 1)
/* write to cache and chip asynchronously */
#define RT_CACHE_WR_BACK (1 << 1)
/* disable cache */
#define RT_CACHE_DISABLE (2 << 1)
#define RT_IO_BLK_MODE_MASK (0x18)
/* pass through all write function */
#define RT_IO_PASS_THROUGH (0 << 3)
/* block all write function */
#define RT_IO_BLK_ALL (1 << 3)
/* block cache write function */
#define RT_IO_BLK_CACHE (2 << 3)
/* block chip write function */
#define RT_IO_BLK_CHIP (3 << 3)
#define RT_DBG_MODE_MASK (0x20)
/* create general debugfs for register map */
#define RT_DBG_GENERAL (0 << 5)
/* create node for each regisetr map by register address */
#define RT_DBG_SPECIAL (1 << 5)
/* struct rt_register
*
* Ricktek register map structure for storing mapping data
* @addr: register address.
* @size: register size in byte.
* @reg_type: register R/W type (RT_NORMAL, RT_WBITS, RT_VOLATILE, RT_RESERVE).
* @wbit_mask: register writeable bits mask.
*/
struct rt_register {
u32 addr;
unsigned int size;
unsigned char reg_type;
unsigned char *wbit_mask;
};
/* Declare a rt_register by RT_REG_DECL
*/
#define RT_REG_DECL(_addr, _reg_length, _reg_type, _mask_...) \
static unsigned char rt_writable_mask_##_addr[_reg_length] = _mask_; \
static struct rt_register rt_register_##_addr = { \
.addr = _addr, \
.size = _reg_length, \
.reg_type = _reg_type, \
.wbit_mask = rt_writable_mask_##_addr, \
}
#define rt_register_map_t struct rt_register *
#define RT_REG(_addr) (&rt_register_##_addr)
/* rt_regmap_properties
* @name: the name of debug node.
* @aliases: alisis name of rt_regmap_device.
* @rm: rt_regiseter_map pointer array.
* @register_num: the number of rt_register_map registers.
* @group: register map access group.
* @rt_format: default is little endian.
* @rt_regmap_mode: rt_regmap_device mode.
* @io_log_en: enable/disable io log
*/
struct rt_regmap_properties {
const char *name;
const char *aliases;
const rt_register_map_t *rm;
int register_num;
struct rt_access_group *group;
const enum rt_data_format rt_format;
unsigned char rt_regmap_mode;
unsigned char io_log_en:1;
};
/* A passing struct for rt_regmap_reg_read and rt_regmap_reg_write function
* reg: regmap addr.
* mask: mask for update bits.
* rt_data: register value.
*/
struct rt_reg_data {
u32 reg;
u32 mask;
union {
u32 data_u32;
u16 data_u16;
u8 data_u8;
u8 data[4];
} rt_data;
};
struct rt_regmap_device;
/* basic chip read/write function */
struct rt_regmap_fops {
int (*read_device)(void *client, u32 addr, int len, void *dst);
int (*write_device)(void *client, u32 addr, int len, const void *src);
};
/* with device address */
extern struct rt_regmap_device*
rt_regmap_device_register_ex(struct rt_regmap_properties *props,
struct rt_regmap_fops *rops,
struct device *parent,
void *client, int dev_addr, void *drvdata);
static inline struct rt_regmap_device*
rt_regmap_device_register(struct rt_regmap_properties *props,
struct rt_regmap_fops *rops,
struct device *parent,
struct i2c_client *client, void *drvdata)
{
return rt_regmap_device_register_ex(props, rops, parent,
client, client->addr, drvdata);
}
extern void rt_regmap_device_unregister(struct rt_regmap_device *rd);
extern int rt_regmap_cache_reload(struct rt_regmap_device *rd);
extern int rt_regmap_block_write(struct rt_regmap_device *rd, u32 reg,
int bytes, const void *src);
extern int rt_asyn_regmap_block_write(struct rt_regmap_device *rd, u32 reg,
int bytes, const void *src);
extern int rt_regmap_block_read(struct rt_regmap_device *rd, u32 reg,
int bytes, void *dst);
extern int rt_regmap_reg_read(struct rt_regmap_device *rd,
struct rt_reg_data *rrd, u32 reg);
extern int rt_regmap_reg_write(struct rt_regmap_device *rd,
struct rt_reg_data *rrd, u32 reg,
const u32 data);
extern int rt_asyn_regmap_reg_write(struct rt_regmap_device *rd,
struct rt_reg_data *rrd, u32 reg,
const u32 data);
extern int rt_regmap_update_bits(struct rt_regmap_device *rd,
struct rt_reg_data *rrd, u32 reg,
u32 mask, u32 data);
extern void rt_regmap_cache_sync(struct rt_regmap_device *rd);
extern void rt_regmap_cache_write_back(struct rt_regmap_device *rd, u32 reg);
extern int rt_is_reg_volatile(struct rt_regmap_device *rd, u32 reg);
extern int rt_get_regsize(struct rt_regmap_device *rd, u32 reg);
extern void rt_cache_getlasterror(struct rt_regmap_device *rd, char *buf,
size_t size);
extern void rt_cache_clrlasterror(struct rt_regmap_device *rd);
extern int rt_regmap_add_debugfs(struct rt_regmap_device *rd, const char *name,
umode_t mode, void *data,
const struct file_operations *fops);
extern int regmap_plat_init(void);
extern void regmap_plat_exit(void);
#endif /* LINUX_MISC_RT_REGMAP_H */

View file

@ -0,0 +1,204 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_RT1711H_H
#define __LINUX_RT1711H_H
#include "std_tcpci_v10.h"
#include "pd_dbg_info.h"
/*show debug message or not */
#define ENABLE_RT1711_DBG 0
/* RT1711H Private RegMap */
#define RT1711H_REG_CONFIG_GPIO0 (0x71)
#define RT1711H_REG_PHY_CTRL1 (0x80)
#define RT1711H_REG_CLK_CTRL2 (0x87)
#define RT1711H_REG_CLK_CTRL3 (0x88)
#define RT1711H_REG_PRL_FSM_RESET (0x8D)
#define RT1711H_REG_BMC_CTRL (0x90)
#define RT1711H_REG_BMCIO_RXDZSEL (0x93)
#define RT1711H_REG_VCONN_CLIMITEN (0x95)
#define RT1711H_REG_RT_STATUS (0x97)
#define RT1711H_REG_RT_INT (0x98)
#define RT1711H_REG_RT_MASK (0x99)
#define RT1711H_REG_IDLE_CTRL (0x9B)
#define RT1711H_REG_INTRST_CTRL (0x9C)
#define RT1711H_REG_WATCHDOG_CTRL (0x9D)
#define RT1711H_REG_I2CRST_CTRL (0X9E)
#define RT1711H_REG_SWRESET (0xA0)
#define RT1711H_REG_TTCPC_FILTER (0xA1)
#define RT1711H_REG_DRP_TOGGLE_CYCLE (0xA2)
#define RT1711H_REG_DRP_DUTY_CTRL (0xA3)
#define RT1711H_REG_BMCIO_RXDZEN (0xAF)
#define RT1711H_REG_UNLOCK_PW_2 (0xF0)
#define RT1711H_REG_UNLOCK_PW_1 (0xF1)
#define RT1711H_REG_EFUSE5 (0xF6)
/*
* Device ID
*/
#define RT1711H_DID_A 0x2170
#define RT1711H_DID_B 0x2171
#define RT1711H_DID_C 0x2172
#define RT1715_DID_D 0x2173
/*
* RT1711H_REG_PHY_CTRL1 (0x80)
*/
#define RT1711H_REG_PHY_CTRL1_SET( \
retry_discard, toggle_cnt, bus_idle_cnt, rx_filter) \
((retry_discard << 7) | (toggle_cnt << 4) | \
(bus_idle_cnt << 2) | (rx_filter & 0x03))
/*
* RT1711H_REG_CLK_CTRL2 (0x87)
*/
#define RT1711H_REG_CLK_DIV_600K_EN (1<<7)
#define RT1711H_REG_CLK_BCLK2_EN (1<<6)
#define RT1711H_REG_CLK_BCLK2_TG_EN (1<<5)
#define RT1711H_REG_CLK_DIV_300K_EN (1<<3)
#define RT1711H_REG_CLK_CK_300K_EN (1<<2)
#define RT1711H_REG_CLK_BCLK_EN (1<<1)
#define RT1711H_REG_CLK_BCLK_TH_EN (1<<0)
/*
* RT1711H_REG_CLK_CTRL3 (0x88)
*/
#define RT1711H_REG_CLK_OSCMUX_RG_EN (1<<7)
#define RT1711H_REG_CLK_CK_24M_EN (1<<6)
#define RT1711H_REG_CLK_OSC_RG_EN (1<<5)
#define RT1711H_REG_CLK_DIV_2P4M_EN (1<<4)
#define RT1711H_REG_CLK_CK_2P4M_EN (1<<3)
#define RT1711H_REG_CLK_PCLK_EN (1<<2)
#define RT1711H_REG_CLK_PCLK_RG_EN (1<<1)
#define RT1711H_REG_CLK_PCLK_TG_EN (1<<0)
/*
* RT1711H_REG_BMC_CTRL (0x90)
*/
#define RT1711H_REG_IDLE_EN (1<<6)
#define RT1711H_REG_DISCHARGE_EN (1<<5)
#define RT1711H_REG_BMCIO_LPRPRD (1<<4)
#define RT1711H_REG_BMCIO_LPEN (1<<3)
#define RT1711H_REG_BMCIO_BG_EN (1<<2)
#define RT1711H_REG_VBUS_DET_EN (1<<1)
#define RT1711H_REG_BMCIO_OSC_EN (1<<0)
/*
* RT1711H_REG_RT_STATUS (0x97)
*/
#define RT1711H_REG_RA_DETACH (1<<5)
#define RT1711H_REG_VBUS_80 (1<<1)
/*
* RT1711H_REG_RT_INT (0x98)
*/
#define RT1711H_REG_INT_RA_DETACH (1<<5)
#define RT1711H_REG_INT_WATCHDOG (1<<2)
#define RT1711H_REG_INT_VBUS_80 (1<<1)
#define RT1711H_REG_INT_WAKEUP (1<<0)
/*
* RT1711H_REG_RT_MASK (0x99)
*/
#define RT1711H_REG_M_RA_DETACH (1<<5)
#define RT1711H_REG_M_WATCHDOG (1<<2)
#define RT1711H_REG_M_VBUS_80 (1<<1)
#define RT1711H_REG_M_WAKEUP (1<<0)
/*
* RT1711H_REG_IDLE_CTRL (0x9B)
*/
#define RT1711H_REG_CK_300K_SEL (1<<7)
#define RT1711H_REG_SHIPPING_OFF (1<<5)
#define RT1711H_REG_ENEXTMSG (1<<4)
#define RT1711H_REG_AUTOIDLE_EN (1<<3)
/*
* RT1711H_REG_EFUSE5 (0xF6)
*/
#define RT1711H_REG_M_VBUS_CAL GENMASK(7, 5)
#define RT1711H_REG_S_VBUS_CAL 5
#define RT1711H_REG_MIN_VBUS_CAL -4
/* timeout = (tout*2+1) * 6.4ms */
#ifdef CONFIG_USB_PD_REV30
#define RT1711H_REG_IDLE_SET(ck300, ship_dis, auto_idle, tout) \
((ck300 << 7) | (ship_dis << 5) | (auto_idle << 3) \
| (tout & 0x07) | RT1711H_REG_ENEXTMSG)
#else
#define RT1711H_REG_IDLE_SET(ck300, ship_dis, auto_idle, tout) \
((ck300 << 7) | (ship_dis << 5) | (auto_idle << 3) | (tout & 0x07))
#endif
/*
* RT1711H_REG_INTRST_CTRL (0x9C)
*/
#define RT1711H_REG_INTRST_EN (1<<7)
/* timeout = (tout+1) * 0.2sec */
#define RT1711H_REG_INTRST_SET(en, tout) \
((en << 7) | (tout & 0x03))
/*
* RT1711H_REG_WATCHDOG_CTRL (0x9D)
*/
#define RT1711H_REG_WATCHDOG_EN (1<<7)
/* timeout = (tout+1) * 0.4sec */
#define RT1711H_REG_WATCHDOG_CTRL_SET(en, tout) \
((en << 7) | (tout & 0x07))
/*
* RT1711H_REG_I2CRST_CTRL (0x9E)
*/
#define RT1711H_REG_I2CRST_EN (1<<7)
/* timeout = (tout+1) * 12.5ms */
#define RT1711H_REG_I2CRST_SET(en, tout) \
((en << 7) | (tout & 0x0f))
#if ENABLE_RT1711_DBG
#define RT1711_INFO(format, args...) \
pd_dbg_info("%s() line-%d: " format,\
__func__, __LINE__, ##args)
#else
#define RT1711_INFO(foramt, args...)
#endif
#endif /* #ifndef __LINUX_RT1711H_H */

View file

@ -0,0 +1,201 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef STD_TCPCI_V10_H_
#define STD_TCPCI_V10_H_
/* Standard TCPC V10 RegMap */
#define TCPC_V10_REG_VID (0x00)
#define TCPC_V10_REG_PID (0x02)
#define TCPC_V10_REG_DID (0x04)
#define TCPC_V10_REG_TYPEC_REV (0x06)
#define TCPC_V10_REG_PD_REV (0x08)
#define TCPC_V10_REG_PDIF_REV (0x0A)
#define TCPC_V10_REG_ALERT (0x10)
#define TCPC_V10_REG_ALERT_MASK (0x12)
#define TCPC_V10_REG_POWER_STATUS_MASK (0x14)
#define TCPC_V10_REG_FAULT_STATUS_MASK (0x15)
#define TCPC_V10_REG_EXT_STATUS_MASK (0x16)
#define TCPC_V10_REG_TCPC_CTRL (0x19)
#define TCPC_V10_REG_ROLE_CTRL (0x1A)
#define TCPC_V10_REG_FAULT_CTRL (0x1B)
#define TCPC_V10_REG_POWER_CTRL (0x1C)
#define TCPC_V10_REG_CC_STATUS (0x1D)
#define TCPC_V10_REG_POWER_STATUS (0x1E)
#define TCPC_V10_REG_FAULT_STATUS (0x1F)
#define TCPC_V10_REG_EXT_STATUS (0x20)
#define TCPC_V10_REG_COMMAND (0x23)
#define TCPC_V10_REG_MSG_HDR_INFO (0x2e)
#define TCPC_V10_REG_RX_DETECT (0x2f)
#define TCPC_V10_REG_RX_BYTE_CNT (0x30)
#define TCPC_V10_REG_RX_BUF_FRAME_TYPE (0x31)
#define TCPC_V10_REG_RX_HDR (0x32)
#define TCPC_V10_REG_RX_DATA (0x34)
#define TCPC_V10_REG_TRANSMIT (0x50)
#define TCPC_V10_REG_TX_BYTE_CNT (0x51)
#define TCPC_V10_REG_TX_HDR (0x52)
#define TCPC_V10_REG_TX_DATA (0x54)/* through 0x6f */
#define TCPC_V10_REG_VBUS_SINK_DISCONNECT_THD (0x72)
#define TCPC_V10_REG_VBUS_STOP_DISCHARGE_THD (0x74)
#define TCPC_V10_REG_VBUS_VOLTAGE_ALARM_HI (0x76)
#define TCPC_V10_REG_VBUS_VOLTAGE_ALARM_LO (0x78)
/*
* TCPC_V10_REG_ALERT (0x10)
* TCPC_V10_REG_ALERT_MASK (0x12)
*/
#define TCPC_V10_REG_ALERT_VENDOR_DEFINED (1<<15)
#define TCPC_V10_REG_VBUS_SINK_DISCONNECT (1<<11)
#define TCPC_V10_REG_RX_OVERFLOW (1<<10)
#define TCPC_V10_REG_ALERT_FAULT (1<<9)
#define TCPC_V10_REG_ALERT_LO_VOLT (1<<8)
#define TCPC_V10_REG_ALERT_HI_VOLT (1<<7)
#define TCPC_V10_REG_ALERT_TX_SUCCESS (1<<6)
#define TCPC_V10_REG_ALERT_TX_DISCARDED (1<<5)
#define TCPC_V10_REG_ALERT_TX_FAILED (1<<4)
#define TCPC_V10_REG_ALERT_RX_HARD_RST (1<<3)
#define TCPC_V10_REG_ALERT_RX_STATUS (1<<2)
#define TCPC_V10_REG_ALERT_POWER_STATUS (1<<1)
#define TCPC_V10_REG_ALERT_CC_STATUS (1<<0)
/*
* TCPC_V10_REG_POWER_STATUS_MASK (0x14)
* TCPC_V10_REG_POWER_STATUS (0x1E)
*/
#define TCPC_V10_REG_POWER_STATUS_TCPC_INITIAL (1<<6)
#define TCPC_V10_REG_POWER_STATUS_SRC_HV (1<<5)
#define TCPC_V10_REG_POWER_STATUS_SRC_VBUS (1<<4)
#define TCPC_V10_REG_POWER_STATUS_VBUS_PRES_DET (1<<3)
#define TCPC_V10_REG_POWER_STATUS_VBUS_PRES (1<<2)
#define TCPC_V10_REG_POWER_STATUS_VCONN_PRES (1<<1)
#define TCPC_V10_REG_POWER_STATUS_SINK_VBUS (1<<0)
/*
* TCPC_V10_REG_FAULT_STATUS_MASK (0x15)
* TCPC_V10_REG_FAULT_STATUS (0x1F)
*/
/* VCONN_OV should not be here */
#define TCPC_V10_REG_FAULT_STATUS_VCONN_OV (1<<7)
#define TCPC_V10_REG_FAULT_STATUS_FORCE_OFF_VBUS (1<<6)
#define TCPC_V10_REG_FAULT_STATUS_AUTO_DISC_FAIL (1<<5)
#define TCPC_V10_REG_FAULT_STATUS_FORCE_DISC_FAIL (1<<4)
#define TCPC_V10_REG_FAULT_STATUS_VBUS_OC (1<<3)
#define TCPC_V10_REG_FAULT_STATUS_VBUS_OV (1<<2)
#define TCPC_V10_REG_FAULT_STATUS_VCONN_OC (1<<1)
#define TCPC_V10_REG_FAULT_STATUS_I2C_ERROR (1<<0)
/*
* TCPC_V10_REG_ROLE_CTRL (0x1A)
*/
#define TCPC_V10_REG_ROLE_CTRL_DRP (1<<6)
#define TCPC_V10_REG_ROLE_CTRL_RES_SET(drp, rp, cc1, cc2) \
((drp) << 6 | (rp) << 4 | (cc2) << 2 | (cc1))
#define CC_RD 0x02
#define CC_RP 0x01
#define CC_OPEN 0x03
#define CC_RA 0x00
/*
* TCPC_V10_REG_TCPC_CTRL (0x19)
*/
#define TCPC_V10_REG_TCPC_CTRL_EN_LOOK4CONNECTION_ALERT (1<<6)
#define TCPC_V10_REG_TCPC_CTRL_EN_WDT (1<<5)
#define TCPC_V10_REG_TCPC_CTRL_BIST_TEST_MODE (1<<1)
#define TCPC_V10_REG_TCPC_CTRL_PLUG_ORIENT (1<<0)
/*
* TCPC_V10_REG_FAULT_CTRL (0x1B)
*/
#define TCPC_V10_REG_FAULT_CTRL_DIS_VCONN_OV (1<<7)
#define TCPC_V10_REG_FAULT_CTRL_DIS_SNK_VBUS_OC (1<<2)
#define TCPC_V10_REG_FAULT_CTRL_DIS_VCONN_OC (1<<0)
/*
* TCPC_V10_REG_POWER_CTRL (0x1C)
*/
#define TCPC_V10_REG_VBUS_MONITOR (1<<6)
#define TCPC_V10_REG_BLEED_DISC_EN (1<<3)
#define TCPC_V10_REG_FORCE_DISC_EN (1<<2)
#define TCPC_V10_REG_POWER_CTRL_VCONN (1<<0)
/*
* TCPC_V10_REG_CC_STATUS (0x1D)
*/
#define TCPC_V10_REG_CC_STATUS_DRP_TOGGLING (1<<5)
#define TCPC_V10_REG_CC_STATUS_DRP_RESULT(reg) (((reg) & 0x10) >> 4)
#define TCPC_V10_REG_CC_STATUS_CC2(reg) (((reg) & 0xc) >> 2)
#define TCPC_V10_REG_CC_STATUS_CC1(reg) ((reg) & 0x3)
/*
* TCPC_V10_REG_COMMAND (0x23)
*/
enum tcpm_v10_command {
TCPM_CMD_WAKE_I2C = 0x11,
TCPM_CMD_DISABLE_VBUS_DETECT = 0x22,
TCPM_CMD_ENABLE_VBUS_DETECT = 0x33,
TCPM_CMD_DISABLE_SINK_VBUS = 0x44,
TCPM_CMD_ENABLE_SINK_VBUS = 0x55,
TCPM_CMD_DISABLE_SOURCE_VBUS = 0x66,
TCPM_CMD_ENABLE_SOURCE_VBUS = 0x77,
TCPM_CMD_SOURCE_VBUS_HV = 0x88,
TCPM_CMD_LOOK_CONNECTION = 0x99,
TCPM_CMD_RX_ONE_MODE = 0xAA,
TCPM_CMD_I2C_IDLE = 0xFF,
};
/*
* TCPC_V10_REG_MSG_HDR_INFO (0x2e)
* According to PD30_Rev11 ECR,
* The sender of a GoodCRC Message should set :
* the Specification Revsiion field to 10b.
*/
#define TCPC_V10_REG_MSG_HDR_INFO_SET(drole, prole) \
((drole) << 3 | ((PD_REV20) << 1) | (prole))
#define TCPC_V10_REG_MSG_HDR_INFO_DROLE(reg) (((reg) & 0x8) >> 3)
#define TCPC_V10_REG_MSG_HDR_INFO_PROLE(reg) ((reg) & 0x1)
/*
* TCPC_V10_REG_TRANSMIT (0x50)
*/
#ifdef CONFIG_USB_PD_REV30
#define TCPC_V10_REG_TRANSMIT_SET(retry, type) \
((retry) << 4 | (type))
#else
#define TCPC_V10_REG_TRANSMIT_SET(retry, type) \
(PD_RETRY_COUNT << 4 | (type))
#endif /* CONFIG_USB_PD_REV30 */
#endif /* STD_TCPCI_V10_H_ */

View file

@ -0,0 +1,201 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_RT_TCPC_H
#define __LINUX_RT_TCPC_H
#include <linux/device.h>
#include <linux/hrtimer.h>
#include <linux/workqueue.h>
#include <linux/pm_wakeup.h>
#include <linux/err.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/sched.h>
#ifdef CONFIG_DUAL_ROLE_USB_INTF
#include <linux/usb/class-dual-role.h>
#endif /* CONFIG_DUAL_ROLE_USB_INTF */
#include "tcpci_core.h"
#ifdef CONFIG_PD_DBG_INFO
#include "pd_dbg_info.h"
#endif /* CONFIG_PD_DBG_INFO */
#ifdef CONFIG_USB_POWER_DELIVERY
#include "pd_core.h"
#endif /* CONFIG_USB_POWER_DELIVERY */
#define PE_STATE_FULL_NAME 0
#define TCPC_LOW_RP_DUTY (100) /* 10 % */
#define TCPC_NORMAL_RP_DUTY (330) /* 33 % */
/* provide to TCPC interface */
extern int tcpci_report_usb_port_changed(struct tcpc_device *tcpc);
extern int tcpci_set_wake_lock(
struct tcpc_device *tcpc, bool pd_lock, bool user_lock);
extern int tcpci_report_power_control(struct tcpc_device *tcpc, bool en);
extern int tcpc_typec_init(struct tcpc_device *tcpc, uint8_t typec_role);
extern void tcpc_typec_deinit(struct tcpc_device *tcpc);
extern int tcpc_dual_role_phy_init(struct tcpc_device *tcpc);
extern struct tcpc_device *tcpc_device_register(
struct device *parent, struct tcpc_desc *tcpc_desc,
struct tcpc_ops *ops, void *drv_data);
extern void tcpc_device_unregister(
struct device *dev, struct tcpc_device *tcpc);
extern int tcpc_schedule_init_work(struct tcpc_device *tcpc);
extern void *tcpc_get_dev_data(struct tcpc_device *tcpc);
extern void tcpci_lock_typec(struct tcpc_device *tcpc);
extern void tcpci_unlock_typec(struct tcpc_device *tcpc);
extern int tcpci_alert(struct tcpc_device *tcpc);
extern void tcpci_vbus_level_init(
struct tcpc_device *tcpc, uint16_t power_status);
static inline int tcpci_check_vbus_valid(struct tcpc_device *tcpc)
{
return tcpc->vbus_level >= TCPC_VBUS_VALID;
}
int tcpci_check_vbus_valid_from_ic(struct tcpc_device *tcpc);
int tcpci_check_vsafe0v(struct tcpc_device *tcpc, bool detect_en);
int tcpci_alert_status_clear(struct tcpc_device *tcpc, uint32_t mask);
int tcpci_fault_status_clear(struct tcpc_device *tcpc, uint8_t status);
int tcpci_set_alert_mask(struct tcpc_device *tcpc, uint32_t mask);
int tcpci_get_alert_mask(struct tcpc_device *tcpc, uint32_t *mask);
int tcpci_get_alert_status(struct tcpc_device *tcpc, uint32_t *alert);
int tcpci_get_fault_status(struct tcpc_device *tcpc, uint8_t *fault);
int tcpci_get_power_status(struct tcpc_device *tcpc, uint16_t *pw_status);
int tcpci_init(struct tcpc_device *tcpc, bool sw_reset);
int tcpci_init_alert_mask(struct tcpc_device *tcpc);
int tcpci_get_cc(struct tcpc_device *tcpc);
int tcpci_set_cc(struct tcpc_device *tcpc, int pull);
int tcpci_set_polarity(struct tcpc_device *tcpc, int polarity);
int tcpci_set_low_rp_duty(struct tcpc_device *tcpc, bool low_rp);
int tcpci_set_vconn(struct tcpc_device *tcpc, int enable);
int tcpci_is_low_power_mode(struct tcpc_device *tcpc);
int tcpci_set_low_power_mode(struct tcpc_device *tcpc, bool en, int pull);
int tcpci_idle_poll_ctrl(struct tcpc_device *tcpc, bool en, bool lock);
int tcpci_set_watchdog(struct tcpc_device *tcpc, bool en);
int tcpci_alert_vendor_defined_handler(struct tcpc_device *tcpc);
#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
int tcpci_is_vsafe0v(struct tcpc_device *tcpc);
#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
#ifdef CONFIG_WATER_DETECTION
int tcpci_is_water_detected(struct tcpc_device *tcpc);
int tcpci_set_water_protection(struct tcpc_device *tcpc, bool en);
int tcpci_set_usbid_polling(struct tcpc_device *tcpc, bool en);
int tcpci_notify_wd_status(struct tcpc_device *tcpc, bool water_detected);
#endif /* CONFIG_WATER_DETECTION */
#ifdef CONFIG_CABLE_TYPE_DETECTION
int tcpci_notify_cable_type(struct tcpc_device *tcpc);
#endif /* CONFIG_CABLE_TYPE_DETECTION */
#ifdef CONFIG_USB_POWER_DELIVERY
int tcpci_set_msg_header(struct tcpc_device *tcpc,
uint8_t power_role, uint8_t data_role);
int tcpci_set_rx_enable(struct tcpc_device *tcpc, uint8_t enable);
int tcpci_protocol_reset(struct tcpc_device *tcpc);
int tcpci_get_message(struct tcpc_device *tcpc,
uint32_t *payload, uint16_t *head, enum tcpm_transmit_type *type);
int tcpci_transmit(struct tcpc_device *tcpc,
enum tcpm_transmit_type type, uint16_t header, const uint32_t *data);
int tcpci_set_bist_test_mode(struct tcpc_device *tcpc, bool en);
int tcpci_set_bist_carrier_mode(struct tcpc_device *tcpc, uint8_t pattern);
#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
int tcpci_retransmit(struct tcpc_device *tcpc);
#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
#endif /* CONFIG_USB_POWER_DELIVERY */
int tcpci_notify_typec_state(struct tcpc_device *tcpc);
int tcpci_notify_role_swap(
struct tcpc_device *tcpc, uint8_t event, uint8_t role);
int tcpci_notify_pd_state(struct tcpc_device *tcpc, uint8_t connect);
int tcpci_set_intrst(struct tcpc_device *tcpc, bool en);
int tcpci_enable_watchdog(struct tcpc_device *tcpc, bool en);
int tcpci_source_vbus(struct tcpc_device *tcpc, uint8_t type, int mv, int ma);
int tcpci_sink_vbus(struct tcpc_device *tcpc, uint8_t type, int mv, int ma);
int tcpci_disable_vbus_control(struct tcpc_device *tcpc);
int tcpci_notify_attachwait_state(struct tcpc_device *tcpc, bool as_sink);
int tcpci_enable_auto_discharge(struct tcpc_device *tcpc, bool en);
int tcpci_enable_force_discharge(struct tcpc_device *tcpc, bool en, int mv);
#ifdef CONFIG_USB_POWER_DELIVERY
int tcpci_notify_hard_reset_state(struct tcpc_device *tcpc, uint8_t state);
int tcpci_enter_mode(struct tcpc_device *tcpc,
uint16_t svid, uint8_t ops, uint32_t mode);
int tcpci_exit_mode(struct tcpc_device *tcpc, uint16_t svid);
#ifdef CONFIG_USB_PD_ALT_MODE
int tcpci_report_hpd_state(struct tcpc_device *tcpc, uint32_t dp_status);
int tcpci_dp_status_update(struct tcpc_device *tcpc, uint32_t dp_status);
int tcpci_dp_configure(struct tcpc_device *tcpc, uint32_t dp_config);
int tcpci_dp_attention(struct tcpc_device *tcpc, uint32_t dp_status);
int tcpci_dp_notify_status_update_done(
struct tcpc_device *tcpc, uint32_t dp_status, bool ack);
int tcpci_dp_notify_config_start(struct tcpc_device *tcpc);
int tcpci_dp_notify_config_done(struct tcpc_device *tcpc,
uint32_t local_cfg, uint32_t remote_cfg, bool ack);
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifdef CONFIG_USB_PD_CUSTOM_VDM
int tcpci_notify_uvdm(struct tcpc_device *tcpc, bool ack);
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
int tcpci_dc_notify_en_unlock(struct tcpc_device *tcpc);
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
int tcpci_notify_alert(struct tcpc_device *tcpc, uint32_t ado);
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
int tcpci_notify_status(struct tcpc_device *tcpc, struct pd_status *sdb);
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_INFO
int tcpci_notify_request_bat_info(
struct tcpc_device *tcpc, enum pd_battery_reference ref);
#endif /* CONFIG_USB_PD_REV30_BAT_INFO */
#endif /* CONFIG_USB_PD_REV30 */
#endif /* CONFIG_USB_POWER_DELIVERY */
#endif /* #ifndef __LINUX_RT_TCPC_H */

View file

@ -0,0 +1,436 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_TCPC_CONFIG_H
#define __LINUX_TCPC_CONFIG_H
#if IS_ENABLED(CONFIG_TCPC_CLASS)
/* default config */
/* #define CONFIG_TYPEC_USE_DIS_VBUS_CTRL */
#define CONFIG_TYPEC_POWER_CTRL_INIT
#define CONFIG_TYPEC_CAP_TRY_SOURCE
#define CONFIG_TYPEC_CAP_TRY_SINK
#define CONFIG_TYPEC_CAP_DBGACC
#define CONFIG_TYPEC_CAP_DBGACC_SNK
#define CONFIG_TYPEC_CAP_CUSTOM_SRC
#define CONFIG_TYPEC_CAP_NORP_SRC
#define CONFIG_COMPATIBLE_APPLE_TA
#if 0 /* CONFIG_MTK_GAUGE_VERSION == 30 */
#define CONFIG_RECV_BAT_ABSENT_NOTIFY
#endif /* CONFIG_MTK_GAUGE_VERSION */
/* #define CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY */
#define CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT
#define CONFIG_TYPEC_CHECK_LEGACY_CABLE
#define CONFIG_TYPEC_CHECK_LEGACY_CABLE2
#define CONFIG_TYPEC_LEGACY2_AUTO_RECYCLE
/* #define CONFIG_TYPEC_CHECK_SRC_UNATTACH_OPEN */
#define CONFIG_TYPEC_CAP_RA_DETACH
#define CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG
#define CONFIG_TYPEC_CAP_POWER_OFF_CHARGE
#define CONFIG_TYPEC_CAP_ROLE_SWAP
#define CONFIG_TYPEC_CAP_ROLE_SWAP_TOUT 1200
#define CONFIG_TYPEC_CAP_DISCHARGE_TOUT 50
#define CONFIG_TYPEC_CAP_AUTO_DISCHARGE
/* #define CONFIG_TCPC_AUTO_DISCHARGE_IC */
#define CONFIG_TYPEC_CAP_FORCE_DISCHARGE
/* #define CONFIG_TCPC_FORCE_DISCHARGE_IC */
#define CONFIG_TCPC_FORCE_DISCHARGE_EXT
#define CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS
#define CONFIG_TYPEC_CAP_LOW_RP_DUTY
#define CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY
/* #define CONFIG_TYPEC_CAP_CUSTOM_HV */
#define CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK
/* #define CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SRC */
#define CONFIG_TCPC_ATTACH_WAKE_LOCK_TOUT 5000
/* #define CONFIG_TCPC_LOG_WITH_PORT_NAME */
#ifdef CONFIG_TCPC_LOG_WITH_PORT_NAME
#define CONFIG_TCPC_DBG_PRESTR "{%s}TCPC-"
#else
#define CONFIG_TCPC_DBG_PRESTR "TCPC-"
#endif /* CONFIG_TCPC_LOG_WITH_PORT_NAME */
/*
* USB 2.0 & 3.0 current
* Unconfigured : 100 / 150 mA
* Configured : 500 / 900 mA
* http://www.testusb.com/power_issue.htm
*/
#define CONFIG_TYPEC_SNK_CURR_DFT 100
#define CONFIG_TYPEC_SRC_CURR_DFT 500
#define CONFIG_TYPEC_SNK_CURR_LIMIT 0
#define CONFIG_TCPC_SOURCE_VCONN
#define CONFIG_TCPC_VCONN_SUPPLY_MODE
#define CONFIG_TCPC_VSAFE0V_DETECT
/* #define CONFIG_TCPC_VSAFE0V_DETECT_EXT */
#define CONFIG_TCPC_VSAFE0V_DETECT_IC
#define CONFIG_TCPC_LPM_CONFIRM
#define CONFIG_TCPC_LPM_POSTPONE
#define CONFIG_TCPC_LOW_POWER_MODE
#define CONFIG_TCPC_CLOCK_GATING
/* #define CONFIG_TCPC_WATCHDOG_EN */
/* #define CONFIG_TCPC_INTRST_EN */
#define CONFIG_TCPC_I2CRST_EN
#define CONFIG_TCPC_SHUTDOWN_CC_DETACH
#define CONFIG_TCPC_SHUTDOWN_VBUS_DISABLE
#define CONFIG_TCPC_NOTIFIER_LATE_SYNC
#define CONFIG_TCPC_NOTIFICATION_NON_BLOCKING
#ifdef CONFIG_USB_POWER_DELIVERY
/* Experimental for CodeSize */
#define CONFIG_USB_PD_DR_SWAP
#define CONFIG_USB_PD_PR_SWAP
#define CONFIG_USB_PD_VCONN_SWAP
#define CONFIG_USB_PD_PE_SINK
#define CONFIG_USB_PD_PE_SOURCE
/* #define CONFIG_USB_PD_DISABLE_PE */
#define CONFIG_USB_PD_TCPM_CB_RETRY 3
#define CONFIG_USB_PD_TCPM_CB_2ND
#define CONFIG_USB_PD_BLOCK_TCPM
/* #define CONFIG_USB_PD_RICHTEK_UVDM */
/**********************************************************
* Mode Operation
**********************************************************/
#define CONFIG_USB_PD_MODE_OPERATION
#ifdef CONFIG_USB_PD_MODE_OPERATION
#define CONFIG_USB_PD_ATTEMP_ENTER_MODE
#define CONFIG_USB_PD_ALT_MODE
#ifdef CONFIG_USB_PD_ALT_MODE
#define CONFIG_USB_PD_ALT_MODE_DFP
#define CONFIG_USB_PD_ALT_MODE_RTDC
#endif /* CONFIG_USB_PD_ALT_MODE */
/* #define CONFIG_USB_PD_DP_CHECK_CABLE */
/* #define CONFIG_USB_PD_RTDC_CHECK_CABLE */
#endif /* CONFIG_USB_PD_MODE_OPERATION */
/**********************************************************
* PD revision 3.0 feature
**********************************************************/
#define CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30
#define CONFIG_USB_PD_REV30_SYNC_SPEC_REV
#define CONFIG_USB_PD_REV30_COLLISION_AVOID
/*
* If DUT send a PD command immediately after Policy Engine is ready,
* it may interrupt the compliance test process and getting a failed result.
* even you make sure the CC state is SinkTxNG or SinkTxOK.
*
* SRC_FLOW_DELAY_STARTUP: For Ellisys
* SNK_FLOW_DELAY_STARTUP: For MQP
*/
#define CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP
#define CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP
#define CONFIG_USB_PD_REV30_DISCOVER_CABLE_WITH_VCONN
/* PD30 Common Feature */
#define CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
#define CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
#define CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
#define CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
#define CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
#define CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
#define CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
#define CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
#define CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
#define CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
#define CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL
#define CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
#define CONFIG_USB_PD_REV30_ALERT_LOCAL
#define CONFIG_USB_PD_REV30_ALERT_REMOTE
#define CONFIG_USB_PD_REV30_STATUS_LOCAL
#define CONFIG_USB_PD_REV30_STATUS_REMOTE
/* #define CONFIG_USB_PD_REV30_CHUNKING_BY_PE */
#define CONFIG_USB_PD_REV30_PPS_SINK
/* #define CONFIG_USB_PD_REV30_PPS_SOURCE */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
#define CONFIG_USB_PD_REV30_STATUS_LOCAL_TEMP
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
#define CONFIG_USB_PD_REV30_BAT_INFO
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
#undef CONFIG_USB_PD_REV30_BAT_INFO
#define CONFIG_USB_PD_REV30_BAT_INFO
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
#define CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL
#undef CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY
#define CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
#define CONFIG_USB_PD_DPM_AUTO_SEND_ALERT
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
#define CONFIG_USB_PD_DPM_AUTO_GET_STATUS
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#define CONFIG_MTK_HANDLE_PPS_TIMEOUT
#endif /* CONFIG_USB_PD_REV30 */
/**********************************************************
* PD direct charge support
**********************************************************/
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
#define CONFIG_USB_PD_DIRECT_CHARGE
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
#undef CONFIG_USB_PD_DIRECT_CHARGE
#define CONFIG_USB_PD_DIRECT_CHARGE
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
#define CONFIG_USB_PD_KEEP_PARTNER_ID
#define CONFIG_USB_PD_KEEP_SVIDS
#define CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
#define CONFIG_USB_PD_DFP_READY_DISCOVER_ID
#define CONFIG_USB_PD_RESET_CABLE
#define CONFIG_USB_PD_RANDOM_FLOW_DELAY
#define CONFIG_USB_PD_DFP_FLOW_DELAY
#define CONFIG_USB_PD_DFP_FLOW_DELAY_STARTUP
#define CONFIG_USB_PD_DFP_FLOW_DELAY_DRSWAP
#define CONFIG_USB_PD_DFP_FLOW_DELAY_RESET
/* Only in startup */
#define CONFIG_USB_PD_UFP_FLOW_DELAY
#define CONFIG_USB_PD_VCONN_STABLE_DELAY
#define CONFIG_USB_PD_VCONN_SAFE5V_ONLY
#define CONFIG_USB_PD_ATTEMP_DISCOVER_ID
#define CONFIG_USB_PD_ATTEMP_DISCOVER_SVID
#define CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN
#define CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN
#define CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY
#define CONFIG_USB_PD_CUSTOM_VDM
#ifdef CONFIG_USB_PD_CUSTOM_VDM
#define CONFIG_USB_PD_SVDM
#define CONFIG_USB_PD_UVDM
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK
/* #define CONFIG_USB_PD_CUSTOM_DBGACC */
#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */
/* S/W patch for ESD issue: repeat HReset Alert */
/* #define CONFIG_USB_PD_RECV_HRESET_COUNTER */
/* S/W patch for NoGoodCRC if SNK_DFT */
#define CONFIG_USB_PD_SNK_DFT_NO_GOOD_CRC
/* S/W patch for NoGoodCRC after PR_SWAP (repeat PS_RDY) */
#define CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
/* S/W patch for delayed ps_change related to PS_RDY during PR_SWAP */
#define CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP
/*
* S/W patch for INT handler was stuck by other task (system busy)
*
* If the communication fails due to a timeout,
* check the rx buffer is empty or not.
*
* If the rx buffer isn't empty, postpone timer.
*/
#define CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT
#define CONFIG_USB_PD_ONLY_PRINT_SYSTEM_BUSY
/*
* S/W patch for
* If the CC pin is shorted to ground and our receive ability is better,
* then we may receive message however always send failed.
*
* For the source role, it becomes an infinite hard reset loop.
* For the sink role, it becomes an infinite error recovery loop.
*
* RENEGOTIATION_COUNTER:
* Auto error recovery
* if retried hard reset many times still negotiation failed.
*
* ERROR_RECOVERY_ONCE:
* If we have tried an error recovery,
* using pure typec mode during next time we reconnect.
*
*/
#define CONFIG_USB_PD_RENEGOTIATION_COUNTER
#define CONFIG_USB_PD_ERROR_RECOVERY_ONCE
#define CONFIG_USB_PD_IGNORE_HRESET_COMPLETE_TIMER
#define CONFIG_USB_PD_DROP_REPEAT_PING
#define CONFIG_USB_PD_CHECK_DATA_ROLE
#define CONFIG_USB_PD_RETRY_CRC_DISCARD
#define CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST
#define CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW
/* #define CONFIG_USB_PD_SNK_IGNORE_HRESET_IF_TYPEC_ONLY */
#define CONFIG_USB_PD_SNK_STANDBY_POWER
#define CONFIG_USB_PD_SNK_GOTOMIN
/* #define CONFIG_USB_PD_SRC_HIGHCAP_POWER */
#define CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW
#define CONFIG_USB_PD_TRANSMIT_BIST2
#define CONFIG_USB_PD_POSTPONE_VDM
#define CONFIG_USB_PD_POSTPONE_RETRY_VDM
#define CONFIG_USB_PD_POSTPONE_FIRST_VDM
#define CONFIG_USB_PD_POSTPONE_OTHER_VDM
/* #define CONFIG_USB_PD_STOP_SEND_VDM_IF_RX_BUSY */
#define CONFIG_USB_PD_STOP_REPLY_VDM_IF_RX_BUSY
/* #define CONFIG_USB_PD_SAFE0V_DELAY */
/* #define CONFIG_USB_PD_SAFE0V_TIMEOUT */
#ifndef CONFIG_USB_PD_DPM_SVDM_RETRY
#define CONFIG_USB_PD_DPM_SVDM_RETRY 2
#endif /* CONFIG_USB_PD_DPM_SVDM_RETRY */
#ifndef CONFIG_USB_PD_DFP_FLOW_RETRY_MAX
#define CONFIG_USB_PD_DFP_FLOW_RETRY_MAX 2
#endif /* CONFIG_USB_PD_DFP_FLOW_RETRY_MAX */
#ifndef CONFIG_USB_PD_VBUS_STABLE_TOUT
#define CONFIG_USB_PD_VBUS_STABLE_TOUT 125
#endif /* CONFIG_USB_PD_VBUS_STABLE_TOUT */
#ifndef CONFIG_USB_PD_VBUS_PRESENT_TOUT
#define CONFIG_USB_PD_VBUS_PRESENT_TOUT 20
#endif /* CONFIG_USB_PD_VBUS_PRESENT_TOUT */
#ifndef CONFIG_USB_PD_CUSTOM_VDM_TOUT
#define CONFIG_USB_PD_CUSTOM_VDM_TOUT 500
#endif /* CONFIG_USB_PD_CUSTOM_VDM_TOUT */
#ifndef CONFIG_USB_PD_VCONN_READY_TOUT
#define CONFIG_USB_PD_VCONN_READY_TOUT 5
#endif /* CONFIG_USB_PD_VCONN_READY_TOUT */
#ifndef CONFIG_USB_PD_DFP_FLOW_DLY
#define CONFIG_USB_PD_DFP_FLOW_DLY 30
#endif /* CONFIG_USB_PD_DFP_FLOW_DLY */
#ifndef CONFIG_USB_PD_UFP_FLOW_DLY
#define CONFIG_USB_PD_UFP_FLOW_DLY 300
#endif /* CONFIG_USB_PD_UFP_FLOW_DLY */
#ifndef CONFIG_USB_PD_PPS_REQUEST_INTERVAL
#define CONFIG_USB_PD_PPS_REQUEST_INTERVAL 7000
#endif /* CONFIG_USB_PD_PPS_REQUEST_INTERVAL */
#undef CONFIG_USB_PD_WAIT_BC12
#endif /* CONFIG_USB_POWER_DELIVERY */
/* debug config */
#define CONFIG_USB_PD_DBG_ALERT_STATUS
/* #define CONFIG_USB_PD_DBG_SKIP_ALERT_HANDLER */
#define CONFIG_USB_PD_DBG_DP_DFP_D_AUTO_UPDATE
/**********************************************************
* TypeC Shield Protection
**********************************************************/
#ifdef CONFIG_MTK_TYPEC_WATER_DETECT
#define CONFIG_WATER_DETECTION
#endif /* CONFIG_MTK_TYPEC_WATER_DETECT */
#ifdef CONFIG_WATER_DETECTION
#define CONFIG_WD_SBU_POLLING
/* #define CONFIG_WATER_CALIBRATION */
#define CONFIG_WD_SBU_CALIB_INIT 1800 /* mV */
#define CONFIG_WD_SBU_PL_BOUND 200 /* mV */
#define CONFIG_WD_SBU_PH_AUDDEV 200 /* mV */
#define CONFIG_WD_SBU_PH_LBOUND 1180 /* mV */
#define CONFIG_WD_SBU_PH_LBOUND1_C2C 2850 /* mV */
#define CONFIG_WD_SBU_PH_UBOUND1_C2C 3150 /* mV */
#define CONFIG_WD_SBU_PH_UBOUND2_C2C 3800 /* mV */
#define CONFIG_WD_SBU_AUD_UBOUND 1600 /* mV */
#define CONFIG_WD_PROTECT_RETRY_COUNT 3
#endif /* CONFIG_WATER_DETECTION */
#ifdef CONFIG_WD_SBU_POLLING
#define CONFIG_WD_POLLING_ONLY
#endif /* CONFIG_WD_SBU_POLLING */
#define CONFIG_CABLE_TYPE_DETECTION
#endif /* CONFIG_TCPC_CLASS */
#endif /* __LINUX_TCPC_CONFIG_H */

View file

@ -0,0 +1,694 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_RT_TCPCI_CORE_H
#define __LINUX_RT_TCPCI_CORE_H
#include <linux/device.h>
#include <linux/hrtimer.h>
#include <linux/alarmtimer.h>
#include <linux/workqueue.h>
#include <linux/pm_wakeup.h>
#include <linux/notifier.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
#include "tcpm.h"
#include "tcpci_timer.h"
#include "tcpci_config.h"
#ifdef CONFIG_USB_POWER_DELIVERY
#include "pd_core.h"
#ifdef CONFIG_USB_PD_WAIT_BC12
#include <linux/power_supply.h>
#endif /* CONFIG_USB_PD_WAIT_BC12 */
#endif
/* The switch of log message */
#define TYPEC_INFO_ENABLE 1
#define TYPEC_INFO2_ENABLE 1
#define PE_EVENT_DBG_ENABLE 1
#define PE_STATE_INFO_ENABLE 1
#define TCPC_INFO_ENABLE 1
#define TCPC_TIMER_DBG_EN 0
#define TCPC_TIMER_INFO_EN 0
#define PE_INFO_ENABLE 1
#define TCPC_DBG_ENABLE 0
#define TCPC_DBG2_ENABLE 0
#define DPM_INFO_ENABLE 1
#define DPM_INFO2_ENABLE 1
#define DPM_DBG_ENABLE 0
#define PD_ERR_ENABLE 1
#define PE_DBG_ENABLE 0
#define TYPEC_DBG_ENABLE 0
#define DP_INFO_ENABLE 1
#define DP_DBG_ENABLE 1
#define UVDM_INFO_ENABLE 1
#define TCPM_DBG_ENABLE 1
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
#define DC_INFO_ENABLE 1
#define DC_DBG_ENABLE 1
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
#define TCPC_ENABLE_ANYMSG \
((TCPC_DBG_ENABLE)|(TCPC_DBG2_ENABLE)|\
(DPM_DBG_ENABLE)|\
(PD_ERR_ENABLE)|(PE_INFO_ENABLE)|(TCPC_TIMER_INFO_EN)|\
(PE_DBG_ENABLE)|(PE_EVENT_DBG_ENABLE)|\
(PE_STATE_INFO_ENABLE)|(TCPC_INFO_ENABLE)|\
(TCPC_TIMER_DBG_EN)|(TYPEC_DBG_ENABLE)|\
(TYPEC_INFO_ENABLE)|\
(DP_INFO_ENABLE)|(DP_DBG_ENABLE)|\
(UVDM_INFO_ENABLE)|(TCPM_DBG_ENABLE))
/* Disable VDM DBG Msg */
#define PE_STATE_INFO_VDM_DIS 0
#define PE_EVT_INFO_VDM_DIS 0
#define PE_DBG_RESET_VDM_DIS 1
#define PD_BUG_ON(x) WARN_ON(x)
extern struct class *tcpc_class;
struct tcpc_device;
struct tcpc_desc {
uint8_t role_def;
uint8_t rp_lvl;
uint8_t vconn_supply;
int notifier_supply_num;
char *name;
};
int tcpc_device_irq_enable(struct tcpc_device *tcpc);
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK
#define CONFIG_TYPEC_NOTIFY_ATTACHWAIT
#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK */
#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SRC
#undef CONFIG_TYPEC_NOTIFY_ATTACHWAIT
#define CONFIG_TYPEC_NOTIFY_ATTACHWAIT
#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK */
#ifdef CONFIG_TCPC_FORCE_DISCHARGE_EXT
#define CONFIG_TCPC_EXT_DISCHARGE
#endif /* CONFIG_TCPC_FORCE_DISCHARGE_EXT */
/*---------------------------------------------------------------------------*/
/* TCPC Power Register Define */
#define TCPC_REG_POWER_STATUS_EXT_VSAFE0V (1<<15) /* extend */
#define TCPC_REG_POWER_STATUS_VBUS_PRES (1<<2)
/* TCPC Alert Register Define */
#define TCPC_REG_ALERT_EXT_RA_DETACH (1<<(16+5))
#define TCPC_REG_ALERT_EXT_WATCHDOG (1<<(16+2))
#define TCPC_REG_ALERT_EXT_VBUS_80 (1<<(16+1))
#define TCPC_REG_ALERT_EXT_WAKEUP (1<<(16+0))
#define TCPC_REG_ALERT_VBUS_DISCNCT (1<<11)
#define TCPC_REG_ALERT_RX_BUF_OVF (1<<10)
#define TCPC_REG_ALERT_FAULT (1<<9)
#define TCPC_REG_ALERT_V_ALARM_LO (1<<8)
#define TCPC_REG_ALERT_V_ALARM_HI (1<<7)
#define TCPC_REG_ALERT_TX_SUCCESS (1<<6)
#define TCPC_REG_ALERT_TX_DISCARDED (1<<5)
#define TCPC_REG_ALERT_TX_FAILED (1<<4)
#define TCPC_REG_ALERT_RX_HARD_RST (1<<3)
#define TCPC_REG_ALERT_RX_STATUS (1<<2)
#define TCPC_REG_ALERT_POWER_STATUS (1<<1)
#define TCPC_REG_ALERT_CC_STATUS (1<<0)
#define TCPC_REG_ALERT_RX_MASK \
(TCPC_REG_ALERT_RX_STATUS | TCPC_REG_ALERT_RX_BUF_OVF)
#define TCPC_REG_ALERT_RX_ALL_MASK \
(TCPC_REG_ALERT_RX_MASK | TCPC_REG_ALERT_RX_HARD_RST)
#define TCPC_REG_ALERT_HRESET_SUCCESS \
(TCPC_REG_ALERT_TX_SUCCESS | TCPC_REG_ALERT_TX_FAILED)
#define TCPC_REG_ALERT_TX_MASK (TCPC_REG_ALERT_TX_SUCCESS | \
TCPC_REG_ALERT_TX_FAILED | TCPC_REG_ALERT_TX_DISCARDED)
#define TCPC_REG_ALERT_TXRX_MASK \
(TCPC_REG_ALERT_TX_MASK | TCPC_REG_ALERT_RX_MASK)
/* TCPC Behavior Flags */
#define TCPC_FLAGS_RETRY_CRC_DISCARD (1<<0)
#define TCPC_FLAGS_WAIT_HRESET_COMPLETE (1<<1) /* Always true */
#define TCPC_FLAGS_CHECK_CC_STABLE (1<<2)
#define TCPC_FLAGS_LPM_WAKEUP_WATCHDOG (1<<3)
#define TCPC_FLAGS_CHECK_RA_DETACHE (1<<4)
#define TCPC_FLAGS_PREFER_LEGACY2 (1<<5)
#define TCPC_FLAGS_DISABLE_LEGACY (1<<6)
#define TCPC_FLAGS_PD_REV30 (1<<7)
#define TCPC_FLAGS_WATCHDOG_EN (1<<8)
#define TCPC_FLAGS_WATER_DETECTION (1<<9)
#define TCPC_FLAGS_CABLE_TYPE_DETECTION (1<<10)
#define TCPC_FLAGS_VCONN_SAFE5V_ONLY (1<<11)
#define TCPC_FLAGS_ALERT_V10 (1<<12)
enum tcpc_cc_pull {
TYPEC_CC_RA = 0,
TYPEC_CC_RP = 1,
TYPEC_CC_RD = 2,
TYPEC_CC_OPEN = 3,
TYPEC_CC_DRP = 4, /* from Rd */
TYPEC_CC_RP_DFT = 1, /* 0x00 + 1 */
TYPEC_CC_RP_1_5 = 9, /* 0x08 + 1*/
TYPEC_CC_RP_3_0 = 17, /* 0x10 + 1 */
TYPEC_CC_DRP_DFT = 4, /* 0x00 + 4 */
TYPEC_CC_DRP_1_5 = 12, /* 0x08 + 4 */
TYPEC_CC_DRP_3_0 = 20, /* 0x10 + 4 */
};
#define TYPEC_CC_PULL_GET_RES(pull) (pull & 0x07)
#define TYPEC_CC_PULL_GET_RP_LVL(pull) ((pull & 0x18) >> 3)
enum tcpm_rx_cap_type {
TCPC_RX_CAP_SOP = 1 << 0,
TCPC_RX_CAP_SOP_PRIME = 1 << 1,
TCPC_RX_CAP_SOP_PRIME_PRIME = 1 << 2,
TCPC_RX_CAP_SOP_DEBUG_PRIME = 1 << 3,
TCPC_RX_CAP_SOP_DEBUG_PRIME_PRIME = 1 << 4,
TCPC_RX_CAP_HARD_RESET = 1 << 5,
TCPC_RX_CAP_CABLE_RESET = 1 << 6,
};
struct tcpc_ops {
int (*init)(struct tcpc_device *tcpc, bool sw_reset);
int (*init_alert_mask)(struct tcpc_device *tcpc);
int (*alert_status_clear)(struct tcpc_device *tcpc, uint32_t mask);
int (*fault_status_clear)(struct tcpc_device *tcpc, uint8_t status);
int (*set_alert_mask)(struct tcpc_device *tcpc, uint32_t mask);
int (*get_alert_mask)(struct tcpc_device *tcpc, uint32_t *mask);
int (*get_alert_status)(struct tcpc_device *tcpc, uint32_t *alert);
int (*get_power_status)(struct tcpc_device *tcpc, uint16_t *pwr_status);
int (*get_fault_status)(struct tcpc_device *tcpc, uint8_t *status);
int (*get_cc)(struct tcpc_device *tcpc, int *cc1, int *cc2);
int (*set_cc)(struct tcpc_device *tcpc, int pull);
int (*set_polarity)(struct tcpc_device *tcpc, int polarity);
int (*set_low_rp_duty)(struct tcpc_device *tcpc, bool low_rp);
int (*set_vconn)(struct tcpc_device *tcpc, int enable);
int (*deinit)(struct tcpc_device *tcpc);
int (*alert_vendor_defined_handler)(struct tcpc_device *tcpc);
#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
int (*is_vsafe0v)(struct tcpc_device *tcpc);
#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
#ifdef CONFIG_WATER_DETECTION
int (*is_water_detected)(struct tcpc_device *tcpc);
int (*set_water_protection)(struct tcpc_device *tcpc, bool en);
int (*set_usbid_polling)(struct tcpc_device *tcpc, bool en);
#endif /* CONFIG_WATER_DETECTION */
#ifdef CONFIG_TCPC_LOW_POWER_MODE
int (*is_low_power_mode)(struct tcpc_device *tcpc);
int (*set_low_power_mode)(struct tcpc_device *tcpc, bool en, int pull);
#endif /* CONFIG_TCPC_LOW_POWER_MODE */
int (*set_watchdog)(struct tcpc_device *tcpc, bool en);
#ifdef CONFIG_TCPC_INTRST_EN
int (*set_intrst)(struct tcpc_device *tcpc, bool en);
#endif /* CONFIG_TCPC_INTRST_EN */
#ifdef CONFIG_TYPEC_CAP_AUTO_DISCHARGE
#ifdef CONFIG_TCPC_AUTO_DISCHARGE_IC
int (*set_auto_discharge)(struct tcpc_device *tcpc, bool en);
#endif /* CONFIG_TCPC_AUTO_DISCHARGE_IC */
#endif /* CONFIG_TYPEC_CAP_AUTO_DISCHARGE */
#ifdef CONFIG_USB_POWER_DELIVERY
int (*set_msg_header)(struct tcpc_device *tcpc,
uint8_t power_role, uint8_t data_role);
int (*set_rx_enable)(struct tcpc_device *tcpc, uint8_t enable);
int (*get_message)(struct tcpc_device *tcpc, uint32_t *payload,
uint16_t *head, enum tcpm_transmit_type *type);
int (*protocol_reset)(struct tcpc_device *tcpc);
int (*transmit)(struct tcpc_device *tcpc,
enum tcpm_transmit_type type,
uint16_t header, const uint32_t *data);
int (*set_bist_test_mode)(struct tcpc_device *tcpc, bool en);
int (*set_bist_carrier_mode)(struct tcpc_device *tcpc, uint8_t pattern);
#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
int (*retransmit)(struct tcpc_device *tcpc);
#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
#ifdef CONFIG_TYPEC_CAP_FORCE_DISCHARGE
#ifdef CONFIG_TCPC_FORCE_DISCHARGE_IC
int (*set_force_discharge)(struct tcpc_device *tcpc, bool en, int mv);
#endif /* CONFIG_TCPC_FORCE_DISCHARGE_IC */
#endif /* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */
#endif /* CONFIG_USB_POWER_DELIVERY */
};
#define TCPC_VBUS_SOURCE_0V (0)
#define TCPC_VBUS_SOURCE_5V (5000)
#define TCPC_VBUS_SINK_0V (0)
#define TCPC_VBUS_SINK_5V (5000)
#define TCPC_LOW_POWER_MODE_RETRY 5
/*
* Confirm DUT is connected to legacy cable or not
* after suupect_counter > this threshold (0 = always check)
*/
#define TCPC_LEGACY_CABLE_SUSPECT_THD 1
/*
* Try another s/w workaround after retry_counter more than this value
* Try which soltuion first is determined by tcpc_flags
*/
#define TCPC_LEGACY_CABLE_RETRY_SOLUTION 2
struct tcpc_managed_res;
/*
* tcpc device
*/
struct tcpc_device {
struct i2c_client *client;
struct tcpc_ops *ops;
void *drv_data;
struct tcpc_desc desc;
struct device dev;
bool wake_lock_user;
uint8_t wake_lock_pd;
struct wakeup_source *attach_wake_lock;
struct wakeup_source *detach_wake_lock;
/* For tcpc timer & event */
uint32_t timer_handle_index;
struct hrtimer tcpc_timer[PD_TIMER_NR];
struct alarm wake_up_timer;
struct delayed_work wake_up_work;
struct wakeup_source *wakeup_wake_lock;
ktime_t last_expire[PD_TIMER_NR];
struct mutex access_lock;
struct mutex typec_lock;
struct mutex timer_lock;
struct semaphore timer_enable_mask_lock;
spinlock_t timer_tick_lock;
atomic_t pending_event;
uint64_t timer_tick;
uint64_t timer_enable_mask;
wait_queue_head_t event_wait_que;
wait_queue_head_t timer_wait_que;
struct task_struct *event_task;
struct task_struct *timer_task;
struct delayed_work init_work;
struct delayed_work event_init_work;
struct workqueue_struct *evt_wq;
struct srcu_notifier_head evt_nh[TCP_NOTIFY_IDX_NR];
struct tcpc_managed_res *mr_head;
struct mutex mr_lock;
/* For TCPC TypeC */
uint8_t typec_state;
uint8_t typec_role;
uint8_t typec_role_new;
uint8_t typec_attach_old;
uint8_t typec_attach_new;
uint8_t typec_local_cc;
uint8_t typec_local_rp_level;
uint8_t typec_remote_cc[2];
uint8_t typec_remote_rp_level;
uint8_t typec_wait_ps_change;
bool typec_polarity;
bool typec_drp_try_timeout;
bool typec_lpm;
bool typec_cable_only;
bool typec_power_ctrl;
bool typec_watchdog;
bool typec_reach_vsafe0v;
bool typec_is_attached_src;
int typec_usb_sink_curr;
#ifdef CONFIG_TYPEC_CAP_CUSTOM_HV
bool typec_during_custom_hv;
#endif /* CONFIG_TYPEC_CAP_CUSTOM_HV */
uint8_t typec_lpm_pull;
uint8_t typec_lpm_retry;
#ifdef CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY
bool typec_wakeup_once;
bool typec_low_rp_duty_cntdown;
#endif /* CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY */
#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
uint8_t typec_legacy_cable;
#if TCPC_LEGACY_CABLE_SUSPECT_THD
uint8_t typec_legacy_cable_suspect;
#endif /* TCPC_LEGACY_CABLE_SUSPECT_THD */
#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2
uint8_t typec_legacy_retry_wk;
#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */
#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP
uint8_t typec_during_role_swap;
#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */
#ifdef CONFIG_TYPEC_CAP_AUTO_DISCHARGE
#ifdef CONFIG_TCPC_AUTO_DISCHARGE_IC
bool typec_auto_discharge;
#endif /* CONFIG_TCPC_AUTO_DISCHARGE_IC */
#endif /* CONFIG_TYPEC_CAP_AUTO_DISCHARGE */
#ifdef CONFIG_TCPC_EXT_DISCHARGE
bool typec_ext_discharge;
#endif /* CONFIG_TCPC_EXT_DISCHARGE */
#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE
uint8_t tcpc_vconn_supply;
#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */
#ifdef CONFIG_TCPC_SOURCE_VCONN
bool tcpc_source_vconn;
#endif /* CONFIG_TCPC_SOURCE_VCONN */
uint32_t tcpc_flags;
#ifdef CONFIG_DUAL_ROLE_USB_INTF
struct dual_role_phy_instance *dr_usb;
uint8_t dual_role_supported_modes;
uint8_t dual_role_mode;
uint8_t dual_role_pr;
uint8_t dual_role_dr;
uint8_t dual_role_vconn;
#endif /* CONFIG_DUAL_ROLE_USB_INTF */
#ifdef CONFIG_USB_POWER_DELIVERY
/* Event */
uint8_t pd_event_count;
uint8_t pd_event_head_index;
uint8_t pd_msg_buffer_allocated;
uint8_t pd_last_vdm_msg_id;
bool pd_pending_vdm_event;
bool pd_pending_vdm_reset;
bool pd_pending_vdm_good_crc;
bool pd_pending_vdm_discard;
bool pd_pending_vdm_attention;
bool pd_postpone_vdm_timeout;
struct pd_msg pd_last_vdm_msg;
struct pd_msg pd_attention_vdm_msg;
struct pd_event pd_vdm_event;
struct pd_msg pd_msg_buffer[PD_MSG_BUF_SIZE];
struct pd_event pd_event_ring_buffer[PD_EVENT_BUF_SIZE];
uint8_t tcp_event_count;
uint8_t tcp_event_head_index;
struct tcp_dpm_event tcp_event_ring_buffer[TCP_EVENT_BUF_SIZE];
bool pd_pe_running;
bool pd_wait_pe_idle;
bool pd_hard_reset_event_pending;
bool pd_wait_hard_reset_complete;
bool pd_wait_pr_swap_complete;
bool pd_ping_event_pending;
uint8_t pd_bist_mode;
uint8_t pd_transmit_state;
uint8_t pd_wait_vbus_once;
#ifdef CONFIG_USB_PD_DIRECT_CHARGE
bool pd_during_direct_charge;
#endif /* CONFIG_USB_PD_DIRECT_CHARGE */
#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
bool pd_discard_pending;
#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
#ifdef CONFIG_TYPEC_CAP_FORCE_DISCHARGE
#ifdef CONFIG_TCPC_FORCE_DISCHARGE_IC
bool pd_force_discharge;
#endif /* CONFIG_TCPC_FORCE_DISCHARGE_IC */
#endif /* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */
#ifdef CONFIG_USB_PD_REV30
uint8_t pd_retry_count;
#endif /* CONFIG_USB_PD_REV30 */
#ifdef CONFIG_USB_PD_DISABLE_PE
bool disable_pe; /* typec only */
#endif /* CONFIG_USB_PD_DISABLE_PE */
struct pd_port pd_port;
#ifdef CONFIG_USB_PD_REV30
struct notifier_block bat_nb;
struct delayed_work bat_update_work;
struct power_supply *bat_psy;
uint8_t charging_status;
int bat_soc;
#endif /* CONFIG_USB_PD_REV30 */
#ifdef CONFIG_USB_PD_WAIT_BC12
uint8_t pd_wait_bc12_count;
struct power_supply *usb_psy;
#endif /* CONFIG_USB_PD_WAIT_BC12 */
#endif /* CONFIG_USB_POWER_DELIVERY */
u8 vbus_level:2;
bool vbus_safe0v;
bool vbus_present;
u8 irq_enabled:1;
u8 pd_inited_flag:1;
/* TypeC Shield Protection */
#ifdef CONFIG_WATER_DETECTION
int usbid_calib;
#endif /* CONFIG_WATER_DETECTION */
#ifdef CONFIG_CABLE_TYPE_DETECTION
enum tcpc_cable_type typec_cable_type;
#endif /* CONFIG_CABLE_TYPE_DETECTION */
};
#define to_tcpc_device(obj) container_of(obj, struct tcpc_device, dev)
#ifdef CONFIG_USB_POWER_DELIVERY
static inline uint8_t pd_get_rev(struct pd_port *pd_port, uint8_t sop_type)
{
uint8_t pd_rev = PD_REV20;
#ifdef CONFIG_USB_PD_REV30_SYNC_SPEC_REV
struct pe_data *pe_data = &pd_port->pe_data;
struct tcpc_device *tcpc = pd_port->tcpc;
if (sop_type == TCPC_TX_SOP) {
pd_rev = pd_port->pd_revision[0];
} else {
if (pe_data->explicit_contract || pe_data->cable_rev_discovered)
pd_rev = pd_port->pd_revision[1];
else if (tcpc->tcpc_flags & TCPC_FLAGS_PD_REV30)
pd_rev = PD_REV30;
}
#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */
return pd_rev;
}
static inline bool pd_check_rev30(struct pd_port *pd_port)
{
return pd_get_rev(pd_port, TCPC_TX_SOP) >= PD_REV30;
}
#endif /* CONFIG_USB_POWER_DELIVERY */
#ifdef CONFIG_PD_DBG_INFO
#define __RT_DBG_INFO pd_dbg_info
#else
#define __RT_DBG_INFO pr_info
#endif /* CONFIG_PD_DBG_INFO */
#ifdef CONFIG_TCPC_LOG_WITH_PORT_NAME
#define RT_DBG_INFO(format, args...) __RT_DBG_INFO(format, \
tcpc->desc.name, ##args)
#else
#define RT_DBG_INFO(format, args...) __RT_DBG_INFO(format, ##args)
#endif /* CONFIG_TCPC_LOG_WITH_PORT_NAME */
#if TYPEC_DBG_ENABLE
#define TYPEC_DBG(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TYPEC:" format, ##args)
#else
#define TYPEC_DBG(format, args...)
#endif /* TYPEC_DBG_ENABLE */
#if TYPEC_INFO_ENABLE
#define TYPEC_INFO(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TYPEC:" format, ##args)
#else
#define TYPEC_INFO(format, args...)
#endif /* TYPEC_INFO_ENABLE */
#if TYPEC_INFO2_ENABLE
#define TYPEC_INFO2(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TYPEC:" format, ##args)
#else
#define TYPEC_INFO2(format, args...)
#endif /* TYPEC_INFO_ENABLE */
#if TCPC_INFO_ENABLE
#define TCPC_INFO(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPC:" format, ##args)
#else
#define TCPC_INFO(foramt, args...)
#endif /* TCPC_INFO_ENABLE */
#if TCPC_DBG_ENABLE
#define TCPC_DBG(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPC:" format, ##args)
#else
#define TCPC_DBG(format, args...)
#endif /* TCPC_DBG_ENABLE */
#if TCPC_DBG2_ENABLE
#define TCPC_DBG2(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPC:" format, ##args)
#else
#define TCPC_DBG2(format, args...)
#endif /* TCPC_DBG2_ENABLE */
#define TCPC_ERR(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPC-ERR:" format, ##args)
#define DP_ERR(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DP-ERR:" format, ##args)
#if DPM_INFO_ENABLE
#define DPM_INFO(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DPM:" format, ##args)
#else
#define DPM_INFO(format, args...)
#endif /* DPM_DBG_INFO */
#if DPM_INFO2_ENABLE
#define DPM_INFO2(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DPM:" format, ##args)
#else
#define DPM_INFO2(format, args...)
#endif /* DPM_DBG_INFO */
#if DPM_DBG_ENABLE
#define DPM_DBG(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DPM:" format, ##args)
#else
#define DPM_DBG(format, args...)
#endif /* DPM_DBG_ENABLE */
#if PD_ERR_ENABLE
#define PD_ERR(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PD-ERR:" format, ##args)
#else
#define PD_ERR(format, args...)
#endif /* PD_ERR_ENABLE */
#if PE_INFO_ENABLE
#define PE_INFO(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PE:" format, ##args)
#else
#define PE_INFO(format, args...)
#endif /* PE_INFO_ENABLE */
#if PE_EVENT_DBG_ENABLE
#define PE_EVT_INFO(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PE-EVT:" format, ##args)
#else
#define PE_EVT_INFO(format, args...)
#endif /* PE_EVENT_DBG_ENABLE */
#if PE_DBG_ENABLE
#define PE_DBG(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PE:" format, ##args)
#else
#define PE_DBG(format, args...)
#endif /* PE_DBG_ENABLE */
#if PE_STATE_INFO_ENABLE
#define PE_STATE_INFO(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PE:" format, ##args)
#else
#define PE_STATE_INFO(format, args...)
#endif /* PE_STATE_IFNO_ENABLE */
#if DP_INFO_ENABLE
#define DP_INFO(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DP:" format, ##args)
#else
#define DP_INFO(format, args...)
#endif /* DP_INFO_ENABLE */
#if DP_DBG_ENABLE
#define DP_DBG(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DP:" format, ##args)
#else
#define DP_DBG(format, args...)
#endif /* DP_DBG_ENABLE */
#if UVDM_INFO_ENABLE
#define UVDM_INFO(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "UVDM:" format, ##args)
#else
#define UVDM_INFO(format, args...)
#endif
#if TCPM_DBG_ENABLE
#define TCPM_DBG(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPM:" format, ##args)
#else
#define TCPM_DBG(format, args...)
#endif
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
#if DC_INFO_ENABLE
#define DC_INFO(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DC> " format, ##args)
#else
#define DC_INFO(format, args...)
#endif
#if DC_DBG_ENABLE
#define DC_DBG(format, args...) \
RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DC> " format, ##args)
#else
#define DC_DBG(format, args...)
#endif
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
#endif /* #ifndef __LINUX_RT_TCPCI_CORE_H */

View file

@ -0,0 +1,264 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef TCPC_EVENT_BUF_H_INCLUDED
#define TCPC_EVENT_BUF_H_INCLUDED
#include "tcpci_timer.h"
#include "tcpm.h"
#define PD_MSG_BUF_SIZE (4*2)
#define PD_EVENT_BUF_SIZE (8*2)
#define TCP_EVENT_BUF_SIZE (2*2)
struct tcpc_device;
struct pd_msg {
uint8_t frame_type;
uint16_t msg_hdr;
uint32_t payload[7];
};
struct pd_event {
uint8_t event_type;
uint8_t msg;
uint8_t msg_sec;
struct pd_msg *pd_msg;
};
struct pd_msg *pd_alloc_msg(struct tcpc_device *tcpc);
void pd_free_msg(struct tcpc_device *tcpc, struct pd_msg *pd_msg);
bool pd_get_event(struct tcpc_device *tcpc, struct pd_event *pd_event);
bool pd_put_event(struct tcpc_device *tcpc,
const struct pd_event *pd_event, bool from_port_partner);
void pd_free_event(struct tcpc_device *tcpc, struct pd_event *pd_event);
bool pd_get_vdm_event(struct tcpc_device *tcpc, struct pd_event *pd_event);
bool pd_put_vdm_event(struct tcpc_device *tcpc,
struct pd_event *pd_event, bool from_port_partner);
bool pd_put_last_vdm_event(struct tcpc_device *tcpc);
bool pd_get_deferred_tcp_event(
struct tcpc_device *tcpc, struct tcp_dpm_event *tcp_event);
bool pd_put_deferred_tcp_event(
struct tcpc_device *tcpc, const struct tcp_dpm_event *tcp_event);
extern int tcpci_event_init(struct tcpc_device *tcpc);
extern int tcpci_event_deinit(struct tcpc_device *tcpc);
extern void pd_event_buf_reset(struct tcpc_device *tcpc);
bool __pd_put_cc_attached_event(struct tcpc_device *tcpc, uint8_t type);
bool pd_put_cc_attached_event(struct tcpc_device *tcpc, uint8_t type);
void pd_put_cc_detached_event(struct tcpc_device *tcpc);
void pd_put_recv_hard_reset_event(struct tcpc_device *tcpc);
void pd_put_sent_hard_reset_event(struct tcpc_device *tcpc);
bool pd_put_pd_msg_event(struct tcpc_device *tcpc, struct pd_msg *pd_msg);
void pd_put_hard_reset_completed_event(struct tcpc_device *tcpc);
void pd_put_vbus_changed_event(struct tcpc_device *tcpc, bool from_ic);
void pd_put_vbus_safe0v_event(struct tcpc_device *tcpc);
void pd_put_vbus_stable_event(struct tcpc_device *tcpc);
void pd_put_vbus_present_event(struct tcpc_device *tcpc);
enum pd_event_type {
PD_EVT_PD_MSG = 0, /* either ctrl msg or data msg */
PD_EVT_CTRL_MSG,
PD_EVT_DATA_MSG,
#ifdef CONFIG_USB_PD_REV30
PD_EVT_EXT_MSG,
#endif /* CONFIG_USB_PD_REV30 */
PD_EVT_PD_MSG_END,
PD_EVT_DPM_MSG = PD_EVT_PD_MSG_END,
PD_EVT_HW_MSG,
PD_EVT_PE_MSG,
PD_EVT_TIMER_MSG,
PD_EVT_TCP_MSG,
};
enum pd_msg_type {
/* Control Message type */
/* 0 Reserved */
PD_CTRL_GOOD_CRC = 1,
PD_CTRL_GOTO_MIN = 2,
PD_CTRL_ACCEPT = 3,
PD_CTRL_REJECT = 4,
PD_CTRL_PING = 5,
PD_CTRL_PS_RDY = 6,
PD_CTRL_GET_SOURCE_CAP = 7,
PD_CTRL_GET_SINK_CAP = 8,
PD_CTRL_DR_SWAP = 9,
PD_CTRL_PR_SWAP = 10,
PD_CTRL_VCONN_SWAP = 11,
PD_CTRL_WAIT = 12,
PD_CTRL_SOFT_RESET = 13,
/* 14-15 Reserved */
PD_CTRL_PD30_START = 0x10 + 0,
#ifdef CONFIG_USB_PD_REV30
PD_CTRL_NOT_SUPPORTED = 0x10 + 0,
PD_CTRL_GET_SOURCE_CAP_EXT = 0x10 + 1,
PD_CTRL_GET_STATUS = 0x10 + 2,
PD_CTRL_FR_SWAP = 0x10 + 3,
PD_CTRL_GET_PPS_STATUS = 0x10 + 4,
PD_CTRL_GET_COUNTRY_CODE = 0x10 + 5,
#endif /* CONFIG_USB_PD_REV30 */
/* 22-31 Reserved */
PD_CTRL_MSG_NR,
/* Data message type */
/* 0 Reserved */
PD_DATA_SOURCE_CAP = 1,
PD_DATA_REQUEST = 2,
PD_DATA_BIST = 3,
PD_DATA_SINK_CAP = 4,
PD_DATA_PD30_START = 5,
#ifdef CONFIG_USB_PD_REV30
PD_DATA_BAT_STATUS = 5,
PD_DATA_ALERT = 6,
PD_DATA_GET_COUNTRY_INFO = 7,
#endif /* CONFIG_USB_PD_REV30 */
/* 7-14 Reserved */
PD_DATA_VENDOR_DEF = 15,
PD_DATA_MSG_NR,
#ifdef CONFIG_USB_PD_REV30
/* Extended message type */
/* 0 Reserved */
PD_EXT_SOURCE_CAP_EXT = 1,
PD_EXT_STATUS = 2,
PD_EXT_GET_BAT_CAP = 3,
PD_EXT_GET_BAT_STATUS = 4,
PD_EXT_BAT_CAP = 5,
PD_EXT_GET_MFR_INFO = 6,
PD_EXT_MFR_INFO = 7,
PD_EXT_SEC_REQUEST = 8,
PD_EXT_SEC_RESPONSE = 9,
PD_EXT_FW_UPDATE_REQUEST = 10,
PD_EXT_FW_UPDATE_RESPONSE = 11,
PD_EXT_PPS_STATUS = 12,
PD_EXT_COUNTRY_INFO = 13,
PD_EXT_COUNTRY_CODES = 14,
/* 15 Reserved */
PD_EXT_MSG_NR,
#endif /* CONFIG_USB_PD_REV30 */
/* HW Message type */
PD_HW_CC_DETACHED = 0,
PD_HW_CC_ATTACHED,
PD_HW_RECV_HARD_RESET,
PD_HW_VBUS_PRESENT,
PD_HW_VBUS_ABSENT,
PD_HW_VBUS_SAFE0V,
PD_HW_VBUS_STABLE,
PD_HW_TX_FAILED, /* no good crc or discard */
PD_HW_TX_DISCARD, /* discard vdm msg */
PD_HW_RETRY_VDM, /* discard vdm msg (retry) */
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
PD_HW_SINK_TX_CHANGE,
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
PD_HW_MSG_NR,
/* PE Message type*/
PD_PE_RESET_PRL_COMPLETED = 0,
PD_PE_POWER_ROLE_AT_DEFAULT,
PD_PE_HARD_RESET_COMPLETED,
PD_PE_IDLE,
PD_PE_VDM_RESET,
PD_PE_VDM_NOT_SUPPORT,
PD_PE_MSG_NR,
/* DPM Message type */
PD_DPM_NOTIFIED = 0,
PD_DPM_ACK = PD_DPM_NOTIFIED,
PD_DPM_NAK,
PD_DPM_CAP_CHANGED,
PD_DPM_NOT_SUPPORT,
PD_DPM_MSG_NR,
};
enum pd_dpm_notify_type {
PD_DPM_NOTIFY_OK = 0,
PD_DPM_NOTIFY_CAP_MISMATCH,
};
enum pd_dpm_nak_type {
PD_DPM_NAK_REJECT = 0,
PD_DPM_NAK_WAIT = 1,
};
enum pd_tcp_sec_msg_type {
PD_TCP_FROM_TCPM = 0,
PD_TCP_FROM_PE = 1,
};
enum pd_tx_transmit_state {
PD_TX_STATE_GOOD_CRC = 0,
PD_TX_STATE_NO_GOOD_CRC,
PD_TX_STATE_DISCARD,
PD_TX_STATE_HARD_RESET,
PD_TX_STATE_NO_RESPONSE,
PD_TX_STATE_WAIT,
PD_TX_STATE_WAIT_CRC_VDM = PD_TX_STATE_WAIT,
PD_TX_STATE_WAIT_CRC_PD,
PD_TX_STATE_WAIT_HARD_RESET,
};
static inline bool pd_event_msg_match(struct pd_event *pd_event,
uint8_t type, uint8_t msg)
{
if (pd_event->event_type != type)
return false;
return pd_event->msg == msg;
}
static inline bool pd_event_ctrl_msg_match(
struct pd_event *pd_event, uint8_t msg)
{
return pd_event_msg_match(pd_event, PD_EVT_CTRL_MSG, msg);
}
static inline bool pd_event_data_msg_match(
struct pd_event *pd_event, uint8_t msg)
{
return pd_event_msg_match(pd_event, PD_EVT_DATA_MSG, msg);
}
static inline bool pd_event_hw_msg_match(struct pd_event *pd_event, uint8_t msg)
{
return pd_event_msg_match(pd_event, PD_EVT_HW_MSG, msg);
}
static inline bool pd_event_pe_msg_match(struct pd_event *pd_event, uint8_t msg)
{
return pd_event_msg_match(pd_event, PD_EVT_PE_MSG, msg);
}
static inline bool pd_event_timer_msg_match(
struct pd_event *pd_event, uint8_t msg)
{
return pd_event_msg_match(pd_event, PD_EVT_TIMER_MSG, msg);
}
#ifdef CONFIG_USB_PD_REV30
static inline bool pd_event_ext_msg_match(
struct pd_event *pd_event, uint8_t msg)
{
return pd_event_msg_match(pd_event, PD_EVT_EXT_MSG, msg);
}
#endif /* CONFIG_USB_PD_REV30 */
#endif /* TCPC_EVENT_BUF_H_INCLUDED */

View file

@ -0,0 +1,136 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef TCPC_TIMER_H_INCLUDED
#define TCPC_TIMER_H_INCLUDED
#include <linux/kernel.h>
#include "tcpci_config.h"
struct tcpc_device;
enum {
#ifdef CONFIG_USB_POWER_DELIVERY
PD_TIMER_DISCOVER_ID = 0,
PD_TIMER_BIST_CONT_MODE,
PD_TIMER_HARD_RESET_COMPLETE,
PD_TIMER_NO_RESPONSE,
PD_TIMER_PS_HARD_RESET,
PD_TIMER_PS_SOURCE_OFF,
PD_TIMER_PS_SOURCE_ON,
PD_TIMER_PS_TRANSITION,
PD_TIMER_SENDER_RESPONSE,
PD_TIMER_SINK_REQUEST,
PD_TIMER_SINK_WAIT_CAP,
PD_TIMER_SOURCE_CAPABILITY,
PD_TIMER_SOURCE_START,
PD_TIMER_VCONN_ON,
#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY
PD_TIMER_VCONN_STABLE,
#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */
PD_TIMER_VDM_MODE_ENTRY,
PD_TIMER_VDM_MODE_EXIT,
PD_TIMER_VDM_RESPONSE,
PD_TIMER_SOURCE_TRANSITION,
PD_TIMER_SRC_RECOVER,
#ifdef CONFIG_USB_PD_REV30
PD_TIMER_CK_NOT_SUPPORTED,
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
PD_TIMER_SINK_TX,
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE
PD_TIMER_SOURCE_PPS_TIMEOUT,
#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */
#endif /* CONFIG_USB_PD_REV30 */
/* PD_TIMER (out of spec )*/
PD_TIMER_VSAFE0V_DELAY,
PD_TIMER_VSAFE0V_TOUT,
PD_TIMER_DISCARD,
PD_TIMER_VBUS_STABLE,
PD_TIMER_VBUS_PRESENT,
PD_TIMER_UVDM_RESPONSE,
PD_TIMER_DFP_FLOW_DELAY,
PD_TIMER_UFP_FLOW_DELAY,
PD_TIMER_VCONN_READY,
PD_PE_VDM_POSTPONE,
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
PD_TIMER_DEFERRED_EVT,
#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP
PD_TIMER_SNK_FLOW_DELAY,
#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
#endif /* CONFIG_USB_PD_REV30 */
PD_TIMER_PE_IDLE_TOUT,
PD_PE_TIMER_END_ID,
#endif /* CONFIG_USB_POWER_DELIVERY */
/* TYPEC_RT_TIMER (out of spec )*/
#ifdef CONFIG_USB_POWER_DELIVERY
TYPEC_RT_TIMER_START_ID = PD_PE_TIMER_END_ID,
#else
TYPEC_RT_TIMER_START_ID = 0,
#endif /* CONFIG_USB_POWER_DELIVERY */
TYPEC_RT_TIMER_SAFE0V_DELAY = TYPEC_RT_TIMER_START_ID,
TYPEC_RT_TIMER_SAFE0V_TOUT,
TYPEC_RT_TIMER_ROLE_SWAP_START,
TYPEC_RT_TIMER_ROLE_SWAP_STOP,
TYPEC_RT_TIMER_STATE_CHANGE,
TYPEC_RT_TIMER_NOT_LEGACY,
TYPEC_RT_TIMER_LEGACY_STABLE,
TYPEC_RT_TIMER_LEGACY_RECYCLE,
TYPEC_RT_TIMER_DISCHARGE,
TYPEC_RT_TIMER_LOW_POWER_MODE,
#ifdef CONFIG_USB_POWER_DELIVERY
TYPEC_RT_TIMER_PE_IDLE,
#ifdef CONFIG_USB_PD_WAIT_BC12
TYPEC_RT_TIMER_PD_WAIT_BC12,
#endif /* CONFIG_USB_PD_WAIT_BC12 */
#endif /* CONFIG_USB_POWER_DELIVERY */
TYPEC_TIMER_ERROR_RECOVERY,
/* TYPEC-TRY-TIMER */
TYPEC_TRY_TIMER_START_ID,
TYPEC_TRY_TIMER_DRP_TRY = TYPEC_TRY_TIMER_START_ID,
TYPEC_TRY_TIMER_DRP_TRYWAIT,
/* TYPEC-DEBOUNCE-TIMER */
TYPEC_TIMER_START_ID,
TYPEC_TIMER_CCDEBOUNCE = TYPEC_TIMER_START_ID,
TYPEC_TIMER_PDDEBOUNCE,
#ifdef CONFIG_COMPATIBLE_APPLE_TA
TYPEC_TIMER_APPLE_CC_OPEN,
#endif /* CONFIG_COMPATIBLE_APPLE_TA */
TYPEC_TIMER_TRYCCDEBOUNCE,
TYPEC_TIMER_SRCDISCONNECT,
TYPEC_TIMER_DRP_SRC_TOGGLE,
#ifdef CONFIG_TYPEC_CAP_NORP_SRC
TYPEC_TIMER_NORP_SRC,
#endif /* CONFIG_TYPEC_CAP_NORP_SRC */
PD_TIMER_NR,
};
extern int tcpci_timer_init(struct tcpc_device *tcpc);
extern int tcpci_timer_deinit(struct tcpc_device *tcpc);
extern void tcpc_restart_timer(struct tcpc_device *tcpc, uint32_t timer_id);
extern void tcpc_enable_timer(struct tcpc_device *tcpc, uint32_t timer_id);
extern void tcpc_enable_wakeup_timer(struct tcpc_device *tcpc, bool en);
extern void tcpc_disable_timer(
struct tcpc_device *tcpc, uint32_t timer_id);
extern void tcpc_reset_typec_try_timer(struct tcpc_device *tcpc);
extern void tcpc_reset_typec_debounce_timer(struct tcpc_device *tcpc);
extern void tcpc_reset_pe_timer(struct tcpc_device *tcpc);
#endif /* TCPC_TIMER_H_INCLUDED */

View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_TCPCI_TYPEC_H
#define __LINUX_TCPCI_TYPEC_H
#include "tcpci.h"
struct tcpc_device;
extern bool tcpc_typec_is_act_as_sink_role(struct tcpc_device *tcpc);
/******************************************************************************
* Call following function to trigger TYPEC Connection State Change
*
* 1. H/W -> CC/PS Change.
* 2. Timer -> CCDebounce or PDDebounce or others Timeout
* 3. Policy Engine -> PR_SWAP, Error_Recovery, PE_Idle
*****************************************************************************/
extern int tcpc_typec_enter_lpm_again(struct tcpc_device *tcpc);
extern int tcpc_typec_handle_cc_change(struct tcpc_device *tcpc);
extern int tcpc_typec_handle_ps_change(
struct tcpc_device *tcpc, int vbus_level);
extern int tcpc_typec_handle_timeout(
struct tcpc_device *tcpc, uint32_t timer_id);
extern int tcpc_typec_handle_vsafe0v(struct tcpc_device *tcpc);
extern int tcpc_typec_set_rp_level(struct tcpc_device *tcpc, uint8_t res);
extern int tcpc_typec_error_recovery(struct tcpc_device *tcpc);
extern int tcpc_typec_disable(struct tcpc_device *tcpc);
extern int tcpc_typec_enable(struct tcpc_device *tcpc);
extern int tcpc_typec_change_role(
struct tcpc_device *tcpc, uint8_t typec_role, bool postpone);
#ifdef CONFIG_USB_POWER_DELIVERY
extern int tcpc_typec_handle_pe_pr_swap(struct tcpc_device *tcpc);
#endif /* CONFIG_USB_POWER_DELIVERY */
#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP
extern int tcpc_typec_swap_role(struct tcpc_device *tcpc);
#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */
#ifdef CONFIG_WATER_DETECTION
extern int tcpc_typec_handle_wd(struct tcpc_device *tcpc, bool wd);
#endif /* CONFIG_WATER_DETECTION */
#ifdef CONFIG_CABLE_TYPE_DETECTION
extern int tcpc_typec_handle_ctd(struct tcpc_device *tcpc,
enum tcpc_cable_type cable_type);
#endif /* CONFIG_CABLE_TYPEC_DETECTION */
#define typec_get_cc1() \
tcpc->typec_remote_cc[0]
#define typec_get_cc2() \
tcpc->typec_remote_cc[1]
#define typec_get_cc_res() \
(tcpc->typec_polarity ? typec_get_cc2() : typec_get_cc1())
#endif /* #ifndef __LINUX_TCPCI_TYPEC_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,334 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef TCPM_PD_H_
#define TCPM_PD_H_
#include "tcpci_config.h"
/* --- PD data message helpers --- */
#define PD_DATA_OBJ_SIZE (7)
#define PDO_MAX_NR (PD_DATA_OBJ_SIZE)
#define VDO_MAX_NR (PD_DATA_OBJ_SIZE-1)
#define VDO_MAX_SVID_NR (VDO_MAX_NR*2)
#define VDO_DISCOVER_ID_IDH 0
#define VDO_DISCOVER_ID_CSTAT 1
#define VDO_DISCOVER_ID_PRODUCT 2
#define VDO_DISCOVER_ID_CABLE 3
#define VDO_DISCOVER_ID_AMA 3
/******************* PD30 *******************/
#ifdef CONFIG_USB_PD_REV30_CHUNKING_BY_PE
#define MAX_EXTENDED_MSG_LEN 260
#else
#define MAX_EXTENDED_MSG_LEN 26
#endif /* CONFIG_USB_PD_REV30_CHUNKING */
/* PD30 Data Message Data Object */
#define PD_BSDO_SIZE 1
#define PD_CCDO_SIZE 1
#define PD_ADO_SIZE 1
/*
* Battery Status Data Object (BSDO)
* ----------
* <31:16> :: Battery Present Capacity (1/10 WH, 0xFFFF = SOC unknown)
* <15:8> :: Battery Info
* <7:0> :: Reserved and Shall be set to zero
*/
#define BSDO(cap, info) (((cap) << 16) | ((info) << 8))
#define BSDO_BAT_INFO(x) ((x>>8) & 0xff)
#define BSDO_BAT_INFO_INVALID_REF (1<<0)
#define BSDO_BAT_INFO_PRESENT (1<<1)
#define BSDO_BAT_INFO_CHARGING ((0<<2) | BSDO_BAT_INFO_PRESENT)
#define BSDO_BAT_INFO_DISCHARGING ((1<<2) | BSDO_BAT_INFO_PRESENT)
#define BSDO_BAT_INFO_IDLE ((2<<2) | BSDO_BAT_INFO_PRESENT)
#define BSDO_BAT_CAP_UNKNOWN (0xffff)
/*
* Country Code Data Object (CCDO)
* ----------
* <31:24> :: First character of the Alpha-2 Country Code
* <23:16> :: Second character of the Alpha-2 Country Code
* <15:0> :: Reserved and Shall be set to zero
*/
#define CCDO(code) ((code) << 16)
#define CCDO_COUNTRY_CODE(x) ((x >> 16) & 0xffff)
#define CCDO_COUNTRY_CODE1(x) ((x >> 24) & 0xff)
#define CCDO_COUNTRY_CODE2(x) ((x >> 16) & 0xff)
/*
* Alert Data Object (ADO)
* ----------
* <31:24> :: Type Of Alert
* <23:20> :: Fixed Batteries (bit field)
* <19:16> :: Hot Swappable Batteries (bit field)
*/
#define ADO_ALERT_BAT_CHANGED (1<<1)
#define ADO_ALERT_OCP (1<<2)
#define ADO_ALERT_OTP (1<<3)
#define ADO_ALERT_OPER_CHANGED (1<<4)
#define ADO_ALERT_SRC_IN_CHANGED (1<<5)
#define ADO_ALERT_OVP (1<<6)
#define ADO_ALERT_TYPE(raw) (raw >> 24)
#define ADO_FIXED_BAT(raw) ((raw >> 20) & 0x0f)
#define ADO_HOT_SWAP_BAT(raw) ((raw >> 16) & 0x0f)
#define ADO_ALERT_TYPE_SET(type) (type << 24)
#define ADO_FIXED_BAT_SET(i) ((i) << 20)
#define ADO_HOT_SWAP_BAT_SET(i) ((i) << 16)
#define ADO(type, fixed, swap) \
(((type) << 24) | ((fixed) << 20) | ((swap) << 16))
#define ADO_GET_STATUS_ONCE_MASK ADO(\
ADO_ALERT_BAT_CHANGED|ADO_ALERT_SRC_IN_CHANGED,\
0xff, 0xff)
/* PD30 Extend Message Data Object */
enum pd_present_temperature_flag {
PD_PTF_NO_SUPPORT = 0,
PD_PTF_NORMAL,
PD_PTF_WARNING,
PD_PTF_OVER_TEMP,
};
enum pd_battery_reference {
PD_BAT_REF_FIXED0 = 0,
PD_BAT_REF_FIXED1,
PD_BAT_REF_FIXED2,
PD_BAT_REF_FIXED3,
PD_BAT_REF_SWAP0 = 4,
PD_BAT_REF_SWAP1,
PD_BAT_REF_SWAP2,
PD_BAT_REF_SWAP3,
PD_BAT_REF_MAX,
/* 8 ~ 255 are reserved and shall not be used */
};
/* SCEDB, Source_Capabilities_Extended */
#define PD_SCEDB_SIZE 24
#define PD_SCEDB_VR(load_step, ioc) \
((load_step) | (ioc << 2))
enum {
PD_SCEDB_VR_LOAD_STEP_150 = 0x00,
PD_SCEDB_VR_LOAD_STEP_500 = 0x01,
};
enum {
PD_SCEDB_VR_IOC_25 = 0x00,
PD_SCEDB_VR_IOC_90 = 0x01,
};
#define PD_SCEDB_COMPLIANCE_LPS (1<<0)
#define PD_SCEDB_COMPLIANCE_PS1 (1<<1)
#define PD_SCEDB_COMPLIANCE_PS2 (1<<2)
#define PD_SCEDB_TC_LOW_TC (1<<0)
#define PD_SCEDB_TC_GROUND (1<<1)
#define PD_SCEDB_TC_GROUND_INTEND (1<<2)
#define PD_SCEDB_TT_IEC_60950 0
#define PD_SCEDB_TT_IEC_62368_TS1 1
#define PD_SCEDB_TT_IEC_62368_TS2 2
#define PD_SCEDB_INPUT_EXT (1<<0)
#define PD_SCEDB_INPUT_EXT_UNCONSTRAINED (1<<1)
#define PD_SCEDB_INPUT_INT (1<<2)
#define PD_SCEDB_BATTERIES(swap_nr, fixed_nr) \
(swap_nr << 4 | fixed_nr)
#define PD_SCEDB_FIX_BAT_NR(raw) (raw & 0xf)
#define PD_SCEDB_SWAP_BAT_NR(raw) ((raw >> 4) & 0xf)
struct pd_source_cap_ext {
uint16_t vid;
uint16_t pid;
uint32_t xid;
uint8_t fw_ver;
uint8_t hw_ver;
uint8_t voltage_regulation;
uint8_t hold_time_ms;
uint8_t compliance; /* bit field */
uint8_t touch_current; /* bit field */
uint16_t peak_current[3];
uint8_t touch_temp;
uint8_t source_inputs; /* bit field */
uint8_t batteries;
uint8_t source_pdp;
};
/* GBSDB, Get_Battery_Status */
#define PD_GBSDB_SIZE 1
struct pd_get_battery_status {
uint8_t bat_status_ref; /* pd_battery_reference */
};
/* GBCDB, Get_Battery_Cap */
#define PD_GBCDB_SIZE 1
struct pd_get_battery_capabilities {
uint8_t bat_cap_ref; /* pd_battery_reference */
};
/* BCDB, Battery_Capabilities */
#define PD_BCDB_SIZE 9
#define PD_BCDB_BAT_CAP_NOT_PRESENT 0x0000
#define PD_BCDB_BAT_CAP_UNKNOWN 0Xffff
#define PD_BCDB_BAT_CAP_RAW(cap_wh) (cap_wh*10)
#define PD_BCDB_BAT_CAP_VAL(raw) (raw/10)
#define PD_BCDB_BAT_TYPE_INVALID (1<<0)
struct pd_battery_capabilities {
uint16_t vid;
uint16_t pid;
uint16_t bat_design_cap;
uint16_t bat_last_full_cap;
uint8_t bat_type;
};
/* GMIDB, Get_Manufacturer_Info */
#define PD_GMIDB_SIZE 2
#define PD_GMIDB_TARGET_PORT 0
#define PD_GMIDB_TARGET_BATTRY 1
struct pd_get_manufacturer_info {
uint8_t info_target;
uint8_t info_ref;
};
/* MIDB, Manufacturer_Info */
#define PD_MIDB_MIN_SIZE 4
#define PD_MIDB_MAX_SIZE 26
#define PD_MIDB_DYNAMIC_SIZE (PD_MIDB_MAX_SIZE-PD_MIDB_MIN_SIZE)
struct pd_manufacturer_info {
uint16_t vid;
uint16_t pid;
uint8_t mfrs_string[PD_MIDB_DYNAMIC_SIZE];
};
/* CCDB, Country_Codes */
#define PD_CCDB_MIN_SIZE 4
#define PD_CCDB_MAX_SIZE MAX_EXTENDED_MSG_LEN
#define PD_CCDB_DYNAMIC_SIZE (PD_CCDB_MAX_SIZE-PD_CCDB_MIN_SIZE)
struct pd_country_codes {
uint16_t length;
uint16_t country_code[1+PD_CCDB_DYNAMIC_SIZE/2];
};
/* CIDB, country_info */
#define PD_CIDB_MIN_SIZE 4
#define PD_CIDB_MAX_SIZE MAX_EXTENDED_MSG_LEN
#define PD_CIDB_DYNAMIC_SIZE (PD_CIDB_MAX_SIZE-PD_CIDB_MIN_SIZE)
struct pd_country_info {
uint16_t country_code;
uint16_t reserved;
uint8_t country_special_data[PD_CIDB_DYNAMIC_SIZE];
};
/* SDB, Status */
#define PD_SDB_SIZE 6
#define PD_STATUS_INPUT_EXT_POWER (1<<1)
#define PD_STATUS_INPUT_EXT_POWER_FROM_AC (1<<2)
#define PD_STATUS_INPUT_INT_POWER_BAT (1<<3)
#define PD_STATUS_INPUT_INT_POWER_NOT_BAT (1<<4)
#define PD_STASUS_EVENT_OCP (1<<1)
#define PD_STATUS_EVENT_OTP (1<<2)
#define PD_STATUS_EVENT_OVP (1<<3)
#define PD_STATUS_EVENT_CF_MODE (1<<4)
#define PD_STASUS_EVENT_READ_CLEAR (\
PD_STASUS_EVENT_OCP|PD_STATUS_EVENT_OTP|PD_STATUS_EVENT_OVP)
#define PD_STASUS_EVENT_MASK (\
PD_STASUS_EVENT_OCP|PD_STATUS_EVENT_OTP|PD_STATUS_EVENT_OVP|\
PD_STATUS_EVENT_CF_MODE)
#define PD_STATUS_TEMP_PTF(raw) ((raw & 0x06) >> 1)
#define PD_STATUS_TEMP_SET_PTF(val) ((val & 0x03) << 1)
struct pd_status {
uint8_t internal_temp; /* 0 means no support */
uint8_t present_input; /* bit filed */
uint8_t present_battey_input; /* bit filed */
uint8_t event_flags; /* bit filed */
uint8_t temp_status; /* bit filed */
uint8_t power_status; /* bit filed */
};
/* PPSSDB, PPSStatus */
#define PD_PPSSDB_SIZE 4 /* PPS_Status */
#define PD_PPS_GET_OUTPUT_MV(raw) (raw*20)
#define PD_PPS_GET_OUTPUT_MA(raw) (raw*50)
#define PD_PPS_SET_OUTPUT_MV(mv) (((mv) / 20) & 0xFFFF)
#define PD_PPS_SET_OUTPUT_MA(ma) (((ma) / 50) & 0xFF)
#define PD_PPS_FLAGS_CFF (1 << 3)
#define PD_PPS_FLGAS_PTF(raw) ((raw & 0x06) >> 1)
#define PD_PPS_FLAGS_SET_PTF(flags) ((flags & 0x03) << 1)
struct pd_pps_status_raw {
uint16_t output_vol_raw; /* 0xffff means no support */
uint8_t output_curr_raw; /* 0xff means no support */
uint8_t real_time_flags;
};
struct pd_pps_status {
int output_mv;
int output_ma;
uint8_t real_time_flags;
};
#endif /* TCPM_PD_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,165 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include "inc/pd_dbg_info.h"
#ifdef CONFIG_PD_DBG_INFO
#define PD_INFO_BUF_SIZE (2048*256)
#define MSG_POLLING_MS 20
#define OUT_BUF_MAX (128)
static struct {
int used;
char buf[PD_INFO_BUF_SIZE + 1 + OUT_BUF_MAX];
} pd_dbg_buffer[2];
static struct mutex buff_lock;
static unsigned int using_buf;
static wait_queue_head_t print_out_wait_que;
static atomic_t pending_print_out;
static atomic_t busy = ATOMIC_INIT(0);
void pd_dbg_info_lock(void)
{
atomic_inc(&busy);
}
void pd_dbg_info_unlock(void)
{
atomic_dec_if_positive(&busy);
}
static inline bool pd_dbg_print_out(void)
{
char temp;
int used;
unsigned int index, i;
mutex_lock(&buff_lock);
index = using_buf;
using_buf ^= 0x01; /* exchange buffer */
mutex_unlock(&buff_lock);
used = pd_dbg_buffer[index].used;
if (used == 0)
return false;
pd_dbg_buffer[index].buf[used] = '\0';
pr_info("///PD dbg info %ud\n", used);
for (i = 0; i < used; i += OUT_BUF_MAX) {
temp = pd_dbg_buffer[index].buf[OUT_BUF_MAX + i];
pd_dbg_buffer[index].buf[OUT_BUF_MAX + i] = '\0';
while (atomic_read(&busy))
usleep_range(1000, 2000);
pr_notice("%s", pd_dbg_buffer[index].buf + i);
pd_dbg_buffer[index].buf[OUT_BUF_MAX + i] = temp;
}
/* pr_info("PD dbg info///\n"); */
pd_dbg_buffer[index].used = 0;
msleep(MSG_POLLING_MS);
return true;
}
static int print_out_thread_fn(void *data)
{
while (true) {
wait_event(print_out_wait_que,
atomic_read(&pending_print_out) ||
kthread_should_stop());
if (kthread_should_stop())
break;
do {
atomic_dec_if_positive(&pending_print_out);
} while (pd_dbg_print_out() && !kthread_should_stop());
}
return 0;
}
int pd_dbg_info(const char *fmt, ...)
{
unsigned int index;
va_list args;
int r;
int used;
u64 ts;
unsigned long rem_usec;
ts = local_clock();
rem_usec = do_div(ts, 1000000000) / 1000 / 1000;
va_start(args, fmt);
mutex_lock(&buff_lock);
index = using_buf;
used = pd_dbg_buffer[index].used;
r = snprintf(pd_dbg_buffer[index].buf + used,
PD_INFO_BUF_SIZE - used, "<%5lu.%03lu>",
(unsigned long)ts, rem_usec);
if (r > 0)
used += r;
r = vsnprintf(pd_dbg_buffer[index].buf + used,
PD_INFO_BUF_SIZE - used, fmt, args);
if (r > 0)
used += r;
if (pd_dbg_buffer[index].used == 0) {
atomic_inc(&pending_print_out);
wake_up(&print_out_wait_que);
}
pd_dbg_buffer[index].used = used;
mutex_unlock(&buff_lock);
va_end(args);
return r;
}
EXPORT_SYMBOL(pd_dbg_info);
static struct task_struct *print_out_task;
int pd_dbg_info_init(void)
{
pr_info("%s\n", __func__);
mutex_init(&buff_lock);
init_waitqueue_head(&print_out_wait_que);
atomic_set(&pending_print_out, 0);
print_out_task = kthread_run(print_out_thread_fn, NULL, "pd_dbg_info");
return 0;
}
void pd_dbg_info_exit(void)
{
kthread_stop(print_out_task);
mutex_destroy(&buff_lock);
}
MODULE_DESCRIPTION("PD Debug Info Module");
MODULE_AUTHOR("Patrick Chang <patrick_chang@richtek.com>");
MODULE_LICENSE("GPL");
#endif /* CONFIG_PD_DBG_INFO */

View file

@ -0,0 +1,603 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* PD Device Policy Manager for Direct Charge
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/random.h>
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
#include "inc/pd_dpm_core.h"
#include "pd_dpm_prv.h"
#ifdef CONFIG_USB_POWER_DELIVERY
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
#define RTDC_UVDM_EN_UNLOCK 0x2024
#define RTDC_UVDM_RECV_EN_UNLOCK 0x4024
#define RTDC_SVDM_PPS_AUTHORIZATION 0x10
#define RTDC_VALID_MODE 0x01
#define RTDC_UVDM_EN_UNLOCK_SUCCESS 0x01
void crcbits(uint32_t data, uint32_t *crc, uint32_t *ppolynomial)
{
uint32_t i, newbit, newword, rl_crc;
for (i = 0; i < 32; i++) {
newbit = ((*crc >> 31) ^ ((data >> i) & 1)) & 1;
if (newbit)
newword = *ppolynomial;
else
newword = 0;
rl_crc = (*crc << 1) | newbit;
*crc = rl_crc ^ newword;
}
}
uint32_t crcwrap(uint32_t V)
{
uint32_t ret = 0, i, j, bit;
V = ~V;
for (i = 0; i < 32; i++) {
j = 31 - i;
bit = (V >> i) & 1;
ret |= bit << j;
}
return ret;
}
static uint32_t dc_get_random_code(void)
{
uint32_t num;
get_random_bytes(&num, sizeof(num));
return num;
}
static uint32_t dc_get_authorization_code(uint32_t data)
{
uint32_t dwpolynomial = 0x04C11DB6, dwCrc = 0xFFFFFFFF;
crcbits(data, &dwCrc, &dwpolynomial);
dwCrc = crcwrap(dwCrc);
return dwCrc;
}
static inline bool dc_dfp_send_en_unlock(struct pd_port *pd_port,
uint32_t cmd, uint32_t data0, uint32_t data1)
{
pd_port->uvdm_cnt = 3;
pd_port->uvdm_wait_resp = true;
pd_port->uvdm_data[0] = PD_UVDM_HDR(USB_VID_DIRECTCHARGE, cmd);
pd_port->uvdm_data[1] = data0;
pd_port->uvdm_data[2] = data1;
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
if (pd_port->pe_data.dc_pps_mode) {
pd_port->uvdm_data[0] = VDO_S(
USB_VID_DIRECTCHARGE,
SVDM_REV20,
CMDT_INIT,
RTDC_SVDM_PPS_AUTHORIZATION,
0);
}
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
return pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_UVDM);
}
enum pd_dc_dfp_state {
DC_DFP_NONE = 0,
DC_DFP_DISCOVER_ID,
DC_DFP_DISCOVER_SVIDS,
DC_DFP_DISCOVER_MODES,
DC_DFP_ENTER_MODE,
DC_DFP_EN_UNLOCK1,
DC_DFP_EN_UNLOCK2,
DC_DFP_OPERATION,
#ifdef RTDC_TA_EMULATE
DC_UFP_T0,
DC_UFP_T1,
DC_UFP_T2,
#endif
DC_DFP_STATE_NR,
DC_DFP_ERR = 0X10,
DC_DFP_ERR_DISCOVER_ID_TYPE,
DC_DFP_ERR_DISCOVER_ID_NAK_TIMEOUT,
DC_DFP_ERR_DISCOVER_SVID_DC_SID,
DC_DFP_ERR_DISCOVER_SVID_NAK_TIMEOUT,
DC_DFP_ERR_DISCOVER_CABLE,
DC_DFP_ERR_DISCOVER_MODE_DC_SID,
DC_DFP_ERR_DISCOVER_MODE_CAP,
DC_DFP_ERR_DISCOVER_MODE_NAK_TIMEROUT,
DC_DFP_ERR_ENTER_MODE_DC_SID,
DC_DFP_ERR_ENTER_MODE_NAK_TIMEOUT,
DC_DFP_ERR_EXIT_MODE_DC_SID,
DC_DFP_ERR_EXIT_MODE_NAK_TIMEOUT,
DC_DFP_ERR_EN_UNLOCK1_FAILED,
DC_DFP_ERR_EN_UNLOCK1_NAK_TIMEOUT,
DC_DFP_ERR_EN_UNLOCK2_FAILED,
DC_DFP_ERR_EN_UNLOCK2_NAK_TIMEOUT,
DC_DFP_ERR_PD_REV30,
};
#if DC_DBG_ENABLE
static const char * const dc_dfp_state_name[] = {
"dc_dfp_none",
"dc_dfp_discover_id",
"dc_dfp_discover_svids",
"dc_dfp_discover_modes",
"dc_dfp_enter_mode",
"dc_dfp_en_unlock1",
"dc_dfp_en_unlock2",
"dc_dfp_operation",
#ifdef RTDC_TA_EMULATE
"dc1",
"dc2",
"dc3",
#endif
};
#endif /* DC_DBG_ENABLE */
void dc_dfp_set_state(struct pd_port *pd_port, uint8_t state)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
pd_port->dc_dfp_state = state;
if (pd_port->dc_dfp_state < DC_DFP_STATE_NR)
DC_DBG("%s\n", dc_dfp_state_name[state]);
else
DC_DBG("dc_dfp_stop (%d)\n", state);
}
bool dc_dfp_start_en_unlock1(struct pd_port *pd_port)
{
uint32_t rn_code[2];
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
rn_code[0] = dc_get_random_code();
rn_code[1] = dc_get_random_code();
pd_port->dc_pass_code = dc_get_authorization_code(
(rn_code[0] & 0xffff) | (rn_code[1] & 0xffff0000));
DC_DBG("en_unlock1: 0x%x, 0x%x\n", rn_code[0], rn_code[1]);
dc_dfp_send_en_unlock(
pd_port, RTDC_UVDM_EN_UNLOCK, rn_code[0], rn_code[1]);
dc_dfp_set_state(pd_port, DC_DFP_EN_UNLOCK1);
return true;
}
#define SVDM_CMD_STATE_MASK(raw) (raw & (0x80df))
#define SVDM_CMD_STATE(cmd, cmd_type) \
((1 << 15) | (cmd & 0x1f) | ((cmd_type & 0x03) << 6))
bool dc_dfp_verify_en_unlock1(struct pd_port *pd_port)
{
uint32_t resp_cmd, expect_resp;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
expect_resp = RTDC_UVDM_RECV_EN_UNLOCK;
resp_cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
if (pd_port->pe_data.dc_pps_mode) {
resp_cmd = SVDM_CMD_STATE_MASK(pd_port->uvdm_data[0]);
expect_resp = SVDM_CMD_STATE(
RTDC_SVDM_PPS_AUTHORIZATION, CMDT_RSP_ACK);
}
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
if (resp_cmd != expect_resp) {
DC_INFO("en_unlock1: unexpect resp (0x%x)\n", resp_cmd);
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK1_FAILED);
return false;
}
if (pd_port->dc_pass_code != pd_port->uvdm_data[1]) {
DC_INFO("en_unlock1: pass wrong 0x%x 0x%x\n",
pd_port->dc_pass_code, pd_port->uvdm_data[1]);
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK1_FAILED);
return false;
}
return true;
}
bool dc_dfp_start_en_unlock2(struct pd_port *pd_port)
{
uint32_t rn_code = dc_get_random_code();
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
pd_port->dc_pass_code =
dc_get_authorization_code(pd_port->uvdm_data[2]);
DC_DBG("en_unlock2: 0x%x, 0x%x\n", pd_port->dc_pass_code, rn_code);
dc_dfp_send_en_unlock(pd_port, RTDC_UVDM_EN_UNLOCK,
pd_port->dc_pass_code, rn_code);
dc_dfp_set_state(pd_port, DC_DFP_EN_UNLOCK2);
return true;
}
bool dc_dfp_verify_en_unlock2(struct pd_port *pd_port)
{
uint32_t resp_cmd, expect_resp;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
expect_resp = RTDC_UVDM_RECV_EN_UNLOCK;
resp_cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
if (pd_port->pe_data.dc_pps_mode) {
resp_cmd = SVDM_CMD_STATE_MASK(pd_port->uvdm_data[0]);
expect_resp = SVDM_CMD_STATE(
RTDC_SVDM_PPS_AUTHORIZATION, CMDT_RSP_ACK);
}
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
if (resp_cmd != expect_resp) {
DC_INFO("en_unlock2: unexpect resp (0x%x)\n", resp_cmd);
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK2_FAILED);
return false;
}
if (pd_port->uvdm_data[1] != RTDC_UVDM_EN_UNLOCK_SUCCESS) {
DC_INFO("en_unlock2: failed\n");
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK2_FAILED);
return false;
}
return true;
}
bool dc_dfp_notify_pe_startup(
struct pd_port *pd_port, struct svdm_svid_data *svid_data)
{
if (!(pd_port->id_vdos[0] & PD_IDH_MODAL_SUPPORT))
return false;
if (pd_port->dpm_caps & DPM_CAP_ATTEMP_ENTER_DC_MODE)
dc_dfp_set_state(pd_port, DC_DFP_DISCOVER_ID);
#ifdef RTDC_TA_EMULATE
dc_dfp_set_state(pd_port, DC_UFP_T0);
#endif
return true;
}
int dc_dfp_notify_pe_ready(struct pd_port *pd_port,
struct svdm_svid_data *svid_data)
{
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
#ifdef RTDC_TA_EMULATE
if (pd_port->data_role == PD_ROLE_DFP && svid_data->exist) {
pd_put_tcp_pd_event(pd_port, TCP_DPM_EVT_DR_SWAP_AS_UFP);
return 1;
} else {
return 0;
}
#endif
if (pd_port->data_role != PD_ROLE_DFP)
return 0;
if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_MODES)
return 0;
#ifdef CONFIG_USB_PD_RTDC_CHECK_CABLE
if (!pd_port->pe_data.power_cable_present) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_CABLE);
return 0;
}
if (pd_get_cable_curr_lvl(pd_port) != CABLE_CURR_5A) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_CABLE);
return 0;
}
#endif /* CONFIG_USB_PD_RTDC_CHECK_CABLE */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
/* If TA support pd revision30, using standard PPS flow */
if (pd_check_rev30(pd_port)) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_PD_REV30);
return 0;
}
if (pd_is_source_support_apdo(pd_port)) {
DC_INFO("pps_mode\n");
pd_port->pe_data.dc_pps_mode = true;
return dc_dfp_start_en_unlock1(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
pd_port->mode_svid = USB_VID_DIRECTCHARGE;
pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_DISCOVER_MODES);
return 1;
}
bool dc_dfp_notify_discover_id(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack)
{
if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_ID)
return true;
if (!ack) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_ID_NAK_TIMEOUT);
return true;
}
/* TODO: Check device ID or Type */
dc_dfp_set_state(pd_port, DC_DFP_DISCOVER_SVIDS);
return true;
}
bool dc_dfp_notify_discover_svid(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack)
{
if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_SVIDS)
return false;
if (!ack) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_SVID_NAK_TIMEOUT);
return false;
}
if (!svid_data->exist) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_SVID_DC_SID);
return false;
}
dpm_reaction_set(pd_port, DPM_REACTION_DISCOVER_CABLE_FLOW);
dc_dfp_set_state(pd_port, DC_DFP_DISCOVER_MODES);
return true;
}
bool dc_dfp_notify_discover_modes(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack)
{
if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_MODES)
return false;
if (!ack) {
dc_dfp_set_state(pd_port,
DC_DFP_ERR_DISCOVER_MODE_NAK_TIMEROUT);
return false;
}
if (svid_data->remote_mode.mode_cnt == 0) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_MODE_DC_SID);
return false;
}
if (svid_data->remote_mode.mode_vdo[0] != RTDC_VALID_MODE) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_MODE_CAP);
return false;
}
pd_port->mode_obj_pos = 1;
dc_dfp_set_state(pd_port, DC_DFP_ENTER_MODE);
pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_ENTER_MODE);
return true;
}
bool dc_dfp_notify_enter_mode(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, uint8_t ops, bool ack)
{
if (pd_port->dc_dfp_state != DC_DFP_ENTER_MODE)
return true;
if (!ack) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_ENTER_MODE_NAK_TIMEOUT);
return true;
}
if (svid_data->active_mode == 0) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_ENTER_MODE_DC_SID);
return false;
}
return dc_dfp_start_en_unlock1(pd_port);
}
bool dc_dfp_notify_exit_mode(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, uint8_t ops)
{
if (pd_port->dc_dfp_state <= DC_DFP_ENTER_MODE)
return false;
if (svid_data->svid != USB_VID_DIRECTCHARGE)
return false;
dc_dfp_set_state(pd_port, DC_DFP_NONE);
return true;
}
static inline bool dc_dfp_notify_en_unlock1(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack)
{
if (pd_port->dc_dfp_state != DC_DFP_EN_UNLOCK1)
return false;
if (!ack) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK1_NAK_TIMEOUT);
return false;
}
if (!dc_dfp_verify_en_unlock1(pd_port))
return false;
return dc_dfp_start_en_unlock2(pd_port);
}
static inline bool dc_dfp_notify_en_unlock2(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack)
{
if (pd_port->dc_dfp_state != DC_DFP_EN_UNLOCK2)
return false;
if (!ack) {
dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK2_NAK_TIMEOUT);
return false;
}
if (!dc_dfp_verify_en_unlock2(pd_port))
return false;
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
#ifdef CONFIG_USB_PD_REV30_SYNC_SPEC_REV
if (pd_port->pe_data.dc_pps_mode)
pd_port->pd_revision[0] = PD_REV30;
#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
dc_dfp_set_state(pd_port, DC_DFP_OPERATION);
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
/* PPS shoult not use en_unlock to notify system */
if (pd_port->pe_data.dc_pps_mode)
return true;
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
tcpci_dc_notify_en_unlock(pd_port->tcpc);
return true;
}
bool dc_dfp_notify_uvdm(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack)
{
switch (pd_port->dc_dfp_state) {
case DC_DFP_EN_UNLOCK1:
dc_dfp_notify_en_unlock1(pd_port, svid_data, ack);
break;
case DC_DFP_EN_UNLOCK2:
dc_dfp_notify_en_unlock2(pd_port, svid_data, ack);
break;
}
return true;
}
bool dc_ufp_notify_uvdm(struct pd_port *pd_port,
struct svdm_svid_data *svid_data)
{
#ifdef RTDC_TA_EMULATE
uint32_t reply_cmd[3];
uint32_t recv_code[2], rn_code, pass_code;
reply_cmd[0] = PD_UVDM_HDR(0x29cf, RTDC_UVDM_EN_UNLOCK);
uint32_t cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
if (cmd != RTDC_UVDM_EN_UNLOCK) {
DC_INFO("What!?");
return true;
}
switch (pd_port->dc_dfp_state) {
case DC_UFP_T0:
{
recv_code[0] = pd_port->uvdm_data[1];
recv_code[1] = pd_port->uvdm_data[2];
DC_INFO("T0: recv_code: 0x%x, 0x%x\n",
recv_code[0], recv_code[1]);
pass_code = dc_get_authorization_code(
(recv_code[0] & 0xffff) |
(recv_code[1] & 0xffff0000));
rn_code = dc_get_random_code();
pd_port->dc_pass_code =
dc_get_authorization_code(rn_code);
DC_INFO("T0: reply: 0x%x, 0x%x\n",
rn_code, pd_port->dc_pass_code);
reply_cmd[1] = pass_code;
reply_cmd[2] = rn_code;
pd_reply_custom_vdm(pd_port, TCPC_TX_SOP, 3, reply_cmd);
dc_dfp_set_state(pd_port, DC_UFP_T1);
}
break;
case DC_UFP_T1:
{
recv_code[0] = pd_port->uvdm_data[1];
recv_code[1] = pd_port->uvdm_data[2];
DC_INFO("T1: recv_code: 0x%x, 0x%x\n",
recv_code[0], recv_code[1]);
if (recv_code[0] != pd_port->dc_pass_code) {
DC_INFO("T1: pass_code error\n");
reply_cmd[1] = 0;
} else {
DC_INFO("T1: pass_code success\n");
reply_cmd[1] = 1;
}
reply_cmd[2] = dc_get_random_code();
pd_reply_custom_vdm(pd_port, TCPC_TX_SOP, 3, reply_cmd);
dc_dfp_set_state(pd_port, DC_UFP_T2);
}
break;
}
#endif /* RTDC_TA_EMULATE */
return true;
}
bool dc_reset_state(struct pd_port *pd_port, struct svdm_svid_data *svid_data)
{
dc_dfp_set_state(pd_port, DC_DFP_NONE);
return true;
}
bool dc_parse_svid_data(struct pd_port *pd_port,
struct svdm_svid_data *svid_data)
{
svid_data->local_mode.mode_cnt = 1;
svid_data->local_mode.mode_vdo[0] = 0x00;
pd_port->dpm_caps |= DPM_CAP_ATTEMP_ENTER_DC_MODE;
return true;
}
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
#endif /* CONFIG_USB_POWER_DELIVERY */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,389 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Core Driver
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_dpm_pdo_select.h"
#ifdef CONFIG_USB_POWER_DELIVERY
struct dpm_select_info_t {
uint8_t pos;
int max_uw;
int cur_mv;
uint8_t policy;
};
static inline void dpm_extract_apdo_info(
uint32_t pdo, struct dpm_pdo_info_t *info)
{
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
switch (APDO_TYPE(pdo)) {
case APDO_TYPE_PPS:
info->apdo_type = DPM_APDO_TYPE_PPS;
if (pdo & APDO_PPS_CURR_FOLDBACK)
info->apdo_type |= DPM_APDO_TYPE_PPS_CF;
info->pwr_limit = APDO_PPS_EXTRACT_PWR_LIMIT(pdo);
info->ma = APDO_PPS_EXTRACT_CURR(pdo);
info->vmin = APDO_PPS_EXTRACT_MIN_VOLT(pdo);
info->vmax = APDO_PPS_EXTRACT_MAX_VOLT(pdo);
info->uw = info->ma * info->vmax;
return;
}
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
info->type = TCPM_POWER_CAP_VAL_TYPE_UNKNOWN;
}
void dpm_extract_pdo_info(
uint32_t pdo, struct dpm_pdo_info_t *info)
{
memset(info, 0, sizeof(struct dpm_pdo_info_t));
info->type = PDO_TYPE_VAL(pdo);
switch (PDO_TYPE(pdo)) {
case PDO_TYPE_FIXED:
info->ma = PDO_FIXED_EXTRACT_CURR(pdo);
info->vmax = info->vmin = PDO_FIXED_EXTRACT_VOLT(pdo);
info->uw = info->ma * info->vmax;
break;
case PDO_TYPE_VARIABLE:
info->ma = PDO_VAR_EXTRACT_CURR(pdo);
info->vmin = PDO_VAR_EXTRACT_MIN_VOLT(pdo);
info->vmax = PDO_VAR_EXTRACT_MAX_VOLT(pdo);
info->uw = info->ma * info->vmax;
break;
case PDO_TYPE_BATTERY:
info->uw = PDO_BATT_EXTRACT_OP_POWER(pdo) * 1000;
info->vmin = PDO_BATT_EXTRACT_MIN_VOLT(pdo);
info->vmax = PDO_BATT_EXTRACT_MAX_VOLT(pdo);
info->ma = info->uw / info->vmin;
break;
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
case PDO_TYPE_APDO:
dpm_extract_apdo_info(pdo, info);
break;
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
}
}
#ifndef MIN
#define MIN(a, b) ((a < b) ? (a) : (b))
#endif
static inline int dpm_calc_src_cap_power_uw(
struct dpm_pdo_info_t *source, struct dpm_pdo_info_t *sink)
{
int uw, ma;
if (source->type == DPM_PDO_TYPE_BAT) {
uw = source->uw;
if (sink->type == DPM_PDO_TYPE_BAT)
uw = MIN(uw, sink->uw);
} else {
ma = source->ma;
if (sink->type != DPM_PDO_TYPE_BAT)
ma = MIN(ma, sink->ma);
uw = ma * source->vmax;
}
return uw;
}
/*
* Select PDO from VSafe5V
*/
static bool dpm_select_pdo_from_vsafe5v(
struct dpm_select_info_t *select_info,
struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source)
{
int uw;
if ((sink->vmax != TCPC_VBUS_SINK_5V) ||
(sink->vmin != TCPC_VBUS_SINK_5V) ||
(source->vmax != TCPC_VBUS_SINK_5V) ||
(source->vmin != TCPC_VBUS_SINK_5V))
return false;
uw = dpm_calc_src_cap_power_uw(source, sink);
if (uw > select_info->max_uw) {
select_info->max_uw = uw;
select_info->cur_mv = source->vmax;
return true;
}
return false;
}
/*
* Select PDO from Direct Charge
*/
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
static bool dpm_select_pdo_from_direct_charge(
struct dpm_select_info_t *select_info,
struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source)
{
int uw;
if (sink->type != DPM_PDO_TYPE_VAR
|| source->type != DPM_PDO_TYPE_VAR)
return false;
if (source->vmin >= TCPC_VBUS_SINK_5V)
return false;
if (sink->vmax < source->vmax)
return false;
if (sink->vmin > source->vmin)
return false;
uw = dpm_calc_src_cap_power_uw(source, sink);
if (uw > select_info->max_uw) {
select_info->max_uw = uw;
select_info->cur_mv = source->vmax;
return true;
}
return false;
}
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
/*
* Select PDO from Custom
*/
static bool dpm_select_pdo_from_custom(
struct dpm_select_info_t *select_info,
struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source)
{
/* TODO */
return dpm_select_pdo_from_vsafe5v(select_info, sink, source);
}
/*
* Select PDO from Max Power
*/
static inline bool dpm_is_valid_pdo_pair(struct dpm_pdo_info_t *sink,
struct dpm_pdo_info_t *source, uint32_t policy)
{
if (sink->vmax < source->vmax)
return false;
if (sink->vmin > source->vmin)
return false;
if (policy & DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR)
return true;
return sink->ma <= source->ma;
}
static bool dpm_select_pdo_from_max_power(
struct dpm_select_info_t *select_info,
struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source)
{
bool overload;
int uw;
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
/* Variable for direct charge only */
if ((sink->type == DPM_PDO_TYPE_VAR) && (sink->vmin < 5000))
return false;
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
#ifdef CONFIG_USB_PD_REV30
if (sink->type == DPM_PDO_TYPE_APDO)
return false;
#endif /* CONFIG_USB_PD_REV30 */
if (!dpm_is_valid_pdo_pair(sink, source, select_info->policy))
return false;
uw = dpm_calc_src_cap_power_uw(source, sink);
overload = uw > select_info->max_uw;
if ((!overload) && (uw == select_info->max_uw)) {
if (select_info->policy &
DPM_CHARGING_POLICY_PREFER_LOW_VOLTAGE)
overload = (source->vmax < select_info->cur_mv);
else if (select_info->policy &
DPM_CHARGING_POLICY_PREFER_HIGH_VOLTAGE)
overload = (source->vmax > select_info->cur_mv);
}
if (overload) {
select_info->max_uw = uw;
select_info->cur_mv = source->vmax;
return true;
}
return false;
}
/*
* Select PDO from PPS
*/
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
static bool dpm_select_pdo_from_pps(
struct dpm_select_info_t *select_info,
struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source)
{
bool overload;
int uw, diff_mv;
const int tolerance = 300; /* 5900 * 5% */
if (sink->type != DPM_PDO_TYPE_APDO ||
source->type != DPM_PDO_TYPE_APDO)
return false;
if (!(source->apdo_type & DPM_APDO_TYPE_PPS))
return false;
if (sink->vmax > source->vmax)
return false;
if (sink->vmin < source->vmin)
return false;
if (!(select_info->policy & DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR)) {
if (source->ma < sink->ma)
return false;
}
uw = sink->vmax * source->ma;
diff_mv = source->vmax - sink->vmax;
if (uw > select_info->max_uw)
overload = true;
else if (uw < select_info->max_uw)
overload = false;
else if ((select_info->cur_mv < tolerance) && (diff_mv > tolerance))
overload = true;
else if (diff_mv < select_info->cur_mv)
overload = true;
else
overload = false;
if (overload) {
select_info->max_uw = uw;
select_info->cur_mv = diff_mv;
return true;
}
return false;
}
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
/*
* Select PDO from defined rule ...
*/
typedef bool (*dpm_select_pdo_fun)(
struct dpm_select_info_t *select_info,
struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source);
bool dpm_find_match_req_info(struct dpm_rdo_info_t *req_info,
struct dpm_pdo_info_t *sink, int cnt, uint32_t *src_pdos,
int min_uw, uint32_t policy)
{
int i;
struct dpm_select_info_t select;
struct dpm_pdo_info_t source;
dpm_select_pdo_fun select_pdo_fun;
select.pos = 0;
select.cur_mv = 0;
select.max_uw = min_uw;
select.policy = policy;
switch (policy & DPM_CHARGING_POLICY_MASK) {
case DPM_CHARGING_POLICY_MAX_POWER:
select_pdo_fun = dpm_select_pdo_from_max_power;
break;
case DPM_CHARGING_POLICY_CUSTOM:
select_pdo_fun = dpm_select_pdo_from_custom;
break;
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
case DPM_CHARGING_POLICY_DIRECT_CHARGE:
select_pdo_fun = dpm_select_pdo_from_direct_charge;
break;
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
case DPM_CHARGING_POLICY_PPS:
select_pdo_fun = dpm_select_pdo_from_pps;
break;
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
default: /* DPM_CHARGING_POLICY_VSAFE5V */
select_pdo_fun = dpm_select_pdo_from_vsafe5v;
break;
}
for (i = 0; i < cnt; i++) {
dpm_extract_pdo_info(src_pdos[i], &source);
if (select_pdo_fun(&select, sink, &source))
select.pos = i+1;
}
if (select.pos > 0) {
dpm_extract_pdo_info(src_pdos[select.pos-1], &source);
req_info->pos = select.pos;
req_info->type = source.type;
req_info->vmax = source.vmax;
req_info->vmin = source.vmin;
if (sink->type == DPM_PDO_TYPE_BAT)
req_info->mismatch = select.max_uw < sink->uw;
else
req_info->mismatch = source.ma < sink->ma;
if (source.type == DPM_PDO_TYPE_BAT) {
req_info->max_uw = sink->uw;
req_info->oper_uw = select.max_uw;
} else {
req_info->max_ma = sink->ma;
req_info->oper_ma = MIN(sink->ma, source.ma);
}
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
if (source.type == DPM_PDO_TYPE_APDO) {
req_info->vmax = sink->vmax;
req_info->vmin = sink->vmin;
}
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
return true;
}
return false;
}
#endif /* CONFIG_USB_POWER_DELIVERY */

View file

@ -0,0 +1,290 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef PD_DPM_PRV_H_INCLUDED
#define PD_DPM_PRV_H_INCLUDED
#include <linux/of.h>
#include <linux/device.h>
#define SVID_DATA_LOCAL_MODE(svid_data, n) \
(svid_data->local_mode.mode_vdo[n])
#define SVID_DATA_REMOTE_MODE(svid_data, n) \
(svid_data->remote_mode.mode_vdo[n])
#define SVID_DATA_DFP_GET_ACTIVE_MODE(svid_data)\
SVID_DATA_REMOTE_MODE(svid_data, svid_data->active_mode-1)
#define SVID_DATA_UFP_GET_ACTIVE_MODE(svid_data)\
SVID_DATA_LOCAL_MODE(svid_data, svid_data->active_mode-1)
extern int dpm_check_supported_modes(void);
struct svdm_svid_ops {
const char *name;
uint16_t svid;
bool (*dfp_inform_id)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
bool (*dfp_inform_svids)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
bool (*dfp_inform_modes)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
bool (*dfp_inform_enter_mode)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, uint8_t ops, bool ack);
bool (*dfp_inform_exit_mode)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, uint8_t ops);
bool (*dfp_inform_attention)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
void (*ufp_request_enter_mode)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, uint8_t ops);
void (*ufp_request_exit_mode)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, uint8_t ops);
bool (*notify_pe_startup)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
int (*notify_pe_ready)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
bool (*notify_pe_shutdown)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
#ifdef CONFIG_USB_PD_CUSTOM_VDM
bool (*dfp_notify_uvdm)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack);
bool (*ufp_notify_uvdm)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
#endif
bool (*reset_state)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
bool (*parse_svid_data)(struct pd_port *pd_port,
struct svdm_svid_data *svid_data);
};
static inline bool dpm_check_data_msg_event(
struct pd_port *pd_port, uint8_t msg)
{
return pd_event_data_msg_match(
pd_get_curr_pd_event(pd_port), msg);
}
#ifdef CONFIG_USB_PD_REV30
static inline bool dpm_check_ext_msg_event(
struct pd_port *pd_port, uint8_t msg)
{
return pd_event_ext_msg_match(
pd_get_curr_pd_event(pd_port), msg);
}
#endif /* CONFIG_USB_PD_REV30 */
static inline uint8_t dpm_vdm_get_ops(struct pd_port *pd_port)
{
return pd_port->curr_vdm_ops;
}
static inline uint16_t dpm_vdm_get_svid(struct pd_port *pd_port)
{
return pd_port->curr_vdm_svid;
}
static inline int dpm_vdm_reply_svdm_request(
struct pd_port *pd_port, bool ack)
{
return pd_reply_svdm_request_simply(
pd_port, ack ? CMDT_RSP_ACK : CMDT_RSP_NAK);
}
static inline int dpm_vdm_reply_svdm_nak(struct pd_port *pd_port)
{
return pd_reply_svdm_request_simply(pd_port, CMDT_RSP_NAK);
}
enum {
GOOD_PW_NONE = 0, /* both no GP */
GOOD_PW_PARTNER, /* partner has GP */
GOOD_PW_LOCAL, /* local has GP */
GOOD_PW_BOTH, /* both have GPs */
};
int dpm_check_good_power(struct pd_port *pd_port);
/* SVDM */
extern struct svdm_svid_data *
dpm_get_svdm_svid_data(struct pd_port *pd_port, uint16_t svid);
extern bool svdm_reset_state(struct pd_port *pd_port);
extern bool svdm_notify_pe_startup(struct pd_port *pd_port);
static inline int svdm_notify_pe_ready(struct pd_port *pd_port)
{
int i, ret;
struct svdm_svid_data *svid_data;
for (i = 0; i < pd_port->svid_data_cnt; i++) {
svid_data = &pd_port->svid_data[i];
if (svid_data->ops && svid_data->ops->notify_pe_ready) {
ret = svid_data->ops->notify_pe_ready(
pd_port, svid_data);
if (ret != 0)
return ret;
}
}
return 0;
}
static inline bool svdm_notify_pe_shutdown(
struct pd_port *pd_port)
{
int i;
struct svdm_svid_data *svid_data;
for (i = 0; i < pd_port->svid_data_cnt; i++) {
svid_data = &pd_port->svid_data[i];
if (svid_data->ops && svid_data->ops->notify_pe_shutdown) {
svid_data->ops->notify_pe_shutdown(
pd_port, svid_data);
}
}
return 0;
}
static inline bool svdm_dfp_inform_id(struct pd_port *pd_port, bool ack)
{
int i;
struct svdm_svid_data *svid_data;
for (i = 0; i < pd_port->svid_data_cnt; i++) {
svid_data = &pd_port->svid_data[i];
if (svid_data->ops && svid_data->ops->dfp_inform_id)
svid_data->ops->dfp_inform_id(pd_port, svid_data, ack);
}
return true;
}
static inline bool svdm_dfp_inform_svids(struct pd_port *pd_port, bool ack)
{
int i;
struct svdm_svid_data *svid_data;
for (i = 0; i < pd_port->svid_data_cnt; i++) {
svid_data = &pd_port->svid_data[i];
if (svid_data->ops && svid_data->ops->dfp_inform_svids)
svid_data->ops->dfp_inform_svids(
pd_port, svid_data, ack);
}
return true;
}
static inline bool svdm_dfp_inform_modes(
struct pd_port *pd_port, uint16_t svid, bool ack)
{
struct svdm_svid_data *svid_data;
svid_data = dpm_get_svdm_svid_data(pd_port, svid);
if (svid_data == NULL)
return false;
if (svid_data->ops && svid_data->ops->dfp_inform_modes)
svid_data->ops->dfp_inform_modes(pd_port, svid_data, ack);
return true;
}
static inline bool svdm_dfp_inform_enter_mode(
struct pd_port *pd_port, uint16_t svid, uint8_t ops, bool ack)
{
struct svdm_svid_data *svid_data;
svid_data = dpm_get_svdm_svid_data(pd_port, svid);
if (svid_data == NULL)
return false;
if (svid_data->ops && svid_data->ops->dfp_inform_enter_mode)
svid_data->ops->dfp_inform_enter_mode(
pd_port, svid_data, ops, ack);
return true;
}
static inline bool svdm_dfp_inform_exit_mode(
struct pd_port *pd_port, uint16_t svid, uint8_t ops)
{
struct svdm_svid_data *svid_data;
svid_data = dpm_get_svdm_svid_data(pd_port, svid);
if (svid_data == NULL)
return false;
if (svid_data->ops && svid_data->ops->dfp_inform_exit_mode)
svid_data->ops->dfp_inform_exit_mode(pd_port, svid_data, ops);
return true;
}
static inline bool svdm_dfp_inform_attention(
struct pd_port *pd_port, uint16_t svid)
{
struct svdm_svid_data *svid_data;
svid_data = dpm_get_svdm_svid_data(pd_port, svid);
if (svid_data == NULL)
return false;
if (svid_data->ops && svid_data->ops->dfp_inform_attention)
svid_data->ops->dfp_inform_attention(pd_port, svid_data);
return true;
}
static inline bool svdm_ufp_request_enter_mode(
struct pd_port *pd_port, uint16_t svid, uint8_t ops)
{
struct svdm_svid_data *svid_data;
svid_data = dpm_get_svdm_svid_data(pd_port, svid);
if (svid_data == NULL)
return false;
if (svid_data->ops && svid_data->ops->ufp_request_enter_mode)
svid_data->ops->ufp_request_enter_mode(pd_port, svid_data, ops);
return true;
}
static inline bool svdm_ufp_request_exit_mode(
struct pd_port *pd_port, uint16_t svid, uint8_t ops)
{
struct svdm_svid_data *svid_data;
svid_data = dpm_get_svdm_svid_data(pd_port, svid);
if (svid_data == NULL)
return false;
if (svid_data->ops && svid_data->ops->ufp_request_exit_mode)
svid_data->ops->ufp_request_exit_mode(pd_port, svid_data, ops);
return true;
}
#endif /* PD_DPM_PRV_H_INCLUDED */

View file

@ -0,0 +1,770 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* PD Device Policy Manager Ready State reactions
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
#include "inc/pd_dpm_core.h"
#include "inc/pd_dpm_pdo_select.h"
#include "pd_dpm_prv.h"
#define DPM_REACTION_COND_ALWAYS (1<<0)
#define DPM_REACTION_COND_UFP_ONLY (1<<1)
#define DPM_REACTION_COND_DFP_ONLY (1<<2)
#define DPM_REACTION_COND_PD30 (1<<3)
#define DPM_REACCOND_DFP \
(DPM_REACTION_COND_ALWAYS | DPM_REACTION_COND_DFP_ONLY)
#define DPM_REACCOND_UFP \
(DPM_REACTION_COND_ALWAYS | DPM_REACTION_COND_UFP_ONLY)
#define DPM_REACTION_COND_CHECK_ONCE (1<<5)
#define DPM_REACTION_COND_ONE_SHOT (1<<6)
#define DPM_REACTION_COND_LIMITED_RETRIES (1<<7)
/*
* DPM flow delay reactions
*/
#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY
static uint8_t dpm_reaction_ufp_flow_delay(struct pd_port *pd_port)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
DPM_INFO("UFP Delay\n");
pd_restart_timer(pd_port, PD_TIMER_UFP_FLOW_DELAY);
return DPM_READY_REACTION_BUSY;
}
#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */
#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY
static uint8_t dpm_reaction_dfp_flow_delay(struct pd_port *pd_port)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
DPM_INFO("DFP Delay\n");
pd_restart_timer(pd_port, PD_TIMER_DFP_FLOW_DELAY);
return DPM_READY_REACTION_BUSY;
}
#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY */
#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY
static uint8_t dpm_reaction_vconn_stable_delay(struct pd_port *pd_port)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_port->vconn_role == PD_ROLE_VCONN_DYNAMIC_ON) {
DPM_INFO("VStable Delay\n");
return DPM_READY_REACTION_BUSY;
}
return 0;
}
#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */
/*
* DPM get cap reaction
*/
#ifdef CONFIG_USB_PD_REV30
static uint8_t dpm_reaction_get_source_cap_ext(struct pd_port *pd_port)
{
if (pd_port->power_role == PD_ROLE_SINK)
return TCP_DPM_EVT_GET_SOURCE_CAP_EXT;
return 0;
}
#endif /* CONFIG_USB_PD_REV30 */
static uint8_t dpm_reaction_get_sink_cap(struct pd_port *pd_port)
{
return TCP_DPM_EVT_GET_SINK_CAP;
}
static uint8_t dpm_reaction_get_source_cap(struct pd_port *pd_port)
{
return TCP_DPM_EVT_GET_SOURCE_CAP;
}
static uint8_t dpm_reaction_attemp_get_flag(struct pd_port *pd_port)
{
return TCP_DPM_EVT_GET_SINK_CAP;
}
/*
* DPM swap reaction
*/
#ifdef CONFIG_USB_PD_PR_SWAP
static uint8_t dpm_reaction_request_pr_swap(struct pd_port *pd_port)
{
uint32_t prefer_role =
DPM_CAP_EXTRACT_PR_CHECK(pd_port->dpm_caps);
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER))
return 0;
if (pd_port->power_role == PD_ROLE_SINK) {
if (prefer_role == DPM_CAP_PR_CHECK_PREFER_SRC)
return TCP_DPM_EVT_PR_SWAP_AS_SRC;
} else {
#ifdef CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW
if (dpm_check_good_power(pd_port) == GOOD_PW_PARTNER)
return TCP_DPM_EVT_PR_SWAP_AS_SNK;
#endif /* CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW */
if (prefer_role == DPM_CAP_PR_CHECK_PREFER_SNK)
return TCP_DPM_EVT_PR_SWAP_AS_SNK;
}
return 0;
}
#endif /* CONFIG_USB_PD_PR_SWAP */
#ifdef CONFIG_USB_PD_DR_SWAP
static uint8_t dpm_reaction_request_dr_swap(struct pd_port *pd_port)
{
uint32_t prefer_role =
DPM_CAP_EXTRACT_DR_CHECK(pd_port->dpm_caps);
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA))
return 0;
if (pd_port->data_role == PD_ROLE_DFP
&& prefer_role == DPM_CAP_DR_CHECK_PREFER_UFP)
return TCP_DPM_EVT_DR_SWAP_AS_UFP;
if (pd_port->data_role == PD_ROLE_UFP
&& prefer_role == DPM_CAP_DR_CHECK_PREFER_DFP)
return TCP_DPM_EVT_DR_SWAP_AS_DFP;
return 0;
}
#endif /* #ifdef CONFIG_USB_PD_DR_SWAP */
/*
* DPM DiscoverCable reaction
*/
#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE
static uint8_t dpm_reaction_dynamic_vconn(struct pd_port *pd_port)
{
pd_dpm_dynamic_enable_vconn(pd_port);
return 0;
}
#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */
#ifdef CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN
static uint8_t dpm_reaction_request_vconn_source(struct pd_port *pd_port)
{
bool return_vconn = true;
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY))
return 0;
if (pd_port->vconn_role)
return 0;
#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE
if (pd_port->tcpc->tcpc_vconn_supply == TCPC_VCONN_SUPPLY_STARTUP)
return_vconn = false;
#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */
if (pd_check_rev30(pd_port))
return_vconn = false;
if (return_vconn)
dpm_reaction_set(pd_port, DPM_REACTION_RETURN_VCONN_SRC);
return TCP_DPM_EVT_VCONN_SWAP_ON;
}
#endif /* CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN */
#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
static uint8_t pd_dpm_reaction_discover_cable(struct pd_port *pd_port)
{
#ifdef CONFIG_PD_DFP_RESET_CABLE
if (pd_is_reset_cable(pd_port))
return TCP_DPM_EVT_CABLE_SOFTRESET;
#endif /* CONFIG_PD_DFP_RESET_CABLE */
if (pd_is_discover_cable(pd_port)) {
pd_restart_timer(pd_port, PD_TIMER_DISCOVER_ID);
return DPM_READY_REACTION_BUSY;
}
return 0;
}
#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
#ifdef CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN
static uint8_t dpm_reaction_return_vconn_source(struct pd_port *pd_port)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_port->vconn_role) {
DPM_DBG("VconnReturn\n");
return TCP_DPM_EVT_VCONN_SWAP_OFF;
}
return 0;
}
#endif /* CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN */
/*
* DPM EnterMode reaction
*/
#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_ID
static uint8_t dpm_reaction_discover_id(struct pd_port *pd_port)
{
return TCP_DPM_EVT_DISCOVER_ID;
}
#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_ID */
#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_SVID
static uint8_t dpm_reaction_discover_svid(struct pd_port *pd_port)
{
return TCP_DPM_EVT_DISCOVER_SVIDS;
}
#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_SVID */
#ifdef CONFIG_USB_PD_MODE_OPERATION
static uint8_t dpm_reaction_mode_operation(struct pd_port *pd_port)
{
if (svdm_notify_pe_ready(pd_port))
return DPM_READY_REACTION_BUSY;
return 0;
}
#endif /* CONFIG_USB_PD_MODE_OPERATION */
/*
* DPM Local/Remote Alert reaction
*/
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_DPM_AUTO_SEND_ALERT
static uint8_t dpm_reaction_send_alert(struct pd_port *pd_port)
{
uint32_t alert_urgent;
struct pe_data *pe_data = &pd_port->pe_data;
alert_urgent = pe_data->local_alert;
alert_urgent &= ~ADO_GET_STATUS_ONCE_MASK;
if (!pe_data->get_status_once)
pe_data->local_alert = alert_urgent;
if ((!pe_data->pe_ready) && (alert_urgent == 0))
return 0;
if (pe_data->local_alert == 0)
return 0;
return TCP_DPM_EVT_ALERT;
}
#endif /* CONFIG_USB_PD_DPM_AUTO_SEND_ALERT */
#ifdef CONFIG_USB_PD_DPM_AUTO_GET_STATUS
const uint32_t c_get_status_alert_type = ADO_ALERT_OCP|
ADO_ALERT_OTP|ADO_ALERT_OVP|ADO_ALERT_OPER_CHANGED|
ADO_ALERT_SRC_IN_CHANGED;
static inline uint8_t dpm_reaction_alert_status_changed(struct pd_port *pd_port)
{
pd_port->pe_data.remote_alert &=
~ADO_ALERT_TYPE_SET(c_get_status_alert_type);
return TCP_DPM_EVT_GET_STATUS;
}
static inline uint8_t dpm_reaction_alert_battry_changed(struct pd_port *pd_port)
{
uint8_t i;
uint8_t mask;
uint8_t bat_change_i = 255;
uint8_t bat_change_mask1, bat_change_mask2;
bat_change_mask1 = ADO_FIXED_BAT(pd_port->pe_data.remote_alert);
bat_change_mask2 = ADO_HOT_SWAP_BAT(pd_port->pe_data.remote_alert);
if (bat_change_mask1) {
for (i = 0; i < 4; i++) {
mask = 1<<i;
if (bat_change_mask1 & mask) {
bat_change_i = i;
bat_change_mask1 &= ~mask;
pd_port->pe_data.remote_alert &=
~ADO_FIXED_BAT_SET(bat_change_mask1);
break;
}
}
} else if (bat_change_mask2) {
for (i = 0; i < 4; i++) {
mask = 1<<i;
if (bat_change_mask2 & mask) {
bat_change_i = i + 4;
bat_change_mask2 &= ~mask;
pd_port->pe_data.remote_alert &=
~ADO_HOT_SWAP_BAT_SET(bat_change_mask2);
break;
}
}
}
if (bat_change_mask1 == 0 && bat_change_mask2 == 0) {
pd_port->pe_data.remote_alert &=
~ADO_ALERT_TYPE_SET(ADO_ALERT_BAT_CHANGED);
}
if (bat_change_i == 255)
return 0;
pd_port->tcp_event.tcp_dpm_data.data_object[0] = bat_change_i;
return TCP_DPM_EVT_GET_BAT_STATUS;
}
static uint8_t dpm_reaction_handle_alert(struct pd_port *pd_port)
{
uint32_t alert_type = ADO_ALERT_TYPE(pd_port->pe_data.remote_alert);
if (alert_type & c_get_status_alert_type)
return dpm_reaction_alert_status_changed(pd_port);
if (alert_type & ADO_ALERT_BAT_CHANGED)
return dpm_reaction_alert_battry_changed(pd_port);
return 0;
}
#endif /* CONFIG_USB_PD_DPM_AUTO_GET_STATUS */
#endif /* CONFIG_USB_PD_REV30 */
/*
* DPM Idle reaction
*/
static inline uint8_t dpm_get_pd_connect_state(struct pd_port *pd_port)
{
if (pd_port->power_role == PD_ROLE_SOURCE) {
if (pd_check_rev30(pd_port))
return PD_CONNECT_PE_READY_SRC_PD30;
return PD_CONNECT_PE_READY_SRC;
}
if (pd_check_rev30(pd_port)) {
if (pd_is_source_support_apdo(pd_port))
return PD_CONNECT_PE_READY_SNK_APDO;
return PD_CONNECT_PE_READY_SNK_PD30;
}
return PD_CONNECT_PE_READY_SNK;
}
static inline void dpm_check_vconn_highv_prot(struct pd_port *pd_port)
{
#ifdef CONFIG_USB_PD_VCONN_SAFE5V_ONLY
struct tcpc_device *tcpc = pd_port->tcpc;
struct pe_data *pe_data = &pd_port->pe_data;
bool vconn_highv_prot = pd_port->request_v_new > 5000;
if (pe_data->vconn_highv_prot && !vconn_highv_prot &&
tcpc->tcpc_flags & TCPC_FLAGS_VCONN_SAFE5V_ONLY) {
DPM_INFO("VC_HIGHV_PROT: %d\n", vconn_highv_prot);
pe_data->vconn_highv_prot = vconn_highv_prot;
pd_set_vconn(pd_port, pe_data->vconn_highv_prot_role);
}
#endif /* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */
}
static uint8_t dpm_reaction_update_pe_ready(struct pd_port *pd_port)
{
uint8_t state;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (!pd_port->pe_data.pe_ready) {
DPM_INFO("PE_READY\n");
pd_port->pe_data.pe_ready = true;
#ifdef CONFIG_DUAL_ROLE_USB_INTF
dual_role_instance_changed(pd_port->tcpc->dr_usb);
#endif /* CONFIG_DUAL_ROLE_USB_INTF */
}
state = dpm_get_pd_connect_state(pd_port);
pd_update_connect_state(pd_port, state);
dpm_check_vconn_highv_prot(pd_port);
pd_dpm_dynamic_disable_vconn(pd_port);
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
pd_port->pe_data.pd_traffic_idle = true;
if (pd_check_rev30(pd_port) &&
(pd_port->power_role == PD_ROLE_SOURCE))
pd_set_sink_tx(pd_port, PD30_SINK_TX_OK);
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
return 0;
}
/*
* DPM reaction declaration
*/
typedef uint8_t (*dpm_reaction_fun)(struct pd_port *pd_port);
struct dpm_ready_reaction {
uint32_t bit_mask;
uint8_t condition;
dpm_reaction_fun handler;
};
#define DECL_DPM_REACTION(xmask, xcond, xhandler) { \
.bit_mask = xmask, \
.condition = xcond, \
.handler = xhandler, \
}
#define DECL_DPM_REACTION_ALWAYS(xhandler) \
DECL_DPM_REACTION(DPM_REACTION_CAP_ALWAYS, \
DPM_REACTION_COND_ALWAYS, \
xhandler)
#define DECL_DPM_REACTION_CHECK_ONCE(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_ALWAYS | \
DPM_REACTION_COND_CHECK_ONCE, \
xhandler)
#define DECL_DPM_REACTION_LIMITED_RETRIES(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_ALWAYS |\
DPM_REACTION_COND_LIMITED_RETRIES, \
xhandler)
#define DECL_DPM_REACTION_ONE_SHOT(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_ALWAYS | \
DPM_REACTION_COND_ONE_SHOT, \
xhandler)
#define DECL_DPM_REACTION_UFP(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_UFP_ONLY, \
xhandler)
#define DECL_DPM_REACTION_DFP(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_DFP_ONLY, \
xhandler)
#define DECL_DPM_REACTION_PD30(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_PD30, \
xhandler)
#define DECL_DPM_REACTION_PD30_LIMITED_RETRIES(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_PD30 |\
DPM_REACTION_COND_LIMITED_RETRIES, \
xhandler)
#define DECL_DPM_REACTION_PD30_ONE_SHOT(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_PD30 | \
DPM_REACTION_COND_ONE_SHOT, \
xhandler)
#define DECL_DPM_REACTION_DFP_PD30_ONE_SHOT(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_DFP_ONLY |\
DPM_REACTION_COND_PD30 | \
DPM_REACTION_COND_ONE_SHOT, \
xhandler)
#define DECL_DPM_REACTION_DFP_PD30_LIMITED_RETRIES(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_DFP_ONLY |\
DPM_REACTION_COND_PD30 | \
DPM_REACTION_COND_LIMITED_RETRIES, \
xhandler)
#define DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_DFP_ONLY |\
DPM_REACTION_COND_PD30 | \
DPM_REACTION_COND_CHECK_ONCE, \
xhandler)
#define DECL_DPM_REACTION_DFP_PD30_RUN_ONCE(xmask, xhandler) \
DECL_DPM_REACTION(xmask, \
DPM_REACTION_COND_DFP_ONLY |\
DPM_REACTION_COND_PD30 | \
DPM_REACTION_COND_CHECK_ONCE | \
DPM_REACTION_COND_ONE_SHOT, \
xhandler)
static const struct dpm_ready_reaction dpm_reactions[] = {
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_DPM_AUTO_SEND_ALERT
DECL_DPM_REACTION_PD30(
DPM_REACTION_CAP_ALWAYS,
dpm_reaction_send_alert),
#endif /* CONFIG_USB_PD_DPM_AUTO_SEND_ALERT */
#ifdef CONFIG_USB_PD_DPM_AUTO_GET_STATUS
DECL_DPM_REACTION_PD30(
DPM_REACTION_CAP_ALWAYS,
dpm_reaction_handle_alert),
#endif /* CONFIG_USB_PD_DPM_AUTO_GET_STATUS */
#endif /* CONFIG_USB_PD_REV30 */
#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY
DECL_DPM_REACTION_DFP(
DPM_REACTION_DFP_FLOW_DELAY,
dpm_reaction_dfp_flow_delay),
#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY */
#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY
DECL_DPM_REACTION_UFP(
DPM_REACTION_UFP_FLOW_DELAY,
dpm_reaction_ufp_flow_delay),
#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */
DECL_DPM_REACTION_LIMITED_RETRIES(
DPM_REACTION_GET_SINK_CAP,
dpm_reaction_get_sink_cap),
DECL_DPM_REACTION_LIMITED_RETRIES(
DPM_REACTION_GET_SOURCE_CAP,
dpm_reaction_get_source_cap),
DECL_DPM_REACTION_LIMITED_RETRIES(
DPM_REACTION_ATTEMPT_GET_FLAG,
dpm_reaction_attemp_get_flag),
#ifdef CONFIG_USB_PD_REV30
DECL_DPM_REACTION_PD30_ONE_SHOT(
DPM_REACTION_GET_SOURCE_CAP_EXT,
dpm_reaction_get_source_cap_ext),
#endif /* CONFIG_USB_PD_REV30 */
#ifdef CONFIG_USB_PD_PR_SWAP
DECL_DPM_REACTION_CHECK_ONCE(
DPM_REACTION_REQUEST_PR_SWAP,
dpm_reaction_request_pr_swap),
#endif /* CONFIG_USB_PD_PR_SWAP */
#ifdef CONFIG_USB_PD_DR_SWAP
DECL_DPM_REACTION_CHECK_ONCE(
DPM_REACTION_REQUEST_DR_SWAP,
dpm_reaction_request_dr_swap),
#endif /* CONFIG_USB_PD_DR_SWAP */
#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE
DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE(
DPM_REACTION_DYNAMIC_VCONN,
dpm_reaction_dynamic_vconn),
#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */
#ifdef CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN
DECL_DPM_REACTION_DFP_PD30_RUN_ONCE(
DPM_REACTION_REQUEST_VCONN_SRC,
dpm_reaction_request_vconn_source),
#endif /* CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN */
#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY
DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE(
DPM_REACTION_VCONN_STABLE_DELAY,
dpm_reaction_vconn_stable_delay),
#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */
#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE(
DPM_REACTION_DISCOVER_CABLE,
pd_dpm_reaction_discover_cable),
#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
#ifdef CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN
DECL_DPM_REACTION_DFP_PD30_RUN_ONCE(
DPM_REACTION_RETURN_VCONN_SRC,
dpm_reaction_return_vconn_source),
#endif /* CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN */
#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_ID
DECL_DPM_REACTION_DFP_PD30_LIMITED_RETRIES(
DPM_REACTION_DISCOVER_ID,
dpm_reaction_discover_id),
#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_ID */
#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_SVID
DECL_DPM_REACTION_DFP_PD30_LIMITED_RETRIES(
DPM_REACTION_DISCOVER_SVID,
dpm_reaction_discover_svid),
#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_SVID */
#ifdef CONFIG_USB_PD_MODE_OPERATION
DECL_DPM_REACTION_ALWAYS(
dpm_reaction_mode_operation),
#endif /* CONFIG_USB_PD_MODE_OPERATION */
DECL_DPM_REACTION_ALWAYS(
dpm_reaction_update_pe_ready),
};
/**
* dpm_get_reaction_env
*
* Get current reaction's environmental conditions.
*
* Returns environmental conditions.
*/
static inline uint8_t dpm_get_reaction_env(struct pd_port *pd_port)
{
uint8_t conditions;
if (pd_port->data_role == PD_ROLE_DFP)
conditions = DPM_REACCOND_DFP;
else
conditions = DPM_REACCOND_UFP;
if (pd_check_rev30(pd_port))
conditions |= DPM_REACTION_COND_PD30;
return conditions;
}
/**
* dpm_check_reaction_busy
*
* check this reaction is still keep busy
*
* @ reaction : which reaction is checked.
*
* Return Boolean to indicate busy or not.
*/
static inline bool dpm_check_reaction_busy(struct pd_port *pd_port,
const struct dpm_ready_reaction *reaction)
{
if (reaction->bit_mask & DPM_REACTION_CAP_ALWAYS)
return false;
return !dpm_reaction_check(
pd_port, DPM_REACTION_CAP_READY_ONCE);
}
/**
* dpm_check_reaction_available
*
* check this reaction is available.
*
* @ env : Environmental conditions of reaction table
* @ reaction : which reaction is checked.
*
* Returns TCP_DPM_EVT_ID if available;
* Returns Zero to indicate if not available.
* Returns DPM_READY_REACTION_BUSY to indicate
* this reaction is waiting for being finished.
*/
static inline uint8_t dpm_check_reaction_available(struct pd_port *pd_port,
uint8_t env, const struct dpm_ready_reaction *reaction)
{
int ret;
if (!dpm_reaction_check(pd_port, reaction->bit_mask))
return 0;
if (!(reaction->condition & env))
return 0;
if (dpm_check_reaction_busy(pd_port, reaction))
return DPM_READY_REACTION_BUSY;
ret = reaction->handler(pd_port);
if (ret == 0 &&
reaction->condition & DPM_REACTION_COND_CHECK_ONCE)
dpm_reaction_clear(pd_port, reaction->bit_mask);
return ret;
}
/**
* dpm_check_reset_reaction
*
* Once trigger one reaction,
* check if automatically clear this reaction flag.
*
* For the following reactions type:
* DPM_REACTION_COND_ONE_SHOT,
* DPM_REACTION_COND_LIMITED_RETRIES
*
* @ reaction : which reaction is triggered.
*
* Return Boolean to indicate automatically clear or not.
*/
static inline bool dpm_check_clear_reaction(struct pd_port *pd_port,
const struct dpm_ready_reaction *reaction)
{
if (pd_port->pe_data.dpm_reaction_id != reaction->bit_mask)
pd_port->pe_data.dpm_reaction_retry = 0;
pd_port->pe_data.dpm_reaction_retry++;
pd_port->pe_data.dpm_reaction_id = reaction->bit_mask;
if (reaction->condition & DPM_REACTION_COND_ONE_SHOT)
return true;
if (reaction->condition & DPM_REACTION_COND_LIMITED_RETRIES)
return pd_port->pe_data.dpm_reaction_retry > 3;
return false;
}
uint8_t pd_dpm_get_ready_reaction(struct pd_port *pd_port)
{
uint8_t evt;
uint8_t env;
uint32_t clear_reaction = DPM_REACTION_CAP_READY_ONCE;
const struct dpm_ready_reaction *reaction = dpm_reactions;
const struct dpm_ready_reaction *reaction_last =
dpm_reactions + ARRAY_SIZE(dpm_reactions);
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
env = dpm_get_reaction_env(pd_port);
do {
evt = dpm_check_reaction_available(pd_port, env, reaction);
} while ((evt == 0) && (++reaction < reaction_last));
if (evt > 0 && dpm_check_clear_reaction(pd_port, reaction)) {
clear_reaction |= reaction->bit_mask;
DPM_DBG("clear_reaction=%d\n", evt);
}
dpm_reaction_clear(pd_port, clear_reaction);
return evt;
}

View file

@ -0,0 +1,99 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* PD Device Policy Manager for UVDM
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
#include "inc/pd_dpm_core.h"
#include "pd_dpm_prv.h"
#ifdef CONFIG_USB_PD_RICHTEK_UVDM
bool richtek_dfp_notify_pe_startup(
struct pd_port *pd_port, struct svdm_svid_data *svid_data)
{
UVDM_INFO("%s\n", __func__);
pd_port->richtek_init_done = false;
return true;
}
int richtek_dfp_notify_pe_ready(
struct pd_port *pd_port, struct svdm_svid_data *svid_data)
{
if (pd_port->data_role != PD_ROLE_DFP)
return 0;
if (pd_port->richtek_init_done)
return 0;
pd_port->richtek_init_done = true;
UVDM_INFO("%s\n", __func__);
#if 0
pd_port->uvdm_cnt = 3;
pd_port->uvdm_wait_resp = true;
pd_port->uvdm_data[0] = PD_UVDM_HDR(USB_VID_RICHTEK, 0x4321);
pd_port->uvdm_data[1] = 0x11223344;
pd_port->uvdm_data[2] = 0x44332211;
pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_UVDM);
#endif
return 1;
}
bool richtek_dfp_notify_uvdm(struct pd_port *pd_port,
struct svdm_svid_data *svid_data, bool ack)
{
uint32_t resp_cmd = 0;
if (ack) {
if (pd_port->uvdm_wait_resp)
resp_cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
UVDM_INFO("dfp_notify: ACK (0x%x)\n", resp_cmd);
} else
UVDM_INFO("dfp_notify: NAK\n");
return true;
}
bool richtek_ufp_notify_uvdm(struct pd_port *pd_port,
struct svdm_svid_data *svid_data)
{
uint8_t i;
uint32_t reply_cmd[VDO_MAX_NR];
uint16_t cmd = (uint16_t) PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]);
UVDM_INFO("ufp_notify: 0x%x\n", cmd);
if (cmd >= 0x1000) {
UVDM_INFO("uvdm_no_reply\n");
VDM_STATE_DPM_INFORMED(pd_port);
return true;
}
reply_cmd[0] = PD_UVDM_HDR(USB_VID_RICHTEK, cmd+1);
for (i = 1; i < pd_port->uvdm_cnt; i++)
reply_cmd[i] = ~pd_port->uvdm_data[i];
pd_reply_custom_vdm(pd_port, TCPC_TX_SOP,
pd_port->uvdm_cnt, reply_cmd);
VDM_STATE_NORESP_CMD(pd_port);
return true;
}
#endif /* CONFIG_USB_PD_RICHTEK_UVDM */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,376 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for Common
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
static inline void *pd_get_tcp_event_data_object(struct pd_port *pd_port)
{
return pd_port->tcp_event.tcp_dpm_data.data_object;
}
/*
* Policy Engine General State Activity
*/
static void pe_idle_reset_data(struct pd_port *pd_port)
{
pd_reset_pe_timer(pd_port);
pd_reset_svid_data(pd_port);
pd_port->pe_data.pd_prev_connected = false;
pd_port->state_machine = PE_STATE_MACHINE_IDLE;
pd_enable_bist_test_mode(pd_port, false);
#ifndef CONFIG_USB_PD_TRANSMIT_BIST2
pd_disable_bist_mode2(pd_port);
#endif /* CONFIG_USB_PD_TRANSMIT_BIST2 */
pd_enable_timer(pd_port, PD_TIMER_PE_IDLE_TOUT);
pd_unlock_msg_output(pd_port);
}
void pe_idle1_entry(struct pd_port *pd_port)
{
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
pd_port->error_recovery_once = 0;
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
pe_idle_reset_data(pd_port);
pd_try_put_pe_idle_event(pd_port);
}
void pe_idle2_entry(struct pd_port *pd_port)
{
memset(&pd_port->pe_data, 0, sizeof(struct pe_data));
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_IDLE);
pd_disable_timer(pd_port, PD_TIMER_PE_IDLE_TOUT);
pd_notify_pe_idle(pd_port);
}
void pe_reject_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_REJECT);
}
void pe_error_recovery_entry(struct pd_port *pd_port)
{
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
pd_port->error_recovery_once++;
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
pe_idle_reset_data(pd_port);
pd_notify_pe_error_recovery(pd_port);
pd_try_put_pe_idle_event(pd_port);
}
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
void pe_error_recovery_once_entry(struct pd_port *pd_port)
{
uint8_t state = PD_CONNECT_TYPEC_ONLY_SNK_DFT;
pd_notify_pe_hard_reset_completed(pd_port);
if (pd_port->power_role == PD_ROLE_SOURCE) {
state = PD_CONNECT_TYPEC_ONLY_SRC;
pd_dpm_dynamic_disable_vconn(pd_port);
} else
pd_dpm_sink_vbus(pd_port, true);
pd_update_connect_state(pd_port, state);
}
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER
void pe_over_recv_hreset_limit_entry(struct pd_port *pd_port)
{
PE_INFO("OverHResetLimit++\n");
pe_idle_reset_data(pd_port);
pd_notify_pe_over_recv_hreset(pd_port);
PE_INFO("OverHResetLimit--\n");
}
#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */
void pe_bist_test_data_entry(struct pd_port *pd_port)
{
PE_STATE_IGNORE_UNKNOWN_EVENT(pd_port);
pd_enable_bist_test_mode(pd_port, true);
}
void pe_bist_test_data_exit(struct pd_port *pd_port)
{
pd_enable_bist_test_mode(pd_port, false);
}
void pe_bist_carrier_mode_2_entry(struct pd_port *pd_port)
{
pd_send_bist_mode2(pd_port);
pd_enable_pe_state_timer(pd_port, PD_TIMER_BIST_CONT_MODE);
}
void pe_bist_carrier_mode_2_exit(struct pd_port *pd_port)
{
pd_disable_bist_mode2(pd_port);
}
/*
* Policy Engine Share State Activity
*/
static inline uint8_t pe30_power_ready_entry(struct pd_port *pd_port)
{
uint8_t rx_cap = PD_RX_CAP_PE_READY_UFP;
if (pd_port->vconn_role)
rx_cap = PD_RX_CAP_PE_READY_DFP;
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
dpm_reaction_clear(pd_port,
DPM_REACTION_DFP_FLOW_DELAY |
DPM_REACTION_UFP_FLOW_DELAY);
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
return rx_cap;
}
static inline uint8_t pe20_power_ready_entry(struct pd_port *pd_port)
{
uint8_t rx_cap = PD_RX_CAP_PE_READY_UFP;
if (pd_port->data_role == PD_ROLE_DFP)
rx_cap = PD_RX_CAP_PE_READY_DFP;
pd_port->pe_data.pe_state_flags |=
PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT;
return rx_cap;
}
void pe_power_ready_entry(struct pd_port *pd_port)
{
uint8_t rx_cap;
pd_port->state_machine = PE_STATE_MACHINE_NORMAL;
pd_port->pe_data.during_swap = false;
pd_port->pe_data.explicit_contract = true;
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
pd_port->pe_data.renegotiation_count = 0;
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
if (pd_check_rev30(pd_port))
rx_cap = pe30_power_ready_entry(pd_port);
else
rx_cap = pe20_power_ready_entry(pd_port);
pd_set_rx_enable(pd_port, rx_cap);
dpm_reaction_set(pd_port, DPM_REACTION_CAP_READY_ONCE);
pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_SUCCESS);
}
#ifdef CONFIG_USB_PD_REV30
/*
* [PD3.0] Figure 8-85 Get Battery Capabilities State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
void pe_get_battery_cap_entry(struct pd_port *pd_port)
{
struct pd_get_battery_capabilities *gbcdb =
pd_get_tcp_event_data_object(pd_port);
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ext_msg(pd_port,
PD_EXT_GET_BAT_CAP, PD_GBCDB_SIZE, gbcdb);
}
void pe_get_battery_cap_exit(struct pd_port *pd_port)
{
pd_dpm_inform_battery_cap(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
/*
* [PD3.0] Figure 8-86 Give Battery Capabilities State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
void pe_give_battery_cap_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_battery_cap(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
/*
* [PD3.0] Figure 8-87 Get Battery Status State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
void pe_get_battery_status_entry(struct pd_port *pd_port)
{
struct pd_get_battery_status *gbsdb =
pd_get_tcp_event_data_object(pd_port);
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ext_msg(pd_port,
PD_EXT_GET_BAT_STATUS, PD_GBSDB_SIZE, gbsdb);
}
void pe_get_battery_status_exit(struct pd_port *pd_port)
{
pd_dpm_inform_battery_status(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
/*
* [PD3.0] Figure 8-88 Give Battery Status State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
void pe_give_battery_status_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_battery_status(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
/*
* [PD3.0] Figure 8-89 Get Manufacturer Information State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
void pe_get_manufacturer_info_entry(struct pd_port *pd_port)
{
struct pd_get_manufacturer_info *gmidb =
pd_get_tcp_event_data_object(pd_port);
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ext_msg(pd_port,
PD_EXT_GET_MFR_INFO, PD_GMIDB_SIZE, gmidb);
}
void pe_get_manufacturer_info_exit(struct pd_port *pd_port)
{
pd_dpm_inform_mfrs_info(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
/*
* [PD3.0] Figure 8-90 Give Manufacturer Information State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
void pe_give_manufacturer_info_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_mfrs_info(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
/*
* [PD3.0] Figure 8-91 Get Country Codes State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
void pe_get_country_codes_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_COUNTRY_CODE);
}
void pe_get_country_codes_exit(struct pd_port *pd_port)
{
pd_dpm_inform_country_codes(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
/*
* [PD3.0] Figure 8-92 Give Country Codes State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
void pe_give_country_codes_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_country_codes(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
/*
* [PD3.0] Figure 8-93 Get Country Information State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
void pe_get_country_info_entry(struct pd_port *pd_port)
{
uint32_t *ccdo =
pd_get_tcp_event_data_object(pd_port);
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_data_msg(pd_port,
PD_DATA_GET_COUNTRY_INFO, PD_CCDO_SIZE, ccdo);
}
void pe_get_country_info_exit(struct pd_port *pd_port)
{
pd_dpm_inform_country_info(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
/*
* [PD3.0] Figure 8-94 Give Country Information State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL
void pe_give_country_info_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_country_info(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */
/*
* [PD3.0] Unsupported, Unrecognized UVDM and Unsupported SVDM.
*/
void pe_vdm_not_supported_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_NOT_SUPPORTED);
}
#endif /* CONFIG_USB_PD_REV30 */

View file

@ -0,0 +1,46 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for DBGACC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
void pe_dbg_ready_entry(struct pd_port *pd_port)
{
uint8_t state;
if (pd_port->pe_data.pe_ready)
return;
pd_port->pe_data.pe_ready = true;
pd_reset_protocol_layer(pd_port, false);
if (pd_port->data_role == PD_ROLE_UFP) {
PE_INFO("Custom_DBGACC : UFP\n");
state = PD_CONNECT_PE_READY_DBGACC_UFP;
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_UFP);
} else {
PE_INFO("Custom_DBGACC : DFP\n");
state = PD_CONNECT_PE_READY_DBGACC_DFP;
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_DFP);
}
pd_update_connect_state(pd_port, state);
}
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */

View file

@ -0,0 +1,221 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for DFP
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
/*
* [PD2.0] Figure 8-64 DFP to UFP VDM Discover Identity State Diagram
*/
void pe_dfp_ufp_vdm_identity_request_entry(struct pd_port *pd_port)
{
pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP);
}
void pe_dfp_ufp_vdm_identity_acked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_id(pd_port, true);
}
void pe_dfp_ufp_vdm_identity_naked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_id(pd_port, false);
}
/*
* [PD2.0] Figure 8-65 DFP VDM Discover Identity State Diagram
*/
void pe_dfp_cbl_vdm_identity_request_entry(struct pd_port *pd_port)
{
pd_port->pe_data.discover_id_counter++;
pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP_PRIME);
}
void pe_dfp_cbl_vdm_identity_acked_entry(struct pd_port *pd_port)
{
pd_dpm_inform_cable_id(pd_port, false);
}
void pe_dfp_cbl_vdm_identity_naked_entry(struct pd_port *pd_port)
{
pd_dpm_inform_cable_id(pd_port, false);
}
/*
* [PD2.0] Figure 8-66 DFP VDM Discover SVIDs State Diagram
*/
void pe_dfp_vdm_svids_request_entry(struct pd_port *pd_port)
{
pd_send_vdm_discover_svids(pd_port, TCPC_TX_SOP);
}
void pe_dfp_vdm_svids_acked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_svids(pd_port, true);
}
void pe_dfp_vdm_svids_naked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_svids(pd_port, false);
}
/*
* [PD2.0] Figure 8-67 DFP VDM Discover Modes State Diagram
*/
void pe_dfp_vdm_modes_request_entry(struct pd_port *pd_port)
{
pd_send_vdm_discover_modes(pd_port, TCPC_TX_SOP, pd_port->mode_svid);
}
void pe_dfp_vdm_modes_acked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_modes(pd_port, true);
}
void pe_dfp_vdm_modes_naked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_modes(pd_port, false);
}
/*
* [PD2.0] Figure 8-68 DFP VDM Mode Entry State Diagram
*/
void pe_dfp_vdm_mode_entry_request_entry(struct pd_port *pd_port)
{
pd_send_vdm_enter_mode(pd_port, TCPC_TX_SOP,
pd_port->mode_svid, pd_port->mode_obj_pos);
}
void pe_dfp_vdm_mode_entry_acked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_enter_mode(pd_port, true);
}
void pe_dfp_vdm_mode_entry_naked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_enter_mode(pd_port, false);
}
/*
* [PD2.0] Figure 8-69 DFP VDM Mode Exit State Diagram
*/
void pe_dfp_vdm_mode_exit_request_entry(struct pd_port *pd_port)
{
pd_send_vdm_exit_mode(pd_port, TCPC_TX_SOP,
pd_port->mode_svid, pd_port->mode_obj_pos);
}
void pe_dfp_vdm_mode_exit_acked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_exit_mode(pd_port);
}
/*
* [PD2.0] Figure 8-70 DFP VDM Attention State Diagram
*/
void pe_dfp_vdm_attention_request_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_attention(pd_port);
}
/*
* [PD2.0] Figure 8-83 DFP Cable Soft Reset or Cable Reset State Diagram
*/
#ifdef CONFIG_PD_DFP_RESET_CABLE
void pe_dfp_cbl_send_soft_reset_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG_OR_TX_FAILED(pd_port);
pd_send_cable_soft_reset(pd_port);
}
void pe_dfp_cbl_send_cable_reset_entry(struct pd_port *pd_port)
{
/* TODO : we don't do cable reset now */
}
#endif /* CONFIG_PD_DFP_RESET_CABLE */
/*
* [PD2.0] Display Port
*/
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
void pe_dfp_vdm_dp_status_update_request_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_send_dp_status_update(pd_port);
}
void pe_dfp_vdm_dp_status_update_acked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_dp_status_update(pd_port, true);
}
void pe_dfp_vdm_dp_status_update_naked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_dp_status_update(pd_port, false);
}
void pe_dfp_vdm_dp_configuration_request_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_send_dp_configuration(pd_port);
}
void pe_dfp_vdm_dp_configuration_acked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_dp_configuration(pd_port, true);
}
void pe_dfp_vdm_dp_configuration_naked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_dp_configuration(pd_port, false);
}
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
/*
* UVDM
*/
#ifdef CONFIG_USB_PD_CUSTOM_VDM
void pe_dfp_uvdm_send_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_send_uvdm(pd_port);
}
void pe_dfp_uvdm_acked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_uvdm(pd_port, true);
}
void pe_dfp_uvdm_naked_entry(struct pd_port *pd_port)
{
pd_dpm_dfp_inform_uvdm(pd_port, false);
}
#endif /* CONFIG_USB_PD_CUSTOM_VDM */

View file

@ -0,0 +1,87 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for DR
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
/*
* [PD2.0]
* Figure 8-53 Dual-Role (Source) Get Source Capabilities diagram
* Figure 8-54 Dual-Role (Source) Give Sink Capabilities diagram
* Figure 8-55 Dual-Role (Sink) Get Sink Capabilities State Diagram
* Figure 8-56 Dual-Role (Sink) Give Source Capabilities State Diagram
*/
void pe_dr_src_get_source_cap_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG_OR_RJ(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP);
}
void pe_dr_src_get_source_cap_exit(struct pd_port *pd_port)
{
pd_dpm_dr_inform_source_cap(pd_port);
}
void pe_dr_src_give_sink_cap_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_sink_caps(pd_port);
}
void pe_dr_snk_get_sink_cap_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG_OR_RJ(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SINK_CAP);
}
void pe_dr_snk_get_sink_cap_exit(struct pd_port *pd_port)
{
pd_dpm_dr_inform_sink_cap(pd_port);
}
void pe_dr_snk_give_source_cap_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_source_caps(pd_port);
}
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
void pe_dr_snk_give_source_cap_ext_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_source_cap_ext(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
void pe_dr_src_get_source_cap_ext_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP_EXT);
}
void pe_dr_src_get_source_cap_ext_exit(struct pd_port *pd_port)
{
pd_dpm_inform_source_cap_ext(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */

View file

@ -0,0 +1,82 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for DRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
/*
* [PD2.0] Figure 8-49: Type-C DFP to UFP Data Role Swap State Diagram
*/
void pe_drs_dfp_ufp_evaluate_dr_swap_entry(struct pd_port *pd_port)
{
pd_dpm_drs_evaluate_swap(pd_port, PD_ROLE_UFP);
}
void pe_drs_dfp_ufp_accept_dr_swap_entry(struct pd_port *pd_port)
{
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
}
void pe_drs_dfp_ufp_change_to_ufp_entry(struct pd_port *pd_port)
{
pd_dpm_drs_change_role(pd_port, PD_ROLE_UFP);
}
void pe_drs_dfp_ufp_send_dr_swap_entry(struct pd_port *pd_port)
{
pe_send_swap_request_entry(pd_port, PD_CTRL_DR_SWAP);
}
void pe_drs_dfp_ufp_reject_dr_swap_entry(struct pd_port *pd_port)
{
pd_reply_wait_reject_msg(pd_port);
}
/*
* [PD2.0] Figure 8-50: Type-C UFP to DFP Data Role Swap State Diagram
*/
void pe_drs_ufp_dfp_evaluate_dr_swap_entry(struct pd_port *pd_port)
{
pd_dpm_drs_evaluate_swap(pd_port, PD_ROLE_DFP);
}
void pe_drs_ufp_dfp_accept_dr_swap_entry(struct pd_port *pd_port)
{
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
}
void pe_drs_ufp_dfp_change_to_dfp_entry(struct pd_port *pd_port)
{
#ifdef CONFIG_USB_PD_RESET_CABLE
dpm_reaction_set(pd_port, DPM_REACTION_CAP_RESET_CABLE);
#endif /* CONFIG_USB_PD_RESET_CABLE */
pd_dpm_drs_change_role(pd_port, PD_ROLE_DFP);
}
void pe_drs_ufp_dfp_send_dr_swap_entry(struct pd_port *pd_port)
{
pe_send_swap_request_entry(pd_port, PD_CTRL_DR_SWAP);
}
void pe_drs_ufp_dfp_reject_dr_swap_entry(
struct pd_port *pd_port)
{
pd_reply_wait_reject_msg(pd_port);
}

View file

@ -0,0 +1,124 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for PRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
/*
* [PD2.0] Figure 8-51:
* Dual-Role Port in Source to Sink Power Role Swap State Diagram
*/
void pe_prs_src_snk_evaluate_pr_swap_entry(struct pd_port *pd_port)
{
pd_dpm_prs_evaluate_swap(pd_port, PD_ROLE_SINK);
}
void pe_prs_src_snk_accept_pr_swap_entry(struct pd_port *pd_port)
{
pd_notify_pe_execute_pr_swap(pd_port, true);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
}
void pe_prs_src_snk_transition_to_off_entry(struct pd_port *pd_port)
{
pd_lock_msg_output(pd_port); /* for tSRCTransition */
pd_notify_pe_execute_pr_swap(pd_port, true);
pd_enable_timer(pd_port, PD_TIMER_SOURCE_TRANSITION);
}
void pe_prs_src_snk_assert_rd_entry(struct pd_port *pd_port)
{
pd_dpm_prs_change_role(pd_port, PD_ROLE_SINK);
}
void pe_prs_src_snk_wait_source_on_entry(struct pd_port *pd_port)
{
PE_STATE_HRESET_IF_TX_FAILED(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY);
}
void pe_prs_src_snk_send_swap_entry(struct pd_port *pd_port)
{
pe_send_swap_request_entry(pd_port, PD_CTRL_PR_SWAP);
}
void pe_prs_src_snk_reject_pr_swap_entry(struct pd_port *pd_port)
{
pd_reply_wait_reject_msg(pd_port);
}
/*
* [PD2.0] Figure 8-52:
* Dual-role Port in Sink to Source Power Role Swap State Diagram
*/
void pe_prs_snk_src_evaluate_pr_swap_entry(struct pd_port *pd_port)
{
pd_dpm_prs_evaluate_swap(pd_port, PD_ROLE_SOURCE);
}
void pe_prs_snk_src_accept_pr_swap_entry(
struct pd_port *pd_port)
{
pd_notify_pe_execute_pr_swap(pd_port, true);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
}
void pe_prs_snk_src_transition_to_off_entry(struct pd_port *pd_port)
{
/*
* Sink should call pd_notify_pe_execute_pr_swap before this state,
* because source may turn off power & change CC before we got
* GoodCRC or Accept.
*/
pd_port->pe_data.during_swap = true;
pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_SOURCE_OFF);
pd_dpm_prs_turn_off_power_sink(pd_port);
}
void pe_prs_snk_src_assert_rp_entry(struct pd_port *pd_port)
{
pd_dpm_prs_change_role(pd_port, PD_ROLE_SOURCE);
}
void pe_prs_snk_src_source_on_entry(struct pd_port *pd_port)
{
#ifdef CONFIG_USB_PD_RESET_CABLE
dpm_reaction_set(pd_port, DPM_REACTION_CAP_RESET_CABLE);
#endif /* CONFIG_USB_PD_RESET_CABLE */
PE_STATE_HRESET_IF_TX_FAILED(pd_port);
pd_dpm_dynamic_enable_vconn(pd_port);
pd_dpm_prs_enable_power_source(pd_port, true);
/* Send PS_Rdy in process_event after source_on */
}
void pe_prs_snk_src_send_swap_entry(struct pd_port *pd_port)
{
pd_notify_pe_execute_pr_swap(pd_port, false);
pe_send_swap_request_entry(pd_port, PD_CTRL_PR_SWAP);
}
void pe_prs_snk_src_reject_swap_entry(struct pd_port *pd_port)
{
pd_reply_wait_reject_msg(pd_port);
}

View file

@ -0,0 +1,319 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for SNK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
/*
* [PD2.0] Figure 8-39 Sink Port state diagram
*/
void pe_snk_startup_entry(struct pd_port *pd_port)
{
uint8_t rx_cap = PD_RX_CAP_PE_STARTUP;
bool pr_swap = pd_port->state_machine == PE_STATE_MACHINE_PR_SWAP;
#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
uint8_t msg_id_last = pd_port->pe_data.msg_id_rx[TCPC_TX_SOP];
#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */
pd_reset_protocol_layer(pd_port, false);
if (pr_swap) {
/*
* If PE reset rx_cap to startup in here,
* maybe can't meet tSwapSink for PR_SWAP case
*/
rx_cap = PD_RX_CAP_PE_SEND_WAIT_CAP;
#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
pd_port->msg_id_pr_swap_last = msg_id_last;
#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */
}
pd_set_rx_enable(pd_port, rx_cap);
pd_put_pe_event(pd_port, PD_PE_RESET_PRL_COMPLETED);
}
void pe_snk_discovery_entry(struct pd_port *pd_port)
{
bool wait_valid = true;
if (pd_check_pe_during_hard_reset(pd_port)) {
wait_valid = false;
pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_TRANSITION);
}
pd_enable_vbus_valid_detection(pd_port, wait_valid);
}
void pe_snk_wait_for_capabilities_entry(
struct pd_port *pd_port)
{
#ifdef CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW
/* Default current draw after HardReset */
if (pd_check_pe_during_hard_reset(pd_port))
pd_dpm_sink_vbus(pd_port, true);
#endif /* CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW */
pd_notify_pe_hard_reset_completed(pd_port);
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SEND_WAIT_CAP);
pd_enable_pe_state_timer(pd_port, PD_TIMER_SINK_WAIT_CAP);
}
void pe_snk_evaluate_capability_entry(struct pd_port *pd_port)
{
/* Disable UART output for Source SenderResponse */
pd_lock_msg_output(pd_port);
pd_handle_hard_reset_recovery(pd_port);
pd_handle_first_pd_command(pd_port);
pd_port->pe_data.explicit_contract = false;
pd_dpm_snk_evaluate_caps(pd_port);
}
void pe_snk_select_capability_entry(struct pd_port *pd_port)
{
struct pd_event *pd_event = pd_get_curr_pd_event(pd_port);
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
PE_STATE_WAIT_MSG_HRESET_IF_TOUT(pd_port);
if (pd_event->event_type == PD_EVT_DPM_MSG) {
PE_DBG("SelectCap%d, rdo:0x%08x\n",
pd_event->msg_sec, pd_port->last_rdo);
} else {
/* new request, for debug only */
/* pd_dpm_sink_vbus(pd_port, false); */
PE_DBG("NewReq, rdo:0x%08x\n", pd_port->last_rdo);
}
/* Disable UART output for Sink SenderResponse */
pd_lock_msg_output(pd_port);
pd_send_sop_data_msg(pd_port,
PD_DATA_REQUEST, 1, &pd_port->last_rdo);
}
void pe_snk_select_capability_exit(struct pd_port *pd_port)
{
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_ACCEPT)) {
pd_port->pe_data.remote_selected_cap =
RDO_POS(pd_port->last_rdo);
pd_port->cap_miss_match = 0;
} else if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_REJECT)) {
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
if (pd_port->cap_miss_match == 0x01) {
PE_INFO("reset renegotiation cnt by cap mismatch\n");
pd_port->pe_data.renegotiation_count = 0;
}
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
pd_port->cap_miss_match |= (1 << 1);
} else
pd_port->cap_miss_match = 0;
/* Waiting for Hard-Reset Done */
if (!pd_check_timer_msg_event(pd_port, PD_TIMER_SENDER_RESPONSE))
pd_unlock_msg_output(pd_port);
}
void pe_snk_transition_sink_entry(struct pd_port *pd_port)
{
pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_TRANSITION);
#ifdef CONFIG_USB_PD_SNK_GOTOMIN
if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_GOTO_MIN)) {
if (pd_port->dpm_caps & DPM_CAP_LOCAL_GIVE_BACK)
pd_port->request_i_new = pd_port->request_i_op;
}
#endif /* CONFIG_USB_PD_SNK_GOTOMIN */
pd_dpm_snk_standby_power(pd_port);
}
void pe_snk_ready_entry(struct pd_port *pd_port)
{
if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_WAIT))
pd_enable_timer(pd_port, PD_TIMER_SINK_REQUEST);
pd_notify_pe_snk_explicit_contract(pd_port);
pe_power_ready_entry(pd_port);
}
void pe_snk_hard_reset_entry(struct pd_port *pd_port)
{
pd_send_hard_reset(pd_port);
}
void pe_snk_transition_to_default_entry(struct pd_port *pd_port)
{
pd_reset_local_hw(pd_port);
pd_dpm_snk_hard_reset(pd_port);
}
void pe_snk_give_sink_cap_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_sink_caps(pd_port);
}
void pe_snk_get_source_cap_entry(struct pd_port *pd_port)
{
#ifdef CONFIG_USB_PD_TCPM_CB_2ND
PE_STATE_WAIT_MSG(pd_port);
#else
PE_STATE_WAIT_TX_SUCCESS(pd_port);
#endif /* CONFIG_USB_PD_TCPM_CB_2ND */
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP);
}
void pe_snk_send_soft_reset_entry(struct pd_port *pd_port)
{
pd_send_soft_reset(pd_port);
}
void pe_snk_soft_reset_entry(struct pd_port *pd_port)
{
pd_handle_soft_reset(pd_port);
}
/* ---- Policy Engine (PD30) ---- */
#ifdef CONFIG_USB_PD_REV30
/*
* [PD3.0] Figure 8-71 Sink Port Not Supported Message State Diagram
*/
void pe_snk_send_not_supported_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_NOT_SUPPORTED);
}
void pe_snk_not_supported_received_entry(struct pd_port *pd_port)
{
PE_STATE_DPM_INFORMED(pd_port);
pd_dpm_inform_not_support(pd_port);
}
void pe_snk_chunk_received_entry(struct pd_port *pd_port)
{
pd_enable_timer(pd_port, PD_TIMER_CK_NOT_SUPPORTED);
}
/*
* [PD3.0] Figure 8-74 Sink Port Source Alert State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
void pe_snk_source_alert_received_entry(struct pd_port *pd_port)
{
PE_STATE_DPM_INFORMED(pd_port);
pd_dpm_inform_alert(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
/*
* [PD3.0] Figure 8-75 Sink Port Sink Alert State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
void pe_snk_send_sink_alert_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_alert(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
/*
* [PD3.0] Figure 8-77 Sink Port Get Source Capabilities Extended State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
void pe_snk_get_source_cap_ext_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP_EXT);
}
void pe_snk_get_source_cap_ext_exit(struct pd_port *pd_port)
{
pd_dpm_inform_source_cap_ext(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
/*
* [PD3.0] Figure 8-79 Sink Port Get Source Status State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
void pe_snk_get_source_status_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_STATUS);
}
void pe_snk_get_source_status_exit(struct pd_port *pd_port)
{
pd_dpm_inform_status(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
/*
* [PD3.0] Figure 8-82 Sink Give Sink Status State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
void pe_snk_give_sink_status_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_status(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
/*
* [PD3.0] Figure 8-83 Sink Port Get Source PPS Status State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
void pe_snk_get_pps_status_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_PPS_STATUS);
}
void pe_snk_get_pps_status_exit(struct pd_port *pd_port)
{
pd_dpm_inform_pps_status(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
#endif /* CONFIG_USB_PD_REV30 */

View file

@ -0,0 +1,311 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for SRC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
/*
* [PD2.0] Figure 8-38 Source Port Policy Engine state diagram
*/
void pe_src_startup_entry(struct pd_port *pd_port)
{
pd_reset_protocol_layer(pd_port, false);
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_STARTUP);
/*
* When entering this state,
* VBUS must be valid even for cc_attached.
*/
pd_enable_timer(pd_port, PD_TIMER_SOURCE_START);
}
void pe_src_discovery_entry(struct pd_port *pd_port)
{
/* The SourceCapabilitiesTimer continues to run during the states
* defined in Source Startup Structured VDM Discover Identity State
* Diagram
*/
pd_port->pe_data.pd_connected = false;
pd_enable_timer(pd_port, PD_TIMER_SOURCE_CAPABILITY);
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
if (pd_is_discover_cable(pd_port))
pd_enable_timer(pd_port, PD_TIMER_DISCOVER_ID);
#endif
}
void pe_src_send_capabilities_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG_HRESET_IF_TOUT(pd_port);
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SEND_WAIT_CAP);
pd_dpm_send_source_caps(pd_port);
pd_port->pe_data.cap_counter++;
}
void pe_src_negotiate_capabilities_entry(
struct pd_port *pd_port)
{
pd_handle_first_pd_command(pd_port);
pd_dpm_src_evaluate_request(pd_port);
}
void pe_src_transition_supply_entry(struct pd_port *pd_port)
{
uint8_t msg = PD_CTRL_ACCEPT;
struct pd_event *pd_event = pd_get_curr_pd_event(pd_port);
/* goto-min */
if (pd_event->event_type == PD_EVT_TCP_MSG) {
msg = PD_CTRL_GOTO_MIN;
pd_port->request_i_new = pd_port->request_i_op;
}
pd_send_sop_ctrl_msg(pd_port, msg);
}
void pe_src_transition_supply2_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY);
}
void pe_src_ready_entry(struct pd_port *pd_port)
{
pd_notify_pe_src_explicit_contract(pd_port);
pe_power_ready_entry(pd_port);
}
void pe_src_disabled_entry(struct pd_port *pd_port)
{
pd_notify_pe_hard_reset_completed(pd_port);
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISABLE);
pd_update_connect_state(pd_port, PD_CONNECT_TYPEC_ONLY_SRC);
pd_dpm_dynamic_disable_vconn(pd_port);
}
void pe_src_capability_response_entry(struct pd_port *pd_port)
{
pd_reply_wait_reject_msg_no_resp(pd_port);
}
void pe_src_hard_reset_entry(struct pd_port *pd_port)
{
pd_send_hard_reset(pd_port);
pd_enable_timer(pd_port, PD_TIMER_PS_HARD_RESET);
}
void pe_src_hard_reset_received_entry(struct pd_port *pd_port)
{
pd_enable_timer(pd_port, PD_TIMER_PS_HARD_RESET);
}
void pe_src_transition_to_default_entry(struct pd_port *pd_port)
{
pd_reset_local_hw(pd_port);
pd_dpm_src_hard_reset(pd_port);
}
void pe_src_transition_to_default_exit(struct pd_port *pd_port)
{
pd_set_vconn(pd_port, PD_ROLE_VCONN_ON);
pd_enable_timer(pd_port, PD_TIMER_NO_RESPONSE);
}
void pe_src_get_sink_cap_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SINK_CAP);
}
void pe_src_get_sink_cap_exit(struct pd_port *pd_port)
{
pd_dpm_dr_inform_sink_cap(pd_port);
}
void pe_src_wait_new_capabilities_entry(
struct pd_port *pd_port)
{
/* Wait for new Source Capabilities */
}
void pe_src_send_soft_reset_entry(struct pd_port *pd_port)
{
pd_send_soft_reset(pd_port);
}
void pe_src_soft_reset_entry(struct pd_port *pd_port)
{
pd_handle_soft_reset(pd_port);
}
/*
* [PD2.0] Figure 8-81
Source Startup Structured VDM Discover Identity State Diagram (TODO)
*/
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
#ifdef CONFIG_PD_SRC_RESET_CABLE
void pe_src_cbl_send_soft_reset_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_RESPONSE(pd_port);
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISCOVER_CABLE);
pd_send_cable_soft_reset(pd_port);
}
#endif /* CONFIG_PD_SRC_RESET_CABLE */
void pe_src_vdm_identity_request_entry(struct pd_port *pd_port)
{
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISCOVER_CABLE);
pd_port->pe_data.discover_id_counter++;
pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP_PRIME);
}
void pe_src_vdm_identity_acked_entry(struct pd_port *pd_port)
{
pd_dpm_inform_cable_id(pd_port, true);
}
void pe_src_vdm_identity_naked_entry(struct pd_port *pd_port)
{
pd_dpm_inform_cable_id(pd_port, true);
}
#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
#ifdef CONFIG_USB_PD_REV30
/*
* [PD3.0] Source Port Not Supported Message State Diagram
*/
void pe_src_send_not_supported_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_NOT_SUPPORTED);
}
void pe_src_not_supported_received_entry(struct pd_port *pd_port)
{
PE_STATE_DPM_INFORMED(pd_port);
pd_dpm_inform_not_support(pd_port);
}
void pe_src_chunk_received_entry(struct pd_port *pd_port)
{
pd_enable_timer(pd_port, PD_TIMER_CK_NOT_SUPPORTED);
}
/*
* [PD3.0] Figure 8-73 Source Port Source Alert State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
void pe_src_send_source_alert_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_alert(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
/*
* [PD3.0] Figure 8-76 Source Port Sink Alert State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
void pe_src_sink_alert_received_entry(struct pd_port *pd_port)
{
PE_STATE_DPM_INFORMED(pd_port);
pd_dpm_inform_alert(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
/*
* [PD3.0] Figure 8-78 Source Give Source Capabilities Extended State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
void pe_src_give_source_cap_ext_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_source_cap_ext(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
/*
* [PD3.0] Figure 8-80 Source Give Source Status State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
void pe_src_give_source_status_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_dpm_send_status(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
/*
* [PD3.0] Figure 8-81 Source Port Get Sink Status State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
void pe_src_get_sink_status_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_MSG(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_STATUS);
}
void pe_src_get_sink_status_exit(struct pd_port *pd_port)
{
pd_dpm_inform_status(pd_port);
}
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
/*
* [PD3.0] Figure 8-84 Source Give Source PPS Status State Diagram
*/
#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE
void pe_src_give_pps_status_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
/* TODO */
PD_BUG_ON(1);
}
#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */
#endif /* CONFIG_USB_PD_REV30 */

View file

@ -0,0 +1,118 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for UFP
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
/*
* [PD2.0] Figure 8-58 UFP Structured VDM Discover Identity State Diagram
*/
void pe_ufp_vdm_get_identity_entry(struct pd_port *pd_port)
{
pd_dpm_ufp_request_id_info(pd_port);
}
/*
* [PD2.0] Figure 8-59 UFP Structured VDM Discover SVIDs State Diagram
*/
void pe_ufp_vdm_get_svids_entry(struct pd_port *pd_port)
{
pd_dpm_ufp_request_svid_info(pd_port);
}
/*
* [PD2.0] Figure 8-60 UFP Structured VDM Discover Modes State Diagram
*/
void pe_ufp_vdm_get_modes_entry(struct pd_port *pd_port)
{
pd_dpm_ufp_request_mode_info(pd_port);
}
/*
* [PD2.0] Figure 8-61 UFP Structured VDM Enter Mode State Diagram
*/
void pe_ufp_vdm_evaluate_mode_entry_entry(
struct pd_port *pd_port)
{
pd_dpm_ufp_request_enter_mode(pd_port);
}
/*
* [PD2.0] Figure 8-62 UFP Structured VDM Exit Mode State Diagram
*/
void pe_ufp_vdm_mode_exit_entry(struct pd_port *pd_port)
{
pd_dpm_ufp_request_exit_mode(pd_port);
}
/*
* [PD2.0] Figure 8-63 UFP VDM Attention State Diagram
*/
void pe_ufp_vdm_attention_request_entry(
struct pd_port *pd_port)
{
VDM_STATE_NORESP_CMD(pd_port);
switch (pd_port->mode_svid) {
#ifdef CONFIG_USB_PD_ALT_MODE
case USB_SID_DISPLAYPORT:
pd_dpm_ufp_send_dp_attention(pd_port);
break;
#endif
default:
pd_send_vdm_attention(pd_port,
TCPC_TX_SOP, pd_port->mode_svid, pd_port->mode_obj_pos);
break;
}
}
/*
* ALT Mode
*/
#ifdef CONFIG_USB_PD_ALT_MODE
void pe_ufp_vdm_dp_status_update_entry(struct pd_port *pd_port)
{
pd_dpm_ufp_request_dp_status(pd_port);
}
void pe_ufp_vdm_dp_configure_entry(struct pd_port *pd_port)
{
pd_dpm_ufp_request_dp_config(pd_port);
}
#endif /* CONFIG_USB_PD_ALT_MODE */
/*
* SVMD/UVDM
*/
#ifdef CONFIG_USB_PD_CUSTOM_VDM
void pe_ufp_uvdm_recv_entry(struct pd_port *pd_port)
{
pd_dpm_ufp_recv_uvdm(pd_port);
}
#endif /* CONFIG_USB_PD_CUSTOM_VDM */

View file

@ -0,0 +1,71 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for VCS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci.h"
#include "inc/pd_policy_engine.h"
/*
* [PD2.0] Figure 8-57 VCONN Swap State Diagram
*/
void pe_vcs_send_swap_entry(struct pd_port *pd_port)
{
pe_send_swap_request_entry(pd_port, PD_CTRL_VCONN_SWAP);
}
void pe_vcs_evaluate_swap_entry(struct pd_port *pd_port)
{
pd_dpm_vcs_evaluate_swap(pd_port);
}
void pe_vcs_accept_swap_entry(struct pd_port *pd_port)
{
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT);
}
void pe_vcs_reject_vconn_swap_entry(struct pd_port *pd_port)
{
pd_reply_wait_reject_msg(pd_port);
}
void pe_vcs_wait_for_vconn_entry(struct pd_port *pd_port)
{
pd_enable_pe_state_timer(pd_port, PD_TIMER_VCONN_ON);
}
void pe_vcs_turn_off_vconn_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_DPM_ACK(pd_port);
pd_dpm_vcs_enable_vconn(pd_port, PD_ROLE_VCONN_OFF);
}
void pe_vcs_turn_on_vconn_entry(struct pd_port *pd_port)
{
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_RESET_CABLE
dpm_reaction_set(pd_port, DPM_REACTION_CAP_RESET_CABLE);
#endif /* CONFIG_USB_PD_RESET_CABLE */
#endif /* CONFIG_USB_PD_REV30 */
pd_dpm_vcs_enable_vconn(pd_port, PD_ROLE_VCONN_DYNAMIC_ON);
}
void pe_vcs_send_ps_rdy_entry(struct pd_port *pd_port)
{
PE_STATE_WAIT_TX_SUCCESS(pd_port);
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY);
}

View file

@ -0,0 +1,837 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Process Event
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
#include "inc/pd_dpm_core.h"
/*
* [BLOCK] print event
*/
#if PE_EVENT_DBG_ENABLE
static const char * const pd_ctrl_msg_name[] = {
"ctrl0",
"good_crc",
"goto_min",
"accept",
"reject",
"ping",
"ps_rdy",
"get_src_cap",
"get_snk_cap",
"dr_swap",
"pr_swap",
"vs_swap",
"wait",
"soft_reset",
"ctrlE",
"ctrlF",
#ifdef CONFIG_USB_PD_REV30
"no_support",
"get_src_cap_ex",
"get_status",
"fr_swap",
"get_pps",
"get_cc",
#endif /* CONFIG_USB_PD_REV30 */
};
static inline void print_ctrl_msg_event(struct tcpc_device *tcpc, uint8_t msg)
{
if (msg < PD_CTRL_MSG_NR)
PE_EVT_INFO("%s\n", pd_ctrl_msg_name[msg]);
}
static const char * const pd_data_msg_name[] = {
"data0",
"src_cap",
"request",
"bist",
"sink_cap",
#ifdef CONFIG_USB_PD_REV30
"bat_status",
"alert",
"get_ci",
#else
"data5",
"data6",
"data7",
#endif /* CONFIG_USB_PD_REV30 */
"data8",
"data9",
"dataA",
"dataB",
"dataC",
"dataD",
"dataE",
"vdm",
};
static inline void print_data_msg_event(struct tcpc_device *tcpc, uint8_t msg)
{
if (msg < PD_DATA_MSG_NR)
PE_EVT_INFO("%s\n", pd_data_msg_name[msg]);
}
#ifdef CONFIG_USB_PD_REV30
static const char *const pd_ext_msg_name[] = {
"ext0",
"src_cap_ex",
"status",
"get_bat_cap",
"get_bat_status",
"bat_cap",
"get_mfr_info",
"mfr_info",
"sec_req",
"sec_resp",
"fw_update_req",
"fw_update_res",
"pps_status",
"ci",
"cc",
};
static inline void print_ext_msg_event(struct tcpc_device *tcpc, uint8_t msg)
{
if (msg < PD_EXT_MSG_NR)
PE_EVT_INFO("%s\n", pd_ext_msg_name[msg]);
}
#endif /* CONFIG_USB_PD_REV30 */
static const char *const pd_hw_msg_name[] = {
"Detached",
"Attached",
"hard_reset",
"vbus_high",
"vbus_low",
"vbus_0v",
"vbus_stable",
"tx_err",
"discard",
"retry_vdm",
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
"sink_tx_change",
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
};
static inline void print_hw_msg_event(struct tcpc_device *tcpc, uint8_t msg)
{
if (msg < PD_HW_MSG_NR)
PE_EVT_INFO("%s\n", pd_hw_msg_name[msg]);
}
static const char *const pd_pe_msg_name[] = {
"reset_prl_done",
"pr_at_dft",
"hard_reset_done",
"pe_idle",
"vdm_reset",
"vdm_not_support",
};
static inline void print_pe_msg_event(struct tcpc_device *tcpc, uint8_t msg)
{
if (msg < PD_PE_MSG_NR)
PE_EVT_INFO("%s\n", pd_pe_msg_name[msg]);
}
static const char * const pd_dpm_msg_name[] = {
"ack",
"nak",
"cap_change",
"not_support",
};
static inline void print_dpm_msg_event(struct tcpc_device *tcpc, uint8_t msg)
{
if (msg < PD_DPM_MSG_NR)
PE_EVT_INFO("dpm_%s\n", pd_dpm_msg_name[msg]);
}
static const char *const tcp_dpm_evt_name[] = {
/* TCP_DPM_EVT_UNKONW */
"unknown",
/* TCP_DPM_EVT_PD_COMMAND */
"pr_swap_snk",
"pr_swap_src",
"dr_swap_ufp",
"dr_swap_dfp",
"vc_swap_off",
"vc_swap_on",
"goto_min",
"soft_reset",
"cable_soft_reset",
"get_src_cap",
"get_snk_cap",
"request",
"request_ex",
"request_again",
"bist_cm2",
"dummy",
#ifdef CONFIG_USB_PD_REV30
"get_src_cap_ext",
"get_status",
"fr_swap_snk",
"fr_swap_src",
"get_cc",
"get_pps",
"alert",
"get_ci",
"get_bat_cap",
"get_bat_status",
"get_mfrs_info",
#endif /* CONFIG_USB_PD_REV30 */
/* TCP_DPM_EVT_VDM_COMMAND */
"disc_cable",
"disc_id",
"disc_svid",
"disc_mode",
"enter_mode",
"exit_mode",
"attention",
#ifdef CONFIG_USB_PD_ALT_MODE
"dp_atten",
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
"dp_status",
"dp_config",
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifdef CONFIG_USB_PD_CUSTOM_VDM
"uvdm",
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
/* TCP_DPM_EVT_IMMEDIATELY */
"hard_reset",
"error_recovery",
};
static inline void print_tcp_event(struct tcpc_device *tcpc, uint8_t msg)
{
if (msg < TCP_DPM_EVT_NR)
PE_EVT_INFO("tcp_event(%s), %d\n",
tcp_dpm_evt_name[msg], msg);
}
#endif
static inline void print_event(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#if PE_EVENT_DBG_ENABLE
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
switch (pd_event->event_type) {
case PD_EVT_CTRL_MSG:
print_ctrl_msg_event(tcpc, pd_event->msg);
break;
case PD_EVT_DATA_MSG:
print_data_msg_event(tcpc, pd_event->msg);
break;
#ifdef CONFIG_USB_PD_REV30
case PD_EVT_EXT_MSG:
print_ext_msg_event(tcpc, pd_event->msg);
break;
#endif /* CONFIG_USB_PD_REV30 */
case PD_EVT_DPM_MSG:
print_dpm_msg_event(tcpc, pd_event->msg);
break;
case PD_EVT_HW_MSG:
print_hw_msg_event(tcpc, pd_event->msg);
break;
case PD_EVT_PE_MSG:
print_pe_msg_event(tcpc, pd_event->msg);
break;
case PD_EVT_TIMER_MSG:
PE_EVT_INFO("timer\n");
break;
case PD_EVT_TCP_MSG:
print_tcp_event(tcpc, pd_event->msg);
break;
}
#endif
}
/*---------------------------------------------------------------------------*/
bool pd_make_pe_state_transit(struct pd_port *pd_port,
uint8_t curr_state, const struct pe_state_reaction *state_reaction)
{
int i;
const struct pe_state_transition *state_transition =
state_reaction->state_transition;
for (i = 0; i < state_reaction->nr_transition; i++) {
if (state_transition[i].curr_state == curr_state) {
PE_TRANSIT_STATE(pd_port,
state_transition[i].next_state);
return true;
}
}
return false;
}
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_USB_PD_REV30
static inline bool pd30_process_ready_protocol_error(struct pd_port *pd_port)
{
bool multi_chunk;
if (!pd_check_rev30(pd_port))
return false;
if (!pd_port->curr_unsupported_msg) {
pe_transit_soft_reset_state(pd_port);
return true;
}
multi_chunk = pd_is_multi_chunk_msg(pd_port);
if (pd_port->power_role == PD_ROLE_SINK) {
PE_TRANSIT_STATE(pd_port, multi_chunk ?
PE_SNK_CHUNK_RECEIVED : PE_SNK_SEND_NOT_SUPPORTED);
return true;
}
PE_TRANSIT_STATE(pd_port, multi_chunk ?
PE_SRC_CHUNK_RECEIVED : PE_SRC_SEND_NOT_SUPPORTED);
return true;
}
#endif /* CONFIG_USB_PD_REV30 */
bool pd_process_protocol_error(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool power_change = false;
#if PE_INFO_ENABLE
uint8_t event_type = pd_event->event_type;
uint8_t msg_id = pd_get_msg_hdr_id(pd_port);
uint8_t msg_type = pd_event->msg;
#endif
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
switch (pd_port->pe_state_curr) {
case PE_SNK_TRANSITION_SINK:
/* fall through */
case PE_SRC_TRANSITION_SUPPLY: /* never recv ping for Source =.=*/
/* fall through */
case PE_SRC_TRANSITION_SUPPLY2:
power_change = true;
if (pd_event_msg_match(pd_event,
PD_EVT_CTRL_MSG, PD_CTRL_PING)) {
PE_DBG("Ignore Ping\n");
return false;
}
break;
#ifdef CONFIG_USB_PD_PR_SWAP
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
#endif /* CONFIG_USB_PD_PR_SWAP */
if (pd_event_msg_match(pd_event,
PD_EVT_CTRL_MSG, PD_CTRL_PING)) {
PE_DBG("Ignore Ping\n");
return false;
}
break;
#ifdef CONFIG_USB_PD_REV30
case PE_SNK_READY:
case PE_SRC_READY:
if (pd30_process_ready_protocol_error(pd_port))
return true;
break;
#endif /* CONFIG_USB_PD_REV30 */
};
if (pd_port->pe_data.pe_state_flags &
PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT) {
PE_DBG("Ignore Unknown Event\n");
return false;
}
if (pd_check_pe_during_hard_reset(pd_port)) {
PE_DBG("Ignore Event during HReset\n");
return false;
}
/*
* msg_type: PD_EVT_CTRL_MSG (1), PD_EVT_DATA_MSG (2)
*/
PE_INFO("PRL_ERR: %d-%d-%d\n", event_type, msg_type, msg_id);
if (pd_port->pe_data.during_swap) {
#ifdef CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
#else
pe_transit_hard_reset_state(pd_port);
#endif
} else if (power_change)
pe_transit_hard_reset_state(pd_port);
else
pe_transit_soft_reset_state(pd_port);
return true;
}
bool pd_process_tx_failed(struct pd_port *pd_port)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_check_pe_state_ready(pd_port) ||
pd_check_pe_during_hard_reset(pd_port)) {
PE_DBG("Ignore tx_failed\n");
return false;
}
pe_transit_soft_reset_state(pd_port);
return true;
}
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_USB_PD_RESET_CABLE
static inline bool pd_process_cable_ctrl_msg_accept(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_port->pe_state_curr) {
#ifdef CONFIG_PD_SRC_RESET_CABLE
case PE_SRC_CBL_SEND_SOFT_RESET:
vdm_put_dpm_discover_cable_event(pd_port);
return false;
#endif /* CONFIG_PD_SRC_RESET_CABLE */
#ifdef CONFIG_PD_DFP_RESET_CABLE
case PE_DFP_CBL_SEND_SOFT_RESET:
pe_transit_ready_state(pd_port);
return true;
#endif /* CONFIG_PD_DFP_RESET_CABLE */
}
return false;
}
#endif /* CONFIG_USB_PD_RESET_CABLE */
static inline bool pd_process_event_cable(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool ret = false;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
#ifdef CONFIG_USB_PD_RESET_CABLE
if (pd_event->msg == PD_CTRL_ACCEPT)
ret = pd_process_cable_ctrl_msg_accept(pd_port, pd_event);
#endif /* CONFIG_USB_PD_RESET_CABLE */
if (!ret)
PE_DBG("Ignore not SOP Ctrl Msg\n");
return ret;
}
/*---------------------------------------------------------------------------*/
static void pd_copy_msg_data(struct pd_port *pd_port,
uint8_t *payload, uint16_t count, uint8_t unit_sz)
{
pd_port->pd_msg_data_size = count * unit_sz;
pd_port->pd_msg_data_count = count;
pd_port->pd_msg_data_payload = payload;
}
#ifdef CONFIG_USB_PD_REV30
static inline void pd_copy_msg_data_from_ext_evt(
struct pd_port *pd_port, struct pd_msg *pd_msg)
{
uint32_t *payload = pd_msg->payload;
uint16_t *ext_hdr = (uint16_t *) payload;
uint8_t *ext_data = (uint8_t *) (ext_hdr+1);
uint16_t size = PD_EXT_HEADER_DATA_SIZE(*ext_hdr);
pd_copy_msg_data(pd_port, ext_data, size, 1);
}
#endif /* CONFIG_USB_PD_REV30 */
static inline void pd_copy_msg_data_from_evt(
struct pd_port *pd_port, struct pd_event *pd_event)
{
struct pd_msg *pd_msg = pd_event->pd_msg;
switch (pd_event->event_type) {
case PD_EVT_DATA_MSG:
PD_BUG_ON(pd_msg == NULL);
pd_copy_msg_data(pd_port, (uint8_t *)pd_msg->payload,
pd_get_msg_hdr_cnt(pd_port), sizeof(uint32_t));
break;
#ifdef CONFIG_USB_PD_REV30
case PD_EVT_EXT_MSG:
PD_BUG_ON(pd_msg == NULL);
pd_copy_msg_data_from_ext_evt(pd_port, pd_msg);
return;
#endif /* CONFIG_USB_PD_REV30 */
default:
pd_copy_msg_data(pd_port, NULL, 0, 0);
}
}
/*---------------------------------------------------------------------------*/
/*
*
* @ true : valid message
* @ false : invalid message, pe should drop the message
*/
static inline bool pe_is_valid_pd_msg_id(struct pd_port *pd_port,
struct pd_event *pd_event, struct pd_msg *pd_msg)
{
uint8_t sop_type = pd_msg->frame_type;
uint8_t msg_id = pd_get_msg_hdr_id(pd_port);
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_port->pe_state_curr == PE_BIST_TEST_DATA)
return false;
if (pd_event->event_type == PD_EVT_CTRL_MSG) {
switch (pd_event->msg) {
/* SofReset always has a MessageID value of zero */
case PD_CTRL_SOFT_RESET:
if (msg_id != 0) {
PE_INFO("Repeat soft_reset\n");
return false;
}
return true;
case PD_CTRL_GOOD_CRC:
PE_DBG("Discard_CRC\n");
return true;
#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
case PD_CTRL_PS_RDY:
if (pd_port->msg_id_pr_swap_last == msg_id) {
PE_INFO("Repeat ps_rdy\n");
return false;
}
break;
#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */
}
}
if (pd_port->pe_data.msg_id_rx[sop_type] == msg_id) {
PE_INFO("Repeat msg: %c:%d:%d\n",
(pd_event->event_type == PD_EVT_CTRL_MSG) ? 'C' : 'D',
pd_event->msg, msg_id);
return false;
}
pd_port->pe_data.msg_id_rx[sop_type] = msg_id;
return true;
}
static inline bool pe_is_valid_pd_msg_role(struct pd_port *pd_port,
struct pd_event *pd_event, struct pd_msg *pd_msg)
{
bool ret = true;
uint8_t msg_pr, msg_dr;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_msg == NULL) /* Good-CRC */
return true;
if (pd_msg->frame_type != TCPC_TX_SOP)
return true;
msg_pr = PD_HEADER_PR(pd_msg->msg_hdr);
msg_dr = PD_HEADER_DR(pd_msg->msg_hdr);
/*
* The Port Power Role field of a received Message shall not be verified
* by the receiver and no error recovery action shall be
* taken if it is incorrect.
*/
if (msg_pr == pd_port->power_role)
PE_DBG("Wrong PR:%d\n", msg_pr);
/*
* Should a Type-C Port receive a Message with the Port Data Role field
* set to the same Data Role as its current Data Role,
* except for the GoodCRC Message,
* Type-C error recovery actions as defined
* in [USBType-C 1.0] shall be performed.
*/
if (msg_dr == pd_port->data_role) {
#ifdef CONFIG_USB_PD_CHECK_DATA_ROLE
ret = false;
#endif
PE_INFO("Wrong DR:%d\n", msg_dr);
}
return ret;
}
static inline void pe_translate_pd_msg_event(struct pd_port *pd_port,
struct pd_event *pd_event, struct pd_msg *pd_msg)
{
uint16_t msg_hdr;
PD_BUG_ON(pd_msg == NULL);
msg_hdr = pd_msg->msg_hdr;
pd_port->curr_msg_hdr = msg_hdr;
pd_event->msg = PD_HEADER_TYPE(msg_hdr);
if (PD_HEADER_CNT(msg_hdr))
pd_event->event_type = PD_EVT_DATA_MSG;
else
pd_event->event_type = PD_EVT_CTRL_MSG;
#ifdef CONFIG_USB_PD_REV30
if (PD_HEADER_EXT(msg_hdr))
pd_event->event_type = PD_EVT_EXT_MSG;
if (pd_msg->frame_type == TCPC_TX_SOP_PRIME) {
pd_sync_sop_prime_spec_revision(
pd_port, PD_HEADER_REV(msg_hdr));
}
#endif /* CONFIG_USB_PD_REV30 */
}
static inline uint8_t pe_get_startup_state(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool act_as_sink = true;
uint8_t startup_state = 0xff;
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
pd_port->custom_dbgacc = false;
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
switch (pd_event->msg_sec) {
case TYPEC_ATTACHED_DBGACC_SNK:
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
pd_port->custom_dbgacc = true;
startup_state = PE_DBG_READY;
break;
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
case TYPEC_ATTACHED_SNK:
startup_state = PE_SNK_STARTUP;
break;
case TYPEC_ATTACHED_SRC:
act_as_sink = false;
startup_state = PE_SRC_STARTUP;
break;
}
/* At least > 2 for Ellisys VNDI PR_SWAP */
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
if (pd_port->error_recovery_once > 2)
startup_state = PE_ERROR_RECOVERY_ONCE;
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
pd_init_message_hdr(pd_port, act_as_sink);
return startup_state;
}
static inline bool pe_transit_startup_state(
struct pd_port *pd_port, struct pd_event *pd_event)
{
uint8_t startup_state =
pe_get_startup_state(pd_port, pd_event);
if (startup_state == 0xff)
return false;
pd_dpm_notify_pe_startup(pd_port);
PE_TRANSIT_STATE(pd_port, startup_state);
return true;
}
enum {
TII_TRAP_IN_IDLE = 0,
TII_TRANSIT_STATE = 1,
TII_PE_RUNNING = 2,
};
static inline uint8_t pe_check_trap_in_idle_state(
struct pd_port *pd_port, struct pd_event *pd_event)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
switch (pd_port->pe_pd_state) {
case PE_IDLE1:
case PE_ERROR_RECOVERY:
if (pd_event_pe_msg_match(pd_event, PD_PE_IDLE)) {
PE_TRANSIT_STATE(pd_port, PE_IDLE2);
return TII_TRANSIT_STATE;
}
pd_try_put_pe_idle_event(pd_port);
break;
case PE_IDLE2:
if (pd_event_hw_msg_match(pd_event, PD_HW_CC_ATTACHED)) {
if (pe_transit_startup_state(pd_port, pd_event))
return TII_TRANSIT_STATE;
}
/* The original IDLE2 may trigger by PE_IDLE_TOUT */
if (pd_event_hw_msg_match(pd_event, PD_HW_CC_DETACHED))
pd_notify_pe_idle(pd_port);
break;
default:
if (pd_event_hw_msg_match(pd_event, PD_HW_CC_DETACHED)) {
PE_TRANSIT_STATE(pd_port, PE_IDLE1);
return TII_TRANSIT_STATE;
}
return TII_PE_RUNNING;
}
PE_DBG("Trap in idle state, Ignore All MSG (%d:%d)\n",
pd_event->event_type, pd_event->msg);
return TII_TRAP_IN_IDLE;
}
static inline void pe_init_curr_state(struct pd_port *pd_port)
{
if (pd_port->power_role == PD_ROLE_SINK) {
pd_port->curr_ready_state = PE_SNK_READY;
pd_port->curr_hreset_state = PE_SNK_HARD_RESET;
pd_port->curr_sreset_state = PE_SNK_SEND_SOFT_RESET;
} else {
pd_port->curr_ready_state = PE_SRC_READY;
pd_port->curr_hreset_state = PE_SRC_HARD_RESET;
pd_port->curr_sreset_state = PE_SRC_SEND_SOFT_RESET;
}
pd_port->curr_unsupported_msg = false;
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
if (pd_port->custom_dbgacc)
pd_port->curr_ready_state = PE_DBG_READY;
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
}
bool pd_process_event(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool ret = false;
struct pd_msg *pd_msg = pd_event->pd_msg;
uint8_t tii = pe_check_trap_in_idle_state(pd_port, pd_event);
if (tii < TII_PE_RUNNING)
return tii;
pe_init_curr_state(pd_port);
if (pd_event->event_type == PD_EVT_PD_MSG)
pe_translate_pd_msg_event(pd_port, pd_event, pd_msg);
#if PE_EVT_INFO_VDM_DIS
if (!pd_curr_is_vdm_evt(pd_port))
#endif
print_event(pd_port, pd_event);
if ((pd_event->event_type < PD_EVT_PD_MSG_END) && (pd_msg != NULL)) {
if (!pe_is_valid_pd_msg_id(pd_port, pd_event, pd_msg))
return false;
if (!pe_is_valid_pd_msg_role(pd_port, pd_event, pd_msg)) {
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
return true;
}
}
pd_copy_msg_data_from_evt(pd_port, pd_event);
if (pd_curr_is_vdm_evt(pd_port))
return pd_process_event_vdm(pd_port, pd_event);
if (pd_event->event_type == PD_EVT_TCP_MSG)
return pd_process_event_tcp(pd_port, pd_event);
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
if (pd_port->custom_dbgacc)
return pd_process_event_dbg(pd_port, pd_event);
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */
if ((pd_event->event_type == PD_EVT_CTRL_MSG) &&
(pd_event->msg != PD_CTRL_GOOD_CRC) &&
(pd_msg != NULL) && (pd_msg->frame_type != TCPC_TX_SOP))
return pd_process_event_cable(pd_port, pd_event);
if (pd_process_event_com(pd_port, pd_event))
return true;
switch (pd_port->state_machine) {
#ifdef CONFIG_USB_PD_DR_SWAP
case PE_STATE_MACHINE_DR_SWAP:
ret = pd_process_event_drs(pd_port, pd_event);
break;
#endif /* CONFIG_USB_PD_DR_SWAP */
#ifdef CONFIG_USB_PD_PR_SWAP
case PE_STATE_MACHINE_PR_SWAP:
ret = pd_process_event_prs(pd_port, pd_event);
break;
#endif /* CONFIG_USB_PD_PR_SWAP */
#ifdef CONFIG_USB_PD_VCONN_SWAP
case PE_STATE_MACHINE_VCONN_SWAP:
ret = pd_process_event_vcs(pd_port, pd_event);
break;
#endif /* CONFIG_USB_PD_VCONN_SWAP */
}
if (ret)
return true;
if (pd_port->power_role == PD_ROLE_SINK)
ret = pd_process_event_snk(pd_port, pd_event);
else
ret = pd_process_event_src(pd_port, pd_event);
return ret;
}

View file

@ -0,0 +1,665 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Policy Engine for Common
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
#include "inc/pd_dpm_core.h"
/*
* [BLOCK] DRP (dr_swap, pr_swap, vconn_swap)
*/
#ifdef CONFIG_USB_PD_DR_SWAP
static inline bool pd_evaluate_reject_dr_swap(struct pd_port *pd_port)
{
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA) {
if (pd_port->power_role == PD_ROLE_DFP)
return pd_port->dpm_caps &
DPM_CAP_DR_SWAP_REJECT_AS_UFP;
return pd_port->dpm_caps & DPM_CAP_DR_SWAP_REJECT_AS_DFP;
}
return true;
}
#endif /* CONFIG_USB_PD_DR_SWAP */
#ifdef CONFIG_USB_PD_PR_SWAP
static inline bool pd_evaluate_reject_pr_swap(struct pd_port *pd_port)
{
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
if (pd_port->power_role == PD_ROLE_SOURCE)
return pd_port->dpm_caps &
DPM_CAP_PR_SWAP_REJECT_AS_SNK;
return pd_port->dpm_caps & DPM_CAP_PR_SWAP_REJECT_AS_SRC;
}
return true;
}
#endif /* CONFIG_USB_PD_PR_SWAP */
#ifdef CONFIG_USB_PD_VCONN_SWAP
static inline bool pd_evaluate_accept_vconn_swap(struct pd_port *pd_port)
{
if (pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY)
return true;
return false;
}
#endif /* CONFIG_USB_PD_VCONN_SWAP */
static inline bool pd_process_ctrl_msg_dr_swap(
struct pd_port *pd_port, struct pd_event *pd_event)
{
if (pd_port->pe_data.modal_operation) {
pe_transit_hard_reset_state(pd_port);
return true;
}
#ifdef CONFIG_USB_PD_DR_SWAP
if (!pd_check_pe_state_ready(pd_port))
return false;
if (!pd_evaluate_reject_dr_swap(pd_port)) {
pd_port->pe_data.during_swap = false;
pd_port->state_machine = PE_STATE_MACHINE_DR_SWAP;
PE_TRANSIT_DATA_STATE(pd_port,
PE_DRS_UFP_DFP_EVALUATE_DR_SWAP,
PE_DRS_DFP_UFP_EVALUATE_DR_SWAP);
return true;
}
#endif /* CONFIG_USB_PD_DR_SWAP */
PE_TRANSIT_STATE(pd_port, PE_REJECT);
return true;
}
static inline bool pd_process_ctrl_msg_pr_swap(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#ifdef CONFIG_USB_PD_PR_SWAP
if (!pd_evaluate_reject_pr_swap(pd_port)) {
pd_port->pe_data.during_swap = false;
pd_port->state_machine = PE_STATE_MACHINE_PR_SWAP;
pe_transit_evaluate_pr_swap_state(pd_port);
return true;
}
#endif /* CONFIG_USB_PD_PR_SWAP */
PE_TRANSIT_STATE(pd_port, PE_REJECT);
return true;
}
static inline bool pd_process_ctrl_msg_vconn_swap(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#ifdef CONFIG_USB_PD_VCONN_SWAP
if (!pd_check_pe_state_ready(pd_port))
return false;
if (pd_evaluate_accept_vconn_swap(pd_port)) {
pd_port->state_machine = PE_STATE_MACHINE_VCONN_SWAP;
PE_TRANSIT_STATE(pd_port, PE_VCS_EVALUATE_SWAP);
return true;
}
#endif /* CONFIG_USB_PD_VCONN_SWAP */
if (!pd_check_rev30(pd_port)) {
PE_TRANSIT_STATE(pd_port, PE_REJECT);
return true;
}
return false;
}
/*
* [BLOCK] BIST
*/
static inline bool pd_process_data_msg_bist(
struct pd_port *pd_port, struct pd_event *pd_event)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_port->request_v > 5000) {
PE_INFO("bist_not_vsafe5v\n");
return false;
}
switch (BDO_MODE(pd_event->pd_msg->payload[0])) {
case BDO_MODE_TEST_DATA:
PE_DBG("bist_test\n");
PE_TRANSIT_STATE(pd_port, PE_BIST_TEST_DATA);
pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_TEST_DATA);
return true;
case BDO_MODE_CARRIER2:
PE_DBG("bist_cm2\n");
PE_TRANSIT_STATE(pd_port, PE_BIST_CARRIER_MODE_2);
pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_DISABLE);
return true;
default:
#if 0
case BDO_MODE_RECV:
case BDO_MODE_TRANSMIT:
case BDO_MODE_COUNTERS:
case BDO_MODE_CARRIER0:
case BDO_MODE_CARRIER1:
case BDO_MODE_CARRIER3:
case BDO_MODE_EYE:
#endif
PE_DBG("Unsupport BIST\n");
pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_DISABLE);
return false;
}
return false;
}
/*
* [BLOCK] Porcess Ctrl MSG
*/
static void pd_cancel_dpm_reaction(struct pd_port *pd_port)
{
if (pd_port->pe_data.dpm_reaction_id < DPM_REACTION_REJECT_CANCEL)
dpm_reaction_clear(pd_port, pd_port->pe_data.dpm_reaction_id);
}
static bool pd_process_ctrl_msg_wait_reject(struct pd_port *pd_port)
{
pd_cancel_dpm_reaction(pd_port);
if (pd_port->state_machine == PE_STATE_MACHINE_PR_SWAP)
pd_notify_pe_cancel_pr_swap(pd_port);
pe_transit_ready_state(pd_port);
return true;
}
static inline bool pd_process_ctrl_msg_wait(struct pd_port *pd_port)
{
return pd_process_ctrl_msg_wait_reject(pd_port);
}
static inline bool pd_process_ctrl_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool ret = false;
#ifdef CONFIG_USB_PD_REV30
if (!pd_check_rev30(pd_port) &&
pd_event->msg >= PD_CTRL_PD30_START) {
pd_event->msg = PD_CTRL_MSG_NR;
return false;
}
#endif /* CONFIG_USB_PD_REV30 */
switch (pd_event->msg) {
case PD_CTRL_GOOD_CRC:
if (pd_port->pe_data.pe_state_flags &
PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER)
pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
if (pd_port->pe_data.pe_state_flags2 &
PE_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC) {
pe_transit_ready_state(pd_port);
return true;
}
break;
case PD_CTRL_REJECT:
pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_REJECT);
if (pd_port->pe_data.pe_state_flags &
PE_STATE_FLAG_BACK_READY_IF_RECV_REJECT) {
return pd_process_ctrl_msg_wait_reject(pd_port);
}
break;
case PD_CTRL_WAIT:
pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_WAIT);
if (pd_port->pe_data.pe_state_flags &
PE_STATE_FLAG_BACK_READY_IF_RECV_WAIT) {
return pd_process_ctrl_msg_wait(pd_port);
}
break;
case PD_CTRL_SOFT_RESET:
if (!pd_port->pe_data.during_swap &&
!pd_check_pe_during_hard_reset(pd_port)) {
pe_transit_soft_reset_recv_state(pd_port);
return true;
}
break;
/* Swap */
case PD_CTRL_DR_SWAP:
ret = pd_process_ctrl_msg_dr_swap(pd_port, pd_event);
break;
case PD_CTRL_PR_SWAP:
ret = pd_process_ctrl_msg_pr_swap(pd_port, pd_event);
break;
case PD_CTRL_VCONN_SWAP:
ret = pd_process_ctrl_msg_vconn_swap(pd_port, pd_event);
break;
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
case PD_CTRL_GET_COUNTRY_CODE:
if (pd_port->country_nr) {
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
pe_get_curr_ready_state(pd_port),
PE_GIVE_COUNTRY_CODES);
}
break;
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
case PD_CTRL_NOT_SUPPORTED:
pd_cancel_dpm_reaction(pd_port);
pd_notify_tcp_event_2nd_result(
pd_port, TCP_DPM_RET_NOT_SUPPORT);
if (pd_port->pe_data.pe_state_flags &
PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT) {
pe_transit_ready_state(pd_port);
return true;
} else if (pd_port->pe_data.vdm_state_timer) {
vdm_put_pe_event(
pd_port->tcpc, PD_PE_VDM_NOT_SUPPORT);
}
break;
#endif /* CONFIG_USB_PD_REV30 */
}
return ret;
}
/*
* [BLOCK] Porcess Data MSG
*/
static inline bool pd_process_data_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool ret = false;
uint8_t ready_state = pe_get_curr_ready_state(pd_port);
#ifdef CONFIG_USB_PD_REV30
if (!pd_check_rev30(pd_port) &&
pd_event->msg >= PD_DATA_PD30_START) {
pd_event->msg = PD_DATA_MSG_NR;
return false;
}
#endif /* CONFIG_USB_PD_REV30 */
switch (pd_event->msg) {
case PD_DATA_BIST:
if (pd_port->pe_state_curr == ready_state)
ret = pd_process_data_msg_bist(pd_port, pd_event);
break;
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
case PD_DATA_BAT_STATUS:
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
PE_GET_BATTERY_STATUS, ready_state);
break;
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
case PD_DATA_GET_COUNTRY_INFO:
if (pd_port->country_nr) {
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
ready_state, PE_GIVE_COUNTRY_INFO);
}
break;
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
#endif /* CONFIG_USB_PD_REV30 */
}
return ret;
}
/*
* [BLOCK] Porcess Extend MSG
*/
#ifdef CONFIG_USB_PD_REV30
static inline bool pd_process_ext_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool ret = false;
uint8_t ready_state = pe_get_curr_ready_state(pd_port);
if (!pd_check_rev30(pd_port)) {
pd_event->msg = PD_DATA_MSG_NR;
return false;
}
#ifndef CONFIG_USB_PD_REV30_CHUNKING_BY_PE
if (pd_port->pe_state_curr == ready_state &&
pd_is_multi_chunk_msg(pd_port)) {
pd_port->curr_unsupported_msg = true;
return pd_process_protocol_error(pd_port, pd_event);
}
#endif /* CONFIG_USB_PD_REV30_CHUNKING_BY_PE */
switch (pd_event->msg) {
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
case PD_EXT_GET_BAT_CAP:
if (pd_port->bat_nr) {
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
ready_state, PE_GIVE_BATTERY_CAP);
}
break;
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
case PD_EXT_GET_BAT_STATUS:
if (pd_port->bat_nr) {
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
ready_state, PE_GIVE_BATTERY_STATUS);
}
break;
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
case PD_EXT_BAT_CAP:
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
PE_GET_BATTERY_CAP, ready_state);
break;
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
case PD_EXT_GET_MFR_INFO:
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
ready_state, PE_GIVE_MANUFACTURER_INFO);
break;
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
case PD_EXT_MFR_INFO:
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
PE_GET_MANUFACTURER_INFO, ready_state);
break;
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
case PD_EXT_COUNTRY_INFO:
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
PE_GET_COUNTRY_INFO, ready_state);
break;
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
case PD_EXT_COUNTRY_CODES:
ret = PE_MAKE_STATE_TRANSIT_SINGLE(
PE_GET_COUNTRY_CODES, ready_state);
break;
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
}
return ret;
}
#endif /* CONFIG_USB_PD_REV30 */
/*
* [BLOCK] Porcess DPM MSG
*/
static inline bool pd_process_dpm_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool ret = false;
switch (pd_event->msg) {
case PD_DPM_ACK:
if (pd_port->pe_data.pe_state_flags2 &
PE_STATE_FLAG_BACK_READY_IF_DPM_ACK) {
pe_transit_ready_state(pd_port);
return true;
}
break;
#ifdef CONFIG_USB_PD_REV30
case PD_DPM_NOT_SUPPORT:
if (pd_check_rev30(pd_port)) {
PE_TRANSIT_STATE(pd_port, PE_VDM_NOT_SUPPORTED);
return true;
}
break;
#endif /* CONFIG_USB_PD_REV30 */
}
return ret;
}
/*
* [BLOCK] Porcess HW MSG
*/
static inline bool pd_process_recv_hard_reset(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER
if (pd_port->pe_data.recv_hard_reset_count > PD_HARD_RESET_COUNT) {
PE_TRANSIT_STATE(pd_port, PE_OVER_RECV_HRESET_LIMIT);
return true;
}
pd_port->pe_data.recv_hard_reset_count++;
#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
if (pd_check_pe_during_hard_reset(pd_port))
pd_port->pe_data.renegotiation_count++;
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
pe_transit_hard_reset_recv_state(pd_port);
return true;
}
static inline bool pd_process_hw_msg_tx_failed(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_port->pe_data.renegotiation_count > PD_HARD_RESET_COUNT) {
PE_INFO("renegotiation failed\n");
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
return true;
}
#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */
if (pd_port->pe_data.pe_state_flags &
PE_STATE_FLAG_BACK_READY_IF_TX_FAILED) {
pd_notify_tcp_event_2nd_result(
pd_port, TCP_DPM_RET_NO_RESPONSE);
pe_transit_ready_state(pd_port);
return true;
} else if (pd_port->pe_data.pe_state_flags &
PE_STATE_FLAG_HRESET_IF_TX_FAILED) {
pe_transit_hard_reset_state(pd_port);
return true;
}
return false;
}
static inline bool pd_process_hw_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_HW_RECV_HARD_RESET:
return pd_process_recv_hard_reset(pd_port, pd_event);
case PD_HW_TX_FAILED:
return pd_process_hw_msg_tx_failed(pd_port, pd_event);
default:
return false;
};
}
/*
* [BLOCK] Porcess Timer MSG
*/
#ifdef CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT
static inline bool pd_check_rx_pending(struct pd_port *pd_port)
{
uint32_t alert;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (tcpci_get_alert_status(tcpc, &alert))
return false;
if (alert & TCPC_REG_ALERT_RX_STATUS) {
PE_INFO("rx_pending\n");
#ifndef CONFIG_USB_PD_ONLY_PRINT_SYSTEM_BUSY
pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE);
#endif
return true;
}
return false;
}
#endif /* CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT */
static inline bool pd_process_timer_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
uint8_t ready_state = pe_get_curr_ready_state(pd_port);
switch (pd_event->msg) {
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
case PD_TIMER_SENDER_RESPONSE:
#ifdef CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT
#ifndef CONFIG_USB_PD_ONLY_PRINT_SYSTEM_BUSY
if (pd_check_rx_pending(pd_port))
return false;
#else
pd_check_rx_pending(pd_port);
#endif /* CONFIG_USB_PD_PRINT_SYSTEM_BUSY */
#endif /* CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT */
pd_cancel_dpm_reaction(pd_port);
pd_notify_pe_cancel_pr_swap(pd_port);
pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_TIMEOUT);
if (pd_port->pe_data.pe_state_flags &
PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT) {
PE_TRANSIT_STATE(pd_port, ready_state);
return true;
}
if (pd_port->pe_data.pe_state_flags &
PE_STATE_FLAG_HRESET_IF_SR_TIMEOUT) {
pe_transit_hard_reset_state(pd_port);
return true;
}
break;
#endif /* CONFIG_USB_PD_DBG_IGRONE_TIMEOUT */
case PD_TIMER_BIST_CONT_MODE:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_BIST_CARRIER_MODE_2, ready_state))
return true;
break;
#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY
case PD_TIMER_DFP_FLOW_DELAY:
if (pd_port->pe_state_curr == ready_state &&
pd_port->data_role == PD_ROLE_DFP) {
dpm_reaction_set_clear(pd_port,
DPM_REACTION_CAP_READY_ONCE,
DPM_REACTION_DFP_FLOW_DELAY);
}
break;
#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY */
#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY
case PD_TIMER_UFP_FLOW_DELAY:
if (pd_port->pe_state_curr == ready_state &&
pd_port->data_role == PD_ROLE_UFP) {
dpm_reaction_set_clear(pd_port,
DPM_REACTION_CAP_READY_ONCE,
DPM_REACTION_UFP_FLOW_DELAY);
}
break;
#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */
#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY
case PD_TIMER_VCONN_STABLE:
if (pd_port->vconn_role == PD_ROLE_VCONN_DYNAMIC_ON) {
pd_set_vconn(pd_port, PD_ROLE_VCONN_ON);
dpm_reaction_set_clear(pd_port,
DPM_REACTION_CAP_READY_ONCE,
DPM_REACTION_VCONN_STABLE_DELAY);
}
break;
#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */
#if defined(CONFIG_USB_PD_REV30) && defined(CONFIG_USB_PD_REV30_COLLISION_AVOID)
case PD_TIMER_DEFERRED_EVT:
pd_notify_tcp_event_buf_reset(
pd_port, TCP_DPM_RET_DROP_PE_BUSY);
break;
#endif
default:
break;
}
return false;
}
bool pd_process_event_com(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->event_type) {
case PD_EVT_CTRL_MSG:
return pd_process_ctrl_msg(pd_port, pd_event);
case PD_EVT_DATA_MSG:
return pd_process_data_msg(pd_port, pd_event);
#ifdef CONFIG_USB_PD_REV30
case PD_EVT_EXT_MSG:
return pd_process_ext_msg(pd_port, pd_event);
#endif /* CONFIG_USB_PD_REV30 */
case PD_EVT_DPM_MSG:
return pd_process_dpm_msg(pd_port, pd_event);
case PD_EVT_HW_MSG:
return pd_process_hw_msg(pd_port, pd_event);
case PD_EVT_TIMER_MSG:
return pd_process_timer_msg(pd_port, pd_event);
default:
return false;
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Process Event For DBGACC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
bool pd_process_event_dbg(struct pd_port *pd_port, struct pd_event *pd_event)
{
/* Don't need to handle any PD message, Keep VBUS 5V, and using VDM */
return false;
}
#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */

View file

@ -0,0 +1,102 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Process Event For DRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
/* PD Control MSG reactions */
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOOD_CRC) = {
{ PE_DRS_DFP_UFP_ACCEPT_DR_SWAP, PE_DRS_DFP_UFP_CHANGE_TO_UFP },
{ PE_DRS_UFP_DFP_ACCEPT_DR_SWAP, PE_DRS_UFP_DFP_CHANGE_TO_DFP },
};
DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOOD_CRC);
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
{ PE_DRS_DFP_UFP_SEND_DR_SWAP, PE_DRS_DFP_UFP_CHANGE_TO_UFP },
{ PE_DRS_UFP_DFP_SEND_DR_SWAP, PE_DRS_UFP_DFP_CHANGE_TO_DFP },
};
DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
/* DPM Event reactions */
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
{ PE_DRS_DFP_UFP_EVALUATE_DR_SWAP, PE_DRS_DFP_UFP_ACCEPT_DR_SWAP },
{ PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, PE_DRS_UFP_DFP_ACCEPT_DR_SWAP },
};
DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
{ PE_DRS_DFP_UFP_EVALUATE_DR_SWAP, PE_DRS_DFP_UFP_REJECT_DR_SWAP },
{ PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, PE_DRS_UFP_DFP_REJECT_DR_SWAP },
};
DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
/*
* [BLOCK] Porcess PD Ctrl MSG
*/
static inline bool pd_process_ctrl_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_CTRL_GOOD_CRC:
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOOD_CRC);
case PD_CTRL_ACCEPT:
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT);
default:
return false;
}
}
/*
* [BLOCK] Porcess DPM MSG
*/
static inline bool pd_process_dpm_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_DPM_ACK:
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
case PD_DPM_NAK:
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
}
return false;
}
/*
* [BLOCK] Process Policy Engine's DRS Message
*/
bool pd_process_event_drs(struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->event_type) {
case PD_EVT_CTRL_MSG:
return pd_process_ctrl_msg(pd_port, pd_event);
case PD_EVT_DPM_MSG:
return pd_process_dpm_msg(pd_port, pd_event);
default:
return false;
}
}

View file

@ -0,0 +1,242 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Process Event For PRS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
#include "inc/pd_dpm_core.h"
#ifdef CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY
#define PE_PRS_SNK_HARD_RESET PE_ERROR_RECOVERY
#define PE_PRS_SRC_HARD_RESET PE_ERROR_RECOVERY
#else
#define PE_PRS_SNK_HARD_RESET PE_SNK_HARD_RESET
#define PE_PRS_SRC_HARD_RESET PE_SRC_HARD_RESET
#endif /* CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY */
/* PD Control MSG reactions */
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOOD_CRC) = {
{ PE_PRS_SRC_SNK_ACCEPT_PR_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF },
{ PE_PRS_SNK_SRC_ACCEPT_PR_SWAP, PE_PRS_SNK_SRC_TRANSITION_TO_OFF },
/* VBUS-ON & PS_RDY SENT */
{ PE_PRS_SNK_SRC_SOURCE_ON, PE_SRC_STARTUP },
};
DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOOD_CRC);
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
{ PE_PRS_SRC_SNK_SEND_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF },
{ PE_PRS_SNK_SRC_SEND_SWAP, PE_PRS_SNK_SRC_TRANSITION_TO_OFF },
};
DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_PS_RDY) = {
{ PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_SNK_STARTUP },
{ PE_PRS_SNK_SRC_TRANSITION_TO_OFF, PE_PRS_SNK_SRC_ASSERT_RP },
};
DECL_PE_STATE_REACTION(PD_CTRL_MSG_PS_RDY);
/* DPM Event reactions */
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
{ PE_PRS_SRC_SNK_EVALUATE_PR_SWAP, PE_PRS_SRC_SNK_ACCEPT_PR_SWAP },
{ PE_PRS_SNK_SRC_EVALUATE_PR_SWAP, PE_PRS_SNK_SRC_ACCEPT_PR_SWAP },
{ PE_PRS_SRC_SNK_ASSERT_RD, PE_PRS_SRC_SNK_WAIT_SOURCE_ON },
{ PE_PRS_SNK_SRC_ASSERT_RP, PE_PRS_SNK_SRC_SOURCE_ON },
};
DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = {
{ PE_PRS_SRC_SNK_EVALUATE_PR_SWAP, PE_PRS_SRC_SNK_REJECT_PR_SWAP },
{ PE_PRS_SNK_SRC_EVALUATE_PR_SWAP, PE_PRS_SNK_SRC_REJECT_SWAP },
};
DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK);
/* HW Event reactions */
DECL_PE_STATE_TRANSITION(PD_HW_VBUS_PRESENT) = {
#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP
{ PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_SNK_STARTUP },
#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */
};
DECL_PE_STATE_REACTION(PD_HW_VBUS_PRESENT);
#ifdef CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY
DECL_PE_STATE_TRANSITION(PD_HW_TX_FAILED) = {
{ PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_ERROR_RECOVERY },
{ PE_PRS_SNK_SRC_SOURCE_ON, PE_ERROR_RECOVERY },
};
DECL_PE_STATE_REACTION(PD_HW_TX_FAILED);
#endif /* CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY */
DECL_PE_STATE_TRANSITION(PD_HW_VBUS_SAFE0V) = {
{ PE_PRS_SRC_SNK_TRANSITION_TO_OFF, PE_PRS_SRC_SNK_ASSERT_RD },
#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP
{ PE_PRS_SNK_SRC_TRANSITION_TO_OFF, PE_PRS_SNK_SRC_ASSERT_RP },
#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */
};
DECL_PE_STATE_REACTION(PD_HW_VBUS_SAFE0V);
/*
* [BLOCK] Porcess PD Ctrl MSG
*/
static inline bool pd_process_ctrl_msg_good_crc(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_port->pe_state_curr) {
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_SOURCE_ON);
pd_unlock_msg_output(pd_port); /* for tSRCTransition */
return false;
default:
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOOD_CRC);
}
}
static inline bool pd_process_ctrl_msg_ps_rdy(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_port->pe_state_curr) {
#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
pd_enable_vbus_valid_detection(pd_port, true);
return false;
case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
pd_enable_vbus_safe0v_detection(pd_port);
return false;
#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */
default:
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_PS_RDY);
}
}
static inline bool pd_process_ctrl_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_CTRL_GOOD_CRC:
return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
case PD_CTRL_ACCEPT:
return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT);
case PD_CTRL_PS_RDY:
return pd_process_ctrl_msg_ps_rdy(pd_port, pd_event);
default:
return false;
}
}
/*
* [BLOCK] Porcess DPM MSG
*/
static inline bool pd_process_dpm_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_DPM_ACK:
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
case PD_DPM_NAK:
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK);
}
return false;
}
/*
* [BLOCK] Porcess HW MSG
*/
static inline bool pd_process_hw_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_HW_VBUS_PRESENT:
if (pd_port->pe_state_curr == PE_PRS_SNK_SRC_SOURCE_ON)
pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY);
return PE_MAKE_STATE_TRANSIT(PD_HW_VBUS_PRESENT);
#ifdef CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY
case PD_HW_TX_FAILED:
return PE_MAKE_STATE_TRANSIT(PD_HW_TX_FAILED);
#endif /* CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY */
case PD_HW_VBUS_SAFE0V:
return PE_MAKE_STATE_TRANSIT(PD_HW_VBUS_SAFE0V);
default:
return false;
}
}
/*
* [BLOCK] Porcess Timer MSG
*/
static inline bool pd_process_timer_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_TIMER_PS_SOURCE_ON:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_PRS_SNK_HARD_RESET);
case PD_TIMER_PS_SOURCE_OFF:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_PRS_SNK_SRC_TRANSITION_TO_OFF,
PE_PRS_SNK_HARD_RESET);
case PD_TIMER_SOURCE_TRANSITION:
pd_dpm_prs_enable_power_source(pd_port, false);
return false;
default:
return false;
}
}
/*
* [BLOCK] Process Policy Engine's PRS Message
*/
bool pd_process_event_prs(struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->event_type) {
case PD_EVT_CTRL_MSG:
return pd_process_ctrl_msg(pd_port, pd_event);
case PD_EVT_DPM_MSG:
return pd_process_dpm_msg(pd_port, pd_event);
case PD_EVT_HW_MSG:
return pd_process_hw_msg(pd_port, pd_event);
case PD_EVT_TIMER_MSG:
return pd_process_timer_msg(pd_port, pd_event);
default:
return false;
}
}

View file

@ -0,0 +1,498 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Process Event For SNK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/pd_dpm_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
#include "inc/tcpci_typec.h"
/* PD Control MSG reactions */
DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = {
{ PE_SNK_SELECT_CAPABILITY, PE_SNK_TRANSITION_SINK },
{ PE_SNK_SEND_SOFT_RESET, PE_SNK_WAIT_FOR_CAPABILITIES },
};
DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT);
/* PD Data MSG reactions */
DECL_PE_STATE_TRANSITION(PD_DATA_MSG_SOURCE_CAP) = {
{ PE_SNK_WAIT_FOR_CAPABILITIES, PE_SNK_EVALUATE_CAPABILITY },
{ PE_SNK_READY, PE_SNK_EVALUATE_CAPABILITY },
/* PR-Swap issue (Check it later) */
{ PE_SNK_STARTUP, PE_SNK_EVALUATE_CAPABILITY },
{ PE_SNK_DISCOVERY, PE_SNK_EVALUATE_CAPABILITY },
#ifdef CONFIG_USB_PD_TCPM_CB_2ND
{ PE_SNK_GET_SOURCE_CAP, PE_SNK_EVALUATE_CAPABILITY },
#endif /* CONFIG_USB_PD_TCPM_CB_2ND */
};
DECL_PE_STATE_REACTION(PD_DATA_MSG_SOURCE_CAP);
/*
* [BLOCK] Porcess Ctrl MSG
*/
static bool pd_process_ctrl_msg_get_source_cap(
struct pd_port *pd_port, uint8_t next)
{
if (pd_port->pe_state_curr != PE_SNK_READY)
return false;
#ifdef CONFIG_USB_PD_PR_SWAP
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
PE_TRANSIT_STATE(pd_port, next);
return true;
}
#endif /* CONFIG_USB_PD_PR_SWAP */
if (!pd_check_rev30(pd_port)) {
PE_TRANSIT_STATE(pd_port, PE_REJECT);
return true;
}
return false;
}
static inline bool pd_process_ctrl_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#ifdef CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
switch (pd_port->pe_state_curr) {
case PE_SNK_GET_SOURCE_CAP:
#ifdef CONFIG_USB_PD_PR_SWAP
case PE_DR_SNK_GET_SINK_CAP:
#endif /* CONFIG_USB_PD_PR_SWAP */
if (pd_event->msg >= PD_CTRL_GET_SOURCE_CAP &&
pd_event->msg <= PD_CTRL_VCONN_SWAP) {
PE_DBG("Port Partner Request First\n");
pd_port->pe_state_curr = PE_SNK_READY;
pd_disable_timer(
pd_port, PD_TIMER_SENDER_RESPONSE);
}
break;
}
#endif /* CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST */
switch (pd_event->msg) {
case PD_CTRL_GOOD_CRC:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_SOFT_RESET, PE_SNK_WAIT_FOR_CAPABILITIES);
case PD_CTRL_GOTO_MIN:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_READY, PE_SNK_TRANSITION_SINK))
return true;
break;
case PD_CTRL_ACCEPT:
if (PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT))
return true;
break;
case PD_CTRL_PS_RDY:
switch (pd_port->pe_state_curr) {
case PE_SNK_TRANSITION_SINK:
pd_dpm_snk_transition_power(pd_port);
PE_TRANSIT_STATE(pd_port, PE_SNK_READY);
return true;
#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
return false;
#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */
default:
break;
}
break;
case PD_CTRL_GET_SOURCE_CAP:
if (pd_process_ctrl_msg_get_source_cap(
pd_port, PE_DR_SNK_GIVE_SOURCE_CAP))
return true;
break;
case PD_CTRL_GET_SINK_CAP:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_READY, PE_SNK_GIVE_SINK_CAP))
return true;
break;
case PD_CTRL_REJECT:
case PD_CTRL_WAIT:
if (pd_port->pe_state_curr == PE_SNK_SELECT_CAPABILITY) {
if (pd_port->pe_data.explicit_contract)
PE_TRANSIT_STATE(pd_port, PE_SNK_READY);
else {
PE_TRANSIT_STATE(pd_port,
PE_SNK_WAIT_FOR_CAPABILITIES);
}
return true;
}
break;
#ifdef CONFIG_USB_PD_REV30
case PD_CTRL_NOT_SUPPORTED:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_READY, PE_SNK_NOT_SUPPORTED_RECEIVED))
return true;
break;
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
case PD_CTRL_GET_SOURCE_CAP_EXT:
if (pd_process_ctrl_msg_get_source_cap(
pd_port, PE_DR_SNK_GIVE_SOURCE_CAP_EXT))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
case PD_CTRL_GET_STATUS:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_READY, PE_SNK_GIVE_SINK_STATUS))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
#endif /* CONFIG_USB_PD_REV30 */
default:
pd_port->curr_unsupported_msg = true;
break;
}
return pd_process_protocol_error(pd_port, pd_event);
}
/*
* [BLOCK] Porcess Data MSG
*/
static inline bool pd_process_data_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_DATA_SOURCE_CAP:
#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
pd_port->msg_id_pr_swap_last = 0xff;
#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */
if (PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_SOURCE_CAP))
return true;
break;
#ifdef CONFIG_USB_PD_PR_SWAP
case PD_DATA_SINK_CAP:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_DR_SNK_GET_SINK_CAP, PE_SNK_READY))
return true;
break;
#endif /* CONFIG_USB_PD_PR_SWAP */
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
case PD_DATA_ALERT:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_READY, PE_SNK_SOURCE_ALERT_RECEIVED))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#endif /* CONFIG_USB_PD_REV30 */
default:
pd_port->curr_unsupported_msg = true;
break;
}
return pd_process_protocol_error(pd_port, pd_event);
}
/*
* [BLOCK] Porcess Extend MSG
*/
#ifdef CONFIG_USB_PD_REV30
static inline bool pd_process_ext_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
case PD_EXT_SOURCE_CAP_EXT:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_GET_SOURCE_CAP_EXT, PE_SNK_READY))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
case PD_EXT_STATUS:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_GET_SOURCE_STATUS, PE_SNK_READY))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
case PD_EXT_PPS_STATUS:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_GET_PPS_STATUS, PE_SNK_READY))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
default:
pd_port->curr_unsupported_msg = true;
break;
}
return pd_process_protocol_error(pd_port, pd_event);
}
#endif /* CONFIG_USB_PD_REV30 */
/*
* [BLOCK] Porcess DPM MSG
*/
static inline bool pd_process_dpm_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_DPM_ACK:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_EVALUATE_CAPABILITY, PE_SNK_SELECT_CAPABILITY);
default:
return false;
}
}
/*
* [BLOCK] Porcess HW MSG
*/
static inline bool pd_process_hw_msg_sink_tx_change(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
struct pe_data *pe_data = &pd_port->pe_data;
uint8_t pd_traffic;
#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP
if (pe_data->pd_traffic_control == PD_SINK_TX_START)
return false;
#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */
if (!pd_check_rev30(pd_port))
return false;
pd_traffic = pd_event->msg_sec ?
PD_SINK_TX_OK : PD_SINK_TX_NG;
if (pe_data->pd_traffic_control == pd_traffic)
return false;
pe_data->pd_traffic_control = pd_traffic;
dpm_reaction_set_ready_once(pd_port);
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
return false;
}
static inline bool pd_process_vbus_absent(struct pd_port *pd_port)
{
if (pd_port->pe_state_curr != PE_SNK_DISCOVERY)
return false;
#ifdef CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW
/* iSafe0mA: Maximum current a Sink
* is allowed to draw when VBUS is driven to vSafe0V
*/
pd_dpm_sink_vbus(pd_port, false);
#endif /* CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW */
pd_disable_pe_state_timer(pd_port);
pd_enable_vbus_valid_detection(pd_port, true);
return false;
}
static inline bool pd_process_hw_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_HW_VBUS_PRESENT:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_DISCOVERY, PE_SNK_WAIT_FOR_CAPABILITIES);
case PD_HW_VBUS_ABSENT:
return pd_process_vbus_absent(pd_port);
case PD_HW_TX_FAILED:
return pd_process_tx_failed(pd_port);
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
case PD_HW_SINK_TX_CHANGE:
return pd_process_hw_msg_sink_tx_change(pd_port, pd_event);
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
};
return false;
}
/*
* [BLOCK] Porcess PE MSG
*/
static inline bool pd_process_pe_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_PE_RESET_PRL_COMPLETED:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_STARTUP, PE_SNK_DISCOVERY);
case PD_PE_HARD_RESET_COMPLETED:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_HARD_RESET, PE_SNK_TRANSITION_TO_DEFAULT);
case PD_PE_POWER_ROLE_AT_DEFAULT:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_TRANSITION_TO_DEFAULT, PE_SNK_STARTUP);
default:
return false;
}
}
/*
* [BLOCK] Porcess Timer MSG
*/
static inline void pd_report_typec_only_charger(struct pd_port *pd_port)
{
uint8_t state;
struct tcpc_device *tcpc = pd_port->tcpc;
if (tcpc->typec_remote_rp_level == TYPEC_CC_VOLT_SNK_DFT)
state = PD_CONNECT_TYPEC_ONLY_SNK_DFT;
else
state = PD_CONNECT_TYPEC_ONLY_SNK;
PE_INFO("TYPE-C Only Charger!\n");
pd_dpm_sink_vbus(pd_port, true);
pd_set_rx_enable(pd_port, PD_RX_CAP_PE_IDLE);
pd_notify_pe_hard_reset_completed(pd_port);
pd_update_connect_state(pd_port, state);
}
static inline bool pd_process_timer_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
#endif /* CONFIG_USB_PD_DBG_IGRONE_TIMEOUT */
struct pe_data __maybe_unused *pe_data = &pd_port->pe_data;
switch (pd_event->msg) {
case PD_TIMER_SINK_REQUEST:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_READY, PE_SNK_SELECT_CAPABILITY);
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
case PD_TIMER_SINK_WAIT_CAP:
case PD_TIMER_PS_TRANSITION:
if ((pd_port->pe_state_curr != PE_SNK_DISCOVERY) &&
(pe_data->hard_reset_counter <= PD_HARD_RESET_COUNT)) {
PE_TRANSIT_STATE(pd_port, PE_SNK_HARD_RESET);
return true;
}
PE_INFO("SRC NoResp\n");
if (pd_port->request_v == TCPC_VBUS_SINK_5V) {
pd_report_typec_only_charger(pd_port);
} else {
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
return true;
}
break;
#endif /* CONFIG_USB_PD_DBG_IGRONE_TIMEOUT */
#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
case PD_TIMER_DISCOVER_ID:
vdm_put_dpm_discover_cable_event(pd_port);
break;
#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
/* fall through */
#ifdef CONFIG_USB_PD_REV30
case PD_TIMER_CK_NOT_SUPPORTED:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SNK_CHUNK_RECEIVED, PE_SNK_SEND_NOT_SUPPORTED))
return true;
/* fall through */
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP
case PD_TIMER_SNK_FLOW_DELAY:
if (pe_data->pd_traffic_control == PD_SINK_TX_START) {
if (typec_get_cc_res() == TYPEC_CC_VOLT_SNK_3_0)
pe_data->pd_traffic_control = PD_SINK_TX_OK;
else
pe_data->pd_traffic_control = PD_SINK_TX_NG;
if (pd_check_rev30(pd_port))
dpm_reaction_set_ready_once(pd_port);
}
break;
#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
#endif /* CONFIG_USB_PD_REV30 */
}
return false;
}
/*
* [BLOCK] Process Policy Engine's SNK Message
*/
bool pd_process_event_snk(struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->event_type) {
case PD_EVT_CTRL_MSG:
return pd_process_ctrl_msg(pd_port, pd_event);
case PD_EVT_DATA_MSG:
return pd_process_data_msg(pd_port, pd_event);
#ifdef CONFIG_USB_PD_REV30
case PD_EVT_EXT_MSG:
return pd_process_ext_msg(pd_port, pd_event);
#endif /* CONFIG_USB_PD_REV30 */
case PD_EVT_DPM_MSG:
return pd_process_dpm_msg(pd_port, pd_event);
case PD_EVT_HW_MSG:
return pd_process_hw_msg(pd_port, pd_event);
case PD_EVT_PE_MSG:
return pd_process_pe_msg(pd_port, pd_event);
case PD_EVT_TIMER_MSG:
return pd_process_timer_msg(pd_port, pd_event);
default:
return false;
}
}

View file

@ -0,0 +1,550 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Process Event For SRC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
#include "inc/pd_dpm_core.h"
/* PD Data MSG reactions */
DECL_PE_STATE_TRANSITION(PD_DATA_MSG_REQUEST) = {
{ PE_SRC_SEND_CAPABILITIES, PE_SRC_NEGOTIATE_CAPABILITIES },
{ PE_SRC_READY, PE_SRC_NEGOTIATE_CAPABILITIES },
};
DECL_PE_STATE_REACTION(PD_DATA_MSG_REQUEST);
/* DPM Event reactions */
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
{ PE_SRC_NEGOTIATE_CAPABILITIES, PE_SRC_TRANSITION_SUPPLY },
};
DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_CAP_CHANGED) = {
{ PE_SRC_READY, PE_SRC_SEND_CAPABILITIES },
{ PE_SRC_WAIT_NEW_CAPABILITIES, PE_SRC_SEND_CAPABILITIES },
};
DECL_PE_STATE_REACTION(PD_DPM_MSG_CAP_CHANGED);
/* Timer Event reactions */
DECL_PE_STATE_TRANSITION(PD_TIMER_PS_HARD_RESET) = {
{ PE_SRC_HARD_RESET, PE_SRC_TRANSITION_TO_DEFAULT },
{ PE_SRC_HARD_RESET_RECEIVED, PE_SRC_TRANSITION_TO_DEFAULT },
};
DECL_PE_STATE_REACTION(PD_TIMER_PS_HARD_RESET);
/*
* [BLOCK] Porcess Ctrl MSG
*/
static inline bool pd_process_ctrl_msg_good_crc(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_port->pe_state_curr) {
case PE_SRC_SEND_CAPABILITIES:
pd_port->pe_data.cap_counter = 0;
pd_handle_hard_reset_recovery(pd_port);
return false;
case PE_SRC_TRANSITION_SUPPLY:
pd_enable_pe_state_timer(pd_port, PD_TIMER_SOURCE_TRANSITION);
return false;
case PE_SRC_CAPABILITY_RESPONSE:
if (!pd_port->pe_data.explicit_contract)
PE_TRANSIT_STATE(pd_port, PE_SRC_WAIT_NEW_CAPABILITIES);
else if (pd_port->pe_data.invalid_contract)
PE_TRANSIT_STATE(pd_port, PE_SRC_HARD_RESET);
else
PE_TRANSIT_STATE(pd_port, PE_SRC_READY);
return true;
case PE_SRC_SOFT_RESET:
PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES);
return true;
default:
return false;
}
}
static inline bool pd_process_ctrl_msg_get_sink_cap(
struct pd_port *pd_port, struct pd_event *pd_event)
{
if (pd_port->pe_state_curr != PE_SRC_READY)
return false;
#ifdef CONFIG_USB_PD_PR_SWAP
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
PE_TRANSIT_STATE(pd_port, PE_DR_SRC_GIVE_SINK_CAP);
return true;
}
#endif /* CONFIG_USB_PD_PR_SWAP */
if (!pd_check_rev30(pd_port)) {
PE_TRANSIT_STATE(pd_port, PE_REJECT);
return true;
}
return false;
}
static inline bool pd_process_ctrl_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#ifdef CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
switch (pd_port->pe_state_curr) {
case PE_SRC_GET_SINK_CAP:
#ifdef CONFIG_USB_PD_PR_SWAP
case PE_DR_SRC_GET_SOURCE_CAP:
#endif /* CONFIG_USB_PD_PR_SWAP */
if (pd_event->msg >= PD_CTRL_GET_SOURCE_CAP &&
pd_event->msg <= PD_CTRL_VCONN_SWAP) {
PE_DBG("Port Partner Request First\n");
pd_port->pe_state_curr = PE_SRC_READY;
pd_disable_timer(
pd_port, PD_TIMER_SENDER_RESPONSE);
}
break;
}
#endif /* CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST */
switch (pd_event->msg) {
case PD_CTRL_GOOD_CRC:
return pd_process_ctrl_msg_good_crc(pd_port, pd_event);
case PD_CTRL_ACCEPT:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_SEND_SOFT_RESET, PE_SRC_SEND_CAPABILITIES))
return true;
break;
case PD_CTRL_GET_SOURCE_CAP:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_READY, PE_SRC_SEND_CAPABILITIES))
return true;
break;
case PD_CTRL_GET_SINK_CAP:
if (pd_process_ctrl_msg_get_sink_cap(pd_port, pd_event))
return true;
break;
#ifdef CONFIG_USB_PD_REV30
case PD_CTRL_NOT_SUPPORTED:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_READY, PE_SRC_NOT_SUPPORTED_RECEIVED))
return true;
break;
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
case PD_CTRL_GET_SOURCE_CAP_EXT:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_READY, PE_SRC_GIVE_SOURCE_CAP_EXT))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
case PD_CTRL_GET_STATUS:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_READY, PE_SRC_GIVE_SOURCE_STATUS))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE
case PD_CTRL_GET_PPS_STATUS:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_READY, PE_SRC_GIVE_PPS_STATUS))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */
#endif /* CONFIG_USB_PD_REV30 */
default:
pd_port->curr_unsupported_msg = true;
break;
}
return pd_process_protocol_error(pd_port, pd_event);
}
/*
* [BLOCK] Porcess Data MSG
*/
static inline bool pd_process_data_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
#ifdef CONFIG_USB_PD_PR_SWAP
case PD_DATA_SOURCE_CAP:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_DR_SRC_GET_SOURCE_CAP, PE_SRC_READY))
return true;
break;
#endif /* CONFIG_USB_PD_PR_SWAP */
case PD_DATA_SINK_CAP:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_GET_SINK_CAP, PE_SRC_READY))
return true;
break;
case PD_DATA_REQUEST:
if (PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_REQUEST))
return true;
break;
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
case PD_DATA_ALERT:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_READY, PE_SRC_SINK_ALERT_RECEIVED))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#endif /* CONFIG_USB_PD_REV30 */
default:
pd_port->curr_unsupported_msg = true;
break;
}
return pd_process_protocol_error(pd_port, pd_event);
}
/*
* [BLOCK] Porcess Extend MSG
*/
#ifdef CONFIG_USB_PD_REV30
static inline bool pd_process_ext_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
case PD_EXT_SOURCE_CAP_EXT:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_DR_SRC_GET_SOURCE_CAP_EXT, PE_SRC_READY))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
case PD_EXT_STATUS:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_GET_SINK_STATUS, PE_SRC_READY))
return true;
break;
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
default:
pd_port->curr_unsupported_msg = true;
break;
}
return pd_process_protocol_error(pd_port, pd_event);
}
#endif /* CONFIG_USB_PD_REV30 */
/*
* [BLOCK] Porcess DPM MSG
*/
static inline bool pd_process_dpm_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_DPM_ACK:
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
case PD_DPM_NAK:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_NEGOTIATE_CAPABILITIES,
PE_SRC_CAPABILITY_RESPONSE);
case PD_DPM_CAP_CHANGED:
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_CAP_CHANGED);
default:
return false;
}
}
/*
* [BLOCK] Porcess HW MSG
*/
static inline bool pd_process_hw_msg_vbus_present(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_port->pe_state_curr) {
case PE_SRC_STARTUP:
pd_enable_timer(pd_port, PD_TIMER_SOURCE_START);
break;
case PE_SRC_TRANSITION_TO_DEFAULT:
pd_put_pe_event(pd_port, PD_PE_POWER_ROLE_AT_DEFAULT);
break;
}
return false;
}
static inline bool pd_process_hw_msg_tx_failed(
struct pd_port *pd_port, struct pd_event *pd_event)
{
struct pe_data *pe_data = &pd_port->pe_data;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_port->pe_state_curr == PE_SRC_SEND_CAPABILITIES) {
if (pe_data->pd_connected) {
if (!pe_data->explicit_contract) {
PE_DBG("PR_SWAP NoResp\n");
return false;
}
} else {
PE_TRANSIT_STATE(pd_port, PE_SRC_DISCOVERY);
return true;
}
}
#ifdef CONFIG_PD_SRC_RESET_CABLE
if (pd_port->pe_state_curr == PE_SRC_CBL_SEND_SOFT_RESET) {
PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES);
return true;
}
#endif /* CONFIG_PD_SRC_RESET_CABLE */
return pd_process_tx_failed(pd_port);
}
static inline bool pd_process_hw_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_HW_VBUS_PRESENT:
return pd_process_hw_msg_vbus_present(pd_port, pd_event);
case PD_HW_VBUS_SAFE0V:
pd_enable_timer(pd_port, PD_TIMER_SRC_RECOVER);
return false;
case PD_HW_VBUS_STABLE:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_TRANSITION_SUPPLY, PE_SRC_TRANSITION_SUPPLY2);
case PD_HW_TX_FAILED:
return pd_process_hw_msg_tx_failed(pd_port, pd_event);
default:
return false;
};
}
/*
* [BLOCK] Porcess PE MSG
*/
static inline bool pd_process_pe_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_PE_RESET_PRL_COMPLETED:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_STARTUP, PE_SRC_SEND_CAPABILITIES);
case PD_PE_POWER_ROLE_AT_DEFAULT:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_TRANSITION_TO_DEFAULT, PE_SRC_STARTUP);
default:
return false;
}
}
/*
* [BLOCK] Porcess Timer MSG
*/
static inline bool pd_process_timer_msg_source_start(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
if (pd_is_discover_cable(pd_port) &&
pd_port->pe_data.msg_id_tx[TCPC_TX_SOP_PRIME] == 0) {
#ifdef CONFIG_PD_SRC_RESET_CABLE
if (pd_is_reset_cable(pd_port)) {
PE_TRANSIT_STATE(pd_port, PE_SRC_CBL_SEND_SOFT_RESET);
return true;
}
#endif /* CONFIG_PD_SRC_RESET_CABLE */
if (vdm_put_dpm_discover_cable_event(pd_port))
return false;
}
#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
switch (pd_port->pe_state_curr) {
case PE_SRC_STARTUP:
#ifdef CONFIG_PD_SRC_RESET_CABLE
case PE_SRC_CBL_SEND_SOFT_RESET:
#endif /* CONFIG_PD_SRC_RESET_CABLE */
PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES);
return true;
}
return false;
};
static inline bool pd_process_timer_msg_source_cap(
struct pd_port *pd_port, struct pd_event *pd_event)
{
if (pd_port->pe_state_curr != PE_SRC_DISCOVERY)
return false;
if (pd_port->pe_data.cap_counter <= PD_CAPS_COUNT)
PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES);
else /* in this state, PD always not connected */
PE_TRANSIT_STATE(pd_port, PE_SRC_DISABLED);
return true;
}
static inline bool pd_process_timer_msg_no_response(
struct pd_port *pd_port, struct pd_event *pd_event)
{
if (pd_port->pe_data.hard_reset_counter <= PD_HARD_RESET_COUNT)
PE_TRANSIT_STATE(pd_port, PE_SRC_HARD_RESET);
else if (pd_port->pe_data.pd_prev_connected)
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
else
PE_TRANSIT_STATE(pd_port, PE_SRC_DISABLED);
return true;
}
static inline bool pd_process_timer_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_TIMER_SOURCE_CAPABILITY:
return pd_process_timer_msg_source_cap(pd_port, pd_event);
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
#ifdef CONFIG_PD_SRC_RESET_CABLE
case PD_TIMER_SENDER_RESPONSE:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_CBL_SEND_SOFT_RESET, PE_SRC_SEND_CAPABILITIES);
#endif /* CONFIG_PD_SRC_RESET_CABLE */
#endif
case PD_TIMER_PS_HARD_RESET:
return PE_MAKE_STATE_TRANSIT(PD_TIMER_PS_HARD_RESET);
case PD_TIMER_SOURCE_START:
return pd_process_timer_msg_source_start(pd_port, pd_event);
#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT
case PD_TIMER_NO_RESPONSE:
return pd_process_timer_msg_no_response(pd_port, pd_event);
#endif
case PD_TIMER_SOURCE_TRANSITION:
if (pd_port->state_machine != PE_STATE_MACHINE_PR_SWAP)
pd_dpm_src_transition_power(pd_port);
break;
#ifdef CONFIG_PD_DISCOVER_CABLE_ID
case PD_TIMER_DISCOVER_ID:
vdm_put_dpm_discover_cable_event(pd_port);
break;
#endif /* CONFIG_PD_DISCOVER_CABLE_ID */
case PD_TIMER_SRC_RECOVER:
pd_dpm_source_vbus(pd_port, true);
pd_enable_vbus_valid_detection(pd_port, true);
break;
#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID
case PD_TIMER_SINK_TX:
if (pd_port->pe_data.pd_traffic_control == PD_SINK_TX_NG)
pd_port->pe_data.pd_traffic_control = PD_SOURCE_TX_OK;
#ifdef CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP
if (pd_port->pe_data.pd_traffic_control == PD_SOURCE_TX_START)
pd_port->pe_data.pd_traffic_control = PD_SINK_TX_OK;
#endif /* CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP */
break;
#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */
/* fall through */
#ifdef CONFIG_USB_PD_REV30
case PD_TIMER_CK_NOT_SUPPORTED:
return PE_MAKE_STATE_TRANSIT_SINGLE(
PE_SRC_CHUNK_RECEIVED, PE_SRC_SEND_NOT_SUPPORTED);
#endif /* CONFIG_USB_PD_REV30 */
}
return false;
}
/*
* [BLOCK] Process Policy Engine's SRC Message
*/
bool pd_process_event_src(struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->event_type) {
case PD_EVT_CTRL_MSG:
return pd_process_ctrl_msg(pd_port, pd_event);
case PD_EVT_DATA_MSG:
return pd_process_data_msg(pd_port, pd_event);
#ifdef CONFIG_USB_PD_REV30
case PD_EVT_EXT_MSG:
return pd_process_ext_msg(pd_port, pd_event);
#endif /* CONFIG_USB_PD_REV30 */
case PD_EVT_DPM_MSG:
return pd_process_dpm_msg(pd_port, pd_event);
case PD_EVT_HW_MSG:
return pd_process_hw_msg(pd_port, pd_event);
case PD_EVT_PE_MSG:
return pd_process_pe_msg(pd_port, pd_event);
case PD_EVT_TIMER_MSG:
return pd_process_timer_msg(pd_port, pd_event);
default:
return false;
}
}

View file

@ -0,0 +1,470 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Process Event for TCP
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
#include "inc/pd_dpm_core.h"
#ifdef CONFIG_USB_PD_PR_SWAP
static inline int pd_handle_tcp_event_pr_swap(
struct pd_port *pd_port, uint8_t new_role)
{
if (pd_port->power_role == new_role)
return TCP_DPM_RET_DENIED_SAME_ROLE;
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER))
return TCP_DPM_RET_DENIED_LOCAL_CAP;
if (!pd_check_pe_state_ready(pd_port))
return TCP_DPM_RET_DENIED_NOT_READY;
pd_port->pe_data.during_swap = false;
pd_port->state_machine = PE_STATE_MACHINE_PR_SWAP;
pe_transit_send_pr_swap_state(pd_port);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_USB_PD_PR_SWAP */
#ifdef CONFIG_USB_PD_DR_SWAP
static inline int pd_handle_tcp_event_dr_swap(
struct pd_port *pd_port, uint8_t new_role)
{
if (pd_port->data_role == new_role)
return TCP_DPM_RET_DENIED_SAME_ROLE;
if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA))
return TCP_DPM_RET_DENIED_LOCAL_CAP;
if (!pd_check_pe_state_ready(pd_port))
return TCP_DPM_RET_DENIED_NOT_READY;
pd_port->pe_data.during_swap = false;
pd_port->state_machine = PE_STATE_MACHINE_DR_SWAP;
PE_TRANSIT_DATA_STATE(pd_port,
PE_DRS_UFP_DFP_SEND_DR_SWAP,
PE_DRS_DFP_UFP_SEND_DR_SWAP);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_USB_PD_DR_SWAP */
#ifdef CONFIG_USB_PD_VCONN_SWAP
static inline int pd_handle_tcp_event_vconn_swap(
struct pd_port *pd_port, uint8_t new_role)
{
uint8_t old_role = pd_port->vconn_role ? 1 : 0;
if (old_role == new_role)
return TCP_DPM_RET_DENIED_SAME_ROLE;
if ((!pd_port->vconn_role) &&
(!(pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY)))
return TCP_DPM_RET_DENIED_LOCAL_CAP;
if (!pd_check_pe_state_ready(pd_port))
return TCP_DPM_RET_DENIED_NOT_READY;
pd_port->state_machine = PE_STATE_MACHINE_VCONN_SWAP;
PE_TRANSIT_STATE(pd_port, PE_VCS_SEND_SWAP);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_USB_PD_VCONN_SWAP */
#ifdef CONFIG_USB_PD_PE_SOURCE
static inline int pd_handle_tcp_event_gotomin(struct pd_port *pd_port)
{
if (pd_port->pe_state_curr != PE_SRC_READY)
return TCP_DPM_RET_DENIED_NOT_READY;
if (!(pd_port->pe_data.dpm_flags & DPM_FLAGS_PARTNER_GIVE_BACK))
return TCP_DPM_RET_DENIED_PARTNER_CAP;
PE_TRANSIT_STATE(pd_port, PE_SRC_TRANSITION_SUPPLY);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_USB_PD_PE_SOURCE */
static inline int pd_handle_tcp_event_softreset(struct pd_port *pd_port)
{
if (!pd_check_pe_state_ready(pd_port))
return TCP_DPM_RET_DENIED_NOT_READY;
pe_transit_soft_reset_state(pd_port);
return TCP_DPM_RET_SENT;
}
#ifdef CONFIG_PD_DFP_RESET_CABLE
static inline int pd_handle_tcp_event_cable_softreset(struct pd_port *pd_port)
{
bool role_check;
if (!pd_check_pe_state_ready(pd_port))
return TCP_DPM_RET_DENIED_NOT_READY;
role_check = pd_port->data_role == PD_ROLE_DFP;
if (pd_check_rev30(pd_port))
role_check = pd_port->vconn_role;
if (!role_check)
return TCP_DPM_RET_DENIED_WRONG_DATA_ROLE;
PE_TRANSIT_STATE(pd_port, PE_DFP_CBL_SEND_SOFT_RESET);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_PD_DFP_RESET_CABLE */
static inline int pd_handle_tcp_event_get_source_cap(struct pd_port *pd_port)
{
switch (pd_port->pe_state_curr) {
case PE_SNK_READY:
PE_TRANSIT_STATE(pd_port, PE_SNK_GET_SOURCE_CAP);
return TCP_DPM_RET_SENT;
#ifdef CONFIG_USB_PD_PR_SWAP
case PE_SRC_READY:
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
PE_TRANSIT_STATE(pd_port, PE_DR_SRC_GET_SOURCE_CAP);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_USB_PD_PR_SWAP */
return TCP_DPM_RET_DENIED_LOCAL_CAP;
}
return TCP_DPM_RET_DENIED_NOT_READY;
}
static inline int pd_handle_tcp_event_get_sink_cap(struct pd_port *pd_port)
{
switch (pd_port->pe_state_curr) {
case PE_SRC_READY:
PE_TRANSIT_STATE(pd_port, PE_SRC_GET_SINK_CAP);
return TCP_DPM_RET_SENT;
#ifdef CONFIG_USB_PD_PR_SWAP
case PE_SNK_READY:
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
PE_TRANSIT_STATE(pd_port, PE_DR_SNK_GET_SINK_CAP);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_USB_PD_PR_SWAP */
return TCP_DPM_RET_DENIED_LOCAL_CAP;
}
return TCP_DPM_RET_DENIED_NOT_READY;
}
#ifdef CONFIG_USB_PD_PE_SINK
static inline int pd_handle_tcp_event_request(struct pd_port *pd_port)
{
int ret = 0;
struct tcp_dpm_event *tcp_event = &pd_port->tcp_event;
if (pd_port->pe_state_curr != PE_SNK_READY)
return TCP_DPM_RET_DENIED_NOT_READY;
switch (pd_get_curr_pd_event(pd_port)->msg) {
case TCP_DPM_EVT_REQUEST:
ret = pd_dpm_update_tcp_request(
pd_port, &tcp_event->tcp_dpm_data.pd_req);
break;
case TCP_DPM_EVT_REQUEST_EX:
ret = pd_dpm_update_tcp_request_ex(
pd_port, &tcp_event->tcp_dpm_data.pd_req_ex);
break;
case TCP_DPM_EVT_REQUEST_AGAIN:
ret = pd_dpm_update_tcp_request_again(pd_port);
break;
}
if (ret != TCP_DPM_RET_SUCCESS)
return ret;
PE_TRANSIT_STATE(pd_port, PE_SNK_SELECT_CAPABILITY);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_USB_PD_PE_SINK */
static inline int pd_handle_tcp_event_bist_cm2(struct pd_port *pd_port)
{
uint32_t bist = BDO_MODE_CARRIER2;
if (!pd_check_pe_state_ready(pd_port))
return TCP_DPM_RET_DENIED_NOT_READY;
pd_send_sop_data_msg(pd_port, PD_DATA_BIST, 1, &bist);
return TCP_DPM_RET_SENT;
}
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
static inline int pd_handle_tcp_event_get_source_cap_ext(
struct pd_port *pd_port)
{
switch (pd_port->pe_state_curr) {
case PE_SNK_READY:
PE_TRANSIT_STATE(pd_port, PE_SNK_GET_SOURCE_CAP_EXT);
return TCP_DPM_RET_SENT;
#ifdef CONFIG_USB_PD_PR_SWAP
case PE_SRC_READY:
if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) {
PE_TRANSIT_STATE(pd_port,
PE_DR_SRC_GET_SOURCE_CAP_EXT);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_USB_PD_PR_SWAP */
return TCP_DPM_RET_DENIED_LOCAL_CAP;
}
return TCP_DPM_RET_DENIED_NOT_READY;
}
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
static inline int pd_handle_tcp_event_get_pps_status(struct pd_port *pd_port)
{
if (pd_port->pe_state_curr != PE_SNK_READY)
return TCP_DPM_RET_DENIED_NOT_READY;
PE_TRANSIT_STATE(pd_port, PE_SNK_GET_PPS_STATUS);
return TCP_DPM_RET_SENT;
}
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
static inline int pd_make_tcp_event_transit_ready(
struct pd_port *pd_port, uint8_t state)
{
if (!pd_check_pe_state_ready(pd_port))
return TCP_DPM_RET_DENIED_NOT_READY;
PE_TRANSIT_STATE(pd_port, state);
return TCP_DPM_RET_SENT;
}
static inline int pd_make_tcp_event_transit_ready2(
struct pd_port *pd_port, uint8_t snk_state, uint8_t src_state)
{
switch (pd_port->pe_state_curr) {
#ifdef CONFIG_USB_PD_PE_SINK
case PE_SNK_READY:
PE_TRANSIT_STATE(pd_port, snk_state);
return TCP_DPM_RET_SENT;
#endif /* CONFIG_USB_PD_PE_SINK */
#ifdef CONFIG_USB_PD_PE_SOURCE
case PE_SRC_READY:
PE_TRANSIT_STATE(pd_port, src_state);
return TCP_DPM_RET_SENT;
#endif /* CONFIG_USB_PD_PE_SOURCE */
}
return TCP_DPM_RET_DENIED_NOT_READY;
}
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
static inline int pd_handle_tcp_event_alert(struct pd_port *pd_port)
{
struct tcp_dpm_event *tcp_event = &pd_port->tcp_event;
if (pd_get_curr_pd_event(pd_port)->msg_sec == PD_TCP_FROM_TCPM)
pd_port->pe_data.local_alert |= tcp_event->tcp_dpm_data.index;
return pd_make_tcp_event_transit_ready2(pd_port,
PE_SNK_SEND_SINK_ALERT, PE_SRC_SEND_SOURCE_ALERT);
}
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
#endif /* CONFIG_USB_PD_REV30 */
static inline int pd_handle_tcp_event_hardreset(struct pd_port *pd_port)
{
pe_transit_hard_reset_state(pd_port);
return TCP_DPM_RET_SENT;
}
static inline int pd_handle_tcp_event_error_recovery(struct pd_port *pd_port)
{
PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY);
return TCP_DPM_RET_SENT;
}
static inline int pd_handle_tcp_dpm_event(
struct pd_port *pd_port, struct pd_event *pd_event)
{
int ret = TCP_DPM_RET_DENIED_UNKNOWN;
#ifdef CONFIG_USB_PD_REV30
if (pd_event->msg >= TCP_DPM_EVT_PD30_COMMAND
&& pd_event->msg < TCP_DPM_EVT_VDM_COMMAND) {
if (!pd_check_rev30(pd_port))
return TCP_DPM_RET_DENIED_PD_REV;
}
#endif /* CONFIG_USB_PD_REV30 */
switch (pd_event->msg) {
default:
break;
case TCP_DPM_EVT_PR_SWAP_AS_SNK:
case TCP_DPM_EVT_PR_SWAP_AS_SRC:
#ifdef CONFIG_USB_PD_PR_SWAP
ret = pd_handle_tcp_event_pr_swap(pd_port,
pd_event->msg - TCP_DPM_EVT_PR_SWAP_AS_SNK);
#endif /* CONFIG_USB_PD_PR_SWAP */
break;
case TCP_DPM_EVT_DR_SWAP_AS_UFP:
case TCP_DPM_EVT_DR_SWAP_AS_DFP:
#ifdef CONFIG_USB_PD_DR_SWAP
ret = pd_handle_tcp_event_dr_swap(pd_port,
pd_event->msg - TCP_DPM_EVT_DR_SWAP_AS_UFP);
#endif /* CONFIG_USB_PD_DR_SWAP */
break;
case TCP_DPM_EVT_VCONN_SWAP_OFF:
case TCP_DPM_EVT_VCONN_SWAP_ON:
#ifdef CONFIG_USB_PD_VCONN_SWAP
ret = pd_handle_tcp_event_vconn_swap(pd_port,
pd_event->msg - TCP_DPM_EVT_VCONN_SWAP_OFF);
#endif /* CONFIG_USB_PD_VCONN_SWAP */
break;
case TCP_DPM_EVT_GOTOMIN:
#ifdef CONFIG_USB_PD_PE_SOURCE
ret = pd_handle_tcp_event_gotomin(pd_port);
#endif /* CONFIG_USB_PD_PE_SOURCE */
break;
case TCP_DPM_EVT_SOFTRESET:
ret = pd_handle_tcp_event_softreset(pd_port);
break;
case TCP_DPM_EVT_CABLE_SOFTRESET:
#ifdef CONFIG_PD_DFP_RESET_CABLE
ret = pd_handle_tcp_event_cable_softreset(pd_port);
#endif /* CONFIG_PD_DFP_RESET_CABLE */
break;
case TCP_DPM_EVT_GET_SOURCE_CAP:
ret = pd_handle_tcp_event_get_source_cap(pd_port);
break;
case TCP_DPM_EVT_GET_SINK_CAP:
ret = pd_handle_tcp_event_get_sink_cap(pd_port);
break;
#ifdef CONFIG_USB_PD_PE_SINK
case TCP_DPM_EVT_REQUEST:
ret = pd_handle_tcp_event_request(pd_port);
break;
case TCP_DPM_EVT_REQUEST_EX:
ret = pd_handle_tcp_event_request(pd_port);
break;
case TCP_DPM_EVT_REQUEST_AGAIN:
ret = pd_handle_tcp_event_request(pd_port);
break;
#endif /* CONFIG_USB_PD_PE_SINK */
case TCP_DPM_EVT_BIST_CM2:
ret = pd_handle_tcp_event_bist_cm2(pd_port);
break;
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
case TCP_DPM_EVT_GET_SOURCE_CAP_EXT:
ret = pd_handle_tcp_event_get_source_cap_ext(pd_port);
break;
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
case TCP_DPM_EVT_GET_STATUS:
ret = pd_make_tcp_event_transit_ready2(pd_port,
PE_SNK_GET_SOURCE_STATUS, PE_SRC_GET_SINK_STATUS);
break;
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
case TCP_DPM_EVT_GET_COUNTRY_CODE:
ret = pd_make_tcp_event_transit_ready(
pd_port, PE_GET_COUNTRY_CODES);
break;
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
case TCP_DPM_EVT_GET_PPS_STATUS:
ret = pd_handle_tcp_event_get_pps_status(pd_port);
break;
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
case TCP_DPM_EVT_ALERT:
ret = pd_handle_tcp_event_alert(pd_port);
break;
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
case TCP_DPM_EVT_GET_COUNTRY_INFO:
ret = pd_make_tcp_event_transit_ready(
pd_port, PE_GET_COUNTRY_INFO);
break;
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
case TCP_DPM_EVT_GET_BAT_CAP:
ret = pd_make_tcp_event_transit_ready(
pd_port, PE_GET_BATTERY_CAP);
break;
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
case TCP_DPM_EVT_GET_BAT_STATUS:
ret = pd_make_tcp_event_transit_ready(
pd_port, PE_GET_BATTERY_STATUS);
break;
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
case TCP_DPM_EVT_GET_MFRS_INFO:
ret = pd_make_tcp_event_transit_ready(
pd_port, PE_GET_MANUFACTURER_INFO);
break;
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
#endif /* CONFIG_USB_PD_REV30 */
case TCP_DPM_EVT_HARD_RESET:
ret = pd_handle_tcp_event_hardreset(pd_port);
break;
case TCP_DPM_EVT_ERROR_RECOVERY:
ret = pd_handle_tcp_event_error_recovery(pd_port);
break;
}
return ret;
}
bool pd_process_event_tcp(struct pd_port *pd_port, struct pd_event *pd_event)
{
int ret = pd_handle_tcp_dpm_event(pd_port, pd_event);
pd_notify_tcp_event_1st_result(pd_port, ret);
return ret == TCP_DPM_RET_SENT;
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Process Event For VCS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
#ifdef CONFIG_USB_PD_VCONN_SWAP
/* DPM Event reactions */
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = {
{ PE_VCS_EVALUATE_SWAP, PE_VCS_ACCEPT_SWAP },
{ PE_VCS_TURN_ON_VCONN, PE_VCS_SEND_PS_RDY },
};
DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK);
/*
* [BLOCK] Porcess PD Ctrl MSG
*/
static inline bool pd_process_ctrl_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
uint8_t vconn_state = pd_port->vconn_role ?
PE_VCS_WAIT_FOR_VCONN : PE_VCS_TURN_ON_VCONN;
switch (pd_event->msg) {
case PD_CTRL_GOOD_CRC:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_VCS_ACCEPT_SWAP, vconn_state))
return true;
break;
case PD_CTRL_ACCEPT:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_VCS_SEND_SWAP, vconn_state))
return true;
break;
case PD_CTRL_PS_RDY:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_VCS_WAIT_FOR_VCONN, PE_VCS_TURN_OFF_VCONN))
return true;
break;
}
return false;
}
/*
* [BLOCK] Porcess DPM MSG
*/
static inline bool pd_process_dpm_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_DPM_ACK:
return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK);
case PD_DPM_NAK:
if (PE_MAKE_STATE_TRANSIT_SINGLE(
PE_VCS_EVALUATE_SWAP, PE_VCS_REJECT_VCONN_SWAP))
return true;
break;
}
return false;
}
/*
* [BLOCK] Porcess Timer MSG
*/
static inline bool pd_process_timer_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_TIMER_VCONN_ON:
if (PE_MAKE_STATE_TRANSIT_TO_HRESET(PE_VCS_WAIT_FOR_VCONN))
return true;
break;
#if CONFIG_USB_PD_VCONN_READY_TOUT != 0
case PD_TIMER_VCONN_READY:
PE_STATE_DPM_ACK_IMMEDIATELY(pd_port);
break;
#endif
}
return false;
}
/*
* [BLOCK] Process Policy Engine's VCS Message
*/
bool pd_process_event_vcs(struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->event_type) {
case PD_EVT_CTRL_MSG:
return pd_process_ctrl_msg(pd_port, pd_event);
case PD_EVT_DPM_MSG:
return pd_process_dpm_msg(pd_port, pd_event);
case PD_EVT_TIMER_MSG:
return pd_process_timer_msg(pd_port, pd_event);
default:
return false;
}
}
#endif /* CONFIG_USB_PD_VCONN_SWAP */

View file

@ -0,0 +1,966 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Power Delivery Process Event For VDM
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/pd_core.h"
#include "inc/tcpci_event.h"
#include "inc/pd_process_evt.h"
#include "inc/pd_dpm_core.h"
/* VDM reactions */
#define VDM_CMD_FLAG_CABLE_CMD (1<<0)
#define VDM_CMD_FLAG_SEND_BY_UFP (1<<1)
#define VDM_CMD_FLAG_SEND_BY_DFP (1<<2)
#define VDM_CMD_FLAG_RECV_BY_UFP (1<<3)
#define VDM_CMD_FLAG_RECV_BY_DFP (1<<4)
#define VDM_CMD_FLAG_PD30_DUPLEX (1<<5)
struct vdm_state_transition {
uint8_t vdm_cmd;
uint8_t vdm_init_state;
uint8_t vdm_request_state;
uint8_t vdm_cmd_flags;
};
#define VDM_CMD_REACTION(cmd, init, req, flags) { \
.vdm_cmd = cmd, \
.vdm_init_state = init, \
.vdm_request_state = req, \
.vdm_cmd_flags = flags, \
}
#define VDM_DFP_CMD_REACTION(cmd, init, req) \
VDM_CMD_REACTION(cmd, init, req, \
VDM_CMD_FLAG_SEND_BY_DFP | VDM_CMD_FLAG_RECV_BY_UFP)
#define VDM_UFP_CMD_REACTION(cmd, init, req) \
VDM_CMD_REACTION(cmd, init, req, \
VDM_CMD_FLAG_SEND_BY_UFP | VDM_CMD_FLAG_RECV_BY_DFP)
#define VDM_CABLE_CMD_REACTION(cmd, init, req) \
VDM_CMD_REACTION(cmd, init, req, \
VDM_CMD_FLAG_SEND_BY_UFP | VDM_CMD_FLAG_SEND_BY_DFP \
| VDM_CMD_FLAG_CABLE_CMD)
#define VDM_DFP_CMD_REACTION_PD30(cmd, init, req) \
VDM_CMD_REACTION(cmd, init, req, \
VDM_CMD_FLAG_SEND_BY_DFP | VDM_CMD_FLAG_RECV_BY_UFP | \
VDM_CMD_FLAG_PD30_DUPLEX)
#define VDM_UFP_CMD_REACTION_PD30(cmd, init, req) \
VDM_CMD_REACTION(cmd, init, req, \
VDM_CMD_FLAG_SEND_BY_UFP | VDM_CMD_FLAG_RECV_BY_DFP | \
VDM_CMD_FLAG_PD30_DUPLEX)
static const struct vdm_state_transition pe_vdm_state_reactions[] = {
VDM_DFP_CMD_REACTION_PD30(CMD_DISCOVER_IDENT,
PE_UFP_VDM_GET_IDENTITY,
PE_DFP_UFP_VDM_IDENTITY_REQUEST
),
VDM_DFP_CMD_REACTION_PD30(CMD_DISCOVER_SVID,
PE_UFP_VDM_GET_SVIDS,
PE_DFP_VDM_SVIDS_REQUEST
),
VDM_DFP_CMD_REACTION_PD30(CMD_DISCOVER_MODES,
PE_UFP_VDM_GET_MODES,
PE_DFP_VDM_MODES_REQUEST
),
VDM_DFP_CMD_REACTION(CMD_ENTER_MODE,
PE_UFP_VDM_EVALUATE_MODE_ENTRY,
PE_DFP_VDM_MODE_ENTRY_REQUEST
),
VDM_DFP_CMD_REACTION(CMD_EXIT_MODE,
PE_UFP_VDM_MODE_EXIT,
PE_DFP_VDM_MODE_EXIT_REQUEST
),
VDM_UFP_CMD_REACTION_PD30(CMD_ATTENTION,
PE_DFP_VDM_ATTENTION_REQUEST,
PE_UFP_VDM_ATTENTION_REQUEST
),
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
VDM_DFP_CMD_REACTION(CMD_DP_STATUS,
PE_UFP_VDM_DP_STATUS_UPDATE,
PE_DFP_VDM_DP_STATUS_UPDATE_REQUEST
),
VDM_DFP_CMD_REACTION(CMD_DP_CONFIG,
PE_UFP_VDM_DP_CONFIGURE,
PE_DFP_VDM_DP_CONFIGURATION_REQUEST
),
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
#ifdef CONFIG_USB_PD_CUSTOM_VDM
/* Only handle Timeout Case */
VDM_DFP_CMD_REACTION_PD30(0,
PE_UFP_UVDM_RECV,
PE_DFP_UVDM_SEND
),
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
VDM_CABLE_CMD_REACTION(CMD_DISCOVER_IDENT,
0,
PE_SRC_VDM_IDENTITY_REQUEST
),
#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
VDM_CABLE_CMD_REACTION(CMD_DISCOVER_IDENT,
0,
PE_DFP_CBL_VDM_IDENTITY_REQUEST
),
#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
};
static inline bool pd_vdm_state_transit_rx(struct pd_port *pd_port,
const struct vdm_state_transition *state_transition)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (!pd_check_pe_state_ready(pd_port)) {
PE_DBG("670 : invalid, current status\n");
return false;
}
PE_TRANSIT_STATE(pd_port, state_transition->vdm_init_state);
return true;
}
static inline bool pd_vdm_state_transit_tx(struct pd_port *pd_port,
uint8_t vdm_cmdt, const struct vdm_state_transition *state_transition)
{
int ret_code = TCP_DPM_RET_VDM_NAK;
uint8_t curr_state = state_transition->vdm_request_state;
uint8_t next_state = curr_state + 1;
switch (vdm_cmdt) {
case CMDT_RSP_NAK:
if (curr_state != PE_DFP_VDM_MODE_EXIT_REQUEST)
next_state = curr_state+2;
break;
case CMDT_RSP_BUSY:
if (curr_state == PE_DFP_VDM_MODE_EXIT_REQUEST)
next_state = pe_get_curr_hard_reset_state(pd_port);
else
next_state = curr_state+2;
break;
default:
ret_code = TCP_DPM_RET_VDM_ACK;
break;
}
pd_notify_tcp_vdm_event_2nd_result(pd_port, ret_code);
PE_TRANSIT_STATE(pd_port, next_state);
return true;
}
bool vdm_is_state_transition_available(struct pd_port *pd_port,
bool recv, const struct vdm_state_transition *state_transition)
{
uint8_t shift = 1;
uint8_t vdm_cmd_flags = state_transition->vdm_cmd_flags;
if (pd_check_rev30(pd_port) &&
(vdm_cmd_flags & VDM_CMD_FLAG_PD30_DUPLEX)) {
return true;
}
if (pd_port->data_role == PD_ROLE_DFP)
shift += 1;
if (recv)
shift += 2;
return vdm_cmd_flags & (1 << shift);
}
static bool pd_vdm_state_transit(
struct pd_port *pd_port, uint8_t vdm_cmdt,
const struct vdm_state_transition *state_transition)
{
uint8_t curr_state;
curr_state = pd_port->pe_state_curr;
if (curr_state == PE_UFP_VDM_ATTENTION_REQUEST) {
VDM_STATE_DPM_INFORMED(pd_port);
pd_notify_tcp_vdm_event_2nd_result(
pd_port, TCP_DPM_RET_TIMEOUT);
return false;
}
if (vdm_cmdt == CMDT_INIT) { /* Recv */
if (!vdm_is_state_transition_available(
pd_port, true, state_transition))
return false;
return pd_vdm_state_transit_rx(pd_port, state_transition);
}
/* Send */
if (!vdm_is_state_transition_available(
pd_port, false, state_transition))
return false;
if (curr_state != state_transition->vdm_request_state)
return false;
return pd_vdm_state_transit_tx(
pd_port, vdm_cmdt, state_transition);
}
enum {
VDM_STATE_TRANSIT_SOP_CMD = 0,
VDM_STATE_TRANSIT_SOP_PRIME_CMD = VDM_CMD_FLAG_CABLE_CMD,
VDM_STATE_TRANSIT_NAK = 2,
VDM_STATE_TRANSIT_CHECK_TX = 3,
};
static bool pe_check_vdm_state_transit_valid(
struct pd_port *pd_port, uint8_t transit_type, uint8_t *vdm_cmdt,
const struct vdm_state_transition *state_transition)
{
uint8_t curr_state;
uint8_t vdm_cmd;
uint8_t cable_cmd;
uint8_t vdm_cmd_flags;
uint32_t curr_vdm_hdr;
curr_state = pd_port->pe_state_curr;
if (transit_type == VDM_STATE_TRANSIT_NAK) {
if (curr_state != state_transition->vdm_request_state)
return false;
*vdm_cmdt = CMDT_RSP_BUSY;
return true;
} else if (transit_type >= VDM_STATE_TRANSIT_CHECK_TX) {
return state_transition->vdm_request_state == transit_type;
}
curr_vdm_hdr = pd_port->curr_vdm_hdr;
vdm_cmd = PD_VDO_CMD(curr_vdm_hdr);
*vdm_cmdt = PD_VDO_CMDT(curr_vdm_hdr);
if (state_transition->vdm_cmd != vdm_cmd)
return false;
vdm_cmd_flags = state_transition->vdm_cmd_flags;
cable_cmd = vdm_cmd_flags & VDM_CMD_FLAG_CABLE_CMD;
if (cable_cmd != transit_type)
return false;
if (cable_cmd && curr_state != state_transition->vdm_request_state)
return false;
return true;
}
static bool pd_make_vdm_state_transit(
struct pd_port *pd_port, uint8_t transit_type)
{
int i;
bool check_tx;
uint8_t vdm_cmdt;
const struct vdm_state_transition *state_transition;
uint8_t nr_transition = ARRAY_SIZE(pe_vdm_state_reactions);
const struct vdm_state_transition *state_reaction =
pe_vdm_state_reactions;
check_tx = transit_type >= VDM_STATE_TRANSIT_CHECK_TX;
for (i = 0; i < nr_transition; i++) {
state_transition = &state_reaction[i];
if (!pe_check_vdm_state_transit_valid(
pd_port, transit_type, &vdm_cmdt, state_transition))
continue;
if (check_tx) {
return vdm_is_state_transition_available(
pd_port, false, state_transition);
} else {
return pd_vdm_state_transit(
pd_port, vdm_cmdt, state_transition);
}
}
return false;
}
static inline bool pd_make_vdm_state_transit_sop(struct pd_port *pd_port)
{
return pd_make_vdm_state_transit(
pd_port, VDM_STATE_TRANSIT_SOP_CMD);
}
static inline bool pd_make_vdm_state_transit_cable(struct pd_port *pd_port)
{
return pd_make_vdm_state_transit(
pd_port, VDM_STATE_TRANSIT_SOP_PRIME_CMD);
}
static inline bool pd_make_vdm_state_transit_nak(struct pd_port *pd_port)
{
return pd_make_vdm_state_transit(
pd_port, VDM_STATE_TRANSIT_NAK);
}
/* Discover Cable ID */
#ifdef CONFIG_PD_DISCOVER_CABLE_ID
DECL_PE_STATE_TRANSITION(PD_DPM_MSG_DISCOVER_CABLE) = {
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
{ PE_SRC_STARTUP, PE_SRC_VDM_IDENTITY_REQUEST},
{ PE_SRC_DISCOVERY, PE_SRC_VDM_IDENTITY_REQUEST},
#endif
#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
{ PE_SRC_READY, PE_DFP_CBL_VDM_IDENTITY_REQUEST},
{ PE_SNK_READY, PE_DFP_CBL_VDM_IDENTITY_REQUEST},
#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
#ifdef CONFIG_PD_SRC_RESET_CABLE
{ PE_SRC_CBL_SEND_SOFT_RESET, PE_SRC_VDM_IDENTITY_REQUEST},
#endif /* CONFIG_PD_SRC_RESET_CABLE */
};
DECL_PE_STATE_REACTION(PD_DPM_MSG_DISCOVER_CABLE);
#endif /* CONFIG_PD_DISCOVER_CABLE_ID */
/*
* [BLOCK] Porcess Ctrl MSG
*/
#ifdef CONFIG_USB_PD_ALT_MODE
#ifdef CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION
static inline bool pd_ufp_u_auto_send_attention(struct pd_port *pd_port)
{
struct dp_data *dp_data = pd_get_dp_data(pd_port);
if (dp_data->local_config != 0) {
PE_TRANSIT_STATE(pd_port,
PE_UFP_VDM_ATTENTION_REQUEST);
return true;
}
return false;
}
#endif /* CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION */
#endif /* CONFIG_USB_PD_ALT_MODE */
static inline bool pd_process_ctrl_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
struct pe_data *pe_data = &pd_port->pe_data;
/* VDM Only handle CtrlMsg = GoodCRC */
if (pd_event->msg != PD_CTRL_GOOD_CRC)
return false;
if (pe_data->vdm_state_flags &
VDM_STATE_FLAG_ENABLE_VDM_RESPONSE_TIMER)
pd_enable_timer(pd_port, pe_data->vdm_state_timer);
switch (pd_port->pe_state_curr) {
#ifdef CONFIG_USB_PD_ALT_MODE
#ifdef CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION
case PE_UFP_VDM_DP_CONFIGURE:
if (pd_ufp_u_auto_send_attention(pd_port))
return true;
break;
#endif /* CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION */
case PE_UFP_VDM_ATTENTION_REQUEST:
pd_notify_tcp_vdm_event_2nd_result(
pd_port, TCP_DPM_RET_VDM_ACK);
break;
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
case PE_SRC_VDM_IDENTITY_REQUEST:
pe_data->power_cable_present = true;
return false;
#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */
#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID
case PE_DFP_CBL_VDM_IDENTITY_REQUEST:
pe_data->power_cable_present = true;
return false;
#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */
#ifdef CONFIG_USB_PD_CUSTOM_VDM
case PE_DFP_UVDM_SEND:
if (!pd_port->uvdm_wait_resp) {
PE_TRANSIT_STATE(pd_port, PE_DFP_UVDM_ACKED);
return true;
}
break;
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
}
if (pe_data->vdm_state_flags
& VDM_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC) {
pe_transit_ready_state(pd_port);
return true;
}
return false;
}
/*
* [BLOCK] Porcess Custom MSG (SVDM/UVDM)
*/
#ifdef CONFIG_USB_PD_CUSTOM_VDM
bool pd_process_custom_vdm(struct pd_port *pd_port, bool svdm)
{
bool recv = false;
if (pd_port->data_role == PD_ROLE_UFP)
recv = true;
if (pd_check_rev30(pd_port) && svdm)
recv = true;
if (pd_check_pe_state_ready(pd_port)) {
if (recv) {
PE_TRANSIT_STATE(pd_port, PE_UFP_UVDM_RECV);
return true;
}
} else if (pd_port->pe_state_curr == PE_DFP_UVDM_SEND) {
PE_TRANSIT_STATE(pd_port, PE_DFP_UVDM_ACKED);
return true;
}
pd_put_dpm_event(pd_port, PD_DPM_NOT_SUPPORT);
return false;
}
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
static inline bool pd_process_uvdm(
struct pd_port *pd_port, struct pd_event *pd_event)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
#ifdef CONFIG_USB_PD_UVDM
/* support sop only */
if (pd_event->pd_msg->frame_type != TCPC_TX_SOP)
return false;
if (pd_process_custom_vdm(pd_port, false))
return true;
#else
pd_put_dpm_event(pd_port, PD_DPM_NOT_SUPPORT);
#endif /* CONFIG_USB_PD_UVDM */
/* TODO: Reply Not_Supported Message*/
PE_DBG("659 : Invalid UVDM\n");
return false;
}
/*
* [BLOCK] Porcess Data MSG (VDM)
*/
#if (PE_EVT_INFO_VDM_DIS == 0)
static const char * const pe_vdm_cmd_name[] = {
"DiscoverID",
"DiscoverSVID",
"DiscoverMode",
"EnterMode",
"ExitMode",
"Attention",
};
#if PE_INFO_ENABLE
static const char *const pe_vdm_cmd_type_name[] = {
"INIT",
"ACK",
"NACK",
"BUSY",
};
#endif /* PE_INFO_ENABLE */
static inline const char *assign_vdm_cmd_name(uint8_t cmd)
{
if (cmd <= ARRAY_SIZE(pe_vdm_cmd_name) && cmd != 0) {
cmd -= 1;
return pe_vdm_cmd_name[cmd];
}
return NULL;
}
#ifdef CONFIG_USB_PD_ALT_MODE
static const char *const pe_vdm_dp_cmd_name[] = {
"DPStatus",
"DPConfig",
};
static inline const char *assign_vdm_dp_cmd_name(uint8_t cmd)
{
if (cmd >= CMD_DP_STATUS) {
cmd -= CMD_DP_STATUS;
if (cmd < ARRAY_SIZE(pe_vdm_dp_cmd_name))
return pe_vdm_dp_cmd_name[cmd];
}
return NULL;
}
#endif /* CONFIG_USB_PD_ALT_MODE */
#endif /* if (PE_EVT_INFO_VDM_DIS == 0) */
static inline void print_vdm_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
#if (PE_EVT_INFO_VDM_DIS == 0)
uint8_t cmd;
uint8_t cmd_type;
uint16_t svid;
const char *name = NULL;
uint32_t vdm_hdr = pd_port->curr_vdm_hdr;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
cmd = PD_VDO_CMD(vdm_hdr);
cmd_type = PD_VDO_CMDT(vdm_hdr);
svid = PD_VDO_VID(vdm_hdr);
name = assign_vdm_cmd_name(cmd);
#ifdef CONFIG_USB_PD_ALT_MODE
if (name == NULL && svid == USB_SID_DISPLAYPORT)
name = assign_vdm_dp_cmd_name(cmd);
#endif /* CONFIG_USB_PD_ALT_MODE */
if (name == NULL)
return;
#if PE_INFO_ENABLE
if (cmd_type >= ARRAY_SIZE(pe_vdm_cmd_type_name))
return;
PE_INFO("%s:%s\n", name, pe_vdm_cmd_type_name[cmd_type]);
#endif /* PE_INFO_ENABLE */
#endif /* PE_EVT_INFO_VDM_DIS */
}
static inline bool pd_process_sop_vdm(
struct pd_port *pd_port, struct pd_event *pd_event)
{
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_make_vdm_state_transit_sop(pd_port))
return true;
#ifdef CONFIG_USB_PD_SVDM
if (pd_process_custom_vdm(pd_port, true))
return true;
#endif /* CONFIG_USB_PD_SVDM */
PE_DBG("Unknown SVDM\n");
return false;
}
static inline bool pd_process_sop_prime_vdm(
struct pd_port *pd_port, struct pd_event *pd_event)
{
return pd_make_vdm_state_transit_cable(pd_port);
}
static inline bool pd_process_data_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool ret = false;
uint32_t vdm_hdr;
struct pd_msg *pd_msg = pd_event->pd_msg;
if (pd_event->msg != PD_DATA_VENDOR_DEF)
return ret;
vdm_hdr = pd_msg->payload[0];
pd_port->curr_vdm_hdr = vdm_hdr;
pd_port->curr_vdm_ops = PD_VDO_OPOS(vdm_hdr);
pd_port->curr_vdm_svid = PD_VDO_VID(vdm_hdr);
if (!PD_VDO_SVDM(vdm_hdr))
return pd_process_uvdm(pd_port, pd_event);
/* From Port Partner, copy curr_state from pd_state */
if (PD_VDO_CMDT(vdm_hdr) == CMDT_INIT) {
pd_port->pe_vdm_state = pd_port->pe_pd_state;
pd_port->pe_state_curr = pd_port->pe_pd_state;
#if PE_DBG_RESET_VDM_DIS == 0
PE_DBG("reset vdm_state\n");
#endif /* if PE_DBG_RESET_VDM_DIS == 0 */
}
print_vdm_msg(pd_port, pd_event);
if (pd_msg->frame_type == TCPC_TX_SOP_PRIME)
ret = pd_process_sop_prime_vdm(pd_port, pd_event);
else
ret = pd_process_sop_vdm(pd_port, pd_event);
return ret;
}
/*
* [BLOCK] Porcess PDM MSG
*/
static inline bool pd_process_dpm_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
if (pd_event->msg != PD_DPM_ACK)
return false;
if (pd_port->pe_data.vdm_state_flags &
VDM_STATE_FLAG_BACK_READY_IF_DPM_ACK) {
pe_transit_ready_state(pd_port);
return true;
}
return false;
}
/*
* [BLOCK] Porcess HW MSG
*/
static inline bool pd_process_hw_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
struct pe_data *pe_data = &pd_port->pe_data;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_event->msg == PD_HW_TX_DISCARD &&
(pe_data->vdm_discard_retry_count < 10)) {
PE_INFO("vdm_discard_retry\n");
pe_data->vdm_discard_retry_flag = true;
pe_data->vdm_discard_retry_count++;
}
switch (pd_event->msg) {
case PD_HW_TX_FAILED:
case PD_HW_TX_DISCARD:
return pd_make_vdm_state_transit_nak(pd_port);
case PD_HW_RETRY_VDM:
if (pd_port->pe_data.vdm_state_timer)
return pd_make_vdm_state_transit_nak(pd_port);
PE_DBG("RetryVDM\n");
return pd_process_sop_vdm(pd_port, pd_event);
}
return false;
}
/*
* [BLOCK] Porcess PE MSG
*/
static inline bool pd_process_pe_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_PE_VDM_NOT_SUPPORT:
break;
case PD_PE_VDM_RESET:
pd_port->pe_data.reset_vdm_state = true;
break;
default:
return false;
}
return pd_make_vdm_state_transit_nak(pd_port);
}
/*
* [BLOCK] Porcess Timer MSG
*/
static inline bool pd_process_timer_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
case PD_TIMER_VDM_MODE_ENTRY:
case PD_TIMER_VDM_MODE_EXIT:
case PD_TIMER_VDM_RESPONSE:
case PD_TIMER_UVDM_RESPONSE:
return pd_make_vdm_state_transit_nak(pd_port);
default:
return false;
}
}
/*
* [BLOCK] Porcess TCP MSG
*/
const uint8_t tcp_vdm_evt_init_state[] = {
PE_DFP_UFP_VDM_IDENTITY_REQUEST, /* TCP_DPM_EVT_DISCOVER_ID */
PE_DFP_VDM_SVIDS_REQUEST, /* TCP_DPM_EVT_DISCOVER_SVIDS */
PE_DFP_VDM_MODES_REQUEST, /* TCP_DPM_EVT_DISCOVER_MODES */
PE_DFP_VDM_MODE_ENTRY_REQUEST, /* TCP_DPM_EVT_ENTER_MODE */
PE_DFP_VDM_MODE_EXIT_REQUEST, /* TCP_DPM_EVT_EXIT_MODE */
PE_UFP_VDM_ATTENTION_REQUEST, /* TCP_DPM_EVT_ATTENTION */
#ifdef CONFIG_USB_PD_ALT_MODE
PE_UFP_VDM_ATTENTION_REQUEST, /* TCP_DPM_EVT_DP_ATTENTION */
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
PE_DFP_VDM_DP_STATUS_UPDATE_REQUEST, /* TCP_DPM_EVT_DP_STATUS_UPDATE */
PE_DFP_VDM_DP_CONFIGURATION_REQUEST, /* TCP_DPM_EVT_DP_CONFIG */
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifdef CONFIG_USB_PD_CUSTOM_VDM
PE_DFP_UVDM_SEND, /* TCP_DPM_EVT_UVDM */
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
};
static inline bool pd_process_tcp_cable_event(
struct pd_port *pd_port, struct pd_event *pd_event)
{
bool ret;
int tcp_ret;
#ifdef CONFIG_PD_DISCOVER_CABLE_ID
bool role_check = true;
if (pd_check_rev30(pd_port))
role_check = pd_port->vconn_role;
if (role_check) {
ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_DISCOVER_CABLE);
tcp_ret = ret ? TCP_DPM_RET_SENT : TCP_DPM_RET_DENIED_NOT_READY;
} else {
ret = false;
tcp_ret = TCP_DPM_RET_DENIED_WRONG_DATA_ROLE;
}
#else
ret = false;
tcp_ret = TCP_DPM_RET_DENIED_UNKNOWN;
#endif /* CONFIG_PD_DISCOVER_CABLE_ID */
pd_notify_tcp_event_1st_result(pd_port, tcp_ret);
return ret;
}
#ifdef CONFIG_USB_PD_ALT_MODE
static inline uint32_t tcpc_update_bits(
uint32_t var, uint32_t update, uint32_t mask)
{
return (var & (~mask)) | (update & mask);
}
static inline void pd_parse_tcp_dpm_evt_svdm(struct pd_port *pd_port)
{
struct tcp_dpm_svdm_data *svdm_data =
&pd_port->tcp_event.tcp_dpm_data.svdm_data;
pd_port->mode_svid = svdm_data->svid;
pd_port->mode_obj_pos = svdm_data->ops;
}
static inline void pd_parse_tcp_dpm_evt_dp_status(struct pd_port *pd_port)
{
struct tcp_dpm_dp_data *dp_data_tcp =
&pd_port->tcp_event.tcp_dpm_data.dp_data;
struct dp_data *dp_data = pd_get_dp_data(pd_port);
pd_port->mode_svid = USB_SID_DISPLAYPORT;
dp_data->local_status = tcpc_update_bits(
dp_data->local_status,
dp_data_tcp->val, dp_data_tcp->mask);
}
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
static inline void pd_parse_tcp_dpm_evt_dp_config(struct pd_port *pd_port)
{
struct tcp_dpm_dp_data *dp_data_tcp =
&pd_port->tcp_event.tcp_dpm_data.dp_data;
struct dp_data *dp_data = pd_get_dp_data(pd_port);
pd_port->mode_svid = USB_SID_DISPLAYPORT;
dp_data->local_config = tcpc_update_bits(
dp_data->local_config, dp_data_tcp->val, dp_data_tcp->mask);
}
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifdef CONFIG_USB_PD_CUSTOM_VDM
static inline void pd_parse_tcp_dpm_evt_uvdm(struct pd_port *pd_port)
{
struct tcp_dpm_custom_vdm_data *vdm_data =
&pd_port->tcp_event.tcp_dpm_data.vdm_data;
pd_port->uvdm_cnt = vdm_data->cnt;
pd_port->uvdm_wait_resp = vdm_data->wait_resp;
memcpy(pd_port->uvdm_data,
vdm_data->vdos, sizeof(uint32_t) * vdm_data->cnt);
#ifdef CONFIG_USB_PD_SVDM
if (pd_check_rev30(pd_port) &&
(pd_port->uvdm_data[0] & VDO_SVDM_TYPE))
pd_port->uvdm_data[0] |= VDO_SVDM_VERS(SVDM_REV20);
#endif /* CONFIG_USB_PD_SVDM */
}
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
static inline void pd_parse_tcp_dpm_evt_from_tcpm(
struct pd_port *pd_port, struct pd_event *pd_event)
{
switch (pd_event->msg) {
#ifdef CONFIG_USB_PD_KEEP_SVIDS
case TCP_DPM_EVT_DISCOVER_SVIDS:
pd_port->pe_data.remote_svid_list.cnt = 0;
break;
#endif /* CONFIG_USB_PD_KEEP_SVIDS */
#ifdef CONFIG_USB_PD_ALT_MODE
case TCP_DPM_EVT_DISCOVER_MODES:
case TCP_DPM_EVT_ENTER_MODE:
case TCP_DPM_EVT_EXIT_MODE:
case TCP_DPM_EVT_ATTENTION:
pd_parse_tcp_dpm_evt_svdm(pd_port);
break;
case TCP_DPM_EVT_DP_ATTENTION:
pd_parse_tcp_dpm_evt_dp_status(pd_port);
break;
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
case TCP_DPM_EVT_DP_STATUS_UPDATE:
pd_parse_tcp_dpm_evt_dp_status(pd_port);
break;
case TCP_DPM_EVT_DP_CONFIG:
pd_parse_tcp_dpm_evt_dp_config(pd_port);
break;
#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifdef CONFIG_USB_PD_CUSTOM_VDM
case TCP_DPM_EVT_UVDM:
pd_parse_tcp_dpm_evt_uvdm(pd_port);
break;
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
}
}
static inline bool pd_check_tcp_msg_valid(
struct pd_port *pd_port, uint8_t new_state)
{
return pd_make_vdm_state_transit(pd_port, new_state);
}
static inline bool pd_process_tcp_msg(
struct pd_port *pd_port, struct pd_event *pd_event)
{
uint8_t new_state;
struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc;
if (pd_event->msg == TCP_DPM_EVT_DISCOVER_CABLE)
return pd_process_tcp_cable_event(pd_port, pd_event);
if (!pd_check_pe_state_ready(pd_port)) {
pd_notify_tcp_event_1st_result(
pd_port, TCP_DPM_RET_DENIED_NOT_READY);
PE_DBG("skip vdm_request, not ready_state (%d)\n",
pd_port->pe_state_curr);
return false;
}
new_state = pd_event->msg - TCP_DPM_EVT_DISCOVER_ID;
if (new_state >= ARRAY_SIZE(tcp_vdm_evt_init_state)) {
PD_BUG_ON(1);
return false;
}
new_state = tcp_vdm_evt_init_state[new_state];
if (!pd_check_tcp_msg_valid(pd_port, new_state)) {
pd_notify_tcp_event_1st_result(
pd_port, TCP_DPM_RET_DENIED_WRONG_DATA_ROLE);
PE_DBG("skip vdm_request, WRONG DATA ROLE\n");
return false;
}
if (pd_event->msg_sec == PD_TCP_FROM_TCPM)
pd_parse_tcp_dpm_evt_from_tcpm(pd_port, pd_event);
PE_TRANSIT_STATE(pd_port, new_state);
pd_notify_tcp_event_1st_result(pd_port, TCP_DPM_RET_SENT);
return true;
}
/*
* [BLOCK] Process Policy Engine's VDM Message
*/
bool pd_process_event_vdm(struct pd_port *pd_port, struct pd_event *pd_event)
{
pd_port->pe_data.vdm_discard_retry_flag = false;
switch (pd_event->event_type) {
case PD_EVT_CTRL_MSG:
return pd_process_ctrl_msg(pd_port, pd_event);
case PD_EVT_DATA_MSG:
return pd_process_data_msg(pd_port, pd_event);
case PD_EVT_DPM_MSG:
return pd_process_dpm_msg(pd_port, pd_event);
case PD_EVT_HW_MSG:
return pd_process_hw_msg(pd_port, pd_event);
case PD_EVT_PE_MSG:
return pd_process_pe_msg(pd_port, pd_event);
case PD_EVT_TIMER_MSG:
return pd_process_timer_msg(pd_port, pd_event);
case PD_EVT_TCP_MSG:
return pd_process_tcp_msg(pd_port, pd_event);
}
return false;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,989 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "inc/tcpci.h"
#include <linux/time.h>
#include <linux/slab.h>
#define TCPC_NOTIFY_OVERTIME (20) /* ms */
#ifdef CONFIG_TCPC_NOTIFICATION_NON_BLOCKING
struct tcp_notify_work {
struct work_struct work;
struct tcpc_device *tcpc;
struct tcp_notify tcp_noti;
uint8_t type;
uint8_t state;
};
static void tcp_notify_func(struct work_struct *work)
{
struct tcp_notify_work *tn_work =
container_of(work, struct tcp_notify_work, work);
struct tcpc_device *tcpc = tn_work->tcpc;
struct tcp_notify *tcp_noti = &tn_work->tcp_noti;
uint8_t type = tn_work->type;
uint8_t state = tn_work->state;
#ifdef CONFIG_PD_BEGUG_ON
struct timeval begin, end;
int timeval = 0;
do_gettimeofday(&begin);
srcu_notifier_call_chain(&tcpc->evt_nh[type], state, tcp_noti);
do_gettimeofday(&end);
timeval = (timeval_to_ns(end) - timeval_to_ns(begin))/1000/1000;
PD_BUG_ON(timeval > TCPC_NOTIFY_OVERTIME);
#else
srcu_notifier_call_chain(&tcpc->evt_nh[type], state, tcp_noti);
#endif
kfree(tn_work);
}
static int tcpc_check_notify_time(struct tcpc_device *tcpc,
struct tcp_notify *tcp_noti, uint8_t type, uint8_t state)
{
struct tcp_notify_work *tn_work;
tn_work = kzalloc(sizeof(*tn_work), GFP_KERNEL);
if (!tn_work)
return -ENOMEM;
INIT_WORK(&tn_work->work, tcp_notify_func);
tn_work->tcpc = tcpc;
tn_work->tcp_noti = *tcp_noti;
tn_work->type = type;
tn_work->state = state;
return queue_work(tcpc->evt_wq, &tn_work->work) ? 0 : -EAGAIN;
}
#else
static int tcpc_check_notify_time(struct tcpc_device *tcpc,
struct tcp_notify *tcp_noti, uint8_t type, uint8_t state)
{
int ret;
#ifdef CONFIG_PD_BEGUG_ON
struct timeval begin, end;
int timeval = 0;
do_gettimeofday(&begin);
ret = srcu_notifier_call_chain(&tcpc->evt_nh[type], state, tcp_noti);
do_gettimeofday(&end);
timeval = (timeval_to_ns(end) - timeval_to_ns(begin))/1000/1000;
PD_BUG_ON(timeval > TCPC_NOTIFY_OVERTIME);
#else
ret = srcu_notifier_call_chain(&tcpc->evt_nh[type], state, tcp_noti);
#endif
return ret;
}
#endif /* CONFIG_TCPC_NOTIFICATION_BLOCKING */
int tcpci_check_vbus_valid_from_ic(struct tcpc_device *tcpc)
{
uint16_t power_status;
int vbus_level = tcpc->vbus_level;
if (tcpci_get_power_status(tcpc, &power_status) == 0) {
if (vbus_level != tcpc->vbus_level) {
TCPC_INFO("[Warning] ps_changed %d ->%d\n",
vbus_level, tcpc->vbus_level);
}
}
return tcpci_check_vbus_valid(tcpc);
}
int tcpci_check_vsafe0v(
struct tcpc_device *tcpc, bool detect_en)
{
int ret = 0;
#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
ret = (tcpc->vbus_level == TCPC_VBUS_SAFE0V);
#else
ret = (tcpc->vbus_level == TCPC_VBUS_INVALID);
#endif
return ret;
}
int tcpci_alert_status_clear(
struct tcpc_device *tcpc, uint32_t mask)
{
PD_BUG_ON(tcpc->ops->alert_status_clear == NULL);
return tcpc->ops->alert_status_clear(tcpc, mask);
}
EXPORT_SYMBOL(tcpci_alert_status_clear);
int tcpci_fault_status_clear(
struct tcpc_device *tcpc, uint8_t status)
{
PD_BUG_ON(tcpc->ops->fault_status_clear == NULL);
return tcpc->ops->fault_status_clear(tcpc, status);
}
int tcpci_set_alert_mask(struct tcpc_device *tcpc, uint32_t mask)
{
int rv = 0;
if (tcpc->ops->set_alert_mask)
return tcpc->ops->set_alert_mask(tcpc, mask);
return rv;
}
int tcpci_get_alert_mask(
struct tcpc_device *tcpc, uint32_t *mask)
{
PD_BUG_ON(tcpc->ops->get_alert_mask == NULL);
return tcpc->ops->get_alert_mask(tcpc, mask);
}
int tcpci_get_alert_status(
struct tcpc_device *tcpc, uint32_t *alert)
{
PD_BUG_ON(tcpc->ops->get_alert_status == NULL);
return tcpc->ops->get_alert_status(tcpc, alert);
}
int tcpci_get_fault_status(
struct tcpc_device *tcpc, uint8_t *fault)
{
if (tcpc->ops->get_fault_status)
return tcpc->ops->get_fault_status(tcpc, fault);
*fault = 0;
return 0;
}
int tcpci_get_power_status(
struct tcpc_device *tcpc, uint16_t *pw_status)
{
int ret;
PD_BUG_ON(tcpc->ops->get_power_status == NULL);
ret = tcpc->ops->get_power_status(tcpc, pw_status);
if (ret < 0)
return ret;
tcpci_vbus_level_init(tcpc, *pw_status);
return 0;
}
int tcpci_init(struct tcpc_device *tcpc, bool sw_reset)
{
int ret;
uint16_t power_status;
PD_BUG_ON(tcpc->ops->init == NULL);
ret = tcpc->ops->init(tcpc, sw_reset);
if (ret < 0)
return ret;
return tcpci_get_power_status(tcpc, &power_status);
}
int tcpci_init_alert_mask(struct tcpc_device *tcpc)
{
if (tcpc->ops->init_alert_mask)
return tcpc->ops->init_alert_mask(tcpc);
return 0;
}
int tcpci_get_cc(struct tcpc_device *tcpc)
{
int ret;
int cc1, cc2;
PD_BUG_ON(tcpc->ops->get_cc == NULL);
ret = tcpc->ops->get_cc(tcpc, &cc1, &cc2);
if (ret < 0)
return ret;
if ((cc1 == tcpc->typec_remote_cc[0]) &&
(cc2 == tcpc->typec_remote_cc[1])) {
return 0;
}
tcpc->typec_remote_cc[0] = cc1;
tcpc->typec_remote_cc[1] = cc2;
return 1;
}
int tcpci_set_cc(struct tcpc_device *tcpc, int pull)
{
PD_BUG_ON(tcpc->ops->set_cc == NULL);
#ifdef CONFIG_USB_PD_DBG_ALWAYS_LOCAL_RP
if (pull == TYPEC_CC_RP)
pull = tcpc->typec_local_rp_level;
#endif /* CONFIG_USB_PD_DBG_ALWAYS_LOCAL_RP */
#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE
if (pull == TYPEC_CC_DRP && tcpc->typec_legacy_cable) {
#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2
if (tcpc->typec_legacy_cable == 2)
pull = TYPEC_CC_RP;
else if (tcpc->typec_legacy_retry_wk > 1)
pull = TYPEC_CC_RP_3_0;
else
#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */
pull = TYPEC_CC_RP_1_5;
TCPC_DBG2("LC->Toggling (%d)\n", pull);
}
#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */
if (pull & TYPEC_CC_DRP) {
tcpc->typec_remote_cc[0] =
tcpc->typec_remote_cc[1] =
TYPEC_CC_DRP_TOGGLING;
}
tcpc->typec_local_cc = pull;
return tcpc->ops->set_cc(tcpc, pull);
}
int tcpci_set_polarity(struct tcpc_device *tcpc, int polarity)
{
PD_BUG_ON(tcpc->ops->set_polarity == NULL);
return tcpc->ops->set_polarity(tcpc, polarity);
}
int tcpci_set_low_rp_duty(struct tcpc_device *tcpc, bool low_rp)
{
#ifdef CONFIG_TYPEC_CAP_LOW_RP_DUTY
if (low_rp)
TCPC_INFO("low_rp_duty\n");
if (tcpc->ops->set_low_rp_duty)
return tcpc->ops->set_low_rp_duty(tcpc, low_rp);
#endif /* CONFIG_TYPEC_CAP_LOW_RP_DUTY */
return 0;
}
int tcpci_set_vconn(struct tcpc_device *tcpc, int enable)
{
#ifdef CONFIG_TCPC_SOURCE_VCONN
struct tcp_notify tcp_noti;
if (tcpc->tcpc_source_vconn == enable)
return 0;
tcpc->tcpc_source_vconn = enable;
tcp_noti.en_state.en = enable != 0;
tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_SOURCE_VCONN);
if (tcpc->ops->set_vconn)
return tcpc->ops->set_vconn(tcpc, enable);
#endif /* CONFIG_TCPC_SOURCE_VCONN */
return 0;
}
int tcpci_is_low_power_mode(struct tcpc_device *tcpc)
{
int rv = 1;
#ifdef CONFIG_TCPC_LOW_POWER_MODE
if (tcpc->ops->is_low_power_mode)
rv = tcpc->ops->is_low_power_mode(tcpc);
#endif /* CONFIG_TCPC_LOW_POWER_MODE */
return rv;
}
int tcpci_set_low_power_mode(
struct tcpc_device *tcpc, bool en, int pull)
{
int rv = 0;
#ifdef CONFIG_TCPC_LOW_POWER_MODE
if (tcpc->ops->set_low_power_mode)
rv = tcpc->ops->set_low_power_mode(tcpc, en, pull);
#endif /* CONFIG_TCPC_LOW_POWER_MODE */
return rv;
}
int tcpci_set_watchdog(struct tcpc_device *tcpc, bool en)
{
int rv = 0;
if (tcpc->tcpc_flags & TCPC_FLAGS_WATCHDOG_EN)
if (tcpc->ops->set_watchdog)
rv = tcpc->ops->set_watchdog(tcpc, en);
return rv;
}
int tcpci_alert_vendor_defined_handler(struct tcpc_device *tcpc)
{
int rv = 0;
if (tcpc->ops->alert_vendor_defined_handler)
rv = tcpc->ops->alert_vendor_defined_handler(tcpc);
return rv;
}
#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
int tcpci_is_vsafe0v(struct tcpc_device *tcpc)
{
int rv = -ENOTSUPP;
if (tcpc->ops->is_vsafe0v)
rv = tcpc->ops->is_vsafe0v(tcpc);
return rv;
}
#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
#ifdef CONFIG_WATER_DETECTION
int tcpci_is_water_detected(struct tcpc_device *tcpc)
{
if (tcpc->ops->is_water_detected)
return tcpc->ops->is_water_detected(tcpc);
return 0;
}
int tcpci_set_water_protection(struct tcpc_device *tcpc, bool en)
{
if (tcpc->ops->set_water_protection)
return tcpc->ops->set_water_protection(tcpc, en);
return 0;
}
int tcpci_set_usbid_polling(struct tcpc_device *tcpc, bool en)
{
if (tcpc->ops->set_usbid_polling)
return tcpc->ops->set_usbid_polling(tcpc, en);
return 0;
}
int tcpci_notify_wd_status(struct tcpc_device *tcpc, bool water_detected)
{
struct tcp_notify tcp_noti;
tcp_noti.wd_status.water_detected = water_detected;
return tcpc_check_notify_time(tcpc, &tcp_noti, TCP_NOTIFY_IDX_MISC,
TCP_NOTIFY_WD_STATUS);
}
#endif /* CONFIG_WATER_DETECTION */
#ifdef CONFIG_CABLE_TYPE_DETECTION
int tcpci_notify_cable_type(struct tcpc_device *tcpc)
{
struct tcp_notify tcp_noti;
tcp_noti.cable_type.type = tcpc->typec_cable_type;
return tcpc_check_notify_time(tcpc, &tcp_noti, TCP_NOTIFY_IDX_MISC,
TCP_NOTIFY_CABLE_TYPE);
}
#endif /* CONFIG_CABLE_TYPE_DETECTION */
#ifdef CONFIG_USB_POWER_DELIVERY
int tcpci_set_msg_header(struct tcpc_device *tcpc,
uint8_t power_role, uint8_t data_role)
{
PD_BUG_ON(tcpc->ops->set_msg_header == NULL);
return tcpc->ops->set_msg_header(tcpc, power_role, data_role);
}
int tcpci_set_rx_enable(struct tcpc_device *tcpc, uint8_t enable)
{
PD_BUG_ON(tcpc->ops->set_rx_enable == NULL);
return tcpc->ops->set_rx_enable(tcpc, enable);
}
int tcpci_protocol_reset(struct tcpc_device *tcpc)
{
if (tcpc->ops->protocol_reset)
return tcpc->ops->protocol_reset(tcpc);
return 0;
}
int tcpci_get_message(struct tcpc_device *tcpc,
uint32_t *payload, uint16_t *head, enum tcpm_transmit_type *type)
{
PD_BUG_ON(tcpc->ops->get_message == NULL);
return tcpc->ops->get_message(tcpc, payload, head, type);
}
int tcpci_transmit(struct tcpc_device *tcpc,
enum tcpm_transmit_type type, uint16_t header, const uint32_t *data)
{
PD_BUG_ON(tcpc->ops->transmit == NULL);
return tcpc->ops->transmit(tcpc, type, header, data);
}
int tcpci_set_bist_test_mode(struct tcpc_device *tcpc, bool en)
{
if (tcpc->ops->set_bist_test_mode)
return tcpc->ops->set_bist_test_mode(tcpc, en);
return 0;
}
int tcpci_set_bist_carrier_mode(struct tcpc_device *tcpc, uint8_t pattern)
{
PD_BUG_ON(tcpc->ops->set_bist_carrier_mode == NULL);
if (pattern) /* wait for GoodCRC */
udelay(240);
return tcpc->ops->set_bist_carrier_mode(tcpc, pattern);
}
#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
int tcpci_retransmit(struct tcpc_device *tcpc)
{
PD_BUG_ON(tcpc->ops->retransmit == NULL);
return tcpc->ops->retransmit(tcpc);
}
#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
#endif /* CONFIG_USB_POWER_DELIVERY */
int tcpci_notify_typec_state(struct tcpc_device *tcpc)
{
struct tcp_notify tcp_noti;
int ret;
tcp_noti.typec_state.polarity = tcpc->typec_polarity;
tcp_noti.typec_state.old_state = tcpc->typec_attach_old;
tcp_noti.typec_state.new_state = tcpc->typec_attach_new;
tcp_noti.typec_state.rp_level = tcpc->typec_remote_rp_level;
tcp_noti.typec_state.local_rp_level = tcpc->typec_local_rp_level;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_USB, TCP_NOTIFY_TYPEC_STATE);
return ret;
}
int tcpci_notify_role_swap(
struct tcpc_device *tcpc, uint8_t event, uint8_t role)
{
struct tcp_notify tcp_noti;
int ret;
tcp_noti.swap_state.new_role = role;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MISC, event);
return ret;
}
int tcpci_notify_pd_state(struct tcpc_device *tcpc, uint8_t connect)
{
struct tcp_notify tcp_noti;
int ret;
tcp_noti.pd_state.connected = connect;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_USB, TCP_NOTIFY_PD_STATE);
return ret;
}
int tcpci_set_intrst(struct tcpc_device *tcpc, bool en)
{
#ifdef CONFIG_TCPC_INTRST_EN
if (tcpc->ops->set_intrst)
tcpc->ops->set_intrst(tcpc, en);
#endif /* CONFIG_TCPC_INTRST_EN */
return 0;
}
int tcpci_enable_watchdog(struct tcpc_device *tcpc, bool en)
{
if (!(tcpc->tcpc_flags & TCPC_FLAGS_WATCHDOG_EN))
return 0;
TCPC_DBG2("enable_WG: %d\n", en);
if (tcpc->typec_watchdog == en)
return 0;
mutex_lock(&tcpc->access_lock);
tcpc->typec_watchdog = en;
if (tcpc->ops->set_watchdog)
tcpc->ops->set_watchdog(tcpc, en);
#ifdef CONFIG_TCPC_INTRST_EN
if (!en || tcpc->attach_wake_lock->active)
tcpci_set_intrst(tcpc, en);
#endif /* CONFIG_TCPC_INTRST_EN */
mutex_unlock(&tcpc->access_lock);
return 0;
}
int tcpci_source_vbus(
struct tcpc_device *tcpc, uint8_t type, int mv, int ma)
{
struct tcp_notify tcp_noti;
int ret;
#ifdef CONFIG_USB_POWER_DELIVERY
if (type >= TCP_VBUS_CTRL_PD &&
tcpc->pd_port.pe_data.pd_prev_connected)
type |= TCP_VBUS_CTRL_PD_DETECT;
#endif /* CONFIG_USB_POWER_DELIVERY */
if (ma < 0) {
if (mv != 0) {
switch (tcpc->typec_local_rp_level) {
case TYPEC_CC_RP_1_5:
ma = 1500;
break;
case TYPEC_CC_RP_3_0:
ma = 3000;
break;
default:
case TYPEC_CC_RP_DFT:
ma = CONFIG_TYPEC_SRC_CURR_DFT;
break;
}
} else
ma = 0;
}
tcp_noti.vbus_state.ma = ma;
tcp_noti.vbus_state.mv = mv;
tcp_noti.vbus_state.type = type;
tcpci_enable_watchdog(tcpc, mv != 0);
TCPC_DBG("source_vbus: %d mV, %d mA\n", mv, ma);
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_SOURCE_VBUS);
return ret;
}
EXPORT_SYMBOL(tcpci_source_vbus);
int tcpci_sink_vbus(
struct tcpc_device *tcpc, uint8_t type, int mv, int ma)
{
struct tcp_notify tcp_noti;
int ret;
#ifdef CONFIG_USB_POWER_DELIVERY
if (type >= TCP_VBUS_CTRL_PD &&
tcpc->pd_port.pe_data.pd_prev_connected)
type |= TCP_VBUS_CTRL_PD_DETECT;
#endif /* CONFIG_USB_POWER_DELIVERY */
if (ma < 0) {
if (mv != 0) {
switch (tcpc->typec_remote_rp_level) {
case TYPEC_CC_VOLT_SNK_1_5:
ma = 1500;
break;
case TYPEC_CC_VOLT_SNK_3_0:
ma = 3000;
break;
default:
case TYPEC_CC_VOLT_SNK_DFT:
ma = tcpc->typec_usb_sink_curr;
break;
}
#if CONFIG_TYPEC_SNK_CURR_LIMIT > 0
if (ma > CONFIG_TYPEC_SNK_CURR_LIMIT)
ma = CONFIG_TYPEC_SNK_CURR_LIMIT;
#endif /* CONFIG_TYPEC_SNK_CURR_LIMIT */
} else
ma = 0;
}
tcp_noti.vbus_state.ma = ma;
tcp_noti.vbus_state.mv = mv;
tcp_noti.vbus_state.type = type;
TCPC_DBG("sink_vbus: %d mV, %d mA\n", mv, ma);
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_SINK_VBUS);
return ret;
}
int tcpci_disable_vbus_control(struct tcpc_device *tcpc)
{
#ifdef CONFIG_TYPEC_USE_DIS_VBUS_CTRL
struct tcp_notify tcp_noti;
int ret;
TCPC_DBG("disable_vbus\n");
tcpci_enable_watchdog(tcpc, false);
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_DIS_VBUS_CTRL);
return ret;
#else
tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_REMOVE, TCPC_VBUS_SINK_0V, 0);
tcpci_source_vbus(tcpc, TCP_VBUS_CTRL_REMOVE, TCPC_VBUS_SOURCE_0V, 0);
return 0;
#endif /* CONFIG_TYPEC_USE_DIS_VBUS_CTRL */
}
int tcpci_notify_attachwait_state(struct tcpc_device *tcpc, bool as_sink)
{
#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT
uint8_t notify = 0;
struct tcp_notify tcp_noti;
int ret;
#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK
if (as_sink)
notify = TCP_NOTIFY_ATTACHWAIT_SNK;
#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK */
#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SRC
if (!as_sink)
notify = TCP_NOTIFY_ATTACHWAIT_SRC;
#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SRC */
if (notify == 0)
return 0;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_VBUS, notify);
return ret;
#else
return 0;
#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT */
}
int tcpci_enable_auto_discharge(struct tcpc_device *tcpc, bool en)
{
int ret = 0;
#ifdef CONFIG_TYPEC_CAP_AUTO_DISCHARGE
#ifdef CONFIG_TCPC_AUTO_DISCHARGE_IC
if (tcpc->typec_auto_discharge != en) {
tcpc->typec_auto_discharge = en;
if (tcpc->ops->set_auto_discharge)
ret = tcpc->ops->set_auto_discharge(tcpc, en);
}
#endif /* CONFIG_TCPC_AUTO_DISCHARGE_IC */
#endif /* CONFIG_TYPEC_CAP_AUTO_DISCHARGE */
return ret;
}
static int __tcpci_enable_force_discharge(
struct tcpc_device *tcpc, bool en, int mv)
{
int ret = 0;
#ifdef CONFIG_TYPEC_CAP_FORCE_DISCHARGE
#ifdef CONFIG_TCPC_FORCE_DISCHARGE_IC
if (tcpc->pd_force_discharge != en) {
tcpc->pd_force_discharge = en;
if (tcpc->ops->set_force_discharge)
ret = tcpc->ops->set_force_discharge(tcpc, en, mv);
}
#endif /* CONFIG_TCPC_FORCE_DISCHARGE_IC */
#endif /* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */
return ret;
}
static int __tcpci_enable_ext_discharge(struct tcpc_device *tcpc, bool en)
{
int ret = 0;
#ifdef CONFIG_TCPC_EXT_DISCHARGE
struct tcp_notify tcp_noti;
if (tcpc->typec_ext_discharge != en) {
tcpc->typec_ext_discharge = en;
tcp_noti.en_state.en = en;
TCPC_DBG("EXT-Discharge: %d\n", en);
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_EXT_DISCHARGE);
}
#endif /* CONFIG_TCPC_EXT_DISCHARGE */
return ret;
}
int tcpci_enable_force_discharge(struct tcpc_device *tcpc, bool en, int mv)
{
int ret = 0;
#ifdef CONFIG_TYPEC_CAP_FORCE_DISCHARGE
ret = __tcpci_enable_force_discharge(tcpc, en, mv);
#ifdef CONFIG_TCPC_FORCE_DISCHARGE_EXT
ret = __tcpci_enable_ext_discharge(tcpc, en);
#endif /* CONFIG_TCPC_FORCE_DISCHARGE_EXT */
#endif /* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */
return ret;
}
#ifdef CONFIG_USB_POWER_DELIVERY
int tcpci_notify_hard_reset_state(struct tcpc_device *tcpc, uint8_t state)
{
struct tcp_notify tcp_noti;
int ret;
tcp_noti.hreset_state.state = state;
if (state >= TCP_HRESET_SIGNAL_SEND)
tcpc->pd_wait_hard_reset_complete = true;
else if (tcpc->pd_wait_hard_reset_complete)
tcpc->pd_wait_hard_reset_complete = false;
else
return 0;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MISC, TCP_NOTIFY_HARD_RESET_STATE);
return ret;
}
int tcpci_enter_mode(struct tcpc_device *tcpc,
uint16_t svid, uint8_t ops, uint32_t mode)
{
struct tcp_notify tcp_noti;
int ret;
tcp_noti.mode_ctrl.svid = svid;
tcp_noti.mode_ctrl.ops = ops;
tcp_noti.mode_ctrl.mode = mode;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_ENTER_MODE);
return ret;
}
int tcpci_exit_mode(struct tcpc_device *tcpc, uint16_t svid)
{
struct tcp_notify tcp_noti;
int ret;
tcp_noti.mode_ctrl.svid = svid;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_EXIT_MODE);
return ret;
}
#ifdef CONFIG_USB_PD_ALT_MODE
int tcpci_report_hpd_state(struct tcpc_device *tcpc, uint32_t dp_status)
{
struct tcp_notify tcp_noti;
struct dp_data *dp_data = pd_get_dp_data(&tcpc->pd_port);
/* UFP_D to DFP_D only */
if (PD_DP_CFG_DFP_D(dp_data->local_config)) {
tcp_noti.ama_dp_hpd_state.irq = PD_VDO_DPSTS_HPD_IRQ(dp_status);
tcp_noti.ama_dp_hpd_state.state =
PD_VDO_DPSTS_HPD_LVL(dp_status);
tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_AMA_DP_HPD_STATE);
}
return 0;
}
int tcpci_dp_status_update(struct tcpc_device *tcpc, uint32_t dp_status)
{
DP_INFO("Status0: 0x%x\n", dp_status);
tcpci_report_hpd_state(tcpc, dp_status);
return 0;
}
int tcpci_dp_configure(struct tcpc_device *tcpc, uint32_t dp_config)
{
struct tcp_notify tcp_noti;
int ret;
DP_INFO("LocalCFG: 0x%x\n", dp_config);
switch (dp_config & 0x03) {
case 0:
tcp_noti.ama_dp_state.sel_config = SW_USB;
break;
case MODE_DP_SNK:
tcp_noti.ama_dp_state.sel_config = SW_DFP_D;
tcp_noti.ama_dp_state.pin_assignment = (dp_config >> 8) & 0xff;
break;
case MODE_DP_SRC:
tcp_noti.ama_dp_state.sel_config = SW_UFP_D;
tcp_noti.ama_dp_state.pin_assignment = (dp_config >> 16) & 0xff;
break;
}
if (tcp_noti.ama_dp_state.pin_assignment == 0)
tcp_noti.ama_dp_state.pin_assignment = (dp_config >> 16) & 0xff;
DP_INFO("pin assignment: 0x%x\n",
tcp_noti.ama_dp_state.pin_assignment);
tcp_noti.ama_dp_state.signal = (dp_config >> 2) & 0x0f;
tcp_noti.ama_dp_state.polarity = tcpc->typec_polarity;
tcp_noti.ama_dp_state.active = 1;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_AMA_DP_STATE);
return ret;
}
int tcpci_dp_attention(struct tcpc_device *tcpc, uint32_t dp_status)
{
/* DFP_U : Not call this function during internal flow */
struct tcp_notify tcp_noti;
DP_INFO("Attention: 0x%x\n", dp_status);
tcp_noti.ama_dp_attention.state = (uint8_t) dp_status;
tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_AMA_DP_ATTENTION);
return tcpci_report_hpd_state(tcpc, dp_status);
}
int tcpci_dp_notify_status_update_done(
struct tcpc_device *tcpc, uint32_t dp_status, bool ack)
{
/* DFP_U : Not call this function during internal flow */
DP_INFO("Status1: 0x%x, ack=%d\n", dp_status, ack);
return 0;
}
int tcpci_dp_notify_config_start(struct tcpc_device *tcpc)
{
/* DFP_U : Put signal & mux into the Safe State */
struct tcp_notify tcp_noti;
DP_INFO("ConfigStart\n");
tcp_noti.ama_dp_state.sel_config = SW_USB;
tcp_noti.ama_dp_state.active = 0;
tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_AMA_DP_STATE);
return 0;
}
int tcpci_dp_notify_config_done(struct tcpc_device *tcpc,
uint32_t local_cfg, uint32_t remote_cfg, bool ack)
{
/* DFP_U : If DP success,
* internal flow will enter this function finally
*/
DP_INFO("ConfigDone, L:0x%x, R:0x%x, ack=%d\n",
local_cfg, remote_cfg, ack);
if (ack)
tcpci_dp_configure(tcpc, local_cfg);
return 0;
}
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifdef CONFIG_USB_PD_CUSTOM_VDM
int tcpci_notify_uvdm(struct tcpc_device *tcpc, bool ack)
{
struct tcp_notify tcp_noti;
struct pd_port *pd_port = &tcpc->pd_port;
tcp_noti.uvdm_msg.ack = ack;
if (ack) {
tcp_noti.uvdm_msg.uvdm_cnt = pd_port->uvdm_cnt;
tcp_noti.uvdm_msg.uvdm_svid = pd_port->uvdm_svid;
tcp_noti.uvdm_msg.uvdm_data = pd_port->uvdm_data;
}
tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_UVDM);
return 0;
}
#endif /* CONFIG_USB_PD_CUSTOM_VDM */
#ifdef CONFIG_USB_PD_ALT_MODE_RTDC
int tcpci_dc_notify_en_unlock(struct tcpc_device *tcpc)
{
struct tcp_notify tcp_noti;
int ret;
DC_INFO("DirectCharge en_unlock\n");
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_DC_EN_UNLOCK);
return ret;
}
#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */
/* ---- Policy Engine (PD30) ---- */
#ifdef CONFIG_USB_PD_REV30
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
int tcpci_notify_alert(struct tcpc_device *tcpc, uint32_t ado)
{
struct tcp_notify tcp_noti;
int ret;
tcp_noti.alert_msg.ado = ado;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MISC, TCP_NOTIFY_ALERT);
return ret;
}
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
int tcpci_notify_status(struct tcpc_device *tcpc, struct pd_status *sdb)
{
struct tcp_notify tcp_noti;
int ret;
tcp_noti.status_msg.sdb = sdb;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MISC, TCP_NOTIFY_STATUS);
return ret;
}
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
#ifdef CONFIG_USB_PD_REV30_BAT_INFO
int tcpci_notify_request_bat_info(
struct tcpc_device *tcpc, enum pd_battery_reference ref)
{
struct tcp_notify tcp_noti;
int ret;
tcp_noti.request_bat.ref = ref;
ret = tcpc_check_notify_time(tcpc, &tcp_noti,
TCP_NOTIFY_IDX_MISC, TCP_NOTIFY_REQUEST_BAT_INFO);
return ret;
}
#endif /* CONFIG_USB_PD_REV30_BAT_INFO */
#endif /* CONFIG_USB_PD_REV30 */
#endif /* CONFIG_USB_POWER_DELIVERY */

View file

@ -0,0 +1,689 @@
/*
* Copyright (C) 2021 Richtek Inc.
*
* TCPC Interface for alert handler
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/cpu.h>
#include "inc/tcpci.h"
#include "inc/tcpci_typec.h"
#ifdef CONFIG_USB_POWER_DELIVERY
#include "inc/tcpci_event.h"
#endif /* CONFIG_USB_POWER_DELIVERY */
#ifdef CONFIG_DUAL_ROLE_USB_INTF
#include <linux/usb/class-dual-role.h>
#endif /* CONFIG_DUAL_ROLE_USB_INTF */
/*
* [BLOCK] TCPCI IRQ Handler
*/
static int tcpci_alert_cc_changed(struct tcpc_device *tcpc)
{
return tcpc_typec_handle_cc_change(tcpc);
}
#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
static inline int tcpci_alert_vsafe0v(struct tcpc_device *tcpc)
{
tcpc_typec_handle_vsafe0v(tcpc);
#ifdef CONFIG_USB_POWER_DELIVERY
#ifdef CONFIG_USB_PD_SAFE0V_DELAY
tcpc_enable_timer(tcpc, PD_TIMER_VSAFE0V_DELAY);
#else
pd_put_vbus_safe0v_event(tcpc);
#endif /* CONFIG_USB_PD_SAFE0V_DELAY */
#endif /* CONFIG_USB_POWER_DELIVERY */
return 0;
}
#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
static inline void tcpci_vbus_level_init_v10(
struct tcpc_device *tcpc, uint16_t power_status)
{
mutex_lock(&tcpc->access_lock);
tcpc->vbus_level =
power_status & TCPC_REG_POWER_STATUS_VBUS_PRES ?
TCPC_VBUS_VALID : TCPC_VBUS_INVALID;
#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
if (power_status & TCPC_REG_POWER_STATUS_EXT_VSAFE0V) {
if (tcpc->vbus_level == TCPC_VBUS_INVALID)
tcpc->vbus_level = TCPC_VBUS_SAFE0V;
else
TCPC_INFO("ps_confused: 0x%02x\n", power_status);
}
#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
mutex_unlock(&tcpc->access_lock);
}
static inline void __tcpci_vbus_level_refresh(struct tcpc_device *tcpc)
{
tcpc->vbus_level = tcpc->vbus_present ? TCPC_VBUS_VALID :
TCPC_VBUS_INVALID;
#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
if (tcpc->vbus_safe0v) {
if (tcpc->vbus_level == TCPC_VBUS_INVALID)
tcpc->vbus_level = TCPC_VBUS_SAFE0V;
else
TCPC_INFO("ps_confused: %d\n", tcpc->vbus_level);
}
#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
}
static inline void tcpci_vbus_level_refresh(struct tcpc_device *tcpc)
{
mutex_lock(&tcpc->access_lock);
__tcpci_vbus_level_refresh(tcpc);
mutex_unlock(&tcpc->access_lock);
}
void tcpci_vbus_level_init(struct tcpc_device *tcpc, uint16_t status)
{
if (tcpc->tcpc_flags & TCPC_FLAGS_ALERT_V10) {
tcpci_vbus_level_init_v10(tcpc, status);
return;
}
mutex_lock(&tcpc->access_lock);
tcpc->vbus_present = status & TCPC_REG_POWER_STATUS_VBUS_PRES ?
true : false;
__tcpci_vbus_level_refresh(tcpc);
mutex_unlock(&tcpc->access_lock);
}
static inline int tcpci_alert_power_status_changed_v10(struct tcpc_device *tcpc)
{
int rv = 0;
bool show_msg = true;
uint16_t power_status = 0;
rv = tcpci_get_power_status(tcpc, &power_status);
if (rv < 0)
return rv;
#ifdef CONFIG_USB_PD_DIRECT_CHARGE
if (tcpc->pd_during_direct_charge && tcpc->vbus_level != 0)
show_msg = false;
#endif /* CONFIG_USB_PD_DIRECT_CHARGE */
if (show_msg)
TCPC_INFO("ps_change=%d\n", tcpc->vbus_level);
rv = tcpc_typec_handle_ps_change(tcpc, tcpc->vbus_level);
if (rv < 0)
return rv;
#ifdef CONFIG_USB_POWER_DELIVERY
pd_put_vbus_changed_event(tcpc, true);
#endif /* CONFIG_USB_POWER_DELIVERY */
#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
if (tcpc->vbus_level == TCPC_VBUS_SAFE0V)
rv = tcpci_alert_vsafe0v(tcpc);
#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
return rv;
}
static inline int tcpci_vbus_level_changed(struct tcpc_device *tcpc)
{
int rv = 0;
bool show_msg = true;
#ifdef CONFIG_USB_PD_DIRECT_CHARGE
if (tcpc->pd_during_direct_charge && tcpc->vbus_level != 0)
show_msg = false;
#endif /* CONFIG_USB_PD_DIRECT_CHARGE */
if (show_msg)
TCPC_INFO("ps_change=%d\n", tcpc->vbus_level);
rv = tcpc_typec_handle_ps_change(tcpc, tcpc->vbus_level);
if (rv < 0)
return rv;
#ifdef CONFIG_USB_POWER_DELIVERY
pd_put_vbus_changed_event(tcpc, true);
#endif /* CONFIG_USB_POWER_DELIVERY */
#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC
if (tcpc->vbus_level == TCPC_VBUS_SAFE0V)
rv = tcpci_alert_vsafe0v(tcpc);
#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */
return rv;
}
static int tcpci_alert_power_status_changed(struct tcpc_device *tcpc)
{
int rv = 0;
uint16_t status = 0;
if (tcpc->tcpc_flags & TCPC_FLAGS_ALERT_V10)
return tcpci_alert_power_status_changed_v10(tcpc);
rv = tcpci_get_power_status(tcpc, &status);
if (rv < 0)
return rv;
tcpc->vbus_present = (status & TCPC_REG_POWER_STATUS_VBUS_PRES) ?
true : false;
return rv;
}
#ifdef CONFIG_USB_POWER_DELIVERY
static int tcpci_alert_tx_success(struct tcpc_device *tcpc)
{
uint8_t tx_state;
struct pd_event evt = {
.event_type = PD_EVT_CTRL_MSG,
.msg = PD_CTRL_GOOD_CRC,
.pd_msg = NULL,
};
mutex_lock(&tcpc->access_lock);
tx_state = tcpc->pd_transmit_state;
tcpc->pd_transmit_state = PD_TX_STATE_GOOD_CRC;
mutex_unlock(&tcpc->access_lock);
if (tx_state == PD_TX_STATE_WAIT_CRC_VDM)
pd_put_vdm_event(tcpc, &evt, false);
else
pd_put_event(tcpc, &evt, false);
return 0;
}
static int tcpci_alert_tx_failed(struct tcpc_device *tcpc)
{
uint8_t tx_state;
mutex_lock(&tcpc->access_lock);
tx_state = tcpc->pd_transmit_state;
tcpc->pd_transmit_state = PD_TX_STATE_NO_GOOD_CRC;
mutex_unlock(&tcpc->access_lock);
if (tx_state == PD_TX_STATE_WAIT_CRC_VDM)
vdm_put_hw_event(tcpc, PD_HW_TX_FAILED);
else
pd_put_hw_event(tcpc, PD_HW_TX_FAILED);
return 0;
}
static int tcpci_alert_tx_discard(struct tcpc_device *tcpc)
{
uint8_t tx_state;
bool retry_crc_discard = false;
mutex_lock(&tcpc->access_lock);
tx_state = tcpc->pd_transmit_state;
tcpc->pd_transmit_state = PD_TX_STATE_DISCARD;
mutex_unlock(&tcpc->access_lock);
TCPC_INFO("Discard\n");
if (tx_state == PD_TX_STATE_WAIT_CRC_VDM)
pd_put_last_vdm_event(tcpc);
else {
retry_crc_discard =
(tcpc->tcpc_flags &
TCPC_FLAGS_RETRY_CRC_DISCARD) != 0;
if (retry_crc_discard) {
#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD
tcpc->pd_discard_pending = true;
tcpc_enable_timer(tcpc, PD_TIMER_DISCARD);
#else
TCPC_ERR("RETRY_CRC_DISCARD\n");
#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
} else {
pd_put_hw_event(tcpc, PD_HW_TX_FAILED);
}
}
return 0;
}
static int tcpci_alert_recv_msg(struct tcpc_device *tcpc)
{
int retval;
struct pd_msg *pd_msg;
enum tcpm_transmit_type type;
pd_msg = pd_alloc_msg(tcpc);
if (pd_msg == NULL) {
tcpci_alert_status_clear(tcpc, TCPC_REG_ALERT_RX_MASK);
return -EINVAL;
}
retval = tcpci_get_message(tcpc,
pd_msg->payload, &pd_msg->msg_hdr, &type);
if (retval < 0) {
TCPC_INFO("recv_msg failed: %d\n", retval);
pd_free_msg(tcpc, pd_msg);
return retval;
}
pd_msg->frame_type = (uint8_t) type;
pd_put_pd_msg_event(tcpc, pd_msg);
return 0;
}
static int tcpci_alert_rx_overflow(struct tcpc_device *tcpc)
{
int rv;
uint32_t alert_status;
TCPC_INFO("RX_OVERFLOW\n");
rv = tcpci_get_alert_status(tcpc, &alert_status);
if (rv)
return rv;
if (alert_status & TCPC_REG_ALERT_RX_STATUS)
return tcpci_alert_recv_msg(tcpc);
return 0;
}
static int tcpci_alert_recv_hard_reset(struct tcpc_device *tcpc)
{
TCPC_INFO("HardResetAlert\n");
pd_put_recv_hard_reset_event(tcpc);
tcpci_init_alert_mask(tcpc);
return 0;
}
#endif /* CONFIG_USB_POWER_DELIVERY */
static int tcpci_alert_vendor_defined(struct tcpc_device *tcpc)
{
tcpci_alert_vendor_defined_handler(tcpc);
return 0;
}
static int tcpci_alert_fault(struct tcpc_device *tcpc)
{
uint8_t status = 0;
tcpci_get_fault_status(tcpc, &status);
TCPC_INFO("FaultAlert=0x%x\n", status);
tcpci_fault_status_clear(tcpc, status);
return 0;
}
EXPORT_SYMBOL(tcpci_alert);
#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG
static int tcpci_alert_wakeup(struct tcpc_device *tcpc)
{
if (tcpc->tcpc_flags & TCPC_FLAGS_LPM_WAKEUP_WATCHDOG) {
TCPC_INFO("Wakeup\n");
if (tcpc->typec_remote_cc[0] == TYPEC_CC_DRP_TOGGLING)
tcpc_enable_wakeup_timer(tcpc, true);
}
return 0;
}
#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */
#ifdef CONFIG_TYPEC_CAP_RA_DETACH
static int tcpci_alert_ra_detach(struct tcpc_device *tcpc)
{
if (tcpc->tcpc_flags & TCPC_FLAGS_CHECK_RA_DETACHE) {
TCPC_DBG("RA_DETACH\n");
if (tcpc->typec_remote_cc[0] == TYPEC_CC_DRP_TOGGLING)
tcpc_typec_enter_lpm_again(tcpc);
}
return 0;
}
#endif /* CONFIG_TYPEC_CAP_RA_DETACH */
struct tcpci_alert_handler {
uint32_t bit_mask;
int (*handler)(struct tcpc_device *tcpc);
};
#define DECL_TCPCI_ALERT_HANDLER(xbit, xhandler) {\
.bit_mask = 1 << xbit,\
.handler = xhandler, \
}
static const struct tcpci_alert_handler tcpci_alert_handlers[] = {
DECL_TCPCI_ALERT_HANDLER(15, tcpci_alert_vendor_defined),
#ifdef CONFIG_USB_POWER_DELIVERY
DECL_TCPCI_ALERT_HANDLER(4, tcpci_alert_tx_failed),
DECL_TCPCI_ALERT_HANDLER(5, tcpci_alert_tx_discard),
DECL_TCPCI_ALERT_HANDLER(6, tcpci_alert_tx_success),
DECL_TCPCI_ALERT_HANDLER(2, tcpci_alert_recv_msg),
DECL_TCPCI_ALERT_HANDLER(7, NULL),
DECL_TCPCI_ALERT_HANDLER(8, NULL),
DECL_TCPCI_ALERT_HANDLER(3, tcpci_alert_recv_hard_reset),
DECL_TCPCI_ALERT_HANDLER(10, tcpci_alert_rx_overflow),
#endif /* CONFIG_USB_POWER_DELIVERY */
#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG
DECL_TCPCI_ALERT_HANDLER(16, tcpci_alert_wakeup),
#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */
#ifdef CONFIG_TYPEC_CAP_RA_DETACH
DECL_TCPCI_ALERT_HANDLER(21, tcpci_alert_ra_detach),
#endif /* CONFIG_TYPEC_CAP_RA_DETACH */
DECL_TCPCI_ALERT_HANDLER(9, tcpci_alert_fault),
DECL_TCPCI_ALERT_HANDLER(0, tcpci_alert_cc_changed),
DECL_TCPCI_ALERT_HANDLER(1, tcpci_alert_power_status_changed),
};
#ifdef CONFIG_USB_POWER_DELIVERY
static inline bool tcpci_check_hard_reset_complete(
struct tcpc_device *tcpc, uint32_t alert_status)
{
if ((alert_status & TCPC_REG_ALERT_HRESET_SUCCESS)
== TCPC_REG_ALERT_HRESET_SUCCESS) {
pd_put_sent_hard_reset_event(tcpc);
return true;
}
if (alert_status & TCPC_REG_ALERT_TX_DISCARDED) {
TCPC_INFO("HResetFailed\n");
tcpci_transmit(tcpc, TCPC_TX_HARD_RESET, 0, NULL);
return false;
}
return false;
}
#endif /* CONFIG_USB_POWER_DELIVERY */
int tcpci_alert(struct tcpc_device *tcpc)
{
int rv, i;
uint32_t alert_status;
uint32_t alert_mask;
rv = tcpci_get_alert_status(tcpc, &alert_status);
if (rv)
return rv;
rv = tcpci_get_alert_mask(tcpc, &alert_mask);
if (rv)
return rv;
/* mask all alert */
rv = tcpci_set_alert_mask(tcpc, 0);
if (rv)
return rv;
#ifdef CONFIG_USB_PD_DBG_ALERT_STATUS
if (alert_status != 0)
TCPC_INFO("Alert:0x%04x, Mask:0x%04x\n",
alert_status, alert_mask);
#endif /* CONFIG_USB_PD_DBG_ALERT_STATUS */
alert_status &= alert_mask;
tcpci_alert_status_clear(tcpc,
alert_status & (~TCPC_REG_ALERT_RX_MASK));
if (tcpc->typec_role == TYPEC_ROLE_UNKNOWN)
return 0;
if ((tcpc->tcpc_flags & TCPC_FLAGS_ALERT_V10) &&
(alert_status & TCPC_REG_ALERT_EXT_VBUS_80))
alert_status |= TCPC_REG_ALERT_POWER_STATUS;
#ifdef CONFIG_USB_POWER_DELIVERY
if (tcpc->pd_transmit_state == PD_TX_STATE_WAIT_HARD_RESET) {
tcpci_check_hard_reset_complete(tcpc, alert_status);
alert_status &= ~TCPC_REG_ALERT_TX_MASK;
}
#endif /* CONFIG_USB_POWER_DELIVERY */
#ifndef CONFIG_USB_PD_DBG_SKIP_ALERT_HANDLER
for (i = 0; i < ARRAY_SIZE(tcpci_alert_handlers); i++) {
if (tcpci_alert_handlers[i].bit_mask & alert_status) {
if (tcpci_alert_handlers[i].handler != 0)
tcpci_alert_handlers[i].handler(tcpc);
}
}
#endif /* CONFIG_USB_PD_DBG_SKIP_ALERT_HANDLER */
/* unmask alert */
rv = tcpci_set_alert_mask(tcpc, alert_mask);
if (rv)
return rv;
if (tcpc->tcpc_flags & TCPC_FLAGS_ALERT_V10)
return 0;
tcpci_vbus_level_refresh(tcpc);
tcpci_vbus_level_changed(tcpc);
return 0;
}
/*
* [BLOCK] TYPEC device changed
*/
static inline void tcpci_attach_wake_lock(struct tcpc_device *tcpc)
{
#ifdef CONFIG_TCPC_ATTACH_WAKE_LOCK_TOUT
__pm_wakeup_event(tcpc->attach_wake_lock,
CONFIG_TCPC_ATTACH_WAKE_LOCK_TOUT);
#else
__pm_stay_awake(tcpc->attach_wake_lock);
#endif /* CONFIG_TCPC_ATTACH_WAKE_LOCK_TOUT */
}
int tcpci_set_wake_lock(
struct tcpc_device *tcpc, bool pd_lock, bool user_lock)
{
bool ori_lock, new_lock;
if (tcpc->wake_lock_pd && tcpc->wake_lock_user)
ori_lock = true;
else
ori_lock = false;
if (pd_lock && user_lock)
new_lock = true;
else
new_lock = false;
if (new_lock != ori_lock) {
if (new_lock) {
TCPC_DBG("wake_lock=1\n");
tcpci_attach_wake_lock(tcpc);
if (tcpc->typec_watchdog)
tcpci_set_intrst(tcpc, true);
} else {
TCPC_DBG("wake_lock=0\n");
if (tcpc->typec_watchdog)
tcpci_set_intrst(tcpc, false);
__pm_relax(tcpc->attach_wake_lock);
}
return 1;
}
return 0;
}
static inline int tcpci_set_wake_lock_pd(
struct tcpc_device *tcpc, bool pd_lock)
{
uint8_t wake_lock_pd;
mutex_lock(&tcpc->access_lock);
wake_lock_pd = tcpc->wake_lock_pd;
if (pd_lock)
wake_lock_pd++;
else if (wake_lock_pd > 0)
wake_lock_pd--;
if (wake_lock_pd == 0)
__pm_wakeup_event(tcpc->detach_wake_lock, 5000);
tcpci_set_wake_lock(tcpc, wake_lock_pd, tcpc->wake_lock_user);
if (wake_lock_pd == 1)
__pm_relax(tcpc->detach_wake_lock);
tcpc->wake_lock_pd = wake_lock_pd;
mutex_unlock(&tcpc->access_lock);
return 0;
}
static inline int tcpci_report_usb_port_attached(struct tcpc_device *tcpc)
{
TCPC_INFO("usb_port_attached\n");
#ifdef CONFIG_DUAL_ROLE_USB_INTF
switch (tcpc->typec_attach_new) {
case TYPEC_ATTACHED_SNK:
case TYPEC_ATTACHED_CUSTOM_SRC:
case TYPEC_ATTACHED_NORP_SRC:
tcpc->dual_role_pr = DUAL_ROLE_PROP_PR_SNK;
tcpc->dual_role_dr = DUAL_ROLE_PROP_DR_DEVICE;
tcpc->dual_role_mode = DUAL_ROLE_PROP_MODE_UFP;
tcpc->dual_role_vconn = DUAL_ROLE_PROP_VCONN_SUPPLY_NO;
break;
case TYPEC_ATTACHED_SRC:
tcpc->dual_role_pr = DUAL_ROLE_PROP_PR_SRC;
tcpc->dual_role_dr = DUAL_ROLE_PROP_DR_HOST;
tcpc->dual_role_mode = DUAL_ROLE_PROP_MODE_DFP;
tcpc->dual_role_vconn = DUAL_ROLE_PROP_VCONN_SUPPLY_YES;
break;
default:
break;
}
dual_role_instance_changed(tcpc->dr_usb);
#endif /* CONFIG_DUAL_ROLE_USB_INTF */
tcpci_set_wake_lock_pd(tcpc, true);
#ifdef CONFIG_USB_POWER_DELIVERY
#ifdef CONFIG_USB_PD_DISABLE_PE
if (tcpc->disable_pe)
return 0;
#endif /* CONFIG_USB_PD_DISABLE_PE */
if (tcpc->pd_inited_flag)
pd_put_cc_attached_event(tcpc, tcpc->typec_attach_new);
#endif /* CONFIG_USB_POWER_DLEIVERY */
return 0;
}
static inline int tcpci_report_usb_port_detached(struct tcpc_device *tcpc)
{
TCPC_INFO("usb_port_detached\n");
#ifdef CONFIG_DUAL_ROLE_USB_INTF
tcpc->dual_role_pr = DUAL_ROLE_PROP_PR_NONE;
tcpc->dual_role_dr = DUAL_ROLE_PROP_DR_NONE;
tcpc->dual_role_mode = DUAL_ROLE_PROP_MODE_NONE;
tcpc->dual_role_vconn = DUAL_ROLE_PROP_VCONN_SUPPLY_NO;
dual_role_instance_changed(tcpc->dr_usb);
#endif /* CONFIG_DUAL_ROLE_USB_INTF */
#ifdef CONFIG_USB_POWER_DELIVERY
if (tcpc->pd_inited_flag)
pd_put_cc_detached_event(tcpc);
else {
pd_event_buf_reset(tcpc);
tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_PE_IDLE);
}
#endif /* CONFIG_USB_POWER_DELIVERY */
tcpci_set_wake_lock_pd(tcpc, false);
return 0;
}
int tcpci_report_usb_port_changed(struct tcpc_device *tcpc)
{
tcpci_notify_typec_state(tcpc);
if (tcpc->typec_attach_old == TYPEC_UNATTACHED)
tcpci_report_usb_port_attached(tcpc);
else if (tcpc->typec_attach_new == TYPEC_UNATTACHED)
tcpci_report_usb_port_detached(tcpc);
else
TCPC_DBG2("TCPC Attach Again\n");
return 0;
}
EXPORT_SYMBOL(tcpci_report_usb_port_changed);
/*
* [BLOCK] TYPEC power control changed
*/
static inline int tcpci_report_power_control_on(struct tcpc_device *tcpc)
{
tcpci_set_wake_lock_pd(tcpc, true);
mutex_lock(&tcpc->access_lock);
tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_DISCHARGE);
tcpci_enable_auto_discharge(tcpc, true);
tcpci_enable_force_discharge(tcpc, false, 0);
mutex_unlock(&tcpc->access_lock);
return 0;
}
static inline int tcpci_report_power_control_off(struct tcpc_device *tcpc)
{
mutex_lock(&tcpc->access_lock);
tcpci_enable_force_discharge(tcpc, true, 0);
tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_DISCHARGE);
mutex_unlock(&tcpc->access_lock);
tcpci_set_wake_lock_pd(tcpc, false);
return 0;
}
int tcpci_report_power_control(struct tcpc_device *tcpc, bool en)
{
if (tcpc->typec_power_ctrl == en)
return 0;
tcpc->typec_power_ctrl = en;
if (en)
tcpci_report_power_control_on(tcpc);
else
tcpci_report_power_control_off(tcpc);
return 0;
}

View file

@ -0,0 +1,980 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* Richtek TypeC Port Control Interface Core Driver
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/power_supply.h>
#include <linux/workqueue.h>
#include "inc/tcpci.h"
#include "inc/tcpci_typec.h"
#ifdef CONFIG_USB_POWER_DELIVERY
#include "pd_dpm_prv.h"
#include "inc/tcpm.h"
#ifdef CONFIG_RECV_BAT_ABSENT_NOTIFY
#include "mtk_battery.h"
#endif /* CONFIG_RECV_BAT_ABSENT_NOTIFY */
#endif /* CONFIG_USB_POWER_DELIVERY */
#include "inc/pd_dbg_info.h"
#include "inc/rt-regmap.h"
#define TCPC_CORE_VERSION "2.0.14_G"
static ssize_t tcpc_show_property(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t tcpc_store_property(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count);
#define TCPC_DEVICE_ATTR(_name, _mode) \
{ \
.attr = { .name = #_name, .mode = _mode }, \
.show = tcpc_show_property, \
.store = tcpc_store_property, \
}
struct class *tcpc_class;
EXPORT_SYMBOL_GPL(tcpc_class);
static struct device_type tcpc_dev_type;
static struct device_attribute tcpc_device_attributes[] = {
TCPC_DEVICE_ATTR(role_def, 0444),
TCPC_DEVICE_ATTR(rp_lvl, 0444),
TCPC_DEVICE_ATTR(pd_test, 0664),
TCPC_DEVICE_ATTR(info, 0444),
TCPC_DEVICE_ATTR(timer, 0664),
TCPC_DEVICE_ATTR(caps_info, 0444),
TCPC_DEVICE_ATTR(pe_ready, 0444),
};
enum {
TCPC_DESC_ROLE_DEF = 0,
TCPC_DESC_RP_LEVEL,
TCPC_DESC_PD_TEST,
TCPC_DESC_INFO,
TCPC_DESC_TIMER,
TCPC_DESC_CAP_INFO,
TCPC_DESC_PE_READY,
};
static struct attribute *__tcpc_attrs[ARRAY_SIZE(tcpc_device_attributes) + 1];
static struct attribute_group tcpc_attr_group = {
.attrs = __tcpc_attrs,
};
static const struct attribute_group *tcpc_attr_groups[] = {
&tcpc_attr_group,
NULL,
};
static const char * const role_text[] = {
"SNK Only",
"SRC Only",
"DRP",
"Try.SRC",
"Try.SNK",
};
static ssize_t tcpc_show_property(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tcpc_device *tcpc = to_tcpc_device(dev);
const ptrdiff_t offset = attr - tcpc_device_attributes;
int i = 0;
#ifdef CONFIG_USB_POWER_DELIVERY
struct pe_data *pe_data;
struct pd_port *pd_port;
struct tcpm_power_cap_val cap;
#endif /* CONFIG_USB_POWER_DELIVERY */
switch (offset) {
#ifdef CONFIG_USB_POWER_DELIVERY
case TCPC_DESC_CAP_INFO:
pd_port = &tcpc->pd_port;
pe_data = &pd_port->pe_data;
snprintf(buf+strlen(buf), 256, "%s = %d\n%s = %d\n",
"local_selected_cap",
pe_data->local_selected_cap,
"remote_selected_cap",
pe_data->remote_selected_cap);
snprintf(buf+strlen(buf), 256, "%s\n",
"local_src_cap(type, vmin, vmax, oper)");
for (i = 0; i < pd_port->local_src_cap.nr; i++) {
tcpm_extract_power_cap_val(
pd_port->local_src_cap.pdos[i],
&cap);
snprintf(buf+strlen(buf), 256, "%d %d %d %d\n",
cap.type, cap.min_mv, cap.max_mv, cap.ma);
}
snprintf(buf+strlen(buf), 256, "%s\n",
"local_snk_cap(type, vmin, vmax, ioper)");
for (i = 0; i < pd_port->local_snk_cap.nr; i++) {
tcpm_extract_power_cap_val(
pd_port->local_snk_cap.pdos[i],
&cap);
snprintf(buf+strlen(buf), 256, "%d %d %d %d\n",
cap.type, cap.min_mv, cap.max_mv, cap.ma);
}
snprintf(buf+strlen(buf), 256, "%s\n",
"remote_src_cap(type, vmin, vmax, ioper)");
for (i = 0; i < pe_data->remote_src_cap.nr; i++) {
tcpm_extract_power_cap_val(
pe_data->remote_src_cap.pdos[i],
&cap);
snprintf(buf+strlen(buf), 256, "%d %d %d %d\n",
cap.type, cap.min_mv, cap.max_mv, cap.ma);
}
snprintf(buf+strlen(buf), 256, "%s\n",
"remote_snk_cap(type, vmin, vmax, ioper)");
for (i = 0; i < pe_data->remote_snk_cap.nr; i++) {
tcpm_extract_power_cap_val(
pe_data->remote_snk_cap.pdos[i],
&cap);
snprintf(buf+strlen(buf), 256, "%d %d %d %d\n",
cap.type, cap.min_mv, cap.max_mv, cap.ma);
}
break;
#endif /* CONFIG_USB_POWER_DELIVERY */
case TCPC_DESC_ROLE_DEF:
snprintf(buf, 256, "%s\n", role_text[tcpc->desc.role_def]);
break;
case TCPC_DESC_RP_LEVEL:
if (tcpc->typec_local_rp_level == TYPEC_CC_RP_DFT)
snprintf(buf, 256, "%s\n", "Default");
else if (tcpc->typec_local_rp_level == TYPEC_CC_RP_1_5)
snprintf(buf, 256, "%s\n", "1.5");
else if (tcpc->typec_local_rp_level == TYPEC_CC_RP_3_0)
snprintf(buf, 256, "%s\n", "3.0");
break;
case TCPC_DESC_PD_TEST:
snprintf(buf, 256, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
"1: pr_swap", "2: dr_swap", "3: vconn_swap",
"4: soft reset", "5: hard reset",
"6: get_src_cap", "7: get_sink_cap",
"8: discover_id", "9: discover_cable");
break;
case TCPC_DESC_INFO:
i += snprintf(buf + i,
256, "|^|==( %s info )==|^|\n", tcpc->desc.name);
i += snprintf(buf + i,
256, "role = %s\n", role_text[tcpc->desc.role_def]);
if (tcpc->typec_local_rp_level == TYPEC_CC_RP_DFT)
i += snprintf(buf + i, 256, "rplvl = %s\n", "Default");
else if (tcpc->typec_local_rp_level == TYPEC_CC_RP_1_5)
i += snprintf(buf + i, 256, "rplvl = %s\n", "1.5");
else if (tcpc->typec_local_rp_level == TYPEC_CC_RP_3_0)
i += snprintf(buf + i, 256, "rplvl = %s\n", "3.0");
break;
#ifdef CONFIG_USB_POWER_DELIVERY
case TCPC_DESC_PE_READY:
pd_port = &tcpc->pd_port;
if (pd_port->pe_data.pe_ready)
snprintf(buf, 256, "%s\n", "yes");
else
snprintf(buf, 256, "%s\n", "no");
break;
#endif
default:
break;
}
return strlen(buf);
}
static int get_parameters(char *buf, long int *param1, int num_of_par)
{
char *token;
int base, cnt;
token = strsep(&buf, " ");
for (cnt = 0; cnt < num_of_par; cnt++) {
if (token != NULL) {
if ((token[1] == 'x') || (token[1] == 'X'))
base = 16;
else
base = 10;
if (kstrtoul(token, base, &param1[cnt]) != 0)
return -EINVAL;
token = strsep(&buf, " ");
}
else
return -EINVAL;
}
return 0;
}
static ssize_t tcpc_store_property(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
#ifdef CONFIG_USB_POWER_DELIVERY
uint8_t role;
#endif /* CONFIG_USB_POWER_DELIVERY */
struct tcpc_device *tcpc = to_tcpc_device(dev);
const ptrdiff_t offset = attr - tcpc_device_attributes;
int ret;
long int val;
switch (offset) {
case TCPC_DESC_ROLE_DEF:
ret = get_parameters((char *)buf, &val, 1);
if (ret < 0) {
dev_err(dev, "get parameters fail\n");
return -EINVAL;
}
tcpm_typec_change_role(tcpc, val);
break;
case TCPC_DESC_TIMER:
ret = get_parameters((char *)buf, &val, 1);
if (ret < 0) {
dev_err(dev, "get parameters fail\n");
return -EINVAL;
}
#ifdef CONFIG_USB_POWER_DELIVERY
if (val > 0 && val <= PD_PE_TIMER_END_ID)
pd_enable_timer(&tcpc->pd_port, val);
else if (val > PD_PE_TIMER_END_ID && val < PD_TIMER_NR)
tcpc_enable_timer(tcpc, val);
#else
if (val > 0 && val < PD_TIMER_NR)
tcpc_enable_timer(tcpc, val);
#endif /* CONFIG_USB_POWER_DELIVERY */
break;
#ifdef CONFIG_USB_POWER_DELIVERY
case TCPC_DESC_PD_TEST:
ret = get_parameters((char *)buf, &val, 1);
if (ret < 0) {
dev_err(dev, "get parameters fail\n");
return -EINVAL;
}
switch (val) {
case 1: /* Power Role Swap */
role = tcpm_inquire_pd_power_role(tcpc);
if (role == PD_ROLE_SINK)
role = PD_ROLE_SOURCE;
else
role = PD_ROLE_SINK;
tcpm_dpm_pd_power_swap(tcpc, role, NULL);
break;
case 2: /* Data Role Swap */
role = tcpm_inquire_pd_data_role(tcpc);
if (role == PD_ROLE_UFP)
role = PD_ROLE_DFP;
else
role = PD_ROLE_UFP;
tcpm_dpm_pd_data_swap(tcpc, role, NULL);
break;
case 3: /* Vconn Swap */
role = tcpm_inquire_pd_vconn_role(tcpc);
if (role == PD_ROLE_VCONN_OFF)
role = PD_ROLE_VCONN_ON;
else
role = PD_ROLE_VCONN_OFF;
tcpm_dpm_pd_vconn_swap(tcpc, role, NULL);
break;
case 4: /* Software Reset */
tcpm_dpm_pd_soft_reset(tcpc, NULL);
break;
case 5: /* Hardware Reset */
tcpm_dpm_pd_hard_reset(tcpc, NULL);
break;
case 6:
tcpm_dpm_pd_get_source_cap(tcpc, NULL);
break;
case 7:
tcpm_dpm_pd_get_sink_cap(tcpc, NULL);
break;
case 8:
tcpm_dpm_vdm_discover_id(tcpc, NULL);
break;
case 9:
tcpm_dpm_vdm_discover_cable(tcpc, NULL);
break;
default:
break;
}
break;
#endif /* CONFIG_USB_POWER_DELIVERY */
default:
break;
}
return count;
}
static int tcpc_match_device_by_name(struct device *dev, const void *data)
{
const char *name = data;
struct tcpc_device *tcpc = dev_get_drvdata(dev);
return strcmp(tcpc->desc.name, name) == 0;
}
struct tcpc_device *tcpc_dev_get_by_name(const char *name)
{
struct device *dev = class_find_device(tcpc_class,
NULL, (const void *)name, tcpc_match_device_by_name);
return dev ? dev_get_drvdata(dev) : NULL;
}
EXPORT_SYMBOL(tcpc_dev_get_by_name);
static void tcpc_device_release(struct device *dev)
{
struct tcpc_device *tcpc = to_tcpc_device(dev);
pr_info("%s : %s device release\n", __func__, dev_name(dev));
PD_BUG_ON(tcpc == NULL);
/* Un-init pe thread */
#ifdef CONFIG_USB_POWER_DELIVERY
tcpci_event_deinit(tcpc);
#endif /* CONFIG_USB_POWER_DELIVERY */
/* Un-init timer thread */
tcpci_timer_deinit(tcpc);
/* Un-init Mutex */
/* Do initialization */
}
static void tcpc_init_work(struct work_struct *work);
static void tcpc_event_init_work(struct work_struct *work);
struct tcpc_device *tcpc_device_register(struct device *parent,
struct tcpc_desc *tcpc_desc, struct tcpc_ops *ops, void *drv_data)
{
struct tcpc_device *tcpc;
int ret = 0, i = 0;
pr_info("%s register tcpc device (%s)\n", __func__, tcpc_desc->name);
tcpc = devm_kzalloc(parent, sizeof(*tcpc), GFP_KERNEL);
if (!tcpc) {
pr_err("%s : allocate tcpc memeory failed\n", __func__);
return NULL;
}
tcpc->evt_wq = alloc_ordered_workqueue("%s", 0, tcpc_desc->name);
for (i = 0; i < TCP_NOTIFY_IDX_NR; i++)
srcu_init_notifier_head(&tcpc->evt_nh[i]);
mutex_init(&tcpc->access_lock);
mutex_init(&tcpc->typec_lock);
mutex_init(&tcpc->timer_lock);
mutex_init(&tcpc->mr_lock);
sema_init(&tcpc->timer_enable_mask_lock, 1);
spin_lock_init(&tcpc->timer_tick_lock);
tcpc->dev.class = tcpc_class;
tcpc->dev.type = &tcpc_dev_type;
tcpc->dev.parent = parent;
tcpc->dev.release = tcpc_device_release;
dev_set_drvdata(&tcpc->dev, tcpc);
tcpc->drv_data = drv_data;
dev_set_name(&tcpc->dev, "%s", tcpc_desc->name);
tcpc->desc = *tcpc_desc;
tcpc->ops = ops;
tcpc->typec_local_rp_level = tcpc_desc->rp_lvl;
#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE
tcpc->tcpc_vconn_supply = tcpc_desc->vconn_supply;
#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */
device_set_of_node_from_dev(&tcpc->dev, parent);
ret = device_register(&tcpc->dev);
if (ret) {
kfree(tcpc);
return ERR_PTR(ret);
}
INIT_DELAYED_WORK(&tcpc->init_work, tcpc_init_work);
INIT_DELAYED_WORK(&tcpc->event_init_work, tcpc_event_init_work);
/* If system support "WAKE_LOCK_IDLE",
* please use it instead of "WAKE_LOCK_SUSPEND"
*/
tcpc->attach_wake_lock =
wakeup_source_register(&tcpc->dev, "tcpc_attach_wake_lock");
tcpc->detach_wake_lock =
wakeup_source_register(&tcpc->dev, "tcpc_detach_wake_lock");
tcpci_timer_init(tcpc);
#ifdef CONFIG_USB_POWER_DELIVERY
pd_core_init(tcpc);
#endif /* CONFIG_USB_POWER_DELIVERY */
#ifdef CONFIG_DUAL_ROLE_USB_INTF
ret = tcpc_dual_role_phy_init(tcpc);
if (ret < 0)
dev_err(&tcpc->dev, "dual role usb init fail\n");
#endif /* CONFIG_DUAL_ROLE_USB_INTF */
return tcpc;
}
EXPORT_SYMBOL(tcpc_device_register);
int tcpc_device_irq_enable(struct tcpc_device *tcpc)
{
int ret;
if (!tcpc->ops->init) {
pr_notice("%s Please implment tcpc ops init function\n",
__func__);
return -EINVAL;
}
tcpci_lock_typec(tcpc);
ret = tcpci_init(tcpc, false);
if (ret < 0) {
tcpci_unlock_typec(tcpc);
pr_err("%s tcpc init fail\n", __func__);
return ret;
}
ret = tcpc_typec_init(tcpc, tcpc->desc.role_def + 1);
tcpci_unlock_typec(tcpc);
if (ret < 0) {
pr_err("%s : tcpc typec init fail\n", __func__);
return ret;
}
schedule_delayed_work(
&tcpc->event_init_work, msecs_to_jiffies(10*1000));
pr_info("%s : tcpc irq enable OK!\n", __func__);
return 0;
}
EXPORT_SYMBOL(tcpc_device_irq_enable);
#ifdef CONFIG_USB_PD_REV30
static void bat_update_work_func(struct work_struct *work)
{
struct tcpc_device *tcpc = container_of(work,
struct tcpc_device, bat_update_work.work);
union power_supply_propval value;
int ret;
ret = power_supply_get_property(
tcpc->bat_psy, POWER_SUPPLY_PROP_CAPACITY, &value);
if (ret == 0) {
TCPC_INFO("%s battery update soc = %d\n",
__func__, value.intval);
tcpc->bat_soc = value.intval;
} else
TCPC_ERR("%s get battery capacity fail\n", __func__);
ret = power_supply_get_property(tcpc->bat_psy,
POWER_SUPPLY_PROP_STATUS, &value);
if (ret == 0) {
if (value.intval == POWER_SUPPLY_STATUS_CHARGING) {
TCPC_INFO("%s Battery Charging\n", __func__);
tcpc->charging_status = BSDO_BAT_INFO_CHARGING;
} else if (value.intval == POWER_SUPPLY_STATUS_DISCHARGING) {
TCPC_INFO("%s Battery Discharging\n", __func__);
tcpc->charging_status = BSDO_BAT_INFO_DISCHARGING;
} else {
TCPC_INFO("%s Battery Idle\n", __func__);
tcpc->charging_status = BSDO_BAT_INFO_IDLE;
}
}
if (ret < 0)
TCPC_ERR("%s get battery charger now fail\n", __func__);
tcpm_update_bat_status_soc(tcpc,
PD_BAT_REF_FIXED0, tcpc->charging_status, tcpc->bat_soc * 10);
}
static int bat_nb_call_func(
struct notifier_block *nb, unsigned long val, void *v)
{
struct tcpc_device *tcpc = container_of(nb, struct tcpc_device, bat_nb);
struct power_supply *psy = (struct power_supply *)v;
if (!tcpc) {
TCPC_ERR("%s tcpc is null\n", __func__);
return NOTIFY_OK;
}
if (val == PSY_EVENT_PROP_CHANGED &&
strcmp(psy->desc->name, "battery") == 0)
schedule_delayed_work(&tcpc->bat_update_work, 0);
return NOTIFY_OK;
}
#endif /* CONFIG_USB_PD_REV30 */
static void tcpc_event_init_work(struct work_struct *work)
{
#ifdef CONFIG_USB_POWER_DELIVERY
struct tcpc_device *tcpc = container_of(
work, struct tcpc_device, event_init_work.work);
#ifdef CONFIG_USB_PD_REV30
int retval;
#endif /* CONFIG_USB_PD_REV30 */
tcpci_lock_typec(tcpc);
tcpci_event_init(tcpc);
#ifdef CONFIG_USB_PD_WAIT_BC12
tcpc->usb_psy = power_supply_get_by_name("usb");
if (!tcpc->usb_psy) {
tcpci_unlock_typec(tcpc);
TCPC_ERR("%s get usb psy fail\n", __func__);
return;
}
#endif /* CONFIG_USB_PD_WAIT_BC12 */
tcpc->pd_inited_flag = 1;
pr_info("%s typec attach new = %d\n",
__func__, tcpc->typec_attach_new);
if (tcpc->typec_attach_new)
pd_put_cc_attached_event(tcpc, tcpc->typec_attach_new);
tcpci_unlock_typec(tcpc);
#ifdef CONFIG_USB_PD_REV30
INIT_DELAYED_WORK(&tcpc->bat_update_work, bat_update_work_func);
tcpc->bat_psy = power_supply_get_by_name("battery");
if (!tcpc->bat_psy) {
TCPC_ERR("%s get battery psy fail\n", __func__);
return;
}
tcpc->charging_status = BSDO_BAT_INFO_IDLE;
tcpc->bat_soc = 0;
tcpc->bat_nb.notifier_call = bat_nb_call_func;
tcpc->bat_nb.priority = 0;
retval = power_supply_reg_notifier(&tcpc->bat_nb);
if (retval < 0)
pr_err("%s register power supply notifier fail\n", __func__);
#endif /* CONFIG_USB_PD_REV30 */
#endif /* CONFIG_USB_POWER_DELIVERY */
}
static void tcpc_init_work(struct work_struct *work)
{
struct tcpc_device *tcpc = container_of(
work, struct tcpc_device, init_work.work);
#ifndef CONFIG_TCPC_NOTIFIER_LATE_SYNC
if (tcpc->desc.notifier_supply_num == 0)
return;
#endif
pr_info("%s force start\n", __func__);
tcpc->desc.notifier_supply_num = 0;
tcpc_device_irq_enable(tcpc);
}
int tcpc_schedule_init_work(struct tcpc_device *tcpc)
{
#ifndef CONFIG_TCPC_NOTIFIER_LATE_SYNC
if (tcpc->desc.notifier_supply_num == 0)
return tcpc_device_irq_enable(tcpc);
pr_info("%s wait %d num\n", __func__, tcpc->desc.notifier_supply_num);
schedule_delayed_work(
&tcpc->init_work, msecs_to_jiffies(30*1000));
#endif
return 0;
}
EXPORT_SYMBOL(tcpc_schedule_init_work);
struct tcp_notifier_block_wrapper {
struct notifier_block stub_nb;
struct notifier_block *action_nb;
};
static int tcp_notifier_func_stub(struct notifier_block *nb,
unsigned long action, void *data)
{
struct tcp_notifier_block_wrapper *nb_wrapper =
container_of(nb, struct tcp_notifier_block_wrapper, stub_nb);
struct notifier_block *action_nb = nb_wrapper->action_nb;
return nb_wrapper->action_nb->notifier_call(action_nb, action, data);
}
struct tcpc_managed_res {
void *res;
void *key;
int prv_id;
struct tcpc_managed_res *next;
};
static int __add_wrapper_to_managed_res_list(struct tcpc_device *tcp_dev,
void *res, void *key, int prv_id)
{
struct tcpc_managed_res *tail;
struct tcpc_managed_res *mres;
mres = devm_kzalloc(&tcp_dev->dev, sizeof(*mres), GFP_KERNEL);
if (!mres)
return -ENOMEM;
mres->res = res;
mres->key = key;
mres->prv_id = prv_id;
mutex_lock(&tcp_dev->mr_lock);
tail = tcp_dev->mr_head;
if (tail) {
while (tail->next)
tail = tail->next;
tail->next = mres;
} else
tcp_dev->mr_head = mres;
mutex_unlock(&tcp_dev->mr_lock);
return 0;
}
static int __register_tcp_dev_notifier(struct tcpc_device *tcp_dev,
struct notifier_block *nb, uint8_t idx)
{
struct tcp_notifier_block_wrapper *nb_wrapper;
int retval;
nb_wrapper = devm_kzalloc(
&tcp_dev->dev, sizeof(*nb_wrapper), GFP_KERNEL);
if (!nb_wrapper)
return -ENOMEM;
nb_wrapper->action_nb = nb;
nb_wrapper->stub_nb.notifier_call = tcp_notifier_func_stub;
retval = srcu_notifier_chain_register(
tcp_dev->evt_nh + idx, &nb_wrapper->stub_nb);
if (retval < 0) {
devm_kfree(&tcp_dev->dev, nb_wrapper);
return retval;
}
retval = __add_wrapper_to_managed_res_list(
tcp_dev, nb_wrapper, nb, idx);
if (retval < 0)
dev_warn(&tcp_dev->dev,
"Failed to add resource to manager(%d)\n", retval);
return 0;
}
static bool __is_mulit_bits_set(uint32_t flags)
{
if (flags) {
flags &= (flags - 1);
return flags ? true : false;
}
return false;
}
int register_tcp_dev_notifier(struct tcpc_device *tcp_dev,
struct notifier_block *nb, uint8_t flags)
{
int ret = 0, i = 0;
if (__is_mulit_bits_set(flags)) {
for (i = 0; i < TCP_NOTIFY_IDX_NR; i++) {
if (flags & (1 << i)) {
ret = __register_tcp_dev_notifier(
tcp_dev, nb, i);
if (ret < 0)
return ret;
}
}
} else { /* single bit */
for (i = 0; i < TCP_NOTIFY_IDX_NR; i++) {
if (flags & (1 << i)) {
ret = srcu_notifier_chain_register(
&tcp_dev->evt_nh[i], nb);
break;
}
}
}
#ifndef CONFIG_TCPC_NOTIFIER_LATE_SYNC
if (tcp_dev->desc.notifier_supply_num == 0) {
pr_info("%s already started\n", __func__);
return 0;
}
tcp_dev->desc.notifier_supply_num--;
pr_info("%s supply_num = %d\n", __func__,
tcp_dev->desc.notifier_supply_num);
if (tcp_dev->desc.notifier_supply_num == 0) {
cancel_delayed_work(&tcp_dev->init_work);
tcpc_device_irq_enable(tcp_dev);
}
#endif
return ret;
}
EXPORT_SYMBOL(register_tcp_dev_notifier);
static void *__remove_wrapper_from_managed_res_list(
struct tcpc_device *tcp_dev, void *key, int prv_id)
{
void *retval = NULL;
struct tcpc_managed_res *mres = tcp_dev->mr_head;
struct tcpc_managed_res *prev = NULL;
mutex_lock(&tcp_dev->mr_lock);
if (mres) {
while (mres) {
if (mres->key == key && mres->prv_id == prv_id) {
retval = mres->res;
if (prev)
prev->next = mres->next;
else
tcp_dev->mr_head = NULL;
devm_kfree(&tcp_dev->dev, mres);
break;
}
prev = mres;
mres = mres->next;
}
}
mutex_unlock(&tcp_dev->mr_lock);
return retval;
}
static int __unregister_tcp_dev_notifier(struct tcpc_device *tcp_dev,
struct notifier_block *nb, uint8_t idx)
{
struct tcp_notifier_block_wrapper *nb_wrapper;
int retval;
nb_wrapper = __remove_wrapper_from_managed_res_list(tcp_dev, nb, idx);
if (nb_wrapper) {
retval = srcu_notifier_chain_unregister(
tcp_dev->evt_nh + idx, &nb_wrapper->stub_nb);
devm_kfree(&tcp_dev->dev, nb_wrapper);
return retval;
}
return -ENOENT;
}
int unregister_tcp_dev_notifier(struct tcpc_device *tcp_dev,
struct notifier_block *nb, uint8_t flags)
{
int i = 0, ret = 0;
for (i = 0; i < TCP_NOTIFY_IDX_NR; i++) {
if (flags & (1 << i)) {
ret = __unregister_tcp_dev_notifier(tcp_dev, nb, i);
if (ret == -ENOENT)
ret = srcu_notifier_chain_unregister(
tcp_dev->evt_nh + i, nb);
if (ret < 0)
return ret;
}
}
return ret;
}
EXPORT_SYMBOL(unregister_tcp_dev_notifier);
void tcpc_device_unregister(struct device *dev, struct tcpc_device *tcpc)
{
if (!tcpc)
return;
tcpc_typec_deinit(tcpc);
#ifdef CONFIG_USB_PD_REV30
wakeup_source_unregister(tcpc->pd_port.pps_request_wake_lock);
#endif /* CONFIG_USB_PD_REV30 */
wakeup_source_unregister(tcpc->detach_wake_lock);
wakeup_source_unregister(tcpc->attach_wake_lock);
#ifdef CONFIG_DUAL_ROLE_USB_INTF
devm_dual_role_instance_unregister(&tcpc->dev, tcpc->dr_usb);
#endif /* CONFIG_DUAL_ROLE_USB_INTF */
device_unregister(&tcpc->dev);
}
EXPORT_SYMBOL(tcpc_device_unregister);
void *tcpc_get_dev_data(struct tcpc_device *tcpc)
{
return tcpc->drv_data;
}
EXPORT_SYMBOL(tcpc_get_dev_data);
void tcpci_lock_typec(struct tcpc_device *tcpc)
{
mutex_lock(&tcpc->typec_lock);
}
EXPORT_SYMBOL(tcpci_lock_typec);
void tcpci_unlock_typec(struct tcpc_device *tcpc)
{
mutex_unlock(&tcpc->typec_lock);
}
EXPORT_SYMBOL(tcpci_unlock_typec);
static void tcpc_init_attrs(struct device_type *dev_type)
{
int i;
dev_type->groups = tcpc_attr_groups;
for (i = 0; i < ARRAY_SIZE(tcpc_device_attributes); i++)
__tcpc_attrs[i] = &tcpc_device_attributes[i].attr;
}
static int __init tcpc_class_init(void)
{
pr_info("%s (%s)\n", __func__, TCPC_CORE_VERSION);
#ifdef CONFIG_USB_POWER_DELIVERY
dpm_check_supported_modes();
#endif /* CONFIG_USB_POWER_DELIVERY */
tcpc_class = class_create(THIS_MODULE, "tcpc");
if (IS_ERR(tcpc_class)) {
pr_info("Unable to create tcpc class; errno = %ld\n",
PTR_ERR(tcpc_class));
return PTR_ERR(tcpc_class);
}
tcpc_init_attrs(&tcpc_dev_type);
pd_dbg_info_init();
regmap_plat_init();
pr_info("TCPC class init OK\n");
return 0;
}
static void __exit tcpc_class_exit(void)
{
regmap_plat_exit();
pd_dbg_info_exit();
class_destroy(tcpc_class);
pr_info("TCPC class un-init OK\n");
}
subsys_initcall(tcpc_class_init);
module_exit(tcpc_class_exit);
MODULE_DESCRIPTION("Richtek TypeC Port Control Core");
MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>");
MODULE_VERSION(TCPC_CORE_VERSION);
MODULE_LICENSE("GPL");
/* Release Version
* 2.0.14_G
* (1) Move out typec_port registration and operation to rt_pd_manager.c
* (2) Rename CONFIG_TYPEC_WAIT_BC12 to CONFIG_USB_PD_WAIT_BC12
* (3) Not to set power/data/vconn role repeatedly
* (4) Revise vconn highV protection
* (5) Revise tcpc timer
* (6) Decrease VBUS present threshold (VBUS_CAL) by 60mV (2LSBs) for RT171x
* (7) Replace \r\n with \n for resolving logs without newlines
* (8) Remove the member time_stamp from struct pd_msg
* (9) Remove NoResponseTimer as Sink for new PD spec
* (10) Revise responses of Reject and Not_Supported
* (11) Revise the usages of pd_traffic_control and typec_power_ctrl
* (12) Revise the usages of wait_event_*()
* (13) Add PD capability for TYPEC_ATTACHED_DBGACC_SNK
* (14) Utilize rt-regmap to reduce I2C accesses
*
* 2.0.13_G
* (1) Add TCPC flags for VCONN_SAFE5V_ONLY
* (2) Add boolean property attemp_discover_svid in dts/dtsi
* (3) Add a TCPM API for postponing Type-C role change until unattached
* (4) Update VDOs according new PD spec
* (5) Add an option for enabling/disabling the support of DebugAccessory.SRC
* (6) Add the workaround for delayed ps_change related to PS_RDY
* during PR_SWAP
* (7) Always Back to PE ready state in pd_dpm_dfp_inform_id() and
* pd_dpm_dfp_inform_svids()
* (8) Re-fetch triggered_timer and enable_mask after lock acquisition
* (9) Leave low power mode only when CC is detached
* (10) Revise code related to pd_check_rev30()
* (11) Bypass BC1.2 for PR_SWAP from Source to Sink (MTK only)
* (12) Support charging icon for AudioAccessory (MTK only)
* (13) Replace tcpc_dev with tcpc
* (14) TCPCI Alert V10 and V20 co-exist
* (15) Resolve DP Source/Sink Both Connected when acting as DFP_U
* (16) Change CONFIG_TYPEC_SNK_CURR_DFT from 150 to 100 (mA)
* (17) Define CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY by default
* (18) Add an option for TCPC log with port name
* (19) USB-C states go from ErrorRecovery to Unattached.SRC with Try.SRC role
* (20) Revise dts/dtsi value for DisplayPort Alternative Mode
* (21) Mask vSafe0V IRQ before entering low power mode
* (22) Disable auto idle mode before entering low power mode
* (23) Reset Protocol FSM and clear RX alerts twice before clock gating
*
* 2.0.12_G
* (1) Fix voltage/current steps of RDO for APDO
* (2) Non-blocking TCPC notification by default
* (3) Fix synchronization/locking problems
* (4) Fix NoRp.SRC support
*
* 2.0.11_G
* (1) Fix PD compliance failures of Ellisys and MQP
* (2) Wait the result of BC1.2 before starting PD policy engine
* (3) Fix compile warnings
* (4) Fix NoRp.SRC support
*
* 2.0.10_G
* (1) fix battery noitifier plug out cause recursive locking detected in
* nh->srcu.
*
* 2.0.9_G
* (1) fix 10k A-to-C legacy cable workaround side effect when
* cable plug in at worakround flow.
*
* 2.0.8_G
* (1) fix timeout thread flow for wakeup pd event thread
* after disable timer first.
*
* 2.0.7_G
* (1) add extract pd source capability pdo defined in
* PD30 v1.1 ECN for pe40 get apdo profile.
*
* 2.0.6_G
* (1) register battery notifier for battery plug out
* avoid TA hardreset 3 times will show charing icon.
*
* 2.0.5_G
* (1) add CONFIG_TYPEC_CAP_NORP_SRC to support
* A-to-C No-Rp cable.
* (2) add handler pd eint with eint mask
*
* 2.0.4_G
* (1) add CONFIG_TCPC_NOTIFIER_LATE_SYNC to
* move irq_enable to late_initcall_sync stage
* to prevent from notifier_supply_num setting wrong.
*
* 2.0.3_G
* (1) use local_irq_XXX to instead raw_local_irq_XXX
* to fix lock prov WARNING
* (2) Remove unnecessary charger detect flow. it does
* not need to switch BC1-2 path on otg_en
*
* 2.0.2_G
* (1) Fix Coverity and check patch issue
* (2) Fix 32-bit project build error
*
* 2.0.1_G
* First released PD3.0 Driver
*/

View file

@ -0,0 +1,291 @@
/*
* Copyright (C) 2020 Richtek Inc.
*
* TCPC Interface for dual role
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include "inc/tcpci.h"
#include "inc/tcpci_typec.h"
#ifdef CONFIG_DUAL_ROLE_USB_INTF
static enum dual_role_property tcpc_dual_role_props[] = {
DUAL_ROLE_PROP_SUPPORTED_MODES,
DUAL_ROLE_PROP_MODE,
DUAL_ROLE_PROP_PR,
DUAL_ROLE_PROP_DR,
DUAL_ROLE_PROP_VCONN_SUPPLY,
};
static int tcpc_dual_role_get_prop(struct dual_role_phy_instance *dual_role,
enum dual_role_property prop, unsigned int *val)
{
struct tcpc_device *tcpc = dev_get_drvdata(dual_role->dev.parent);
int ret = 0;
switch (prop) {
case DUAL_ROLE_PROP_SUPPORTED_MODES:
*val = tcpc->dual_role_supported_modes;
break;
case DUAL_ROLE_PROP_MODE:
*val = tcpc->dual_role_mode;
break;
case DUAL_ROLE_PROP_PR:
*val = tcpc->dual_role_pr;
break;
case DUAL_ROLE_PROP_DR:
*val = tcpc->dual_role_dr;
break;
case DUAL_ROLE_PROP_VCONN_SUPPLY:
*val = tcpc->dual_role_vconn;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int tcpc_dual_role_prop_is_writeable(
struct dual_role_phy_instance *dual_role, enum dual_role_property prop)
{
int retval = -EINVAL;
struct tcpc_device *tcpc = dev_get_drvdata(dual_role->dev.parent);
switch (prop) {
#ifdef CONFIG_USB_POWER_DELIVERY
case DUAL_ROLE_PROP_PR:
case DUAL_ROLE_PROP_DR:
case DUAL_ROLE_PROP_VCONN_SUPPLY:
#else
case DUAL_ROLE_PROP_MODE:
#endif /* CONFIG_USB_POWER_DELIVERY */
if (tcpc->dual_role_supported_modes ==
DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP)
retval = 1;
break;
default:
break;
}
return retval;
}
#ifdef CONFIG_USB_POWER_DELIVERY
static int tcpc_dual_role_set_prop_pr(
struct tcpc_device *tcpc, unsigned int val)
{
int ret;
uint8_t role;
switch (val) {
case DUAL_ROLE_PROP_PR_SRC:
role = PD_ROLE_SOURCE;
break;
case DUAL_ROLE_PROP_PR_SNK:
role = PD_ROLE_SINK;
break;
default:
return 0;
}
if (val == tcpc->dual_role_pr) {
pr_info("%s wrong role (%d->%d)\n",
__func__, tcpc->dual_role_pr, val);
return 0;
}
ret = tcpm_dpm_pd_power_swap(tcpc, role, NULL);
pr_info("%s power role swap (%d->%d): %d\n",
__func__, tcpc->dual_role_pr, val, ret);
if (ret == TCPM_ERROR_NO_PD_CONNECTED) {
ret = tcpm_typec_role_swap(tcpc);
pr_info("%s typec role swap (%d->%d): %d\n",
__func__, tcpc->dual_role_pr, val, ret);
}
return ret;
}
static int tcpc_dual_role_set_prop_dr(
struct tcpc_device *tcpc, unsigned int val)
{
int ret;
uint8_t role;
switch (val) {
case DUAL_ROLE_PROP_DR_HOST:
role = PD_ROLE_DFP;
break;
case DUAL_ROLE_PROP_DR_DEVICE:
role = PD_ROLE_UFP;
break;
default:
return 0;
}
if (val == tcpc->dual_role_dr) {
pr_info("%s wrong role (%d->%d)\n",
__func__, tcpc->dual_role_dr, val);
return 0;
}
ret = tcpm_dpm_pd_data_swap(tcpc, role, NULL);
pr_info("%s data role swap (%d->%d): %d\n",
__func__, tcpc->dual_role_dr, val, ret);
return ret;
}
static int tcpc_dual_role_set_prop_vconn(
struct tcpc_device *tcpc, unsigned int val)
{
int ret;
uint8_t role;
switch (val) {
case DUAL_ROLE_PROP_VCONN_SUPPLY_NO:
role = PD_ROLE_VCONN_OFF;
break;
case DUAL_ROLE_PROP_VCONN_SUPPLY_YES:
role = PD_ROLE_VCONN_ON;
break;
default:
return 0;
}
if (val == tcpc->dual_role_vconn) {
pr_info("%s wrong role (%d->%d)\n",
__func__, tcpc->dual_role_vconn, val);
return 0;
}
ret = tcpm_dpm_pd_vconn_swap(tcpc, role, NULL);
pr_info("%s vconn swap (%d->%d): %d\n",
__func__, tcpc->dual_role_vconn, val, ret);
return ret;
}
#else /* TypeC Only */
static int tcpc_dual_role_set_prop_mode(
struct tcpc_device *tcpc, unsigned int val)
{
int ret;
if (val == tcpc->dual_role_mode) {
pr_info("%s wrong role (%d->%d)\n",
__func__, tcpc->dual_role_mode, val);
return 0;
}
ret = tcpm_typec_role_swap(tcpc);
pr_info("%s typec role swap (%d->%d): %d\n",
__func__, tcpc->dual_role_mode, val, ret);
return ret;
}
#endif /* CONFIG_USB_POWER_DELIVERY */
static int tcpc_dual_role_set_prop(struct dual_role_phy_instance *dual_role,
enum dual_role_property prop, const unsigned int *val)
{
struct tcpc_device *tcpc = dev_get_drvdata(dual_role->dev.parent);
switch (prop) {
#ifdef CONFIG_USB_POWER_DELIVERY
case DUAL_ROLE_PROP_PR:
tcpc_dual_role_set_prop_pr(tcpc, *val);
break;
case DUAL_ROLE_PROP_DR:
tcpc_dual_role_set_prop_dr(tcpc, *val);
break;
case DUAL_ROLE_PROP_VCONN_SUPPLY:
tcpc_dual_role_set_prop_vconn(tcpc, *val);
break;
#else /* TypeC Only */
case DUAL_ROLE_PROP_MODE:
tcpc_dual_role_set_prop_mode(tcpc, *val);
break;
#endif /* CONFIG_USB_POWER_DELIVERY */
default:
break;
}
return 0;
}
static void tcpc_get_dual_desc(struct tcpc_device *tcpc)
{
struct device_node *np = of_find_node_by_name(NULL, tcpc->desc.name);
u32 val;
if (!np)
return;
if (of_property_read_u32(np, "tcpc-dual,supported_modes", &val) >= 0) {
if (val > DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL)
tcpc->dual_role_supported_modes =
DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP;
else
tcpc->dual_role_supported_modes = val;
}
}
int tcpc_dual_role_phy_init(
struct tcpc_device *tcpc)
{
struct dual_role_phy_desc *dual_desc;
int len;
char *str_name;
tcpc->dr_usb = devm_kzalloc(&tcpc->dev,
sizeof(*tcpc->dr_usb), GFP_KERNEL);
dual_desc = devm_kzalloc(&tcpc->dev, sizeof(*dual_desc), GFP_KERNEL);
if (!dual_desc)
return -ENOMEM;
tcpc_get_dual_desc(tcpc);
len = strlen(tcpc->desc.name);
str_name = devm_kzalloc(&tcpc->dev, len+11, GFP_KERNEL);
snprintf(str_name, PAGE_SIZE, "dual-role-%s", tcpc->desc.name);
dual_desc->name = str_name;
dual_desc->properties = tcpc_dual_role_props;
dual_desc->num_properties = ARRAY_SIZE(tcpc_dual_role_props);
dual_desc->get_property = tcpc_dual_role_get_prop;
dual_desc->set_property = tcpc_dual_role_set_prop;
dual_desc->property_is_writeable = tcpc_dual_role_prop_is_writeable;
tcpc->dr_usb = devm_dual_role_instance_register(&tcpc->dev, dual_desc);
if (IS_ERR(tcpc->dr_usb)) {
dev_err(&tcpc->dev, "tcpc fail to register dual role usb\n");
return -EINVAL;
}
/* init dual role phy instance property */
tcpc->dual_role_pr = DUAL_ROLE_PROP_PR_NONE;
tcpc->dual_role_dr = DUAL_ROLE_PROP_DR_NONE;
tcpc->dual_role_mode = DUAL_ROLE_PROP_MODE_NONE;
tcpc->dual_role_vconn = DUAL_ROLE_PROP_VCONN_SUPPLY_NO;
return 0;
}
#endif /* CONFIG_DUAL_ROLE_USB_INTF */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff