1 /*
2 * Copyright (c) 2023 The Chromium OS Authors
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_DECLARE(usbc_stack, CONFIG_USBC_STACK_LOG_LEVEL);
9
10 #include "usbc_stack.h"
11 #include "usbc_tc_src_states_internal.h"
12
13 /**
14 * @brief Spec. Release 1.3, section 4.5.2.2.7 Unattached.SRC State
15 *
16 * When in the Unattached.SRC state, the port is waiting to detect the
17 * presence of a Sink or an Accessory.
18 *
19 * Requirements:
20 * 1: The port shall not drive VBUS or VCONN.
21 * NOTE: Implemented in the tc_attached_src_exit
22 * function and initially set the tc_init function.
23 *
24 * 2: The port shall provide a separate Rp termination on the CC1 and
25 * CC2 pins.
26 * NOTE: Implemented in the tc_cc_rp super state.
27 */
28
tc_unattached_src_entry(void * obj)29 void tc_unattached_src_entry(void *obj)
30 {
31 LOG_INF("Unattached.SRC");
32 }
33
tc_unattached_src_run(void * obj)34 void tc_unattached_src_run(void *obj)
35 {
36 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
37 const struct device *dev = tc->dev;
38
39 /*
40 * Transition to AttachWait.SRC when:
41 * The SRC.Rd is detected on either CC1 or CC2 pin or
42 * SRC.Ra is detected on both CC1 and CC2 pins.
43 * NOTE: Audio Adapter Accessory Mode is not supported, so
44 * SRC.Ra will not be checked.
45 */
46 if (tcpc_is_cc_at_least_one_rd(tc->cc1, tc->cc2)) {
47 tc_set_state(dev, TC_ATTACH_WAIT_SRC_STATE);
48 }
49 }
50
51 /**
52 * @brief Spec. Release 1.3, section 4.5.2.2.6 UnattachedWait.SRC State
53 *
54 * When in the UnattachedWait.SRC state, the port is discharging the CC pin
55 * that was providing VCONN in the previous Attached.SRC state.
56 *
57 * Requirements:
58 * 1: The port shall not enable VBUS or VCONN.
59 * NOTE: Implemented in tc_attached_src_exit
60 *
61 * 2: The port shall continue to provide an Rp termination on the CC pin not
62 * being discharged.
63 * NOTE: Implemented in TC_CC_RP_SUPER_STATE super state.
64 *
65 * 3: The port shall provide an Rdch termination on the CC pin being
66 * discharged.
67 * NOTE: Implemented in tc_unattached_wait_src_entry
68 */
69
tc_unattached_wait_src_entry(void * obj)70 void tc_unattached_wait_src_entry(void *obj)
71 {
72 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
73 const struct device *dev = tc->dev;
74 struct usbc_port_data *data = dev->data;
75 const struct device *tcpc = data->tcpc;
76
77 LOG_INF("UnattachedWait.SRC");
78
79 /* Start discharging VCONN */
80 tcpc_vconn_discharge(tcpc, true);
81
82 /* Start VCONN off timer */
83 usbc_timer_start(&tc->tc_t_vconn_off);
84 }
85
tc_unattached_wait_src_run(void * obj)86 void tc_unattached_wait_src_run(void *obj)
87 {
88 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
89 const struct device *dev = tc->dev;
90
91 /* CC Debounce time should be enough time for VCONN to discharge */
92 if (usbc_timer_expired(&tc->tc_t_vconn_off)) {
93 tc_set_state(dev, TC_UNATTACHED_SRC_STATE);
94 }
95 }
96
tc_unattached_wait_src_exit(void * obj)97 void tc_unattached_wait_src_exit(void *obj)
98 {
99 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
100 const struct device *dev = tc->dev;
101 struct usbc_port_data *data = dev->data;
102 const struct device *tcpc = data->tcpc;
103
104 /* Stop discharging VCONN */
105 tcpc_vconn_discharge(tcpc, false);
106
107 /* Stop timer */
108 usbc_timer_stop(&tc->tc_t_vconn_off);
109 }
110
111 /**
112 * @brief Spec. Release 1.3, section 4.5.2.2.8 AttachWait.SRC State
113 *
114 * The AttachWait.SRC state is used to ensure that the state of both of
115 * the CC1 and CC2 pins is stable after a Sink is connected.
116 *
117 * Requirements:
118 * The requirements for this state are identical to Unattached.SRC.
119 */
120
tc_attach_wait_src_entry(void * obj)121 void tc_attach_wait_src_entry(void *obj)
122 {
123 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
124
125 LOG_INF("AttachWait.SRC");
126
127 /* Initialize the cc state to open */
128 tc->cc_state = TC_CC_NONE;
129 }
130
tc_attach_wait_src_run(void * obj)131 void tc_attach_wait_src_run(void *obj)
132 {
133 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
134 const struct device *dev = tc->dev;
135 struct usbc_port_data *data = dev->data;
136 const struct device *vbus = data->vbus;
137 enum tc_cc_states new_cc_state;
138
139 /* Is a connection detected? */
140 if (tcpc_is_cc_at_least_one_rd(tc->cc1, tc->cc2)) {
141 /* UFP attached */
142 new_cc_state = TC_CC_UFP_ATTACHED;
143 } else {
144 /* No UFP */
145 tc_set_state(dev, TC_UNATTACHED_SRC_STATE);
146 return;
147 }
148
149 /* Debounce the cc state */
150 if (new_cc_state != tc->cc_state) {
151 /* Start debouce timer */
152 usbc_timer_start(&tc->tc_t_cc_debounce);
153 tc->cc_state = new_cc_state;
154 }
155
156 /* Wait for CC debounce */
157 if (usbc_timer_running(&tc->tc_t_cc_debounce) &&
158 !usbc_timer_expired(&tc->tc_t_cc_debounce)) {
159 return;
160 }
161
162 /*
163 * The port shall transition to Attached.SRC when VBUS is at vSafe0V
164 * and the SRC.Rd state is detected on exactly one of the CC1 or CC2
165 * pins for at least tCCDebounce.
166 */
167 if (usbc_vbus_check_level(vbus, TC_VBUS_SAFE0V)) {
168 if (new_cc_state == TC_CC_UFP_ATTACHED) {
169 tc_set_state(dev, TC_ATTACHED_SRC_STATE);
170 }
171 }
172 }
173
tc_attach_wait_src_exit(void * obj)174 void tc_attach_wait_src_exit(void *obj)
175 {
176 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
177
178 /* Stop debounce timer */
179 usbc_timer_stop(&tc->tc_t_cc_debounce);
180 }
181
182 /**
183 * @brief Spec. Release 1.3, section 4.5.2.2.9 Attached.SRC State
184 *
185 * When in the Attached.SRC state, the port is attached and operating as a
186 * Source. When the port initially enters this state it is also operating
187 * as a DFP. Subsequently, the initial power and data roles can be changed
188 * using USB PD commands.
189 *
190 * Requirements:
191 * 1: If the port needs to determine the orientation of the connector, it
192 * shall do so only upon entry to the Attached.SRC state by detecting
193 * which of the CC1 or CC2 pins is connected through the
194 * cable, i.e., which CC pin is in the SRC.Rd state.
195 * NOTE: Implemented in tc_attached_src_entry.
196 *
197 * 2: If the port has entered this state from the AttachWait.SRC state,
198 * the SRC.Rd state will be on only one of the CC1 or CC2 pins. The
199 * port shall source current on this CC pin and monitor its state.
200 * NOTE: Implemented in the super state of AttachWait.SRC.
201 *
202 * 3: The port shall provide an Rp
203 * NOTE: Implemented in the super state of AttachWait.SRC.
204 *
205 * 5: The port shall supply VBUS current at the level it advertises on Rp.
206 * NOTE: Implemented in tc_attached_src_entry.
207 *
208 * 7: The port shall not initiate any USB PD communications until VBUS
209 * reaches vSafe5V.
210 * NOTE: Implemented in tc_attached_src_run.
211 *
212 * 8: The port may negotiate a USB PD PR_Swap, DR_Swap or VCONN_Swap.
213 * NOTE: Implemented in tc_attached_src_run.
214 *
215 * 9: If the port supplies VCONN, it shall do so within t_VCONN_ON.
216 * NOTE: Implemented in tc_attached_src_entry.
217 */
218
tc_attached_src_entry(void * obj)219 void tc_attached_src_entry(void *obj)
220 {
221 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
222 const struct device *dev = tc->dev;
223 struct usbc_port_data *data = dev->data;
224 const struct device *tcpc = data->tcpc;
225
226 LOG_INF("Attached.SRC");
227
228 /* Initial data role for source is DFP */
229 tcpc_set_roles(tcpc, TC_ROLE_SOURCE, TC_ROLE_DFP);
230
231 /* Set cc polarity */
232 tcpc_set_cc_polarity(tcpc, tc->cc_polarity);
233
234 /* Start sourcing VBUS */
235 if (data->policy_cb_src_en(dev, true) == 0) {
236 /* Start sourcing VCONN */
237 if (policy_check(dev, CHECK_VCONN_CONTROL)) {
238 if (tcpc_set_vconn(tcpc, true) == 0) {
239 atomic_set_bit(&tc->flags, TC_FLAGS_VCONN_ON);
240 } else {
241 LOG_ERR("VCONN can't be enabled\n");
242 }
243 }
244 } else {
245 LOG_ERR("Power Supply can't be enabled\n");
246 }
247
248 /* Enable PD */
249 tc_pd_enable(dev, true);
250 }
251
tc_attached_src_run(void * obj)252 void tc_attached_src_run(void *obj)
253 {
254 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
255 const struct device *dev = tc->dev;
256
257 /* Monitor for CC disconnection */
258 if (tcpc_is_cc_open(tc->cc1, tc->cc2)) {
259 /*
260 * A Source that is supplying VCONN or has yielded VCONN source
261 * responsibility to the Sink through USBPD VCONN_Swap messaging
262 * shall transition to UnattachedWait.SRC when the SRC.Open state
263 * is detected on the monitored CC pin. The Source shall detect
264 * the SRC.Open state within tSRCDisconnect, but should detect
265 * it as quickly as possible.
266 */
267 if (atomic_test_and_clear_bit(&tc->flags, TC_FLAGS_VCONN_ON)) {
268 tc_set_state(dev, TC_UNATTACHED_WAIT_SRC_STATE);
269 }
270 /*
271 * A Source that is not supplying VCONN and has not yielded
272 * VCONN responsibility to the Sink through USBPD VCONN_Swap
273 * messaging shall transition to Unattached.SRC when the
274 * SRC.Open state is detected on the monitored CC pin. The
275 * Source shall detect the SRC.Open state within tSRCDisconnect,
276 * but should detect it as quickly as possible.
277 */
278 else {
279 tc_set_state(dev, TC_UNATTACHED_SRC_STATE);
280 }
281 }
282 }
283
tc_attached_src_exit(void * obj)284 void tc_attached_src_exit(void *obj)
285 {
286 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
287 const struct device *dev = tc->dev;
288 struct usbc_port_data *data = dev->data;
289 const struct device *tcpc = data->tcpc;
290
291 __ASSERT(data->policy_cb_src_en != NULL,
292 "policy_cb_src_en must not be NULL");
293
294 /* Disable PD */
295 tc_pd_enable(dev, false);
296
297 /* Stop sourcing VBUS */
298 data->policy_cb_src_en(dev, false);
299
300 /* Stop sourcing VCONN */
301 tcpc_set_vconn(tcpc, false);
302 }
303
304 /**
305 * @brief This is a super state for Source States that
306 * requirement the Rp value placed on the CC lines.
307 */
tc_cc_rp_entry(void * obj)308 void tc_cc_rp_entry(void *obj)
309 {
310 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
311 const struct device *dev = tc->dev;
312 struct usbc_port_data *data = dev->data;
313 const struct device *tcpc = data->tcpc;
314 enum tc_rp_value rp = TC_RP_USB;
315
316 /*
317 * Get initial Rp value from Device Policy Manager or use
318 * default TC_RP_USB.
319 */
320 if (data->policy_cb_get_src_rp) {
321 data->policy_cb_get_src_rp(dev, &rp);
322 }
323
324 /* Select Rp value */
325 tcpc_select_rp_value(tcpc, rp);
326
327 /* Place Rp on CC lines */
328 tcpc_set_cc(tcpc, TC_CC_RP);
329 }
330