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, &reg, 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, &reg, 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