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 
14 /**
15  * @brief Sink power sub states. Only called if a PD contract is not in place
16  */
sink_power_sub_states(const struct device * dev)17 static void sink_power_sub_states(const struct device *dev)
18 {
19 	struct usbc_port_data *data = dev->data;
20 	enum tc_cc_voltage_state cc;
21 	enum tc_cc_voltage_state new_cc_voltage;
22 	enum usbc_policy_check_t dpm_pwr_change_notify;
23 	struct tc_sm_t *tc = data->tc;
24 
25 	/* Get the active CC line */
26 	cc = tc->cc_polarity ? tc->cc2 : tc->cc1;
27 
28 	if (cc == TC_CC_VOLT_RP_DEF) {
29 		/*
30 		 * This sub-state supports Sinks consuming current within the
31 		 * lowest range (default) of Source-supplied current.
32 		 */
33 		new_cc_voltage = TC_CC_VOLT_RP_DEF;
34 		dpm_pwr_change_notify = POWER_CHANGE_DEF;
35 	} else if (cc == TC_CC_VOLT_RP_1A5) {
36 		/*
37 		 * This sub-state supports Sinks consuming current within the
38 		 * two lower ranges (default and 1.5 A) of Source-supplied
39 		 * current.
40 		 */
41 		new_cc_voltage = TC_CC_VOLT_RP_1A5;
42 		dpm_pwr_change_notify = POWER_CHANGE_1A5;
43 	} else if (cc == TC_CC_VOLT_RP_3A0) {
44 		/*
45 		 * This sub-state supports Sinks consuming current within all
46 		 * three ranges (default, 1.5 A and 3.0 A) of Source-supplied
47 		 * current.
48 		 */
49 		new_cc_voltage = TC_CC_VOLT_RP_3A0;
50 		dpm_pwr_change_notify = POWER_CHANGE_3A0;
51 	} else {
52 		/* Disconnect detected */
53 		new_cc_voltage = TC_CC_VOLT_OPEN;
54 		dpm_pwr_change_notify = POWER_CHANGE_0A0;
55 	}
56 
57 	/* Debounce the Rp state */
58 	if (new_cc_voltage != tc->cc_voltage) {
59 		tc->cc_voltage = new_cc_voltage;
60 		atomic_set_bit(&tc->flags, TC_FLAGS_RP_SUBSTATE_CHANGE);
61 		usbc_timer_start(&tc->tc_t_rp_value_change);
62 	}
63 
64 	/* Wait for Rp debounce */
65 	if (usbc_timer_expired(&tc->tc_t_rp_value_change) == false) {
66 		return;
67 	}
68 
69 	/* Notify DPM of sink sub-state power change */
70 	if (atomic_test_and_clear_bit(&tc->flags, TC_FLAGS_RP_SUBSTATE_CHANGE)) {
71 		if (data->policy_cb_notify) {
72 			data->policy_cb_notify(dev, dpm_pwr_change_notify);
73 		}
74 	}
75 }
76 
77 /**
78  * @brief Unattached.SNK Entry
79  */
tc_unattached_snk_entry(void * obj)80 void tc_unattached_snk_entry(void *obj)
81 {
82 	LOG_INF("Unattached.SNK");
83 }
84 
85 /**
86  * @brief Unattached.SNK Run
87  */
tc_unattached_snk_run(void * obj)88 void tc_unattached_snk_run(void *obj)
89 {
90 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
91 	const struct device *dev = tc->dev;
92 
93 	/*
94 	 * Transition to AttachWait.SNK when the SNK.Rp state is present
95 	 * on at least one of its CC pins.
96 	 */
97 	if (tcpc_is_cc_rp(tc->cc1) || tcpc_is_cc_rp(tc->cc2)) {
98 		tc_set_state(dev, TC_ATTACH_WAIT_SNK_STATE);
99 	}
100 }
101 
102 /**
103  * @brief AttachWait.SNK Entry
104  */
tc_attach_wait_snk_entry(void * obj)105 void tc_attach_wait_snk_entry(void *obj)
106 {
107 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
108 
109 	LOG_INF("AttachWait.SNK");
110 
111 	tc->cc_state = TC_CC_NONE;
112 }
113 
114 /**
115  * @brief AttachWait.SNK Run
116  */
tc_attach_wait_snk_run(void * obj)117 void tc_attach_wait_snk_run(void *obj)
118 {
119 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
120 	const struct device *dev = tc->dev;
121 	struct usbc_port_data *data = dev->data;
122 	const struct device *vbus = data->vbus;
123 	enum tc_cc_states new_cc_state;
124 	bool vbus_present;
125 
126 	if (tcpc_is_cc_rp(tc->cc1) || tcpc_is_cc_rp(tc->cc2)) {
127 		new_cc_state = TC_CC_DFP_ATTACHED;
128 	} else {
129 		new_cc_state = TC_CC_NONE;
130 	}
131 
132 	/* Debounce the cc state */
133 	if (new_cc_state != tc->cc_state) {
134 		usbc_timer_start(&tc->tc_t_cc_debounce);
135 		tc->cc_state = new_cc_state;
136 	}
137 
138 	/* Wait for CC debounce */
139 	if (usbc_timer_running(&tc->tc_t_cc_debounce) &&
140 	    usbc_timer_expired(&tc->tc_t_cc_debounce) == false) {
141 		return;
142 	}
143 
144 	/* Transition to UnAttached.SNK if CC lines are open */
145 	if (new_cc_state == TC_CC_NONE) {
146 		tc_set_state(dev, TC_UNATTACHED_SNK_STATE);
147 	}
148 
149 	/*
150 	 * The port shall transition to Attached.SNK after the state of only
151 	 * one of the CC1 or CC2 pins is SNK.Rp for at least tCCDebounce and
152 	 * VBUS is detected.
153 	 */
154 	vbus_present = usbc_vbus_check_level(vbus, TC_VBUS_PRESENT);
155 
156 	if (vbus_present) {
157 		tc_set_state(dev, TC_ATTACHED_SNK_STATE);
158 	}
159 }
160 
tc_attach_wait_snk_exit(void * obj)161 void tc_attach_wait_snk_exit(void *obj)
162 {
163 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
164 
165 	usbc_timer_stop(&tc->tc_t_cc_debounce);
166 }
167 
168 /**
169  * @brief Attached.SNK Entry
170  */
tc_attached_snk_entry(void * obj)171 void tc_attached_snk_entry(void *obj)
172 {
173 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
174 	const struct device *dev = tc->dev;
175 	struct usbc_port_data *data = dev->data;
176 	const struct device *tcpc = data->tcpc;
177 
178 	LOG_INF("Attached.SNK");
179 
180 	/* Set CC polarity */
181 	tcpc_set_cc_polarity(tcpc, tc->cc_polarity);
182 
183 	/* Enable PD */
184 	tc_pd_enable(dev, true);
185 }
186 
187 /**
188  * @brief Attached.SNK and DebugAccessory.SNK Run
189  */
tc_attached_snk_run(void * obj)190 void tc_attached_snk_run(void *obj)
191 {
192 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
193 	const struct device *dev = tc->dev;
194 	struct usbc_port_data *data = dev->data;
195 	const struct device *vbus = data->vbus;
196 
197 	/* Detach detection */
198 	if (usbc_vbus_check_level(vbus, TC_VBUS_PRESENT) == false) {
199 		tc_set_state(dev, TC_UNATTACHED_SNK_STATE);
200 		return;
201 	}
202 
203 	/* Run Sink Power Sub-State if not in an explicit contract */
204 	if (pe_is_explicit_contract(dev) == false) {
205 		sink_power_sub_states(dev);
206 	}
207 }
208 
209 /**
210  * @brief Attached.SNK and DebugAccessory.SNK Exit
211  */
tc_attached_snk_exit(void * obj)212 void tc_attached_snk_exit(void *obj)
213 {
214 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
215 	const struct device *dev = tc->dev;
216 
217 	/* Disable PD */
218 	tc_pd_enable(dev, false);
219 }
220 
221 /**
222  * @brief Rd on CC lines Entry
223  */
tc_cc_rd_entry(void * obj)224 void tc_cc_rd_entry(void *obj)
225 {
226 	struct tc_sm_t *tc = (struct tc_sm_t *)obj;
227 	const struct device *dev = tc->dev;
228 	struct usbc_port_data *data = dev->data;
229 	const struct device *tcpc = data->tcpc;
230 
231 	tcpc_set_cc(tcpc, TC_CC_RD);
232 }
233