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_src_states_internal.h"
13 #include "usbc_tc_common_internal.h"
14 #include <zephyr/drivers/usb_c/usbc_ppc.h>
15
16 static const struct smf_state tc_states[TC_STATE_COUNT];
17 static int tc_init(const struct device *dev);
18
19 /**
20 * @brief Initializes the state machine and enters the Disabled state
21 */
tc_subsys_init(const struct device * dev)22 void tc_subsys_init(const struct device *dev)
23 {
24 struct usbc_port_data *data = dev->data;
25 struct tc_sm_t *tc = data->tc;
26
27 /* Save the port device object so states can access it */
28 tc->dev = dev;
29
30 /* Initialize the state machine */
31 smf_set_initial(SMF_CTX(tc), &tc_states[TC_DISABLED_STATE]);
32 }
33
34 /**
35 * @brief Runs the Type-C layer
36 */
tc_run(const struct device * dev,const int32_t dpm_request)37 void tc_run(const struct device *dev, const int32_t dpm_request)
38 {
39 struct usbc_port_data *data = dev->data;
40 const struct device *tcpc = data->tcpc;
41 struct tc_sm_t *tc = data->tc;
42 int ret;
43
44 /* These requests are implicitly set by the Device Policy Manager */
45 if (dpm_request == PRIV_PORT_REQUEST_START) {
46 data->tc_enabled = true;
47 } else if (dpm_request == PRIV_PORT_REQUEST_SUSPEND) {
48 data->tc_enabled = false;
49 tc_set_state(dev, TC_DISABLED_STATE);
50 }
51
52 switch (data->tc_sm_state) {
53 case SM_PAUSED:
54 if (data->tc_enabled == false) {
55 break;
56 }
57 /* fall through */
58 case SM_INIT:
59 /* Initialize the Type-C layer */
60 ret = tc_init(dev);
61 if (ret != 0 && ret != -EAGAIN) {
62 /* Transition to Disabled State */
63 LOG_ERR("Disabling the Type-C Layer");
64 data->tc_enabled = false;
65 tc_set_state(dev, TC_DISABLED_STATE);
66 }
67
68 if (ret != 0) {
69 break;
70 }
71
72 data->tc_sm_state = SM_RUN;
73 /* fall through */
74 case SM_RUN:
75 if (data->tc_enabled == false) {
76 tc_pd_enable(dev, false);
77 data->tc_sm_state = SM_PAUSED;
78 break;
79 }
80
81 /* Sample CC lines */
82 if (tcpc_get_cc(tcpc, &tc->cc1, &tc->cc2) != 0) {
83 /* If this function fails, it may mean that the TCPC is in sleep mode or
84 * the communication with TCPC has failed, so we can assume that the CC
85 * lines are open or existing connection is faulty.
86 */
87 tc->cc1 = TC_CC_VOLT_OPEN;
88 tc->cc2 = TC_CC_VOLT_OPEN;
89 }
90
91 /* Detect polarity */
92 tc->cc_polarity = (tc->cc1 > tc->cc2) ? TC_POLARITY_CC1 : TC_POLARITY_CC2;
93
94 /* Execute any asyncronous Device Policy Manager Requests */
95 if (dpm_request == REQUEST_TC_ERROR_RECOVERY) {
96 /* Transition to Error Recovery State */
97 tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
98 } else if (dpm_request == REQUEST_TC_DISABLED) {
99 /* Transition to Disabled State */
100 tc_set_state(dev, TC_DISABLED_STATE);
101 }
102
103 /* Run state machine */
104 smf_run_state(SMF_CTX(tc));
105 }
106 }
107
108 /**
109 * @brief Checks if the TC Layer is in an Attached state
110 */
tc_is_in_attached_state(const struct device * dev)111 bool tc_is_in_attached_state(const struct device *dev)
112 {
113 #ifdef CONFIG_USBC_CSM_SINK_ONLY
114 return (tc_get_state(dev) == TC_ATTACHED_SNK_STATE);
115 #else
116 return (tc_get_state(dev) == TC_ATTACHED_SRC_STATE);
117 #endif
118 }
119
120 /**
121 * @brief Initializes the Type-C layer
122 */
tc_init(const struct device * dev)123 static int tc_init(const struct device *dev)
124 {
125 struct usbc_port_data *data = dev->data;
126 struct tc_sm_t *tc = data->tc;
127 const struct device *tcpc = data->tcpc;
128 int ret;
129
130 /* Initialize the timers */
131 usbc_timer_init(&tc->tc_t_error_recovery, TC_T_ERROR_RECOVERY_SOURCE_MIN_MS);
132 usbc_timer_init(&tc->tc_t_cc_debounce, TC_T_CC_DEBOUNCE_MAX_MS);
133 usbc_timer_init(&tc->tc_t_rp_value_change, TC_T_RP_VALUE_CHANGE_MAX_MS);
134 #ifdef CONFIG_USBC_CSM_SOURCE_ONLY
135 usbc_timer_init(&tc->tc_t_vconn_off, TC_T_VCONN_OFF_MAX_MS);
136 #endif
137
138 /* Clear the flags */
139 tc->flags = ATOMIC_INIT(0);
140
141 /* Initialize the TCPC */
142 ret = tcpc_init(tcpc);
143 if (ret != 0) {
144 LOG_ERR("TCPC initialization failed: %d", ret);
145 return ret;
146 }
147
148 #ifdef CONFIG_USBC_CSM_SOURCE_ONLY
149 /* Stop sourcing VBUS by policy callback and/or TCPC */
150 ret = usbc_policy_src_en(dev, tcpc, false);
151 if (ret != 0) {
152 LOG_ERR("Couldn't disable vbus sourcing: %d", ret);
153 return ret;
154 }
155
156 /* Disable VBUS sourcing by the PPC */
157 if (data->ppc != NULL) {
158 ppc_set_src_ctrl(data->ppc, false);
159 }
160
161 /* Stop sourcing VCONN */
162 ret = tcpc_set_vconn(tcpc, false);
163 if (ret != 0 && ret != -ENOTSUP) {
164 LOG_ERR("Couldn't disable vconn: %d", ret);
165 return ret;
166 }
167 #endif
168
169 /* Initialize the state machine */
170 /*
171 * Start out in error recovery state so the CC lines are opened for a
172 * short while if this is a system reset.
173 */
174 tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
175
176 return 0;
177 }
178
179 /**
180 * @brief Sets a Type-C state
181 */
tc_set_state(const struct device * dev,const enum tc_state_t state)182 void tc_set_state(const struct device *dev, const enum tc_state_t state)
183 {
184 struct usbc_port_data *data = dev->data;
185 struct tc_sm_t *tc = data->tc;
186
187 __ASSERT(state < ARRAY_SIZE(tc_states), "invalid tc_state %d", state);
188 smf_set_state(SMF_CTX(tc), &tc_states[state]);
189 }
190
191 /**
192 * @brief Get the Type-C current state
193 */
tc_get_state(const struct device * dev)194 enum tc_state_t tc_get_state(const struct device *dev)
195 {
196 struct usbc_port_data *data = dev->data;
197
198 return data->tc->ctx.current - &tc_states[0];
199 }
200
201 /**
202 * @brief Enable Power Delivery
203 */
tc_pd_enable(const struct device * dev,const bool enable)204 void tc_pd_enable(const struct device *dev, const bool enable)
205 {
206 if (enable) {
207 prl_start(dev);
208 pe_start(dev);
209 } else {
210 prl_suspend(dev);
211 pe_suspend(dev);
212 }
213 }
214
215 /**
216 * @brief TCPC CC/Rp management
217 */
tc_select_src_collision_rp(const struct device * dev,enum tc_rp_value rp)218 void tc_select_src_collision_rp(const struct device *dev, enum tc_rp_value rp)
219 {
220 struct usbc_port_data *data = dev->data;
221 const struct device *tcpc = data->tcpc;
222 int ret;
223
224 /* Select Rp value */
225 ret = tcpc_select_rp_value(tcpc, rp);
226 if (ret != 0 && ret != -ENOTSUP) {
227 LOG_ERR("Couldn't set Rp value to %d: %d", rp, ret);
228 tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
229 return;
230 }
231
232 /* Place Rp on CC lines */
233 ret = tcpc_set_cc(tcpc, TC_CC_RP);
234 if (ret != 0) {
235 LOG_ERR("Couldn't set CC lines to Rp: %d", ret);
236 tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
237 }
238 }
239
240 /**
241 * @brief CC Open Entry
242 */
tc_cc_open_entry(void * obj)243 static void tc_cc_open_entry(void *obj)
244 {
245 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
246 const struct device *dev = tc->dev;
247 struct usbc_port_data *data = dev->data;
248 const struct device *tcpc = data->tcpc;
249 int ret;
250
251 tc->cc_voltage = TC_CC_VOLT_OPEN;
252
253 /* Disable VCONN */
254 ret = tcpc_set_vconn(tcpc, false);
255 if (ret != 0 && ret != -ENOSYS) {
256 LOG_ERR("Couldn't disable vconn: %d", ret);
257 tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
258 return;
259 }
260
261 /* Open CC lines */
262 ret = tcpc_set_cc(tcpc, TC_CC_OPEN);
263 if (ret != 0) {
264 LOG_ERR("Couldn't set CC lines to open: %d", ret);
265 tc_set_state(dev, TC_ERROR_RECOVERY_STATE);
266 }
267 }
268
269 /**
270 * @brief Disabled Entry
271 */
tc_disabled_entry(void * obj)272 static void tc_disabled_entry(void *obj)
273 {
274 LOG_INF("Disabled");
275 }
276
277 /**
278 * @brief Disabled Run
279 */
tc_disabled_run(void * obj)280 static void tc_disabled_run(void *obj)
281 {
282 /* Do nothing */
283 }
284
285 /**
286 * @brief ErrorRecovery Entry
287 */
tc_error_recovery_entry(void * obj)288 static void tc_error_recovery_entry(void *obj)
289 {
290 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
291
292 LOG_INF("ErrorRecovery");
293
294 /* Start tErrorRecovery timer */
295 usbc_timer_start(&tc->tc_t_error_recovery);
296 }
297
298 /**
299 * @brief ErrorRecovery Run
300 */
tc_error_recovery_run(void * obj)301 static void tc_error_recovery_run(void *obj)
302 {
303 struct tc_sm_t *tc = (struct tc_sm_t *)obj;
304 const struct device *dev = tc->dev;
305
306 /* Wait for expiry */
307 if (usbc_timer_expired(&tc->tc_t_error_recovery) == false) {
308 return;
309 }
310
311 #ifdef CONFIG_USBC_CSM_SINK_ONLY
312 /* Transition to Unattached.SNK */
313 tc_set_state(dev, TC_UNATTACHED_SNK_STATE);
314 #else
315 /* Transition to Unattached.SRC */
316 tc_set_state(dev, TC_UNATTACHED_SRC_STATE);
317 #endif
318 }
319
320 /**
321 * @brief Type-C State Table
322 */
323 static const struct smf_state tc_states[TC_STATE_COUNT] = {
324 /* Super States */
325 [TC_CC_OPEN_SUPER_STATE] = SMF_CREATE_STATE(
326 tc_cc_open_entry,
327 NULL,
328 NULL,
329 NULL,
330 NULL),
331 #ifdef CONFIG_USBC_CSM_SINK_ONLY
332 [TC_CC_RD_SUPER_STATE] = SMF_CREATE_STATE(
333 tc_cc_rd_entry,
334 NULL,
335 NULL,
336 NULL,
337 NULL),
338 #else
339 [TC_CC_RP_SUPER_STATE] = SMF_CREATE_STATE(
340 tc_cc_rp_entry,
341 NULL,
342 NULL,
343 NULL,
344 NULL),
345 #endif
346 /* Normal States */
347 #ifdef CONFIG_USBC_CSM_SINK_ONLY
348 [TC_UNATTACHED_SNK_STATE] = SMF_CREATE_STATE(
349 tc_unattached_snk_entry,
350 tc_unattached_snk_run,
351 NULL,
352 &tc_states[TC_CC_RD_SUPER_STATE],
353 NULL),
354 [TC_ATTACH_WAIT_SNK_STATE] = SMF_CREATE_STATE(
355 tc_attach_wait_snk_entry,
356 tc_attach_wait_snk_run,
357 tc_attach_wait_snk_exit,
358 &tc_states[TC_CC_RD_SUPER_STATE],
359 NULL),
360 [TC_ATTACHED_SNK_STATE] = SMF_CREATE_STATE(
361 tc_attached_snk_entry,
362 tc_attached_snk_run,
363 tc_attached_snk_exit,
364 NULL,
365 NULL),
366 #else
367 [TC_UNATTACHED_SRC_STATE] = SMF_CREATE_STATE(
368 tc_unattached_src_entry,
369 tc_unattached_src_run,
370 NULL,
371 &tc_states[TC_CC_RP_SUPER_STATE],
372 NULL),
373 [TC_UNATTACHED_WAIT_SRC_STATE] = SMF_CREATE_STATE(
374 tc_unattached_wait_src_entry,
375 tc_unattached_wait_src_run,
376 tc_unattached_wait_src_exit,
377 NULL,
378 NULL),
379 [TC_ATTACH_WAIT_SRC_STATE] = SMF_CREATE_STATE(
380 tc_attach_wait_src_entry,
381 tc_attach_wait_src_run,
382 tc_attach_wait_src_exit,
383 &tc_states[TC_CC_RP_SUPER_STATE],
384 NULL),
385 [TC_ATTACHED_SRC_STATE] = SMF_CREATE_STATE(
386 tc_attached_src_entry,
387 tc_attached_src_run,
388 tc_attached_src_exit,
389 NULL,
390 NULL),
391 #endif
392 [TC_DISABLED_STATE] = SMF_CREATE_STATE(
393 tc_disabled_entry,
394 tc_disabled_run,
395 NULL,
396 &tc_states[TC_CC_OPEN_SUPER_STATE],
397 NULL),
398 [TC_ERROR_RECOVERY_STATE] = SMF_CREATE_STATE(
399 tc_error_recovery_entry,
400 tc_error_recovery_run,
401 NULL,
402 &tc_states[TC_CC_OPEN_SUPER_STATE],
403 NULL),
404 };
405 BUILD_ASSERT(ARRAY_SIZE(tc_states) == TC_STATE_COUNT);
406