summaryrefslogtreecommitdiff
path: root/driver/tcpm/tusb422.c
blob: 6d12d1758b844f8b56ea18339d7f141acbbeb154 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/* Copyright 2018 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Type-C port manager for TI TUSB422 Port Controller */

#include "common.h"
#include "tusb422.h"
#include "tcpm/tcpci.h"
#include "tcpm/tcpm.h"
#include "timer.h"
#include "usb_pd.h"

#ifndef CONFIG_USB_PD_TCPM_TCPCI
#error "TUSB422 is using a standard TCPCI interface"
#error "Please upgrade your board configuration"

#endif

#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \
	!defined(CONFIG_USB_PD_TCPC_LOW_POWER)
#error "TUSB422 driver requires CONFIG_USB_PD_TCPC_LOW_POWER if " \
		"CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE is enabled"
#endif

#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \
	defined(CONFIG_USB_PD_DISCHARGE_TCPC)
#error "TUSB422 must disable TCPC discharge to support enabling Auto " \
		"Discharge Disconnect all the time."
#endif

enum tusb422_reg_addr {
	TUSB422_REG_VBUS_AND_VCONN_CONTROL = 0x98,
};

enum vbus_and_vconn_control_mask {
	INT_VCONNDIS_DISABLE = BIT(1),
	INT_VBUSDIS_DISABLE = BIT(2),
};

/* The TUSB422 cannot drive an FRS GPIO, but can detect FRS */
static int tusb422_set_frs_enable(int port, int enable)
{
	return tcpc_update8(port, TUSB422_REG_PHY_BMC_RX_CTRL,
			    TUSB422_REG_PHY_BMC_RX_CTRL_FRS_RX_EN,
			    enable ? MASK_SET : MASK_CLR);
}

static int tusb422_tcpci_tcpm_init(int port)
{
	int rv;

	/*
	 * Do not perform TCPC soft reset while waking from Low Power Mode,
	 * because it makes DRP incapable of looking for connection correctly
	 * (see b/176986511) and probably breaks firmware_PDTrySrc test
	 * (see b/179234089).
	 *
	 * TODO(b/179234089): Consider implementing function that performs
	 * only necessary things when leaving Low Power Mode, so we can perform
	 * TCPC soft reset here.
	 */

	rv = tcpci_tcpm_init(port);
	if (rv)
		return rv;

	if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE)) {
		/*
		 * When dual role auto toggle is enabled, the TUSB422 needs
		 * auto discharge disconnect enabled so that the CC state
		 * is detected correctly.
		 * Without this, the CC lines get stuck in the SRC.Open state
		 * after updating the ROLE Control register on a device connect.
		 */
		tusb422_tcpm_drv.tcpc_enable_auto_discharge_disconnect(port, 1);

		/*
		 * Disable internal VBUS discharge. AutoDischargeDisconnect must
		 * generally remain enabled to keep TUSB422 in active mode.
		 * However, this will interfere with FRS by default by
		 * discharging at inappropriate times. Mitigate this by
		 * disabling internal VBUS discharge. The TUSB422 must rely on
		 * external VBUS discharge. See TUSB422 datasheet, 7.4.2 Active
		 * Mode.
		 */
		tcpc_write(port, TUSB422_REG_VBUS_AND_VCONN_CONTROL,
			   INT_VBUSDIS_DISABLE);
	}
	if (IS_ENABLED(CONFIG_USB_PD_FRS_TCPC)) {
		/* Disable FRS detection, and enable the FRS detection alert */
		tusb422_set_frs_enable(port, 0);
		tcpc_update16(port, TCPC_REG_ALERT_MASK,
			      TCPC_REG_ALERT_MASK_VENDOR_DEF, MASK_SET);
		tcpc_update8(port, TUSB422_REG_VENDOR_INTERRUPTS_MASK,
			     TUSB422_REG_VENDOR_INTERRUPTS_MASK_FRS_RX,
			     MASK_SET);
	}
	/*
	 * VBUS detection is supposed to be enabled by default, however the
	 * TUSB422 has this disabled following reset.
	 */
	/* Enable VBUS detection */
	return tcpc_write16(port, TCPC_REG_COMMAND,
			    TCPC_REG_COMMAND_ENABLE_VBUS_DETECT);
}

static int tusb422_tcpm_set_cc(int port, int pull)
{
	/*
	 * Enable AutoDischargeDisconnect to keep TUSB422 in active mode through
	 * this transition. Note that the configuration keeps the TCPC from
	 * actually discharging VBUS in this case.
	 */
	if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE))
		tusb422_tcpm_drv.tcpc_enable_auto_discharge_disconnect(port, 1);

	return tcpci_tcpm_set_cc(port, pull);
}

#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
static int tusb422_tcpc_drp_toggle(int port)
{
	/*
	 * The TUSB422 requires auto discharge disconnect to be enabled for
	 * active mode (not unattached) operation. Make sure it is disabled
	 * before enabling DRP toggling.
	 *
	 * USB Type-C Port Controller Interface Specification revision 2.0,
	 * Figure 4-21 Source Disconnect and Figure 4-22 Sink Disconnect
	 */
	tusb422_tcpm_drv.tcpc_enable_auto_discharge_disconnect(port, 0);

	return tcpci_tcpc_drp_toggle(port);
}
#endif

static void tusb422_tcpci_tcpc_alert(int port)
{
	if (IS_ENABLED(CONFIG_USB_PD_FRS_TCPC)) {
		int regval;

		/* FRS detection is a vendor defined alert */
		tcpc_read(port, TUSB422_REG_VENDOR_INTERRUPTS_STATUS, &regval);
		if (regval & TUSB422_REG_VENDOR_INTERRUPTS_STATUS_FRS_RX) {
			tusb422_set_frs_enable(port, 0);
			tcpc_write(port, TUSB422_REG_VENDOR_INTERRUPTS_STATUS,
				   regval);
			pd_got_frs_signal(port);
		}
	}
	tcpci_tcpc_alert(port);
}

const struct tcpm_drv tusb422_tcpm_drv = {
	.init = &tusb422_tcpci_tcpm_init,
	.release = &tcpci_tcpm_release,
	.get_cc = &tcpci_tcpm_get_cc,
#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
	.check_vbus_level = &tcpci_tcpm_check_vbus_level,
#endif
	.select_rp_value = &tcpci_tcpm_select_rp_value,
	.set_cc = &tusb422_tcpm_set_cc,
	.set_polarity = &tcpci_tcpm_set_polarity,
#ifdef CONFIG_USB_PD_DECODE_SOP
	.sop_prime_enable = &tcpci_tcpm_sop_prime_enable,
#endif
	.set_vconn = &tcpci_tcpm_set_vconn,
	.set_msg_header = &tcpci_tcpm_set_msg_header,
	.set_rx_enable = &tcpci_tcpm_set_rx_enable,
	.get_message_raw = &tcpci_tcpm_get_message_raw,
	.transmit = &tcpci_tcpm_transmit,
	.tcpc_alert = &tusb422_tcpci_tcpc_alert,
#ifdef CONFIG_USB_PD_DISCHARGE_TCPC
	.tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus,
#endif
	.tcpc_enable_auto_discharge_disconnect =
		&tcpci_tcpc_enable_auto_discharge_disconnect,
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
	.drp_toggle = &tusb422_tcpc_drp_toggle,
#endif
	.get_chip_info = &tcpci_get_chip_info,
	.set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl,
	.set_src_ctrl = &tcpci_tcpm_set_src_ctrl,
#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
	.enter_low_power_mode = &tcpci_enter_low_power_mode,
#endif
	.set_bist_test_mode = &tcpci_set_bist_test_mode,
	.get_bist_test_mode = &tcpci_get_bist_test_mode,
#ifdef CONFIG_USB_PD_FRS_TCPC
	.set_frs_enable = &tusb422_set_frs_enable,
#endif
};