1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 2015-2017 Google, Inc
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * USB Power Delivery protocol stack.
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <asm/io.h>
9*4882a593Smuzhiyun #include <common.h>
10*4882a593Smuzhiyun #include <dm.h>
11*4882a593Smuzhiyun #include <asm/gpio.h>
12*4882a593Smuzhiyun #include <irq-generic.h>
13*4882a593Smuzhiyun #include <rk_timer_irq.h>
14*4882a593Smuzhiyun #include <power/power_delivery/tcpm.h>
15*4882a593Smuzhiyun #include <power/power_delivery/pd_vdo.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #define FOREACH_STATE(S) \
18*4882a593Smuzhiyun S(INVALID_STATE), \
19*4882a593Smuzhiyun S(TOGGLING), \
20*4882a593Smuzhiyun S(SRC_UNATTACHED), \
21*4882a593Smuzhiyun S(SRC_ATTACH_WAIT), \
22*4882a593Smuzhiyun S(SRC_ATTACHED), \
23*4882a593Smuzhiyun S(SRC_STARTUP), \
24*4882a593Smuzhiyun S(SRC_SEND_CAPABILITIES), \
25*4882a593Smuzhiyun S(SRC_SEND_CAPABILITIES_TIMEOUT), \
26*4882a593Smuzhiyun S(SRC_NEGOTIATE_CAPABILITIES), \
27*4882a593Smuzhiyun S(SRC_TRANSITION_SUPPLY), \
28*4882a593Smuzhiyun S(SRC_READY), \
29*4882a593Smuzhiyun S(SRC_WAIT_NEW_CAPABILITIES), \
30*4882a593Smuzhiyun \
31*4882a593Smuzhiyun S(SNK_UNATTACHED), \
32*4882a593Smuzhiyun S(SNK_ATTACH_WAIT), \
33*4882a593Smuzhiyun S(SNK_DEBOUNCED), \
34*4882a593Smuzhiyun S(SNK_ATTACHED), \
35*4882a593Smuzhiyun S(SNK_STARTUP), \
36*4882a593Smuzhiyun S(SNK_DISCOVERY), \
37*4882a593Smuzhiyun S(SNK_DISCOVERY_DEBOUNCE), \
38*4882a593Smuzhiyun S(SNK_DISCOVERY_DEBOUNCE_DONE), \
39*4882a593Smuzhiyun S(SNK_WAIT_CAPABILITIES), \
40*4882a593Smuzhiyun S(SNK_NEGOTIATE_CAPABILITIES), \
41*4882a593Smuzhiyun S(SNK_NEGOTIATE_PPS_CAPABILITIES), \
42*4882a593Smuzhiyun S(SNK_TRANSITION_SINK), \
43*4882a593Smuzhiyun S(SNK_TRANSITION_SINK_VBUS), \
44*4882a593Smuzhiyun S(SNK_READY), \
45*4882a593Smuzhiyun \
46*4882a593Smuzhiyun S(ACC_UNATTACHED), \
47*4882a593Smuzhiyun S(DEBUG_ACC_ATTACHED), \
48*4882a593Smuzhiyun S(AUDIO_ACC_ATTACHED), \
49*4882a593Smuzhiyun S(AUDIO_ACC_DEBOUNCE), \
50*4882a593Smuzhiyun \
51*4882a593Smuzhiyun S(HARD_RESET_SEND), \
52*4882a593Smuzhiyun S(HARD_RESET_START), \
53*4882a593Smuzhiyun S(SRC_HARD_RESET_VBUS_OFF), \
54*4882a593Smuzhiyun S(SRC_HARD_RESET_VBUS_ON), \
55*4882a593Smuzhiyun S(SNK_HARD_RESET_SINK_OFF), \
56*4882a593Smuzhiyun S(SNK_HARD_RESET_WAIT_VBUS), \
57*4882a593Smuzhiyun S(SNK_HARD_RESET_SINK_ON), \
58*4882a593Smuzhiyun \
59*4882a593Smuzhiyun S(SOFT_RESET), \
60*4882a593Smuzhiyun S(SRC_SOFT_RESET_WAIT_SNK_TX), \
61*4882a593Smuzhiyun S(SNK_SOFT_RESET), \
62*4882a593Smuzhiyun S(SOFT_RESET_SEND), \
63*4882a593Smuzhiyun \
64*4882a593Smuzhiyun S(DR_SWAP_ACCEPT), \
65*4882a593Smuzhiyun S(DR_SWAP_SEND), \
66*4882a593Smuzhiyun S(DR_SWAP_SEND_TIMEOUT), \
67*4882a593Smuzhiyun S(DR_SWAP_CANCEL), \
68*4882a593Smuzhiyun S(DR_SWAP_CHANGE_DR), \
69*4882a593Smuzhiyun \
70*4882a593Smuzhiyun S(PR_SWAP_ACCEPT), \
71*4882a593Smuzhiyun S(PR_SWAP_SEND), \
72*4882a593Smuzhiyun S(PR_SWAP_SEND_TIMEOUT), \
73*4882a593Smuzhiyun S(PR_SWAP_CANCEL), \
74*4882a593Smuzhiyun S(PR_SWAP_START), \
75*4882a593Smuzhiyun S(PR_SWAP_SRC_SNK_TRANSITION_OFF), \
76*4882a593Smuzhiyun S(PR_SWAP_SRC_SNK_SOURCE_OFF), \
77*4882a593Smuzhiyun S(PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED), \
78*4882a593Smuzhiyun S(PR_SWAP_SRC_SNK_SINK_ON), \
79*4882a593Smuzhiyun S(PR_SWAP_SNK_SRC_SINK_OFF), \
80*4882a593Smuzhiyun S(PR_SWAP_SNK_SRC_SOURCE_ON), \
81*4882a593Smuzhiyun S(PR_SWAP_SNK_SRC_SOURCE_ON_VBUS_RAMPED_UP), \
82*4882a593Smuzhiyun \
83*4882a593Smuzhiyun S(VCONN_SWAP_ACCEPT), \
84*4882a593Smuzhiyun S(VCONN_SWAP_SEND), \
85*4882a593Smuzhiyun S(VCONN_SWAP_SEND_TIMEOUT), \
86*4882a593Smuzhiyun S(VCONN_SWAP_CANCEL), \
87*4882a593Smuzhiyun S(VCONN_SWAP_START), \
88*4882a593Smuzhiyun S(VCONN_SWAP_WAIT_FOR_VCONN), \
89*4882a593Smuzhiyun S(VCONN_SWAP_TURN_ON_VCONN), \
90*4882a593Smuzhiyun S(VCONN_SWAP_TURN_OFF_VCONN), \
91*4882a593Smuzhiyun \
92*4882a593Smuzhiyun S(FR_SWAP_SEND), \
93*4882a593Smuzhiyun S(FR_SWAP_SEND_TIMEOUT), \
94*4882a593Smuzhiyun S(FR_SWAP_SNK_SRC_TRANSITION_TO_OFF), \
95*4882a593Smuzhiyun S(FR_SWAP_SNK_SRC_NEW_SINK_READY), \
96*4882a593Smuzhiyun S(FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED), \
97*4882a593Smuzhiyun S(FR_SWAP_CANCEL), \
98*4882a593Smuzhiyun \
99*4882a593Smuzhiyun S(SNK_TRY), \
100*4882a593Smuzhiyun S(SNK_TRY_WAIT), \
101*4882a593Smuzhiyun S(SNK_TRY_WAIT_DEBOUNCE), \
102*4882a593Smuzhiyun S(SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS), \
103*4882a593Smuzhiyun S(SRC_TRYWAIT), \
104*4882a593Smuzhiyun S(SRC_TRYWAIT_DEBOUNCE), \
105*4882a593Smuzhiyun S(SRC_TRYWAIT_UNATTACHED), \
106*4882a593Smuzhiyun \
107*4882a593Smuzhiyun S(SRC_TRY), \
108*4882a593Smuzhiyun S(SRC_TRY_WAIT), \
109*4882a593Smuzhiyun S(SRC_TRY_DEBOUNCE), \
110*4882a593Smuzhiyun S(SNK_TRYWAIT), \
111*4882a593Smuzhiyun S(SNK_TRYWAIT_DEBOUNCE), \
112*4882a593Smuzhiyun S(SNK_TRYWAIT_VBUS), \
113*4882a593Smuzhiyun S(BIST_RX), \
114*4882a593Smuzhiyun \
115*4882a593Smuzhiyun S(GET_STATUS_SEND), \
116*4882a593Smuzhiyun S(GET_STATUS_SEND_TIMEOUT), \
117*4882a593Smuzhiyun S(GET_PPS_STATUS_SEND), \
118*4882a593Smuzhiyun S(GET_PPS_STATUS_SEND_TIMEOUT), \
119*4882a593Smuzhiyun \
120*4882a593Smuzhiyun S(GET_SINK_CAP), \
121*4882a593Smuzhiyun S(GET_SINK_CAP_TIMEOUT), \
122*4882a593Smuzhiyun \
123*4882a593Smuzhiyun S(ERROR_RECOVERY), \
124*4882a593Smuzhiyun S(PORT_RESET), \
125*4882a593Smuzhiyun S(PORT_RESET_WAIT_OFF), \
126*4882a593Smuzhiyun \
127*4882a593Smuzhiyun S(AMS_START), \
128*4882a593Smuzhiyun S(CHUNK_NOT_SUPP)
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun #define FOREACH_AMS(S) \
131*4882a593Smuzhiyun S(NONE_AMS), \
132*4882a593Smuzhiyun S(POWER_NEGOTIATION), \
133*4882a593Smuzhiyun S(GOTOMIN), \
134*4882a593Smuzhiyun S(SOFT_RESET_AMS), \
135*4882a593Smuzhiyun S(HARD_RESET), \
136*4882a593Smuzhiyun S(CABLE_RESET), \
137*4882a593Smuzhiyun S(GET_SOURCE_CAPABILITIES), \
138*4882a593Smuzhiyun S(GET_SINK_CAPABILITIES), \
139*4882a593Smuzhiyun S(POWER_ROLE_SWAP), \
140*4882a593Smuzhiyun S(FAST_ROLE_SWAP), \
141*4882a593Smuzhiyun S(DATA_ROLE_SWAP), \
142*4882a593Smuzhiyun S(VCONN_SWAP), \
143*4882a593Smuzhiyun S(SOURCE_ALERT), \
144*4882a593Smuzhiyun S(GETTING_SOURCE_EXTENDED_CAPABILITIES),\
145*4882a593Smuzhiyun S(GETTING_SOURCE_SINK_STATUS), \
146*4882a593Smuzhiyun S(GETTING_BATTERY_CAPABILITIES), \
147*4882a593Smuzhiyun S(GETTING_BATTERY_STATUS), \
148*4882a593Smuzhiyun S(GETTING_MANUFACTURER_INFORMATION), \
149*4882a593Smuzhiyun S(SECURITY), \
150*4882a593Smuzhiyun S(FIRMWARE_UPDATE), \
151*4882a593Smuzhiyun S(DISCOVER_IDENTITY), \
152*4882a593Smuzhiyun S(SOURCE_STARTUP_CABLE_PLUG_DISCOVER_IDENTITY), \
153*4882a593Smuzhiyun S(DISCOVER_SVIDS), \
154*4882a593Smuzhiyun S(DISCOVER_MODES), \
155*4882a593Smuzhiyun S(DFP_TO_UFP_ENTER_MODE), \
156*4882a593Smuzhiyun S(DFP_TO_UFP_EXIT_MODE), \
157*4882a593Smuzhiyun S(DFP_TO_CABLE_PLUG_ENTER_MODE), \
158*4882a593Smuzhiyun S(DFP_TO_CABLE_PLUG_EXIT_MODE), \
159*4882a593Smuzhiyun S(ATTENTION), \
160*4882a593Smuzhiyun S(BIST), \
161*4882a593Smuzhiyun S(UNSTRUCTURED_VDMS), \
162*4882a593Smuzhiyun S(STRUCTURED_VDMS), \
163*4882a593Smuzhiyun S(COUNTRY_INFO), \
164*4882a593Smuzhiyun S(COUNTRY_CODES)
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun #define GENERATE_ENUM(e) e
167*4882a593Smuzhiyun #define GENERATE_STRING(s) #s
168*4882a593Smuzhiyun #define TCPM_POLL_EVENT_TIME_OUT 2000
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun enum tcpm_state {
171*4882a593Smuzhiyun FOREACH_STATE(GENERATE_ENUM)
172*4882a593Smuzhiyun };
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun static const char * const tcpm_states[] = {
175*4882a593Smuzhiyun FOREACH_STATE(GENERATE_STRING)
176*4882a593Smuzhiyun };
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun enum tcpm_ams {
179*4882a593Smuzhiyun FOREACH_AMS(GENERATE_ENUM)
180*4882a593Smuzhiyun };
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun static const char * const tcpm_ams_str[] = {
183*4882a593Smuzhiyun FOREACH_AMS(GENERATE_STRING)
184*4882a593Smuzhiyun };
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun enum vdm_states {
187*4882a593Smuzhiyun VDM_STATE_ERR_BUSY = -3,
188*4882a593Smuzhiyun VDM_STATE_ERR_SEND = -2,
189*4882a593Smuzhiyun VDM_STATE_ERR_TMOUT = -1,
190*4882a593Smuzhiyun VDM_STATE_DONE = 0,
191*4882a593Smuzhiyun /* Anything >0 represents an active state */
192*4882a593Smuzhiyun VDM_STATE_READY = 1,
193*4882a593Smuzhiyun VDM_STATE_BUSY = 2,
194*4882a593Smuzhiyun VDM_STATE_WAIT_RSP_BUSY = 3,
195*4882a593Smuzhiyun VDM_STATE_SEND_MESSAGE = 4,
196*4882a593Smuzhiyun };
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun enum pd_msg_request {
199*4882a593Smuzhiyun PD_MSG_NONE = 0,
200*4882a593Smuzhiyun PD_MSG_CTRL_REJECT,
201*4882a593Smuzhiyun PD_MSG_CTRL_WAIT,
202*4882a593Smuzhiyun PD_MSG_CTRL_NOT_SUPP,
203*4882a593Smuzhiyun PD_MSG_DATA_SINK_CAP,
204*4882a593Smuzhiyun PD_MSG_DATA_SOURCE_CAP,
205*4882a593Smuzhiyun };
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun enum adev_actions {
208*4882a593Smuzhiyun ADEV_NONE = 0,
209*4882a593Smuzhiyun ADEV_NOTIFY_USB_AND_QUEUE_VDM,
210*4882a593Smuzhiyun ADEV_QUEUE_VDM,
211*4882a593Smuzhiyun ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL,
212*4882a593Smuzhiyun ADEV_ATTENTION,
213*4882a593Smuzhiyun };
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /*
216*4882a593Smuzhiyun * Initial current capability of the new source when vSafe5V is applied during PD3.0 Fast Role Swap.
217*4882a593Smuzhiyun * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0,
218*4882a593Smuzhiyun * Version 1.2"
219*4882a593Smuzhiyun */
220*4882a593Smuzhiyun enum frs_typec_current {
221*4882a593Smuzhiyun FRS_NOT_SUPPORTED,
222*4882a593Smuzhiyun FRS_DEFAULT_POWER,
223*4882a593Smuzhiyun FRS_5V_1P5A,
224*4882a593Smuzhiyun FRS_5V_3A,
225*4882a593Smuzhiyun };
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun /* Events from low level driver */
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun #define TCPM_CC_EVENT BIT(0)
230*4882a593Smuzhiyun #define TCPM_VBUS_EVENT BIT(1)
231*4882a593Smuzhiyun #define TCPM_RESET_EVENT BIT(2)
232*4882a593Smuzhiyun #define TCPM_FRS_EVENT BIT(3)
233*4882a593Smuzhiyun #define TCPM_SOURCING_VBUS BIT(4)
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun #define LOG_BUFFER_ENTRIES 1024
236*4882a593Smuzhiyun #define LOG_BUFFER_ENTRY_SIZE 128
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun /* Alternate mode support */
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun #define SVID_DISCOVERY_MAX 16
241*4882a593Smuzhiyun #define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun #define GET_SINK_CAP_RETRY_MS 100
244*4882a593Smuzhiyun #define SEND_DISCOVER_RETRY_MS 100
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun /*
247*4882a593Smuzhiyun * @min_volt: Actual min voltage at the local port
248*4882a593Smuzhiyun * @req_min_volt: Requested min voltage to the port partner
249*4882a593Smuzhiyun * @max_volt: Actual max voltage at the local port
250*4882a593Smuzhiyun * @req_max_volt: Requested max voltage to the port partner
251*4882a593Smuzhiyun * @max_curr: Actual max current at the local port
252*4882a593Smuzhiyun * @req_max_curr: Requested max current of the port partner
253*4882a593Smuzhiyun * @req_out_volt: Requested output voltage to the port partner
254*4882a593Smuzhiyun * @req_op_curr: Requested operating current to the port partner
255*4882a593Smuzhiyun * @supported: Parter has atleast one APDO hence supports PPS
256*4882a593Smuzhiyun * @active: PPS mode is active
257*4882a593Smuzhiyun */
258*4882a593Smuzhiyun struct pd_pps_data {
259*4882a593Smuzhiyun u32 min_volt;
260*4882a593Smuzhiyun u32 req_min_volt;
261*4882a593Smuzhiyun u32 max_volt;
262*4882a593Smuzhiyun u32 req_max_volt;
263*4882a593Smuzhiyun u32 max_curr;
264*4882a593Smuzhiyun u32 req_max_curr;
265*4882a593Smuzhiyun u32 req_out_volt;
266*4882a593Smuzhiyun u32 req_op_curr;
267*4882a593Smuzhiyun bool supported;
268*4882a593Smuzhiyun bool active;
269*4882a593Smuzhiyun };
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun enum power_supply_usb_type {
272*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_UNKNOWN = 0,
273*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_SDP, /* Standard Downstream Port */
274*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_DCP, /* Dedicated Charging Port */
275*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_CDP, /* Charging Downstream Port */
276*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_ACA, /* Accessory Charger Adapters */
277*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_C, /* Type C Port */
278*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_PD, /* Power Delivery Port */
279*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_PD_DRP, /* PD Dual Role Port */
280*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_PD_PPS, /* PD Programmable Power Supply */
281*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */
282*4882a593Smuzhiyun };
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun struct tcpm_port {
285*4882a593Smuzhiyun struct udevice *dev;
286*4882a593Smuzhiyun struct typec_capability typec_caps;
287*4882a593Smuzhiyun struct tcpc_dev *tcpc;
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun enum typec_role vconn_role;
290*4882a593Smuzhiyun enum typec_role pwr_role;
291*4882a593Smuzhiyun enum typec_data_role data_role;
292*4882a593Smuzhiyun enum typec_pwr_opmode pwr_opmode;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun struct usb_pd_identity partner_ident;
295*4882a593Smuzhiyun struct typec_partner_desc partner_desc;
296*4882a593Smuzhiyun struct typec_partner *partner;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun enum typec_cc_status cc_req;
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun enum typec_cc_status cc1;
301*4882a593Smuzhiyun enum typec_cc_status cc2;
302*4882a593Smuzhiyun enum typec_cc_polarity polarity;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun bool attached;
305*4882a593Smuzhiyun bool connected;
306*4882a593Smuzhiyun int poll_event_cnt;
307*4882a593Smuzhiyun enum typec_port_type port_type;
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun /*
310*4882a593Smuzhiyun * Set to true when vbus is greater than VSAFE5V min.
311*4882a593Smuzhiyun * Set to false when vbus falls below vSinkDisconnect max threshold.
312*4882a593Smuzhiyun */
313*4882a593Smuzhiyun bool vbus_present;
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun /*
316*4882a593Smuzhiyun * Set to true when vbus is less than VSAFE0V max.
317*4882a593Smuzhiyun * Set to false when vbus is greater than VSAFE0V max.
318*4882a593Smuzhiyun */
319*4882a593Smuzhiyun bool vbus_vsafe0v;
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun bool vbus_never_low;
322*4882a593Smuzhiyun bool vbus_source;
323*4882a593Smuzhiyun bool vbus_charge;
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun /* Set to true when Discover_Identity Command is expected to be sent in Ready states. */
326*4882a593Smuzhiyun bool send_discover;
327*4882a593Smuzhiyun bool op_vsafe5v;
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun int try_role;
330*4882a593Smuzhiyun int try_snk_count;
331*4882a593Smuzhiyun int try_src_count;
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun enum pd_msg_request queued_message;
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun enum tcpm_state enter_state;
336*4882a593Smuzhiyun enum tcpm_state prev_state;
337*4882a593Smuzhiyun enum tcpm_state state;
338*4882a593Smuzhiyun enum tcpm_state delayed_state;
339*4882a593Smuzhiyun unsigned long delay_ms;
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun spinlock_t pd_event_lock;
342*4882a593Smuzhiyun u32 pd_events;
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun bool state_machine_running;
345*4882a593Smuzhiyun /* Set to true when VDM State Machine has following actions. */
346*4882a593Smuzhiyun bool vdm_sm_running;
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun bool tx_complete;
349*4882a593Smuzhiyun enum tcpm_transmit_status tx_status;
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun struct mutex swap_lock; /* swap command lock */
352*4882a593Smuzhiyun bool swap_pending;
353*4882a593Smuzhiyun bool non_pd_role_swap;
354*4882a593Smuzhiyun int swap_status;
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun unsigned int negotiated_rev;
357*4882a593Smuzhiyun unsigned int message_id;
358*4882a593Smuzhiyun unsigned int caps_count;
359*4882a593Smuzhiyun unsigned int hard_reset_count;
360*4882a593Smuzhiyun bool pd_capable;
361*4882a593Smuzhiyun bool explicit_contract;
362*4882a593Smuzhiyun unsigned int rx_msgid;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun /* Partner capabilities/requests */
365*4882a593Smuzhiyun u32 sink_request;
366*4882a593Smuzhiyun u32 source_caps[PDO_MAX_OBJECTS];
367*4882a593Smuzhiyun unsigned int nr_source_caps;
368*4882a593Smuzhiyun u32 sink_caps[PDO_MAX_OBJECTS];
369*4882a593Smuzhiyun unsigned int nr_sink_caps;
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun /*
372*4882a593Smuzhiyun * whether to wait for the Type-C device to send the DR_SWAP Message flag
373*4882a593Smuzhiyun * For Type-C device with Dual-Role Power and Dual-Role Data, the port side
374*4882a593Smuzhiyun * is used as sink + ufp, then the tcpm framework needs to wait for Type-C
375*4882a593Smuzhiyun * device to initiate DR_swap Message.
376*4882a593Smuzhiyun */
377*4882a593Smuzhiyun bool wait_dr_swap_Message;
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun /* Local capabilities */
380*4882a593Smuzhiyun u32 src_pdo[PDO_MAX_OBJECTS];
381*4882a593Smuzhiyun unsigned int nr_src_pdo;
382*4882a593Smuzhiyun u32 snk_pdo[PDO_MAX_OBJECTS];
383*4882a593Smuzhiyun unsigned int nr_snk_pdo;
384*4882a593Smuzhiyun u32 snk_vdo_v1[VDO_MAX_OBJECTS];
385*4882a593Smuzhiyun unsigned int nr_snk_vdo_v1;
386*4882a593Smuzhiyun u32 snk_vdo[VDO_MAX_OBJECTS];
387*4882a593Smuzhiyun unsigned int nr_snk_vdo;
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun unsigned int operating_snk_mw;
390*4882a593Smuzhiyun bool update_sink_caps;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun /* Requested current / voltage to the port partner */
393*4882a593Smuzhiyun u32 req_current_limit;
394*4882a593Smuzhiyun u32 req_supply_voltage;
395*4882a593Smuzhiyun /* Actual current / voltage limit of the local port */
396*4882a593Smuzhiyun u32 current_limit;
397*4882a593Smuzhiyun u32 supply_voltage;
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun enum power_supply_usb_type usb_type;
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun u32 bist_request;
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun /* PD state for Vendor Defined Messages */
404*4882a593Smuzhiyun enum vdm_states vdm_state;
405*4882a593Smuzhiyun u32 vdm_retries;
406*4882a593Smuzhiyun /* next Vendor Defined Message to send */
407*4882a593Smuzhiyun u32 vdo_data[VDO_MAX_SIZE];
408*4882a593Smuzhiyun u8 vdo_count;
409*4882a593Smuzhiyun /* VDO to retry if UFP responder replied busy */
410*4882a593Smuzhiyun u32 vdo_retry;
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun /* PPS */
413*4882a593Smuzhiyun struct pd_pps_data pps_data;
414*4882a593Smuzhiyun bool pps_pending;
415*4882a593Smuzhiyun int pps_status;
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun /* Deadline in jiffies to exit src_try_wait state */
418*4882a593Smuzhiyun unsigned long max_wait;
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun /* port belongs to a self powered device */
421*4882a593Smuzhiyun bool self_powered;
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun /* Sink FRS */
424*4882a593Smuzhiyun enum frs_typec_current new_source_frs_current;
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun /* Sink caps have been queried */
427*4882a593Smuzhiyun bool sink_cap_done;
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun /* Port is still in tCCDebounce */
430*4882a593Smuzhiyun bool debouncing;
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun /* Collision Avoidance and Atomic Message Sequence */
433*4882a593Smuzhiyun enum tcpm_state upcoming_state;
434*4882a593Smuzhiyun enum tcpm_ams ams;
435*4882a593Smuzhiyun enum tcpm_ams next_ams;
436*4882a593Smuzhiyun bool in_ams;
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun /* Auto vbus discharge status */
439*4882a593Smuzhiyun bool auto_vbus_discharge_enabled;
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun /*
442*4882a593Smuzhiyun * When set, port requests PD_P_SNK_STDBY_MW upon entering SNK_DISCOVERY and
443*4882a593Smuzhiyun * the actual currrent limit after RX of PD_CTRL_PSRDY for PD link,
444*4882a593Smuzhiyun * SNK_READY for non-pd link.
445*4882a593Smuzhiyun */
446*4882a593Smuzhiyun bool slow_charger_loop;
447*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
448*4882a593Smuzhiyun struct dentry *dentry;
449*4882a593Smuzhiyun struct mutex logbuffer_lock; /* log buffer access lock */
450*4882a593Smuzhiyun int logbuffer_head;
451*4882a593Smuzhiyun int logbuffer_tail;
452*4882a593Smuzhiyun u8 *logbuffer[LOG_BUFFER_ENTRIES];
453*4882a593Smuzhiyun #endif
454*4882a593Smuzhiyun };
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun struct pd_rx_event {
457*4882a593Smuzhiyun struct tcpm_port *port;
458*4882a593Smuzhiyun struct pd_message msg;
459*4882a593Smuzhiyun };
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun static const char * const pd_rev[] = {
462*4882a593Smuzhiyun [PD_REV10] = "rev1",
463*4882a593Smuzhiyun [PD_REV20] = "rev2",
464*4882a593Smuzhiyun [PD_REV30] = "rev3",
465*4882a593Smuzhiyun };
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun #define tcpm_cc_is_sink(cc) \
468*4882a593Smuzhiyun ((cc) == TYPEC_CC_RP_DEF || (cc) == TYPEC_CC_RP_1_5 || \
469*4882a593Smuzhiyun (cc) == TYPEC_CC_RP_3_0)
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun #define tcpm_port_is_sink(port) \
472*4882a593Smuzhiyun ((tcpm_cc_is_sink((port)->cc1) && !tcpm_cc_is_sink((port)->cc2)) || \
473*4882a593Smuzhiyun (tcpm_cc_is_sink((port)->cc2) && !tcpm_cc_is_sink((port)->cc1)))
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun #define tcpm_cc_is_source(cc) ((cc) == TYPEC_CC_RD)
476*4882a593Smuzhiyun #define tcpm_cc_is_audio(cc) ((cc) == TYPEC_CC_RA)
477*4882a593Smuzhiyun #define tcpm_cc_is_open(cc) ((cc) == TYPEC_CC_OPEN)
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun #define tcpm_port_is_source(port) \
480*4882a593Smuzhiyun ((tcpm_cc_is_source((port)->cc1) && \
481*4882a593Smuzhiyun !tcpm_cc_is_source((port)->cc2)) || \
482*4882a593Smuzhiyun (tcpm_cc_is_source((port)->cc2) && \
483*4882a593Smuzhiyun !tcpm_cc_is_source((port)->cc1)))
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun #define tcpm_port_is_debug(port) \
486*4882a593Smuzhiyun (tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2))
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun #define tcpm_port_is_audio(port) \
489*4882a593Smuzhiyun (tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_audio((port)->cc2))
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun #define tcpm_port_is_audio_detached(port) \
492*4882a593Smuzhiyun ((tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_open((port)->cc2)) || \
493*4882a593Smuzhiyun (tcpm_cc_is_audio((port)->cc2) && tcpm_cc_is_open((port)->cc1)))
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun #define tcpm_try_snk(port) \
496*4882a593Smuzhiyun ((port)->try_snk_count == 0 && (port)->try_role == TYPEC_SINK && \
497*4882a593Smuzhiyun (port)->port_type == TYPEC_PORT_DRP)
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun #define tcpm_try_src(port) \
500*4882a593Smuzhiyun ((port)->try_src_count == 0 && (port)->try_role == TYPEC_SOURCE && \
501*4882a593Smuzhiyun (port)->port_type == TYPEC_PORT_DRP)
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun #define tcpm_data_role_for_source(port) \
504*4882a593Smuzhiyun ((port)->typec_caps.data == TYPEC_PORT_UFP ? \
505*4882a593Smuzhiyun TYPEC_DEVICE : TYPEC_HOST)
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun #define tcpm_data_role_for_sink(port) \
508*4882a593Smuzhiyun ((port)->typec_caps.data == TYPEC_PORT_DFP ? \
509*4882a593Smuzhiyun TYPEC_HOST : TYPEC_DEVICE)
510*4882a593Smuzhiyun
tcpm_default_state(struct tcpm_port * port)511*4882a593Smuzhiyun static enum tcpm_state tcpm_default_state(struct tcpm_port *port)
512*4882a593Smuzhiyun {
513*4882a593Smuzhiyun if (port->port_type == TYPEC_PORT_DRP) {
514*4882a593Smuzhiyun if (port->try_role == TYPEC_SINK)
515*4882a593Smuzhiyun return SNK_UNATTACHED;
516*4882a593Smuzhiyun else if (port->try_role == TYPEC_SOURCE)
517*4882a593Smuzhiyun return SRC_UNATTACHED;
518*4882a593Smuzhiyun /* Fall through to return SRC_UNATTACHED */
519*4882a593Smuzhiyun } else if (port->port_type == TYPEC_PORT_SNK) {
520*4882a593Smuzhiyun return SNK_UNATTACHED;
521*4882a593Smuzhiyun }
522*4882a593Smuzhiyun return SRC_UNATTACHED;
523*4882a593Smuzhiyun }
524*4882a593Smuzhiyun
tcpm_port_is_disconnected(struct tcpm_port * port)525*4882a593Smuzhiyun static bool tcpm_port_is_disconnected(struct tcpm_port *port)
526*4882a593Smuzhiyun {
527*4882a593Smuzhiyun return (!port->attached && port->cc1 == TYPEC_CC_OPEN &&
528*4882a593Smuzhiyun port->cc2 == TYPEC_CC_OPEN) ||
529*4882a593Smuzhiyun (port->attached && ((port->polarity == TYPEC_POLARITY_CC1 &&
530*4882a593Smuzhiyun port->cc1 == TYPEC_CC_OPEN) ||
531*4882a593Smuzhiyun (port->polarity == TYPEC_POLARITY_CC2 &&
532*4882a593Smuzhiyun port->cc2 == TYPEC_CC_OPEN)));
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun
tcpm_set_cc(struct tcpm_port * port,enum typec_cc_status cc)535*4882a593Smuzhiyun static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc)
536*4882a593Smuzhiyun {
537*4882a593Smuzhiyun debug("%s: cc = %d", __func__, cc);
538*4882a593Smuzhiyun port->cc_req = cc;
539*4882a593Smuzhiyun port->tcpc->set_cc(port->tcpc, cc);
540*4882a593Smuzhiyun }
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun /*
543*4882a593Smuzhiyun * Determine RP value to set based on maximum current supported
544*4882a593Smuzhiyun * by a port if configured as source.
545*4882a593Smuzhiyun * Returns CC value to report to link partner.
546*4882a593Smuzhiyun */
tcpm_rp_cc(struct tcpm_port * port)547*4882a593Smuzhiyun static enum typec_cc_status tcpm_rp_cc(struct tcpm_port *port)
548*4882a593Smuzhiyun {
549*4882a593Smuzhiyun const u32 *src_pdo = port->src_pdo;
550*4882a593Smuzhiyun int nr_pdo = port->nr_src_pdo;
551*4882a593Smuzhiyun int i;
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun /*
554*4882a593Smuzhiyun * Search for first entry with matching voltage.
555*4882a593Smuzhiyun * It should report the maximum supported current.
556*4882a593Smuzhiyun */
557*4882a593Smuzhiyun for (i = 0; i < nr_pdo; i++) {
558*4882a593Smuzhiyun const u32 pdo = src_pdo[i];
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun if (pdo_type(pdo) == PDO_TYPE_FIXED &&
561*4882a593Smuzhiyun pdo_fixed_voltage(pdo) == 5000) {
562*4882a593Smuzhiyun unsigned int curr = pdo_max_current(pdo);
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun if (curr >= 3000)
565*4882a593Smuzhiyun return TYPEC_CC_RP_3_0;
566*4882a593Smuzhiyun else if (curr >= 1500)
567*4882a593Smuzhiyun return TYPEC_CC_RP_1_5;
568*4882a593Smuzhiyun return TYPEC_CC_RP_DEF;
569*4882a593Smuzhiyun }
570*4882a593Smuzhiyun }
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun return TYPEC_CC_RP_DEF;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun
tcpm_pd_transmit(struct tcpm_port * port,enum tcpm_transmit_type type,const struct pd_message * msg)575*4882a593Smuzhiyun static int tcpm_pd_transmit(struct tcpm_port *port,
576*4882a593Smuzhiyun enum tcpm_transmit_type type,
577*4882a593Smuzhiyun const struct pd_message *msg)
578*4882a593Smuzhiyun {
579*4882a593Smuzhiyun int ret;
580*4882a593Smuzhiyun int timeout = PD_T_TCPC_TX_TIMEOUT;
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun if (msg)
583*4882a593Smuzhiyun debug("PD TX, header: %#x\n", le16_to_cpu(msg->header));
584*4882a593Smuzhiyun else
585*4882a593Smuzhiyun debug("PD TX, type: %#x\n", type);
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun port->tx_complete = false;
588*4882a593Smuzhiyun ret = port->tcpc->pd_transmit(port->tcpc, type, msg, port->negotiated_rev);
589*4882a593Smuzhiyun if (ret < 0)
590*4882a593Smuzhiyun return ret;
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun while ((timeout > 0) && (!port->tx_complete)) {
593*4882a593Smuzhiyun port->tcpc->poll_event(port->tcpc);
594*4882a593Smuzhiyun udelay(1000);
595*4882a593Smuzhiyun timeout--;
596*4882a593Smuzhiyun }
597*4882a593Smuzhiyun
598*4882a593Smuzhiyun if (!timeout) {
599*4882a593Smuzhiyun printf("%s: pd transmit data timeout\n", __func__);
600*4882a593Smuzhiyun return -ETIMEDOUT;
601*4882a593Smuzhiyun }
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun switch (port->tx_status) {
604*4882a593Smuzhiyun case TCPC_TX_SUCCESS:
605*4882a593Smuzhiyun port->message_id = (port->message_id + 1) & PD_HEADER_ID_MASK;
606*4882a593Smuzhiyun break;
607*4882a593Smuzhiyun case TCPC_TX_DISCARDED:
608*4882a593Smuzhiyun ret = -EAGAIN;
609*4882a593Smuzhiyun break;
610*4882a593Smuzhiyun case TCPC_TX_FAILED:
611*4882a593Smuzhiyun default:
612*4882a593Smuzhiyun ret = -EIO;
613*4882a593Smuzhiyun break;
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun return ret;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun
tcpm_pd_transmit_complete(struct tcpm_port * port,enum tcpm_transmit_status status)619*4882a593Smuzhiyun void tcpm_pd_transmit_complete(struct tcpm_port *port,
620*4882a593Smuzhiyun enum tcpm_transmit_status status)
621*4882a593Smuzhiyun {
622*4882a593Smuzhiyun debug("%s: PD TX complete, status: %u\n", __func__, status);
623*4882a593Smuzhiyun port->poll_event_cnt = 0;
624*4882a593Smuzhiyun port->tx_status = status;
625*4882a593Smuzhiyun port->tx_complete = true;
626*4882a593Smuzhiyun }
627*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete);
628*4882a593Smuzhiyun
tcpm_set_polarity(struct tcpm_port * port,enum typec_cc_polarity polarity)629*4882a593Smuzhiyun static int tcpm_set_polarity(struct tcpm_port *port,
630*4882a593Smuzhiyun enum typec_cc_polarity polarity)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun int ret;
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun debug("%s: polarity %d\n", __func__, polarity);
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun ret = port->tcpc->set_polarity(port->tcpc, polarity);
637*4882a593Smuzhiyun if (ret < 0)
638*4882a593Smuzhiyun return ret;
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun port->polarity = polarity;
641*4882a593Smuzhiyun
642*4882a593Smuzhiyun return 0;
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun
tcpm_set_vconn(struct tcpm_port * port,bool enable)645*4882a593Smuzhiyun static int tcpm_set_vconn(struct tcpm_port *port, bool enable)
646*4882a593Smuzhiyun {
647*4882a593Smuzhiyun int ret;
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun debug("%s: vconn = %d\n", __func__, enable);
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun ret = port->tcpc->set_vconn(port->tcpc, enable);
652*4882a593Smuzhiyun if (!ret)
653*4882a593Smuzhiyun port->vconn_role = enable ? TYPEC_SOURCE : TYPEC_SINK;
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun return ret;
656*4882a593Smuzhiyun }
657*4882a593Smuzhiyun
tcpm_get_current_limit(struct tcpm_port * port)658*4882a593Smuzhiyun static u32 tcpm_get_current_limit(struct tcpm_port *port)
659*4882a593Smuzhiyun {
660*4882a593Smuzhiyun enum typec_cc_status cc;
661*4882a593Smuzhiyun u32 limit;
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun cc = port->polarity ? port->cc2 : port->cc1;
664*4882a593Smuzhiyun switch (cc) {
665*4882a593Smuzhiyun case TYPEC_CC_RP_1_5:
666*4882a593Smuzhiyun limit = 1500;
667*4882a593Smuzhiyun break;
668*4882a593Smuzhiyun case TYPEC_CC_RP_3_0:
669*4882a593Smuzhiyun limit = 3000;
670*4882a593Smuzhiyun break;
671*4882a593Smuzhiyun case TYPEC_CC_RP_DEF:
672*4882a593Smuzhiyun default:
673*4882a593Smuzhiyun if (port->tcpc->get_current_limit)
674*4882a593Smuzhiyun limit = port->tcpc->get_current_limit(port->tcpc);
675*4882a593Smuzhiyun else
676*4882a593Smuzhiyun limit = 0;
677*4882a593Smuzhiyun break;
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun return limit;
681*4882a593Smuzhiyun }
682*4882a593Smuzhiyun
tcpm_set_current_limit(struct tcpm_port * port,u32 max_ma,u32 mv)683*4882a593Smuzhiyun static int tcpm_set_current_limit(struct tcpm_port *port, u32 max_ma, u32 mv)
684*4882a593Smuzhiyun {
685*4882a593Smuzhiyun int ret = -EOPNOTSUPP;
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun debug("Setting voltage/current limit %u mV %u mA\n", mv, max_ma);
688*4882a593Smuzhiyun
689*4882a593Smuzhiyun port->supply_voltage = mv;
690*4882a593Smuzhiyun port->current_limit = max_ma;
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun if (port->tcpc->set_current_limit)
693*4882a593Smuzhiyun ret = port->tcpc->set_current_limit(port->tcpc, max_ma, mv);
694*4882a593Smuzhiyun
695*4882a593Smuzhiyun return ret;
696*4882a593Smuzhiyun }
697*4882a593Smuzhiyun
tcpm_set_attached_state(struct tcpm_port * port,bool attached)698*4882a593Smuzhiyun static int tcpm_set_attached_state(struct tcpm_port *port, bool attached)
699*4882a593Smuzhiyun {
700*4882a593Smuzhiyun return port->tcpc->set_roles(port->tcpc, attached, port->pwr_role,
701*4882a593Smuzhiyun port->data_role);
702*4882a593Smuzhiyun }
703*4882a593Smuzhiyun
tcpm_set_roles(struct tcpm_port * port,bool attached,enum typec_role role,enum typec_data_role data)704*4882a593Smuzhiyun static int tcpm_set_roles(struct tcpm_port *port, bool attached,
705*4882a593Smuzhiyun enum typec_role role, enum typec_data_role data)
706*4882a593Smuzhiyun {
707*4882a593Smuzhiyun #if 0
708*4882a593Smuzhiyun enum typec_orientation orientation;
709*4882a593Smuzhiyun enum usb_role usb_role;
710*4882a593Smuzhiyun #endif
711*4882a593Smuzhiyun int ret;
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun #if 0
714*4882a593Smuzhiyun if (port->polarity == TYPEC_POLARITY_CC1)
715*4882a593Smuzhiyun orientation = TYPEC_ORIENTATION_NORMAL;
716*4882a593Smuzhiyun else
717*4882a593Smuzhiyun orientation = TYPEC_ORIENTATION_REVERSE;
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun if (data == TYPEC_HOST)
720*4882a593Smuzhiyun usb_role = USB_ROLE_HOST;
721*4882a593Smuzhiyun else
722*4882a593Smuzhiyun usb_role = USB_ROLE_DEVICE;
723*4882a593Smuzhiyun
724*4882a593Smuzhiyun ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation);
725*4882a593Smuzhiyun if (ret < 0)
726*4882a593Smuzhiyun return ret;
727*4882a593Smuzhiyun #endif
728*4882a593Smuzhiyun
729*4882a593Smuzhiyun ret = port->tcpc->set_roles(port->tcpc, attached, role, data);
730*4882a593Smuzhiyun if (ret < 0)
731*4882a593Smuzhiyun return ret;
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun port->pwr_role = role;
734*4882a593Smuzhiyun port->data_role = data;
735*4882a593Smuzhiyun #if 0
736*4882a593Smuzhiyun typec_set_data_role(port->typec_port, data);
737*4882a593Smuzhiyun typec_set_pwr_role(port->typec_port, role);
738*4882a593Smuzhiyun #endif
739*4882a593Smuzhiyun
740*4882a593Smuzhiyun return 0;
741*4882a593Smuzhiyun }
742*4882a593Smuzhiyun
tcpm_pd_send_source_caps(struct tcpm_port * port)743*4882a593Smuzhiyun static int tcpm_pd_send_source_caps(struct tcpm_port *port)
744*4882a593Smuzhiyun {
745*4882a593Smuzhiyun struct pd_message msg;
746*4882a593Smuzhiyun int i;
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun memset(&msg, 0, sizeof(msg));
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun if (!port->nr_src_pdo) {
751*4882a593Smuzhiyun /* No source capabilities defined, sink only */
752*4882a593Smuzhiyun msg.header = PD_HEADER_LE(PD_CTRL_REJECT,
753*4882a593Smuzhiyun port->pwr_role,
754*4882a593Smuzhiyun port->data_role,
755*4882a593Smuzhiyun port->negotiated_rev,
756*4882a593Smuzhiyun port->message_id, 0);
757*4882a593Smuzhiyun } else {
758*4882a593Smuzhiyun msg.header = PD_HEADER_LE(PD_DATA_SOURCE_CAP,
759*4882a593Smuzhiyun port->pwr_role,
760*4882a593Smuzhiyun port->data_role,
761*4882a593Smuzhiyun port->negotiated_rev,
762*4882a593Smuzhiyun port->message_id,
763*4882a593Smuzhiyun port->nr_src_pdo);
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun for (i = 0; i < port->nr_src_pdo; i++)
767*4882a593Smuzhiyun msg.payload[i] = cpu_to_le32(port->src_pdo[i]);
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
770*4882a593Smuzhiyun }
771*4882a593Smuzhiyun
tcpm_pd_send_sink_caps(struct tcpm_port * port)772*4882a593Smuzhiyun static int tcpm_pd_send_sink_caps(struct tcpm_port *port)
773*4882a593Smuzhiyun {
774*4882a593Smuzhiyun struct pd_message msg;
775*4882a593Smuzhiyun unsigned int i;
776*4882a593Smuzhiyun
777*4882a593Smuzhiyun memset(&msg, 0, sizeof(msg));
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun if (!port->nr_snk_pdo) {
780*4882a593Smuzhiyun /* No sink capabilities defined, source only */
781*4882a593Smuzhiyun msg.header = PD_HEADER_LE(PD_CTRL_REJECT,
782*4882a593Smuzhiyun port->pwr_role,
783*4882a593Smuzhiyun port->data_role,
784*4882a593Smuzhiyun port->negotiated_rev,
785*4882a593Smuzhiyun port->message_id, 0);
786*4882a593Smuzhiyun } else {
787*4882a593Smuzhiyun msg.header = PD_HEADER_LE(PD_DATA_SINK_CAP,
788*4882a593Smuzhiyun port->pwr_role,
789*4882a593Smuzhiyun port->data_role,
790*4882a593Smuzhiyun port->negotiated_rev,
791*4882a593Smuzhiyun port->message_id,
792*4882a593Smuzhiyun port->nr_snk_pdo);
793*4882a593Smuzhiyun }
794*4882a593Smuzhiyun
795*4882a593Smuzhiyun for (i = 0; i < port->nr_snk_pdo; i++)
796*4882a593Smuzhiyun msg.payload[i] = cpu_to_le32(port->snk_pdo[i]);
797*4882a593Smuzhiyun
798*4882a593Smuzhiyun return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
799*4882a593Smuzhiyun }
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun static void tcpm_state_machine(struct tcpm_port *port);
802*4882a593Smuzhiyun static void tcpm_timer_uninit(struct tcpm_port *port);
tcpm_timer_irq(int irq,void * data)803*4882a593Smuzhiyun static void tcpm_timer_irq(int irq, void *data)
804*4882a593Smuzhiyun {
805*4882a593Smuzhiyun struct tcpm_port *port = data;
806*4882a593Smuzhiyun
807*4882a593Smuzhiyun writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
808*4882a593Smuzhiyun tcpm_timer_uninit(port);
809*4882a593Smuzhiyun tcpm_state_machine(port);
810*4882a593Smuzhiyun }
811*4882a593Smuzhiyun
tcpm_timer_init(struct tcpm_port * port,uint32_t ms)812*4882a593Smuzhiyun static void tcpm_timer_init(struct tcpm_port *port, uint32_t ms)
813*4882a593Smuzhiyun {
814*4882a593Smuzhiyun uint64_t period = 24000ULL * ms;
815*4882a593Smuzhiyun
816*4882a593Smuzhiyun /* Disable before conifg */
817*4882a593Smuzhiyun writel(0, TIMER_BASE + TIMER_CTRL);
818*4882a593Smuzhiyun
819*4882a593Smuzhiyun /* Config */
820*4882a593Smuzhiyun writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0);
821*4882a593Smuzhiyun writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1);
822*4882a593Smuzhiyun writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
823*4882a593Smuzhiyun writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL);
824*4882a593Smuzhiyun
825*4882a593Smuzhiyun /* IRQ */
826*4882a593Smuzhiyun irq_install_handler(TIMER_IRQ,
827*4882a593Smuzhiyun (interrupt_handler_t *)tcpm_timer_irq, port);
828*4882a593Smuzhiyun irq_handler_enable(TIMER_IRQ);
829*4882a593Smuzhiyun }
830*4882a593Smuzhiyun
tcpm_timer_uninit(struct tcpm_port * port)831*4882a593Smuzhiyun static void tcpm_timer_uninit(struct tcpm_port *port)
832*4882a593Smuzhiyun {
833*4882a593Smuzhiyun writel(0, TIMER_BASE + TIMER_CTRL);
834*4882a593Smuzhiyun
835*4882a593Smuzhiyun irq_handler_disable(TIMER_IRQ);
836*4882a593Smuzhiyun irq_free_handler(TIMER_IRQ);
837*4882a593Smuzhiyun }
838*4882a593Smuzhiyun
mod_tcpm_delayed_work(struct tcpm_port * port,unsigned int delay_ms)839*4882a593Smuzhiyun static void mod_tcpm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
840*4882a593Smuzhiyun {
841*4882a593Smuzhiyun if (delay_ms) {
842*4882a593Smuzhiyun tcpm_timer_init(port, delay_ms);
843*4882a593Smuzhiyun } else {
844*4882a593Smuzhiyun tcpm_timer_uninit(port);
845*4882a593Smuzhiyun tcpm_state_machine(port);
846*4882a593Smuzhiyun }
847*4882a593Smuzhiyun }
848*4882a593Smuzhiyun
tcpm_set_state(struct tcpm_port * port,enum tcpm_state state,unsigned int delay_ms)849*4882a593Smuzhiyun static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state,
850*4882a593Smuzhiyun unsigned int delay_ms)
851*4882a593Smuzhiyun {
852*4882a593Smuzhiyun debug("%s: line = %d, delay_ms = %d, set state = %s\n",
853*4882a593Smuzhiyun __func__, __LINE__, delay_ms, tcpm_states[state]);
854*4882a593Smuzhiyun
855*4882a593Smuzhiyun if (delay_ms) {
856*4882a593Smuzhiyun debug("pending state change %s -> %s @ %u ms [%s]\n",
857*4882a593Smuzhiyun tcpm_states[port->state], tcpm_states[state], delay_ms,
858*4882a593Smuzhiyun pd_rev[port->negotiated_rev]);
859*4882a593Smuzhiyun port->delayed_state = state;
860*4882a593Smuzhiyun mod_tcpm_delayed_work(port, delay_ms);
861*4882a593Smuzhiyun port->delay_ms = delay_ms;
862*4882a593Smuzhiyun } else {
863*4882a593Smuzhiyun debug("state change %s -> %s\n",
864*4882a593Smuzhiyun tcpm_states[port->state], tcpm_states[state]);
865*4882a593Smuzhiyun port->delayed_state = INVALID_STATE;
866*4882a593Smuzhiyun port->prev_state = port->state;
867*4882a593Smuzhiyun port->state = state;
868*4882a593Smuzhiyun /*
869*4882a593Smuzhiyun * Don't re-queue the state machine work item if we're currently
870*4882a593Smuzhiyun * in the state machine and we're immediately changing states.
871*4882a593Smuzhiyun * tcpm_state_machine_work() will continue running the state
872*4882a593Smuzhiyun * machine.
873*4882a593Smuzhiyun */
874*4882a593Smuzhiyun if (!port->state_machine_running)
875*4882a593Smuzhiyun mod_tcpm_delayed_work(port, 0);
876*4882a593Smuzhiyun }
877*4882a593Smuzhiyun }
878*4882a593Smuzhiyun
tcpm_set_state_cond(struct tcpm_port * port,enum tcpm_state state,unsigned int delay_ms)879*4882a593Smuzhiyun static void tcpm_set_state_cond(struct tcpm_port *port, enum tcpm_state state,
880*4882a593Smuzhiyun unsigned int delay_ms)
881*4882a593Smuzhiyun {
882*4882a593Smuzhiyun if (port->enter_state == port->state)
883*4882a593Smuzhiyun tcpm_set_state(port, state, delay_ms);
884*4882a593Smuzhiyun else
885*4882a593Smuzhiyun debug("skipped %sstate change %s -> %s [%u ms], context state %s [%s %s]\n",
886*4882a593Smuzhiyun delay_ms ? "delayed " : "",
887*4882a593Smuzhiyun tcpm_states[port->state], tcpm_states[state],
888*4882a593Smuzhiyun delay_ms, tcpm_states[port->enter_state],
889*4882a593Smuzhiyun pd_rev[port->negotiated_rev], tcpm_ams_str[port->ams]);
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun
tcpm_queue_message(struct tcpm_port * port,enum pd_msg_request message)892*4882a593Smuzhiyun static void tcpm_queue_message(struct tcpm_port *port,
893*4882a593Smuzhiyun enum pd_msg_request message)
894*4882a593Smuzhiyun {
895*4882a593Smuzhiyun port->queued_message = message;
896*4882a593Smuzhiyun mod_tcpm_delayed_work(port, 0);
897*4882a593Smuzhiyun }
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun #if 0
900*4882a593Smuzhiyun static void tcpm_pd_handle_msg(struct tcpm_port *port,
901*4882a593Smuzhiyun enum pd_msg_request message,
902*4882a593Smuzhiyun enum tcpm_ams ams);
903*4882a593Smuzhiyun #endif
904*4882a593Smuzhiyun
905*4882a593Smuzhiyun enum pdo_err {
906*4882a593Smuzhiyun PDO_NO_ERR,
907*4882a593Smuzhiyun PDO_ERR_NO_VSAFE5V,
908*4882a593Smuzhiyun PDO_ERR_VSAFE5V_NOT_FIRST,
909*4882a593Smuzhiyun PDO_ERR_PDO_TYPE_NOT_IN_ORDER,
910*4882a593Smuzhiyun PDO_ERR_FIXED_NOT_SORTED,
911*4882a593Smuzhiyun PDO_ERR_VARIABLE_BATT_NOT_SORTED,
912*4882a593Smuzhiyun PDO_ERR_DUPE_PDO,
913*4882a593Smuzhiyun PDO_ERR_PPS_APDO_NOT_SORTED,
914*4882a593Smuzhiyun PDO_ERR_DUPE_PPS_APDO,
915*4882a593Smuzhiyun };
916*4882a593Smuzhiyun
917*4882a593Smuzhiyun static const char * const pdo_err_msg[] = {
918*4882a593Smuzhiyun [PDO_ERR_NO_VSAFE5V] =
919*4882a593Smuzhiyun " err: source/sink caps should atleast have vSafe5V",
920*4882a593Smuzhiyun [PDO_ERR_VSAFE5V_NOT_FIRST] =
921*4882a593Smuzhiyun " err: vSafe5V Fixed Supply Object Shall always be the first object",
922*4882a593Smuzhiyun [PDO_ERR_PDO_TYPE_NOT_IN_ORDER] =
923*4882a593Smuzhiyun " err: PDOs should be in the following order: Fixed; Battery; Variable",
924*4882a593Smuzhiyun [PDO_ERR_FIXED_NOT_SORTED] =
925*4882a593Smuzhiyun " err: Fixed supply pdos should be in increasing order of their fixed voltage",
926*4882a593Smuzhiyun [PDO_ERR_VARIABLE_BATT_NOT_SORTED] =
927*4882a593Smuzhiyun " err: Variable/Battery supply pdos should be in increasing order of their minimum voltage",
928*4882a593Smuzhiyun [PDO_ERR_DUPE_PDO] =
929*4882a593Smuzhiyun " err: Variable/Batt supply pdos cannot have same min/max voltage",
930*4882a593Smuzhiyun [PDO_ERR_PPS_APDO_NOT_SORTED] =
931*4882a593Smuzhiyun " err: Programmable power supply apdos should be in increasing order of their maximum voltage",
932*4882a593Smuzhiyun [PDO_ERR_DUPE_PPS_APDO] =
933*4882a593Smuzhiyun " err: Programmable power supply apdos cannot have same min/max voltage and max current",
934*4882a593Smuzhiyun };
935*4882a593Smuzhiyun
tcpm_caps_err(struct tcpm_port * port,const u32 * pdo,unsigned int nr_pdo)936*4882a593Smuzhiyun static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo,
937*4882a593Smuzhiyun unsigned int nr_pdo)
938*4882a593Smuzhiyun {
939*4882a593Smuzhiyun unsigned int i;
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun /* Should at least contain vSafe5v */
942*4882a593Smuzhiyun if (nr_pdo < 1)
943*4882a593Smuzhiyun return PDO_ERR_NO_VSAFE5V;
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun /* The vSafe5V Fixed Supply Object Shall always be the first object */
946*4882a593Smuzhiyun if (pdo_type(pdo[0]) != PDO_TYPE_FIXED ||
947*4882a593Smuzhiyun pdo_fixed_voltage(pdo[0]) != VSAFE5V)
948*4882a593Smuzhiyun return PDO_ERR_VSAFE5V_NOT_FIRST;
949*4882a593Smuzhiyun
950*4882a593Smuzhiyun for (i = 1; i < nr_pdo; i++) {
951*4882a593Smuzhiyun if (pdo_type(pdo[i]) < pdo_type(pdo[i - 1])) {
952*4882a593Smuzhiyun return PDO_ERR_PDO_TYPE_NOT_IN_ORDER;
953*4882a593Smuzhiyun } else if (pdo_type(pdo[i]) == pdo_type(pdo[i - 1])) {
954*4882a593Smuzhiyun enum pd_pdo_type type = pdo_type(pdo[i]);
955*4882a593Smuzhiyun
956*4882a593Smuzhiyun switch (type) {
957*4882a593Smuzhiyun /*
958*4882a593Smuzhiyun * The remaining Fixed Supply Objects, if
959*4882a593Smuzhiyun * present, shall be sent in voltage order;
960*4882a593Smuzhiyun * lowest to highest.
961*4882a593Smuzhiyun */
962*4882a593Smuzhiyun case PDO_TYPE_FIXED:
963*4882a593Smuzhiyun if (pdo_fixed_voltage(pdo[i]) <=
964*4882a593Smuzhiyun pdo_fixed_voltage(pdo[i - 1]))
965*4882a593Smuzhiyun return PDO_ERR_FIXED_NOT_SORTED;
966*4882a593Smuzhiyun break;
967*4882a593Smuzhiyun /*
968*4882a593Smuzhiyun * The Battery Supply Objects and Variable
969*4882a593Smuzhiyun * supply, if present shall be sent in Minimum
970*4882a593Smuzhiyun * Voltage order; lowest to highest.
971*4882a593Smuzhiyun */
972*4882a593Smuzhiyun case PDO_TYPE_VAR:
973*4882a593Smuzhiyun case PDO_TYPE_BATT:
974*4882a593Smuzhiyun if (pdo_min_voltage(pdo[i]) <
975*4882a593Smuzhiyun pdo_min_voltage(pdo[i - 1]))
976*4882a593Smuzhiyun return PDO_ERR_VARIABLE_BATT_NOT_SORTED;
977*4882a593Smuzhiyun else if ((pdo_min_voltage(pdo[i]) ==
978*4882a593Smuzhiyun pdo_min_voltage(pdo[i - 1])) &&
979*4882a593Smuzhiyun (pdo_max_voltage(pdo[i]) ==
980*4882a593Smuzhiyun pdo_max_voltage(pdo[i - 1])))
981*4882a593Smuzhiyun return PDO_ERR_DUPE_PDO;
982*4882a593Smuzhiyun break;
983*4882a593Smuzhiyun /*
984*4882a593Smuzhiyun * The Programmable Power Supply APDOs, if present,
985*4882a593Smuzhiyun * shall be sent in Maximum Voltage order;
986*4882a593Smuzhiyun * lowest to highest.
987*4882a593Smuzhiyun */
988*4882a593Smuzhiyun case PDO_TYPE_APDO:
989*4882a593Smuzhiyun if (pdo_apdo_type(pdo[i]) != APDO_TYPE_PPS)
990*4882a593Smuzhiyun break;
991*4882a593Smuzhiyun
992*4882a593Smuzhiyun if (pdo_pps_apdo_max_voltage(pdo[i]) <
993*4882a593Smuzhiyun pdo_pps_apdo_max_voltage(pdo[i - 1]))
994*4882a593Smuzhiyun return PDO_ERR_PPS_APDO_NOT_SORTED;
995*4882a593Smuzhiyun else if (pdo_pps_apdo_min_voltage(pdo[i]) ==
996*4882a593Smuzhiyun pdo_pps_apdo_min_voltage(pdo[i - 1]) &&
997*4882a593Smuzhiyun pdo_pps_apdo_max_voltage(pdo[i]) ==
998*4882a593Smuzhiyun pdo_pps_apdo_max_voltage(pdo[i - 1]) &&
999*4882a593Smuzhiyun pdo_pps_apdo_max_current(pdo[i]) ==
1000*4882a593Smuzhiyun pdo_pps_apdo_max_current(pdo[i - 1]))
1001*4882a593Smuzhiyun return PDO_ERR_DUPE_PPS_APDO;
1002*4882a593Smuzhiyun break;
1003*4882a593Smuzhiyun default:
1004*4882a593Smuzhiyun printf("%s: Unknown pdo type\n", __func__);
1005*4882a593Smuzhiyun }
1006*4882a593Smuzhiyun }
1007*4882a593Smuzhiyun }
1008*4882a593Smuzhiyun
1009*4882a593Smuzhiyun return PDO_NO_ERR;
1010*4882a593Smuzhiyun }
1011*4882a593Smuzhiyun
tcpm_validate_caps(struct tcpm_port * port,const u32 * pdo,unsigned int nr_pdo)1012*4882a593Smuzhiyun static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo,
1013*4882a593Smuzhiyun unsigned int nr_pdo)
1014*4882a593Smuzhiyun {
1015*4882a593Smuzhiyun enum pdo_err err_index = tcpm_caps_err(port, pdo, nr_pdo);
1016*4882a593Smuzhiyun
1017*4882a593Smuzhiyun if (err_index != PDO_NO_ERR) {
1018*4882a593Smuzhiyun printf("%s", pdo_err_msg[err_index]);
1019*4882a593Smuzhiyun return -EINVAL;
1020*4882a593Smuzhiyun }
1021*4882a593Smuzhiyun
1022*4882a593Smuzhiyun return 0;
1023*4882a593Smuzhiyun }
1024*4882a593Smuzhiyun
1025*4882a593Smuzhiyun /*
1026*4882a593Smuzhiyun * PD (data, control) command handling functions
1027*4882a593Smuzhiyun */
ready_state(struct tcpm_port * port)1028*4882a593Smuzhiyun static inline enum tcpm_state ready_state(struct tcpm_port *port)
1029*4882a593Smuzhiyun {
1030*4882a593Smuzhiyun if (port->pwr_role == TYPEC_SOURCE)
1031*4882a593Smuzhiyun return SRC_READY;
1032*4882a593Smuzhiyun else
1033*4882a593Smuzhiyun return SNK_READY;
1034*4882a593Smuzhiyun }
1035*4882a593Smuzhiyun
1036*4882a593Smuzhiyun static int tcpm_pd_send_control(struct tcpm_port *port,
1037*4882a593Smuzhiyun enum pd_ctrl_msg_type type);
1038*4882a593Smuzhiyun
1039*4882a593Smuzhiyun #if 0
1040*4882a593Smuzhiyun static void tcpm_pd_handle_msg(struct tcpm_port *port,
1041*4882a593Smuzhiyun enum pd_msg_request message,
1042*4882a593Smuzhiyun enum tcpm_ams ams)
1043*4882a593Smuzhiyun {
1044*4882a593Smuzhiyun switch (port->state) {
1045*4882a593Smuzhiyun case SRC_READY:
1046*4882a593Smuzhiyun case SNK_READY:
1047*4882a593Smuzhiyun port->ams = ams;
1048*4882a593Smuzhiyun tcpm_queue_message(port, message);
1049*4882a593Smuzhiyun break;
1050*4882a593Smuzhiyun /* PD 3.0 Spec 8.3.3.4.1.1 and 6.8.1 */
1051*4882a593Smuzhiyun case SNK_TRANSITION_SINK:
1052*4882a593Smuzhiyun case SNK_TRANSITION_SINK_VBUS:
1053*4882a593Smuzhiyun case SRC_TRANSITION_SUPPLY:
1054*4882a593Smuzhiyun tcpm_set_state(port, HARD_RESET_SEND, 0);
1055*4882a593Smuzhiyun break;
1056*4882a593Smuzhiyun default:
1057*4882a593Smuzhiyun if (!tcpm_ams_interruptible(port)) {
1058*4882a593Smuzhiyun tcpm_set_state(port, port->pwr_role == TYPEC_SOURCE ?
1059*4882a593Smuzhiyun SRC_SOFT_RESET_WAIT_SNK_TX :
1060*4882a593Smuzhiyun SNK_SOFT_RESET,
1061*4882a593Smuzhiyun 0);
1062*4882a593Smuzhiyun } else {
1063*4882a593Smuzhiyun port->next_ams = ams;
1064*4882a593Smuzhiyun tcpm_set_state(port, ready_state(port), 0);
1065*4882a593Smuzhiyun /* 6.8.1 process the Message */
1066*4882a593Smuzhiyun tcpm_queue_message(port, message);
1067*4882a593Smuzhiyun }
1068*4882a593Smuzhiyun break;
1069*4882a593Smuzhiyun }
1070*4882a593Smuzhiyun }
1071*4882a593Smuzhiyun #endif
1072*4882a593Smuzhiyun
tcpm_pd_data_request(struct tcpm_port * port,const struct pd_message * msg)1073*4882a593Smuzhiyun static void tcpm_pd_data_request(struct tcpm_port *port,
1074*4882a593Smuzhiyun const struct pd_message *msg)
1075*4882a593Smuzhiyun {
1076*4882a593Smuzhiyun enum pd_data_msg_type type = pd_header_type_le(msg->header);
1077*4882a593Smuzhiyun unsigned int cnt = pd_header_cnt_le(msg->header);
1078*4882a593Smuzhiyun unsigned int rev = pd_header_rev_le(msg->header);
1079*4882a593Smuzhiyun unsigned int i;
1080*4882a593Smuzhiyun
1081*4882a593Smuzhiyun switch (type) {
1082*4882a593Smuzhiyun case PD_DATA_SOURCE_CAP:
1083*4882a593Smuzhiyun for (i = 0; i < cnt; i++)
1084*4882a593Smuzhiyun port->source_caps[i] = le32_to_cpu(msg->payload[i]);
1085*4882a593Smuzhiyun
1086*4882a593Smuzhiyun port->nr_source_caps = cnt;
1087*4882a593Smuzhiyun
1088*4882a593Smuzhiyun tcpm_validate_caps(port, port->source_caps,
1089*4882a593Smuzhiyun port->nr_source_caps);
1090*4882a593Smuzhiyun
1091*4882a593Smuzhiyun /*
1092*4882a593Smuzhiyun * Adjust revision in subsequent message headers, as required,
1093*4882a593Smuzhiyun * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't
1094*4882a593Smuzhiyun * support Rev 1.0 so just do nothing in that scenario.
1095*4882a593Smuzhiyun */
1096*4882a593Smuzhiyun if (rev == PD_REV10) {
1097*4882a593Smuzhiyun break;
1098*4882a593Smuzhiyun }
1099*4882a593Smuzhiyun
1100*4882a593Smuzhiyun if (rev < PD_MAX_REV)
1101*4882a593Smuzhiyun port->negotiated_rev = rev;
1102*4882a593Smuzhiyun
1103*4882a593Smuzhiyun if ((pdo_type(port->source_caps[0]) == PDO_TYPE_FIXED) &&
1104*4882a593Smuzhiyun (port->source_caps[0] & PDO_FIXED_DUAL_ROLE) &&
1105*4882a593Smuzhiyun (port->source_caps[0] & PDO_FIXED_DATA_SWAP)) {
1106*4882a593Smuzhiyun /* Dual role power and data, eg: self-powered Type-C */
1107*4882a593Smuzhiyun port->wait_dr_swap_Message = true;
1108*4882a593Smuzhiyun } else {
1109*4882a593Smuzhiyun /* Non-Dual role power, eg: adapter */
1110*4882a593Smuzhiyun port->wait_dr_swap_Message = false;
1111*4882a593Smuzhiyun }
1112*4882a593Smuzhiyun
1113*4882a593Smuzhiyun /*
1114*4882a593Smuzhiyun * This message may be received even if VBUS is not
1115*4882a593Smuzhiyun * present. This is quite unexpected; see USB PD
1116*4882a593Smuzhiyun * specification, sections 8.3.3.6.3.1 and 8.3.3.6.3.2.
1117*4882a593Smuzhiyun * However, at the same time, we must be ready to
1118*4882a593Smuzhiyun * receive this message and respond to it 15ms after
1119*4882a593Smuzhiyun * receiving PS_RDY during power swap operations, no matter
1120*4882a593Smuzhiyun * if VBUS is available or not (USB PD specification,
1121*4882a593Smuzhiyun * section 6.5.9.2).
1122*4882a593Smuzhiyun * So we need to accept the message either way,
1123*4882a593Smuzhiyun * but be prepared to keep waiting for VBUS after it was
1124*4882a593Smuzhiyun * handled.
1125*4882a593Smuzhiyun */
1126*4882a593Smuzhiyun tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0);
1127*4882a593Smuzhiyun break;
1128*4882a593Smuzhiyun case PD_DATA_REQUEST:
1129*4882a593Smuzhiyun /*
1130*4882a593Smuzhiyun * Adjust revision in subsequent message headers, as required,
1131*4882a593Smuzhiyun * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't
1132*4882a593Smuzhiyun * support Rev 1.0 so just reject in that scenario.
1133*4882a593Smuzhiyun */
1134*4882a593Smuzhiyun if (rev == PD_REV10) {
1135*4882a593Smuzhiyun tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
1136*4882a593Smuzhiyun break;
1137*4882a593Smuzhiyun }
1138*4882a593Smuzhiyun
1139*4882a593Smuzhiyun if (rev < PD_MAX_REV)
1140*4882a593Smuzhiyun port->negotiated_rev = rev;
1141*4882a593Smuzhiyun
1142*4882a593Smuzhiyun port->sink_request = le32_to_cpu(msg->payload[0]);
1143*4882a593Smuzhiyun
1144*4882a593Smuzhiyun tcpm_set_state(port, SRC_NEGOTIATE_CAPABILITIES, 0);
1145*4882a593Smuzhiyun break;
1146*4882a593Smuzhiyun case PD_DATA_SINK_CAP:
1147*4882a593Smuzhiyun /* We don't do anything with this at the moment... */
1148*4882a593Smuzhiyun for (i = 0; i < cnt; i++)
1149*4882a593Smuzhiyun port->sink_caps[i] = le32_to_cpu(msg->payload[i]);
1150*4882a593Smuzhiyun
1151*4882a593Smuzhiyun port->nr_sink_caps = cnt;
1152*4882a593Smuzhiyun break;
1153*4882a593Smuzhiyun default:
1154*4882a593Smuzhiyun break;
1155*4882a593Smuzhiyun }
1156*4882a593Smuzhiyun }
1157*4882a593Smuzhiyun
tcpm_pd_ctrl_request(struct tcpm_port * port,const struct pd_message * msg)1158*4882a593Smuzhiyun static void tcpm_pd_ctrl_request(struct tcpm_port *port,
1159*4882a593Smuzhiyun const struct pd_message *msg)
1160*4882a593Smuzhiyun {
1161*4882a593Smuzhiyun enum pd_ctrl_msg_type type = pd_header_type_le(msg->header);
1162*4882a593Smuzhiyun enum tcpm_state next_state;
1163*4882a593Smuzhiyun
1164*4882a593Smuzhiyun switch (type) {
1165*4882a593Smuzhiyun case PD_CTRL_GOOD_CRC:
1166*4882a593Smuzhiyun case PD_CTRL_PING:
1167*4882a593Smuzhiyun break;
1168*4882a593Smuzhiyun case PD_CTRL_GET_SOURCE_CAP:
1169*4882a593Smuzhiyun switch (port->state) {
1170*4882a593Smuzhiyun case SRC_READY:
1171*4882a593Smuzhiyun case SNK_READY:
1172*4882a593Smuzhiyun tcpm_queue_message(port, PD_MSG_DATA_SOURCE_CAP);
1173*4882a593Smuzhiyun break;
1174*4882a593Smuzhiyun default:
1175*4882a593Smuzhiyun tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
1176*4882a593Smuzhiyun break;
1177*4882a593Smuzhiyun }
1178*4882a593Smuzhiyun break;
1179*4882a593Smuzhiyun case PD_CTRL_GET_SINK_CAP:
1180*4882a593Smuzhiyun switch (port->state) {
1181*4882a593Smuzhiyun case SRC_READY:
1182*4882a593Smuzhiyun case SNK_READY:
1183*4882a593Smuzhiyun tcpm_queue_message(port, PD_MSG_DATA_SINK_CAP);
1184*4882a593Smuzhiyun break;
1185*4882a593Smuzhiyun default:
1186*4882a593Smuzhiyun tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
1187*4882a593Smuzhiyun break;
1188*4882a593Smuzhiyun }
1189*4882a593Smuzhiyun break;
1190*4882a593Smuzhiyun case PD_CTRL_GOTO_MIN:
1191*4882a593Smuzhiyun break;
1192*4882a593Smuzhiyun case PD_CTRL_PS_RDY:
1193*4882a593Smuzhiyun switch (port->state) {
1194*4882a593Smuzhiyun case SNK_TRANSITION_SINK:
1195*4882a593Smuzhiyun if (port->vbus_present) {
1196*4882a593Smuzhiyun tcpm_set_current_limit(port,
1197*4882a593Smuzhiyun port->req_current_limit,
1198*4882a593Smuzhiyun port->req_supply_voltage);
1199*4882a593Smuzhiyun port->explicit_contract = true;
1200*4882a593Smuzhiyun tcpm_set_state(port, SNK_READY, 0);
1201*4882a593Smuzhiyun } else {
1202*4882a593Smuzhiyun /*
1203*4882a593Smuzhiyun * Seen after power swap. Keep waiting for VBUS
1204*4882a593Smuzhiyun * in a transitional state.
1205*4882a593Smuzhiyun */
1206*4882a593Smuzhiyun tcpm_set_state(port,
1207*4882a593Smuzhiyun SNK_TRANSITION_SINK_VBUS, 0);
1208*4882a593Smuzhiyun }
1209*4882a593Smuzhiyun break;
1210*4882a593Smuzhiyun default:
1211*4882a593Smuzhiyun break;
1212*4882a593Smuzhiyun }
1213*4882a593Smuzhiyun break;
1214*4882a593Smuzhiyun case PD_CTRL_REJECT:
1215*4882a593Smuzhiyun case PD_CTRL_WAIT:
1216*4882a593Smuzhiyun case PD_CTRL_NOT_SUPP:
1217*4882a593Smuzhiyun switch (port->state) {
1218*4882a593Smuzhiyun case SNK_NEGOTIATE_CAPABILITIES:
1219*4882a593Smuzhiyun /* USB PD specification, Figure 8-43 */
1220*4882a593Smuzhiyun if (port->explicit_contract)
1221*4882a593Smuzhiyun next_state = SNK_READY;
1222*4882a593Smuzhiyun else
1223*4882a593Smuzhiyun next_state = SNK_WAIT_CAPABILITIES;
1224*4882a593Smuzhiyun
1225*4882a593Smuzhiyun tcpm_set_state(port, next_state, 0);
1226*4882a593Smuzhiyun break;
1227*4882a593Smuzhiyun case SNK_NEGOTIATE_PPS_CAPABILITIES:
1228*4882a593Smuzhiyun /* Revert data back from any requested PPS updates */
1229*4882a593Smuzhiyun port->pps_data.req_out_volt = port->supply_voltage;
1230*4882a593Smuzhiyun port->pps_data.req_op_curr = port->current_limit;
1231*4882a593Smuzhiyun port->pps_status = (type == PD_CTRL_WAIT ?
1232*4882a593Smuzhiyun -EAGAIN : -EOPNOTSUPP);
1233*4882a593Smuzhiyun tcpm_set_state(port, SNK_READY, 0);
1234*4882a593Smuzhiyun break;
1235*4882a593Smuzhiyun default:
1236*4882a593Smuzhiyun break;
1237*4882a593Smuzhiyun }
1238*4882a593Smuzhiyun break;
1239*4882a593Smuzhiyun case PD_CTRL_ACCEPT:
1240*4882a593Smuzhiyun switch (port->state) {
1241*4882a593Smuzhiyun case SNK_NEGOTIATE_CAPABILITIES:
1242*4882a593Smuzhiyun port->pps_data.active = false;
1243*4882a593Smuzhiyun tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
1244*4882a593Smuzhiyun break;
1245*4882a593Smuzhiyun case SNK_NEGOTIATE_PPS_CAPABILITIES:
1246*4882a593Smuzhiyun port->pps_data.active = true;
1247*4882a593Smuzhiyun /* ???? */
1248*4882a593Smuzhiyun port->pps_data.min_volt = port->pps_data.req_min_volt;
1249*4882a593Smuzhiyun port->pps_data.max_volt = port->pps_data.req_max_volt;
1250*4882a593Smuzhiyun port->pps_data.max_curr = port->pps_data.req_max_curr;
1251*4882a593Smuzhiyun port->req_supply_voltage = port->pps_data.req_out_volt;
1252*4882a593Smuzhiyun port->req_current_limit = port->pps_data.req_op_curr;
1253*4882a593Smuzhiyun tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
1254*4882a593Smuzhiyun break;
1255*4882a593Smuzhiyun case SOFT_RESET_SEND:
1256*4882a593Smuzhiyun port->message_id = 0;
1257*4882a593Smuzhiyun port->rx_msgid = -1;
1258*4882a593Smuzhiyun if (port->pwr_role == TYPEC_SOURCE)
1259*4882a593Smuzhiyun next_state = SRC_SEND_CAPABILITIES;
1260*4882a593Smuzhiyun else
1261*4882a593Smuzhiyun next_state = SNK_WAIT_CAPABILITIES;
1262*4882a593Smuzhiyun tcpm_set_state(port, next_state, 0);
1263*4882a593Smuzhiyun break;
1264*4882a593Smuzhiyun default:
1265*4882a593Smuzhiyun break;
1266*4882a593Smuzhiyun }
1267*4882a593Smuzhiyun break;
1268*4882a593Smuzhiyun case PD_CTRL_SOFT_RESET:
1269*4882a593Smuzhiyun tcpm_set_state(port, SOFT_RESET, 0);
1270*4882a593Smuzhiyun break;
1271*4882a593Smuzhiyun case PD_CTRL_DR_SWAP:
1272*4882a593Smuzhiyun if (port->port_type != TYPEC_PORT_DRP) {
1273*4882a593Smuzhiyun tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
1274*4882a593Smuzhiyun break;
1275*4882a593Smuzhiyun }
1276*4882a593Smuzhiyun /*
1277*4882a593Smuzhiyun * XXX
1278*4882a593Smuzhiyun * 6.3.9: If an alternate mode is active, a request to swap
1279*4882a593Smuzhiyun * alternate modes shall trigger a port reset.
1280*4882a593Smuzhiyun */
1281*4882a593Smuzhiyun switch (port->state) {
1282*4882a593Smuzhiyun case SRC_READY:
1283*4882a593Smuzhiyun case SNK_READY:
1284*4882a593Smuzhiyun #if 0
1285*4882a593Smuzhiyun if (port->vdm_sm_running) {
1286*4882a593Smuzhiyun tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
1287*4882a593Smuzhiyun break;
1288*4882a593Smuzhiyun }
1289*4882a593Smuzhiyun #endif
1290*4882a593Smuzhiyun tcpm_set_state(port, DR_SWAP_ACCEPT, 0);
1291*4882a593Smuzhiyun break;
1292*4882a593Smuzhiyun default:
1293*4882a593Smuzhiyun tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
1294*4882a593Smuzhiyun break;
1295*4882a593Smuzhiyun }
1296*4882a593Smuzhiyun break;
1297*4882a593Smuzhiyun case PD_CTRL_PR_SWAP:
1298*4882a593Smuzhiyun case PD_CTRL_VCONN_SWAP:
1299*4882a593Smuzhiyun case PD_CTRL_GET_SOURCE_CAP_EXT:
1300*4882a593Smuzhiyun case PD_CTRL_GET_STATUS:
1301*4882a593Smuzhiyun case PD_CTRL_FR_SWAP:
1302*4882a593Smuzhiyun case PD_CTRL_GET_PPS_STATUS:
1303*4882a593Smuzhiyun case PD_CTRL_GET_COUNTRY_CODES:
1304*4882a593Smuzhiyun /* Currently not supported */
1305*4882a593Smuzhiyun printf("Currently not supported type %#x \n", type);
1306*4882a593Smuzhiyun tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
1307*4882a593Smuzhiyun break;
1308*4882a593Smuzhiyun default:
1309*4882a593Smuzhiyun printf("Unrecognized ctrl message type %#x\n", type);
1310*4882a593Smuzhiyun break;
1311*4882a593Smuzhiyun }
1312*4882a593Smuzhiyun }
1313*4882a593Smuzhiyun
1314*4882a593Smuzhiyun #if 0
1315*4882a593Smuzhiyun static void tcpm_pd_ext_msg_request(struct tcpm_port *port,
1316*4882a593Smuzhiyun const struct pd_message *msg)
1317*4882a593Smuzhiyun {
1318*4882a593Smuzhiyun enum pd_ext_msg_type type = pd_header_type_le(msg->header);
1319*4882a593Smuzhiyun unsigned int data_size = pd_ext_header_data_size_le(msg->ext_msg.header);
1320*4882a593Smuzhiyun
1321*4882a593Smuzhiyun if (!(le16_to_cpu(msg->ext_msg.header) & PD_EXT_HDR_CHUNKED)) {
1322*4882a593Smuzhiyun tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS);
1323*4882a593Smuzhiyun printf("Unchunked extended messages unsupported\n");
1324*4882a593Smuzhiyun return;
1325*4882a593Smuzhiyun }
1326*4882a593Smuzhiyun
1327*4882a593Smuzhiyun if (data_size > PD_EXT_MAX_CHUNK_DATA) {
1328*4882a593Smuzhiyun tcpm_pd_handle_state(port, CHUNK_NOT_SUPP, NONE_AMS, PD_T_CHUNK_NOT_SUPP);
1329*4882a593Smuzhiyun printf("Chunk handling not yet supported\n");
1330*4882a593Smuzhiyun return;
1331*4882a593Smuzhiyun }
1332*4882a593Smuzhiyun
1333*4882a593Smuzhiyun switch (type) {
1334*4882a593Smuzhiyun case PD_EXT_STATUS:
1335*4882a593Smuzhiyun case PD_EXT_PPS_STATUS:
1336*4882a593Smuzhiyun tcpm_set_state(port, ready_state(port), 0);
1337*4882a593Smuzhiyun }
1338*4882a593Smuzhiyun break;
1339*4882a593Smuzhiyun case PD_EXT_SOURCE_CAP_EXT:
1340*4882a593Smuzhiyun case PD_EXT_GET_BATT_CAP:
1341*4882a593Smuzhiyun case PD_EXT_GET_BATT_STATUS:
1342*4882a593Smuzhiyun case PD_EXT_BATT_CAP:
1343*4882a593Smuzhiyun case PD_EXT_GET_MANUFACTURER_INFO:
1344*4882a593Smuzhiyun case PD_EXT_MANUFACTURER_INFO:
1345*4882a593Smuzhiyun case PD_EXT_SECURITY_REQUEST:
1346*4882a593Smuzhiyun case PD_EXT_SECURITY_RESPONSE:
1347*4882a593Smuzhiyun case PD_EXT_FW_UPDATE_REQUEST:
1348*4882a593Smuzhiyun case PD_EXT_FW_UPDATE_RESPONSE:
1349*4882a593Smuzhiyun case PD_EXT_COUNTRY_INFO:
1350*4882a593Smuzhiyun case PD_EXT_COUNTRY_CODES:
1351*4882a593Smuzhiyun tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS);
1352*4882a593Smuzhiyun break;
1353*4882a593Smuzhiyun default:
1354*4882a593Smuzhiyun tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS);
1355*4882a593Smuzhiyun printf("Unrecognized extended message type %#x\n", type);
1356*4882a593Smuzhiyun break;
1357*4882a593Smuzhiyun }
1358*4882a593Smuzhiyun }
1359*4882a593Smuzhiyun #endif
1360*4882a593Smuzhiyun
tcpm_pd_rx_handler(struct tcpm_port * port,struct pd_rx_event * event)1361*4882a593Smuzhiyun static void tcpm_pd_rx_handler(struct tcpm_port *port,
1362*4882a593Smuzhiyun struct pd_rx_event *event)
1363*4882a593Smuzhiyun {
1364*4882a593Smuzhiyun const struct pd_message *msg = &event->msg;
1365*4882a593Smuzhiyun unsigned int cnt = pd_header_cnt_le(msg->header);
1366*4882a593Smuzhiyun
1367*4882a593Smuzhiyun debug("PD RX, header: %#x [%d]\n", le16_to_cpu(msg->header),
1368*4882a593Smuzhiyun port->attached);
1369*4882a593Smuzhiyun
1370*4882a593Smuzhiyun if (port->attached) {
1371*4882a593Smuzhiyun enum pd_ctrl_msg_type type = pd_header_type_le(msg->header);
1372*4882a593Smuzhiyun unsigned int msgid = pd_header_msgid_le(msg->header);
1373*4882a593Smuzhiyun
1374*4882a593Smuzhiyun /*
1375*4882a593Smuzhiyun * USB PD standard, 6.6.1.2:
1376*4882a593Smuzhiyun * "... if MessageID value in a received Message is the
1377*4882a593Smuzhiyun * same as the stored value, the receiver shall return a
1378*4882a593Smuzhiyun * GoodCRC Message with that MessageID value and drop
1379*4882a593Smuzhiyun * the Message (this is a retry of an already received
1380*4882a593Smuzhiyun * Message). Note: this shall not apply to the Soft_Reset
1381*4882a593Smuzhiyun * Message which always has a MessageID value of zero."
1382*4882a593Smuzhiyun */
1383*4882a593Smuzhiyun if (msgid == port->rx_msgid && type != PD_CTRL_SOFT_RESET)
1384*4882a593Smuzhiyun goto done;
1385*4882a593Smuzhiyun port->rx_msgid = msgid;
1386*4882a593Smuzhiyun
1387*4882a593Smuzhiyun /*
1388*4882a593Smuzhiyun * If both ends believe to be DFP/host, we have a data role
1389*4882a593Smuzhiyun * mismatch.
1390*4882a593Smuzhiyun */
1391*4882a593Smuzhiyun if (!!(le16_to_cpu(msg->header) & PD_HEADER_DATA_ROLE) ==
1392*4882a593Smuzhiyun (port->data_role == TYPEC_HOST)) {
1393*4882a593Smuzhiyun printf("Data role mismatch, initiating error recovery\n");
1394*4882a593Smuzhiyun tcpm_set_state(port, ERROR_RECOVERY, 0);
1395*4882a593Smuzhiyun } else {
1396*4882a593Smuzhiyun if (cnt)
1397*4882a593Smuzhiyun tcpm_pd_data_request(port, msg);
1398*4882a593Smuzhiyun else
1399*4882a593Smuzhiyun tcpm_pd_ctrl_request(port, msg);
1400*4882a593Smuzhiyun }
1401*4882a593Smuzhiyun }
1402*4882a593Smuzhiyun
1403*4882a593Smuzhiyun done:
1404*4882a593Smuzhiyun kfree(event);
1405*4882a593Smuzhiyun }
1406*4882a593Smuzhiyun
tcpm_pd_receive(struct tcpm_port * port,const struct pd_message * msg)1407*4882a593Smuzhiyun void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg)
1408*4882a593Smuzhiyun {
1409*4882a593Smuzhiyun struct pd_rx_event *event;
1410*4882a593Smuzhiyun
1411*4882a593Smuzhiyun port->poll_event_cnt = 0;
1412*4882a593Smuzhiyun event = kzalloc(sizeof(*event), GFP_ATOMIC);
1413*4882a593Smuzhiyun if (!event)
1414*4882a593Smuzhiyun return;
1415*4882a593Smuzhiyun
1416*4882a593Smuzhiyun event->port = port;
1417*4882a593Smuzhiyun memcpy(&event->msg, msg, sizeof(*msg));
1418*4882a593Smuzhiyun tcpm_pd_rx_handler(port, event);
1419*4882a593Smuzhiyun }
1420*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_pd_receive);
1421*4882a593Smuzhiyun
tcpm_pd_send_control(struct tcpm_port * port,enum pd_ctrl_msg_type type)1422*4882a593Smuzhiyun static int tcpm_pd_send_control(struct tcpm_port *port,
1423*4882a593Smuzhiyun enum pd_ctrl_msg_type type)
1424*4882a593Smuzhiyun {
1425*4882a593Smuzhiyun struct pd_message msg;
1426*4882a593Smuzhiyun
1427*4882a593Smuzhiyun memset(&msg, 0, sizeof(msg));
1428*4882a593Smuzhiyun msg.header = PD_HEADER_LE(type, port->pwr_role,
1429*4882a593Smuzhiyun port->data_role,
1430*4882a593Smuzhiyun port->negotiated_rev,
1431*4882a593Smuzhiyun port->message_id, 0);
1432*4882a593Smuzhiyun
1433*4882a593Smuzhiyun return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
1434*4882a593Smuzhiyun }
1435*4882a593Smuzhiyun
1436*4882a593Smuzhiyun /*
1437*4882a593Smuzhiyun * Send queued message without affecting state.
1438*4882a593Smuzhiyun * Return true if state machine should go back to sleep,
1439*4882a593Smuzhiyun * false otherwise.
1440*4882a593Smuzhiyun */
tcpm_send_queued_message(struct tcpm_port * port)1441*4882a593Smuzhiyun static bool tcpm_send_queued_message(struct tcpm_port *port)
1442*4882a593Smuzhiyun {
1443*4882a593Smuzhiyun enum pd_msg_request queued_message;
1444*4882a593Smuzhiyun
1445*4882a593Smuzhiyun do {
1446*4882a593Smuzhiyun queued_message = port->queued_message;
1447*4882a593Smuzhiyun port->queued_message = PD_MSG_NONE;
1448*4882a593Smuzhiyun
1449*4882a593Smuzhiyun switch (queued_message) {
1450*4882a593Smuzhiyun case PD_MSG_CTRL_WAIT:
1451*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_WAIT);
1452*4882a593Smuzhiyun break;
1453*4882a593Smuzhiyun case PD_MSG_CTRL_REJECT:
1454*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_REJECT);
1455*4882a593Smuzhiyun break;
1456*4882a593Smuzhiyun case PD_MSG_CTRL_NOT_SUPP:
1457*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP);
1458*4882a593Smuzhiyun break;
1459*4882a593Smuzhiyun case PD_MSG_DATA_SINK_CAP:
1460*4882a593Smuzhiyun tcpm_pd_send_sink_caps(port);
1461*4882a593Smuzhiyun break;
1462*4882a593Smuzhiyun case PD_MSG_DATA_SOURCE_CAP:
1463*4882a593Smuzhiyun tcpm_pd_send_source_caps(port);
1464*4882a593Smuzhiyun break;
1465*4882a593Smuzhiyun default:
1466*4882a593Smuzhiyun break;
1467*4882a593Smuzhiyun }
1468*4882a593Smuzhiyun } while (port->queued_message != PD_MSG_NONE);
1469*4882a593Smuzhiyun
1470*4882a593Smuzhiyun #if 0
1471*4882a593Smuzhiyun /* ??? */
1472*4882a593Smuzhiyun if (port->delayed_state != INVALID_STATE) {
1473*4882a593Smuzhiyun if (ktime_after(port->delayed_runtime, ktime_get())) {
1474*4882a593Smuzhiyun mod_tcpm_delayed_work(port, ktime_to_ms(ktime_sub(port->delayed_runtime,
1475*4882a593Smuzhiyun ktime_get())));
1476*4882a593Smuzhiyun return true;
1477*4882a593Smuzhiyun }
1478*4882a593Smuzhiyun port->delayed_state = INVALID_STATE;
1479*4882a593Smuzhiyun }
1480*4882a593Smuzhiyun #endif
1481*4882a593Smuzhiyun return false;
1482*4882a593Smuzhiyun }
1483*4882a593Smuzhiyun
tcpm_pd_check_request(struct tcpm_port * port)1484*4882a593Smuzhiyun static int tcpm_pd_check_request(struct tcpm_port *port)
1485*4882a593Smuzhiyun {
1486*4882a593Smuzhiyun u32 pdo, rdo = port->sink_request;
1487*4882a593Smuzhiyun unsigned int max, op, pdo_max, index;
1488*4882a593Smuzhiyun enum pd_pdo_type type;
1489*4882a593Smuzhiyun
1490*4882a593Smuzhiyun index = rdo_index(rdo);
1491*4882a593Smuzhiyun if (!index || index > port->nr_src_pdo)
1492*4882a593Smuzhiyun return -EINVAL;
1493*4882a593Smuzhiyun
1494*4882a593Smuzhiyun pdo = port->src_pdo[index - 1];
1495*4882a593Smuzhiyun type = pdo_type(pdo);
1496*4882a593Smuzhiyun switch (type) {
1497*4882a593Smuzhiyun case PDO_TYPE_FIXED:
1498*4882a593Smuzhiyun case PDO_TYPE_VAR:
1499*4882a593Smuzhiyun max = rdo_max_current(rdo);
1500*4882a593Smuzhiyun op = rdo_op_current(rdo);
1501*4882a593Smuzhiyun pdo_max = pdo_max_current(pdo);
1502*4882a593Smuzhiyun
1503*4882a593Smuzhiyun if (op > pdo_max)
1504*4882a593Smuzhiyun return -EINVAL;
1505*4882a593Smuzhiyun if (max > pdo_max && !(rdo & RDO_CAP_MISMATCH))
1506*4882a593Smuzhiyun return -EINVAL;
1507*4882a593Smuzhiyun
1508*4882a593Smuzhiyun if (type == PDO_TYPE_FIXED)
1509*4882a593Smuzhiyun debug("Requested %u mV, %u mA for %u / %u mA\n",
1510*4882a593Smuzhiyun pdo_fixed_voltage(pdo), pdo_max, op, max);
1511*4882a593Smuzhiyun else
1512*4882a593Smuzhiyun debug("Requested %u -> %u mV, %u mA for %u / %u mA\n",
1513*4882a593Smuzhiyun pdo_min_voltage(pdo), pdo_max_voltage(pdo),
1514*4882a593Smuzhiyun pdo_max, op, max);
1515*4882a593Smuzhiyun break;
1516*4882a593Smuzhiyun case PDO_TYPE_BATT:
1517*4882a593Smuzhiyun max = rdo_max_power(rdo);
1518*4882a593Smuzhiyun op = rdo_op_power(rdo);
1519*4882a593Smuzhiyun pdo_max = pdo_max_power(pdo);
1520*4882a593Smuzhiyun
1521*4882a593Smuzhiyun if (op > pdo_max)
1522*4882a593Smuzhiyun return -EINVAL;
1523*4882a593Smuzhiyun if (max > pdo_max && !(rdo & RDO_CAP_MISMATCH))
1524*4882a593Smuzhiyun return -EINVAL;
1525*4882a593Smuzhiyun printf("Requested %u -> %u mV, %u mW for %u / %u mW\n",
1526*4882a593Smuzhiyun pdo_min_voltage(pdo), pdo_max_voltage(pdo),
1527*4882a593Smuzhiyun pdo_max, op, max);
1528*4882a593Smuzhiyun break;
1529*4882a593Smuzhiyun default:
1530*4882a593Smuzhiyun return -EINVAL;
1531*4882a593Smuzhiyun }
1532*4882a593Smuzhiyun
1533*4882a593Smuzhiyun port->op_vsafe5v = index == 1;
1534*4882a593Smuzhiyun
1535*4882a593Smuzhiyun return 0;
1536*4882a593Smuzhiyun }
1537*4882a593Smuzhiyun
1538*4882a593Smuzhiyun #define min_power(x, y) min(pdo_max_power(x), pdo_max_power(y))
1539*4882a593Smuzhiyun #define min_current(x, y) min(pdo_max_current(x), pdo_max_current(y))
1540*4882a593Smuzhiyun
tcpm_pd_select_pdo(struct tcpm_port * port,int * sink_pdo,int * src_pdo)1541*4882a593Smuzhiyun static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
1542*4882a593Smuzhiyun int *src_pdo)
1543*4882a593Smuzhiyun {
1544*4882a593Smuzhiyun unsigned int i, j, max_src_mv = 0, min_src_mv = 0, max_mw = 0,
1545*4882a593Smuzhiyun max_mv = 0, src_mw = 0, src_ma = 0, max_snk_mv = 0,
1546*4882a593Smuzhiyun min_snk_mv = 0;
1547*4882a593Smuzhiyun int ret = -EINVAL;
1548*4882a593Smuzhiyun
1549*4882a593Smuzhiyun port->pps_data.supported = false;
1550*4882a593Smuzhiyun port->usb_type = POWER_SUPPLY_USB_TYPE_PD;
1551*4882a593Smuzhiyun
1552*4882a593Smuzhiyun /*
1553*4882a593Smuzhiyun * Select the source PDO providing the most power which has a
1554*4882a593Smuzhiyun * matchig sink cap.
1555*4882a593Smuzhiyun */
1556*4882a593Smuzhiyun for (i = 0; i < port->nr_source_caps; i++) {
1557*4882a593Smuzhiyun u32 pdo = port->source_caps[i];
1558*4882a593Smuzhiyun enum pd_pdo_type type = pdo_type(pdo);
1559*4882a593Smuzhiyun
1560*4882a593Smuzhiyun switch (type) {
1561*4882a593Smuzhiyun case PDO_TYPE_FIXED:
1562*4882a593Smuzhiyun max_src_mv = pdo_fixed_voltage(pdo);
1563*4882a593Smuzhiyun min_src_mv = max_src_mv;
1564*4882a593Smuzhiyun break;
1565*4882a593Smuzhiyun case PDO_TYPE_BATT:
1566*4882a593Smuzhiyun case PDO_TYPE_VAR:
1567*4882a593Smuzhiyun max_src_mv = pdo_max_voltage(pdo);
1568*4882a593Smuzhiyun min_src_mv = pdo_min_voltage(pdo);
1569*4882a593Smuzhiyun break;
1570*4882a593Smuzhiyun case PDO_TYPE_APDO:
1571*4882a593Smuzhiyun if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) {
1572*4882a593Smuzhiyun port->pps_data.supported = true;
1573*4882a593Smuzhiyun port->usb_type =
1574*4882a593Smuzhiyun POWER_SUPPLY_USB_TYPE_PD_PPS;
1575*4882a593Smuzhiyun }
1576*4882a593Smuzhiyun continue;
1577*4882a593Smuzhiyun default:
1578*4882a593Smuzhiyun printf("Invalid source PDO type, ignoring\n");
1579*4882a593Smuzhiyun continue;
1580*4882a593Smuzhiyun }
1581*4882a593Smuzhiyun
1582*4882a593Smuzhiyun switch (type) {
1583*4882a593Smuzhiyun case PDO_TYPE_FIXED:
1584*4882a593Smuzhiyun case PDO_TYPE_VAR:
1585*4882a593Smuzhiyun src_ma = pdo_max_current(pdo);
1586*4882a593Smuzhiyun src_mw = src_ma * min_src_mv / 1000;
1587*4882a593Smuzhiyun break;
1588*4882a593Smuzhiyun case PDO_TYPE_BATT:
1589*4882a593Smuzhiyun src_mw = pdo_max_power(pdo);
1590*4882a593Smuzhiyun break;
1591*4882a593Smuzhiyun case PDO_TYPE_APDO:
1592*4882a593Smuzhiyun continue;
1593*4882a593Smuzhiyun default:
1594*4882a593Smuzhiyun printf("Invalid source PDO type, ignoring\n");
1595*4882a593Smuzhiyun continue;
1596*4882a593Smuzhiyun }
1597*4882a593Smuzhiyun
1598*4882a593Smuzhiyun for (j = 0; j < port->nr_snk_pdo; j++) {
1599*4882a593Smuzhiyun pdo = port->snk_pdo[j];
1600*4882a593Smuzhiyun
1601*4882a593Smuzhiyun switch (pdo_type(pdo)) {
1602*4882a593Smuzhiyun case PDO_TYPE_FIXED:
1603*4882a593Smuzhiyun max_snk_mv = pdo_fixed_voltage(pdo);
1604*4882a593Smuzhiyun min_snk_mv = max_snk_mv;
1605*4882a593Smuzhiyun break;
1606*4882a593Smuzhiyun case PDO_TYPE_BATT:
1607*4882a593Smuzhiyun case PDO_TYPE_VAR:
1608*4882a593Smuzhiyun max_snk_mv = pdo_max_voltage(pdo);
1609*4882a593Smuzhiyun min_snk_mv = pdo_min_voltage(pdo);
1610*4882a593Smuzhiyun break;
1611*4882a593Smuzhiyun case PDO_TYPE_APDO:
1612*4882a593Smuzhiyun continue;
1613*4882a593Smuzhiyun default:
1614*4882a593Smuzhiyun printf("Invalid sink PDO type, ignoring\n");
1615*4882a593Smuzhiyun continue;
1616*4882a593Smuzhiyun }
1617*4882a593Smuzhiyun
1618*4882a593Smuzhiyun if (max_src_mv <= max_snk_mv &&
1619*4882a593Smuzhiyun min_src_mv >= min_snk_mv) {
1620*4882a593Smuzhiyun /* Prefer higher voltages if available */
1621*4882a593Smuzhiyun if ((src_mw == max_mw && min_src_mv > max_mv) ||
1622*4882a593Smuzhiyun src_mw > max_mw) {
1623*4882a593Smuzhiyun *src_pdo = i;
1624*4882a593Smuzhiyun *sink_pdo = j;
1625*4882a593Smuzhiyun max_mw = src_mw;
1626*4882a593Smuzhiyun max_mv = min_src_mv;
1627*4882a593Smuzhiyun ret = 0;
1628*4882a593Smuzhiyun }
1629*4882a593Smuzhiyun }
1630*4882a593Smuzhiyun }
1631*4882a593Smuzhiyun }
1632*4882a593Smuzhiyun
1633*4882a593Smuzhiyun return ret;
1634*4882a593Smuzhiyun }
1635*4882a593Smuzhiyun
1636*4882a593Smuzhiyun #define min_pps_apdo_current(x, y) \
1637*4882a593Smuzhiyun min(pdo_pps_apdo_max_current(x), pdo_pps_apdo_max_current(y))
1638*4882a593Smuzhiyun
tcpm_pd_select_pps_apdo(struct tcpm_port * port)1639*4882a593Smuzhiyun static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
1640*4882a593Smuzhiyun {
1641*4882a593Smuzhiyun unsigned int i, j, max_mw = 0, max_mv = 0;
1642*4882a593Smuzhiyun unsigned int min_src_mv, max_src_mv, src_ma, src_mw;
1643*4882a593Smuzhiyun unsigned int min_snk_mv, max_snk_mv;
1644*4882a593Smuzhiyun unsigned int max_op_mv;
1645*4882a593Smuzhiyun u32 pdo, src, snk;
1646*4882a593Smuzhiyun unsigned int src_pdo = 0, snk_pdo = 0;
1647*4882a593Smuzhiyun
1648*4882a593Smuzhiyun /*
1649*4882a593Smuzhiyun * Select the source PPS APDO providing the most power while staying
1650*4882a593Smuzhiyun * within the board's limits. We skip the first PDO as this is always
1651*4882a593Smuzhiyun * 5V 3A.
1652*4882a593Smuzhiyun */
1653*4882a593Smuzhiyun for (i = 1; i < port->nr_source_caps; ++i) {
1654*4882a593Smuzhiyun pdo = port->source_caps[i];
1655*4882a593Smuzhiyun
1656*4882a593Smuzhiyun switch (pdo_type(pdo)) {
1657*4882a593Smuzhiyun case PDO_TYPE_APDO:
1658*4882a593Smuzhiyun if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
1659*4882a593Smuzhiyun printf("Not PPS APDO (source), ignoring\n");
1660*4882a593Smuzhiyun continue;
1661*4882a593Smuzhiyun }
1662*4882a593Smuzhiyun
1663*4882a593Smuzhiyun min_src_mv = pdo_pps_apdo_min_voltage(pdo);
1664*4882a593Smuzhiyun max_src_mv = pdo_pps_apdo_max_voltage(pdo);
1665*4882a593Smuzhiyun src_ma = pdo_pps_apdo_max_current(pdo);
1666*4882a593Smuzhiyun src_mw = (src_ma * max_src_mv) / 1000;
1667*4882a593Smuzhiyun
1668*4882a593Smuzhiyun /*
1669*4882a593Smuzhiyun * Now search through the sink PDOs to find a matching
1670*4882a593Smuzhiyun * PPS APDO. Again skip the first sink PDO as this will
1671*4882a593Smuzhiyun * always be 5V 3A.
1672*4882a593Smuzhiyun */
1673*4882a593Smuzhiyun for (j = 1; j < port->nr_snk_pdo; j++) {
1674*4882a593Smuzhiyun pdo = port->snk_pdo[j];
1675*4882a593Smuzhiyun
1676*4882a593Smuzhiyun switch (pdo_type(pdo)) {
1677*4882a593Smuzhiyun case PDO_TYPE_APDO:
1678*4882a593Smuzhiyun if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
1679*4882a593Smuzhiyun printf("Not PPS APDO (sink), ignoring\n");
1680*4882a593Smuzhiyun continue;
1681*4882a593Smuzhiyun }
1682*4882a593Smuzhiyun
1683*4882a593Smuzhiyun min_snk_mv =
1684*4882a593Smuzhiyun pdo_pps_apdo_min_voltage(pdo);
1685*4882a593Smuzhiyun max_snk_mv =
1686*4882a593Smuzhiyun pdo_pps_apdo_max_voltage(pdo);
1687*4882a593Smuzhiyun break;
1688*4882a593Smuzhiyun default:
1689*4882a593Smuzhiyun printf("Not APDO type (sink), ignoring\n");
1690*4882a593Smuzhiyun continue;
1691*4882a593Smuzhiyun }
1692*4882a593Smuzhiyun
1693*4882a593Smuzhiyun if (min_src_mv <= max_snk_mv &&
1694*4882a593Smuzhiyun max_src_mv >= min_snk_mv) {
1695*4882a593Smuzhiyun max_op_mv = min(max_src_mv, max_snk_mv);
1696*4882a593Smuzhiyun src_mw = (max_op_mv * src_ma) / 1000;
1697*4882a593Smuzhiyun /* Prefer higher voltages if available */
1698*4882a593Smuzhiyun if ((src_mw == max_mw &&
1699*4882a593Smuzhiyun max_op_mv > max_mv) ||
1700*4882a593Smuzhiyun src_mw > max_mw) {
1701*4882a593Smuzhiyun src_pdo = i;
1702*4882a593Smuzhiyun snk_pdo = j;
1703*4882a593Smuzhiyun max_mw = src_mw;
1704*4882a593Smuzhiyun max_mv = max_op_mv;
1705*4882a593Smuzhiyun }
1706*4882a593Smuzhiyun }
1707*4882a593Smuzhiyun }
1708*4882a593Smuzhiyun
1709*4882a593Smuzhiyun break;
1710*4882a593Smuzhiyun default:
1711*4882a593Smuzhiyun printf("Not APDO type (source), ignoring\n");
1712*4882a593Smuzhiyun continue;
1713*4882a593Smuzhiyun }
1714*4882a593Smuzhiyun }
1715*4882a593Smuzhiyun
1716*4882a593Smuzhiyun if (src_pdo) {
1717*4882a593Smuzhiyun src = port->source_caps[src_pdo];
1718*4882a593Smuzhiyun snk = port->snk_pdo[snk_pdo];
1719*4882a593Smuzhiyun
1720*4882a593Smuzhiyun port->pps_data.req_min_volt = max(pdo_pps_apdo_min_voltage(src),
1721*4882a593Smuzhiyun pdo_pps_apdo_min_voltage(snk));
1722*4882a593Smuzhiyun port->pps_data.req_max_volt = min(pdo_pps_apdo_max_voltage(src),
1723*4882a593Smuzhiyun pdo_pps_apdo_max_voltage(snk));
1724*4882a593Smuzhiyun port->pps_data.req_max_curr = min_pps_apdo_current(src, snk);
1725*4882a593Smuzhiyun port->pps_data.req_out_volt = min(port->pps_data.req_max_volt,
1726*4882a593Smuzhiyun max(port->pps_data.req_min_volt,
1727*4882a593Smuzhiyun port->pps_data.req_out_volt));
1728*4882a593Smuzhiyun port->pps_data.req_op_curr = min(port->pps_data.req_max_curr,
1729*4882a593Smuzhiyun port->pps_data.req_op_curr);
1730*4882a593Smuzhiyun }
1731*4882a593Smuzhiyun
1732*4882a593Smuzhiyun return src_pdo;
1733*4882a593Smuzhiyun }
1734*4882a593Smuzhiyun
tcpm_pd_build_request(struct tcpm_port * port,u32 * rdo)1735*4882a593Smuzhiyun static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo)
1736*4882a593Smuzhiyun {
1737*4882a593Smuzhiyun unsigned int mv, ma, mw, flags;
1738*4882a593Smuzhiyun unsigned int max_ma, max_mw;
1739*4882a593Smuzhiyun enum pd_pdo_type type;
1740*4882a593Smuzhiyun u32 pdo, matching_snk_pdo;
1741*4882a593Smuzhiyun int src_pdo_index = 0;
1742*4882a593Smuzhiyun int snk_pdo_index = 0;
1743*4882a593Smuzhiyun int ret;
1744*4882a593Smuzhiyun
1745*4882a593Smuzhiyun ret = tcpm_pd_select_pdo(port, &snk_pdo_index, &src_pdo_index);
1746*4882a593Smuzhiyun if (ret < 0)
1747*4882a593Smuzhiyun return ret;
1748*4882a593Smuzhiyun
1749*4882a593Smuzhiyun pdo = port->source_caps[src_pdo_index];
1750*4882a593Smuzhiyun matching_snk_pdo = port->snk_pdo[snk_pdo_index];
1751*4882a593Smuzhiyun type = pdo_type(pdo);
1752*4882a593Smuzhiyun
1753*4882a593Smuzhiyun switch (type) {
1754*4882a593Smuzhiyun case PDO_TYPE_FIXED:
1755*4882a593Smuzhiyun mv = pdo_fixed_voltage(pdo);
1756*4882a593Smuzhiyun break;
1757*4882a593Smuzhiyun case PDO_TYPE_BATT:
1758*4882a593Smuzhiyun case PDO_TYPE_VAR:
1759*4882a593Smuzhiyun mv = pdo_min_voltage(pdo);
1760*4882a593Smuzhiyun break;
1761*4882a593Smuzhiyun default:
1762*4882a593Smuzhiyun printf("Invalid PDO selected!\n");
1763*4882a593Smuzhiyun return -EINVAL;
1764*4882a593Smuzhiyun }
1765*4882a593Smuzhiyun
1766*4882a593Smuzhiyun /* Select maximum available current within the sink pdo's limit */
1767*4882a593Smuzhiyun if (type == PDO_TYPE_BATT) {
1768*4882a593Smuzhiyun mw = min_power(pdo, matching_snk_pdo);
1769*4882a593Smuzhiyun ma = 1000 * mw / mv;
1770*4882a593Smuzhiyun } else {
1771*4882a593Smuzhiyun ma = min_current(pdo, matching_snk_pdo);
1772*4882a593Smuzhiyun mw = ma * mv / 1000;
1773*4882a593Smuzhiyun }
1774*4882a593Smuzhiyun
1775*4882a593Smuzhiyun flags = RDO_USB_COMM | RDO_NO_SUSPEND;
1776*4882a593Smuzhiyun
1777*4882a593Smuzhiyun /* Set mismatch bit if offered power is less than operating power */
1778*4882a593Smuzhiyun max_ma = ma;
1779*4882a593Smuzhiyun max_mw = mw;
1780*4882a593Smuzhiyun if (mw < port->operating_snk_mw) {
1781*4882a593Smuzhiyun flags |= RDO_CAP_MISMATCH;
1782*4882a593Smuzhiyun if (type == PDO_TYPE_BATT &&
1783*4882a593Smuzhiyun (pdo_max_power(matching_snk_pdo) > pdo_max_power(pdo)))
1784*4882a593Smuzhiyun max_mw = pdo_max_power(matching_snk_pdo);
1785*4882a593Smuzhiyun else if (pdo_max_current(matching_snk_pdo) >
1786*4882a593Smuzhiyun pdo_max_current(pdo))
1787*4882a593Smuzhiyun max_ma = pdo_max_current(matching_snk_pdo);
1788*4882a593Smuzhiyun }
1789*4882a593Smuzhiyun
1790*4882a593Smuzhiyun debug("cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d\n",
1791*4882a593Smuzhiyun port->cc_req, port->cc1, port->cc2, port->vbus_source,
1792*4882a593Smuzhiyun port->vconn_role == TYPEC_SOURCE ? "source" : "sink",
1793*4882a593Smuzhiyun port->polarity);
1794*4882a593Smuzhiyun
1795*4882a593Smuzhiyun if (type == PDO_TYPE_BATT) {
1796*4882a593Smuzhiyun *rdo = RDO_BATT(src_pdo_index + 1, mw, max_mw, flags);
1797*4882a593Smuzhiyun
1798*4882a593Smuzhiyun printf("Requesting PDO %d: %u mV, %u mW%s\n",
1799*4882a593Smuzhiyun src_pdo_index, mv, mw,
1800*4882a593Smuzhiyun flags & RDO_CAP_MISMATCH ? " [mismatch]" : "");
1801*4882a593Smuzhiyun } else {
1802*4882a593Smuzhiyun *rdo = RDO_FIXED(src_pdo_index + 1, ma, max_ma, flags);
1803*4882a593Smuzhiyun
1804*4882a593Smuzhiyun printf("Requesting PDO %d: %u mV, %u mA%s\n",
1805*4882a593Smuzhiyun src_pdo_index, mv, ma,
1806*4882a593Smuzhiyun flags & RDO_CAP_MISMATCH ? " [mismatch]" : "");
1807*4882a593Smuzhiyun }
1808*4882a593Smuzhiyun
1809*4882a593Smuzhiyun port->req_current_limit = ma;
1810*4882a593Smuzhiyun port->req_supply_voltage = mv;
1811*4882a593Smuzhiyun
1812*4882a593Smuzhiyun return 0;
1813*4882a593Smuzhiyun }
1814*4882a593Smuzhiyun
tcpm_pd_send_request(struct tcpm_port * port)1815*4882a593Smuzhiyun static int tcpm_pd_send_request(struct tcpm_port *port)
1816*4882a593Smuzhiyun {
1817*4882a593Smuzhiyun struct pd_message msg;
1818*4882a593Smuzhiyun int ret;
1819*4882a593Smuzhiyun u32 rdo;
1820*4882a593Smuzhiyun
1821*4882a593Smuzhiyun ret = tcpm_pd_build_request(port, &rdo);
1822*4882a593Smuzhiyun if (ret < 0)
1823*4882a593Smuzhiyun return ret;
1824*4882a593Smuzhiyun
1825*4882a593Smuzhiyun memset(&msg, 0, sizeof(msg));
1826*4882a593Smuzhiyun msg.header = PD_HEADER_LE(PD_DATA_REQUEST,
1827*4882a593Smuzhiyun port->pwr_role,
1828*4882a593Smuzhiyun port->data_role,
1829*4882a593Smuzhiyun port->negotiated_rev,
1830*4882a593Smuzhiyun port->message_id, 1);
1831*4882a593Smuzhiyun msg.payload[0] = cpu_to_le32(rdo);
1832*4882a593Smuzhiyun
1833*4882a593Smuzhiyun return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
1834*4882a593Smuzhiyun }
1835*4882a593Smuzhiyun
tcpm_pd_build_pps_request(struct tcpm_port * port,u32 * rdo)1836*4882a593Smuzhiyun static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo)
1837*4882a593Smuzhiyun {
1838*4882a593Smuzhiyun unsigned int out_mv, op_ma, op_mw, max_mv, max_ma, flags;
1839*4882a593Smuzhiyun enum pd_pdo_type type;
1840*4882a593Smuzhiyun unsigned int src_pdo_index;
1841*4882a593Smuzhiyun u32 pdo;
1842*4882a593Smuzhiyun
1843*4882a593Smuzhiyun src_pdo_index = tcpm_pd_select_pps_apdo(port);
1844*4882a593Smuzhiyun if (!src_pdo_index)
1845*4882a593Smuzhiyun return -EOPNOTSUPP;
1846*4882a593Smuzhiyun
1847*4882a593Smuzhiyun pdo = port->source_caps[src_pdo_index];
1848*4882a593Smuzhiyun type = pdo_type(pdo);
1849*4882a593Smuzhiyun
1850*4882a593Smuzhiyun switch (type) {
1851*4882a593Smuzhiyun case PDO_TYPE_APDO:
1852*4882a593Smuzhiyun if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
1853*4882a593Smuzhiyun printf("Invalid APDO selected!\n");
1854*4882a593Smuzhiyun return -EINVAL;
1855*4882a593Smuzhiyun }
1856*4882a593Smuzhiyun max_mv = port->pps_data.req_max_volt;
1857*4882a593Smuzhiyun max_ma = port->pps_data.req_max_curr;
1858*4882a593Smuzhiyun out_mv = port->pps_data.req_out_volt;
1859*4882a593Smuzhiyun op_ma = port->pps_data.req_op_curr;
1860*4882a593Smuzhiyun break;
1861*4882a593Smuzhiyun default:
1862*4882a593Smuzhiyun printf("Invalid PDO selected!\n");
1863*4882a593Smuzhiyun return -EINVAL;
1864*4882a593Smuzhiyun }
1865*4882a593Smuzhiyun
1866*4882a593Smuzhiyun flags = RDO_USB_COMM | RDO_NO_SUSPEND;
1867*4882a593Smuzhiyun
1868*4882a593Smuzhiyun op_mw = (op_ma * out_mv) / 1000;
1869*4882a593Smuzhiyun if (op_mw < port->operating_snk_mw) {
1870*4882a593Smuzhiyun /*
1871*4882a593Smuzhiyun * Try raising current to meet power needs. If that's not enough
1872*4882a593Smuzhiyun * then try upping the voltage. If that's still not enough
1873*4882a593Smuzhiyun * then we've obviously chosen a PPS APDO which really isn't
1874*4882a593Smuzhiyun * suitable so abandon ship.
1875*4882a593Smuzhiyun */
1876*4882a593Smuzhiyun op_ma = (port->operating_snk_mw * 1000) / out_mv;
1877*4882a593Smuzhiyun if ((port->operating_snk_mw * 1000) % out_mv)
1878*4882a593Smuzhiyun ++op_ma;
1879*4882a593Smuzhiyun op_ma += RDO_PROG_CURR_MA_STEP - (op_ma % RDO_PROG_CURR_MA_STEP);
1880*4882a593Smuzhiyun
1881*4882a593Smuzhiyun if (op_ma > max_ma) {
1882*4882a593Smuzhiyun op_ma = max_ma;
1883*4882a593Smuzhiyun out_mv = (port->operating_snk_mw * 1000) / op_ma;
1884*4882a593Smuzhiyun if ((port->operating_snk_mw * 1000) % op_ma)
1885*4882a593Smuzhiyun ++out_mv;
1886*4882a593Smuzhiyun out_mv += RDO_PROG_VOLT_MV_STEP -
1887*4882a593Smuzhiyun (out_mv % RDO_PROG_VOLT_MV_STEP);
1888*4882a593Smuzhiyun
1889*4882a593Smuzhiyun if (out_mv > max_mv) {
1890*4882a593Smuzhiyun printf("Invalid PPS APDO selected!\n");
1891*4882a593Smuzhiyun return -EINVAL;
1892*4882a593Smuzhiyun }
1893*4882a593Smuzhiyun }
1894*4882a593Smuzhiyun }
1895*4882a593Smuzhiyun
1896*4882a593Smuzhiyun debug("cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d\n",
1897*4882a593Smuzhiyun port->cc_req, port->cc1, port->cc2, port->vbus_source,
1898*4882a593Smuzhiyun port->vconn_role == TYPEC_SOURCE ? "source" : "sink",
1899*4882a593Smuzhiyun port->polarity);
1900*4882a593Smuzhiyun
1901*4882a593Smuzhiyun *rdo = RDO_PROG(src_pdo_index + 1, out_mv, op_ma, flags);
1902*4882a593Smuzhiyun
1903*4882a593Smuzhiyun printf("Requesting APDO %d: %u mV, %u mA\n",
1904*4882a593Smuzhiyun src_pdo_index, out_mv, op_ma);
1905*4882a593Smuzhiyun
1906*4882a593Smuzhiyun port->pps_data.req_op_curr = op_ma;
1907*4882a593Smuzhiyun port->pps_data.req_out_volt = out_mv;
1908*4882a593Smuzhiyun
1909*4882a593Smuzhiyun return 0;
1910*4882a593Smuzhiyun }
1911*4882a593Smuzhiyun
tcpm_pd_send_pps_request(struct tcpm_port * port)1912*4882a593Smuzhiyun static int tcpm_pd_send_pps_request(struct tcpm_port *port)
1913*4882a593Smuzhiyun {
1914*4882a593Smuzhiyun struct pd_message msg;
1915*4882a593Smuzhiyun int ret;
1916*4882a593Smuzhiyun u32 rdo;
1917*4882a593Smuzhiyun
1918*4882a593Smuzhiyun ret = tcpm_pd_build_pps_request(port, &rdo);
1919*4882a593Smuzhiyun if (ret < 0)
1920*4882a593Smuzhiyun return ret;
1921*4882a593Smuzhiyun
1922*4882a593Smuzhiyun memset(&msg, 0, sizeof(msg));
1923*4882a593Smuzhiyun msg.header = PD_HEADER_LE(PD_DATA_REQUEST,
1924*4882a593Smuzhiyun port->pwr_role,
1925*4882a593Smuzhiyun port->data_role,
1926*4882a593Smuzhiyun port->negotiated_rev,
1927*4882a593Smuzhiyun port->message_id, 1);
1928*4882a593Smuzhiyun msg.payload[0] = cpu_to_le32(rdo);
1929*4882a593Smuzhiyun
1930*4882a593Smuzhiyun return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
1931*4882a593Smuzhiyun }
1932*4882a593Smuzhiyun
tcpm_set_vbus(struct tcpm_port * port,bool enable)1933*4882a593Smuzhiyun static int tcpm_set_vbus(struct tcpm_port *port, bool enable)
1934*4882a593Smuzhiyun {
1935*4882a593Smuzhiyun int ret;
1936*4882a593Smuzhiyun
1937*4882a593Smuzhiyun if (enable && port->vbus_charge)
1938*4882a593Smuzhiyun return -EINVAL;
1939*4882a593Smuzhiyun
1940*4882a593Smuzhiyun debug("vbus = %d charge = %d\n", enable, port->vbus_charge);
1941*4882a593Smuzhiyun
1942*4882a593Smuzhiyun ret = port->tcpc->set_vbus(port->tcpc, enable, port->vbus_charge);
1943*4882a593Smuzhiyun if (ret < 0)
1944*4882a593Smuzhiyun return ret;
1945*4882a593Smuzhiyun
1946*4882a593Smuzhiyun port->vbus_source = enable;
1947*4882a593Smuzhiyun return 0;
1948*4882a593Smuzhiyun }
1949*4882a593Smuzhiyun
tcpm_set_charge(struct tcpm_port * port,bool charge)1950*4882a593Smuzhiyun static int tcpm_set_charge(struct tcpm_port *port, bool charge)
1951*4882a593Smuzhiyun {
1952*4882a593Smuzhiyun int ret;
1953*4882a593Smuzhiyun
1954*4882a593Smuzhiyun if (charge && port->vbus_source)
1955*4882a593Smuzhiyun return -EINVAL;
1956*4882a593Smuzhiyun
1957*4882a593Smuzhiyun if (charge != port->vbus_charge) {
1958*4882a593Smuzhiyun debug("vbus = %d charge = %d\n", port->vbus_source, charge);
1959*4882a593Smuzhiyun ret = port->tcpc->set_vbus(port->tcpc, port->vbus_source,
1960*4882a593Smuzhiyun charge);
1961*4882a593Smuzhiyun if (ret < 0)
1962*4882a593Smuzhiyun return ret;
1963*4882a593Smuzhiyun }
1964*4882a593Smuzhiyun port->vbus_charge = charge;
1965*4882a593Smuzhiyun return 0;
1966*4882a593Smuzhiyun }
1967*4882a593Smuzhiyun
tcpm_start_toggling(struct tcpm_port * port,enum typec_cc_status cc)1968*4882a593Smuzhiyun static bool tcpm_start_toggling(struct tcpm_port *port, enum typec_cc_status cc)
1969*4882a593Smuzhiyun {
1970*4882a593Smuzhiyun int ret;
1971*4882a593Smuzhiyun
1972*4882a593Smuzhiyun if (!port->tcpc->start_toggling)
1973*4882a593Smuzhiyun return false;
1974*4882a593Smuzhiyun
1975*4882a593Smuzhiyun printf("Start toggling\n");
1976*4882a593Smuzhiyun ret = port->tcpc->start_toggling(port->tcpc, port->port_type, cc);
1977*4882a593Smuzhiyun return ret == 0;
1978*4882a593Smuzhiyun }
1979*4882a593Smuzhiyun
tcpm_init_vbus(struct tcpm_port * port)1980*4882a593Smuzhiyun static int tcpm_init_vbus(struct tcpm_port *port)
1981*4882a593Smuzhiyun {
1982*4882a593Smuzhiyun int ret;
1983*4882a593Smuzhiyun
1984*4882a593Smuzhiyun ret = port->tcpc->set_vbus(port->tcpc, false, false);
1985*4882a593Smuzhiyun port->vbus_source = false;
1986*4882a593Smuzhiyun port->vbus_charge = false;
1987*4882a593Smuzhiyun return ret;
1988*4882a593Smuzhiyun }
1989*4882a593Smuzhiyun
tcpm_init_vconn(struct tcpm_port * port)1990*4882a593Smuzhiyun static int tcpm_init_vconn(struct tcpm_port *port)
1991*4882a593Smuzhiyun {
1992*4882a593Smuzhiyun int ret;
1993*4882a593Smuzhiyun
1994*4882a593Smuzhiyun ret = port->tcpc->set_vconn(port->tcpc, false);
1995*4882a593Smuzhiyun port->vconn_role = TYPEC_SINK;
1996*4882a593Smuzhiyun return ret;
1997*4882a593Smuzhiyun }
1998*4882a593Smuzhiyun
tcpm_typec_connect(struct tcpm_port * port)1999*4882a593Smuzhiyun static void tcpm_typec_connect(struct tcpm_port *port)
2000*4882a593Smuzhiyun {
2001*4882a593Smuzhiyun if (!port->connected) {
2002*4882a593Smuzhiyun port->connected = true;
2003*4882a593Smuzhiyun }
2004*4882a593Smuzhiyun }
2005*4882a593Smuzhiyun
tcpm_src_attach(struct tcpm_port * port)2006*4882a593Smuzhiyun static int tcpm_src_attach(struct tcpm_port *port)
2007*4882a593Smuzhiyun {
2008*4882a593Smuzhiyun enum typec_cc_polarity polarity =
2009*4882a593Smuzhiyun port->cc2 == TYPEC_CC_RD ? TYPEC_POLARITY_CC2
2010*4882a593Smuzhiyun : TYPEC_POLARITY_CC1;
2011*4882a593Smuzhiyun int ret;
2012*4882a593Smuzhiyun
2013*4882a593Smuzhiyun if (port->attached)
2014*4882a593Smuzhiyun return 0;
2015*4882a593Smuzhiyun
2016*4882a593Smuzhiyun ret = tcpm_set_polarity(port, polarity);
2017*4882a593Smuzhiyun if (ret < 0)
2018*4882a593Smuzhiyun return ret;
2019*4882a593Smuzhiyun
2020*4882a593Smuzhiyun ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST);
2021*4882a593Smuzhiyun if (ret < 0)
2022*4882a593Smuzhiyun return ret;
2023*4882a593Smuzhiyun
2024*4882a593Smuzhiyun ret = port->tcpc->set_pd_rx(port->tcpc, true);
2025*4882a593Smuzhiyun if (ret < 0)
2026*4882a593Smuzhiyun goto out_disable_mux;
2027*4882a593Smuzhiyun
2028*4882a593Smuzhiyun /*
2029*4882a593Smuzhiyun * USB Type-C specification, version 1.2,
2030*4882a593Smuzhiyun * chapter 4.5.2.2.8.1 (Attached.SRC Requirements)
2031*4882a593Smuzhiyun * Enable VCONN only if the non-RD port is set to RA.
2032*4882a593Smuzhiyun */
2033*4882a593Smuzhiyun if ((polarity == TYPEC_POLARITY_CC1 && port->cc2 == TYPEC_CC_RA) ||
2034*4882a593Smuzhiyun (polarity == TYPEC_POLARITY_CC2 && port->cc1 == TYPEC_CC_RA)) {
2035*4882a593Smuzhiyun ret = tcpm_set_vconn(port, true);
2036*4882a593Smuzhiyun if (ret < 0)
2037*4882a593Smuzhiyun goto out_disable_pd;
2038*4882a593Smuzhiyun }
2039*4882a593Smuzhiyun
2040*4882a593Smuzhiyun ret = tcpm_set_vbus(port, true);
2041*4882a593Smuzhiyun if (ret < 0)
2042*4882a593Smuzhiyun goto out_disable_vconn;
2043*4882a593Smuzhiyun
2044*4882a593Smuzhiyun port->pd_capable = false;
2045*4882a593Smuzhiyun
2046*4882a593Smuzhiyun port->partner = NULL;
2047*4882a593Smuzhiyun
2048*4882a593Smuzhiyun port->attached = true;
2049*4882a593Smuzhiyun port->debouncing = false;
2050*4882a593Smuzhiyun //port->send_discover = true;
2051*4882a593Smuzhiyun
2052*4882a593Smuzhiyun return 0;
2053*4882a593Smuzhiyun
2054*4882a593Smuzhiyun out_disable_vconn:
2055*4882a593Smuzhiyun tcpm_set_vconn(port, false);
2056*4882a593Smuzhiyun out_disable_pd:
2057*4882a593Smuzhiyun port->tcpc->set_pd_rx(port->tcpc, false);
2058*4882a593Smuzhiyun out_disable_mux:
2059*4882a593Smuzhiyun printf("CC connected in %s as DFP\n",
2060*4882a593Smuzhiyun polarity ? "CC2" : "CC1");
2061*4882a593Smuzhiyun return 0;
2062*4882a593Smuzhiyun }
2063*4882a593Smuzhiyun
tcpm_typec_disconnect(struct tcpm_port * port)2064*4882a593Smuzhiyun static void tcpm_typec_disconnect(struct tcpm_port *port)
2065*4882a593Smuzhiyun {
2066*4882a593Smuzhiyun if (port->connected) {
2067*4882a593Smuzhiyun port->partner = NULL;
2068*4882a593Smuzhiyun port->connected = false;
2069*4882a593Smuzhiyun }
2070*4882a593Smuzhiyun }
2071*4882a593Smuzhiyun
tcpm_reset_port(struct tcpm_port * port)2072*4882a593Smuzhiyun static void tcpm_reset_port(struct tcpm_port *port)
2073*4882a593Smuzhiyun {
2074*4882a593Smuzhiyun tcpm_timer_uninit(port);
2075*4882a593Smuzhiyun tcpm_typec_disconnect(port);
2076*4882a593Smuzhiyun port->poll_event_cnt = 0;
2077*4882a593Smuzhiyun port->wait_dr_swap_Message = false;
2078*4882a593Smuzhiyun port->attached = false;
2079*4882a593Smuzhiyun port->pd_capable = false;
2080*4882a593Smuzhiyun port->pps_data.supported = false;
2081*4882a593Smuzhiyun
2082*4882a593Smuzhiyun /*
2083*4882a593Smuzhiyun * First Rx ID should be 0; set this to a sentinel of -1 so that
2084*4882a593Smuzhiyun * we can check tcpm_pd_rx_handler() if we had seen it before.
2085*4882a593Smuzhiyun */
2086*4882a593Smuzhiyun port->rx_msgid = -1;
2087*4882a593Smuzhiyun
2088*4882a593Smuzhiyun port->tcpc->set_pd_rx(port->tcpc, false);
2089*4882a593Smuzhiyun tcpm_init_vbus(port); /* also disables charging */
2090*4882a593Smuzhiyun tcpm_init_vconn(port);
2091*4882a593Smuzhiyun tcpm_set_current_limit(port, 0, 0);
2092*4882a593Smuzhiyun tcpm_set_polarity(port, TYPEC_POLARITY_CC1);
2093*4882a593Smuzhiyun tcpm_set_attached_state(port, false);
2094*4882a593Smuzhiyun port->usb_type = POWER_SUPPLY_USB_TYPE_C;
2095*4882a593Smuzhiyun port->nr_sink_caps = 0;
2096*4882a593Smuzhiyun port->sink_cap_done = false;
2097*4882a593Smuzhiyun }
2098*4882a593Smuzhiyun
tcpm_detach(struct tcpm_port * port)2099*4882a593Smuzhiyun static void tcpm_detach(struct tcpm_port *port)
2100*4882a593Smuzhiyun {
2101*4882a593Smuzhiyun if (tcpm_port_is_disconnected(port))
2102*4882a593Smuzhiyun port->hard_reset_count = 0;
2103*4882a593Smuzhiyun
2104*4882a593Smuzhiyun if (!port->attached)
2105*4882a593Smuzhiyun return;
2106*4882a593Smuzhiyun
2107*4882a593Smuzhiyun tcpm_reset_port(port);
2108*4882a593Smuzhiyun }
2109*4882a593Smuzhiyun
tcpm_src_detach(struct tcpm_port * port)2110*4882a593Smuzhiyun static void tcpm_src_detach(struct tcpm_port *port)
2111*4882a593Smuzhiyun {
2112*4882a593Smuzhiyun tcpm_detach(port);
2113*4882a593Smuzhiyun }
2114*4882a593Smuzhiyun
tcpm_snk_attach(struct tcpm_port * port)2115*4882a593Smuzhiyun static int tcpm_snk_attach(struct tcpm_port *port)
2116*4882a593Smuzhiyun {
2117*4882a593Smuzhiyun int ret;
2118*4882a593Smuzhiyun
2119*4882a593Smuzhiyun if (port->attached)
2120*4882a593Smuzhiyun return 0;
2121*4882a593Smuzhiyun
2122*4882a593Smuzhiyun ret = tcpm_set_polarity(port, port->cc2 != TYPEC_CC_OPEN ?
2123*4882a593Smuzhiyun TYPEC_POLARITY_CC2 : TYPEC_POLARITY_CC1);
2124*4882a593Smuzhiyun if (ret < 0)
2125*4882a593Smuzhiyun return ret;
2126*4882a593Smuzhiyun
2127*4882a593Smuzhiyun ret = tcpm_set_roles(port, true, TYPEC_SINK, TYPEC_DEVICE);
2128*4882a593Smuzhiyun if (ret < 0)
2129*4882a593Smuzhiyun return ret;
2130*4882a593Smuzhiyun
2131*4882a593Smuzhiyun port->pd_capable = false;
2132*4882a593Smuzhiyun
2133*4882a593Smuzhiyun port->partner = NULL;
2134*4882a593Smuzhiyun
2135*4882a593Smuzhiyun port->attached = true;
2136*4882a593Smuzhiyun port->debouncing = false;
2137*4882a593Smuzhiyun printf("CC connected in %s as UFP\n",
2138*4882a593Smuzhiyun port->cc1 != TYPEC_CC_OPEN ? "CC1" : "CC2");
2139*4882a593Smuzhiyun
2140*4882a593Smuzhiyun return 0;
2141*4882a593Smuzhiyun }
2142*4882a593Smuzhiyun
tcpm_snk_detach(struct tcpm_port * port)2143*4882a593Smuzhiyun static void tcpm_snk_detach(struct tcpm_port *port)
2144*4882a593Smuzhiyun {
2145*4882a593Smuzhiyun tcpm_detach(port);
2146*4882a593Smuzhiyun }
2147*4882a593Smuzhiyun
tcpm_acc_attach(struct tcpm_port * port)2148*4882a593Smuzhiyun static int tcpm_acc_attach(struct tcpm_port *port)
2149*4882a593Smuzhiyun {
2150*4882a593Smuzhiyun int ret;
2151*4882a593Smuzhiyun
2152*4882a593Smuzhiyun if (port->attached)
2153*4882a593Smuzhiyun return 0;
2154*4882a593Smuzhiyun
2155*4882a593Smuzhiyun ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST);
2156*4882a593Smuzhiyun if (ret < 0)
2157*4882a593Smuzhiyun return ret;
2158*4882a593Smuzhiyun
2159*4882a593Smuzhiyun port->partner = NULL;
2160*4882a593Smuzhiyun
2161*4882a593Smuzhiyun tcpm_typec_connect(port);
2162*4882a593Smuzhiyun
2163*4882a593Smuzhiyun port->attached = true;
2164*4882a593Smuzhiyun
2165*4882a593Smuzhiyun dev_info(port->dev, "CC connected as Audio Accessory\n");
2166*4882a593Smuzhiyun
2167*4882a593Smuzhiyun return 0;
2168*4882a593Smuzhiyun }
2169*4882a593Smuzhiyun
tcpm_acc_detach(struct tcpm_port * port)2170*4882a593Smuzhiyun static void tcpm_acc_detach(struct tcpm_port *port)
2171*4882a593Smuzhiyun {
2172*4882a593Smuzhiyun tcpm_detach(port);
2173*4882a593Smuzhiyun }
2174*4882a593Smuzhiyun
hard_reset_state(struct tcpm_port * port)2175*4882a593Smuzhiyun static inline enum tcpm_state hard_reset_state(struct tcpm_port *port)
2176*4882a593Smuzhiyun {
2177*4882a593Smuzhiyun if (port->hard_reset_count < PD_N_HARD_RESET_COUNT)
2178*4882a593Smuzhiyun return HARD_RESET_SEND;
2179*4882a593Smuzhiyun if (port->pd_capable)
2180*4882a593Smuzhiyun return ERROR_RECOVERY;
2181*4882a593Smuzhiyun if (port->pwr_role == TYPEC_SOURCE)
2182*4882a593Smuzhiyun return SRC_UNATTACHED;
2183*4882a593Smuzhiyun if (port->state == SNK_WAIT_CAPABILITIES)
2184*4882a593Smuzhiyun return SNK_READY;
2185*4882a593Smuzhiyun return SNK_UNATTACHED;
2186*4882a593Smuzhiyun }
2187*4882a593Smuzhiyun
unattached_state(struct tcpm_port * port)2188*4882a593Smuzhiyun static inline enum tcpm_state unattached_state(struct tcpm_port *port)
2189*4882a593Smuzhiyun {
2190*4882a593Smuzhiyun if (port->port_type == TYPEC_PORT_DRP) {
2191*4882a593Smuzhiyun if (port->pwr_role == TYPEC_SOURCE)
2192*4882a593Smuzhiyun return SRC_UNATTACHED;
2193*4882a593Smuzhiyun else
2194*4882a593Smuzhiyun return SNK_UNATTACHED;
2195*4882a593Smuzhiyun } else if (port->port_type == TYPEC_PORT_SRC) {
2196*4882a593Smuzhiyun return SRC_UNATTACHED;
2197*4882a593Smuzhiyun }
2198*4882a593Smuzhiyun
2199*4882a593Smuzhiyun return SNK_UNATTACHED;
2200*4882a593Smuzhiyun }
2201*4882a593Smuzhiyun
tcpm_is_toggling(struct tcpm_port * port)2202*4882a593Smuzhiyun bool tcpm_is_toggling(struct tcpm_port *port)
2203*4882a593Smuzhiyun {
2204*4882a593Smuzhiyun if (port->port_type == TYPEC_PORT_DRP)
2205*4882a593Smuzhiyun return port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED ||
2206*4882a593Smuzhiyun port->state == TOGGLING;
2207*4882a593Smuzhiyun
2208*4882a593Smuzhiyun return false;
2209*4882a593Smuzhiyun }
2210*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_is_toggling);
2211*4882a593Smuzhiyun
run_state_machine(struct tcpm_port * port)2212*4882a593Smuzhiyun static void run_state_machine(struct tcpm_port *port)
2213*4882a593Smuzhiyun {
2214*4882a593Smuzhiyun int ret;
2215*4882a593Smuzhiyun
2216*4882a593Smuzhiyun port->enter_state = port->state;
2217*4882a593Smuzhiyun switch (port->state) {
2218*4882a593Smuzhiyun case TOGGLING:
2219*4882a593Smuzhiyun break;
2220*4882a593Smuzhiyun /* SRC states */
2221*4882a593Smuzhiyun case SRC_UNATTACHED:
2222*4882a593Smuzhiyun tcpm_src_detach(port);
2223*4882a593Smuzhiyun if (tcpm_start_toggling(port, tcpm_rp_cc(port))) {
2224*4882a593Smuzhiyun tcpm_set_state(port, TOGGLING, 0);
2225*4882a593Smuzhiyun break;
2226*4882a593Smuzhiyun }
2227*4882a593Smuzhiyun tcpm_set_cc(port, tcpm_rp_cc(port));
2228*4882a593Smuzhiyun if (port->port_type == TYPEC_PORT_DRP)
2229*4882a593Smuzhiyun tcpm_set_state(port, SNK_UNATTACHED, PD_T_DRP_SNK);
2230*4882a593Smuzhiyun break;
2231*4882a593Smuzhiyun case SRC_ATTACH_WAIT:
2232*4882a593Smuzhiyun if (tcpm_port_is_debug(port))
2233*4882a593Smuzhiyun tcpm_set_state(port, DEBUG_ACC_ATTACHED,
2234*4882a593Smuzhiyun PD_T_CC_DEBOUNCE);
2235*4882a593Smuzhiyun else if (tcpm_port_is_audio(port))
2236*4882a593Smuzhiyun tcpm_set_state(port, AUDIO_ACC_ATTACHED,
2237*4882a593Smuzhiyun PD_T_CC_DEBOUNCE);
2238*4882a593Smuzhiyun else if (tcpm_port_is_source(port))
2239*4882a593Smuzhiyun tcpm_set_state(port, SRC_ATTACHED, PD_T_CC_DEBOUNCE);
2240*4882a593Smuzhiyun break;
2241*4882a593Smuzhiyun
2242*4882a593Smuzhiyun case SRC_ATTACHED:
2243*4882a593Smuzhiyun ret = tcpm_src_attach(port);
2244*4882a593Smuzhiyun /*
2245*4882a593Smuzhiyun * Currently, vbus control is not implemented,
2246*4882a593Smuzhiyun * and the SRC detection process cannot be fully implemented.
2247*4882a593Smuzhiyun */
2248*4882a593Smuzhiyun tcpm_set_state(port, SRC_READY, 0);
2249*4882a593Smuzhiyun #if 0
2250*4882a593Smuzhiyun tcpm_set_state(port, SRC_UNATTACHED,
2251*4882a593Smuzhiyun ret < 0 ? 0 : PD_T_PS_SOURCE_ON);
2252*4882a593Smuzhiyun #endif
2253*4882a593Smuzhiyun break;
2254*4882a593Smuzhiyun case SRC_STARTUP:
2255*4882a593Smuzhiyun port->caps_count = 0;
2256*4882a593Smuzhiyun port->negotiated_rev = PD_MAX_REV;
2257*4882a593Smuzhiyun port->message_id = 0;
2258*4882a593Smuzhiyun port->rx_msgid = -1;
2259*4882a593Smuzhiyun port->explicit_contract = false;
2260*4882a593Smuzhiyun tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0);
2261*4882a593Smuzhiyun break;
2262*4882a593Smuzhiyun case SRC_SEND_CAPABILITIES:
2263*4882a593Smuzhiyun port->caps_count++;
2264*4882a593Smuzhiyun if (port->caps_count > PD_N_CAPS_COUNT) {
2265*4882a593Smuzhiyun tcpm_set_state(port, SRC_READY, 0);
2266*4882a593Smuzhiyun break;
2267*4882a593Smuzhiyun }
2268*4882a593Smuzhiyun ret = tcpm_pd_send_source_caps(port);
2269*4882a593Smuzhiyun if (ret < 0) {
2270*4882a593Smuzhiyun tcpm_set_state(port, SRC_SEND_CAPABILITIES,
2271*4882a593Smuzhiyun PD_T_SEND_SOURCE_CAP);
2272*4882a593Smuzhiyun } else {
2273*4882a593Smuzhiyun /*
2274*4882a593Smuzhiyun * Per standard, we should clear the reset counter here.
2275*4882a593Smuzhiyun * However, that can result in state machine hang-ups.
2276*4882a593Smuzhiyun * Reset it only in READY state to improve stability.
2277*4882a593Smuzhiyun */
2278*4882a593Smuzhiyun /* port->hard_reset_count = 0; */
2279*4882a593Smuzhiyun port->caps_count = 0;
2280*4882a593Smuzhiyun port->pd_capable = true;
2281*4882a593Smuzhiyun tcpm_set_state_cond(port, SRC_SEND_CAPABILITIES_TIMEOUT,
2282*4882a593Smuzhiyun PD_T_SEND_SOURCE_CAP);
2283*4882a593Smuzhiyun }
2284*4882a593Smuzhiyun break;
2285*4882a593Smuzhiyun case SRC_SEND_CAPABILITIES_TIMEOUT:
2286*4882a593Smuzhiyun /*
2287*4882a593Smuzhiyun * Error recovery for a PD_DATA_SOURCE_CAP reply timeout.
2288*4882a593Smuzhiyun *
2289*4882a593Smuzhiyun * PD 2.0 sinks are supposed to accept src-capabilities with a
2290*4882a593Smuzhiyun * 3.0 header and simply ignore any src PDOs which the sink does
2291*4882a593Smuzhiyun * not understand such as PPS but some 2.0 sinks instead ignore
2292*4882a593Smuzhiyun * the entire PD_DATA_SOURCE_CAP message, causing contract
2293*4882a593Smuzhiyun * negotiation to fail.
2294*4882a593Smuzhiyun *
2295*4882a593Smuzhiyun * After PD_N_HARD_RESET_COUNT hard-reset attempts, we try
2296*4882a593Smuzhiyun * sending src-capabilities with a lower PD revision to
2297*4882a593Smuzhiyun * make these broken sinks work.
2298*4882a593Smuzhiyun */
2299*4882a593Smuzhiyun if (port->hard_reset_count < PD_N_HARD_RESET_COUNT) {
2300*4882a593Smuzhiyun tcpm_set_state(port, HARD_RESET_SEND, 0);
2301*4882a593Smuzhiyun } else if (port->negotiated_rev > PD_REV20) {
2302*4882a593Smuzhiyun port->negotiated_rev--;
2303*4882a593Smuzhiyun port->hard_reset_count = 0;
2304*4882a593Smuzhiyun tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0);
2305*4882a593Smuzhiyun } else {
2306*4882a593Smuzhiyun tcpm_set_state(port, hard_reset_state(port), 0);
2307*4882a593Smuzhiyun }
2308*4882a593Smuzhiyun break;
2309*4882a593Smuzhiyun case SRC_NEGOTIATE_CAPABILITIES:
2310*4882a593Smuzhiyun ret = tcpm_pd_check_request(port);
2311*4882a593Smuzhiyun if (ret < 0) {
2312*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_REJECT);
2313*4882a593Smuzhiyun if (!port->explicit_contract) {
2314*4882a593Smuzhiyun tcpm_set_state(port,
2315*4882a593Smuzhiyun SRC_WAIT_NEW_CAPABILITIES, 0);
2316*4882a593Smuzhiyun } else {
2317*4882a593Smuzhiyun tcpm_set_state(port, SRC_READY, 0);
2318*4882a593Smuzhiyun }
2319*4882a593Smuzhiyun } else {
2320*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
2321*4882a593Smuzhiyun tcpm_set_state(port, SRC_TRANSITION_SUPPLY,
2322*4882a593Smuzhiyun PD_T_SRC_TRANSITION);
2323*4882a593Smuzhiyun }
2324*4882a593Smuzhiyun break;
2325*4882a593Smuzhiyun case SRC_TRANSITION_SUPPLY:
2326*4882a593Smuzhiyun /* XXX: regulator_set_voltage(vbus, ...) */
2327*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_PS_RDY);
2328*4882a593Smuzhiyun port->explicit_contract = true;
2329*4882a593Smuzhiyun tcpm_set_state_cond(port, SRC_READY, 0);
2330*4882a593Smuzhiyun break;
2331*4882a593Smuzhiyun case SRC_READY:
2332*4882a593Smuzhiyun #if 1
2333*4882a593Smuzhiyun port->hard_reset_count = 0;
2334*4882a593Smuzhiyun #endif
2335*4882a593Smuzhiyun port->try_src_count = 0;
2336*4882a593Smuzhiyun
2337*4882a593Smuzhiyun tcpm_typec_connect(port);
2338*4882a593Smuzhiyun break;
2339*4882a593Smuzhiyun case SRC_WAIT_NEW_CAPABILITIES:
2340*4882a593Smuzhiyun /* Nothing to do... */
2341*4882a593Smuzhiyun break;
2342*4882a593Smuzhiyun
2343*4882a593Smuzhiyun /* SNK states */
2344*4882a593Smuzhiyun case SNK_UNATTACHED:
2345*4882a593Smuzhiyun tcpm_snk_detach(port);
2346*4882a593Smuzhiyun if (tcpm_start_toggling(port, TYPEC_CC_RD)) {
2347*4882a593Smuzhiyun tcpm_set_state(port, TOGGLING, 0);
2348*4882a593Smuzhiyun break;
2349*4882a593Smuzhiyun }
2350*4882a593Smuzhiyun tcpm_set_cc(port, TYPEC_CC_RD);
2351*4882a593Smuzhiyun if (port->port_type == TYPEC_PORT_DRP)
2352*4882a593Smuzhiyun tcpm_set_state(port, SRC_UNATTACHED, PD_T_DRP_SRC);
2353*4882a593Smuzhiyun break;
2354*4882a593Smuzhiyun case SNK_ATTACH_WAIT:
2355*4882a593Smuzhiyun if ((port->cc1 == TYPEC_CC_OPEN &&
2356*4882a593Smuzhiyun port->cc2 != TYPEC_CC_OPEN) ||
2357*4882a593Smuzhiyun (port->cc1 != TYPEC_CC_OPEN &&
2358*4882a593Smuzhiyun port->cc2 == TYPEC_CC_OPEN))
2359*4882a593Smuzhiyun tcpm_set_state(port, SNK_DEBOUNCED,
2360*4882a593Smuzhiyun PD_T_CC_DEBOUNCE);
2361*4882a593Smuzhiyun else if (tcpm_port_is_disconnected(port))
2362*4882a593Smuzhiyun tcpm_set_state(port, SNK_UNATTACHED,
2363*4882a593Smuzhiyun PD_T_CC_DEBOUNCE);
2364*4882a593Smuzhiyun break;
2365*4882a593Smuzhiyun case SNK_DEBOUNCED:
2366*4882a593Smuzhiyun if (tcpm_port_is_disconnected(port)) {
2367*4882a593Smuzhiyun tcpm_set_state(port, SNK_UNATTACHED,
2368*4882a593Smuzhiyun PD_T_PD_DEBOUNCE);
2369*4882a593Smuzhiyun } else if (port->vbus_present)
2370*4882a593Smuzhiyun tcpm_set_state(port, SNK_ATTACHED, 0);
2371*4882a593Smuzhiyun else
2372*4882a593Smuzhiyun /* Wait for VBUS, but not forever */
2373*4882a593Smuzhiyun tcpm_set_state(port, PORT_RESET, PD_T_PS_SOURCE_ON);
2374*4882a593Smuzhiyun break;
2375*4882a593Smuzhiyun
2376*4882a593Smuzhiyun case SNK_ATTACHED:
2377*4882a593Smuzhiyun ret = tcpm_snk_attach(port);
2378*4882a593Smuzhiyun if (ret < 0)
2379*4882a593Smuzhiyun tcpm_set_state(port, SNK_UNATTACHED, 0);
2380*4882a593Smuzhiyun else
2381*4882a593Smuzhiyun tcpm_set_state(port, SNK_STARTUP, 0);
2382*4882a593Smuzhiyun break;
2383*4882a593Smuzhiyun case SNK_STARTUP:
2384*4882a593Smuzhiyun port->negotiated_rev = PD_MAX_REV;
2385*4882a593Smuzhiyun port->message_id = 0;
2386*4882a593Smuzhiyun port->rx_msgid = -1;
2387*4882a593Smuzhiyun port->explicit_contract = false;
2388*4882a593Smuzhiyun tcpm_set_state(port, SNK_DISCOVERY, 0);
2389*4882a593Smuzhiyun break;
2390*4882a593Smuzhiyun case SNK_DISCOVERY:
2391*4882a593Smuzhiyun if (port->vbus_present) {
2392*4882a593Smuzhiyun tcpm_set_current_limit(port,
2393*4882a593Smuzhiyun tcpm_get_current_limit(port),
2394*4882a593Smuzhiyun 5000);
2395*4882a593Smuzhiyun tcpm_set_charge(port, true);
2396*4882a593Smuzhiyun tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
2397*4882a593Smuzhiyun break;
2398*4882a593Smuzhiyun }
2399*4882a593Smuzhiyun /*
2400*4882a593Smuzhiyun * For DRP, timeouts differ. Also, handling is supposed to be
2401*4882a593Smuzhiyun * different and much more complex (dead battery detection;
2402*4882a593Smuzhiyun * see USB power delivery specification, section 8.3.3.6.1.5.1).
2403*4882a593Smuzhiyun */
2404*4882a593Smuzhiyun tcpm_set_state(port, hard_reset_state(port),
2405*4882a593Smuzhiyun port->port_type == TYPEC_PORT_DRP ?
2406*4882a593Smuzhiyun PD_T_DB_DETECT : PD_T_NO_RESPONSE);
2407*4882a593Smuzhiyun break;
2408*4882a593Smuzhiyun case SNK_DISCOVERY_DEBOUNCE:
2409*4882a593Smuzhiyun tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE_DONE,
2410*4882a593Smuzhiyun PD_T_CC_DEBOUNCE);
2411*4882a593Smuzhiyun break;
2412*4882a593Smuzhiyun case SNK_DISCOVERY_DEBOUNCE_DONE:
2413*4882a593Smuzhiyun #if 0
2414*4882a593Smuzhiyun if (!tcpm_port_is_disconnected(port) &&
2415*4882a593Smuzhiyun tcpm_port_is_sink(port) &&
2416*4882a593Smuzhiyun ktime_after(port->delayed_runtime, ktime_get())) {
2417*4882a593Smuzhiyun tcpm_set_state(port, SNK_DISCOVERY,
2418*4882a593Smuzhiyun ktime_to_ms(ktime_sub(port->delayed_runtime, ktime_get())));
2419*4882a593Smuzhiyun break;
2420*4882a593Smuzhiyun }
2421*4882a593Smuzhiyun #endif
2422*4882a593Smuzhiyun tcpm_set_state(port, unattached_state(port), 0);
2423*4882a593Smuzhiyun break;
2424*4882a593Smuzhiyun case SNK_WAIT_CAPABILITIES:
2425*4882a593Smuzhiyun ret = port->tcpc->set_pd_rx(port->tcpc, true);
2426*4882a593Smuzhiyun if (ret < 0) {
2427*4882a593Smuzhiyun tcpm_set_state(port, SNK_READY, 0);
2428*4882a593Smuzhiyun break;
2429*4882a593Smuzhiyun }
2430*4882a593Smuzhiyun /*
2431*4882a593Smuzhiyun * If VBUS has never been low, and we time out waiting
2432*4882a593Smuzhiyun * for source cap, try a soft reset first, in case we
2433*4882a593Smuzhiyun * were already in a stable contract before this boot.
2434*4882a593Smuzhiyun * Do this only once.
2435*4882a593Smuzhiyun */
2436*4882a593Smuzhiyun if (port->vbus_never_low) {
2437*4882a593Smuzhiyun port->vbus_never_low = false;
2438*4882a593Smuzhiyun tcpm_set_state(port, SOFT_RESET_SEND,
2439*4882a593Smuzhiyun PD_T_SINK_WAIT_CAP);
2440*4882a593Smuzhiyun } else {
2441*4882a593Smuzhiyun tcpm_set_state(port, hard_reset_state(port),
2442*4882a593Smuzhiyun PD_T_SINK_WAIT_CAP);
2443*4882a593Smuzhiyun }
2444*4882a593Smuzhiyun break;
2445*4882a593Smuzhiyun case SNK_NEGOTIATE_CAPABILITIES:
2446*4882a593Smuzhiyun port->pd_capable = true;
2447*4882a593Smuzhiyun port->hard_reset_count = 0;
2448*4882a593Smuzhiyun ret = tcpm_pd_send_request(port);
2449*4882a593Smuzhiyun if (ret < 0) {
2450*4882a593Smuzhiyun /* Let the Source send capabilities again. */
2451*4882a593Smuzhiyun tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
2452*4882a593Smuzhiyun } else {
2453*4882a593Smuzhiyun tcpm_set_state_cond(port, hard_reset_state(port),
2454*4882a593Smuzhiyun PD_T_SENDER_RESPONSE);
2455*4882a593Smuzhiyun }
2456*4882a593Smuzhiyun break;
2457*4882a593Smuzhiyun case SNK_NEGOTIATE_PPS_CAPABILITIES:
2458*4882a593Smuzhiyun ret = tcpm_pd_send_pps_request(port);
2459*4882a593Smuzhiyun if (ret < 0) {
2460*4882a593Smuzhiyun port->pps_status = ret;
2461*4882a593Smuzhiyun /*
2462*4882a593Smuzhiyun * If this was called due to updates to sink
2463*4882a593Smuzhiyun * capabilities, and pps is no longer valid, we should
2464*4882a593Smuzhiyun * safely fall back to a standard PDO.
2465*4882a593Smuzhiyun */
2466*4882a593Smuzhiyun if (port->update_sink_caps)
2467*4882a593Smuzhiyun tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0);
2468*4882a593Smuzhiyun else
2469*4882a593Smuzhiyun tcpm_set_state(port, SNK_READY, 0);
2470*4882a593Smuzhiyun } else {
2471*4882a593Smuzhiyun tcpm_set_state_cond(port, hard_reset_state(port),
2472*4882a593Smuzhiyun PD_T_SENDER_RESPONSE);
2473*4882a593Smuzhiyun }
2474*4882a593Smuzhiyun break;
2475*4882a593Smuzhiyun case SNK_TRANSITION_SINK:
2476*4882a593Smuzhiyun case SNK_TRANSITION_SINK_VBUS:
2477*4882a593Smuzhiyun tcpm_set_state(port, hard_reset_state(port),
2478*4882a593Smuzhiyun PD_T_PS_TRANSITION);
2479*4882a593Smuzhiyun break;
2480*4882a593Smuzhiyun case SNK_READY:
2481*4882a593Smuzhiyun port->try_snk_count = 0;
2482*4882a593Smuzhiyun port->update_sink_caps = false;
2483*4882a593Smuzhiyun tcpm_typec_connect(port);
2484*4882a593Smuzhiyun /*
2485*4882a593Smuzhiyun * Here poll_event_cnt is cleared, waiting for self-powered Type-C devices
2486*4882a593Smuzhiyun * to send DR_swap Messge until 1s (TCPM_POLL_EVENT_TIME_OUT * 500us)timeout
2487*4882a593Smuzhiyun */
2488*4882a593Smuzhiyun if (port->wait_dr_swap_Message)
2489*4882a593Smuzhiyun port->poll_event_cnt = 0;
2490*4882a593Smuzhiyun
2491*4882a593Smuzhiyun break;
2492*4882a593Smuzhiyun
2493*4882a593Smuzhiyun /* Accessory states */
2494*4882a593Smuzhiyun case ACC_UNATTACHED:
2495*4882a593Smuzhiyun tcpm_acc_detach(port);
2496*4882a593Smuzhiyun tcpm_set_state(port, SRC_UNATTACHED, 0);
2497*4882a593Smuzhiyun break;
2498*4882a593Smuzhiyun case DEBUG_ACC_ATTACHED:
2499*4882a593Smuzhiyun case AUDIO_ACC_ATTACHED:
2500*4882a593Smuzhiyun ret = tcpm_acc_attach(port);
2501*4882a593Smuzhiyun if (ret < 0)
2502*4882a593Smuzhiyun tcpm_set_state(port, ACC_UNATTACHED, 0);
2503*4882a593Smuzhiyun break;
2504*4882a593Smuzhiyun case AUDIO_ACC_DEBOUNCE:
2505*4882a593Smuzhiyun tcpm_set_state(port, ACC_UNATTACHED, PD_T_CC_DEBOUNCE);
2506*4882a593Smuzhiyun break;
2507*4882a593Smuzhiyun
2508*4882a593Smuzhiyun /* Hard_Reset states */
2509*4882a593Smuzhiyun case HARD_RESET_SEND:
2510*4882a593Smuzhiyun tcpm_pd_transmit(port, TCPC_TX_HARD_RESET, NULL);
2511*4882a593Smuzhiyun tcpm_set_state(port, HARD_RESET_START, 0);
2512*4882a593Smuzhiyun port->wait_dr_swap_Message = false;
2513*4882a593Smuzhiyun break;
2514*4882a593Smuzhiyun case HARD_RESET_START:
2515*4882a593Smuzhiyun port->hard_reset_count++;
2516*4882a593Smuzhiyun port->tcpc->set_pd_rx(port->tcpc, false);
2517*4882a593Smuzhiyun port->nr_sink_caps = 0;
2518*4882a593Smuzhiyun port->send_discover = true;
2519*4882a593Smuzhiyun if (port->pwr_role == TYPEC_SOURCE)
2520*4882a593Smuzhiyun tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF,
2521*4882a593Smuzhiyun PD_T_PS_HARD_RESET);
2522*4882a593Smuzhiyun else
2523*4882a593Smuzhiyun tcpm_set_state(port, SNK_HARD_RESET_SINK_OFF, 0);
2524*4882a593Smuzhiyun break;
2525*4882a593Smuzhiyun case SRC_HARD_RESET_VBUS_OFF:
2526*4882a593Smuzhiyun tcpm_set_vconn(port, true);
2527*4882a593Smuzhiyun tcpm_set_vbus(port, false);
2528*4882a593Smuzhiyun tcpm_set_roles(port, port->self_powered, TYPEC_SOURCE,
2529*4882a593Smuzhiyun TYPEC_HOST);
2530*4882a593Smuzhiyun tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER);
2531*4882a593Smuzhiyun break;
2532*4882a593Smuzhiyun case SRC_HARD_RESET_VBUS_ON:
2533*4882a593Smuzhiyun tcpm_set_vconn(port, true);
2534*4882a593Smuzhiyun tcpm_set_vbus(port, true);
2535*4882a593Smuzhiyun port->tcpc->set_pd_rx(port->tcpc, true);
2536*4882a593Smuzhiyun tcpm_set_attached_state(port, true);
2537*4882a593Smuzhiyun tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON);
2538*4882a593Smuzhiyun break;
2539*4882a593Smuzhiyun case SNK_HARD_RESET_SINK_OFF:
2540*4882a593Smuzhiyun memset(&port->pps_data, 0, sizeof(port->pps_data));
2541*4882a593Smuzhiyun tcpm_set_vconn(port, false);
2542*4882a593Smuzhiyun if (port->pd_capable)
2543*4882a593Smuzhiyun tcpm_set_charge(port, false);
2544*4882a593Smuzhiyun tcpm_set_roles(port, port->self_powered, TYPEC_SINK,
2545*4882a593Smuzhiyun TYPEC_DEVICE);
2546*4882a593Smuzhiyun /*
2547*4882a593Smuzhiyun * VBUS may or may not toggle, depending on the adapter.
2548*4882a593Smuzhiyun * If it doesn't toggle, transition to SNK_HARD_RESET_SINK_ON
2549*4882a593Smuzhiyun * directly after timeout.
2550*4882a593Smuzhiyun */
2551*4882a593Smuzhiyun tcpm_set_state(port, SNK_HARD_RESET_SINK_ON, PD_T_SAFE_0V);
2552*4882a593Smuzhiyun break;
2553*4882a593Smuzhiyun case SNK_HARD_RESET_WAIT_VBUS:
2554*4882a593Smuzhiyun /* Assume we're disconnected if VBUS doesn't come back. */
2555*4882a593Smuzhiyun tcpm_set_state(port, SNK_UNATTACHED,
2556*4882a593Smuzhiyun PD_T_SRC_RECOVER_MAX + PD_T_SRC_TURN_ON);
2557*4882a593Smuzhiyun break;
2558*4882a593Smuzhiyun case SNK_HARD_RESET_SINK_ON:
2559*4882a593Smuzhiyun /* Note: There is no guarantee that VBUS is on in this state */
2560*4882a593Smuzhiyun /*
2561*4882a593Smuzhiyun * XXX:
2562*4882a593Smuzhiyun * The specification suggests that dual mode ports in sink
2563*4882a593Smuzhiyun * mode should transition to state PE_SRC_Transition_to_default.
2564*4882a593Smuzhiyun * See USB power delivery specification chapter 8.3.3.6.1.3.
2565*4882a593Smuzhiyun * This would mean to to
2566*4882a593Smuzhiyun * - turn off VCONN, reset power supply
2567*4882a593Smuzhiyun * - request hardware reset
2568*4882a593Smuzhiyun * - turn on VCONN
2569*4882a593Smuzhiyun * - Transition to state PE_Src_Startup
2570*4882a593Smuzhiyun * SNK only ports shall transition to state Snk_Startup
2571*4882a593Smuzhiyun * (see chapter 8.3.3.3.8).
2572*4882a593Smuzhiyun * Similar, dual-mode ports in source mode should transition
2573*4882a593Smuzhiyun * to PE_SNK_Transition_to_default.
2574*4882a593Smuzhiyun */
2575*4882a593Smuzhiyun if (port->pd_capable) {
2576*4882a593Smuzhiyun tcpm_set_current_limit(port,
2577*4882a593Smuzhiyun tcpm_get_current_limit(port),
2578*4882a593Smuzhiyun 5000);
2579*4882a593Smuzhiyun tcpm_set_charge(port, true);
2580*4882a593Smuzhiyun }
2581*4882a593Smuzhiyun tcpm_set_attached_state(port, true);
2582*4882a593Smuzhiyun tcpm_set_state(port, SNK_STARTUP, 0);
2583*4882a593Smuzhiyun break;
2584*4882a593Smuzhiyun
2585*4882a593Smuzhiyun /* Soft_Reset states */
2586*4882a593Smuzhiyun case SOFT_RESET:
2587*4882a593Smuzhiyun port->message_id = 0;
2588*4882a593Smuzhiyun port->rx_msgid = -1;
2589*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
2590*4882a593Smuzhiyun if (port->pwr_role == TYPEC_SOURCE) {
2591*4882a593Smuzhiyun tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0);
2592*4882a593Smuzhiyun } else {
2593*4882a593Smuzhiyun tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
2594*4882a593Smuzhiyun }
2595*4882a593Smuzhiyun break;
2596*4882a593Smuzhiyun case SOFT_RESET_SEND:
2597*4882a593Smuzhiyun port->message_id = 0;
2598*4882a593Smuzhiyun port->rx_msgid = -1;
2599*4882a593Smuzhiyun if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET))
2600*4882a593Smuzhiyun tcpm_set_state_cond(port, hard_reset_state(port), 0);
2601*4882a593Smuzhiyun else
2602*4882a593Smuzhiyun tcpm_set_state_cond(port, hard_reset_state(port),
2603*4882a593Smuzhiyun PD_T_SENDER_RESPONSE);
2604*4882a593Smuzhiyun break;
2605*4882a593Smuzhiyun
2606*4882a593Smuzhiyun /* DR_Swap states */
2607*4882a593Smuzhiyun case DR_SWAP_SEND:
2608*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_DR_SWAP);
2609*4882a593Smuzhiyun tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT,
2610*4882a593Smuzhiyun PD_T_SENDER_RESPONSE);
2611*4882a593Smuzhiyun break;
2612*4882a593Smuzhiyun case DR_SWAP_ACCEPT:
2613*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
2614*4882a593Smuzhiyun #if 0
2615*4882a593Smuzhiyun /* Set VDM state machine running flag ASAP */
2616*4882a593Smuzhiyun if (port->data_role == TYPEC_DEVICE && port->send_discover)
2617*4882a593Smuzhiyun port->vdm_sm_running = true;
2618*4882a593Smuzhiyun #endif
2619*4882a593Smuzhiyun tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0);
2620*4882a593Smuzhiyun break;
2621*4882a593Smuzhiyun case DR_SWAP_SEND_TIMEOUT:
2622*4882a593Smuzhiyun //tcpm_swap_complete(port, -ETIMEDOUT);
2623*4882a593Smuzhiyun tcpm_set_state(port, ready_state(port), 0);
2624*4882a593Smuzhiyun break;
2625*4882a593Smuzhiyun case DR_SWAP_CHANGE_DR:
2626*4882a593Smuzhiyun if (port->data_role == TYPEC_HOST) {
2627*4882a593Smuzhiyun //tcpm_unregister_altmodes(port);
2628*4882a593Smuzhiyun tcpm_set_roles(port, true, port->pwr_role,
2629*4882a593Smuzhiyun TYPEC_DEVICE);
2630*4882a593Smuzhiyun } else {
2631*4882a593Smuzhiyun tcpm_set_roles(port, true, port->pwr_role,
2632*4882a593Smuzhiyun TYPEC_HOST);
2633*4882a593Smuzhiyun //port->send_discover = true;
2634*4882a593Smuzhiyun }
2635*4882a593Smuzhiyun /* DR_swap process complete, wait_dr_swap_Message is cleared */
2636*4882a593Smuzhiyun port->wait_dr_swap_Message = false;
2637*4882a593Smuzhiyun tcpm_set_state(port, ready_state(port), 0);
2638*4882a593Smuzhiyun break;
2639*4882a593Smuzhiyun
2640*4882a593Smuzhiyun #if 0
2641*4882a593Smuzhiyun
2642*4882a593Smuzhiyun /* PR_Swap states */
2643*4882a593Smuzhiyun case PR_SWAP_ACCEPT:
2644*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
2645*4882a593Smuzhiyun tcpm_set_state(port, PR_SWAP_START, 0);
2646*4882a593Smuzhiyun break;
2647*4882a593Smuzhiyun case PR_SWAP_SEND:
2648*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_PR_SWAP);
2649*4882a593Smuzhiyun tcpm_set_state_cond(port, PR_SWAP_SEND_TIMEOUT,
2650*4882a593Smuzhiyun PD_T_SENDER_RESPONSE);
2651*4882a593Smuzhiyun break;
2652*4882a593Smuzhiyun case PR_SWAP_SEND_TIMEOUT:
2653*4882a593Smuzhiyun tcpm_set_state(port, ready_state(port), 0);
2654*4882a593Smuzhiyun break;
2655*4882a593Smuzhiyun case PR_SWAP_START:
2656*4882a593Smuzhiyun tcpm_apply_rc(port);
2657*4882a593Smuzhiyun if (port->pwr_role == TYPEC_SOURCE)
2658*4882a593Smuzhiyun tcpm_set_state(port, PR_SWAP_SRC_SNK_TRANSITION_OFF,
2659*4882a593Smuzhiyun PD_T_SRC_TRANSITION);
2660*4882a593Smuzhiyun else
2661*4882a593Smuzhiyun tcpm_set_state(port, PR_SWAP_SNK_SRC_SINK_OFF, 0);
2662*4882a593Smuzhiyun break;
2663*4882a593Smuzhiyun case PR_SWAP_SRC_SNK_TRANSITION_OFF:
2664*4882a593Smuzhiyun /*
2665*4882a593Smuzhiyun * Prevent vbus discharge circuit from turning on during PR_SWAP
2666*4882a593Smuzhiyun * as this is not a disconnect.
2667*4882a593Smuzhiyun */
2668*4882a593Smuzhiyun tcpm_set_vbus(port, false);
2669*4882a593Smuzhiyun port->explicit_contract = false;
2670*4882a593Smuzhiyun /* allow time for Vbus discharge, must be < tSrcSwapStdby */
2671*4882a593Smuzhiyun tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF,
2672*4882a593Smuzhiyun PD_T_SRCSWAPSTDBY);
2673*4882a593Smuzhiyun break;
2674*4882a593Smuzhiyun case PR_SWAP_SRC_SNK_SOURCE_OFF:
2675*4882a593Smuzhiyun timer_val_msecs = PD_T_CC_DEBOUNCE;
2676*4882a593Smuzhiyun trace_android_vh_typec_tcpm_get_timer(tcpm_states[PR_SWAP_SRC_SNK_SOURCE_OFF],
2677*4882a593Smuzhiyun CC_DEBOUNCE, &timer_val_msecs);
2678*4882a593Smuzhiyun tcpm_set_cc(port, TYPEC_CC_RD);
2679*4882a593Smuzhiyun /* allow CC debounce */
2680*4882a593Smuzhiyun tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED,
2681*4882a593Smuzhiyun timer_val_msecs);
2682*4882a593Smuzhiyun break;
2683*4882a593Smuzhiyun case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED:
2684*4882a593Smuzhiyun /*
2685*4882a593Smuzhiyun * USB-PD standard, 6.2.1.4, Port Power Role:
2686*4882a593Smuzhiyun * "During the Power Role Swap Sequence, for the initial Source
2687*4882a593Smuzhiyun * Port, the Port Power Role field shall be set to Sink in the
2688*4882a593Smuzhiyun * PS_RDY Message indicating that the initial Source’s power
2689*4882a593Smuzhiyun * supply is turned off"
2690*4882a593Smuzhiyun */
2691*4882a593Smuzhiyun tcpm_set_pwr_role(port, TYPEC_SINK);
2692*4882a593Smuzhiyun if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) {
2693*4882a593Smuzhiyun tcpm_set_state(port, ERROR_RECOVERY, 0);
2694*4882a593Smuzhiyun break;
2695*4882a593Smuzhiyun }
2696*4882a593Smuzhiyun tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_ON_PRS);
2697*4882a593Smuzhiyun break;
2698*4882a593Smuzhiyun case PR_SWAP_SRC_SNK_SINK_ON:
2699*4882a593Smuzhiyun tcpm_enable_auto_vbus_discharge(port, true);
2700*4882a593Smuzhiyun /* Set the vbus disconnect threshold for implicit contract */
2701*4882a593Smuzhiyun tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V);
2702*4882a593Smuzhiyun tcpm_set_state(port, SNK_STARTUP, 0);
2703*4882a593Smuzhiyun break;
2704*4882a593Smuzhiyun case PR_SWAP_SNK_SRC_SINK_OFF:
2705*4882a593Smuzhiyun timer_val_msecs = PD_T_PS_SOURCE_OFF;
2706*4882a593Smuzhiyun trace_android_vh_typec_tcpm_get_timer(tcpm_states[PR_SWAP_SNK_SRC_SINK_OFF],
2707*4882a593Smuzhiyun SOURCE_OFF, &timer_val_msecs);
2708*4882a593Smuzhiyun /*
2709*4882a593Smuzhiyun * Prevent vbus discharge circuit from turning on during PR_SWAP
2710*4882a593Smuzhiyun * as this is not a disconnect.
2711*4882a593Smuzhiyun */
2712*4882a593Smuzhiyun tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB,
2713*4882a593Smuzhiyun port->pps_data.active, 0);
2714*4882a593Smuzhiyun tcpm_set_charge(port, false);
2715*4882a593Smuzhiyun tcpm_set_state(port, hard_reset_state(port), timer_val_msecs);
2716*4882a593Smuzhiyun break;
2717*4882a593Smuzhiyun case PR_SWAP_SNK_SRC_SOURCE_ON:
2718*4882a593Smuzhiyun tcpm_enable_auto_vbus_discharge(port, true);
2719*4882a593Smuzhiyun tcpm_set_cc(port, tcpm_rp_cc(port));
2720*4882a593Smuzhiyun tcpm_set_vbus(port, true);
2721*4882a593Smuzhiyun /*
2722*4882a593Smuzhiyun * allow time VBUS ramp-up, must be < tNewSrc
2723*4882a593Smuzhiyun * Also, this window overlaps with CC debounce as well.
2724*4882a593Smuzhiyun * So, Wait for the max of two which is PD_T_NEWSRC
2725*4882a593Smuzhiyun */
2726*4882a593Smuzhiyun tcpm_set_state(port, PR_SWAP_SNK_SRC_SOURCE_ON_VBUS_RAMPED_UP,
2727*4882a593Smuzhiyun PD_T_NEWSRC);
2728*4882a593Smuzhiyun break;
2729*4882a593Smuzhiyun case PR_SWAP_SNK_SRC_SOURCE_ON_VBUS_RAMPED_UP:
2730*4882a593Smuzhiyun /*
2731*4882a593Smuzhiyun * USB PD standard, 6.2.1.4:
2732*4882a593Smuzhiyun * "Subsequent Messages initiated by the Policy Engine,
2733*4882a593Smuzhiyun * such as the PS_RDY Message sent to indicate that Vbus
2734*4882a593Smuzhiyun * is ready, will have the Port Power Role field set to
2735*4882a593Smuzhiyun * Source."
2736*4882a593Smuzhiyun */
2737*4882a593Smuzhiyun tcpm_set_pwr_role(port, TYPEC_SOURCE);
2738*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_PS_RDY);
2739*4882a593Smuzhiyun tcpm_set_state(port, SRC_STARTUP, PD_T_SWAP_SRC_START);
2740*4882a593Smuzhiyun break;
2741*4882a593Smuzhiyun #endif
2742*4882a593Smuzhiyun case GET_STATUS_SEND:
2743*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_GET_STATUS);
2744*4882a593Smuzhiyun tcpm_set_state(port, GET_STATUS_SEND_TIMEOUT,
2745*4882a593Smuzhiyun PD_T_SENDER_RESPONSE);
2746*4882a593Smuzhiyun break;
2747*4882a593Smuzhiyun case GET_STATUS_SEND_TIMEOUT:
2748*4882a593Smuzhiyun tcpm_set_state(port, ready_state(port), 0);
2749*4882a593Smuzhiyun break;
2750*4882a593Smuzhiyun case GET_PPS_STATUS_SEND:
2751*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_GET_PPS_STATUS);
2752*4882a593Smuzhiyun tcpm_set_state(port, GET_PPS_STATUS_SEND_TIMEOUT,
2753*4882a593Smuzhiyun PD_T_SENDER_RESPONSE);
2754*4882a593Smuzhiyun break;
2755*4882a593Smuzhiyun case GET_PPS_STATUS_SEND_TIMEOUT:
2756*4882a593Smuzhiyun tcpm_set_state(port, ready_state(port), 0);
2757*4882a593Smuzhiyun break;
2758*4882a593Smuzhiyun case GET_SINK_CAP:
2759*4882a593Smuzhiyun tcpm_pd_send_control(port, PD_CTRL_GET_SINK_CAP);
2760*4882a593Smuzhiyun tcpm_set_state(port, GET_SINK_CAP_TIMEOUT, PD_T_SENDER_RESPONSE);
2761*4882a593Smuzhiyun break;
2762*4882a593Smuzhiyun case GET_SINK_CAP_TIMEOUT:
2763*4882a593Smuzhiyun tcpm_set_state(port, ready_state(port), 0);
2764*4882a593Smuzhiyun break;
2765*4882a593Smuzhiyun case ERROR_RECOVERY:
2766*4882a593Smuzhiyun tcpm_set_state(port, PORT_RESET, 0);
2767*4882a593Smuzhiyun break;
2768*4882a593Smuzhiyun case PORT_RESET:
2769*4882a593Smuzhiyun tcpm_reset_port(port);
2770*4882a593Smuzhiyun tcpm_set_cc(port, TYPEC_CC_OPEN);
2771*4882a593Smuzhiyun tcpm_set_state(port, PORT_RESET_WAIT_OFF,
2772*4882a593Smuzhiyun PD_T_ERROR_RECOVERY);
2773*4882a593Smuzhiyun break;
2774*4882a593Smuzhiyun case PORT_RESET_WAIT_OFF:
2775*4882a593Smuzhiyun tcpm_set_state(port,
2776*4882a593Smuzhiyun tcpm_default_state(port),
2777*4882a593Smuzhiyun port->vbus_present ? PD_T_PS_SOURCE_OFF : 0);
2778*4882a593Smuzhiyun break;
2779*4882a593Smuzhiyun default:
2780*4882a593Smuzhiyun printf("Unexpected port state %d\n", port->state);
2781*4882a593Smuzhiyun break;
2782*4882a593Smuzhiyun }
2783*4882a593Smuzhiyun }
2784*4882a593Smuzhiyun
tcpm_state_machine(struct tcpm_port * port)2785*4882a593Smuzhiyun static void tcpm_state_machine(struct tcpm_port *port)
2786*4882a593Smuzhiyun {
2787*4882a593Smuzhiyun enum tcpm_state prev_state;
2788*4882a593Smuzhiyun
2789*4882a593Smuzhiyun mutex_lock(&port->lock);
2790*4882a593Smuzhiyun port->state_machine_running = true;
2791*4882a593Smuzhiyun
2792*4882a593Smuzhiyun if (port->queued_message && tcpm_send_queued_message(port))
2793*4882a593Smuzhiyun goto done;
2794*4882a593Smuzhiyun
2795*4882a593Smuzhiyun /* If we were queued due to a delayed state change, update it now */
2796*4882a593Smuzhiyun if (port->delayed_state) {
2797*4882a593Smuzhiyun debug("state change %s -> %s [delayed %ld ms]\n",
2798*4882a593Smuzhiyun tcpm_states[port->state],
2799*4882a593Smuzhiyun tcpm_states[port->delayed_state], port->delay_ms);
2800*4882a593Smuzhiyun port->prev_state = port->state;
2801*4882a593Smuzhiyun port->state = port->delayed_state;
2802*4882a593Smuzhiyun port->delayed_state = INVALID_STATE;
2803*4882a593Smuzhiyun }
2804*4882a593Smuzhiyun
2805*4882a593Smuzhiyun /*
2806*4882a593Smuzhiyun * Continue running as long as we have (non-delayed) state changes
2807*4882a593Smuzhiyun * to make.
2808*4882a593Smuzhiyun */
2809*4882a593Smuzhiyun do {
2810*4882a593Smuzhiyun prev_state = port->state;
2811*4882a593Smuzhiyun run_state_machine(port);
2812*4882a593Smuzhiyun if (port->queued_message)
2813*4882a593Smuzhiyun tcpm_send_queued_message(port);
2814*4882a593Smuzhiyun } while (port->state != prev_state && !port->delayed_state);
2815*4882a593Smuzhiyun
2816*4882a593Smuzhiyun done:
2817*4882a593Smuzhiyun port->state_machine_running = false;
2818*4882a593Smuzhiyun mutex_unlock(&port->lock);
2819*4882a593Smuzhiyun }
2820*4882a593Smuzhiyun
_tcpm_cc_change(struct tcpm_port * port,enum typec_cc_status cc1,enum typec_cc_status cc2)2821*4882a593Smuzhiyun static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
2822*4882a593Smuzhiyun enum typec_cc_status cc2)
2823*4882a593Smuzhiyun {
2824*4882a593Smuzhiyun enum typec_cc_status old_cc1, old_cc2;
2825*4882a593Smuzhiyun enum tcpm_state new_state;
2826*4882a593Smuzhiyun
2827*4882a593Smuzhiyun old_cc1 = port->cc1;
2828*4882a593Smuzhiyun old_cc2 = port->cc2;
2829*4882a593Smuzhiyun port->cc1 = cc1;
2830*4882a593Smuzhiyun port->cc2 = cc2;
2831*4882a593Smuzhiyun
2832*4882a593Smuzhiyun debug("CC1: %u -> %u, CC2: %u -> %u [state %s, polarity %d, %s]\n",
2833*4882a593Smuzhiyun old_cc1, cc1, old_cc2, cc2, tcpm_states[port->state],
2834*4882a593Smuzhiyun port->polarity,
2835*4882a593Smuzhiyun tcpm_port_is_disconnected(port) ? "disconnected" : "connected");
2836*4882a593Smuzhiyun
2837*4882a593Smuzhiyun switch (port->state) {
2838*4882a593Smuzhiyun case TOGGLING:
2839*4882a593Smuzhiyun if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
2840*4882a593Smuzhiyun tcpm_port_is_source(port))
2841*4882a593Smuzhiyun tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
2842*4882a593Smuzhiyun else if (tcpm_port_is_sink(port))
2843*4882a593Smuzhiyun tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
2844*4882a593Smuzhiyun break;
2845*4882a593Smuzhiyun case SRC_UNATTACHED:
2846*4882a593Smuzhiyun case ACC_UNATTACHED:
2847*4882a593Smuzhiyun if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
2848*4882a593Smuzhiyun tcpm_port_is_source(port))
2849*4882a593Smuzhiyun tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
2850*4882a593Smuzhiyun break;
2851*4882a593Smuzhiyun case SRC_ATTACH_WAIT:
2852*4882a593Smuzhiyun if (tcpm_port_is_disconnected(port) ||
2853*4882a593Smuzhiyun tcpm_port_is_audio_detached(port))
2854*4882a593Smuzhiyun tcpm_set_state(port, SRC_UNATTACHED, 0);
2855*4882a593Smuzhiyun else if (cc1 != old_cc1 || cc2 != old_cc2)
2856*4882a593Smuzhiyun tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
2857*4882a593Smuzhiyun break;
2858*4882a593Smuzhiyun case SRC_ATTACHED:
2859*4882a593Smuzhiyun case SRC_SEND_CAPABILITIES:
2860*4882a593Smuzhiyun case SRC_READY:
2861*4882a593Smuzhiyun if (tcpm_port_is_disconnected(port) ||
2862*4882a593Smuzhiyun !tcpm_port_is_source(port))
2863*4882a593Smuzhiyun tcpm_set_state(port, SRC_UNATTACHED, 0);
2864*4882a593Smuzhiyun break;
2865*4882a593Smuzhiyun case SNK_UNATTACHED:
2866*4882a593Smuzhiyun if (tcpm_port_is_sink(port))
2867*4882a593Smuzhiyun tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
2868*4882a593Smuzhiyun break;
2869*4882a593Smuzhiyun case SNK_ATTACH_WAIT:
2870*4882a593Smuzhiyun if ((port->cc1 == TYPEC_CC_OPEN &&
2871*4882a593Smuzhiyun port->cc2 != TYPEC_CC_OPEN) ||
2872*4882a593Smuzhiyun (port->cc1 != TYPEC_CC_OPEN &&
2873*4882a593Smuzhiyun port->cc2 == TYPEC_CC_OPEN))
2874*4882a593Smuzhiyun new_state = SNK_DEBOUNCED;
2875*4882a593Smuzhiyun else if (tcpm_port_is_disconnected(port))
2876*4882a593Smuzhiyun new_state = SNK_UNATTACHED;
2877*4882a593Smuzhiyun else
2878*4882a593Smuzhiyun break;
2879*4882a593Smuzhiyun if (new_state != port->delayed_state)
2880*4882a593Smuzhiyun tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
2881*4882a593Smuzhiyun break;
2882*4882a593Smuzhiyun case SNK_DEBOUNCED:
2883*4882a593Smuzhiyun if (tcpm_port_is_disconnected(port))
2884*4882a593Smuzhiyun new_state = SNK_UNATTACHED;
2885*4882a593Smuzhiyun else if (port->vbus_present)
2886*4882a593Smuzhiyun new_state = tcpm_try_src(port) ? SRC_TRY : SNK_ATTACHED;
2887*4882a593Smuzhiyun else
2888*4882a593Smuzhiyun new_state = SNK_UNATTACHED;
2889*4882a593Smuzhiyun if (new_state != port->delayed_state)
2890*4882a593Smuzhiyun tcpm_set_state(port, SNK_DEBOUNCED, 0);
2891*4882a593Smuzhiyun break;
2892*4882a593Smuzhiyun case SNK_READY:
2893*4882a593Smuzhiyun if (tcpm_port_is_disconnected(port))
2894*4882a593Smuzhiyun tcpm_set_state(port, unattached_state(port), 0);
2895*4882a593Smuzhiyun else if (!port->pd_capable &&
2896*4882a593Smuzhiyun (cc1 != old_cc1 || cc2 != old_cc2))
2897*4882a593Smuzhiyun tcpm_set_current_limit(port,
2898*4882a593Smuzhiyun tcpm_get_current_limit(port),
2899*4882a593Smuzhiyun 5000);
2900*4882a593Smuzhiyun break;
2901*4882a593Smuzhiyun
2902*4882a593Smuzhiyun case AUDIO_ACC_ATTACHED:
2903*4882a593Smuzhiyun if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN)
2904*4882a593Smuzhiyun tcpm_set_state(port, AUDIO_ACC_DEBOUNCE, 0);
2905*4882a593Smuzhiyun break;
2906*4882a593Smuzhiyun case AUDIO_ACC_DEBOUNCE:
2907*4882a593Smuzhiyun if (tcpm_port_is_audio(port))
2908*4882a593Smuzhiyun tcpm_set_state(port, AUDIO_ACC_ATTACHED, 0);
2909*4882a593Smuzhiyun break;
2910*4882a593Smuzhiyun
2911*4882a593Smuzhiyun case DEBUG_ACC_ATTACHED:
2912*4882a593Smuzhiyun if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN)
2913*4882a593Smuzhiyun tcpm_set_state(port, ACC_UNATTACHED, 0);
2914*4882a593Smuzhiyun break;
2915*4882a593Smuzhiyun
2916*4882a593Smuzhiyun case SNK_TRY:
2917*4882a593Smuzhiyun /* Do nothing, waiting for timeout */
2918*4882a593Smuzhiyun break;
2919*4882a593Smuzhiyun
2920*4882a593Smuzhiyun case SNK_DISCOVERY:
2921*4882a593Smuzhiyun /* CC line is unstable, wait for debounce */
2922*4882a593Smuzhiyun if (tcpm_port_is_disconnected(port))
2923*4882a593Smuzhiyun tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE, 0);
2924*4882a593Smuzhiyun break;
2925*4882a593Smuzhiyun case SNK_DISCOVERY_DEBOUNCE:
2926*4882a593Smuzhiyun break;
2927*4882a593Smuzhiyun
2928*4882a593Smuzhiyun case SRC_TRYWAIT:
2929*4882a593Smuzhiyun /* Hand over to state machine if needed */
2930*4882a593Smuzhiyun if (!port->vbus_present && tcpm_port_is_source(port))
2931*4882a593Smuzhiyun tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0);
2932*4882a593Smuzhiyun break;
2933*4882a593Smuzhiyun case SRC_TRYWAIT_DEBOUNCE:
2934*4882a593Smuzhiyun if (port->vbus_present || !tcpm_port_is_source(port))
2935*4882a593Smuzhiyun tcpm_set_state(port, SRC_TRYWAIT, 0);
2936*4882a593Smuzhiyun break;
2937*4882a593Smuzhiyun case SNK_TRY_WAIT_DEBOUNCE:
2938*4882a593Smuzhiyun if (!tcpm_port_is_sink(port)) {
2939*4882a593Smuzhiyun port->max_wait = 0;
2940*4882a593Smuzhiyun tcpm_set_state(port, SRC_TRYWAIT, 0);
2941*4882a593Smuzhiyun }
2942*4882a593Smuzhiyun break;
2943*4882a593Smuzhiyun case SRC_TRY_WAIT:
2944*4882a593Smuzhiyun if (tcpm_port_is_source(port))
2945*4882a593Smuzhiyun tcpm_set_state(port, SRC_TRY_DEBOUNCE, 0);
2946*4882a593Smuzhiyun break;
2947*4882a593Smuzhiyun case SRC_TRY_DEBOUNCE:
2948*4882a593Smuzhiyun tcpm_set_state(port, SRC_TRY_WAIT, 0);
2949*4882a593Smuzhiyun break;
2950*4882a593Smuzhiyun case SNK_TRYWAIT_DEBOUNCE:
2951*4882a593Smuzhiyun if (tcpm_port_is_sink(port))
2952*4882a593Smuzhiyun tcpm_set_state(port, SNK_TRYWAIT_VBUS, 0);
2953*4882a593Smuzhiyun break;
2954*4882a593Smuzhiyun case SNK_TRYWAIT_VBUS:
2955*4882a593Smuzhiyun if (!tcpm_port_is_sink(port))
2956*4882a593Smuzhiyun tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0);
2957*4882a593Smuzhiyun break;
2958*4882a593Smuzhiyun case SNK_TRYWAIT:
2959*4882a593Smuzhiyun /* Do nothing, waiting for tCCDebounce */
2960*4882a593Smuzhiyun break;
2961*4882a593Smuzhiyun case PR_SWAP_SNK_SRC_SINK_OFF:
2962*4882a593Smuzhiyun case PR_SWAP_SRC_SNK_TRANSITION_OFF:
2963*4882a593Smuzhiyun case PR_SWAP_SRC_SNK_SOURCE_OFF:
2964*4882a593Smuzhiyun case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED:
2965*4882a593Smuzhiyun case PR_SWAP_SNK_SRC_SOURCE_ON:
2966*4882a593Smuzhiyun /*
2967*4882a593Smuzhiyun * CC state change is expected in PR_SWAP
2968*4882a593Smuzhiyun * Ignore it.
2969*4882a593Smuzhiyun */
2970*4882a593Smuzhiyun break;
2971*4882a593Smuzhiyun
2972*4882a593Smuzhiyun case PORT_RESET:
2973*4882a593Smuzhiyun case PORT_RESET_WAIT_OFF:
2974*4882a593Smuzhiyun /*
2975*4882a593Smuzhiyun * State set back to default mode once the timer completes.
2976*4882a593Smuzhiyun * Ignore CC changes here.
2977*4882a593Smuzhiyun */
2978*4882a593Smuzhiyun break;
2979*4882a593Smuzhiyun default:
2980*4882a593Smuzhiyun /*
2981*4882a593Smuzhiyun * While acting as sink and auto vbus discharge is enabled, Allow disconnect
2982*4882a593Smuzhiyun * to be driven by vbus disconnect.
2983*4882a593Smuzhiyun */
2984*4882a593Smuzhiyun if (tcpm_port_is_disconnected(port))
2985*4882a593Smuzhiyun tcpm_set_state(port, unattached_state(port), 0);
2986*4882a593Smuzhiyun break;
2987*4882a593Smuzhiyun }
2988*4882a593Smuzhiyun }
2989*4882a593Smuzhiyun
_tcpm_pd_vbus_on(struct tcpm_port * port)2990*4882a593Smuzhiyun static void _tcpm_pd_vbus_on(struct tcpm_port *port)
2991*4882a593Smuzhiyun {
2992*4882a593Smuzhiyun debug("%s: VBUS on\n", __func__);
2993*4882a593Smuzhiyun port->vbus_present = true;
2994*4882a593Smuzhiyun /*
2995*4882a593Smuzhiyun * When vbus_present is true i.e. Voltage at VBUS is greater than VSAFE5V implicitly
2996*4882a593Smuzhiyun * states that vbus is not at VSAFE0V, hence clear the vbus_vsafe0v flag here.
2997*4882a593Smuzhiyun */
2998*4882a593Smuzhiyun port->vbus_vsafe0v = false;
2999*4882a593Smuzhiyun
3000*4882a593Smuzhiyun switch (port->state) {
3001*4882a593Smuzhiyun case SNK_TRANSITION_SINK_VBUS:
3002*4882a593Smuzhiyun port->explicit_contract = true;
3003*4882a593Smuzhiyun tcpm_set_state(port, SNK_READY, 0);
3004*4882a593Smuzhiyun break;
3005*4882a593Smuzhiyun case SNK_DISCOVERY:
3006*4882a593Smuzhiyun tcpm_set_state(port, SNK_DISCOVERY, 0);
3007*4882a593Smuzhiyun break;
3008*4882a593Smuzhiyun case SNK_DEBOUNCED:
3009*4882a593Smuzhiyun tcpm_set_state(port, SNK_ATTACHED, 0);
3010*4882a593Smuzhiyun break;
3011*4882a593Smuzhiyun case SNK_HARD_RESET_WAIT_VBUS:
3012*4882a593Smuzhiyun tcpm_set_state(port, SNK_HARD_RESET_SINK_ON, 0);
3013*4882a593Smuzhiyun break;
3014*4882a593Smuzhiyun case SRC_ATTACHED:
3015*4882a593Smuzhiyun tcpm_set_state(port, SRC_STARTUP, 0);
3016*4882a593Smuzhiyun break;
3017*4882a593Smuzhiyun case SRC_HARD_RESET_VBUS_ON:
3018*4882a593Smuzhiyun tcpm_set_state(port, SRC_STARTUP, 0);
3019*4882a593Smuzhiyun break;
3020*4882a593Smuzhiyun
3021*4882a593Smuzhiyun case SNK_TRY:
3022*4882a593Smuzhiyun /* Do nothing, waiting for timeout */
3023*4882a593Smuzhiyun break;
3024*4882a593Smuzhiyun case SRC_TRYWAIT:
3025*4882a593Smuzhiyun /* Do nothing, Waiting for Rd to be detected */
3026*4882a593Smuzhiyun break;
3027*4882a593Smuzhiyun case SRC_TRYWAIT_DEBOUNCE:
3028*4882a593Smuzhiyun tcpm_set_state(port, SRC_TRYWAIT, 0);
3029*4882a593Smuzhiyun break;
3030*4882a593Smuzhiyun case SNK_TRY_WAIT_DEBOUNCE:
3031*4882a593Smuzhiyun /* Do nothing, waiting for PD_DEBOUNCE to do be done */
3032*4882a593Smuzhiyun break;
3033*4882a593Smuzhiyun case SNK_TRYWAIT:
3034*4882a593Smuzhiyun /* Do nothing, waiting for tCCDebounce */
3035*4882a593Smuzhiyun break;
3036*4882a593Smuzhiyun case SNK_TRYWAIT_VBUS:
3037*4882a593Smuzhiyun if (tcpm_port_is_sink(port))
3038*4882a593Smuzhiyun tcpm_set_state(port, SNK_ATTACHED, 0);
3039*4882a593Smuzhiyun break;
3040*4882a593Smuzhiyun case SNK_TRYWAIT_DEBOUNCE:
3041*4882a593Smuzhiyun /* Do nothing, waiting for Rp */
3042*4882a593Smuzhiyun break;
3043*4882a593Smuzhiyun case SRC_TRY_WAIT:
3044*4882a593Smuzhiyun case SRC_TRY_DEBOUNCE:
3045*4882a593Smuzhiyun /* Do nothing, waiting for sink detection */
3046*4882a593Smuzhiyun break;
3047*4882a593Smuzhiyun
3048*4882a593Smuzhiyun case PORT_RESET:
3049*4882a593Smuzhiyun case PORT_RESET_WAIT_OFF:
3050*4882a593Smuzhiyun /*
3051*4882a593Smuzhiyun * State set back to default mode once the timer completes.
3052*4882a593Smuzhiyun * Ignore vbus changes here.
3053*4882a593Smuzhiyun */
3054*4882a593Smuzhiyun break;
3055*4882a593Smuzhiyun
3056*4882a593Smuzhiyun default:
3057*4882a593Smuzhiyun break;
3058*4882a593Smuzhiyun }
3059*4882a593Smuzhiyun }
3060*4882a593Smuzhiyun
_tcpm_pd_vbus_off(struct tcpm_port * port)3061*4882a593Smuzhiyun static void _tcpm_pd_vbus_off(struct tcpm_port *port)
3062*4882a593Smuzhiyun {
3063*4882a593Smuzhiyun debug("%s: VBUS off\n", __func__);
3064*4882a593Smuzhiyun port->vbus_present = false;
3065*4882a593Smuzhiyun port->vbus_never_low = false;
3066*4882a593Smuzhiyun switch (port->state) {
3067*4882a593Smuzhiyun case SNK_HARD_RESET_SINK_OFF:
3068*4882a593Smuzhiyun tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0);
3069*4882a593Smuzhiyun break;
3070*4882a593Smuzhiyun case HARD_RESET_SEND:
3071*4882a593Smuzhiyun break;
3072*4882a593Smuzhiyun case SNK_TRY:
3073*4882a593Smuzhiyun /* Do nothing, waiting for timeout */
3074*4882a593Smuzhiyun break;
3075*4882a593Smuzhiyun case SRC_TRYWAIT:
3076*4882a593Smuzhiyun /* Hand over to state machine if needed */
3077*4882a593Smuzhiyun if (tcpm_port_is_source(port))
3078*4882a593Smuzhiyun tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0);
3079*4882a593Smuzhiyun break;
3080*4882a593Smuzhiyun case SNK_TRY_WAIT_DEBOUNCE:
3081*4882a593Smuzhiyun /* Do nothing, waiting for PD_DEBOUNCE to do be done */
3082*4882a593Smuzhiyun break;
3083*4882a593Smuzhiyun case SNK_TRYWAIT:
3084*4882a593Smuzhiyun case SNK_TRYWAIT_VBUS:
3085*4882a593Smuzhiyun case SNK_TRYWAIT_DEBOUNCE:
3086*4882a593Smuzhiyun break;
3087*4882a593Smuzhiyun case SNK_ATTACH_WAIT:
3088*4882a593Smuzhiyun port->debouncing = false;
3089*4882a593Smuzhiyun tcpm_set_state(port, SNK_UNATTACHED, 0);
3090*4882a593Smuzhiyun break;
3091*4882a593Smuzhiyun
3092*4882a593Smuzhiyun case SNK_NEGOTIATE_CAPABILITIES:
3093*4882a593Smuzhiyun break;
3094*4882a593Smuzhiyun
3095*4882a593Smuzhiyun case PR_SWAP_SRC_SNK_TRANSITION_OFF:
3096*4882a593Smuzhiyun tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF, 0);
3097*4882a593Smuzhiyun break;
3098*4882a593Smuzhiyun
3099*4882a593Smuzhiyun case PR_SWAP_SNK_SRC_SINK_OFF:
3100*4882a593Smuzhiyun /* Do nothing, expected */
3101*4882a593Smuzhiyun break;
3102*4882a593Smuzhiyun
3103*4882a593Smuzhiyun case PR_SWAP_SNK_SRC_SOURCE_ON:
3104*4882a593Smuzhiyun /*
3105*4882a593Smuzhiyun * Do nothing when vbus off notification is received.
3106*4882a593Smuzhiyun * TCPM can wait for PD_T_NEWSRC in PR_SWAP_SNK_SRC_SOURCE_ON
3107*4882a593Smuzhiyun * for the vbus source to ramp up.
3108*4882a593Smuzhiyun */
3109*4882a593Smuzhiyun break;
3110*4882a593Smuzhiyun
3111*4882a593Smuzhiyun case PORT_RESET_WAIT_OFF:
3112*4882a593Smuzhiyun tcpm_set_state(port, tcpm_default_state(port), 0);
3113*4882a593Smuzhiyun break;
3114*4882a593Smuzhiyun
3115*4882a593Smuzhiyun case SRC_TRY_WAIT:
3116*4882a593Smuzhiyun case SRC_TRY_DEBOUNCE:
3117*4882a593Smuzhiyun /* Do nothing, waiting for sink detection */
3118*4882a593Smuzhiyun break;
3119*4882a593Smuzhiyun
3120*4882a593Smuzhiyun case PORT_RESET:
3121*4882a593Smuzhiyun /*
3122*4882a593Smuzhiyun * State set back to default mode once the timer completes.
3123*4882a593Smuzhiyun * Ignore vbus changes here.
3124*4882a593Smuzhiyun */
3125*4882a593Smuzhiyun break;
3126*4882a593Smuzhiyun
3127*4882a593Smuzhiyun default:
3128*4882a593Smuzhiyun if (port->pwr_role == TYPEC_SINK && port->attached)
3129*4882a593Smuzhiyun tcpm_set_state(port, SNK_UNATTACHED, 0);
3130*4882a593Smuzhiyun break;
3131*4882a593Smuzhiyun }
3132*4882a593Smuzhiyun }
3133*4882a593Smuzhiyun
_tcpm_pd_hard_reset(struct tcpm_port * port)3134*4882a593Smuzhiyun static void _tcpm_pd_hard_reset(struct tcpm_port *port)
3135*4882a593Smuzhiyun {
3136*4882a593Smuzhiyun debug("Received hard reset\n");
3137*4882a593Smuzhiyun port->poll_event_cnt = 0;
3138*4882a593Smuzhiyun
3139*4882a593Smuzhiyun /* If a hard reset message is received during the port reset process,
3140*4882a593Smuzhiyun * we should ignore it, that is, do not set port->state to HARD_RESET_START.
3141*4882a593Smuzhiyun */
3142*4882a593Smuzhiyun if (port->state == PORT_RESET || port->state == PORT_RESET_WAIT_OFF)
3143*4882a593Smuzhiyun return ;
3144*4882a593Smuzhiyun
3145*4882a593Smuzhiyun /*
3146*4882a593Smuzhiyun * If we keep receiving hard reset requests, executing the hard reset
3147*4882a593Smuzhiyun * must have failed. Revert to error recovery if that happens.
3148*4882a593Smuzhiyun */
3149*4882a593Smuzhiyun tcpm_set_state(port,
3150*4882a593Smuzhiyun port->hard_reset_count < PD_N_HARD_RESET_COUNT ?
3151*4882a593Smuzhiyun HARD_RESET_START : ERROR_RECOVERY,
3152*4882a593Smuzhiyun 0);
3153*4882a593Smuzhiyun }
3154*4882a593Smuzhiyun
3155*4882a593Smuzhiyun #if 0
3156*4882a593Smuzhiyun static void tcpm_pd_event_handler(struct tcpm_port *port)
3157*4882a593Smuzhiyun {
3158*4882a593Smuzhiyun u32 events;
3159*4882a593Smuzhiyun
3160*4882a593Smuzhiyun while (port->pd_events) {
3161*4882a593Smuzhiyun events = port->pd_events;
3162*4882a593Smuzhiyun port->pd_events = 0;
3163*4882a593Smuzhiyun if (events & TCPM_RESET_EVENT)
3164*4882a593Smuzhiyun _tcpm_pd_hard_reset(port);
3165*4882a593Smuzhiyun if (events & TCPM_VBUS_EVENT) {
3166*4882a593Smuzhiyun bool vbus;
3167*4882a593Smuzhiyun
3168*4882a593Smuzhiyun vbus = port->tcpc->get_vbus(port->tcpc);
3169*4882a593Smuzhiyun if (vbus) {
3170*4882a593Smuzhiyun _tcpm_pd_vbus_on(port);
3171*4882a593Smuzhiyun } else {
3172*4882a593Smuzhiyun _tcpm_pd_vbus_off(port);
3173*4882a593Smuzhiyun /*
3174*4882a593Smuzhiyun * When TCPC does not support detecting vsafe0v voltage level,
3175*4882a593Smuzhiyun * treat vbus absent as vsafe0v. Else invoke is_vbus_vsafe0v
3176*4882a593Smuzhiyun * to see if vbus has discharge to VSAFE0V.
3177*4882a593Smuzhiyun */
3178*4882a593Smuzhiyun if (!port->tcpc->is_vbus_vsafe0v ||
3179*4882a593Smuzhiyun port->tcpc->is_vbus_vsafe0v(port->tcpc))
3180*4882a593Smuzhiyun _tcpm_pd_vbus_vsafe0v(port);
3181*4882a593Smuzhiyun }
3182*4882a593Smuzhiyun }
3183*4882a593Smuzhiyun if (events & TCPM_CC_EVENT) {
3184*4882a593Smuzhiyun enum typec_cc_status cc1, cc2;
3185*4882a593Smuzhiyun
3186*4882a593Smuzhiyun if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0)
3187*4882a593Smuzhiyun _tcpm_cc_change(port, cc1, cc2);
3188*4882a593Smuzhiyun }
3189*4882a593Smuzhiyun if (events & TCPM_FRS_EVENT) {
3190*4882a593Smuzhiyun if (port->state == SNK_READY) {
3191*4882a593Smuzhiyun int ret;
3192*4882a593Smuzhiyun
3193*4882a593Smuzhiyun port->upcoming_state = FR_SWAP_SEND;
3194*4882a593Smuzhiyun ret = tcpm_ams_start(port, FAST_ROLE_SWAP);
3195*4882a593Smuzhiyun if (ret == -EAGAIN)
3196*4882a593Smuzhiyun port->upcoming_state = INVALID_STATE;
3197*4882a593Smuzhiyun } else {
3198*4882a593Smuzhiyun tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready");
3199*4882a593Smuzhiyun }
3200*4882a593Smuzhiyun }
3201*4882a593Smuzhiyun if (events & TCPM_SOURCING_VBUS) {
3202*4882a593Smuzhiyun tcpm_log(port, "sourcing vbus");
3203*4882a593Smuzhiyun /*
3204*4882a593Smuzhiyun * In fast role swap case TCPC autonomously sources vbus. Set vbus_source
3205*4882a593Smuzhiyun * true as TCPM wouldn't have called tcpm_set_vbus.
3206*4882a593Smuzhiyun *
3207*4882a593Smuzhiyun * When vbus is sourced on the command on TCPM i.e. TCPM called
3208*4882a593Smuzhiyun * tcpm_set_vbus to source vbus, vbus_source would already be true.
3209*4882a593Smuzhiyun */
3210*4882a593Smuzhiyun port->vbus_source = true;
3211*4882a593Smuzhiyun _tcpm_pd_vbus_on(port);
3212*4882a593Smuzhiyun }
3213*4882a593Smuzhiyun }
3214*4882a593Smuzhiyun }
3215*4882a593Smuzhiyun #endif
3216*4882a593Smuzhiyun
tcpm_cc_change(struct tcpm_port * port)3217*4882a593Smuzhiyun void tcpm_cc_change(struct tcpm_port *port)
3218*4882a593Smuzhiyun {
3219*4882a593Smuzhiyun enum typec_cc_status cc1, cc2;
3220*4882a593Smuzhiyun
3221*4882a593Smuzhiyun port->poll_event_cnt = 0;
3222*4882a593Smuzhiyun if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0)
3223*4882a593Smuzhiyun _tcpm_cc_change(port, cc1, cc2);
3224*4882a593Smuzhiyun }
3225*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_cc_change);
3226*4882a593Smuzhiyun
tcpm_vbus_change(struct tcpm_port * port)3227*4882a593Smuzhiyun void tcpm_vbus_change(struct tcpm_port *port)
3228*4882a593Smuzhiyun {
3229*4882a593Smuzhiyun bool vbus;
3230*4882a593Smuzhiyun
3231*4882a593Smuzhiyun port->poll_event_cnt = 0;
3232*4882a593Smuzhiyun vbus = port->tcpc->get_vbus(port->tcpc);
3233*4882a593Smuzhiyun if (vbus)
3234*4882a593Smuzhiyun _tcpm_pd_vbus_on(port);
3235*4882a593Smuzhiyun else
3236*4882a593Smuzhiyun _tcpm_pd_vbus_off(port);
3237*4882a593Smuzhiyun }
3238*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_vbus_change);
3239*4882a593Smuzhiyun
tcpm_pd_hard_reset(struct tcpm_port * port)3240*4882a593Smuzhiyun void tcpm_pd_hard_reset(struct tcpm_port *port)
3241*4882a593Smuzhiyun {
3242*4882a593Smuzhiyun port->poll_event_cnt = 0;
3243*4882a593Smuzhiyun _tcpm_pd_hard_reset(port);
3244*4882a593Smuzhiyun }
3245*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset);
3246*4882a593Smuzhiyun
tcpm_init(struct tcpm_port * port)3247*4882a593Smuzhiyun static void tcpm_init(struct tcpm_port *port)
3248*4882a593Smuzhiyun {
3249*4882a593Smuzhiyun enum typec_cc_status cc1, cc2;
3250*4882a593Smuzhiyun
3251*4882a593Smuzhiyun port->tcpc->init(port->tcpc);
3252*4882a593Smuzhiyun
3253*4882a593Smuzhiyun tcpm_reset_port(port);
3254*4882a593Smuzhiyun
3255*4882a593Smuzhiyun /*
3256*4882a593Smuzhiyun * XXX
3257*4882a593Smuzhiyun * Should possibly wait for VBUS to settle if it was enabled locally
3258*4882a593Smuzhiyun * since tcpm_reset_port() will disable VBUS.
3259*4882a593Smuzhiyun */
3260*4882a593Smuzhiyun port->vbus_present = port->tcpc->get_vbus(port->tcpc);
3261*4882a593Smuzhiyun if (port->vbus_present)
3262*4882a593Smuzhiyun port->vbus_never_low = true;
3263*4882a593Smuzhiyun
3264*4882a593Smuzhiyun /*
3265*4882a593Smuzhiyun * 1. When vbus_present is true, voltage on VBUS is already at VSAFE5V.
3266*4882a593Smuzhiyun * So implicitly vbus_vsafe0v = false.
3267*4882a593Smuzhiyun *
3268*4882a593Smuzhiyun * 2. When vbus_present is false and TCPC does NOT support querying
3269*4882a593Smuzhiyun * vsafe0v status, then, it's best to assume vbus is at VSAFE0V i.e.
3270*4882a593Smuzhiyun * vbus_vsafe0v is true.
3271*4882a593Smuzhiyun *
3272*4882a593Smuzhiyun * 3. When vbus_present is false and TCPC does support querying vsafe0v,
3273*4882a593Smuzhiyun * then, query tcpc for vsafe0v status.
3274*4882a593Smuzhiyun */
3275*4882a593Smuzhiyun if (port->vbus_present)
3276*4882a593Smuzhiyun port->vbus_vsafe0v = false;
3277*4882a593Smuzhiyun else if (!port->tcpc->is_vbus_vsafe0v)
3278*4882a593Smuzhiyun port->vbus_vsafe0v = true;
3279*4882a593Smuzhiyun else
3280*4882a593Smuzhiyun port->vbus_vsafe0v = port->tcpc->is_vbus_vsafe0v(port->tcpc);
3281*4882a593Smuzhiyun
3282*4882a593Smuzhiyun tcpm_set_state(port, tcpm_default_state(port), 0);
3283*4882a593Smuzhiyun
3284*4882a593Smuzhiyun if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0)
3285*4882a593Smuzhiyun _tcpm_cc_change(port, cc1, cc2);
3286*4882a593Smuzhiyun }
3287*4882a593Smuzhiyun
tcpm_tcpc_reset(struct tcpm_port * port)3288*4882a593Smuzhiyun void tcpm_tcpc_reset(struct tcpm_port *port)
3289*4882a593Smuzhiyun {
3290*4882a593Smuzhiyun mutex_lock(&port->lock);
3291*4882a593Smuzhiyun /* XXX: Maintain PD connection if possible? */
3292*4882a593Smuzhiyun tcpm_init(port);
3293*4882a593Smuzhiyun mutex_unlock(&port->lock);
3294*4882a593Smuzhiyun }
3295*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_tcpc_reset);
3296*4882a593Smuzhiyun
tcpm_fw_get_caps(struct tcpm_port * port)3297*4882a593Smuzhiyun static int tcpm_fw_get_caps(struct tcpm_port *port)
3298*4882a593Smuzhiyun {
3299*4882a593Smuzhiyun const char *cap_str;
3300*4882a593Smuzhiyun ofnode node = port->tcpc->connector_node;
3301*4882a593Smuzhiyun int ret;
3302*4882a593Smuzhiyun u32 mw, frs_current;
3303*4882a593Smuzhiyun
3304*4882a593Smuzhiyun #if 0
3305*4882a593Smuzhiyun /* USB data support is optional */
3306*4882a593Smuzhiyun cap_str = ofnode_read_string(node, "data-role");
3307*4882a593Smuzhiyun if (cap_str) {
3308*4882a593Smuzhiyun ret = typec_find_port_data_role(cap_str);
3309*4882a593Smuzhiyun if (ret < 0)
3310*4882a593Smuzhiyun return ret;
3311*4882a593Smuzhiyun port->typec_caps.data = ret;
3312*4882a593Smuzhiyun }
3313*4882a593Smuzhiyun #endif
3314*4882a593Smuzhiyun
3315*4882a593Smuzhiyun cap_str = ofnode_read_string(node, "power-role");
3316*4882a593Smuzhiyun if (!cap_str) {
3317*4882a593Smuzhiyun return -EINVAL;
3318*4882a593Smuzhiyun } else {
3319*4882a593Smuzhiyun if (!strcmp("dual", cap_str))
3320*4882a593Smuzhiyun port->typec_caps.type = TYPEC_PORT_DRP;
3321*4882a593Smuzhiyun else if (!strcmp("source", cap_str))
3322*4882a593Smuzhiyun port->typec_caps.type = TYPEC_PORT_SRC;
3323*4882a593Smuzhiyun else if (!strcmp("sink", cap_str))
3324*4882a593Smuzhiyun port->typec_caps.type = TYPEC_PORT_SNK;
3325*4882a593Smuzhiyun else
3326*4882a593Smuzhiyun return EINVAL;
3327*4882a593Smuzhiyun }
3328*4882a593Smuzhiyun
3329*4882a593Smuzhiyun port->port_type = port->typec_caps.type;
3330*4882a593Smuzhiyun
3331*4882a593Smuzhiyun port->slow_charger_loop = ofnode_read_bool(node, "slow-charger-loop");
3332*4882a593Smuzhiyun if (port->port_type == TYPEC_PORT_SNK)
3333*4882a593Smuzhiyun goto sink;
3334*4882a593Smuzhiyun
3335*4882a593Smuzhiyun /* Get source pdos */
3336*4882a593Smuzhiyun ret = ofnode_read_size(node, "source-pdos") / sizeof(u32);
3337*4882a593Smuzhiyun if (ret <= 0)
3338*4882a593Smuzhiyun return -EINVAL;
3339*4882a593Smuzhiyun
3340*4882a593Smuzhiyun port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS);
3341*4882a593Smuzhiyun ret = ofnode_read_u32_array(node, "source-pdos",
3342*4882a593Smuzhiyun port->src_pdo, port->nr_src_pdo);
3343*4882a593Smuzhiyun if (ret || tcpm_validate_caps(port, port->src_pdo,
3344*4882a593Smuzhiyun port->nr_src_pdo))
3345*4882a593Smuzhiyun return -EINVAL;
3346*4882a593Smuzhiyun
3347*4882a593Smuzhiyun if (port->port_type == TYPEC_PORT_SRC)
3348*4882a593Smuzhiyun return 0;
3349*4882a593Smuzhiyun
3350*4882a593Smuzhiyun /* Get the preferred power role for DRP */
3351*4882a593Smuzhiyun cap_str = ofnode_read_string(node, "try-power-role");
3352*4882a593Smuzhiyun if (!cap_str) {
3353*4882a593Smuzhiyun return -EINVAL;
3354*4882a593Smuzhiyun } else {
3355*4882a593Smuzhiyun if (!strcmp("sink", cap_str))
3356*4882a593Smuzhiyun port->typec_caps.prefer_role = TYPEC_SINK;
3357*4882a593Smuzhiyun else if (!strcmp("source", cap_str))
3358*4882a593Smuzhiyun port->typec_caps.prefer_role = TYPEC_SOURCE;
3359*4882a593Smuzhiyun else
3360*4882a593Smuzhiyun return -EINVAL;
3361*4882a593Smuzhiyun }
3362*4882a593Smuzhiyun if (port->typec_caps.prefer_role < 0)
3363*4882a593Smuzhiyun return -EINVAL;
3364*4882a593Smuzhiyun sink:
3365*4882a593Smuzhiyun /* Get sink pdos */
3366*4882a593Smuzhiyun ret = ofnode_read_size(node, "sink-pdos") / sizeof(u32);
3367*4882a593Smuzhiyun if (ret <= 0)
3368*4882a593Smuzhiyun return -EINVAL;
3369*4882a593Smuzhiyun
3370*4882a593Smuzhiyun port->nr_snk_pdo = min(ret, PDO_MAX_OBJECTS);
3371*4882a593Smuzhiyun ret = ofnode_read_u32_array(node, "sink-pdos",
3372*4882a593Smuzhiyun port->snk_pdo, port->nr_snk_pdo);
3373*4882a593Smuzhiyun if (ret || tcpm_validate_caps(port, port->snk_pdo,
3374*4882a593Smuzhiyun port->nr_snk_pdo))
3375*4882a593Smuzhiyun return -EINVAL;
3376*4882a593Smuzhiyun
3377*4882a593Smuzhiyun if (ofnode_read_u32_array(node, "op-sink-microwatt", &mw, 1))
3378*4882a593Smuzhiyun return -EINVAL;
3379*4882a593Smuzhiyun port->operating_snk_mw = mw / 1000;
3380*4882a593Smuzhiyun
3381*4882a593Smuzhiyun port->self_powered = ofnode_read_bool(node, "self-powered");
3382*4882a593Smuzhiyun
3383*4882a593Smuzhiyun /* FRS can only be supported by DRP ports */
3384*4882a593Smuzhiyun if (port->port_type == TYPEC_PORT_DRP) {
3385*4882a593Smuzhiyun ret = ofnode_read_u32_array(node, "new-source-frs-typec-current",
3386*4882a593Smuzhiyun &frs_current, 1);
3387*4882a593Smuzhiyun if (ret >= 0 && frs_current <= FRS_5V_3A)
3388*4882a593Smuzhiyun port->new_source_frs_current = frs_current;
3389*4882a593Smuzhiyun }
3390*4882a593Smuzhiyun
3391*4882a593Smuzhiyun /* sink-vdos is optional */
3392*4882a593Smuzhiyun ret = ofnode_read_size(node, "sink-vdos") / sizeof(u32);
3393*4882a593Smuzhiyun if (ret < 0)
3394*4882a593Smuzhiyun ret = 0;
3395*4882a593Smuzhiyun
3396*4882a593Smuzhiyun port->nr_snk_vdo = min(ret, VDO_MAX_OBJECTS);
3397*4882a593Smuzhiyun if (port->nr_snk_vdo) {
3398*4882a593Smuzhiyun ret = ofnode_read_u32_array(node, "sink-vdos",
3399*4882a593Smuzhiyun port->snk_vdo, port->nr_snk_vdo);
3400*4882a593Smuzhiyun if (ret)
3401*4882a593Smuzhiyun return ret;
3402*4882a593Smuzhiyun }
3403*4882a593Smuzhiyun
3404*4882a593Smuzhiyun /* If sink-vdos is found, sink-vdos-v1 is expected for backward compatibility. */
3405*4882a593Smuzhiyun if (port->nr_snk_vdo) {
3406*4882a593Smuzhiyun ret = ofnode_read_size(node, "sink-vdos-v1") / sizeof(u32);
3407*4882a593Smuzhiyun if (ret < 0)
3408*4882a593Smuzhiyun return ret;
3409*4882a593Smuzhiyun else if (ret == 0)
3410*4882a593Smuzhiyun return -ENODATA;
3411*4882a593Smuzhiyun
3412*4882a593Smuzhiyun port->nr_snk_vdo_v1 = min(ret, VDO_MAX_OBJECTS);
3413*4882a593Smuzhiyun ret = ofnode_read_u32_array(node, "sink-vdos-v1",
3414*4882a593Smuzhiyun port->snk_vdo_v1,
3415*4882a593Smuzhiyun port->nr_snk_vdo_v1);
3416*4882a593Smuzhiyun if (ret)
3417*4882a593Smuzhiyun return ret;
3418*4882a593Smuzhiyun }
3419*4882a593Smuzhiyun
3420*4882a593Smuzhiyun return 0;
3421*4882a593Smuzhiyun }
3422*4882a593Smuzhiyun
tcpm_port_init(struct udevice * dev,struct tcpc_dev * tcpc)3423*4882a593Smuzhiyun struct tcpm_port *tcpm_port_init(struct udevice *dev, struct tcpc_dev *tcpc)
3424*4882a593Smuzhiyun {
3425*4882a593Smuzhiyun struct tcpm_port *port;
3426*4882a593Smuzhiyun int err;
3427*4882a593Smuzhiyun
3428*4882a593Smuzhiyun if (!dev || !tcpc ||
3429*4882a593Smuzhiyun !tcpc->get_vbus || !tcpc->set_cc || !tcpc->get_cc ||
3430*4882a593Smuzhiyun !tcpc->set_polarity || !tcpc->set_vconn || !tcpc->set_vbus ||
3431*4882a593Smuzhiyun !tcpc->set_pd_rx || !tcpc->set_roles || !tcpc->pd_transmit)
3432*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
3433*4882a593Smuzhiyun
3434*4882a593Smuzhiyun port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
3435*4882a593Smuzhiyun if (!port)
3436*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
3437*4882a593Smuzhiyun
3438*4882a593Smuzhiyun port->dev = dev;
3439*4882a593Smuzhiyun port->tcpc = tcpc;
3440*4882a593Smuzhiyun
3441*4882a593Smuzhiyun err = tcpm_fw_get_caps(port);
3442*4882a593Smuzhiyun if (err < 0) {
3443*4882a593Smuzhiyun printf("%s: please check the dts config of %s node(%d)\n",
3444*4882a593Smuzhiyun __func__, dev_read_name(dev), err);
3445*4882a593Smuzhiyun return ERR_PTR(err);
3446*4882a593Smuzhiyun }
3447*4882a593Smuzhiyun
3448*4882a593Smuzhiyun port->try_role = port->typec_caps.prefer_role;
3449*4882a593Smuzhiyun
3450*4882a593Smuzhiyun port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */
3451*4882a593Smuzhiyun port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */
3452*4882a593Smuzhiyun port->typec_caps.svdm_version = SVDM_VER_2_0;
3453*4882a593Smuzhiyun port->typec_caps.driver_data = port;
3454*4882a593Smuzhiyun port->typec_caps.orientation_aware = 1;
3455*4882a593Smuzhiyun
3456*4882a593Smuzhiyun port->port_type = port->typec_caps.type;
3457*4882a593Smuzhiyun
3458*4882a593Smuzhiyun tcpm_init(port);
3459*4882a593Smuzhiyun
3460*4882a593Smuzhiyun printf("%s: init finished\n", dev_read_name(dev));
3461*4882a593Smuzhiyun
3462*4882a593Smuzhiyun return port;
3463*4882a593Smuzhiyun }
3464*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_port_init);
3465*4882a593Smuzhiyun
tcpm_poll_event(struct tcpm_port * port)3466*4882a593Smuzhiyun void tcpm_poll_event(struct tcpm_port *port)
3467*4882a593Smuzhiyun {
3468*4882a593Smuzhiyun if (!port->tcpc->get_vbus(port->tcpc))
3469*4882a593Smuzhiyun return ;
3470*4882a593Smuzhiyun
3471*4882a593Smuzhiyun while (port->poll_event_cnt < TCPM_POLL_EVENT_TIME_OUT) {
3472*4882a593Smuzhiyun if (!port->wait_dr_swap_Message &&
3473*4882a593Smuzhiyun ((port->state == SNK_READY) ||
3474*4882a593Smuzhiyun (port->state == SRC_READY) ||
3475*4882a593Smuzhiyun (port->state == DEBUG_ACC_ATTACHED) ||
3476*4882a593Smuzhiyun (port->state == AUDIO_ACC_ATTACHED)))
3477*4882a593Smuzhiyun break;
3478*4882a593Smuzhiyun
3479*4882a593Smuzhiyun port->tcpc->poll_event(port->tcpc);
3480*4882a593Smuzhiyun port->poll_event_cnt++;
3481*4882a593Smuzhiyun udelay(500);
3482*4882a593Smuzhiyun }
3483*4882a593Smuzhiyun
3484*4882a593Smuzhiyun /*
3485*4882a593Smuzhiyun * At this time, call the callback function of the respective pd chip
3486*4882a593Smuzhiyun * to enter the low-power mode. In order to reduce the time spent on
3487*4882a593Smuzhiyun * the PD chip driver as much as possible, the tcpm framework does not
3488*4882a593Smuzhiyun * fully process the communication initiated by the device,so it should
3489*4882a593Smuzhiyun * be noted that we can disable the internal oscillator, etc., but do
3490*4882a593Smuzhiyun * not turn off the power of the transceiver module, otherwise the
3491*4882a593Smuzhiyun * self-powered Type-C device will initiate a Message(eg: self-powered
3492*4882a593Smuzhiyun * Type-C hub initiates a SINK capability request(PD_CTRL_GET_SINK_CAP))
3493*4882a593Smuzhiyun * and the pd chip cannot reply to GoodCRC, causing the self-powered Type-C
3494*4882a593Smuzhiyun * device to switch vbus to vSafe5v, or even turn off vbus.
3495*4882a593Smuzhiyun */
3496*4882a593Smuzhiyun if (port->tcpc->enter_low_power_mode) {
3497*4882a593Smuzhiyun if (port->tcpc->enter_low_power_mode(port->tcpc,
3498*4882a593Smuzhiyun port->attached,
3499*4882a593Smuzhiyun port->pd_capable))
3500*4882a593Smuzhiyun printf("failed to enter low power\n");
3501*4882a593Smuzhiyun else
3502*4882a593Smuzhiyun printf("PD chip enter low power mode\n");
3503*4882a593Smuzhiyun }
3504*4882a593Smuzhiyun }
3505*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_poll_event);
3506*4882a593Smuzhiyun
tcpm_get_voltage(struct tcpm_port * port)3507*4882a593Smuzhiyun int tcpm_get_voltage(struct tcpm_port *port)
3508*4882a593Smuzhiyun {
3509*4882a593Smuzhiyun return port->supply_voltage * 1000;
3510*4882a593Smuzhiyun }
3511*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_get_voltage);
3512*4882a593Smuzhiyun
tcpm_get_current(struct tcpm_port * port)3513*4882a593Smuzhiyun int tcpm_get_current(struct tcpm_port *port)
3514*4882a593Smuzhiyun {
3515*4882a593Smuzhiyun return port->current_limit * 1000;
3516*4882a593Smuzhiyun }
3517*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_get_voltage);
3518*4882a593Smuzhiyun
tcpm_get_online(struct tcpm_port * port)3519*4882a593Smuzhiyun int tcpm_get_online(struct tcpm_port *port)
3520*4882a593Smuzhiyun {
3521*4882a593Smuzhiyun if (port->state == SNK_READY)
3522*4882a593Smuzhiyun return 1;
3523*4882a593Smuzhiyun else
3524*4882a593Smuzhiyun return 0;
3525*4882a593Smuzhiyun }
3526*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_get_online);
3527*4882a593Smuzhiyun
tcpm_uninit_port(struct tcpm_port * port)3528*4882a593Smuzhiyun void tcpm_uninit_port(struct tcpm_port *port)
3529*4882a593Smuzhiyun {
3530*4882a593Smuzhiyun tcpm_reset_port(port);
3531*4882a593Smuzhiyun }
3532*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tcpm_unregister_port);
3533