1 /*
2 * Copyright (c) 2022 The Chromium OS Authors.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/devicetree.h>
10 #include <zephyr/usb_c/usbc.h>
11
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
14
15 #define USBC_PORT0_NODE DT_ALIAS(usbc_port0)
16 #define USBC_PORT0_POWER_ROLE DT_ENUM_IDX(USBC_PORT0_NODE, power_role)
17
18 #if (USBC_PORT0_POWER_ROLE != TC_ROLE_SINK)
19 #error "Unsupported board: Only Sink device supported"
20 #endif
21
22 #define SINK_PDO(node_id, prop, idx) (DT_PROP_BY_IDX(node_id, prop, idx)),
23
24 /* usbc.rst port data object start */
25 /**
26 * @brief A structure that encapsulates Port data.
27 */
28 static struct port0_data_t {
29 /** Sink Capabilities */
30 uint32_t snk_caps[DT_PROP_LEN(USBC_PORT0_NODE, sink_pdos)];
31 /** Number of Sink Capabilities */
32 int snk_cap_cnt;
33 /** Source Capabilities */
34 uint32_t src_caps[PDO_MAX_DATA_OBJECTS];
35 /** Number of Source Capabilities */
36 int src_cap_cnt;
37 /* Power Supply Ready flag */
38 atomic_t ps_ready;
39 } port0_data = {
40 .snk_caps = {DT_FOREACH_PROP_ELEM(USBC_PORT0_NODE, sink_pdos, SINK_PDO)},
41 .snk_cap_cnt = DT_PROP_LEN(USBC_PORT0_NODE, sink_pdos),
42 .src_caps = {0},
43 .src_cap_cnt = 0,
44 .ps_ready = 0
45 };
46
47 /* usbc.rst port data object end */
48
49 /**
50 * @brief Builds a Request Data Object (RDO) with the following properties:
51 * - Maximum operating current 100mA
52 * - Operating current is 100mA
53 * - Unchunked Extended Messages Not Supported
54 * - No USB Suspend
55 * - Not USB Communications Capable
56 * - No capability mismatch
57 * - Does not Giveback
58 * - Select object position 1 (5V Power Data Object (PDO))
59 *
60 * @note Generally a sink application would build an RDO from the
61 * Source Capabilities stored in the dpm_data object
62 */
build_rdo(const struct port0_data_t * dpm_data)63 static uint32_t build_rdo(const struct port0_data_t *dpm_data)
64 {
65 union pd_rdo rdo;
66
67 /* Maximum operating current 100mA (GIVEBACK = 0) */
68 rdo.fixed.min_or_max_operating_current = PD_CONVERT_MA_TO_FIXED_PDO_CURRENT(100);
69 /* Operating current 100mA */
70 rdo.fixed.operating_current = PD_CONVERT_MA_TO_FIXED_PDO_CURRENT(100);
71 /* Unchunked Extended Messages Not Supported */
72 rdo.fixed.unchunked_ext_msg_supported = 0;
73 /* No USB Suspend */
74 rdo.fixed.no_usb_suspend = 1;
75 /* Not USB Communications Capable */
76 rdo.fixed.usb_comm_capable = 0;
77 /* No capability mismatch */
78 rdo.fixed.cap_mismatch = 0;
79 /* Don't giveback */
80 rdo.fixed.giveback = 0;
81 /* Object position 1 (5V PDO) */
82 rdo.fixed.object_pos = 1;
83
84 return rdo.raw_value;
85 }
86
display_pdo(const int idx,const uint32_t pdo_value)87 static void display_pdo(const int idx,
88 const uint32_t pdo_value)
89 {
90 union pd_fixed_supply_pdo_source pdo;
91
92 /* Default to fixed supply pdo source until type is detected */
93 pdo.raw_value = pdo_value;
94
95 LOG_INF("PDO %d:", idx);
96 switch (pdo.type) {
97 case PDO_FIXED: {
98 LOG_INF("\tType: FIXED");
99 LOG_INF("\tCurrent: %d",
100 PD_CONVERT_FIXED_PDO_CURRENT_TO_MA(pdo.max_current));
101 LOG_INF("\tVoltage: %d",
102 PD_CONVERT_FIXED_PDO_VOLTAGE_TO_MV(pdo.voltage));
103 LOG_INF("\tPeak Current: %d", pdo.peak_current);
104 LOG_INF("\tUchunked Support: %d",
105 pdo.unchunked_ext_msg_supported);
106 LOG_INF("\tDual Role Data: %d",
107 pdo.dual_role_data);
108 LOG_INF("\tUSB Comms: %d",
109 pdo.usb_comms_capable);
110 LOG_INF("\tUnconstrained Pwr: %d",
111 pdo.unconstrained_power);
112 LOG_INF("\tUSB Suspend: %d",
113 pdo.usb_suspend_supported);
114 LOG_INF("\tDual Role Power: %d",
115 pdo.dual_role_power);
116 }
117 break;
118 case PDO_BATTERY: {
119 union pd_battery_supply_pdo_source batt_pdo;
120
121 batt_pdo.raw_value = pdo_value;
122 LOG_INF("\tType: BATTERY");
123 LOG_INF("\tMin Voltage: %d",
124 PD_CONVERT_BATTERY_PDO_VOLTAGE_TO_MV(batt_pdo.min_voltage));
125 LOG_INF("\tMax Voltage: %d",
126 PD_CONVERT_BATTERY_PDO_VOLTAGE_TO_MV(batt_pdo.max_voltage));
127 LOG_INF("\tMax Power: %d",
128 PD_CONVERT_BATTERY_PDO_POWER_TO_MW(batt_pdo.max_power));
129 }
130 break;
131 case PDO_VARIABLE: {
132 union pd_variable_supply_pdo_source var_pdo;
133
134 var_pdo.raw_value = pdo_value;
135 LOG_INF("\tType: VARIABLE");
136 LOG_INF("\tMin Voltage: %d",
137 PD_CONVERT_VARIABLE_PDO_VOLTAGE_TO_MV(var_pdo.min_voltage));
138 LOG_INF("\tMax Voltage: %d",
139 PD_CONVERT_VARIABLE_PDO_VOLTAGE_TO_MV(var_pdo.max_voltage));
140 LOG_INF("\tMax Current: %d",
141 PD_CONVERT_VARIABLE_PDO_CURRENT_TO_MA(var_pdo.max_current));
142 }
143 break;
144 case PDO_AUGMENTED: {
145 union pd_augmented_supply_pdo_source aug_pdo;
146
147 aug_pdo.raw_value = pdo_value;
148 LOG_INF("\tType: AUGMENTED");
149 LOG_INF("\tMin Voltage: %d",
150 PD_CONVERT_AUGMENTED_PDO_VOLTAGE_TO_MV(aug_pdo.min_voltage));
151 LOG_INF("\tMax Voltage: %d",
152 PD_CONVERT_AUGMENTED_PDO_VOLTAGE_TO_MV(aug_pdo.max_voltage));
153 LOG_INF("\tMax Current: %d",
154 PD_CONVERT_AUGMENTED_PDO_CURRENT_TO_MA(aug_pdo.max_current));
155 LOG_INF("\tPPS Power Limited: %d", aug_pdo.pps_power_limited);
156 }
157 break;
158 }
159 }
160
display_source_caps(const struct device * dev)161 static void display_source_caps(const struct device *dev)
162 {
163 struct port0_data_t *dpm_data = usbc_get_dpm_data(dev);
164
165 LOG_INF("Source Caps:");
166 for (int i = 0; i < dpm_data->src_cap_cnt; i++) {
167 display_pdo(i, dpm_data->src_caps[i]);
168 k_msleep(50);
169 }
170 }
171
172 /* usbc.rst callbacks start */
port0_policy_cb_get_snk_cap(const struct device * dev,uint32_t ** pdos,int * num_pdos)173 static int port0_policy_cb_get_snk_cap(const struct device *dev,
174 uint32_t **pdos,
175 int *num_pdos)
176 {
177 struct port0_data_t *dpm_data = usbc_get_dpm_data(dev);
178
179 *pdos = dpm_data->snk_caps;
180 *num_pdos = dpm_data->snk_cap_cnt;
181
182 return 0;
183 }
184
port0_policy_cb_set_src_cap(const struct device * dev,const uint32_t * pdos,const int num_pdos)185 static void port0_policy_cb_set_src_cap(const struct device *dev,
186 const uint32_t *pdos,
187 const int num_pdos)
188 {
189 struct port0_data_t *dpm_data;
190 int num;
191 int i;
192
193 dpm_data = usbc_get_dpm_data(dev);
194
195 num = num_pdos;
196 if (num > PDO_MAX_DATA_OBJECTS) {
197 num = PDO_MAX_DATA_OBJECTS;
198 }
199
200 for (i = 0; i < num; i++) {
201 dpm_data->src_caps[i] = *(pdos + i);
202 }
203
204 dpm_data->src_cap_cnt = num;
205 }
206
port0_policy_cb_get_rdo(const struct device * dev)207 static uint32_t port0_policy_cb_get_rdo(const struct device *dev)
208 {
209 struct port0_data_t *dpm_data = usbc_get_dpm_data(dev);
210
211 return build_rdo(dpm_data);
212 }
213 /* usbc.rst callbacks end */
214
215 /* usbc.rst notify start */
port0_notify(const struct device * dev,const enum usbc_policy_notify_t policy_notify)216 static void port0_notify(const struct device *dev,
217 const enum usbc_policy_notify_t policy_notify)
218 {
219 struct port0_data_t *dpm_data = usbc_get_dpm_data(dev);
220
221 switch (policy_notify) {
222 case PROTOCOL_ERROR:
223 break;
224 case MSG_DISCARDED:
225 break;
226 case MSG_ACCEPT_RECEIVED:
227 break;
228 case MSG_REJECTED_RECEIVED:
229 break;
230 case MSG_NOT_SUPPORTED_RECEIVED:
231 break;
232 case TRANSITION_PS:
233 atomic_set_bit(&dpm_data->ps_ready, 0);
234 break;
235 case PD_CONNECTED:
236 break;
237 case NOT_PD_CONNECTED:
238 break;
239 case POWER_CHANGE_0A0:
240 LOG_INF("PWR 0A");
241 break;
242 case POWER_CHANGE_DEF:
243 LOG_INF("PWR DEF");
244 break;
245 case POWER_CHANGE_1A5:
246 LOG_INF("PWR 1A5");
247 break;
248 case POWER_CHANGE_3A0:
249 LOG_INF("PWR 3A0");
250 break;
251 case DATA_ROLE_IS_UFP:
252 break;
253 case DATA_ROLE_IS_DFP:
254 break;
255 case PORT_PARTNER_NOT_RESPONSIVE:
256 LOG_INF("Port Partner not PD Capable");
257 break;
258 case SNK_TRANSITION_TO_DEFAULT:
259 break;
260 case HARD_RESET_RECEIVED:
261 break;
262 case SENDER_RESPONSE_TIMEOUT:
263 break;
264 case SOURCE_CAPABILITIES_RECEIVED:
265 break;
266 }
267 }
268 /* usbc.rst notify end */
269
270 /* usbc.rst check start */
port0_policy_check(const struct device * dev,const enum usbc_policy_check_t policy_check)271 bool port0_policy_check(const struct device *dev,
272 const enum usbc_policy_check_t policy_check)
273 {
274 switch (policy_check) {
275 case CHECK_POWER_ROLE_SWAP:
276 /* Reject power role swaps */
277 return false;
278 case CHECK_DATA_ROLE_SWAP_TO_DFP:
279 /* Reject data role swap to DFP */
280 return false;
281 case CHECK_DATA_ROLE_SWAP_TO_UFP:
282 /* Accept data role swap to UFP */
283 return true;
284 case CHECK_SNK_AT_DEFAULT_LEVEL:
285 /* This device is always at the default power level */
286 return true;
287 default:
288 /* Reject all other policy checks */
289 return false;
290
291 }
292 }
293 /* usbc.rst check end */
294
main(void)295 int main(void)
296 {
297 const struct device *usbc_port0;
298
299 /* Get the device for this port */
300 usbc_port0 = DEVICE_DT_GET(USBC_PORT0_NODE);
301 if (!device_is_ready(usbc_port0)) {
302 LOG_ERR("PORT0 device not ready");
303 return 0;
304 }
305
306 /* usbc.rst register start */
307 /* Register USB-C Callbacks */
308
309 /* Register Policy Check callback */
310 usbc_set_policy_cb_check(usbc_port0, port0_policy_check);
311 /* Register Policy Notify callback */
312 usbc_set_policy_cb_notify(usbc_port0, port0_notify);
313 /* Register Policy Get Sink Capabilities callback */
314 usbc_set_policy_cb_get_snk_cap(usbc_port0, port0_policy_cb_get_snk_cap);
315 /* Register Policy Set Source Capabilities callback */
316 usbc_set_policy_cb_set_src_cap(usbc_port0, port0_policy_cb_set_src_cap);
317 /* Register Policy Get Request Data Object callback */
318 usbc_set_policy_cb_get_rdo(usbc_port0, port0_policy_cb_get_rdo);
319 /* usbc.rst register end */
320
321 /* usbc.rst user data start */
322 /* Set Application port data object. This object is passed to the policy callbacks */
323 port0_data.ps_ready = ATOMIC_INIT(0);
324 usbc_set_dpm_data(usbc_port0, &port0_data);
325 /* usbc.rst user data end */
326
327 /* usbc.rst usbc start */
328 /* Start the USB-C Subsystem */
329 usbc_start(usbc_port0);
330 /* usbc.rst usbc end */
331
332 while (1) {
333 /* Perform Application Specific functions */
334 if (atomic_test_and_clear_bit(&port0_data.ps_ready, 0)) {
335 /* Display the Source Capabilities */
336 display_source_caps(usbc_port0);
337 }
338
339 /* Arbitrary delay */
340 k_msleep(1000);
341 }
342 return 0;
343 }
344