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