xref: /openbsd-src/sys/dev/fdt/tcpci.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1*471aeecfSnaddy /* $OpenBSD: tcpci.c,v 1.3 2022/04/06 18:59:28 naddy Exp $ */
2e2419160Spatrick /*
3e2419160Spatrick  * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
4e2419160Spatrick  *
5e2419160Spatrick  * Permission to use, copy, modify, and distribute this software for any
6e2419160Spatrick  * purpose with or without fee is hereby granted, provided that the above
7e2419160Spatrick  * copyright notice and this permission notice appear in all copies.
8e2419160Spatrick  *
9e2419160Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10e2419160Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11e2419160Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12e2419160Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13e2419160Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14e2419160Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15e2419160Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16e2419160Spatrick  */
17e2419160Spatrick 
18e2419160Spatrick #include <sys/param.h>
19e2419160Spatrick #include <sys/systm.h>
20e2419160Spatrick #include <sys/kernel.h>
21e2419160Spatrick #include <sys/device.h>
22e2419160Spatrick #include <sys/malloc.h>
23e2419160Spatrick #include <sys/stdint.h>
24e2419160Spatrick #include <sys/task.h>
25e2419160Spatrick 
26e2419160Spatrick #include <machine/bus.h>
27e2419160Spatrick #include <machine/fdt.h>
28e2419160Spatrick 
29e2419160Spatrick #include <dev/i2c/i2cvar.h>
30e2419160Spatrick #include <dev/ofw/openfirm.h>
31e2419160Spatrick #include <dev/ofw/ofw_gpio.h>
32e2419160Spatrick #include <dev/ofw/ofw_misc.h>
33e2419160Spatrick #include <dev/ofw/ofw_pinctrl.h>
34e2419160Spatrick 
35e2419160Spatrick /* #define TCPCI_DEBUG */
36e2419160Spatrick 
37e2419160Spatrick #ifdef TCPCI_DEBUG
38e2419160Spatrick #define DPRINTF(x) printf x
39e2419160Spatrick #else
40e2419160Spatrick #define DPRINTF(x)
41e2419160Spatrick #endif
42e2419160Spatrick 
43e2419160Spatrick #define TCPC_VENDOR_ID				0x00
44e2419160Spatrick #define TCPC_PRODUCT_ID				0x02
45e2419160Spatrick #define TCPC_BCD_DEV				0x04
46e2419160Spatrick #define TCPC_TC_REV				0x06
47e2419160Spatrick #define TCPC_PD_REV				0x08
48e2419160Spatrick #define TCPC_PD_INT_REV				0x0a
49e2419160Spatrick #define TCPC_ALERT				0x10
50e2419160Spatrick #define  TCPC_ALERT_CC_STATUS				(1 << 0)
51e2419160Spatrick #define  TCPC_ALERT_POWER_STATUS			(1 << 1)
52e2419160Spatrick #define  TCPC_ALERT_RX_STATUS				(1 << 2)
53e2419160Spatrick #define  TCPC_ALERT_RX_HARD_RST				(1 << 3)
54e2419160Spatrick #define  TCPC_ALERT_TX_FAILED				(1 << 4)
55e2419160Spatrick #define  TCPC_ALERT_TX_DISCARDED			(1 << 5)
56e2419160Spatrick #define  TCPC_ALERT_TX_SUCCESS				(1 << 6)
57e2419160Spatrick #define  TCPC_ALERT_V_ALARM_HI				(1 << 7)
58e2419160Spatrick #define  TCPC_ALERT_V_ALARM_LO				(1 << 8)
59e2419160Spatrick #define  TCPC_ALERT_FAULT				(1 << 9)
60e2419160Spatrick #define  TCPC_ALERT_RX_BUF_OVF				(1 << 10)
61e2419160Spatrick #define  TCPC_ALERT_VBUS_DISCNCT			(1 << 11)
62e2419160Spatrick #define TCPC_ALERT_MASK				0x12
63e2419160Spatrick #define TCPC_POWER_STATUS_MASK			0x14
64e2419160Spatrick #define  TCPC_POWER_STATUS_VBUS_PRES			(1 << 2)
65e2419160Spatrick #define TCPC_FAULT_STATUS_MASK			0x15
66e2419160Spatrick #define TCPC_CONFIG_STD_OUTPUT			0x18
67e2419160Spatrick #define TCPC_TCPC_CTRL				0x19
68e2419160Spatrick #define  TCPC_TCPC_CTRL_ORIENTATION			(1 << 0)
69e2419160Spatrick #define  TCPC_TCPC_CTRL_BIST_MODE			(1 << 1)
70e2419160Spatrick #define TCPC_ROLE_CTRL				0x1a
71e2419160Spatrick #define  TCPC_ROLE_CTRL_CC1_SHIFT			0
72e2419160Spatrick #define  TCPC_ROLE_CTRL_CC2_SHIFT			2
73e2419160Spatrick #define  TCPC_ROLE_CTRL_CC_RA				0x0
74e2419160Spatrick #define  TCPC_ROLE_CTRL_CC_RP				0x1
75e2419160Spatrick #define  TCPC_ROLE_CTRL_CC_RD				0x2
76e2419160Spatrick #define  TCPC_ROLE_CTRL_CC_OPEN				0x3
77e2419160Spatrick #define  TCPC_ROLE_CTRL_CC_MASK				0x3
78e2419160Spatrick #define  TCPC_ROLE_CTRL_RP_VAL_MASK			(0x3 << 4)
79e2419160Spatrick #define  TCPC_ROLE_CTRL_RP_VAL_DEF			(0x0 << 4)
80e2419160Spatrick #define  TCPC_ROLE_CTRL_RP_VAL_1_5			(0x1 << 4)
81e2419160Spatrick #define  TCPC_ROLE_CTRL_RP_VAL_3_0			(0x2 << 4)
82e2419160Spatrick #define  TCPC_ROLE_CTRL_DRP				(1 << 6)
83e2419160Spatrick #define TCPC_FAULT_CTRL				0x1b
84e2419160Spatrick #define TCPC_POWER_CTRL				0x1c
85e2419160Spatrick #define  TCPC_POWER_CTRL_VCONN_ENABLE			(1 << 0)
86e2419160Spatrick #define  TCPC_POWER_CTRL_FORCEDISCH			(1 << 2)
87e2419160Spatrick #define  TCPC_POWER_CTRL_DIS_VOL_ALARM			(1 << 5)
88e2419160Spatrick #define TCPC_CC_STATUS				0x1d
89e2419160Spatrick #define  TCPC_CC_STATUS_CC1_SHIFT			0
90e2419160Spatrick #define  TCPC_CC_STATUS_CC2_SHIFT			2
91e2419160Spatrick #define  TCPC_CC_STATUS_CC_MASK				0x3
92e2419160Spatrick #define  TCPC_CC_STATUS_TERM				(1 << 4)
93e2419160Spatrick #define  TCPC_CC_STATUS_TOGGLING			(1 << 5)
94e2419160Spatrick #define TCPC_POWER_STATUS			0x1e
95e2419160Spatrick #define TCPC_FAULT_STATUS			0x1f
96e2419160Spatrick #define  TCPC_FAULT_STATUS_CLEAR			(1 << 7)
97e2419160Spatrick #define TCPC_COMMAND				0x23
98e2419160Spatrick #define  TCPC_COMMAND_WAKE_I2C				0x11
99e2419160Spatrick #define  TCPC_COMMAND_DISABLE_VBUS_DETECT		0x22
100e2419160Spatrick #define  TCPC_COMMAND_ENABLE_VBUS_DETECT		0x33
101e2419160Spatrick #define  TCPC_COMMAND_DISABLE_SINK_VBUS			0x44
102e2419160Spatrick #define  TCPC_COMMAND_SINK_VBUS				0x55
103e2419160Spatrick #define  TCPC_COMMAND_DISABLE_SRC_VBUS			0x66
104e2419160Spatrick #define  TCPC_COMMAND_SRC_VBUS_DEFAULT			0x77
105e2419160Spatrick #define  TCPC_COMMAND_SRC_VBUS_HIGH			0x88
106e2419160Spatrick #define  TCPC_COMMAND_LOOK4CONNECTION			0x99
107e2419160Spatrick #define  TCPC_COMMAND_RXONEMORE				0xAA
108e2419160Spatrick #define  TCPC_COMMAND_I2C_IDLE				0xFF
109e2419160Spatrick #define TCPC_DEV_CAP_1				0x24
110e2419160Spatrick #define TCPC_DEV_CAP_2				0x26
111e2419160Spatrick #define TCPC_STD_INPUT_CAP			0x28
112e2419160Spatrick #define TCPC_STD_OUTPUT_CAP			0x29
113e2419160Spatrick #define TCPC_MSG_HDR_INFO			0x2e
114e2419160Spatrick #define  TCPC_MSG_HDR_INFO_PWR_ROLE			(1 << 0)
115e2419160Spatrick #define  TCPC_MSG_HDR_INFO_PD_REV10			(0 << 1)
116e2419160Spatrick #define  TCPC_MSG_HDR_INFO_PD_REV20			(1 << 1)
117e2419160Spatrick #define  TCPC_MSG_HDR_INFO_DATA_ROLE			(1 << 3)
118e2419160Spatrick #define TCPC_RX_DETECT				0x2f
119e2419160Spatrick #define  TCPC_RX_DETECT_SOP				(1 << 0)
120e2419160Spatrick #define  TCPC_RX_DETECT_HARD_RESET			(1 << 5)
121e2419160Spatrick #define TCPC_RX_BYTE_CNT			0x30
122e2419160Spatrick #define TCPC_RX_BUF_FRAME_TYPE			0x31
123e2419160Spatrick #define TCPC_RX_HDR				0x32
124e2419160Spatrick #define TCPC_RX_DATA				0x34 /* through 0x4f */
125e2419160Spatrick #define TCPC_TRANSMIT				0x50
126e2419160Spatrick #define TCPC_TX_BYTE_CNT			0x51
127e2419160Spatrick #define TCPC_TX_HDR				0x52
128e2419160Spatrick #define TCPC_TX_DATA				0x54 /* through 0x6f */
129e2419160Spatrick #define TCPC_VBUS_VOLTAGE			0x70
130e2419160Spatrick #define TCPC_VBUS_SINK_DISCONNECT_THRESH	0x72
131e2419160Spatrick #define TCPC_VBUS_STOP_DISCHARGE_THRESH		0x74
132e2419160Spatrick #define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG		0x76
133e2419160Spatrick #define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG		0x78
134e2419160Spatrick 
135e2419160Spatrick enum typec_cc_status {
136e2419160Spatrick 	TYPEC_CC_OPEN,
137e2419160Spatrick 	TYPEC_CC_RA,
138e2419160Spatrick 	TYPEC_CC_RD,
139e2419160Spatrick 	TYPEC_CC_RP_DEF,
140e2419160Spatrick 	TYPEC_CC_RP_1_5,
141e2419160Spatrick 	TYPEC_CC_RP_3_0,
142e2419160Spatrick };
143e2419160Spatrick 
144e2419160Spatrick enum typec_data_role {
145e2419160Spatrick 	TYPEC_DEVICE,
146e2419160Spatrick 	TYPEC_HOST,
147e2419160Spatrick };
148e2419160Spatrick 
149e2419160Spatrick enum typec_power_role {
150e2419160Spatrick 	TYPEC_SINK,
151e2419160Spatrick 	TYPEC_SOURCE,
152e2419160Spatrick };
153e2419160Spatrick 
154e2419160Spatrick enum typec_polarity {
155e2419160Spatrick 	TYPEC_POLARITY_CC1,
156e2419160Spatrick 	TYPEC_POLARITY_CC2,
157e2419160Spatrick };
158e2419160Spatrick 
159e2419160Spatrick struct tcpci_softc {
160e2419160Spatrick 	struct device		 sc_dev;
161e2419160Spatrick 	i2c_tag_t		 sc_tag;
162e2419160Spatrick 	i2c_addr_t		 sc_addr;
163e2419160Spatrick 	int			 sc_node;
164e2419160Spatrick 	void			*sc_ih;
165e2419160Spatrick 
166e2419160Spatrick 	struct task		 sc_task;
167e2419160Spatrick 
168e2419160Spatrick 	int			 sc_attached;
169e2419160Spatrick 	enum typec_data_role	 sc_try_data;
170e2419160Spatrick 	enum typec_power_role	 sc_try_power;
171e2419160Spatrick 
172e2419160Spatrick 	uint32_t		*sc_ss_sel;
173e2419160Spatrick 	uint8_t			 sc_cc;
174e2419160Spatrick 	uint8_t			 sc_vbus_det;
175e2419160Spatrick };
176e2419160Spatrick 
177e2419160Spatrick int	 tcpci_match(struct device *, void *, void *);
178e2419160Spatrick void	 tcpci_attach(struct device *, struct device *, void *);
179e2419160Spatrick int	 tcpci_detach(struct device *, int);
180e2419160Spatrick 
181e2419160Spatrick int	 tcpci_intr(void *);
182e2419160Spatrick void	 tcpci_task(void *);
183e2419160Spatrick void	 tcpci_cc_change(struct tcpci_softc *);
184e2419160Spatrick void	 tcpci_power_change(struct tcpci_softc *);
185e2419160Spatrick void	 tcpci_set_polarity(struct tcpci_softc *, int);
186e2419160Spatrick void	 tcpci_set_vbus(struct tcpci_softc *, int, int);
187e2419160Spatrick void	 tcpci_set_roles(struct tcpci_softc *, enum typec_data_role,
188e2419160Spatrick 	    enum typec_power_role);
189e2419160Spatrick 
190e2419160Spatrick void	 tcpci_write_reg16(struct tcpci_softc *, uint8_t, uint16_t);
191e2419160Spatrick uint16_t tcpci_read_reg16(struct tcpci_softc *, uint8_t);
192e2419160Spatrick void	 tcpci_write_reg8(struct tcpci_softc *, uint8_t, uint8_t);
193e2419160Spatrick uint8_t	 tcpci_read_reg8(struct tcpci_softc *, uint8_t);
194e2419160Spatrick 
195*471aeecfSnaddy const struct cfattach tcpci_ca = {
196e2419160Spatrick 	sizeof(struct tcpci_softc),
197e2419160Spatrick 	tcpci_match,
198e2419160Spatrick 	tcpci_attach,
199e2419160Spatrick 	tcpci_detach,
200e2419160Spatrick };
201e2419160Spatrick 
202e2419160Spatrick struct cfdriver tcpci_cd = {
203e2419160Spatrick 	NULL, "tcpci", DV_DULL
204e2419160Spatrick };
205e2419160Spatrick 
206e2419160Spatrick int
tcpci_match(struct device * parent,void * match,void * aux)207e2419160Spatrick tcpci_match(struct device *parent, void *match, void *aux)
208e2419160Spatrick {
209e2419160Spatrick 	struct i2c_attach_args *ia = aux;
210e2419160Spatrick 
211e2419160Spatrick 	if (strcmp(ia->ia_name, "nxp,ptn5110") == 0)
212e2419160Spatrick 		return 1;
213e2419160Spatrick 
214e2419160Spatrick 	return 0;
215e2419160Spatrick }
216e2419160Spatrick 
217e2419160Spatrick void
tcpci_attach(struct device * parent,struct device * self,void * aux)218e2419160Spatrick tcpci_attach(struct device *parent, struct device *self, void *aux)
219e2419160Spatrick {
220e2419160Spatrick 	struct tcpci_softc *sc = (struct tcpci_softc *)self;
221e2419160Spatrick 	struct i2c_attach_args *ia = aux;
222e2419160Spatrick 	int len;
223e2419160Spatrick 
224e2419160Spatrick 	sc->sc_tag = ia->ia_tag;
225e2419160Spatrick 	sc->sc_addr = ia->ia_addr;
226e2419160Spatrick 	sc->sc_node = *(int *)ia->ia_cookie;
227e2419160Spatrick 
228e2419160Spatrick 	/* Automatic DRP toggling should try first as ... */
229e2419160Spatrick 	sc->sc_try_data = TYPEC_HOST;
230e2419160Spatrick 	sc->sc_try_power = TYPEC_SOURCE;
231e2419160Spatrick 
232e2419160Spatrick 	pinctrl_byname(sc->sc_node, "default");
233e2419160Spatrick 
234e2419160Spatrick 	task_set(&sc->sc_task, tcpci_task, sc);
235f776eaaeSpatrick 	sc->sc_ih = fdt_intr_establish(sc->sc_node, IPL_BIO,
236e2419160Spatrick 	    tcpci_intr, sc, sc->sc_dev.dv_xname);
237e2419160Spatrick 	if (sc->sc_ih == NULL) {
238e2419160Spatrick 		printf(": unable to establish interrupt\n");
239e2419160Spatrick 		return;
240e2419160Spatrick 	}
241e2419160Spatrick 
242e2419160Spatrick 	len = OF_getproplen(sc->sc_node, "ss-sel-gpios");
243e2419160Spatrick 	if (len > 0) {
244e2419160Spatrick 		sc->sc_ss_sel = malloc(len, M_TEMP, M_WAITOK);
245e2419160Spatrick 		OF_getpropintarray(sc->sc_node, "ss-sel-gpios",
246e2419160Spatrick 		    sc->sc_ss_sel, len);
247e2419160Spatrick 		gpio_controller_config_pin(sc->sc_ss_sel,
248e2419160Spatrick 		    GPIO_CONFIG_OUTPUT);
249e2419160Spatrick 		gpio_controller_set_pin(sc->sc_ss_sel, 1);
250e2419160Spatrick 	}
251e2419160Spatrick 
252e2419160Spatrick 	tcpci_write_reg16(sc, TCPC_ALERT, 0xffff);
253e2419160Spatrick 	tcpci_write_reg8(sc, TCPC_FAULT_STATUS, 0x80);
254e2419160Spatrick 	tcpci_write_reg8(sc, TCPC_POWER_STATUS_MASK,
255e2419160Spatrick 	    TCPC_POWER_STATUS_VBUS_PRES);
256e2419160Spatrick 	tcpci_write_reg8(sc, TCPC_POWER_CTRL, tcpci_read_reg8(sc,
257e2419160Spatrick 	    TCPC_POWER_CTRL) & ~TCPC_POWER_CTRL_DIS_VOL_ALARM);
258e2419160Spatrick 	tcpci_write_reg16(sc, TCPC_ALERT_MASK,
259e2419160Spatrick 	    TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED |
260e2419160Spatrick 	    TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS |
261e2419160Spatrick 	    TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS |
262e2419160Spatrick 	    TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_FAULT |
263e2419160Spatrick 	    TCPC_ALERT_V_ALARM_LO | TCPC_ALERT_POWER_STATUS);
264e2419160Spatrick 
265e2419160Spatrick 	if (sc->sc_try_data == TYPEC_HOST)
266e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP | 0xa);
267e2419160Spatrick 	else
268e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP | 0x5);
269e2419160Spatrick 	tcpci_write_reg8(sc, TCPC_COMMAND, TCPC_COMMAND_LOOK4CONNECTION);
270e2419160Spatrick 
271e2419160Spatrick 	printf("\n");
272e2419160Spatrick }
273e2419160Spatrick 
274e2419160Spatrick int
tcpci_detach(struct device * self,int flags)275e2419160Spatrick tcpci_detach(struct device *self, int flags)
276e2419160Spatrick {
277e2419160Spatrick 	return 0;
278e2419160Spatrick }
279e2419160Spatrick 
280e2419160Spatrick int
tcpci_intr(void * args)281e2419160Spatrick tcpci_intr(void *args)
282e2419160Spatrick {
283e2419160Spatrick 	struct tcpci_softc *sc = args;
284e2419160Spatrick 	fdt_intr_disable(sc->sc_ih);
285e2419160Spatrick 	task_add(systq, &sc->sc_task);
286e2419160Spatrick 	return 1;
287e2419160Spatrick }
288e2419160Spatrick 
289e2419160Spatrick void
tcpci_task(void * args)290e2419160Spatrick tcpci_task(void *args)
291e2419160Spatrick {
292e2419160Spatrick 	struct tcpci_softc *sc = args;
293e2419160Spatrick 	uint16_t status;
294e2419160Spatrick 
295e2419160Spatrick 	status = tcpci_read_reg16(sc, TCPC_ALERT);
296e2419160Spatrick 	tcpci_write_reg16(sc, TCPC_ALERT, status);
297e2419160Spatrick 
298e2419160Spatrick 	DPRINTF(("%s: alert %x\n", __func__, status));
299e2419160Spatrick 
300e2419160Spatrick 	if (status & TCPC_ALERT_CC_STATUS)
301e2419160Spatrick 		tcpci_cc_change(sc);
302e2419160Spatrick 
303e2419160Spatrick 	if (status & TCPC_ALERT_POWER_STATUS)
304e2419160Spatrick 		tcpci_power_change(sc);
305e2419160Spatrick 
306e2419160Spatrick 	if (status & TCPC_ALERT_V_ALARM_LO) {
307e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_VBUS_VOLTAGE_ALARM_LO_CFG, 0);
308e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_POWER_CTRL, tcpci_read_reg8(sc,
309e2419160Spatrick 		    TCPC_POWER_CTRL) & ~TCPC_POWER_CTRL_FORCEDISCH);
310e2419160Spatrick 	}
311e2419160Spatrick 
312e2419160Spatrick 	if (status & TCPC_ALERT_FAULT)
313e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_FAULT_STATUS, tcpci_read_reg8(sc,
314e2419160Spatrick 		    TCPC_FAULT_STATUS) | TCPC_FAULT_STATUS_CLEAR);
315e2419160Spatrick 
316e2419160Spatrick 	fdt_intr_enable(sc->sc_ih);
317e2419160Spatrick }
318e2419160Spatrick 
319e2419160Spatrick uint8_t
tcpci_typec_to_rp(int typec)320e2419160Spatrick tcpci_typec_to_rp(int typec)
321e2419160Spatrick {
322e2419160Spatrick 	switch (typec) {
323e2419160Spatrick 	case TYPEC_CC_RP_DEF:
324e2419160Spatrick 		return TCPC_ROLE_CTRL_RP_VAL_DEF;
325e2419160Spatrick 	case TYPEC_CC_RP_1_5:
326e2419160Spatrick 		return TCPC_ROLE_CTRL_RP_VAL_1_5;
327e2419160Spatrick 	case TYPEC_CC_RP_3_0:
328e2419160Spatrick 		return TCPC_ROLE_CTRL_RP_VAL_3_0;
329e2419160Spatrick 	default:
330e2419160Spatrick 		panic("%s:%d", __func__, __LINE__);
331e2419160Spatrick 	}
332e2419160Spatrick }
333e2419160Spatrick 
334e2419160Spatrick int
tcpci_cc_to_typec(int cc,int sink)335e2419160Spatrick tcpci_cc_to_typec(int cc, int sink)
336e2419160Spatrick {
337e2419160Spatrick 	if (sink) {
338e2419160Spatrick 		if (cc == 0x1)
339e2419160Spatrick 			return TYPEC_CC_RP_DEF;
340e2419160Spatrick 		if (cc == 0x2)
341e2419160Spatrick 			return TYPEC_CC_RP_1_5;
342e2419160Spatrick 		if (cc == 0x3)
343e2419160Spatrick 			return TYPEC_CC_RP_3_0;
344e2419160Spatrick 	} else {
345e2419160Spatrick 		if (cc == 0x1)
346e2419160Spatrick 			return TYPEC_CC_RA;
347e2419160Spatrick 		if (cc == 0x2)
348e2419160Spatrick 			return TYPEC_CC_RD;
349e2419160Spatrick 	}
350e2419160Spatrick 
351e2419160Spatrick 	return TYPEC_CC_OPEN;
352e2419160Spatrick }
353e2419160Spatrick 
354e2419160Spatrick int
tcpci_cc_is_sink(int cc1,int cc2)355e2419160Spatrick tcpci_cc_is_sink(int cc1, int cc2)
356e2419160Spatrick {
357e2419160Spatrick 	if ((cc1 == TYPEC_CC_RP_DEF || cc1 == TYPEC_CC_RP_1_5 ||
358e2419160Spatrick 	    cc1 == TYPEC_CC_RP_3_0) && cc2 == TYPEC_CC_OPEN)
359e2419160Spatrick 		return 1;
360e2419160Spatrick 	if ((cc2 == TYPEC_CC_RP_DEF || cc2 == TYPEC_CC_RP_1_5 ||
361e2419160Spatrick 	    cc2 == TYPEC_CC_RP_3_0) && cc1 == TYPEC_CC_OPEN)
362e2419160Spatrick 		return 1;
363e2419160Spatrick 	return 0;
364e2419160Spatrick }
365e2419160Spatrick 
366e2419160Spatrick int
tcpci_cc_is_source(int cc1,int cc2)367e2419160Spatrick tcpci_cc_is_source(int cc1, int cc2)
368e2419160Spatrick {
369e2419160Spatrick 	if (cc1 == TYPEC_CC_RD && cc2 != TYPEC_CC_RD)
370e2419160Spatrick 		return 1;
371e2419160Spatrick 	if (cc2 == TYPEC_CC_RD && cc1 != TYPEC_CC_RD)
372e2419160Spatrick 		return 1;
373e2419160Spatrick 	return 0;
374e2419160Spatrick }
375e2419160Spatrick 
376e2419160Spatrick int
tcpci_cc_is_audio(int cc1,int cc2)377e2419160Spatrick tcpci_cc_is_audio(int cc1, int cc2)
378e2419160Spatrick {
379e2419160Spatrick 	if (cc1 == TYPEC_CC_RA && cc2 == TYPEC_CC_RA)
380e2419160Spatrick 		return 1;
381e2419160Spatrick 	return 0;
382e2419160Spatrick }
383e2419160Spatrick 
384e2419160Spatrick int
tcpci_cc_is_audio_detached(int cc1,int cc2)385e2419160Spatrick tcpci_cc_is_audio_detached(int cc1, int cc2)
386e2419160Spatrick {
387e2419160Spatrick 	if (cc1 == TYPEC_CC_RA && cc2 == TYPEC_CC_OPEN)
388e2419160Spatrick 		return 1;
389e2419160Spatrick 	if (cc2 == TYPEC_CC_RA && cc1 == TYPEC_CC_OPEN)
390e2419160Spatrick 		return 1;
391e2419160Spatrick 	return 0;
392e2419160Spatrick }
393e2419160Spatrick 
394e2419160Spatrick void
tcpci_cc_change(struct tcpci_softc * sc)395e2419160Spatrick tcpci_cc_change(struct tcpci_softc *sc)
396e2419160Spatrick {
397e2419160Spatrick 	uint8_t cc, cc1, cc2;
398e2419160Spatrick 
399e2419160Spatrick 	cc = tcpci_read_reg8(sc, TCPC_CC_STATUS);
400e2419160Spatrick 	if (sc->sc_cc == cc)
401e2419160Spatrick 		return;
402e2419160Spatrick 
403e2419160Spatrick 	cc1 = (cc >> TCPC_ROLE_CTRL_CC1_SHIFT) & TCPC_ROLE_CTRL_CC_MASK;
404e2419160Spatrick 	cc1 = tcpci_cc_to_typec(cc1, cc & TCPC_CC_STATUS_TERM);
405e2419160Spatrick 	cc2 = (cc >> TCPC_ROLE_CTRL_CC2_SHIFT) & TCPC_ROLE_CTRL_CC_MASK;
406e2419160Spatrick 	cc2 = tcpci_cc_to_typec(cc2, cc & TCPC_CC_STATUS_TERM);
407e2419160Spatrick 
408e2419160Spatrick 	if (cc1 == TYPEC_CC_OPEN && cc2 == TYPEC_CC_OPEN) {
409e2419160Spatrick 		/* No CC, wait for new connection. */
410e2419160Spatrick 		DPRINTF(("%s: disconnected\n", __func__));
411e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_RX_DETECT, 0);
412e2419160Spatrick 		tcpci_set_vbus(sc, 0, 0);
413e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_POWER_CTRL, tcpci_read_reg8(sc,
414e2419160Spatrick 		    TCPC_POWER_CTRL) & ~TCPC_POWER_CTRL_VCONN_ENABLE);
415e2419160Spatrick 		tcpci_set_polarity(sc, TYPEC_POLARITY_CC1);
416e2419160Spatrick 		if (sc->sc_try_data == TYPEC_HOST) {
417e2419160Spatrick 			tcpci_write_reg8(sc, TCPC_ROLE_CTRL,
418e2419160Spatrick 			    TCPC_ROLE_CTRL_DRP | 0xa);
419e2419160Spatrick 		} else {
420e2419160Spatrick 			tcpci_write_reg8(sc, TCPC_ROLE_CTRL,
421e2419160Spatrick 			    TCPC_ROLE_CTRL_DRP | 0x5);
422e2419160Spatrick 		}
423e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_COMMAND,
424e2419160Spatrick 		    TCPC_COMMAND_LOOK4CONNECTION);
425e2419160Spatrick 		sc->sc_attached = 0;
426e2419160Spatrick 	} else if (tcpci_cc_is_source(cc1, cc2)) {
427e2419160Spatrick 		/* Host */
428e2419160Spatrick 		DPRINTF(("%s: attached as source\n", __func__));
429e2419160Spatrick 		if (cc1 == TYPEC_CC_RD)
430e2419160Spatrick 			tcpci_set_polarity(sc, TYPEC_POLARITY_CC1);
431e2419160Spatrick 		else
432e2419160Spatrick 			tcpci_set_polarity(sc, TYPEC_POLARITY_CC2);
433e2419160Spatrick 		tcpci_set_roles(sc, TYPEC_HOST, TYPEC_SOURCE);
434e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_RX_DETECT,
435e2419160Spatrick 		    TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET);
436e2419160Spatrick 		if ((cc1 == TYPEC_CC_RD && cc2 == TYPEC_CC_RA) ||
437e2419160Spatrick 		    (cc2 == TYPEC_CC_RD && cc1 == TYPEC_CC_RA))
438e2419160Spatrick 			tcpci_write_reg8(sc, TCPC_POWER_CTRL, tcpci_read_reg8(sc,
439e2419160Spatrick 			    TCPC_POWER_CTRL) | TCPC_POWER_CTRL_VCONN_ENABLE);
440e2419160Spatrick 		tcpci_set_vbus(sc, 1, 0);
441e2419160Spatrick 		sc->sc_attached = 1;
442e2419160Spatrick 	} else if (tcpci_cc_is_sink(cc1, cc2)) {
443e2419160Spatrick 		/* Device */
444e2419160Spatrick 		DPRINTF(("%s: attached as sink\n", __func__));
445e2419160Spatrick 		if (cc1 != TYPEC_CC_OPEN) {
446e2419160Spatrick 			tcpci_set_polarity(sc, TYPEC_POLARITY_CC1);
447e2419160Spatrick 			tcpci_write_reg8(sc, TCPC_ROLE_CTRL,
448e2419160Spatrick 			    TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT |
449e2419160Spatrick 			    TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
450e2419160Spatrick 		} else {
451e2419160Spatrick 			tcpci_set_polarity(sc, TYPEC_POLARITY_CC2);
452e2419160Spatrick 			tcpci_write_reg8(sc, TCPC_ROLE_CTRL,
453e2419160Spatrick 			    TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT |
454e2419160Spatrick 			    TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
455e2419160Spatrick 		}
456e2419160Spatrick 		tcpci_set_roles(sc, TYPEC_DEVICE, TYPEC_SINK);
457e2419160Spatrick 		tcpci_set_vbus(sc, 0, 0);
458e2419160Spatrick 		sc->sc_attached = 1;
459e2419160Spatrick 	} else if (tcpci_cc_is_audio_detached(cc1, cc2)) {
460e2419160Spatrick 		/* Audio Detached */
461e2419160Spatrick 		DPRINTF(("%s: audio detached\n", __func__));
462e2419160Spatrick 	} else {
463e2419160Spatrick 		panic("%s: unknown combination cc %x", __func__, cc);
464e2419160Spatrick 	}
465e2419160Spatrick 
466e2419160Spatrick 	sc->sc_cc = cc;
467e2419160Spatrick }
468e2419160Spatrick 
469e2419160Spatrick void
tcpci_power_change(struct tcpci_softc * sc)470e2419160Spatrick tcpci_power_change(struct tcpci_softc *sc)
471e2419160Spatrick {
472e2419160Spatrick 	uint8_t power;
473e2419160Spatrick 
474e2419160Spatrick 	if (tcpci_read_reg8(sc, TCPC_POWER_STATUS_MASK) == 0xff)
475e2419160Spatrick 		DPRINTF(("%s: power reset\n", __func__));
476e2419160Spatrick 
477e2419160Spatrick 	power = tcpci_read_reg8(sc, TCPC_POWER_STATUS);
478e2419160Spatrick 	power &= TCPC_POWER_STATUS_VBUS_PRES;
479e2419160Spatrick 	if (sc->sc_vbus_det == power)
480e2419160Spatrick 		return;
481e2419160Spatrick 
482e2419160Spatrick 	DPRINTF(("%s: power %d\n", __func__, power));
483e2419160Spatrick 	sc->sc_vbus_det = power;
484e2419160Spatrick }
485e2419160Spatrick 
486e2419160Spatrick void
tcpci_set_roles(struct tcpci_softc * sc,enum typec_data_role data,enum typec_power_role power)487e2419160Spatrick tcpci_set_roles(struct tcpci_softc *sc, enum typec_data_role data,
488e2419160Spatrick     enum typec_power_role power)
489e2419160Spatrick {
490e2419160Spatrick 	uint8_t reg;
491e2419160Spatrick 
492e2419160Spatrick 	reg = TCPC_MSG_HDR_INFO_PD_REV20;
493e2419160Spatrick 	if (power == TYPEC_SOURCE)
494e2419160Spatrick 		reg |= TCPC_MSG_HDR_INFO_PWR_ROLE;
495e2419160Spatrick 	if (data == TYPEC_HOST)
496e2419160Spatrick 		reg |= TCPC_MSG_HDR_INFO_DATA_ROLE;
497e2419160Spatrick 
498e2419160Spatrick 	tcpci_write_reg8(sc, TCPC_MSG_HDR_INFO, reg);
499e2419160Spatrick 
500e2419160Spatrick 	if (data == TYPEC_HOST)
501e2419160Spatrick 		printf("%s: connected in host mode\n",
502e2419160Spatrick 		    sc->sc_dev.dv_xname);
503e2419160Spatrick 	else
504e2419160Spatrick 		printf("%s: connected in device mode\n",
505e2419160Spatrick 		    sc->sc_dev.dv_xname);
506e2419160Spatrick }
507e2419160Spatrick 
508e2419160Spatrick void
tcpci_set_polarity(struct tcpci_softc * sc,int cc)509e2419160Spatrick tcpci_set_polarity(struct tcpci_softc *sc, int cc)
510e2419160Spatrick {
511e2419160Spatrick 	if (cc == TYPEC_POLARITY_CC1) {
512e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_TCPC_CTRL, 0);
513e2419160Spatrick 		if (sc->sc_ss_sel)
514e2419160Spatrick 			gpio_controller_set_pin(sc->sc_ss_sel, 1);
515e2419160Spatrick 	}
516e2419160Spatrick 	if (cc == TYPEC_POLARITY_CC2) {
517e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_TCPC_CTRL,
518e2419160Spatrick 		    TCPC_TCPC_CTRL_ORIENTATION);
519e2419160Spatrick 		if (sc->sc_ss_sel)
520e2419160Spatrick 			gpio_controller_set_pin(sc->sc_ss_sel, 0);
521e2419160Spatrick 	}
522e2419160Spatrick }
523e2419160Spatrick 
524e2419160Spatrick void
tcpci_set_vbus(struct tcpci_softc * sc,int source,int sink)525e2419160Spatrick tcpci_set_vbus(struct tcpci_softc *sc, int source, int sink)
526e2419160Spatrick {
527e2419160Spatrick 	if (!source)
528e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_COMMAND,
529e2419160Spatrick 		    TCPC_COMMAND_DISABLE_SRC_VBUS);
530e2419160Spatrick 
531e2419160Spatrick 	if (!sink)
532e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_COMMAND,
533e2419160Spatrick 		    TCPC_COMMAND_DISABLE_SINK_VBUS);
534e2419160Spatrick 
535e2419160Spatrick 	if (!source && !sink) {
536e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_VBUS_VOLTAGE_ALARM_LO_CFG, 0x1c);
537e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_POWER_CTRL, tcpci_read_reg8(sc,
538e2419160Spatrick 		    TCPC_POWER_CTRL) | TCPC_POWER_CTRL_FORCEDISCH);
539e2419160Spatrick 	}
540e2419160Spatrick 
541e2419160Spatrick 	if (source)
542e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_COMMAND,
543e2419160Spatrick 		    TCPC_COMMAND_SRC_VBUS_DEFAULT);
544e2419160Spatrick 
545e2419160Spatrick 	if (sink)
546e2419160Spatrick 		tcpci_write_reg8(sc, TCPC_COMMAND,
547e2419160Spatrick 		    TCPC_COMMAND_SINK_VBUS);
548e2419160Spatrick }
549e2419160Spatrick 
550e2419160Spatrick uint8_t
tcpci_read_reg8(struct tcpci_softc * sc,uint8_t reg)551e2419160Spatrick tcpci_read_reg8(struct tcpci_softc *sc, uint8_t reg)
552e2419160Spatrick {
553e2419160Spatrick 	uint8_t val = 0;
554e2419160Spatrick 
555e2419160Spatrick 	iic_acquire_bus(sc->sc_tag, 0);
556e2419160Spatrick 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
557e2419160Spatrick 	    sc->sc_addr, &reg, sizeof(reg), &val, sizeof(val), 0)) {
558e2419160Spatrick 		printf("%s: cannot read register 0x%x\n",
559e2419160Spatrick 		    sc->sc_dev.dv_xname, reg);
560e2419160Spatrick 	}
561e2419160Spatrick 	iic_release_bus(sc->sc_tag, 0);
562e2419160Spatrick 
563e2419160Spatrick 	return val;
564e2419160Spatrick }
565e2419160Spatrick 
566e2419160Spatrick void
tcpci_write_reg8(struct tcpci_softc * sc,uint8_t reg,uint8_t val)567e2419160Spatrick tcpci_write_reg8(struct tcpci_softc *sc, uint8_t reg, uint8_t val)
568e2419160Spatrick {
569e2419160Spatrick 	iic_acquire_bus(sc->sc_tag, 0);
570e2419160Spatrick 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
571e2419160Spatrick 	    sc->sc_addr, &reg, sizeof(reg), &val, sizeof(val), 0)) {
572e2419160Spatrick 		printf("%s: cannot write register 0x%x\n",
573e2419160Spatrick 		    sc->sc_dev.dv_xname, reg);
574e2419160Spatrick 	}
575e2419160Spatrick 	iic_release_bus(sc->sc_tag, 0);
576e2419160Spatrick }
577e2419160Spatrick 
578e2419160Spatrick uint16_t
tcpci_read_reg16(struct tcpci_softc * sc,uint8_t reg)579e2419160Spatrick tcpci_read_reg16(struct tcpci_softc *sc, uint8_t reg)
580e2419160Spatrick {
581e2419160Spatrick 	uint16_t val = 0;
582e2419160Spatrick 
583e2419160Spatrick 	iic_acquire_bus(sc->sc_tag, 0);
584e2419160Spatrick 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
585e2419160Spatrick 	    sc->sc_addr, &reg, sizeof(reg), &val, sizeof(val), 0)) {
586e2419160Spatrick 		printf("%s: cannot read register 0x%x\n",
587e2419160Spatrick 		    sc->sc_dev.dv_xname, reg);
588e2419160Spatrick 	}
589e2419160Spatrick 	iic_release_bus(sc->sc_tag, 0);
590e2419160Spatrick 
591e2419160Spatrick 	return val;
592e2419160Spatrick }
593e2419160Spatrick 
594e2419160Spatrick void
tcpci_write_reg16(struct tcpci_softc * sc,uint8_t reg,uint16_t val)595e2419160Spatrick tcpci_write_reg16(struct tcpci_softc *sc, uint8_t reg, uint16_t val)
596e2419160Spatrick {
597e2419160Spatrick 	iic_acquire_bus(sc->sc_tag, 0);
598e2419160Spatrick 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
599e2419160Spatrick 	    sc->sc_addr, &reg, sizeof(reg), &val, sizeof(val), 0)) {
600e2419160Spatrick 		printf("%s: cannot write register 0x%x\n",
601e2419160Spatrick 		    sc->sc_dev.dv_xname, reg);
602e2419160Spatrick 	}
603e2419160Spatrick 	iic_release_bus(sc->sc_tag, 0);
604e2419160Spatrick }
605