FILE
maxim_contaminant.c
File preview with syntax highlighting.
linux-6.19.14/drivers/usb/typec/tcpm/maxim_contaminant.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.