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