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