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