1 /*
2 * Copyright (c) 2022 The Chromium OS Authors
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT usb_c_connector
8
9 #include <zephyr/devicetree.h>
10 #include <zephyr/init.h>
11 #include <zephyr/smf.h>
12 #include <zephyr/usb_c/usbc.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(usbc_stack, CONFIG_USBC_STACK_LOG_LEVEL);
16
17 #include "usbc_stack.h"
18 #include "usbc_pe_common_internal.h"
19 #include "usbc_tc_common_internal.h"
20
21 static int usbc_subsys_init(const struct device *dev);
22
usbc_handler(void * port_dev)23 static ALWAYS_INLINE void usbc_handler(void *port_dev)
24 {
25 const struct device *dev = (const struct device *)port_dev;
26 struct usbc_port_data *port = dev->data;
27 struct request_value *req;
28 int32_t request;
29
30 req = k_fifo_get(&port->request_fifo, K_NO_WAIT);
31 request = (req != NULL) ? req->val : REQUEST_NOP;
32 pe_run(dev, request);
33 prl_run(dev);
34 tc_run(dev, request);
35
36 if (request == PRIV_PORT_REQUEST_SUSPEND) {
37 k_thread_suspend(port->port_thread);
38 }
39
40 /* Check if there wasn't any request to do a one more iteration of USB-C state machines */
41 if (!port->bypass_next_sleep) {
42 k_msleep(CONFIG_USBC_STATE_MACHINE_CYCLE_TIME);
43 } else {
44 port->bypass_next_sleep = false;
45 }
46 }
47
48 /* format makes the backslashes exceed 100 lines: */
49 /* clang-format off */
50 #define USBC_SUBSYS_INIT(inst) \
51 K_THREAD_STACK_DEFINE(my_stack_area_##inst, CONFIG_USBC_STACK_SIZE); \
52 \
53 static struct tc_sm_t tc_##inst; \
54 static struct policy_engine pe_##inst; \
55 static struct protocol_layer_rx_t prl_rx_##inst; \
56 static struct protocol_layer_tx_t prl_tx_##inst; \
57 static struct protocol_hard_reset_t prl_hr_##inst; \
58 \
59 static void run_usbc_##inst(void *port_dev, void *unused1, void *unused2) \
60 { \
61 while (1) { \
62 usbc_handler(port_dev); \
63 } \
64 } \
65 \
66 static void create_thread_##inst(const struct device *dev) \
67 { \
68 struct usbc_port_data *port = dev->data; \
69 \
70 port->port_thread = k_thread_create( \
71 &port->thread_data, my_stack_area_##inst, \
72 K_THREAD_STACK_SIZEOF(my_stack_area_##inst), run_usbc_##inst, (void *)dev, \
73 0, 0, CONFIG_USBC_THREAD_PRIORITY, K_ESSENTIAL, K_NO_WAIT); \
74 k_thread_suspend(port->port_thread); \
75 } \
76 \
77 static struct usbc_port_data usbc_port_data_##inst = { \
78 .tc = &tc_##inst, \
79 .pe = &pe_##inst, \
80 .prl_rx = &prl_rx_##inst, \
81 .prl_tx = &prl_tx_##inst, \
82 .prl_hr = &prl_hr_##inst, \
83 .tcpc = DEVICE_DT_GET(DT_INST_PROP(inst, tcpc)), \
84 .vbus = DEVICE_DT_GET(DT_INST_PROP(inst, vbus)), \
85 .ppc = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, ppc), \
86 (DEVICE_DT_GET(DT_INST_PROP(inst, ppc))), (NULL)), \
87 }; \
88 \
89 static const struct usbc_port_config usbc_port_config_##inst = { \
90 .create_thread = create_thread_##inst, \
91 }; \
92 \
93 DEVICE_DT_INST_DEFINE(inst, &usbc_subsys_init, NULL, &usbc_port_data_##inst, \
94 &usbc_port_config_##inst, POST_KERNEL, \
95 CONFIG_USBC_STACK_INIT_PRIORITY, NULL);
96 /* clang-format on */
DT_INST_FOREACH_STATUS_OKAY(USBC_SUBSYS_INIT)97 DT_INST_FOREACH_STATUS_OKAY(USBC_SUBSYS_INIT)
98
99 /**
100 * @brief Called by the Device Policy Manager to start the USB-C Subsystem
101 */
102 int usbc_start(const struct device *dev)
103 {
104 struct usbc_port_data *data = dev->data;
105
106 /* Add private start request to fifo */
107 data->request.val = PRIV_PORT_REQUEST_START;
108 k_fifo_put(&data->request_fifo, &data->request);
109
110 /* Start the port thread */
111 k_thread_resume(data->port_thread);
112
113 return 0;
114 }
115
116 /**
117 * @brief Called by the Device Policy Manager to suspend the USB-C Subsystem
118 */
usbc_suspend(const struct device * dev)119 int usbc_suspend(const struct device *dev)
120 {
121 struct usbc_port_data *data = dev->data;
122
123 /* Add private suspend request to fifo */
124 data->request.val = PRIV_PORT_REQUEST_SUSPEND;
125 k_fifo_put(&data->request_fifo, &data->request);
126
127 return 0;
128 }
129
130 /**
131 * @brief Called by the Device Policy Manager to make a request of the
132 * USB-C Subsystem
133 */
usbc_request(const struct device * dev,const enum usbc_policy_request_t req)134 int usbc_request(const struct device *dev, const enum usbc_policy_request_t req)
135 {
136 struct usbc_port_data *data = dev->data;
137
138 /* Add public request to fifo */
139 data->request.val = req;
140 k_fifo_put(&data->request_fifo, &data->request);
141
142 return 0;
143 }
144
usbc_bypass_next_sleep(const struct device * dev)145 void usbc_bypass_next_sleep(const struct device *dev)
146 {
147 struct usbc_port_data *data = dev->data;
148
149 data->bypass_next_sleep = true;
150 }
151
152 /**
153 * @brief Sets the Device Policy Manager's data
154 */
usbc_set_dpm_data(const struct device * dev,void * dpm_data)155 void usbc_set_dpm_data(const struct device *dev, void *dpm_data)
156 {
157 struct usbc_port_data *data = dev->data;
158
159 data->dpm_data = dpm_data;
160 }
161
162 /**
163 * @brief Gets the Device Policy Manager's data
164 */
usbc_get_dpm_data(const struct device * dev)165 void *usbc_get_dpm_data(const struct device *dev)
166 {
167 struct usbc_port_data *data = dev->data;
168
169 return data->dpm_data;
170 }
171
172 #ifdef CONFIG_USBC_CSM_SINK_ONLY
173 /**
174 * @brief Set the callback that gets the Sink Capabilities from the
175 * Device Policy Manager
176 */
usbc_set_policy_cb_get_snk_cap(const struct device * dev,const policy_cb_get_snk_cap_t policy_cb_get_snk_cap)177 void usbc_set_policy_cb_get_snk_cap(const struct device *dev,
178 const policy_cb_get_snk_cap_t policy_cb_get_snk_cap)
179 {
180 struct usbc_port_data *data = dev->data;
181
182 data->policy_cb_get_snk_cap = policy_cb_get_snk_cap;
183 }
184
185 /**
186 * @brief Set the callback that sends the received Source Capabilities to the
187 * Device Policy Manager
188 */
usbc_set_policy_cb_set_src_cap(const struct device * dev,const policy_cb_set_src_cap_t policy_cb_set_src_cap)189 void usbc_set_policy_cb_set_src_cap(const struct device *dev,
190 const policy_cb_set_src_cap_t policy_cb_set_src_cap)
191 {
192 struct usbc_port_data *data = dev->data;
193
194 data->policy_cb_set_src_cap = policy_cb_set_src_cap;
195 }
196
197 /**
198 * @brief Set the callback for requesting the data object (RDO)
199 */
usbc_set_policy_cb_get_rdo(const struct device * dev,const policy_cb_get_rdo_t policy_cb_get_rdo)200 void usbc_set_policy_cb_get_rdo(const struct device *dev,
201 const policy_cb_get_rdo_t policy_cb_get_rdo)
202 {
203 struct usbc_port_data *data = dev->data;
204
205 data->policy_cb_get_rdo = policy_cb_get_rdo;
206 }
207
208 /**
209 * @brief Set the callback for checking if Sink Power Supply is at
210 * default level
211 */
usbc_set_policy_cb_is_snk_at_default(const struct device * dev,const policy_cb_is_snk_at_default_t policy_cb_is_snk_at_default)212 void usbc_set_policy_cb_is_snk_at_default(
213 const struct device *dev, const policy_cb_is_snk_at_default_t policy_cb_is_snk_at_default)
214 {
215 struct usbc_port_data *data = dev->data;
216
217 data->policy_cb_is_snk_at_default = policy_cb_is_snk_at_default;
218 }
219
220 #else /* CONFIG_USBC_CSM_SOURCE_ONLY */
221
222 /**
223 * @brief Set the callback for sending the Sink Caps to the DPM
224 */
usbc_set_policy_cb_set_port_partner_snk_cap(const struct device * dev,const policy_cb_set_port_partner_snk_cap_t cb)225 void usbc_set_policy_cb_set_port_partner_snk_cap(const struct device *dev,
226 const policy_cb_set_port_partner_snk_cap_t cb)
227 {
228 struct usbc_port_data *data = dev->data;
229
230 data->policy_cb_set_port_partner_snk_cap = cb;
231 }
232
233 /**
234 * @brief Set the callback that gets the Source Capabilities from the
235 * Device Policy Manager
236 */
usbc_set_policy_cb_get_src_caps(const struct device * dev,const policy_cb_get_src_caps_t cb)237 void usbc_set_policy_cb_get_src_caps(const struct device *dev, const policy_cb_get_src_caps_t cb)
238 {
239 struct usbc_port_data *data = dev->data;
240
241 data->policy_cb_get_src_caps = cb;
242 }
243
244 /**
245 * @brief Set the callback that gets the Source Rp value from the
246 * Device Policy Manager
247 */
usbc_set_policy_cb_get_src_rp(const struct device * dev,const policy_cb_get_src_rp_t policy_cb_get_src_rp)248 void usbc_set_policy_cb_get_src_rp(const struct device *dev,
249 const policy_cb_get_src_rp_t policy_cb_get_src_rp)
250 {
251 struct usbc_port_data *data = dev->data;
252
253 data->policy_cb_get_src_rp = policy_cb_get_src_rp;
254 }
255
256 /**
257 * @brief Set the callback that controls the sourcing of VBUS from the
258 * Device Policy Manager
259 */
usbc_set_policy_cb_src_en(const struct device * dev,const policy_cb_src_en_t policy_cb_src_en)260 void usbc_set_policy_cb_src_en(const struct device *dev, const policy_cb_src_en_t policy_cb_src_en)
261 {
262 struct usbc_port_data *data = dev->data;
263
264 data->policy_cb_src_en = policy_cb_src_en;
265 }
266
267 /**
268 * @brief Set the callback for checking if a Sink Request is valid
269 */
usbc_set_policy_cb_check_sink_request(const struct device * dev,const policy_cb_check_sink_request_t cb)270 void usbc_set_policy_cb_check_sink_request(const struct device *dev,
271 const policy_cb_check_sink_request_t cb)
272 {
273 struct usbc_port_data *data = dev->data;
274
275 data->policy_cb_check_sink_request = cb;
276 }
277
278 /**
279 * @brief Set the callback for checking if the Source Power Supply is ready
280 */
usbc_set_policy_cb_is_ps_ready(const struct device * dev,const policy_cb_is_ps_ready_t cb)281 void usbc_set_policy_cb_is_ps_ready(const struct device *dev, const policy_cb_is_ps_ready_t cb)
282 {
283 struct usbc_port_data *data = dev->data;
284
285 data->policy_is_ps_ready = cb;
286 }
287
288 /**
289 * @brief Set the callback for checking if the Present Contract is still valid
290 */
usbc_set_policy_cb_present_contract_is_valid(const struct device * dev,const policy_cb_present_contract_is_valid_t cb)291 void usbc_set_policy_cb_present_contract_is_valid(const struct device *dev,
292 const policy_cb_present_contract_is_valid_t cb)
293 {
294 struct usbc_port_data *data = dev->data;
295
296 data->policy_present_contract_is_valid = cb;
297 }
298
299 /**
300 * @brief Set the callback that requests the use of a new set of Sources Caps if
301 * they're available
302 */
usbc_set_policy_cb_change_src_caps(const struct device * dev,const policy_cb_change_src_caps_t cb)303 void usbc_set_policy_cb_change_src_caps(const struct device *dev,
304 const policy_cb_change_src_caps_t cb)
305 {
306 struct usbc_port_data *data = dev->data;
307
308 data->policy_change_src_caps = cb;
309 }
310
311 /**
312 * @brief Set the callback that controls the sourcing of VCONN from the
313 * Device Policy Manager
314 */
usbc_set_vconn_control_cb(const struct device * dev,const tcpc_vconn_control_cb_t cb)315 void usbc_set_vconn_control_cb(const struct device *dev, const tcpc_vconn_control_cb_t cb)
316 {
317 struct usbc_port_data *data = dev->data;
318 const struct device *tcpc = data->tcpc;
319
320 tcpc_set_vconn_cb(tcpc, cb);
321 }
322
323 /**
324 * @brief Set the callback that discharges VCONN from the
325 * Device Policy Manager
326 */
usbc_set_vconn_discharge(const struct device * dev,const tcpc_vconn_discharge_cb_t cb)327 void usbc_set_vconn_discharge(const struct device *dev, const tcpc_vconn_discharge_cb_t cb)
328 {
329 struct usbc_port_data *data = dev->data;
330 const struct device *tcpc = data->tcpc;
331
332 tcpc_set_vconn_discharge_cb(tcpc, cb);
333 }
334
335 #endif /* CONFIG_USBC_CSM_SINK_ONLY */
336
337 /**
338 * @brief Set the callback for the Device Policy Manager policy check
339 */
usbc_set_policy_cb_check(const struct device * dev,const policy_cb_check_t policy_cb_check)340 void usbc_set_policy_cb_check(const struct device *dev, const policy_cb_check_t policy_cb_check)
341 {
342 struct usbc_port_data *data = dev->data;
343
344 data->policy_cb_check = policy_cb_check;
345 }
346
347 /**
348 * @brief Set the callback for the Device Policy Manager policy change notify
349 */
usbc_set_policy_cb_notify(const struct device * dev,const policy_cb_notify_t policy_cb_notify)350 void usbc_set_policy_cb_notify(const struct device *dev, const policy_cb_notify_t policy_cb_notify)
351 {
352 struct usbc_port_data *data = dev->data;
353
354 data->policy_cb_notify = policy_cb_notify;
355 }
356
357 /**
358 * @brief Set the callback for the Device Policy Manager policy change notify
359 */
usbc_set_policy_cb_wait_notify(const struct device * dev,const policy_cb_wait_notify_t policy_cb_wait_notify)360 void usbc_set_policy_cb_wait_notify(const struct device *dev,
361 const policy_cb_wait_notify_t policy_cb_wait_notify)
362 {
363 struct usbc_port_data *data = dev->data;
364
365 data->policy_cb_wait_notify = policy_cb_wait_notify;
366 }
367
368 /**
369 * @brief Initialize the USB-C Subsystem
370 */
usbc_subsys_init(const struct device * dev)371 static int usbc_subsys_init(const struct device *dev)
372 {
373 struct usbc_port_data *data = dev->data;
374 const struct usbc_port_config *const config = dev->config;
375 const struct device *tcpc = data->tcpc;
376
377 /* Make sure TCPC is ready */
378 if (!device_is_ready(tcpc)) {
379 LOG_ERR("TCPC NOT READY");
380 return -ENODEV;
381 }
382
383 /* Initialize the state machines */
384 tc_subsys_init(dev);
385 pe_subsys_init(dev);
386 prl_subsys_init(dev);
387
388 /* Initialize the request fifo */
389 k_fifo_init(&data->request_fifo);
390
391 /* Create the thread for this port */
392 config->create_thread(dev);
393 return 0;
394 }
395