1 /*
2  * Copyright (c) 2022 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_snk_states_internal.h"
12 #include "usbc_tc_common_internal.h"
13 #include <zephyr/drivers/usb_c/usbc_ppc.h>
14 
15 /**
16  * @brief Sink power sub states. Only called if a PD contract is not in place
17  */
sink_power_sub_states(const struct device * dev)18 static void sink_power_sub_states(const struct device *dev)
19 {
20 	struct usbc_port_data *data = dev->data;
21 	enum tc_cc_voltage_state cc;
22 	enum tc_cc_voltage_state new_cc_voltage;
23 	enum usbc_policy_check_t dpm_pwr_change_notify;
24 	struct tc_sm_t *tc = data->tc;
25 
26 	/* Get the active CC line */
27 	cc = tc->cc_polarity ? tc->cc2 : tc->cc1;
28 
29 	if (cc == TC_CC_VOLT_RP_DEF) {
30 		/*
31 		 * This sub-state supports Sinks consuming current within the
32 		 * lowest range (default) of Source-supplied current.
33 		 */
34 		new_cc_voltage = TC_CC_VOLT_RP_DEF;
35 		dpm_pwr_change_notify = POWER_CHANGE_DEF;
36 	} else if (cc == TC_CC_VOLT_RP_1A5) {
37 		/*
38 		 * This sub-state supports Sinks consuming current within the
39 		 * two lower ranges (default and 1.5 A) of Source-supplied
40 		 * current.
41 		 */
42 		new_cc_voltage = TC_CC_VOLT_RP_1A5;
43 		dpm_pwr_change_notify = POWER_CHANGE_1A5;
44 	} else if (cc == TC_CC_VOLT_RP_3A0) {
45 		/*
46 		 * This sub-state supports Sinks consuming current within all
47 		 * three ranges (default, 1.5 A and 3.0 A) of Source-supplied
48 		 * current.
49 		 */
50 		new_cc_voltage = TC_CC_VOLT_RP_3A0;
51 		dpm_pwr_change_notify = POWER_CHANGE_3A0;
52 	} else {
53 		/* Disconnect detected */
54 		new_cc_voltage = TC_CC_VOLT_OPEN;
55 		dpm_pwr_change_notify = POWER_CHANGE_0A0;
56 	}
57 
58 	/* Debounce the Rp state */
59 	if (new_cc_voltage != tc->cc_voltage) {
60 		tc->cc_voltage = new_cc_voltage;
61 		atomic_set_bit(&tc->flags, TC_FLAGS_RP_SUBSTATE_CHANGE);
62 		usbc_timer_start(&tc->tc_t_rp_value_change);
63 	}
64 
65 	/* Wait for Rp debounce */
66 	if (usbc_timer_expired(&tc->tc_t_rp_value_change) == false) {
67 		return;
68 	}
69 
70 	/* Notify DPM of sink sub-state power change */
71 	if (atomic_test_and_clear_bit(&tc->flags, TC_FLAGS_RP_SUBSTATE_CHANGE)) {
72 		if (data->policy_cb_notify) {
73 			data->policy_cb_notify(dev, dpm_pwr_change_notify);
74 		}
75 	}
76 }
77 
78 /**
79  * @brief Unattached.SNK Entry
80  */
tc_unattached_snk_entry(void * obj)81 void tc_unattached_snk_entry(void *obj)
82 {
83 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
84 
85 	LOG_INF("Unattached.SNK");
86 
87 	/*
88 	 * Allow the state machine to immediately check the state of CC lines and go into
89 	 * Attach.Wait state in case the Rp value is detected on the CC lines
90 	 */
91 	usbc_bypass_next_sleep(tc->dev);
92 }
93 
94 /**
95  * @brief Unattached.SNK Run
96  */
tc_unattached_snk_run(void * obj)97 void tc_unattached_snk_run(void *obj)
98 {
99 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
100 	const struct device *dev = tc->dev;
101 
102 	/*
103 	 * Transition to AttachWait.SNK when the SNK.Rp state is present
104 	 * on at least one of its CC pins.
105 	 */
106 	if (tcpc_is_cc_rp(tc->cc1) || tcpc_is_cc_rp(tc->cc2)) {
107 		tc_set_state(dev, TC_ATTACH_WAIT_SNK_STATE);
108 	}
109 }
110 
111 /**
112  * @brief AttachWait.SNK Entry
113  */
tc_attach_wait_snk_entry(void * obj)114 void tc_attach_wait_snk_entry(void *obj)
115 {
116 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
117 
118 	LOG_INF("AttachWait.SNK");
119 
120 	tc->cc_state = TC_CC_NONE;
121 
122 	/*
123 	 * Allow the debounce timers to start immediately without additional delay added
124 	 * by going into sleep
125 	 */
126 	usbc_bypass_next_sleep(tc->dev);
127 }
128 
129 /**
130  * @brief AttachWait.SNK Run
131  */
tc_attach_wait_snk_run(void * obj)132 void tc_attach_wait_snk_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 	bool vbus_present;
140 
141 	if (tcpc_is_cc_rp(tc->cc1) || tcpc_is_cc_rp(tc->cc2)) {
142 		new_cc_state = TC_CC_DFP_ATTACHED;
143 	} else {
144 		new_cc_state = TC_CC_NONE;
145 	}
146 
147 	/* Debounce the cc state */
148 	if (new_cc_state != tc->cc_state) {
149 		usbc_timer_start(&tc->tc_t_cc_debounce);
150 		tc->cc_state = new_cc_state;
151 	}
152 
153 	/* Wait for CC debounce */
154 	if (usbc_timer_running(&tc->tc_t_cc_debounce) &&
155 	    usbc_timer_expired(&tc->tc_t_cc_debounce) == false) {
156 		if (CONFIG_USBC_STATE_MACHINE_CYCLE_TIME >= TC_T_CC_DEBOUNCE_MIN_MS) {
157 			/* Make sure the debounce time won't be longer than specified */
158 			usbc_bypass_next_sleep(tc->dev);
159 		}
160 
161 		return;
162 	}
163 
164 	/* Transition to UnAttached.SNK if CC lines are open */
165 	if (new_cc_state == TC_CC_NONE) {
166 		tc_set_state(dev, TC_UNATTACHED_SNK_STATE);
167 	}
168 
169 	/*
170 	 * The port shall transition to Attached.SNK after the state of only
171 	 * one of the CC1 or CC2 pins is SNK.Rp for at least tCCDebounce and
172 	 * VBUS is detected.
173 	 */
174 	vbus_present = usbc_vbus_check_level(vbus, TC_VBUS_PRESENT);
175 
176 	if (vbus_present) {
177 		tc_set_state(dev, TC_ATTACHED_SNK_STATE);
178 	}
179 
180 	/*
181 	 * In case of no VBUS present, this call prevents going into the sleep and allows for
182 	 * faster VBUS detection. In case of VBUS present, allows for immediate execution of logic
183 	 * from new state.
184 	 */
185 	usbc_bypass_next_sleep(tc->dev);
186 }
187 
tc_attach_wait_snk_exit(void * obj)188 void tc_attach_wait_snk_exit(void *obj)
189 {
190 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
191 
192 	usbc_timer_stop(&tc->tc_t_cc_debounce);
193 }
194 
195 /**
196  * @brief Attached.SNK Entry
197  */
tc_attached_snk_entry(void * obj)198 void tc_attached_snk_entry(void *obj)
199 {
200 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
201 	const struct device *dev = tc->dev;
202 	struct usbc_port_data *data = dev->data;
203 	const struct device *tcpc = data->tcpc;
204 	int ret;
205 
206 	LOG_INF("Attached.SNK");
207 
208 	/* Set CC polarity */
209 	ret = tcpc_set_cc_polarity(tcpc, tc->cc_polarity);
210 	if (ret != 0) {
211 		LOG_ERR("Couldn't set CC polarity to %d: %d", tc->cc_polarity, ret);
212 		tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
213 		return;
214 	}
215 
216 	/* Enable PD */
217 	tc_pd_enable(dev, true);
218 
219 	/* Enable sink path for the PPC */
220 	if (data->ppc != NULL) {
221 		ret = ppc_set_snk_ctrl(data->ppc, true);
222 		if (ret != 0 && ret != -ENOTSUP) {
223 			LOG_ERR("Couldn't enable PPC sink path: %d", ret);
224 		}
225 	}
226 }
227 
228 /**
229  * @brief Attached.SNK and DebugAccessory.SNK Run
230  */
tc_attached_snk_run(void * obj)231 void tc_attached_snk_run(void *obj)
232 {
233 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
234 	const struct device *dev = tc->dev;
235 	struct usbc_port_data *data = dev->data;
236 	const struct device *vbus = data->vbus;
237 
238 	/* Detach detection */
239 	if (usbc_vbus_check_level(vbus, TC_VBUS_PRESENT) == false) {
240 		tc_set_state(dev, TC_UNATTACHED_SNK_STATE);
241 		return;
242 	}
243 
244 	/* Run Sink Power Sub-State if not in an explicit contract */
245 	if (pe_is_explicit_contract(dev) == false) {
246 		sink_power_sub_states(dev);
247 	}
248 }
249 
250 /**
251  * @brief Attached.SNK and DebugAccessory.SNK Exit
252  */
tc_attached_snk_exit(void * obj)253 void tc_attached_snk_exit(void *obj)
254 {
255 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
256 	const struct device *dev = tc->dev;
257 	struct usbc_port_data *data = dev->data;
258 	int ret;
259 
260 	/* Disable PD */
261 	tc_pd_enable(dev, false);
262 
263 	/* Disable sink path for the PPC */
264 	if (data->ppc != NULL) {
265 		ret = ppc_set_snk_ctrl(data->ppc, false);
266 		if (ret != 0 && ret != -ENOTSUP) {
267 			LOG_ERR("Couldn't disable PPC sink path: %d", ret);
268 		}
269 	}
270 }
271 
272 /**
273  * @brief Rd on CC lines Entry
274  */
tc_cc_rd_entry(void * obj)275 void tc_cc_rd_entry(void *obj)
276 {
277 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
278 	const struct device *dev = tc->dev;
279 	struct usbc_port_data *data = dev->data;
280 	const struct device *tcpc = data->tcpc;
281 	int ret;
282 
283 	ret = tcpc_set_cc(tcpc, TC_CC_RD);
284 	if (ret != 0) {
285 		LOG_ERR("Couldn't set CC lines to Rd: %d", ret);
286 		tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
287 	}
288 }
289