File Index
Files and directories
Sign in
FILE

maxim_contaminant.c

File preview with syntax highlighting.

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