summaryrefslogtreecommitdiff
path: root/docs/usb-tcpmv2.md
blob: b09805177fcc451c498ee629dbffbd264573baa3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# EC USB-C Power Delivery TCPMv2 Overview

As the original USB-C Power Delivery (PD) solution for the ChromeOS Embedded
Controller has aged, it has grown to the point where it is difficult to add new
features and address bugs. A new PD stack (generally referred to as TCPMv2) has
been introduced to the codebase for use moving forward. It implements a layered,
state-based design which tracks more closely with the USB Type-C and USB PD
specifications.

[TOC]

## Enabling TCPMv2

Boards may enable TCPMv2 by adding the following defines:

*   `CONFIG_USB_PD_TCPMV2`Configures the board to use the new stack.
*   `CONFIG_USB_DRP_ACC_TRYSRC`: Configures the type of state machine to run (in
    this case, a DRP which performs Try.SRC behavior). Currently available are
    DRP and charge-through Vconn-powered device options
*   `CONFIG_USB_PD_DECODE_SOP`: Sets up messaging for SOP’ and SOP’’, which is
    strongly encouraged in the TCPMv2 code moving forward
*   `CONFIG_USB_PID 0x1234`: Sets the USB Product Identifier. This will be
    shared for all boards within one reference design, and new PIDs may be
    requested by sending an email to the ChromeOS FW Team.
*   `CONFIG_USB_PD_REV30`: The TCPMv2 stack defaults to PD2.0 operation but
    defining this macro enables PD3.0 functionality.

Other configurations to specify behaviors within the task still apply (ex.
`CONFIG_USB_PD_ALT_MODE_DFP` and `CONFIG_USB_PD_TCPC_LOW_POWER`).

## State Machine Framework

The basis of the TCPMv2 state machines is a generic state machine framework.
This framework can be found in common/usbc/usb\_sm.c. For each state, there may
be defined:

*   Entry: Called upon entering a state
*   Run: Called while steady in a state
*   Exit: Called upon leaving a state
*   Parent: Superstate. Enters, exits, and runs alongside the child state. Only
    enters and exits when transitioning between states which do not share the
    parent.

All fields are optional and may be set to NULL. A new state is transitioned to
with a call into set\_state(), which exits the old state and parents and enters
the new parents and state. States may be changed with set\_state() in any entry
or run function, but any call from an exit function is ignored since exit is
only called when a change is already in progress. While in a state, run\_state()
executes the run function for the current state and parents. If set\_state() is
called from either an entry function or a run function, the remaining run or
entry functions are stopped.

Below is a graphical example of what some states may look like. States 1 and 2
share Parent State 1, while State 3 has Parent State of 2.

![Example States](images/TCPMv2-ExampleStates.png "Example States")

Translated into code, this would be something like below (note it is not
necessary that the states be a part of an array, but the TCPMv2 code generally
organizes the states in this way):

```
static const struct usb_state test_states[] = {
        [PARENT_1] = {
                .entry = parent_1_entry,
                .run = parent_1_run,
                .exit = parent_1_exit,
        },
        [PARENT_2] = {
                .entry = parent_2_entry,
                .run = parent_2_run,
                .exit = parent_2_exit,
        },
        [STATE_1] = {
                .entry = state_1_entry,
                .run = state_1_run,
                .exit = state_1_exit,
                .parent = &test_states[PARENT_1],
        },
        [STATE_2] = {
                .entry = state_2_entry,
                .run = state_2_run,
                .exit = state_2_exit,
                .parent = &test_states[PARENT_1],
        },
        [STATE_3] = {
                .entry = state_3_entry,
                .run = state_3_run,
                .exit = state_3_exit,
                .parent = &test_states[PARENT_2],
        },
};
```

For this example, each test state is written simply to produce a print of its
function name. The two exceptions are:

*   parent\_1\_run() calls set\_state() into STATE\_2 when called a second time
*   state\_2\_entry() calls set\_state() into STATE\_3

Graphically, this is represented below:

![Example Transitions](images/TCPMv2-ExampleTransitions.png "Example state transitions and resulting called functions")

And the following code output is produced:

```
Calling run_state()
state_1_run
parent_1_run

Calling run_state()
state_1_run
state_1_run calling set_state() to state 2
state_1_exit
state_2_entry
state_2_entry calling set_state() to state 3
state_2_exit
parent_1_exit
parent_2_entry
state_3_entry

Calling run_state()
state_3_run
parent_2_run
```

## TCPMv2 PD Task

The TCPMv2 PD task is built upon state machines using the above framework and is
located in common/usbc/usbc\_task.c. It is separated into three layers which
track with the USB Type-C and USB PD specification states and run in a loop with
5 ms periods between executions. A graphical representation of these layers is
below.

![PD Task Loop](images/TCPMv2-TaskLoop.png "PD task loop state machine calls")

The task is designed such that the Type-C (TC) layer could run independently for
any application which doesn’t wish to enable PD messaging. Boards define their
appropriate Policy Engine (PE) and TC state machines through their selection of
a CONFIG\_USB\_\* define, with current options supporting both Dual-Role Ports
(DRPs) and Charge-Through Vconn-Powered Device (CTVPD). All boards use the same
Protocol Layer (PRL) code.

## TCPMv2 Layers

### Overview

The three state machines mentioned above interact with each other and the EC
drivers in order to orchestrate all Type-C connection behavior. Graphically,
they are represented below.

![PD Task Layers](images/TCPMv2-TaskLayers.png "PD task layer interactions")

Layers communicate with each other using atomic operations on flags and shared
buffers. Functions calling into each layer are clearly named to indicate the
layer they are acting on, and anything calling into the PD task should be doing
so through pd\_\* named functions.

Some specific examples of how this communication works between layers is below.
If a port partner sends in a Vconn\_swap request, then:

*   PRL will communicate that a message was received to the PE layer through
    pe\_message\_received(), which sets PE\_FLAGS\_MSG\_RECEIVED and indicates
    the receive buffer has a message
*   PE asks with the TC whether the board is sourcing Vconn with
    tc\_is\_vconn\_src() which checks TC\_FLAGS\_VCONN\_ON
*   PE tells the PRL to send an ACCEPT message to the port partner through
    prl\_send\_ctrl\_msg() which fills in shared message information and sets
    PRL\_FLAGS\_MSG\_XMIT
*   PRL lets the PE know that the message transmit was successful through
    pe\_message\_sent() which sets PE\_FLAGS\_TX\_COMPLETE
*   TC tells the PE layer that the Vconn swap completed with
    pe\_vconn\_swap\_complete() which sets PE\_FLAGS\_VCONN\_SWAP\_COMPLETE

### Type-C Layer

Defined in the USB Type-C specification, this layer is responsible for basic
connection. It takes care of setting the CC lines, detecting and debouncing the
partner CC lines, and performs most of the interactions needed with the PPC and
USB mux. Once the TC layer has gotten the connection to the point of being
Attached.SNK or Attached.SRC, it will enable the PRL and PE layers accordingly.

### Protocol Layer

A part of the USB PD specification, the protocol layer is responsible for the
actual sending and receiving of PD messages with the TCPCs. The layer is
actually composed of 4 separate state machines running one after the other.
These state machines are:

*   Chunked receive (RCH): passes messages up to the PE and requests chunks when
    chunking
*   Chunked transmit (TCH): receives messages from the PE and waits for chunk
    requests when chunking
*   Protocol transmit (PRL\_TX): passes messages to the TCPCs and handles PD 3.0
    collision avoidance
*   Protocol hard reset (PRL\_HR): responds to or transmits hard resets, resets
    PRL layer variables, notifies PE of hard reset receipt or sent

### Policy Engine Layer

The PE layer states are defined as a part of the USB PD specification. State
names are meant to track very closely with the specification so they can be
easily searchable and understood. The PE’s primary responsibility is to send and
process PD messages in order to implement the port’s policy.

### Device Policy Manager Layer

The USB PD specification defines some responsibilities of the DPM, e.g.
selecting _Source Capabilities_ to offer and alternate modes to enter. Broadly,
the DPM is supposed to make discretionary decisions about the behavior of the
PE, i.e. policy decisions. However, the spec is relatively silent on the
structure and scope of the DPM.

In TCPMv2, the DPM consists of a central code module in `usb_pd_dpm.c` and
several code modules for supported alternate or USB modes. The mode entry and
exit logic in the DPM takes the form of a state machine, and each mode has its
own state machine to control the specific mode entry and exit steps. The DPM
also contains logic to choose which Source PDO to offer in _Source
Capabilities_.

The DPM state machines do not currently use the USB state machine framework.
Additionally, there are policy decisions encoded in the lower layers and in
board code that ought to be made by the DPM. An effort to centralize policy
decisions and improve DPM code organization is ongoing.

## Alternate modes and USB4

TCPMv2 supports several modes of operation besides USB 3.2. To determine which
modes the port partner and cable support, the TCPM sends _SOP_ and _SOP'_
Vendor-Defined Messages, specifically _Discover Identity_, _Discover SVIDs_,
and _Discover Modes_. The responses to these messages list supported modes. The
AP may query this information via `EC_CMD_TYPEC_DISCOVERY`.

Based on the discovery responses, the DPM decides which mode to enter (if any)
and drives mode entry. If `CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY` is enabled, the
AP directs the DPM to enter and exit modes via `EC_CMD_TYPEC_CONTROL`.

### DisplayPort

DisplayPort alternate mode repurposes the SuperSpeed lanes as video data lanes,
all flowing from the host to the monitor. DP mode can use 2 lanes, allowing
simultaneous USB traffic on the cable, or it can use all 4 lanes, supporting
greater display bandwidth at the expense of USB.

The sequence to enter DP alternate mode (v. 1.4) is
*   The TCPM sends an SOP VDM _Enter Mode_ REQ with the DP VID.
*   The TCPM sends an SOP _DP Status_ VDM REQ.
*   The TCPM sends an SOP _DP Configure_ VDM REQ.
*   Display sends an SOP _Attention_ VDM REQ.

To exit DP alternate mode, the TCPM sends an SOP VDM _Exit Mode_ REQ.

`dp_alt_mode.c` and `usb_pd_alt_mode_dfp.c` contain the majority of the DP entry
and exit logic.

### Thunderbolt 3

TBT3 alternate mode repurposes all 4 SuperSpeed lanes for Thunderbolt 3, which
is itself a bidirectional tunneling protocol supporting various types of
hardware, most commonly docks.

The sequence to enter TBT3 alternate mode depends on the cable used:
*   If the cable supports TBT3, the TCPM sends an SOP' VDM _Enter Mode_ REQ with
    the Intel VID.
*   If the cable is also an active cable with SOP'' support, the TCPM sends an
    SOP'' VDM _Enter Mode_ REQ. (The TBT3 mode VDO may identify the cable as
    active for this purpose when the ID Header VDO does not.)
*   The TCPM sends an SOP VDM _Enter Mode_ REQ.

To exit TBT3 alternate mode, the TCPM sends VDM _Exit Mode_ REQs to SOP, SOP'',
and SOP', as appropriate.

`tbt_alt_mode.c` and `usb_pd_alt_mode_dfp.c` contain the TBT entry and exit
logic.

### USB4

USB4 is not an alternate mode, but it does repurpose the SuperSpeed lines (using
different signaling and routing than USB 3.2), so the TCPM must explicitly enter
USB4.

The sequence to enter USB4 is conceptually similar to that for TBT3:
*   If the cable supports USB4, the TCPM sends an SOP' _Enter USB_ message.
*   If the cable is also an active cable, the TCPM sends an SOP'' _Enter USB_.
*   The TCPM sends an SOP _Enter USB_.
Complicating matters, if the cable does not support USB4 but does support TBT3,
the TCPM may enter TBT3 for the cable as above, then enter USB4 for the port
partner.

To exit USB4 (returning to a USB 3.2 mux configuration), the TCPM performs a
_Data Reset_ sequence.

`usb_mode.c` and `usb_pd_alt_mode_dfp.c` contain the USB4 entry and exit logic.

## Best Practices

*   Always call return after set\_state(). Once the state has been changed,
    nothing further should be done in the current state.
*   Never call set\_state() from an exit function. The call will be ignored as
    there is already a state transition taking place.
*   Never call set\_state() from outside the PD task. The task may be waiting in
    any number of locations and the context should not change around it while it
    does so.
*   Always use flags to communicate between layers, and to communicate with the
    PD task from other tasks. Flags should be accessed through atomic
    operations.
*   Always use pd\_\* functions to access the PD task from other tasks.
*   Always write unit tests as code is added, to verify new code works and
    protect against regressions as development continues.