xref: /openbmc/linux/drivers/usb/typec/tcpm/wcove.c (revision 72d70bf7)
1ae8a2ca8SHeikki Krogerus // SPDX-License-Identifier: GPL-2.0
292c6dc0bSAndy Shevchenko /*
3ae8a2ca8SHeikki Krogerus  * typec_wcove.c - WhiskeyCove PMIC USB Type-C PHY driver
4ae8a2ca8SHeikki Krogerus  *
5ae8a2ca8SHeikki Krogerus  * Copyright (C) 2017 Intel Corporation
6ae8a2ca8SHeikki Krogerus  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7ae8a2ca8SHeikki Krogerus  */
8ae8a2ca8SHeikki Krogerus 
9ae8a2ca8SHeikki Krogerus #include <linux/acpi.h>
10ae8a2ca8SHeikki Krogerus #include <linux/module.h>
11ae8a2ca8SHeikki Krogerus #include <linux/usb/tcpm.h>
12ae8a2ca8SHeikki Krogerus #include <linux/interrupt.h>
13ae8a2ca8SHeikki Krogerus #include <linux/platform_device.h>
14ae8a2ca8SHeikki Krogerus #include <linux/mfd/intel_soc_pmic.h>
15ae8a2ca8SHeikki Krogerus 
16ae8a2ca8SHeikki Krogerus /* Register offsets */
17ae8a2ca8SHeikki Krogerus #define WCOVE_CHGRIRQ0		0x4e09
18ae8a2ca8SHeikki Krogerus 
19ae8a2ca8SHeikki Krogerus #define USBC_CONTROL1		0x7001
20ae8a2ca8SHeikki Krogerus #define USBC_CONTROL2		0x7002
21ae8a2ca8SHeikki Krogerus #define USBC_CONTROL3		0x7003
22ae8a2ca8SHeikki Krogerus #define USBC_CC1_CTRL		0x7004
23ae8a2ca8SHeikki Krogerus #define USBC_CC2_CTRL		0x7005
24ae8a2ca8SHeikki Krogerus #define USBC_STATUS1		0x7007
25ae8a2ca8SHeikki Krogerus #define USBC_STATUS2		0x7008
26ae8a2ca8SHeikki Krogerus #define USBC_STATUS3		0x7009
27ae8a2ca8SHeikki Krogerus #define USBC_CC1		0x700a
28ae8a2ca8SHeikki Krogerus #define USBC_CC2		0x700b
29ae8a2ca8SHeikki Krogerus #define USBC_CC1_STATUS		0x700c
30ae8a2ca8SHeikki Krogerus #define USBC_CC2_STATUS		0x700d
31ae8a2ca8SHeikki Krogerus #define USBC_IRQ1		0x7015
32ae8a2ca8SHeikki Krogerus #define USBC_IRQ2		0x7016
33ae8a2ca8SHeikki Krogerus #define USBC_IRQMASK1		0x7017
34ae8a2ca8SHeikki Krogerus #define USBC_IRQMASK2		0x7018
35ae8a2ca8SHeikki Krogerus #define USBC_PDCFG2		0x701a
36ae8a2ca8SHeikki Krogerus #define USBC_PDCFG3		0x701b
37ae8a2ca8SHeikki Krogerus #define USBC_PDSTATUS		0x701c
38ae8a2ca8SHeikki Krogerus #define USBC_RXSTATUS		0x701d
39ae8a2ca8SHeikki Krogerus #define USBC_RXINFO		0x701e
40ae8a2ca8SHeikki Krogerus #define USBC_TXCMD		0x701f
41ae8a2ca8SHeikki Krogerus #define USBC_TXINFO		0x7020
42ae8a2ca8SHeikki Krogerus #define USBC_RX_DATA		0x7028
43ae8a2ca8SHeikki Krogerus #define USBC_TX_DATA		0x7047
44ae8a2ca8SHeikki Krogerus 
45ae8a2ca8SHeikki Krogerus /* Register bits */
46ae8a2ca8SHeikki Krogerus 
47ae8a2ca8SHeikki Krogerus #define USBC_CONTROL1_MODE_MASK		0x3
48ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_MODE_SNK	0
49ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_MODE_SNKACC	1
50ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_MODE_SRC	2
51ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_MODE_SRCACC	3
52ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_MODE_DRP	4
53ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_MODE_DRPACC	5
54ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_MODE_TEST	7
55ae8a2ca8SHeikki Krogerus #define USBC_CONTROL1_CURSRC_MASK	0xc
56ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_CURSRC_UA_0	(0 << 3)
57ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_CURSRC_UA_80	(1 << 3)
58ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_CURSRC_UA_180	(2 << 3)
59ae8a2ca8SHeikki Krogerus #define   USBC_CONTROL1_CURSRC_UA_330	(3 << 3)
60ae8a2ca8SHeikki Krogerus #define USBC_CONTROL1_DRPTOGGLE_RANDOM	0xe0
61ae8a2ca8SHeikki Krogerus 
62ae8a2ca8SHeikki Krogerus #define USBC_CONTROL2_UNATT_SNK		BIT(0)
63ae8a2ca8SHeikki Krogerus #define USBC_CONTROL2_UNATT_SRC		BIT(1)
64ae8a2ca8SHeikki Krogerus #define USBC_CONTROL2_DIS_ST		BIT(2)
65ae8a2ca8SHeikki Krogerus 
66ae8a2ca8SHeikki Krogerus #define USBC_CONTROL3_DET_DIS		BIT(0)
67ae8a2ca8SHeikki Krogerus #define USBC_CONTROL3_PD_DIS		BIT(1)
68ae8a2ca8SHeikki Krogerus #define USBC_CONTROL3_RESETPHY		BIT(2)
69ae8a2ca8SHeikki Krogerus 
70ae8a2ca8SHeikki Krogerus #define USBC_CC_CTRL_PU_EN		BIT(0)
71ae8a2ca8SHeikki Krogerus #define USBC_CC_CTRL_VCONN_EN		BIT(1)
72ae8a2ca8SHeikki Krogerus #define USBC_CC_CTRL_TX_EN		BIT(2)
73ae8a2ca8SHeikki Krogerus #define USBC_CC_CTRL_PD_EN		BIT(3)
74ae8a2ca8SHeikki Krogerus #define USBC_CC_CTRL_CDET_EN		BIT(4)
75ae8a2ca8SHeikki Krogerus #define USBC_CC_CTRL_RDET_EN		BIT(5)
76ae8a2ca8SHeikki Krogerus #define USBC_CC_CTRL_ADC_EN		BIT(6)
77ae8a2ca8SHeikki Krogerus #define USBC_CC_CTRL_VBUSOK		BIT(7)
78ae8a2ca8SHeikki Krogerus 
79ae8a2ca8SHeikki Krogerus #define USBC_STATUS1_DET_ONGOING	BIT(6)
80ae8a2ca8SHeikki Krogerus #define USBC_STATUS1_RSLT(r)		((r) & 0xf)
81ae8a2ca8SHeikki Krogerus #define USBC_RSLT_NOTHING		0
82ae8a2ca8SHeikki Krogerus #define USBC_RSLT_SRC_DEFAULT		1
83ae8a2ca8SHeikki Krogerus #define USBC_RSLT_SRC_1_5A		2
84ae8a2ca8SHeikki Krogerus #define USBC_RSLT_SRC_3_0A		3
85ae8a2ca8SHeikki Krogerus #define USBC_RSLT_SNK			4
86ae8a2ca8SHeikki Krogerus #define USBC_RSLT_DEBUG_ACC		5
87ae8a2ca8SHeikki Krogerus #define USBC_RSLT_AUDIO_ACC		6
88ae8a2ca8SHeikki Krogerus #define USBC_RSLT_UNDEF			15
89ae8a2ca8SHeikki Krogerus #define USBC_STATUS1_ORIENT(r)		(((r) >> 4) & 0x3)
90ae8a2ca8SHeikki Krogerus #define USBC_ORIENT_NORMAL		1
91ae8a2ca8SHeikki Krogerus #define USBC_ORIENT_REVERSE		2
92ae8a2ca8SHeikki Krogerus 
93ae8a2ca8SHeikki Krogerus #define USBC_STATUS2_VBUS_REQ		BIT(5)
94ae8a2ca8SHeikki Krogerus 
95ae8a2ca8SHeikki Krogerus #define UCSC_CC_STATUS_SNK_RP		BIT(0)
96ae8a2ca8SHeikki Krogerus #define UCSC_CC_STATUS_PWRDEFSNK	BIT(1)
97ae8a2ca8SHeikki Krogerus #define UCSC_CC_STATUS_PWR_1P5A_SNK	BIT(2)
98ae8a2ca8SHeikki Krogerus #define UCSC_CC_STATUS_PWR_3A_SNK	BIT(3)
99ae8a2ca8SHeikki Krogerus #define UCSC_CC_STATUS_SRC_RP		BIT(4)
100ae8a2ca8SHeikki Krogerus #define UCSC_CC_STATUS_RX(r)		(((r) >> 5) & 0x3)
101ae8a2ca8SHeikki Krogerus #define   USBC_CC_STATUS_RD		1
102ae8a2ca8SHeikki Krogerus #define   USBC_CC_STATUS_RA		2
103ae8a2ca8SHeikki Krogerus 
104ae8a2ca8SHeikki Krogerus #define USBC_IRQ1_ADCDONE1		BIT(2)
105ae8a2ca8SHeikki Krogerus #define USBC_IRQ1_OVERTEMP		BIT(1)
106ae8a2ca8SHeikki Krogerus #define USBC_IRQ1_SHORT			BIT(0)
107ae8a2ca8SHeikki Krogerus 
108ae8a2ca8SHeikki Krogerus #define USBC_IRQ2_CC_CHANGE		BIT(7)
109ae8a2ca8SHeikki Krogerus #define USBC_IRQ2_RX_PD			BIT(6)
110ae8a2ca8SHeikki Krogerus #define USBC_IRQ2_RX_HR			BIT(5)
111ae8a2ca8SHeikki Krogerus #define USBC_IRQ2_RX_CR			BIT(4)
112ae8a2ca8SHeikki Krogerus #define USBC_IRQ2_TX_SUCCESS		BIT(3)
113ae8a2ca8SHeikki Krogerus #define USBC_IRQ2_TX_FAIL		BIT(2)
114ae8a2ca8SHeikki Krogerus 
115ae8a2ca8SHeikki Krogerus #define USBC_IRQMASK1_ALL	(USBC_IRQ1_ADCDONE1 | USBC_IRQ1_OVERTEMP | \
116ae8a2ca8SHeikki Krogerus 				 USBC_IRQ1_SHORT)
117ae8a2ca8SHeikki Krogerus 
118ae8a2ca8SHeikki Krogerus #define USBC_IRQMASK2_ALL	(USBC_IRQ2_CC_CHANGE | USBC_IRQ2_RX_PD | \
119ae8a2ca8SHeikki Krogerus 				 USBC_IRQ2_RX_HR | USBC_IRQ2_RX_CR | \
120ae8a2ca8SHeikki Krogerus 				 USBC_IRQ2_TX_SUCCESS | USBC_IRQ2_TX_FAIL)
121ae8a2ca8SHeikki Krogerus 
122ae8a2ca8SHeikki Krogerus #define USBC_PDCFG2_SOP			BIT(0)
123ae8a2ca8SHeikki Krogerus #define USBC_PDCFG2_SOP_P		BIT(1)
124ae8a2ca8SHeikki Krogerus #define USBC_PDCFG2_SOP_PP		BIT(2)
125ae8a2ca8SHeikki Krogerus #define USBC_PDCFG2_SOP_P_DEBUG		BIT(3)
126ae8a2ca8SHeikki Krogerus #define USBC_PDCFG2_SOP_PP_DEBUG	BIT(4)
127ae8a2ca8SHeikki Krogerus 
128ae8a2ca8SHeikki Krogerus #define USBC_PDCFG3_DATAROLE_SHIFT	1
129ae8a2ca8SHeikki Krogerus #define USBC_PDCFG3_SOP_SHIFT		2
130ae8a2ca8SHeikki Krogerus 
131ae8a2ca8SHeikki Krogerus #define USBC_RXSTATUS_RXCLEAR		BIT(0)
132ae8a2ca8SHeikki Krogerus #define USBC_RXSTATUS_RXDATA		BIT(7)
133ae8a2ca8SHeikki Krogerus 
134ae8a2ca8SHeikki Krogerus #define USBC_RXINFO_RXBYTES(i)		(((i) >> 3) & 0x1f)
135ae8a2ca8SHeikki Krogerus 
136ae8a2ca8SHeikki Krogerus #define USBC_TXCMD_BUF_RDY		BIT(0)
137ae8a2ca8SHeikki Krogerus #define USBC_TXCMD_START		BIT(1)
138ae8a2ca8SHeikki Krogerus #define USBC_TXCMD_NOP			(0 << 5)
139ae8a2ca8SHeikki Krogerus #define USBC_TXCMD_MSG			(1 << 5)
140ae8a2ca8SHeikki Krogerus #define USBC_TXCMD_CR			(2 << 5)
141ae8a2ca8SHeikki Krogerus #define USBC_TXCMD_HR			(3 << 5)
142ae8a2ca8SHeikki Krogerus #define USBC_TXCMD_BIST			(4 << 5)
143ae8a2ca8SHeikki Krogerus 
144ae8a2ca8SHeikki Krogerus #define USBC_TXINFO_RETRIES(d)		(d << 3)
145ae8a2ca8SHeikki Krogerus 
146ae8a2ca8SHeikki Krogerus struct wcove_typec {
147ae8a2ca8SHeikki Krogerus 	struct mutex lock; /* device lock */
148ae8a2ca8SHeikki Krogerus 	struct device *dev;
149ae8a2ca8SHeikki Krogerus 	struct regmap *regmap;
150ae8a2ca8SHeikki Krogerus 	guid_t guid;
151ae8a2ca8SHeikki Krogerus 
152ae8a2ca8SHeikki Krogerus 	bool vbus;
153ae8a2ca8SHeikki Krogerus 
154ae8a2ca8SHeikki Krogerus 	struct tcpc_dev tcpc;
155ae8a2ca8SHeikki Krogerus 	struct tcpm_port *tcpm;
156ae8a2ca8SHeikki Krogerus };
157ae8a2ca8SHeikki Krogerus 
158ae8a2ca8SHeikki Krogerus #define tcpc_to_wcove(_tcpc_) container_of(_tcpc_, struct wcove_typec, tcpc)
159ae8a2ca8SHeikki Krogerus 
160ae8a2ca8SHeikki Krogerus enum wcove_typec_func {
161ae8a2ca8SHeikki Krogerus 	WCOVE_FUNC_DRIVE_VBUS = 1,
162ae8a2ca8SHeikki Krogerus 	WCOVE_FUNC_ORIENTATION,
163ae8a2ca8SHeikki Krogerus 	WCOVE_FUNC_ROLE,
164ae8a2ca8SHeikki Krogerus 	WCOVE_FUNC_DRIVE_VCONN,
165ae8a2ca8SHeikki Krogerus };
166ae8a2ca8SHeikki Krogerus 
167ae8a2ca8SHeikki Krogerus enum wcove_typec_orientation {
168ae8a2ca8SHeikki Krogerus 	WCOVE_ORIENTATION_NORMAL,
169ae8a2ca8SHeikki Krogerus 	WCOVE_ORIENTATION_REVERSE,
170ae8a2ca8SHeikki Krogerus };
171ae8a2ca8SHeikki Krogerus 
172ae8a2ca8SHeikki Krogerus enum wcove_typec_role {
173ae8a2ca8SHeikki Krogerus 	WCOVE_ROLE_HOST,
174ae8a2ca8SHeikki Krogerus 	WCOVE_ROLE_DEVICE,
175ae8a2ca8SHeikki Krogerus };
176ae8a2ca8SHeikki Krogerus 
177ae8a2ca8SHeikki Krogerus #define WCOVE_DSM_UUID		"482383f0-2876-4e49-8685-db66211af037"
178ae8a2ca8SHeikki Krogerus 
wcove_typec_func(struct wcove_typec * wcove,enum wcove_typec_func func,int param)179ae8a2ca8SHeikki Krogerus static int wcove_typec_func(struct wcove_typec *wcove,
180ae8a2ca8SHeikki Krogerus 			    enum wcove_typec_func func, int param)
181ae8a2ca8SHeikki Krogerus {
182ae8a2ca8SHeikki Krogerus 	union acpi_object *obj;
183ae8a2ca8SHeikki Krogerus 	union acpi_object tmp;
184ae8a2ca8SHeikki Krogerus 	union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
185ae8a2ca8SHeikki Krogerus 
186ae8a2ca8SHeikki Krogerus 	tmp.type = ACPI_TYPE_INTEGER;
187ae8a2ca8SHeikki Krogerus 	tmp.integer.value = param;
188ae8a2ca8SHeikki Krogerus 
189ae8a2ca8SHeikki Krogerus 	obj = acpi_evaluate_dsm(ACPI_HANDLE(wcove->dev), &wcove->guid, 1, func,
190ae8a2ca8SHeikki Krogerus 				&argv4);
191ae8a2ca8SHeikki Krogerus 	if (!obj) {
192ae8a2ca8SHeikki Krogerus 		dev_err(wcove->dev, "%s: failed to evaluate _DSM\n", __func__);
193ae8a2ca8SHeikki Krogerus 		return -EIO;
194ae8a2ca8SHeikki Krogerus 	}
195ae8a2ca8SHeikki Krogerus 
196ae8a2ca8SHeikki Krogerus 	ACPI_FREE(obj);
197ae8a2ca8SHeikki Krogerus 	return 0;
198ae8a2ca8SHeikki Krogerus }
199ae8a2ca8SHeikki Krogerus 
wcove_init(struct tcpc_dev * tcpc)200ae8a2ca8SHeikki Krogerus static int wcove_init(struct tcpc_dev *tcpc)
201ae8a2ca8SHeikki Krogerus {
202ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
203ae8a2ca8SHeikki Krogerus 	int ret;
204ae8a2ca8SHeikki Krogerus 
205ae8a2ca8SHeikki Krogerus 	ret = regmap_write(wcove->regmap, USBC_CONTROL1, 0);
206ae8a2ca8SHeikki Krogerus 	if (ret)
207ae8a2ca8SHeikki Krogerus 		return ret;
208ae8a2ca8SHeikki Krogerus 
209ae8a2ca8SHeikki Krogerus 	/* Unmask everything */
210ae8a2ca8SHeikki Krogerus 	ret = regmap_write(wcove->regmap, USBC_IRQMASK1, 0);
211ae8a2ca8SHeikki Krogerus 	if (ret)
212ae8a2ca8SHeikki Krogerus 		return ret;
213ae8a2ca8SHeikki Krogerus 
214ae8a2ca8SHeikki Krogerus 	return regmap_write(wcove->regmap, USBC_IRQMASK2, 0);
215ae8a2ca8SHeikki Krogerus }
216ae8a2ca8SHeikki Krogerus 
wcove_get_vbus(struct tcpc_dev * tcpc)217ae8a2ca8SHeikki Krogerus static int wcove_get_vbus(struct tcpc_dev *tcpc)
218ae8a2ca8SHeikki Krogerus {
219ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
220ae8a2ca8SHeikki Krogerus 	unsigned int cc1ctrl;
221ae8a2ca8SHeikki Krogerus 	int ret;
222ae8a2ca8SHeikki Krogerus 
223ae8a2ca8SHeikki Krogerus 	ret = regmap_read(wcove->regmap, USBC_CC1_CTRL, &cc1ctrl);
224ae8a2ca8SHeikki Krogerus 	if (ret)
225ae8a2ca8SHeikki Krogerus 		return ret;
226ae8a2ca8SHeikki Krogerus 
227ae8a2ca8SHeikki Krogerus 	wcove->vbus = !!(cc1ctrl & USBC_CC_CTRL_VBUSOK);
228ae8a2ca8SHeikki Krogerus 
229ae8a2ca8SHeikki Krogerus 	return wcove->vbus;
230ae8a2ca8SHeikki Krogerus }
231ae8a2ca8SHeikki Krogerus 
wcove_set_vbus(struct tcpc_dev * tcpc,bool on,bool sink)232ae8a2ca8SHeikki Krogerus static int wcove_set_vbus(struct tcpc_dev *tcpc, bool on, bool sink)
233ae8a2ca8SHeikki Krogerus {
234ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
235ae8a2ca8SHeikki Krogerus 
236ae8a2ca8SHeikki Krogerus 	return wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VBUS, on);
237ae8a2ca8SHeikki Krogerus }
238ae8a2ca8SHeikki Krogerus 
wcove_set_vconn(struct tcpc_dev * tcpc,bool on)239ae8a2ca8SHeikki Krogerus static int wcove_set_vconn(struct tcpc_dev *tcpc, bool on)
240ae8a2ca8SHeikki Krogerus {
241ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
242ae8a2ca8SHeikki Krogerus 
243ae8a2ca8SHeikki Krogerus 	return wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, on);
244ae8a2ca8SHeikki Krogerus }
245ae8a2ca8SHeikki Krogerus 
wcove_to_typec_cc(unsigned int cc)246ae8a2ca8SHeikki Krogerus static enum typec_cc_status wcove_to_typec_cc(unsigned int cc)
247ae8a2ca8SHeikki Krogerus {
248ae8a2ca8SHeikki Krogerus 	if (cc & UCSC_CC_STATUS_SNK_RP) {
249ae8a2ca8SHeikki Krogerus 		if (cc & UCSC_CC_STATUS_PWRDEFSNK)
250ae8a2ca8SHeikki Krogerus 			return TYPEC_CC_RP_DEF;
251ae8a2ca8SHeikki Krogerus 		else if (cc & UCSC_CC_STATUS_PWR_1P5A_SNK)
252ae8a2ca8SHeikki Krogerus 			return TYPEC_CC_RP_1_5;
253ae8a2ca8SHeikki Krogerus 		else if (cc & UCSC_CC_STATUS_PWR_3A_SNK)
254ae8a2ca8SHeikki Krogerus 			return TYPEC_CC_RP_3_0;
255ae8a2ca8SHeikki Krogerus 	} else {
256ae8a2ca8SHeikki Krogerus 		switch (UCSC_CC_STATUS_RX(cc)) {
257ae8a2ca8SHeikki Krogerus 		case USBC_CC_STATUS_RD:
258ae8a2ca8SHeikki Krogerus 			return TYPEC_CC_RD;
259ae8a2ca8SHeikki Krogerus 		case USBC_CC_STATUS_RA:
260ae8a2ca8SHeikki Krogerus 			return TYPEC_CC_RA;
261ae8a2ca8SHeikki Krogerus 		default:
262ae8a2ca8SHeikki Krogerus 			break;
263ae8a2ca8SHeikki Krogerus 		}
264ae8a2ca8SHeikki Krogerus 	}
265ae8a2ca8SHeikki Krogerus 	return TYPEC_CC_OPEN;
266ae8a2ca8SHeikki Krogerus }
267ae8a2ca8SHeikki Krogerus 
wcove_get_cc(struct tcpc_dev * tcpc,enum typec_cc_status * cc1,enum typec_cc_status * cc2)268ae8a2ca8SHeikki Krogerus static int wcove_get_cc(struct tcpc_dev *tcpc, enum typec_cc_status *cc1,
269ae8a2ca8SHeikki Krogerus 			enum typec_cc_status *cc2)
270ae8a2ca8SHeikki Krogerus {
271ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
272ae8a2ca8SHeikki Krogerus 	unsigned int cc1_status;
273ae8a2ca8SHeikki Krogerus 	unsigned int cc2_status;
274ae8a2ca8SHeikki Krogerus 	int ret;
275ae8a2ca8SHeikki Krogerus 
276ae8a2ca8SHeikki Krogerus 	ret = regmap_read(wcove->regmap, USBC_CC1_STATUS, &cc1_status);
277ae8a2ca8SHeikki Krogerus 	if (ret)
278ae8a2ca8SHeikki Krogerus 		return ret;
279ae8a2ca8SHeikki Krogerus 
280ae8a2ca8SHeikki Krogerus 	ret = regmap_read(wcove->regmap, USBC_CC2_STATUS, &cc2_status);
281ae8a2ca8SHeikki Krogerus 	if (ret)
282ae8a2ca8SHeikki Krogerus 		return ret;
283ae8a2ca8SHeikki Krogerus 
284ae8a2ca8SHeikki Krogerus 	*cc1 = wcove_to_typec_cc(cc1_status);
285ae8a2ca8SHeikki Krogerus 	*cc2 = wcove_to_typec_cc(cc2_status);
286ae8a2ca8SHeikki Krogerus 
287ae8a2ca8SHeikki Krogerus 	return 0;
288ae8a2ca8SHeikki Krogerus }
289ae8a2ca8SHeikki Krogerus 
wcove_set_cc(struct tcpc_dev * tcpc,enum typec_cc_status cc)290ae8a2ca8SHeikki Krogerus static int wcove_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
291ae8a2ca8SHeikki Krogerus {
292ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
293ae8a2ca8SHeikki Krogerus 	unsigned int ctrl;
294ae8a2ca8SHeikki Krogerus 
295ae8a2ca8SHeikki Krogerus 	switch (cc) {
296ae8a2ca8SHeikki Krogerus 	case TYPEC_CC_RD:
297ae8a2ca8SHeikki Krogerus 		ctrl = USBC_CONTROL1_MODE_SNK;
298ae8a2ca8SHeikki Krogerus 		break;
299ae8a2ca8SHeikki Krogerus 	case TYPEC_CC_RP_DEF:
300ae8a2ca8SHeikki Krogerus 		ctrl = USBC_CONTROL1_CURSRC_UA_80 | USBC_CONTROL1_MODE_SRC;
301ae8a2ca8SHeikki Krogerus 		break;
302ae8a2ca8SHeikki Krogerus 	case TYPEC_CC_RP_1_5:
303ae8a2ca8SHeikki Krogerus 		ctrl = USBC_CONTROL1_CURSRC_UA_180 | USBC_CONTROL1_MODE_SRC;
304ae8a2ca8SHeikki Krogerus 		break;
305ae8a2ca8SHeikki Krogerus 	case TYPEC_CC_RP_3_0:
306ae8a2ca8SHeikki Krogerus 		ctrl = USBC_CONTROL1_CURSRC_UA_330 | USBC_CONTROL1_MODE_SRC;
307ae8a2ca8SHeikki Krogerus 		break;
308ae8a2ca8SHeikki Krogerus 	case TYPEC_CC_OPEN:
309ae8a2ca8SHeikki Krogerus 		ctrl = 0;
310ae8a2ca8SHeikki Krogerus 		break;
311ae8a2ca8SHeikki Krogerus 	default:
312ae8a2ca8SHeikki Krogerus 		return -EINVAL;
313ae8a2ca8SHeikki Krogerus 	}
314ae8a2ca8SHeikki Krogerus 
315ae8a2ca8SHeikki Krogerus 	return regmap_write(wcove->regmap, USBC_CONTROL1, ctrl);
316ae8a2ca8SHeikki Krogerus }
317ae8a2ca8SHeikki Krogerus 
wcove_set_polarity(struct tcpc_dev * tcpc,enum typec_cc_polarity pol)318ae8a2ca8SHeikki Krogerus static int wcove_set_polarity(struct tcpc_dev *tcpc, enum typec_cc_polarity pol)
319ae8a2ca8SHeikki Krogerus {
320ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
321ae8a2ca8SHeikki Krogerus 
322ae8a2ca8SHeikki Krogerus 	return wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION, pol);
323ae8a2ca8SHeikki Krogerus }
324ae8a2ca8SHeikki Krogerus 
wcove_set_current_limit(struct tcpc_dev * tcpc,u32 max_ma,u32 mv)325ae8a2ca8SHeikki Krogerus static int wcove_set_current_limit(struct tcpc_dev *tcpc, u32 max_ma, u32 mv)
326ae8a2ca8SHeikki Krogerus {
327ae8a2ca8SHeikki Krogerus 	return 0;
328ae8a2ca8SHeikki Krogerus }
329ae8a2ca8SHeikki Krogerus 
wcove_set_roles(struct tcpc_dev * tcpc,bool attached,enum typec_role role,enum typec_data_role data)330ae8a2ca8SHeikki Krogerus static int wcove_set_roles(struct tcpc_dev *tcpc, bool attached,
331ae8a2ca8SHeikki Krogerus 			   enum typec_role role, enum typec_data_role data)
332ae8a2ca8SHeikki Krogerus {
333ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
334ae8a2ca8SHeikki Krogerus 	unsigned int val;
335ae8a2ca8SHeikki Krogerus 	int ret;
336ae8a2ca8SHeikki Krogerus 
337ae8a2ca8SHeikki Krogerus 	ret = wcove_typec_func(wcove, WCOVE_FUNC_ROLE, data == TYPEC_HOST ?
338ae8a2ca8SHeikki Krogerus 			       WCOVE_ROLE_HOST : WCOVE_ROLE_DEVICE);
339ae8a2ca8SHeikki Krogerus 	if (ret)
340ae8a2ca8SHeikki Krogerus 		return ret;
341ae8a2ca8SHeikki Krogerus 
342ae8a2ca8SHeikki Krogerus 	val = role;
343ae8a2ca8SHeikki Krogerus 	val |= data << USBC_PDCFG3_DATAROLE_SHIFT;
344ae8a2ca8SHeikki Krogerus 	val |= PD_REV20 << USBC_PDCFG3_SOP_SHIFT;
345ae8a2ca8SHeikki Krogerus 
346ae8a2ca8SHeikki Krogerus 	return regmap_write(wcove->regmap, USBC_PDCFG3, val);
347ae8a2ca8SHeikki Krogerus }
348ae8a2ca8SHeikki Krogerus 
wcove_set_pd_rx(struct tcpc_dev * tcpc,bool on)349ae8a2ca8SHeikki Krogerus static int wcove_set_pd_rx(struct tcpc_dev *tcpc, bool on)
350ae8a2ca8SHeikki Krogerus {
351ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
352ae8a2ca8SHeikki Krogerus 
353ae8a2ca8SHeikki Krogerus 	return regmap_write(wcove->regmap, USBC_PDCFG2,
354ae8a2ca8SHeikki Krogerus 			    on ? USBC_PDCFG2_SOP : 0);
355ae8a2ca8SHeikki Krogerus }
356ae8a2ca8SHeikki Krogerus 
wcove_pd_transmit(struct tcpc_dev * tcpc,enum tcpm_transmit_type type,const struct pd_message * msg,unsigned int negotiated_rev)357ae8a2ca8SHeikki Krogerus static int wcove_pd_transmit(struct tcpc_dev *tcpc,
358ae8a2ca8SHeikki Krogerus 			     enum tcpm_transmit_type type,
359e4a93780SBadhri Jagan Sridharan 			     const struct pd_message *msg,
360e4a93780SBadhri Jagan Sridharan 			     unsigned int negotiated_rev)
361ae8a2ca8SHeikki Krogerus {
362ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
363ae8a2ca8SHeikki Krogerus 	unsigned int info = 0;
364ae8a2ca8SHeikki Krogerus 	unsigned int cmd;
365ae8a2ca8SHeikki Krogerus 	int ret;
366ae8a2ca8SHeikki Krogerus 
367ae8a2ca8SHeikki Krogerus 	ret = regmap_read(wcove->regmap, USBC_TXCMD, &cmd);
368ae8a2ca8SHeikki Krogerus 	if (ret)
369ae8a2ca8SHeikki Krogerus 		return ret;
370ae8a2ca8SHeikki Krogerus 
371ae8a2ca8SHeikki Krogerus 	if (!(cmd & USBC_TXCMD_BUF_RDY)) {
372ae8a2ca8SHeikki Krogerus 		dev_warn(wcove->dev, "%s: Last transmission still ongoing!",
373ae8a2ca8SHeikki Krogerus 			 __func__);
374ae8a2ca8SHeikki Krogerus 		return -EBUSY;
375ae8a2ca8SHeikki Krogerus 	}
376ae8a2ca8SHeikki Krogerus 
377ae8a2ca8SHeikki Krogerus 	if (msg) {
378ae8a2ca8SHeikki Krogerus 		const u8 *data = (void *)msg;
379ae8a2ca8SHeikki Krogerus 		int i;
380ae8a2ca8SHeikki Krogerus 
381d5ab95daSAndy Shevchenko 		for (i = 0; i < pd_header_cnt_le(msg->header) * 4 + 2; i++) {
382ae8a2ca8SHeikki Krogerus 			ret = regmap_write(wcove->regmap, USBC_TX_DATA + i,
383ae8a2ca8SHeikki Krogerus 					   data[i]);
384ae8a2ca8SHeikki Krogerus 			if (ret)
385ae8a2ca8SHeikki Krogerus 				return ret;
386ae8a2ca8SHeikki Krogerus 		}
387ae8a2ca8SHeikki Krogerus 	}
388ae8a2ca8SHeikki Krogerus 
389ae8a2ca8SHeikki Krogerus 	switch (type) {
390ae8a2ca8SHeikki Krogerus 	case TCPC_TX_SOP:
391ae8a2ca8SHeikki Krogerus 	case TCPC_TX_SOP_PRIME:
392ae8a2ca8SHeikki Krogerus 	case TCPC_TX_SOP_PRIME_PRIME:
393ae8a2ca8SHeikki Krogerus 	case TCPC_TX_SOP_DEBUG_PRIME:
394ae8a2ca8SHeikki Krogerus 	case TCPC_TX_SOP_DEBUG_PRIME_PRIME:
395ae8a2ca8SHeikki Krogerus 		info = type + 1;
396ae8a2ca8SHeikki Krogerus 		cmd = USBC_TXCMD_MSG;
397ae8a2ca8SHeikki Krogerus 		break;
398ae8a2ca8SHeikki Krogerus 	case TCPC_TX_HARD_RESET:
399ae8a2ca8SHeikki Krogerus 		cmd = USBC_TXCMD_HR;
400ae8a2ca8SHeikki Krogerus 		break;
401ae8a2ca8SHeikki Krogerus 	case TCPC_TX_CABLE_RESET:
402ae8a2ca8SHeikki Krogerus 		cmd = USBC_TXCMD_CR;
403ae8a2ca8SHeikki Krogerus 		break;
404ae8a2ca8SHeikki Krogerus 	case TCPC_TX_BIST_MODE_2:
405ae8a2ca8SHeikki Krogerus 		cmd = USBC_TXCMD_BIST;
406ae8a2ca8SHeikki Krogerus 		break;
407ae8a2ca8SHeikki Krogerus 	default:
408ae8a2ca8SHeikki Krogerus 		return -EINVAL;
409ae8a2ca8SHeikki Krogerus 	}
410ae8a2ca8SHeikki Krogerus 
411ae8a2ca8SHeikki Krogerus 	/* NOTE Setting maximum number of retries (7) */
412ae8a2ca8SHeikki Krogerus 	ret = regmap_write(wcove->regmap, USBC_TXINFO,
413ae8a2ca8SHeikki Krogerus 			   info | USBC_TXINFO_RETRIES(7));
414ae8a2ca8SHeikki Krogerus 	if (ret)
415ae8a2ca8SHeikki Krogerus 		return ret;
416ae8a2ca8SHeikki Krogerus 
417ae8a2ca8SHeikki Krogerus 	return regmap_write(wcove->regmap, USBC_TXCMD, cmd | USBC_TXCMD_START);
418ae8a2ca8SHeikki Krogerus }
419ae8a2ca8SHeikki Krogerus 
wcove_start_toggling(struct tcpc_dev * tcpc,enum typec_port_type port_type,enum typec_cc_status cc)4207893f9e1SHans de Goede static int wcove_start_toggling(struct tcpc_dev *tcpc,
4217893f9e1SHans de Goede 				enum typec_port_type port_type,
422ae8a2ca8SHeikki Krogerus 				enum typec_cc_status cc)
423ae8a2ca8SHeikki Krogerus {
424ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
425ae8a2ca8SHeikki Krogerus 	unsigned int usbc_ctrl;
426ae8a2ca8SHeikki Krogerus 
4277893f9e1SHans de Goede 	if (port_type != TYPEC_PORT_DRP)
4287893f9e1SHans de Goede 		return -EOPNOTSUPP;
4297893f9e1SHans de Goede 
430ae8a2ca8SHeikki Krogerus 	usbc_ctrl = USBC_CONTROL1_MODE_DRP | USBC_CONTROL1_DRPTOGGLE_RANDOM;
431ae8a2ca8SHeikki Krogerus 
432ae8a2ca8SHeikki Krogerus 	switch (cc) {
433ae8a2ca8SHeikki Krogerus 	case TYPEC_CC_RP_1_5:
434ae8a2ca8SHeikki Krogerus 		usbc_ctrl |= USBC_CONTROL1_CURSRC_UA_180;
435ae8a2ca8SHeikki Krogerus 		break;
436ae8a2ca8SHeikki Krogerus 	case TYPEC_CC_RP_3_0:
437ae8a2ca8SHeikki Krogerus 		usbc_ctrl |= USBC_CONTROL1_CURSRC_UA_330;
438ae8a2ca8SHeikki Krogerus 		break;
439ae8a2ca8SHeikki Krogerus 	default:
440ae8a2ca8SHeikki Krogerus 		usbc_ctrl |= USBC_CONTROL1_CURSRC_UA_80;
441ae8a2ca8SHeikki Krogerus 		break;
442ae8a2ca8SHeikki Krogerus 	}
443ae8a2ca8SHeikki Krogerus 
444ae8a2ca8SHeikki Krogerus 	return regmap_write(wcove->regmap, USBC_CONTROL1, usbc_ctrl);
445ae8a2ca8SHeikki Krogerus }
446ae8a2ca8SHeikki Krogerus 
wcove_read_rx_buffer(struct wcove_typec * wcove,void * msg)447ae8a2ca8SHeikki Krogerus static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg)
448ae8a2ca8SHeikki Krogerus {
449ae8a2ca8SHeikki Krogerus 	unsigned int info;
450ae8a2ca8SHeikki Krogerus 	int ret;
451ae8a2ca8SHeikki Krogerus 	int i;
452ae8a2ca8SHeikki Krogerus 
453ae8a2ca8SHeikki Krogerus 	ret = regmap_read(wcove->regmap, USBC_RXINFO, &info);
454ae8a2ca8SHeikki Krogerus 	if (ret)
455ae8a2ca8SHeikki Krogerus 		return ret;
456ae8a2ca8SHeikki Krogerus 
457ae8a2ca8SHeikki Krogerus 	/* FIXME: Check that USBC_RXINFO_RXBYTES(info) matches the header */
458ae8a2ca8SHeikki Krogerus 
459ae8a2ca8SHeikki Krogerus 	for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) {
460ae8a2ca8SHeikki Krogerus 		ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i);
461ae8a2ca8SHeikki Krogerus 		if (ret)
462ae8a2ca8SHeikki Krogerus 			return ret;
463ae8a2ca8SHeikki Krogerus 	}
464ae8a2ca8SHeikki Krogerus 
465ae8a2ca8SHeikki Krogerus 	return regmap_write(wcove->regmap, USBC_RXSTATUS,
466ae8a2ca8SHeikki Krogerus 			    USBC_RXSTATUS_RXCLEAR);
467ae8a2ca8SHeikki Krogerus }
468ae8a2ca8SHeikki Krogerus 
wcove_typec_irq(int irq,void * data)469ae8a2ca8SHeikki Krogerus static irqreturn_t wcove_typec_irq(int irq, void *data)
470ae8a2ca8SHeikki Krogerus {
471ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = data;
472ae8a2ca8SHeikki Krogerus 	unsigned int usbc_irq1 = 0;
473ae8a2ca8SHeikki Krogerus 	unsigned int usbc_irq2 = 0;
474ae8a2ca8SHeikki Krogerus 	unsigned int cc1ctrl;
475ae8a2ca8SHeikki Krogerus 	int ret;
476ae8a2ca8SHeikki Krogerus 
477ae8a2ca8SHeikki Krogerus 	mutex_lock(&wcove->lock);
478ae8a2ca8SHeikki Krogerus 
479ae8a2ca8SHeikki Krogerus 	/* Read.. */
480ae8a2ca8SHeikki Krogerus 	ret = regmap_read(wcove->regmap, USBC_IRQ1, &usbc_irq1);
481ae8a2ca8SHeikki Krogerus 	if (ret)
482ae8a2ca8SHeikki Krogerus 		goto err;
483ae8a2ca8SHeikki Krogerus 
484ae8a2ca8SHeikki Krogerus 	ret = regmap_read(wcove->regmap, USBC_IRQ2, &usbc_irq2);
485ae8a2ca8SHeikki Krogerus 	if (ret)
486ae8a2ca8SHeikki Krogerus 		goto err;
487ae8a2ca8SHeikki Krogerus 
488ae8a2ca8SHeikki Krogerus 	ret = regmap_read(wcove->regmap, USBC_CC1_CTRL, &cc1ctrl);
489ae8a2ca8SHeikki Krogerus 	if (ret)
490ae8a2ca8SHeikki Krogerus 		goto err;
491ae8a2ca8SHeikki Krogerus 
492ae8a2ca8SHeikki Krogerus 	if (!wcove->tcpm)
493ae8a2ca8SHeikki Krogerus 		goto err;
494ae8a2ca8SHeikki Krogerus 
495ae8a2ca8SHeikki Krogerus 	/* ..check.. */
496ae8a2ca8SHeikki Krogerus 	if (usbc_irq1 & USBC_IRQ1_OVERTEMP) {
497ae8a2ca8SHeikki Krogerus 		dev_err(wcove->dev, "VCONN Switch Over Temperature!\n");
498ae8a2ca8SHeikki Krogerus 		wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, false);
499ae8a2ca8SHeikki Krogerus 		/* REVISIT: Report an error? */
500ae8a2ca8SHeikki Krogerus 	}
501ae8a2ca8SHeikki Krogerus 
502ae8a2ca8SHeikki Krogerus 	if (usbc_irq1 & USBC_IRQ1_SHORT) {
503ae8a2ca8SHeikki Krogerus 		dev_err(wcove->dev, "VCONN Switch Short Circuit!\n");
504ae8a2ca8SHeikki Krogerus 		wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, false);
505ae8a2ca8SHeikki Krogerus 		/* REVISIT: Report an error? */
506ae8a2ca8SHeikki Krogerus 	}
507ae8a2ca8SHeikki Krogerus 
508ae8a2ca8SHeikki Krogerus 	if (wcove->vbus != !!(cc1ctrl & USBC_CC_CTRL_VBUSOK))
509ae8a2ca8SHeikki Krogerus 		tcpm_vbus_change(wcove->tcpm);
510ae8a2ca8SHeikki Krogerus 
511ae8a2ca8SHeikki Krogerus 	/* REVISIT: See if tcpm code can be made to consider Type-C HW FSMs */
512ae8a2ca8SHeikki Krogerus 	if (usbc_irq2 & USBC_IRQ2_CC_CHANGE)
513ae8a2ca8SHeikki Krogerus 		tcpm_cc_change(wcove->tcpm);
514ae8a2ca8SHeikki Krogerus 
515ae8a2ca8SHeikki Krogerus 	if (usbc_irq2 & USBC_IRQ2_RX_PD) {
516ae8a2ca8SHeikki Krogerus 		unsigned int status;
517ae8a2ca8SHeikki Krogerus 
518ae8a2ca8SHeikki Krogerus 		/*
519ae8a2ca8SHeikki Krogerus 		 * FIXME: Need to check if TX is ongoing and report
520ae8a2ca8SHeikki Krogerus 		 * TX_DIREGARDED if needed?
521ae8a2ca8SHeikki Krogerus 		 */
522ae8a2ca8SHeikki Krogerus 
523ae8a2ca8SHeikki Krogerus 		ret = regmap_read(wcove->regmap, USBC_RXSTATUS, &status);
524ae8a2ca8SHeikki Krogerus 		if (ret)
525ae8a2ca8SHeikki Krogerus 			goto err;
526ae8a2ca8SHeikki Krogerus 
527ae8a2ca8SHeikki Krogerus 		/* Flush all buffers */
528ae8a2ca8SHeikki Krogerus 		while (status & USBC_RXSTATUS_RXDATA) {
529ae8a2ca8SHeikki Krogerus 			struct pd_message msg;
530ae8a2ca8SHeikki Krogerus 
531ae8a2ca8SHeikki Krogerus 			ret = wcove_read_rx_buffer(wcove, &msg);
532ae8a2ca8SHeikki Krogerus 			if (ret) {
533ae8a2ca8SHeikki Krogerus 				dev_err(wcove->dev, "%s: RX read failed\n",
534ae8a2ca8SHeikki Krogerus 					__func__);
535ae8a2ca8SHeikki Krogerus 				goto err;
536ae8a2ca8SHeikki Krogerus 			}
537ae8a2ca8SHeikki Krogerus 
538ae8a2ca8SHeikki Krogerus 			tcpm_pd_receive(wcove->tcpm, &msg);
539ae8a2ca8SHeikki Krogerus 
540ae8a2ca8SHeikki Krogerus 			ret = regmap_read(wcove->regmap, USBC_RXSTATUS,
541ae8a2ca8SHeikki Krogerus 					  &status);
542ae8a2ca8SHeikki Krogerus 			if (ret)
543ae8a2ca8SHeikki Krogerus 				goto err;
544ae8a2ca8SHeikki Krogerus 		}
545ae8a2ca8SHeikki Krogerus 	}
546ae8a2ca8SHeikki Krogerus 
547ae8a2ca8SHeikki Krogerus 	if (usbc_irq2 & USBC_IRQ2_RX_HR)
548ae8a2ca8SHeikki Krogerus 		tcpm_pd_hard_reset(wcove->tcpm);
549ae8a2ca8SHeikki Krogerus 
550ae8a2ca8SHeikki Krogerus 	/* REVISIT: if (usbc_irq2 & USBC_IRQ2_RX_CR) */
551ae8a2ca8SHeikki Krogerus 
552ae8a2ca8SHeikki Krogerus 	if (usbc_irq2 & USBC_IRQ2_TX_SUCCESS)
553ae8a2ca8SHeikki Krogerus 		tcpm_pd_transmit_complete(wcove->tcpm, TCPC_TX_SUCCESS);
554ae8a2ca8SHeikki Krogerus 
555ae8a2ca8SHeikki Krogerus 	if (usbc_irq2 & USBC_IRQ2_TX_FAIL)
556ae8a2ca8SHeikki Krogerus 		tcpm_pd_transmit_complete(wcove->tcpm, TCPC_TX_FAILED);
557ae8a2ca8SHeikki Krogerus 
558ae8a2ca8SHeikki Krogerus err:
559ae8a2ca8SHeikki Krogerus 	/* ..and clear. */
560ae8a2ca8SHeikki Krogerus 	if (usbc_irq1) {
561ae8a2ca8SHeikki Krogerus 		ret = regmap_write(wcove->regmap, USBC_IRQ1, usbc_irq1);
562ae8a2ca8SHeikki Krogerus 		if (ret)
563ae8a2ca8SHeikki Krogerus 			dev_WARN(wcove->dev, "%s failed to clear IRQ1\n",
564ae8a2ca8SHeikki Krogerus 				 __func__);
565ae8a2ca8SHeikki Krogerus 	}
566ae8a2ca8SHeikki Krogerus 
567ae8a2ca8SHeikki Krogerus 	if (usbc_irq2) {
568ae8a2ca8SHeikki Krogerus 		ret = regmap_write(wcove->regmap, USBC_IRQ2, usbc_irq2);
569ae8a2ca8SHeikki Krogerus 		if (ret)
570ae8a2ca8SHeikki Krogerus 			dev_WARN(wcove->dev, "%s failed to clear IRQ2\n",
571ae8a2ca8SHeikki Krogerus 				 __func__);
572ae8a2ca8SHeikki Krogerus 	}
573ae8a2ca8SHeikki Krogerus 
574ae8a2ca8SHeikki Krogerus 	/* REVISIT: Clear WhiskeyCove CHGR Type-C interrupt */
575ae8a2ca8SHeikki Krogerus 	regmap_write(wcove->regmap, WCOVE_CHGRIRQ0, BIT(5));
576ae8a2ca8SHeikki Krogerus 
577ae8a2ca8SHeikki Krogerus 	mutex_unlock(&wcove->lock);
578ae8a2ca8SHeikki Krogerus 	return IRQ_HANDLED;
579ae8a2ca8SHeikki Krogerus }
580ae8a2ca8SHeikki Krogerus 
581ae8a2ca8SHeikki Krogerus /*
582ae8a2ca8SHeikki Krogerus  * The following power levels should be safe to use with Joule board.
583ae8a2ca8SHeikki Krogerus  */
584ae8a2ca8SHeikki Krogerus static const u32 src_pdo[] = {
585ae8a2ca8SHeikki Krogerus 	PDO_FIXED(5000, 1500, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |
586ae8a2ca8SHeikki Krogerus 		  PDO_FIXED_USB_COMM),
587ae8a2ca8SHeikki Krogerus };
588ae8a2ca8SHeikki Krogerus 
589ae8a2ca8SHeikki Krogerus static const u32 snk_pdo[] = {
590ae8a2ca8SHeikki Krogerus 	PDO_FIXED(5000, 500, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |
591ae8a2ca8SHeikki Krogerus 		  PDO_FIXED_USB_COMM),
592ae8a2ca8SHeikki Krogerus 	PDO_VAR(5000, 12000, 3000),
593ae8a2ca8SHeikki Krogerus };
594ae8a2ca8SHeikki Krogerus 
5954c912bffSHeikki Krogerus static const struct property_entry wcove_props[] = {
5964c912bffSHeikki Krogerus 	PROPERTY_ENTRY_STRING("data-role", "dual"),
5974c912bffSHeikki Krogerus 	PROPERTY_ENTRY_STRING("power-role", "dual"),
5984c912bffSHeikki Krogerus 	PROPERTY_ENTRY_STRING("try-power-role", "sink"),
5994c912bffSHeikki Krogerus 	PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo),
6004c912bffSHeikki Krogerus 	PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo),
6010e64350bSThomas Hebb 	PROPERTY_ENTRY_U32("op-sink-microwatt", 15000000),
6024c912bffSHeikki Krogerus 	{ }
603ae8a2ca8SHeikki Krogerus };
604ae8a2ca8SHeikki Krogerus 
wcove_typec_probe(struct platform_device * pdev)605ae8a2ca8SHeikki Krogerus static int wcove_typec_probe(struct platform_device *pdev)
606ae8a2ca8SHeikki Krogerus {
607ae8a2ca8SHeikki Krogerus 	struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
608ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove;
609ae8a2ca8SHeikki Krogerus 	int irq;
610ae8a2ca8SHeikki Krogerus 	int ret;
611ae8a2ca8SHeikki Krogerus 
612ae8a2ca8SHeikki Krogerus 	wcove = devm_kzalloc(&pdev->dev, sizeof(*wcove), GFP_KERNEL);
613ae8a2ca8SHeikki Krogerus 	if (!wcove)
614ae8a2ca8SHeikki Krogerus 		return -ENOMEM;
615ae8a2ca8SHeikki Krogerus 
616ae8a2ca8SHeikki Krogerus 	mutex_init(&wcove->lock);
617ae8a2ca8SHeikki Krogerus 	wcove->dev = &pdev->dev;
618ae8a2ca8SHeikki Krogerus 	wcove->regmap = pmic->regmap;
619ae8a2ca8SHeikki Krogerus 
620e82adc10SGustavo A. R. Silva 	irq = platform_get_irq(pdev, 0);
621b33f3706SStephen Boyd 	if (irq < 0)
622e82adc10SGustavo A. R. Silva 		return irq;
623e82adc10SGustavo A. R. Silva 
624e82adc10SGustavo A. R. Silva 	irq = regmap_irq_get_virq(pmic->irq_chip_data_chgr, irq);
625ae8a2ca8SHeikki Krogerus 	if (irq < 0)
626ae8a2ca8SHeikki Krogerus 		return irq;
627ae8a2ca8SHeikki Krogerus 
628ae8a2ca8SHeikki Krogerus 	ret = guid_parse(WCOVE_DSM_UUID, &wcove->guid);
629ae8a2ca8SHeikki Krogerus 	if (ret)
630ae8a2ca8SHeikki Krogerus 		return ret;
631ae8a2ca8SHeikki Krogerus 
632ae8a2ca8SHeikki Krogerus 	if (!acpi_check_dsm(ACPI_HANDLE(&pdev->dev), &wcove->guid, 0, 0x1f)) {
633ae8a2ca8SHeikki Krogerus 		dev_err(&pdev->dev, "Missing _DSM functions\n");
634ae8a2ca8SHeikki Krogerus 		return -ENODEV;
635ae8a2ca8SHeikki Krogerus 	}
636ae8a2ca8SHeikki Krogerus 
637ae8a2ca8SHeikki Krogerus 	wcove->tcpc.init = wcove_init;
638ae8a2ca8SHeikki Krogerus 	wcove->tcpc.get_vbus = wcove_get_vbus;
639ae8a2ca8SHeikki Krogerus 	wcove->tcpc.set_vbus = wcove_set_vbus;
640ae8a2ca8SHeikki Krogerus 	wcove->tcpc.set_cc = wcove_set_cc;
641ae8a2ca8SHeikki Krogerus 	wcove->tcpc.get_cc = wcove_get_cc;
642ae8a2ca8SHeikki Krogerus 	wcove->tcpc.set_polarity = wcove_set_polarity;
643ae8a2ca8SHeikki Krogerus 	wcove->tcpc.set_vconn = wcove_set_vconn;
644ae8a2ca8SHeikki Krogerus 	wcove->tcpc.set_current_limit = wcove_set_current_limit;
6457893f9e1SHans de Goede 	wcove->tcpc.start_toggling = wcove_start_toggling;
646ae8a2ca8SHeikki Krogerus 
647ae8a2ca8SHeikki Krogerus 	wcove->tcpc.set_pd_rx = wcove_set_pd_rx;
648ae8a2ca8SHeikki Krogerus 	wcove->tcpc.set_roles = wcove_set_roles;
649ae8a2ca8SHeikki Krogerus 	wcove->tcpc.pd_transmit = wcove_pd_transmit;
650ae8a2ca8SHeikki Krogerus 
6514c912bffSHeikki Krogerus 	wcove->tcpc.fwnode = fwnode_create_software_node(wcove_props, NULL);
6524c912bffSHeikki Krogerus 	if (IS_ERR(wcove->tcpc.fwnode))
6534c912bffSHeikki Krogerus 		return PTR_ERR(wcove->tcpc.fwnode);
654ae8a2ca8SHeikki Krogerus 
655ae8a2ca8SHeikki Krogerus 	wcove->tcpm = tcpm_register_port(wcove->dev, &wcove->tcpc);
6564c912bffSHeikki Krogerus 	if (IS_ERR(wcove->tcpm)) {
6574c912bffSHeikki Krogerus 		fwnode_remove_software_node(wcove->tcpc.fwnode);
658ae8a2ca8SHeikki Krogerus 		return PTR_ERR(wcove->tcpm);
6594c912bffSHeikki Krogerus 	}
660ae8a2ca8SHeikki Krogerus 
661ae8a2ca8SHeikki Krogerus 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
662ae8a2ca8SHeikki Krogerus 					wcove_typec_irq, IRQF_ONESHOT,
663ae8a2ca8SHeikki Krogerus 					"wcove_typec", wcove);
664ae8a2ca8SHeikki Krogerus 	if (ret) {
665ae8a2ca8SHeikki Krogerus 		tcpm_unregister_port(wcove->tcpm);
6664c912bffSHeikki Krogerus 		fwnode_remove_software_node(wcove->tcpc.fwnode);
667ae8a2ca8SHeikki Krogerus 		return ret;
668ae8a2ca8SHeikki Krogerus 	}
669ae8a2ca8SHeikki Krogerus 
670ae8a2ca8SHeikki Krogerus 	platform_set_drvdata(pdev, wcove);
671ae8a2ca8SHeikki Krogerus 	return 0;
672ae8a2ca8SHeikki Krogerus }
673ae8a2ca8SHeikki Krogerus 
wcove_typec_remove(struct platform_device * pdev)674*72d70bf7SUwe Kleine-König static void wcove_typec_remove(struct platform_device *pdev)
675ae8a2ca8SHeikki Krogerus {
676ae8a2ca8SHeikki Krogerus 	struct wcove_typec *wcove = platform_get_drvdata(pdev);
677ae8a2ca8SHeikki Krogerus 	unsigned int val;
678ae8a2ca8SHeikki Krogerus 
679ae8a2ca8SHeikki Krogerus 	/* Mask everything */
680ae8a2ca8SHeikki Krogerus 	regmap_read(wcove->regmap, USBC_IRQMASK1, &val);
681ae8a2ca8SHeikki Krogerus 	regmap_write(wcove->regmap, USBC_IRQMASK1, val | USBC_IRQMASK1_ALL);
682ae8a2ca8SHeikki Krogerus 	regmap_read(wcove->regmap, USBC_IRQMASK2, &val);
683ae8a2ca8SHeikki Krogerus 	regmap_write(wcove->regmap, USBC_IRQMASK2, val | USBC_IRQMASK2_ALL);
684ae8a2ca8SHeikki Krogerus 
685ae8a2ca8SHeikki Krogerus 	tcpm_unregister_port(wcove->tcpm);
6864c912bffSHeikki Krogerus 	fwnode_remove_software_node(wcove->tcpc.fwnode);
687ae8a2ca8SHeikki Krogerus }
688ae8a2ca8SHeikki Krogerus 
689ae8a2ca8SHeikki Krogerus static struct platform_driver wcove_typec_driver = {
690ae8a2ca8SHeikki Krogerus 	.driver = {
691ae8a2ca8SHeikki Krogerus 		.name		= "bxt_wcove_usbc",
692ae8a2ca8SHeikki Krogerus 	},
693ae8a2ca8SHeikki Krogerus 	.probe			= wcove_typec_probe,
694*72d70bf7SUwe Kleine-König 	.remove_new		= wcove_typec_remove,
695ae8a2ca8SHeikki Krogerus };
696ae8a2ca8SHeikki Krogerus 
697ae8a2ca8SHeikki Krogerus module_platform_driver(wcove_typec_driver);
698ae8a2ca8SHeikki Krogerus 
699ae8a2ca8SHeikki Krogerus MODULE_AUTHOR("Intel Corporation");
700ae8a2ca8SHeikki Krogerus MODULE_LICENSE("GPL v2");
701ae8a2ca8SHeikki Krogerus MODULE_DESCRIPTION("WhiskeyCove PMIC USB Type-C PHY driver");
702ae8a2ca8SHeikki Krogerus MODULE_ALIAS("platform:bxt_wcove_usbc");
703