1 /*
2 * Copyright 2024 Google LLC
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include <zephyr/device.h>
7 #include <zephyr/logging/log.h>
8 #include <zephyr/sys/util.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/usb_c/usbc.h>
11 #include <zephyr/usb_c/tcpci.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/drivers/usb_c/tcpci_priv.h>
14
15 LOG_MODULE_REGISTER(tcpci, CONFIG_USBC_LOG_LEVEL);
16
17 #define LOG_COMM_ERR_STR "Can't communicate with TCPC %s@%x (%s %x = %04x)"
18
19 const struct tcpci_reg_dump_map tcpci_std_regs[TCPCI_STD_REGS_SIZE] = {
20 {
21 .addr = TCPC_REG_VENDOR_ID,
22 .name = "VENDOR_ID",
23 .size = 2,
24 },
25 {
26 .addr = TCPC_REG_PRODUCT_ID,
27 .name = "PRODUCT_ID",
28 .size = 2,
29 },
30 {
31 .addr = TCPC_REG_BCD_DEV,
32 .name = "DEVICE_ID",
33 .size = 2,
34 },
35 {
36 .addr = TCPC_REG_TC_REV,
37 .name = "USBTYPEC_REV",
38 .size = 2,
39 },
40 {
41 .addr = TCPC_REG_PD_REV,
42 .name = "USBPD_REV_VER",
43 .size = 2,
44 },
45 {
46 .addr = TCPC_REG_PD_INT_REV,
47 .name = "PD_INTERFACE_REV",
48 .size = 2,
49 },
50 {
51 .addr = TCPC_REG_ALERT,
52 .name = "ALERT",
53 .size = 2,
54 },
55 {
56 .addr = TCPC_REG_ALERT_MASK,
57 .name = "ALERT_MASK",
58 .size = 2,
59 },
60 {
61 .addr = TCPC_REG_POWER_STATUS_MASK,
62 .name = "POWER_STATUS_MASK",
63 .size = 1,
64 },
65 {
66 .addr = TCPC_REG_FAULT_STATUS_MASK,
67 .name = "FAULT_STATUS_MASK",
68 .size = 1,
69 },
70 {
71 .addr = TCPC_REG_EXT_STATUS_MASK,
72 .name = "EXTENDED_STATUS_MASK",
73 .size = 1,
74 },
75 {
76 .addr = TCPC_REG_ALERT_EXT_MASK,
77 .name = "ALERT_EXTENDED_MASK",
78 .size = 1,
79 },
80 {
81 .addr = TCPC_REG_CONFIG_STD_OUTPUT,
82 .name = "CFG_STANDARD_OUTPUT",
83 .size = 1,
84 },
85 {
86 .addr = TCPC_REG_TCPC_CTRL,
87 .name = "TCPC_CONTROL",
88 .size = 1,
89 },
90 {
91 .addr = TCPC_REG_ROLE_CTRL,
92 .name = "ROLE_CONTROL",
93 .size = 1,
94 },
95 {
96 .addr = TCPC_REG_FAULT_CTRL,
97 .name = "FAULT_CONTROL",
98 .size = 1,
99 },
100 {
101 .addr = TCPC_REG_POWER_CTRL,
102 .name = "POWER_CONTROL",
103 .size = 1,
104 },
105 {
106 .addr = TCPC_REG_CC_STATUS,
107 .name = "CC_STATUS",
108 .size = 1,
109 },
110 {
111 .addr = TCPC_REG_POWER_STATUS,
112 .name = "POWER_STATUS",
113 .size = 1,
114 },
115 {
116 .addr = TCPC_REG_FAULT_STATUS,
117 .name = "FAULT_STATUS",
118 .size = 1,
119 },
120 {
121 .addr = TCPC_REG_EXT_STATUS,
122 .name = "EXTENDED_STATUS",
123 .size = 1,
124 },
125 {
126 .addr = TCPC_REG_ALERT_EXT,
127 .name = "ALERT_EXTENDED",
128 .size = 1,
129 },
130 {
131 .addr = TCPC_REG_DEV_CAP_1,
132 .name = "DEVICE_CAPABILITIES_1",
133 .size = 2,
134 },
135 {
136 .addr = TCPC_REG_DEV_CAP_2,
137 .name = "DEVICE_CAPABILITIES_2",
138 .size = 2,
139 },
140 {
141 .addr = TCPC_REG_STD_INPUT_CAP,
142 .name = "STANDARD_INPUT_CAPABILITIES",
143 .size = 1,
144 },
145 {
146 .addr = TCPC_REG_STD_OUTPUT_CAP,
147 .name = "STANDARD_OUTPUT_CAPABILITIES",
148 .size = 1,
149 },
150 {
151 .addr = TCPC_REG_CONFIG_EXT_1,
152 .name = "CFG_EXTENDED1",
153 .size = 1,
154 },
155 {
156 .addr = TCPC_REG_GENERIC_TIMER,
157 .name = "GENERIC_TIMER",
158 .size = 2,
159 },
160 {
161 .addr = TCPC_REG_MSG_HDR_INFO,
162 .name = "MESSAGE_HEADER_INFO",
163 .size = 1,
164 },
165 {
166 .addr = TCPC_REG_RX_DETECT,
167 .name = "RECEIVE_DETECT",
168 .size = 1,
169 },
170 {
171 .addr = TCPC_REG_TRANSMIT,
172 .name = "TRANSMIT",
173 .size = 1,
174 },
175 {
176 .addr = TCPC_REG_VBUS_VOLTAGE,
177 .name = "VBUS_VOLTAGE",
178 .size = 2,
179 },
180 {
181 .addr = TCPC_REG_VBUS_SINK_DISCONNECT_THRESH,
182 .name = "VBUS_SINK_DISCONNECT_THRESHOLD",
183 .size = 2,
184 },
185 {
186 .addr = TCPC_REG_VBUS_STOP_DISCHARGE_THRESH,
187 .name = "VBUS_STOP_DISCHARGE_THRESHOLD",
188 .size = 2,
189 },
190 {
191 .addr = TCPC_REG_VBUS_VOLTAGE_ALARM_HI_CFG,
192 .name = "VBUS_VOLTAGE_ALARM_HI_CFG",
193 .size = 2,
194 },
195 {
196 .addr = TCPC_REG_VBUS_VOLTAGE_ALARM_LO_CFG,
197 .name = "VBUS_VOLTAGE_ALARM_LO_CFG",
198 .size = 2,
199 },
200 {
201 .addr = TCPC_REG_VBUS_NONDEFAULT_TARGET,
202 .name = "VBUS_NONDEFAULT_TARGET",
203 .size = 2,
204 },
205 {
206 .addr = TCPC_REG_DEV_CAP_3,
207 .name = "DEVICE_CAPABILITIES_3",
208 .size = 2,
209 },
210 };
211
tcpci_read_reg8(const struct i2c_dt_spec * i2c,uint8_t reg,uint8_t * value)212 int tcpci_read_reg8(const struct i2c_dt_spec *i2c, uint8_t reg, uint8_t *value)
213 {
214 int ret;
215
216 for (int a = 0; a < CONFIG_USBC_TCPC_TCPCI_I2C_RETRIES; a++) {
217 ret = i2c_write_read(i2c->bus, i2c->addr, ®, sizeof(reg), value, sizeof(*value));
218
219 if (ret == 0) {
220 break;
221 }
222 }
223
224 if (ret != 0) {
225 LOG_ERR(LOG_COMM_ERR_STR, i2c->bus->name, i2c->addr, "r8", reg, *value);
226 }
227
228 return ret;
229 }
230
tcpci_write_reg8(const struct i2c_dt_spec * i2c,uint8_t reg,uint8_t value)231 int tcpci_write_reg8(const struct i2c_dt_spec *i2c, uint8_t reg, uint8_t value)
232 {
233 uint8_t buf[2] = {reg, value};
234 int ret;
235
236 for (int a = 0; a < CONFIG_USBC_TCPC_TCPCI_I2C_RETRIES; a++) {
237 ret = i2c_write(i2c->bus, buf, 2, i2c->addr);
238
239 if (ret == 0) {
240 break;
241 }
242 }
243
244 if (ret != 0) {
245 LOG_ERR(LOG_COMM_ERR_STR, i2c->bus->name, i2c->addr, "w8", reg, value);
246 }
247
248 return ret;
249 }
250
tcpci_update_reg8(const struct i2c_dt_spec * i2c,uint8_t reg,uint8_t mask,uint8_t value)251 int tcpci_update_reg8(const struct i2c_dt_spec *i2c, uint8_t reg, uint8_t mask, uint8_t value)
252 {
253 uint8_t old_value;
254 int ret;
255
256 ret = tcpci_read_reg8(i2c, reg, &old_value);
257 if (ret != 0) {
258 return ret;
259 }
260
261 old_value &= ~mask;
262 old_value |= (value & mask);
263
264 ret = tcpci_write_reg8(i2c, reg, old_value);
265
266 return ret;
267 }
268
tcpci_read_reg16(const struct i2c_dt_spec * i2c,uint8_t reg,uint16_t * value)269 int tcpci_read_reg16(const struct i2c_dt_spec *i2c, uint8_t reg, uint16_t *value)
270 {
271 int ret;
272
273 for (int a = 0; a < CONFIG_USBC_TCPC_TCPCI_I2C_RETRIES; a++) {
274 ret = i2c_write_read(i2c->bus, i2c->addr, ®, sizeof(reg), value, sizeof(*value));
275
276 if (ret == 0) {
277 *value = sys_le16_to_cpu(*value);
278 break;
279 }
280 }
281
282 if (ret != 0) {
283 LOG_ERR(LOG_COMM_ERR_STR, i2c->bus->name, i2c->addr, "r16", reg, *value);
284 }
285
286 return ret;
287 }
288
tcpci_write_reg16(const struct i2c_dt_spec * i2c,uint8_t reg,uint16_t value)289 int tcpci_write_reg16(const struct i2c_dt_spec *i2c, uint8_t reg, uint16_t value)
290 {
291 value = sys_cpu_to_le16(value);
292 uint8_t *value_ptr = (uint8_t *)&value;
293 uint8_t buf[3] = {reg, value_ptr[0], value_ptr[1]};
294 int ret;
295
296 for (int a = 0; a < CONFIG_USBC_TCPC_TCPCI_I2C_RETRIES; a++) {
297 ret = i2c_write(i2c->bus, buf, 3, i2c->addr);
298 if (ret == 0) {
299 break;
300 }
301 }
302
303 if (ret != 0) {
304 LOG_ERR(LOG_COMM_ERR_STR, i2c->bus->name, i2c->addr, "w16", reg, value);
305 }
306
307 return ret;
308 }
309
tcpci_alert_reg_to_enum(uint16_t reg)310 enum tcpc_alert tcpci_alert_reg_to_enum(uint16_t reg)
311 {
312 /**
313 * Hard reset enum has priority since it causes other bits to be ignored. Other values
314 * are sorted by corresponding bits index in the register.
315 */
316 if (reg & TCPC_REG_ALERT_RX_HARD_RST) {
317 /** Received Hard Reset message */
318 return TCPC_ALERT_HARD_RESET_RECEIVED;
319 } else if (reg & TCPC_REG_ALERT_CC_STATUS) {
320 /** CC status changed */
321 return TCPC_ALERT_CC_STATUS;
322 } else if (reg & TCPC_REG_ALERT_POWER_STATUS) {
323 /** Power status changed */
324 return TCPC_ALERT_POWER_STATUS;
325 } else if (reg & TCPC_REG_ALERT_RX_STATUS) {
326 /** Receive Buffer register changed */
327 return TCPC_ALERT_MSG_STATUS;
328 } else if (reg & TCPC_REG_ALERT_TX_FAILED) {
329 /** SOP* message transmission not successful */
330 return TCPC_ALERT_TRANSMIT_MSG_FAILED;
331 } else if (reg & TCPC_REG_ALERT_TX_DISCARDED) {
332 /**
333 * Reset or SOP* message transmission not sent
334 * due to an incoming receive message
335 */
336 return TCPC_ALERT_TRANSMIT_MSG_DISCARDED;
337 } else if (reg & TCPC_REG_ALERT_TX_SUCCESS) {
338 /** Reset or SOP* message transmission successful */
339 return TCPC_ALERT_TRANSMIT_MSG_SUCCESS;
340 } else if (reg & TCPC_REG_ALERT_V_ALARM_HI) {
341 /** A high-voltage alarm has occurred */
342 return TCPC_ALERT_VBUS_ALARM_HI;
343 } else if (reg & TCPC_REG_ALERT_V_ALARM_LO) {
344 /** A low-voltage alarm has occurred */
345 return TCPC_ALERT_VBUS_ALARM_LO;
346 } else if (reg & TCPC_REG_ALERT_FAULT) {
347 /** A fault has occurred. Read the FAULT_STATUS register */
348 return TCPC_ALERT_FAULT_STATUS;
349 } else if (reg & TCPC_REG_ALERT_RX_BUF_OVF) {
350 /** TCPC RX buffer has overflowed */
351 return TCPC_ALERT_RX_BUFFER_OVERFLOW;
352 } else if (reg & TCPC_REG_ALERT_VBUS_DISCNCT) {
353 /** The TCPC in Attached.SNK state has detected a sink disconnect */
354 return TCPC_ALERT_VBUS_SNK_DISCONNECT;
355 } else if (reg & TCPC_REG_ALERT_RX_BEGINNING) {
356 /** Receive buffer register changed */
357 return TCPC_ALERT_BEGINNING_MSG_STATUS;
358 } else if (reg & TCPC_REG_ALERT_EXT_STATUS) {
359 /** Extended status changed */
360 return TCPC_ALERT_EXTENDED_STATUS;
361 } else if (reg & TCPC_REG_ALERT_ALERT_EXT) {
362 /**
363 * An extended interrupt event has occurred. Read the alert_extended
364 * register
365 */
366 return TCPC_ALERT_EXTENDED;
367 } else if (reg & TCPC_REG_ALERT_VENDOR_DEF) {
368 /** A vendor defined alert has been detected */
369 return TCPC_ALERT_VENDOR_DEFINED;
370 }
371
372 LOG_ERR("Invalid alert register value");
373 return -1;
374 }
375
tcpci_tcpm_get_cc(const struct i2c_dt_spec * bus,enum tc_cc_voltage_state * cc1,enum tc_cc_voltage_state * cc2)376 int tcpci_tcpm_get_cc(const struct i2c_dt_spec *bus, enum tc_cc_voltage_state *cc1,
377 enum tc_cc_voltage_state *cc2)
378 {
379 uint8_t role;
380 uint8_t status;
381 int cc1_present_rd, cc2_present_rd;
382 int rv;
383
384 if (cc1 == NULL || cc2 == NULL) {
385 return -EINVAL;
386 }
387
388 /* errors will return CC as open */
389 *cc1 = TC_CC_VOLT_OPEN;
390 *cc2 = TC_CC_VOLT_OPEN;
391
392 /* Get the ROLE CONTROL and CC STATUS values */
393 rv = tcpci_read_reg8(bus, TCPC_REG_ROLE_CTRL, &role);
394 if (rv != 0) {
395 return rv;
396 }
397
398 rv = tcpci_read_reg8(bus, TCPC_REG_CC_STATUS, &status);
399 if (rv != 0) {
400 return rv;
401 }
402
403 /* Get the current CC values from the CC STATUS */
404 *cc1 = TCPC_REG_CC_STATUS_CC1_STATE(status);
405 *cc2 = TCPC_REG_CC_STATUS_CC2_STATE(status);
406
407 /* Determine if we are presenting Rd */
408 cc1_present_rd = 0;
409 cc2_present_rd = 0;
410 if (role & TCPC_REG_ROLE_CTRL_DRP_MASK) {
411 /*
412 * We are doing DRP. We will use the CC STATUS
413 * ConnectResult to determine if we are presenting
414 * Rd or Rp.
415 */
416 int term;
417
418 term = !!(status & TCPC_REG_CC_STATUS_CONNECT_RESULT);
419
420 if (*cc1 != TC_CC_VOLT_OPEN) {
421 cc1_present_rd = term;
422 }
423 if (*cc2 != TC_CC_VOLT_OPEN) {
424 cc2_present_rd = term;
425 }
426 } else {
427 /*
428 * We are not doing DRP. We will use the ROLE CONTROL
429 * CC values to determine if we are presenting Rd or Rp.
430 */
431 int role_cc1, role_cc2;
432
433 role_cc1 = TCPC_REG_ROLE_CTRL_CC1(role);
434 role_cc2 = TCPC_REG_ROLE_CTRL_CC2(role);
435
436 if (*cc1 != TC_CC_VOLT_OPEN) {
437 cc1_present_rd = (role_cc1 == TC_CC_RD);
438 }
439 if (*cc2 != TC_CC_VOLT_OPEN) {
440 cc2_present_rd = (role_cc2 == TC_CC_RD);
441 }
442 }
443
444 *cc1 |= cc1_present_rd << 2;
445 *cc2 |= cc2_present_rd << 2;
446
447 return 0;
448 }
449