1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2022 Google, Inc
4 *
5 * USB-C module to reduce wakeups due to contaminants.
6 */
7
8#include <linux/device.h>
9#include <linux/irqreturn.h>
10#include <linux/module.h>
11#include <linux/regmap.h>
12#include <linux/usb/tcpci.h>
13#include <linux/usb/tcpm.h>
14#include <linux/usb/typec.h>
15
16#include "tcpci_maxim.h"
17
18enum fladc_select {
19 CC1_SCALE1 = 1,
20 CC1_SCALE2,
21 CC2_SCALE1,
22 CC2_SCALE2,
23 SBU1,
24 SBU2,
25};
26
27#define FLADC_1uA_LSB_MV 25
28/* High range CC */
29#define FLADC_CC_HIGH_RANGE_LSB_MV 208
30/* Low range CC */
31#define FLADC_CC_LOW_RANGE_LSB_MV 126
32
33/* 1uA current source */
34#define FLADC_CC_SCALE1 1
35/* 5 uA current source */
36#define FLADC_CC_SCALE2 5
37
38#define FLADC_1uA_CC_OFFSET_MV 300
39#define FLADC_CC_HIGH_RANGE_OFFSET_MV 624
40#define FLADC_CC_LOW_RANGE_OFFSET_MV 378
41
42#define CONTAMINANT_THRESHOLD_SBU_K 1000
43#define CONTAMINANT_THRESHOLD_CC_K 1000
44
45#define READ1_SLEEP_MS 10
46#define READ2_SLEEP_MS 5
47
48#define STATUS_CHECK(reg, mask, val) (((reg) & (mask)) == (val))
49
50#define IS_CC_OPEN(cc_status) \
51 (STATUS_CHECK((cc_status), TCPC_CC_STATUS_CC1_MASK << TCPC_CC_STATUS_CC1_SHIFT, \
52 TCPC_CC_STATE_SRC_OPEN) && STATUS_CHECK((cc_status), \
53 TCPC_CC_STATUS_CC2_MASK << \
54 TCPC_CC_STATUS_CC2_SHIFT, \
55 TCPC_CC_STATE_SRC_OPEN))
56
57static int max_contaminant_adc_to_mv(struct max_tcpci_chip *chip, enum fladc_select channel,
58 bool ua_src, u8 fladc)
59{
60 /* SBU channels only have 1 scale with 1uA. */
61 if ((ua_src && (channel == CC1_SCALE2 || channel == CC2_SCALE2 || channel == SBU1 ||
62 channel == SBU2)))
63 /* Mean of range */
64 return FLADC_1uA_CC_OFFSET_MV + (fladc * FLADC_1uA_LSB_MV);
65 else if (!ua_src && (channel == CC1_SCALE1 || channel == CC2_SCALE1))
66 return FLADC_CC_HIGH_RANGE_OFFSET_MV + (fladc * FLADC_CC_HIGH_RANGE_LSB_MV);
67 else if (!ua_src && (channel == CC1_SCALE2 || channel == CC2_SCALE2))
68 return FLADC_CC_LOW_RANGE_OFFSET_MV + (fladc * FLADC_CC_LOW_RANGE_LSB_MV);
69
70 dev_err_once(chip->dev, "ADC ERROR: SCALE UNKNOWN");
71
72 return -EINVAL;
73}
74
75static int max_contaminant_read_adc_mv(struct max_tcpci_chip *chip, enum fladc_select channel,
76 int sleep_msec, bool raw, bool ua_src)
77{
78 struct regmap *regmap = chip->data.regmap;
79 u8 fladc;
80 int ret;
81
82 /* Channel & scale select */
83 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK,
84 val: channel << ADC_CHANNEL_OFFSET);
85 if (ret < 0)
86 return ret;
87
88 /* Enable ADC */
89 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_ADC_CTRL1, ADCEN, ADCEN);
90 if (ret < 0)
91 return ret;
92
93 usleep_range(min: sleep_msec * 1000, max: (sleep_msec + 1) * 1000);
94 ret = max_tcpci_read8(chip, TCPC_VENDOR_FLADC_STATUS, val: &fladc);
95 if (ret < 0)
96 return ret;
97
98 /* Disable ADC */
99 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_ADC_CTRL1, ADCEN, val: 0);
100 if (ret < 0)
101 return ret;
102
103 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, val: 0);
104 if (ret < 0)
105 return ret;
106
107 if (!raw)
108 return max_contaminant_adc_to_mv(chip, channel, ua_src, fladc);
109 else
110 return fladc;
111}
112
113static int max_contaminant_read_resistance_kohm(struct max_tcpci_chip *chip,
114 enum fladc_select channel, int sleep_msec, bool raw)
115{
116 struct regmap *regmap = chip->data.regmap;
117 int mv;
118 int ret;
119
120 if (channel == CC1_SCALE1 || channel == CC2_SCALE1 || channel == CC1_SCALE2 ||
121 channel == CC2_SCALE2) {
122 /* Enable 1uA current source */
123 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK,
124 ULTRA_LOW_POWER_MODE);
125 if (ret < 0)
126 return ret;
127
128 /* Enable 1uA current source */
129 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_1_SRC);
130 if (ret < 0)
131 return ret;
132
133 /* OVP disable */
134 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, CCOVPDIS, CCOVPDIS);
135 if (ret < 0)
136 return ret;
137
138 mv = max_contaminant_read_adc_mv(chip, channel, sleep_msec, raw, ua_src: true);
139 if (mv < 0)
140 return ret;
141
142 /* OVP enable */
143 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, CCOVPDIS, val: 0);
144 if (ret < 0)
145 return ret;
146 /* returns KOhm as 1uA source is used. */
147 return mv;
148 }
149
150 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, SBUOVPDIS, SBUOVPDIS);
151 if (ret < 0)
152 return ret;
153
154 /* SBU switches auto configure when channel is selected. */
155 /* Enable 1ua current source */
156 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, SBURPCTRL, SBURPCTRL);
157 if (ret < 0)
158 return ret;
159
160 mv = max_contaminant_read_adc_mv(chip, channel, sleep_msec, raw, ua_src: true);
161 if (mv < 0)
162 return ret;
163 /* Disable current source */
164 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, SBURPCTRL, val: 0);
165 if (ret < 0)
166 return ret;
167
168 /* OVP disable */
169 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, SBUOVPDIS, val: 0);
170 if (ret < 0)
171 return ret;
172
173 return mv;
174}
175
176static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *vendor_cc_status2_cc1,
177 u8 *vendor_cc_status2_cc2)
178{
179 struct regmap *regmap = chip->data.regmap;
180 int ret;
181
182 /* Enable 80uA source */
183 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_80_SRC);
184 if (ret < 0)
185 return ret;
186
187 /* Enable comparators */
188 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL1, CCCOMPEN, CCCOMPEN);
189 if (ret < 0)
190 return ret;
191
192 /* Sleep to allow comparators settle */
193 usleep_range(min: 5000, max: 6000);
194 ret = regmap_update_bits(map: regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC1);
195 if (ret < 0)
196 return ret;
197
198 usleep_range(min: 5000, max: 6000);
199 ret = max_tcpci_read8(chip, VENDOR_CC_STATUS2, val: vendor_cc_status2_cc1);
200 if (ret < 0)
201 return ret;
202
203 ret = regmap_update_bits(map: regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC2);
204 if (ret < 0)
205 return ret;
206
207 usleep_range(min: 5000, max: 6000);
208 ret = max_tcpci_read8(chip, VENDOR_CC_STATUS2, val: vendor_cc_status2_cc2);
209 if (ret < 0)
210 return ret;
211
212 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL1, CCCOMPEN, val: 0);
213 if (ret < 0)
214 return ret;
215
216 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, val: 0);
217 if (ret < 0)
218 return ret;
219
220 return 0;
221}
222
223static int max_contaminant_detect_contaminant(struct max_tcpci_chip *chip)
224{
225 int cc1_k, cc2_k, sbu1_k, sbu2_k, ret;
226 u8 vendor_cc_status2_cc1 = 0xff, vendor_cc_status2_cc2 = 0xff;
227 u8 role_ctrl = 0, role_ctrl_backup = 0;
228 int inferred_state = NOT_DETECTED;
229
230 ret = max_tcpci_read8(chip, TCPC_ROLE_CTRL, val: &role_ctrl);
231 if (ret < 0)
232 return NOT_DETECTED;
233
234 role_ctrl_backup = role_ctrl;
235 role_ctrl = 0x0F;
236 ret = max_tcpci_write8(chip, TCPC_ROLE_CTRL, val: role_ctrl);
237 if (ret < 0)
238 return NOT_DETECTED;
239
240 cc1_k = max_contaminant_read_resistance_kohm(chip, channel: CC1_SCALE2, READ1_SLEEP_MS, raw: false);
241 if (cc1_k < 0)
242 goto exit;
243
244 cc2_k = max_contaminant_read_resistance_kohm(chip, channel: CC2_SCALE2, READ2_SLEEP_MS, raw: false);
245 if (cc2_k < 0)
246 goto exit;
247
248 sbu1_k = max_contaminant_read_resistance_kohm(chip, channel: SBU1, READ1_SLEEP_MS, raw: false);
249 if (sbu1_k < 0)
250 goto exit;
251
252 sbu2_k = max_contaminant_read_resistance_kohm(chip, channel: SBU2, READ2_SLEEP_MS, raw: false);
253 if (sbu2_k < 0)
254 goto exit;
255
256 ret = max_contaminant_read_comparators(chip, vendor_cc_status2_cc1: &vendor_cc_status2_cc1,
257 vendor_cc_status2_cc2: &vendor_cc_status2_cc2);
258
259 if (ret < 0)
260 goto exit;
261
262 if ((!(CC1_VUFP_RD0P5 & vendor_cc_status2_cc1) ||
263 !(CC2_VUFP_RD0P5 & vendor_cc_status2_cc2)) &&
264 !(CC1_VUFP_RD0P5 & vendor_cc_status2_cc1 && CC2_VUFP_RD0P5 & vendor_cc_status2_cc2))
265 inferred_state = SINK;
266 else if ((cc1_k < CONTAMINANT_THRESHOLD_CC_K || cc2_k < CONTAMINANT_THRESHOLD_CC_K) &&
267 (sbu1_k < CONTAMINANT_THRESHOLD_SBU_K || sbu2_k < CONTAMINANT_THRESHOLD_SBU_K))
268 inferred_state = DETECTED;
269
270 if (inferred_state == NOT_DETECTED)
271 max_tcpci_write8(chip, TCPC_ROLE_CTRL, val: role_ctrl_backup);
272 else
273 max_tcpci_write8(chip, TCPC_ROLE_CTRL, val: (TCPC_ROLE_CTRL_DRP | 0xA));
274
275 return inferred_state;
276exit:
277 max_tcpci_write8(chip, TCPC_ROLE_CTRL, val: role_ctrl_backup);
278 return NOT_DETECTED;
279}
280
281static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
282{
283 struct regmap *regmap = chip->data.regmap;
284 u8 temp;
285 int ret;
286
287 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL3, CCWTRDEB_MASK | CCWTRSEL_MASK
288 | WTRCYCLE_MASK, CCWTRDEB_1MS << CCWTRDEB_SHIFT |
289 CCWTRSEL_1V << CCWTRSEL_SHIFT | WTRCYCLE_4_8_S <<
290 WTRCYCLE_SHIFT);
291 if (ret < 0)
292 return ret;
293
294 ret = regmap_update_bits(map: regmap, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP, TCPC_ROLE_CTRL_DRP);
295 if (ret < 0)
296 return ret;
297
298 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL1, CCCONNDRY, CCCONNDRY);
299 if (ret < 0)
300 return ret;
301 ret = max_tcpci_read8(chip, TCPC_VENDOR_CC_CTRL1, val: &temp);
302 if (ret < 0)
303 return ret;
304
305 ret = regmap_update_bits(map: regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK,
306 ULTRA_LOW_POWER_MODE);
307 if (ret < 0)
308 return ret;
309 ret = max_tcpci_read8(chip, TCPC_VENDOR_CC_CTRL2, val: &temp);
310 if (ret < 0)
311 return ret;
312
313 /* Enable Look4Connection before sending the command */
314 ret = regmap_update_bits(map: regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_EN_LK4CONN_ALRT,
315 TCPC_TCPC_CTRL_EN_LK4CONN_ALRT);
316 if (ret < 0)
317 return ret;
318
319 ret = max_tcpci_write8(chip, TCPC_COMMAND, TCPC_CMD_LOOK4CONNECTION);
320 if (ret < 0)
321 return ret;
322 return 0;
323}
324
325bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce)
326{
327 u8 cc_status, pwr_cntl;
328 int ret;
329
330 ret = max_tcpci_read8(chip, TCPC_CC_STATUS, val: &cc_status);
331 if (ret < 0)
332 return false;
333
334 ret = max_tcpci_read8(chip, TCPC_POWER_CTRL, val: &pwr_cntl);
335 if (ret < 0)
336 return false;
337
338 if (chip->contaminant_state == NOT_DETECTED || chip->contaminant_state == SINK) {
339 if (!disconnect_while_debounce)
340 msleep(msecs: 100);
341
342 ret = max_tcpci_read8(chip, TCPC_CC_STATUS, val: &cc_status);
343 if (ret < 0)
344 return false;
345
346 if (IS_CC_OPEN(cc_status)) {
347 u8 role_ctrl, role_ctrl_backup;
348
349 ret = max_tcpci_read8(chip, TCPC_ROLE_CTRL, val: &role_ctrl);
350 if (ret < 0)
351 return false;
352
353 role_ctrl_backup = role_ctrl;
354 role_ctrl |= 0x0F;
355 role_ctrl &= ~(TCPC_ROLE_CTRL_DRP);
356 ret = max_tcpci_write8(chip, TCPC_ROLE_CTRL, val: role_ctrl);
357 if (ret < 0)
358 return false;
359
360 chip->contaminant_state = max_contaminant_detect_contaminant(chip);
361
362 ret = max_tcpci_write8(chip, TCPC_ROLE_CTRL, val: role_ctrl_backup);
363 if (ret < 0)
364 return false;
365
366 if (chip->contaminant_state == DETECTED) {
367 max_contaminant_enable_dry_detection(chip);
368 return true;
369 }
370 }
371 return false;
372 } else if (chip->contaminant_state == DETECTED) {
373 if (STATUS_CHECK(cc_status, TCPC_CC_STATUS_TOGGLING, 0)) {
374 chip->contaminant_state = max_contaminant_detect_contaminant(chip);
375 if (chip->contaminant_state == DETECTED) {
376 max_contaminant_enable_dry_detection(chip);
377 return true;
378 }
379 }
380 }
381
382 return false;
383}
384
385MODULE_DESCRIPTION("MAXIM TCPC CONTAMINANT Module");
386MODULE_AUTHOR("Badhri Jagan Sridharan <badhri@google.com>");
387MODULE_LICENSE("GPL");
388

source code of linux/drivers/usb/typec/tcpm/maxim_contaminant.c