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