diff options
author | Badhri Jagan Sridharan <badhri@google.com> | 2022-09-06 19:48:23 -0700 |
---|---|---|
committer | Kyle Tso <kyletso@google.com> | 2022-09-22 09:16:10 +0000 |
commit | 6269afd8d08df762856d47c57bd73dcb2d1d550b (patch) | |
tree | 05b697335cd2b521df59aa8ce1c96a75a452082c | |
parent | d24534ba18ee6cddbf028b619c9320b5023e3f50 (diff) | |
download | gs-android-gs-bluejay-5.10-android13-qpr1-beta-3.tar.gz |
tcpci_max77759: Handle IO error by deferring and retryingandroid-t-qpr1-beta-3_r0.4android-t-qpr1-beta-3_r0.3android-13.0.0_r0.49android-13.0.0_r0.48android-gs-raviole-5.10-android13-qpr1-beta-3android-gs-bluejay-5.10-android13-qpr1-beta-3
When register reads/writes fail, exit early and retry after
3 seconds instead of continuing with successive I2C IO. This
prevents I2C transactions that are anyways going to fail.
Instead, The last read irq status is cached and retried after
the 3 seconds.
Bug: 235540519
Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
Change-Id: Ie2a18c7fc9d310fe0a334b3c5b16840ef4159f29
(cherry picked from commit 105cf682df05aa51b0dbcdaba76999680e400633)
-rw-r--r-- | drivers/usb/typec/tcpm/google/max77759_contaminant.c | 257 | ||||
-rw-r--r-- | drivers/usb/typec/tcpm/google/tcpci_max77759.c | 134 | ||||
-rw-r--r-- | drivers/usb/typec/tcpm/google/tcpci_max77759.h | 13 |
3 files changed, 269 insertions, 135 deletions
diff --git a/drivers/usb/typec/tcpm/google/max77759_contaminant.c b/drivers/usb/typec/tcpm/google/max77759_contaminant.c index 3f3ce534ee60..ee00c96ee678 100644 --- a/drivers/usb/typec/tcpm/google/max77759_contaminant.c +++ b/drivers/usb/typec/tcpm/google/max77759_contaminant.c @@ -103,29 +103,29 @@ static int read_adc_mv(struct max77759_contaminant *contaminant, ret = max77759_update_bits8(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, channel << ADC_CHANNEL_OFFSET); if (ret < 0) - return ret; + return -EIO; /* Enable ADC */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_ADC_CTRL1, ADCEN, ADCEN); if (ret < 0) - return ret; + return -EIO; MAX77759_LOG_REGISTER(regmap, TCPC_VENDOR_ADC_CTRL1, log); usleep_range(sleep_msec * 1000, (sleep_msec + 1) * 1000); ret = max77759_read8(regmap, TCPC_VENDOR_FLADC_STATUS, &fladc); if (ret < 0) - return ret; + return -EIO; logbuffer_log(log, "Contaminant: ADC %u", fladc); /* Disable ADC */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_ADC_CTRL1, ADCEN, 0); if (ret < 0) - return ret; + return -EIO; ret = max77759_update_bits8(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, 0); if (ret < 0) - return ret; + return -EIO; if (!raw) return adc_to_mv(contaminant, channel, ua_src, fladc); @@ -149,7 +149,7 @@ static int read_resistance_kohm(struct max77759_contaminant *contaminant, ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, ULTRA_LOW_POWER_MODE); if (ret < 0) - return ret; + return -EIO; /* * CC resistive ladder is automatically disabled when * 1uA source is ON and Flash ADC channel is not CC scale1. @@ -161,12 +161,12 @@ static int read_resistance_kohm(struct max77759_contaminant *contaminant, /* Enable 1uA current source */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_1_SRC); if (ret < 0) - return ret; + return -EIO; /* OVP disable */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCOVPDIS, CCOVPDIS); if (ret < 0) - return ret; + return -EIO; MAX77759_LOG_REGISTER(regmap, TCPC_VENDOR_CC_CTRL2, log); @@ -174,7 +174,7 @@ static int read_resistance_kohm(struct max77759_contaminant *contaminant, /* OVP enable */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCOVPDIS, 0); if (ret < 0) - return ret; + return -EIO; /* returns KOhm as 1uA source is used. */ return mv; } @@ -187,39 +187,39 @@ static int read_resistance_kohm(struct max77759_contaminant *contaminant, ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, ULTRA_LOW_POWER_MODE); if (ret < 0) - return ret; + return -EIO; ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, SBUOVPDIS, SBUOVPDIS); if (ret < 0) - return ret; + return -EIO; /* Cache switch setting */ ret = max77759_read8(regmap, TCPC_VENDOR_SBUSW_CTRL, &switch_setting); if (ret < 0) - return ret; + return -EIO; MAX77759_LOG_REGISTER(regmap, TCPC_VENDOR_SBUSW_CTRL, log); /* SBU switches auto configure when channel is selected. */ /* Enable 1ua current source */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, SBURPCTRL, SBURPCTRL); if (ret < 0) - return ret; + return -EIO; MAX77759_LOG_REGISTER(regmap, TCPC_VENDOR_CC_CTRL2, log); mv = read_adc_mv(contaminant, channel, sleep_msec, raw, true); /* Disable current source */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, SBURPCTRL, 0); if (ret < 0) - return ret; + return -EIO; /* Set switch to original setting */ ret = max77759_write8(regmap, TCPC_VENDOR_SBUSW_CTRL, switch_setting); if (ret < 0) - return ret; + return -EIO; /* OVP disable */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, SBUOVPDIS, 0); if (ret < 0) - return ret; + return -EIO; /* * 1ua current source on sbu; @@ -229,9 +229,9 @@ static int read_resistance_kohm(struct max77759_contaminant *contaminant, return mv; } -static void read_comparators(struct max77759_contaminant *contaminant, - u8 *vendor_cc_status2_cc1, - u8 *vendor_cc_status2_cc2) +static int read_comparators(struct max77759_contaminant *contaminant, + u8 *vendor_cc_status2_cc1, + u8 *vendor_cc_status2_cc2) { struct regmap *regmap = contaminant->chip->data.regmap; struct logbuffer *log = contaminant->chip->log; @@ -242,12 +242,12 @@ static void read_comparators(struct max77759_contaminant *contaminant, /* Enable 80uA source */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_80_SRC); if (ret < 0) - return; + return -EIO; /* Enable comparators */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL1, CCCOMPEN, CCCOMPEN); if (ret < 0) - return; + return -EIO; MAX77759_LOG_REGISTER(regmap, TCPC_VENDOR_CC_CTRL1, log); @@ -255,7 +255,7 @@ static void read_comparators(struct max77759_contaminant *contaminant, ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, LOW_POWER_MODE_DISABLE); if (ret < 0) - return; + return -EIO; MAX77759_LOG_REGISTER(regmap, TCPC_VENDOR_CC_CTRL2, log); /* Sleep to allow comparators settle */ @@ -263,47 +263,55 @@ static void read_comparators(struct max77759_contaminant *contaminant, ret = max77759_update_bits8(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC1); if (ret < 0) - return; + return -EIO; MAX77759_LOG_REGISTER(regmap, TCPC_TCPC_CTRL, log); usleep_range(5000, 6000); ret = max77759_read8(regmap, VENDOR_CC_STATUS2, vendor_cc_status2_cc1); if (ret < 0) - return; + return -EIO; logbuffer_log(log, "Contaminant: VENDOR_CC_STATUS2: %u", *vendor_cc_status2_cc1); ret = max77759_update_bits8(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC2); if (ret < 0) - return; + return -EIO; MAX77759_LOG_REGISTER(regmap, TCPC_TCPC_CTRL, log); usleep_range(5000, 6000); ret = max77759_read8(regmap, VENDOR_CC_STATUS2, vendor_cc_status2_cc2); if (ret < 0) - return; + return -EIO; logbuffer_log(contaminant->chip->log, "Contaminant: VENDOR_CC_STATUS2: %u", *vendor_cc_status2_cc2); ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL1, CCCOMPEN, 0); if (ret < 0) - return; - max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, 0); + return -EIO; + ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, 0); + if (ret < 0) + return -EIO; + + return 0; } static int detect_contaminant(struct max77759_contaminant *contaminant) { - int cc1_k, cc2_k, sbu1_k, sbu2_k; + int cc1_k, cc2_k, sbu1_k, sbu2_k, ret; u8 vendor_cc_status2_cc1 = 0xff, vendor_cc_status2_cc2 = 0xff; u8 role_ctrl = 0, role_ctrl_backup = 0; struct max77759_plat *chip = contaminant->chip; int inferred_state = NOT_DETECTED; struct regmap *regmap = contaminant->chip->data.regmap; - max77759_read8(regmap, TCPC_ROLE_CTRL, &role_ctrl); + ret = max77759_read8(regmap, TCPC_ROLE_CTRL, &role_ctrl); + if (ret < 0) + return -EIO; role_ctrl_backup = role_ctrl; role_ctrl = 0x0F; - max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl); + ret = max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl); + if (ret < 0) + return -EIO; /* CCLPMODESEL_AUTO_LOW_POWER in use. */ cc1_k = read_resistance_kohm(contaminant, CC1_SCALE2, READ1_SLEEP_MS, false); @@ -313,7 +321,9 @@ static int detect_contaminant(struct max77759_contaminant *contaminant) sbu1_k = read_resistance_kohm(contaminant, SBU1, READ1_SLEEP_MS, false); sbu2_k = read_resistance_kohm(contaminant, SBU2, READ2_SLEEP_MS, false); logbuffer_log(chip->log, "Contaminant: sbu1_k:%u sbu2_k:%u", sbu1_k, sbu2_k); - read_comparators(contaminant, &vendor_cc_status2_cc1, &vendor_cc_status2_cc2); + ret = read_comparators(contaminant, &vendor_cc_status2_cc1, &vendor_cc_status2_cc2); + if (ret == -EIO) + return ret; logbuffer_log(chip->log, "Contaminant: vcc2_cc1:%u vcc2_cc2:%u", vendor_cc_status2_cc1, vendor_cc_status2_cc2); @@ -338,9 +348,12 @@ static int detect_contaminant(struct max77759_contaminant *contaminant) } if (inferred_state == NOT_DETECTED) - max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl_backup); + ret = max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl_backup); else - max77759_write8(regmap, TCPC_ROLE_CTRL, (TCPC_ROLE_CTRL_DRP | 0xA)); + ret = max77759_write8(regmap, TCPC_ROLE_CTRL, (TCPC_ROLE_CTRL_DRP | 0xA)); + + if (ret < 0) + return -EIO; return inferred_state; } @@ -362,40 +375,40 @@ static int enable_dry_detection(struct max77759_contaminant *contaminant) CCWTRSEL_1V << CCWTRSEL_SHIFT | WTRCYCLE_4_8_S << WTRCYCLE_SHIFT); if (ret < 0) - return ret; + return -EIO; ret = max77759_update_bits8(regmap, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP, TCPC_ROLE_CTRL_DRP); if (ret < 0) - return ret; + return -EIO; /* tunable: 1ua / Ultra low power mode enabled. */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL1, CCCONNDRY, CCCONNDRY); if (ret < 0) - return ret; + return -EIO; ret = max77759_read8(regmap, TCPC_VENDOR_CC_CTRL1, &temp); if (ret < 0) - return ret; + return -EIO; logbuffer_log(chip->log, "Contaminant: TCPC_VENDOR_CC_CTRL1 %u", temp); ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, ULTRA_LOW_POWER_MODE); if (ret < 0) - return ret; + return -EIO; ret = max77759_read8(regmap, TCPC_VENDOR_CC_CTRL2, &temp); if (ret < 0) - return ret; + return -EIO; logbuffer_log(chip->log, "Contaminant: TCPC_VENDOR_CC_CTRL2 %u", temp); /* Enable Look4Connection before sending the command */ ret = max77759_update_bits8(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_EN_LK4CONN_ALRT, TCPC_TCPC_CTRL_EN_LK4CONN_ALRT); if (ret < 0) - return ret; + return -EIO; ret = max77759_write8(regmap, TCPC_COMMAND, TCPC_CMD_LOOK4CONNECTION); if (ret < 0) - return ret; + return -EIO; logbuffer_log(chip->log, "Contaminant: Dry detecion enabled"); return 0; } @@ -410,10 +423,14 @@ static int maxq_detect_contaminant(struct max77759_contaminant *contaminant, u8 struct regmap *regmap = contaminant->chip->data.regmap; u8 response[5]; - max77759_read8(regmap, TCPC_ROLE_CTRL, &role_ctrl); + ret = max77759_read8(regmap, TCPC_ROLE_CTRL, &role_ctrl); + if (ret < 0) + return -EIO; role_ctrl_backup = role_ctrl; role_ctrl = 0x0F; - max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl); + ret = max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl); + if (ret < 0) + return -EIO; logbuffer_log(chip->log, "Contaminant: Query Maxq"); if (contaminant->state == NOT_DETECTED) { @@ -449,9 +466,12 @@ static int maxq_detect_contaminant(struct max77759_contaminant *contaminant, u8 response[0], response[2], response[3], response[4]); if (ret == NOT_DETECTED) - max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl_backup); + ret = max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl_backup); else - max77759_write8(regmap, TCPC_ROLE_CTRL, (TCPC_ROLE_CTRL_DRP | 0xA)); + ret = max77759_write8(regmap, TCPC_ROLE_CTRL, (TCPC_ROLE_CTRL_DRP | 0xA)); + + if (ret < 0) + return -EIO; return ret; } @@ -481,23 +501,28 @@ static void update_contaminant_state(struct max77759_contaminant *contaminant, * Don't want to be in workqueue as this is time critical for the state machine * to forward progress. */ -bool process_contaminant_alert(struct max77759_contaminant *contaminant, bool debounce_path, - bool tcpm_toggling) +int process_contaminant_alert(struct max77759_contaminant *contaminant, bool debounce_path, + bool tcpm_toggling, bool *cc_update_handled) { u8 cc_status, pwr_cntl; struct regmap *regmap = contaminant->chip->data.regmap; enum contamiant_state state; struct max77759_plat *chip = contaminant->chip; + int ret; /* * Contaminant alert should only be processed when ALERT.CC_STAT is set. * Caller i.e. the top level interrupt handler can check this to * prevent redundant reads. */ - max77759_read8(regmap, TCPC_CC_STATUS, &cc_status); + ret = max77759_read8(regmap, TCPC_CC_STATUS, &cc_status); + if (ret < 0) + return -EIO; logbuffer_log(chip->log, "Contaminant: CC_STATUS: %#x", cc_status); - max77759_read8(regmap, TCPC_POWER_CTRL, &pwr_cntl); + ret = max77759_read8(regmap, TCPC_POWER_CTRL, &pwr_cntl); + if (ret < 0) + return -EIO; logbuffer_log(chip->log, "Contaminant: POWER_CONTROL: %#x", pwr_cntl); /* Exit if still LookingForConnection. */ @@ -505,9 +530,13 @@ bool process_contaminant_alert(struct max77759_contaminant *contaminant, bool de logbuffer_log(chip->log, "Contaminant: Looking for connection"); /* Restart toggling before returning in debounce path */ if (debounce_path && (contaminant->state == NOT_DETECTED || - contaminant->state == SINK)) - enable_contaminant_detection(contaminant->chip, contaminant_detect_maxq); - return false; + contaminant->state == SINK)) { + ret = enable_contaminant_detection(contaminant->chip, + contaminant_detect_maxq); + if (ret == -EIO) + return ret; + } + *cc_update_handled = false; } if (contaminant->state == NOT_DETECTED || contaminant->state == SINK || @@ -520,19 +549,27 @@ bool process_contaminant_alert(struct max77759_contaminant *contaminant, bool de TCPC_CC_STATE_WTRSEL << TCPC_CC_STATUS_CC2_SHIFT))) && (status_check(cc_status, TCPC_CC_STATUS_TOGGLING, 0))) { logbuffer_log(chip->log, "Contaminant: Check if wet: CC 0x3"); - state = contaminant_detect_maxq ? + ret = contaminant_detect_maxq ? maxq_detect_contaminant(contaminant, cc_status) : detect_contaminant(contaminant); + if (ret == -EIO) + return ret; + state = ret; update_contaminant_state(contaminant, state); if (state == DETECTED) { - enable_dry_detection(contaminant); - return true; + ret = enable_dry_detection(contaminant); + if (ret == -EIO) + return ret; + *cc_update_handled = true; } /* Sink or Not detected */ - enable_contaminant_detection(contaminant->chip, contaminant_detect_maxq); - return true; + ret = enable_contaminant_detection(contaminant->chip, + contaminant_detect_maxq); + if (ret == -EIO) + return ret; + *cc_update_handled = true; } else { /* Need to check again after tCCDebounce */ if (((cc_status & TCPC_CC_STATUS_TOGGLING) == 0) && @@ -546,42 +583,62 @@ bool process_contaminant_alert(struct max77759_contaminant *contaminant, bool de msleep(100); } - max77759_read8(regmap, TCPC_CC_STATUS, &cc_status); + ret = max77759_read8(regmap, TCPC_CC_STATUS, &cc_status); + if (ret < 0) + return ret; logbuffer_log(chip->log, "Contaminant: CC_STATUS check stage 3 sw WAR: %#x", cc_status); if (is_cc_open(cc_status)) { u8 role_ctrl, role_ctrl_backup; - max77759_read8(regmap, TCPC_ROLE_CTRL, &role_ctrl); + ret = max77759_read8(regmap, TCPC_ROLE_CTRL, &role_ctrl); + if (ret < 0) + return ret; role_ctrl_backup = role_ctrl; role_ctrl |= 0x0F; role_ctrl &= ~(TCPC_ROLE_CTRL_DRP); - max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl); + ret = max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl); + if (ret < 0) + return ret; logbuffer_log(chip->log, "Contaminant: Check if wet (stage 3)"); - state = contaminant_detect_maxq ? + ret = contaminant_detect_maxq ? maxq_detect_contaminant(contaminant, cc_status) : detect_contaminant(contaminant); + if (ret == -EIO) + return ret; + state = ret; update_contaminant_state(contaminant, state); - max77759_write8(regmap, TCPC_ROLE_CTRL, role_ctrl_backup); + ret = max77759_write8(regmap, TCPC_ROLE_CTRL, + role_ctrl_backup); + if (ret < 0) + return ret; if (state == DETECTED) { - enable_dry_detection(contaminant); - return true; + ret = enable_dry_detection(contaminant); + if (ret == -EIO) + return ret; + *cc_update_handled = true; } /* Sink or Not detected */ - enable_contaminant_detection(contaminant->chip, - contaminant_detect_maxq); + ret = enable_contaminant_detection(contaminant->chip, + contaminant_detect_maxq); + if (ret == -EIO) + return ret; } } } /* Restart toggling before returning in debounce path */ - if (debounce_path) - enable_contaminant_detection(contaminant->chip, contaminant_detect_maxq); - return false; + if (debounce_path) { + ret = enable_contaminant_detection(contaminant->chip, + contaminant_detect_maxq); + if (ret == -EIO) + return ret; + } + *cc_update_handled = false; } else if (contaminant->state == DETECTED) { if (status_check(cc_status, TCPC_CC_STATUS_TOGGLING, 0)) { logbuffer_log(chip->log, "Contaminant: Check if dry"); @@ -591,8 +648,10 @@ bool process_contaminant_alert(struct max77759_contaminant *contaminant, bool de update_contaminant_state(contaminant, state); if (state == DETECTED) { - enable_dry_detection(contaminant); - return true; + ret = enable_dry_detection(contaminant); + if (ret == -EIO) + return ret; + *cc_update_handled = true; } /* @@ -600,59 +659,65 @@ bool process_contaminant_alert(struct max77759_contaminant *contaminant, bool de * auto_ultra_low_power_mode as well. */ disable_auto_ultra_low_power_mode(chip, false); - enable_contaminant_detection(contaminant->chip, contaminant_detect_maxq); - return true; + ret = enable_contaminant_detection(contaminant->chip, + contaminant_detect_maxq); + if (ret == -EIO) + return ret; + *cc_update_handled = true; } /* TCPM does not manage ports in dry detection phase. */ - return true; + *cc_update_handled = true; } - return false; + *cc_update_handled = false; + return 0; } EXPORT_SYMBOL_GPL(process_contaminant_alert); -void disable_contaminant_detection(struct max77759_plat *chip) +int disable_contaminant_detection(struct max77759_plat *chip) { struct regmap *regmap = chip->data.regmap; struct max77759_contaminant *contaminant = chip->contaminant; int ret; if (!contaminant) - return; + return 0; ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, 0); if (ret < 0) - return; + return -EIO; ret = max77759_write8(regmap, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP | (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT)); if (ret < 0) - return; + return -EIO; ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, LOW_POWER_MODE_DISABLE); if (ret < 0) - return; + return -EIO; ret = max77759_update_bits8(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_EN_LK4CONN_ALRT, TCPC_TCPC_CTRL_EN_LK4CONN_ALRT); if (ret < 0) - return; + return -EIO; ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL1, CCCONNDRY, 0); if (ret < 0) - return; + return -EIO; ret = max77759_write8(regmap, TCPC_COMMAND, TCPC_CMD_LOOK4CONNECTION); if (ret < 0) - return; + return -EIO; /* Reset state before disabling detection */ if (contaminant->state != NOT_DETECTED && contaminant->state != SINK) contaminant->state = NOT_DETECTED; logbuffer_log(chip->log, "Contaminant: Contaminant detection disabled"); + + return 0; } EXPORT_SYMBOL_GPL(disable_contaminant_detection); @@ -677,43 +742,47 @@ int enable_contaminant_detection(struct max77759_plat *chip, bool maxq) CCWTRSEL_1V << CCWTRSEL_SHIFT | WTRCYCLE_4_8_S << WTRCYCLE_SHIFT); if (ret < 0) - return ret; + return -EIO; /* Contaminant detection mode: contaminant detection */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL1, CCCONNDRY, 0); if (ret < 0) - return ret; + return -EIO; ret = max77759_read8(regmap, TCPC_VENDOR_CC_CTRL2, &vcc2); if (ret < 0) - return ret; + return -EIO; if (!contaminant->auto_ultra_low_power_mode_disabled) { /* tunable: Periodic contaminant detection */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, AUTO_ULTRA_LOW_POWER_MODE); if (ret < 0) - return ret; + return -EIO; } ret = max77759_read8(regmap, TCPC_VENDOR_CC_CTRL2, &vcc2); if (ret < 0) - return ret; + return -EIO; /* Mask flash adc interrupt */ ret = max77759_update_bits8(regmap, TCPC_VENDOR_ALERT_MASK2, MSK_FLASH_ADCINT, 0); if (ret < 0) - return ret; + return -EIO; /* Disable Auto disacharge before enabling toggling */ ret = max77759_read8(regmap, TCPC_POWER_CTRL, &pwr_ctrl); + if (ret < 0) + return -EIO; logbuffer_log(chip->log, "TCPC_POWER_CTRL:0x%x ret:%d", pwr_ctrl, ret); if (pwr_ctrl & TCPC_POWER_CTRL_AUTO_DISCHARGE) { logbuffer_log(chip->log, "TCPC_POWER_CTRL_AUTO_DISCHARGE not cleared"); ret = regmap_update_bits(regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_AUTO_DISCHARGE, 0); - if (ret < 0) + if (ret < 0) { logbuffer_log(chip->log, "[%s]: Disabling auto discharge failed", __func__); + return -EIO; + } } ret = max77759_write8(regmap, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP | @@ -724,7 +793,7 @@ int enable_contaminant_detection(struct max77759_plat *chip, bool maxq) if (ret < 0) { logbuffer_log(chip->log, "[%s]: Enabling DRP failed ret:%d", __func__, ret); - return ret; + return -EIO; } /* Enable Look4Connection before sending the command */ @@ -733,12 +802,12 @@ int enable_contaminant_detection(struct max77759_plat *chip, bool maxq) if (ret < 0) { logbuffer_log(chip->log, "[%s]: Enabling looking for connection failed ret:%d", __func__, ret); - return ret; + return -EIO; } ret = max77759_write8(regmap, TCPC_COMMAND, TCPC_CMD_LOOK4CONNECTION); if (ret < 0) - return ret; + return -EIO; logbuffer_log(chip->log, "Contaminant: Contaminant detection enabled"); diff --git a/drivers/usb/typec/tcpm/google/tcpci_max77759.c b/drivers/usb/typec/tcpm/google/tcpci_max77759.c index f70087c1669d..a06ab515308b 100644 --- a/drivers/usb/typec/tcpm/google/tcpci_max77759.c +++ b/drivers/usb/typec/tcpm/google/tcpci_max77759.c @@ -51,6 +51,7 @@ #define TCPC_RECEIVE_BUFFER_LEN 32 #define PD_ACTIVITY_TIMEOUT_MS 10000 +#define IO_ERROR_RETRY_MS 3000 #define VSAFE0V_DEBOUNCE_MS 15 #define VBUS_RAMPUP_TIMEOUT_MS 250 #define VBUS_RAMPUP_MAX_RETRY 8 @@ -620,7 +621,7 @@ static void max77759_init_regs(struct regmap *regmap, struct logbuffer *log) logbuffer_log(log, "TCPC_VENDOR_VCON_CTRL: update vcnilim to 300mA failed"); } -static void process_rx(struct max77759_plat *chip, u16 status) +static int process_rx(struct max77759_plat *chip, u16 status) { struct pd_message msg; u8 count, frame_type, rx_buf[TCPC_RECEIVE_BUFFER_LEN]; @@ -639,17 +640,18 @@ static void process_rx(struct max77759_plat *chip, u16 status) logbuffer_log(log, "%d", __LINE__); if (ret < 0) { dev_err(chip->dev, "TCPC_RX_BYTE_CNT read failed ret:%d", ret); - return; + return -EIO; } count = rx_buf[TCPC_RECEIVE_BUFFER_COUNT_OFFSET]; frame_type = rx_buf[TCPC_RECEIVE_BUFFER_FRAME_TYPE_OFFSET]; if (count == 0 || frame_type != TCPC_RX_BUF_FRAME_TYPE_SOP) { - max77759_write16(chip->data.regmap, TCPC_ALERT, TCPC_ALERT_RX_STATUS); + ret = max77759_write16(chip->data.regmap, TCPC_ALERT, TCPC_ALERT_RX_STATUS); dev_err(chip->dev, "%s", count == 0 ? "error: count is 0" : "error frame_type is not SOP"); - return; + if (ret < 0) + return -EIO; } /* @@ -658,7 +660,7 @@ static void process_rx(struct max77759_plat *chip, u16 status) */ if (count > sizeof(struct pd_message) + 1 || count + 1 > TCPC_RECEIVE_BUFFER_LEN) { dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d", count); - return; + return 0; } /* @@ -670,7 +672,7 @@ static void process_rx(struct max77759_plat *chip, u16 status) logbuffer_log(log, "%d", __LINE__); if (ret < 0) { dev_err(chip->dev, "Error: TCPC_RX_BYTE_CNT read failed: %d", ret); - return; + return -EIO; } rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET; @@ -690,7 +692,7 @@ static void process_rx(struct max77759_plat *chip, u16 status) TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF : TCPC_ALERT_RX_STATUS); if (ret < 0) - return; + return -EIO; logbuffer_log(log, "rx clear"); pd_type = pd_header_type_le(msg.header); @@ -700,10 +702,11 @@ static void process_rx(struct max77759_plat *chip, u16 status) ret = max77759_write16(chip->data.regmap, TCPC_VBUS_SINK_DISCONNECT_THRESH, 0); /* TODO: tcpci->pr_swap = true; */ if (ret < 0) - return; + return -EIO; } tcpm_pd_receive(chip->port, &msg); + return 0; } static void enable_dp_pulse(struct max77759_plat *chip) @@ -1210,8 +1213,9 @@ static void max77759_cache_cc(struct max77759_plat *chip) chip->cc2 = cc2; } -static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, - struct logbuffer *log) +/* hold irq_status_lock before calling */ +static irqreturn_t _max77759_irq_locked(struct max77759_plat *chip, u16 status, + struct logbuffer *log) { u16 vendor_status = 0, vendor_status2 = 0, raw; struct tcpci *tcpci = chip->tcpci; @@ -1220,6 +1224,8 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, ~(TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF) : status & ~TCPC_ALERT_RX_STATUS; u8 reg_status; + bool contaminant_cc_update_handled = false; + bool invoke_tcpm_for_cc_update = false; pm_wakeup_event(chip->dev, PD_ACTIVITY_TIMEOUT_MS); logbuffer_log(log, "TCPC_ALERT status: %#x", status); @@ -1230,7 +1236,7 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, if (status & ~TCPC_ALERT_RX_STATUS) { ret = max77759_write16(tcpci->regmap, TCPC_ALERT, mask); if (ret < 0) - return ret; + goto reschedule; } if (status & TCPC_ALERT_RX_BUF_OVF && !(status & @@ -1240,17 +1246,17 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, (TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF)); if (ret < 0) - return ret; + goto reschedule; } if (status & TCPC_ALERT_EXTND) { ret = max77759_read8(tcpci->regmap, TCPC_ALERT_EXTENDED, ®_status); if (ret < 0) - return ret; + goto reschedule; ret = max77759_write8(tcpci->regmap, TCPC_ALERT_EXTENDED, reg_status); if (ret < 0) - return ret; + goto reschedule; if (reg_status & TCPC_SINK_FAST_ROLE_SWAP) { logbuffer_log(log, "FRS Signal"); @@ -1261,7 +1267,9 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, if (status & TCPC_ALERT_RX_STATUS) { logbuffer_log(log, "Enter process rx"); - process_rx(chip, status); + ret = process_rx(chip, status); + if (ret == -EIO) + goto reschedule; } if (status & TCPC_ALERT_TX_DISCARDED) @@ -1272,18 +1280,18 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, ret = max77759_write8(tcpci->regmap, TCPC_VENDOR_ALERT_MASK , 0x0); if (ret < 0) - return ret; + goto reschedule; ret = max77759_write8(tcpci->regmap, TCPC_VENDOR_ALERT_MASK2, 0x0); if (ret < 0) - return ret; + goto reschedule; /* Clear VENDOR_ALERT*/ ret = max77759_read16(tcpci->regmap, TCPC_VENDOR_ALERT, &vendor_status); if (ret < 0) - return ret; + goto reschedule; logbuffer_log(log, "TCPC_VENDOR_ALERT 0x%x", vendor_status); process_bc12_alert(chip->bc12, vendor_status); @@ -1292,12 +1300,12 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, ret = max77759_read16(tcpci->regmap, TCPC_VENDOR_ALERT2, &vendor_status2); if (ret < 0) - return ret; + goto reschedule; logbuffer_log(log, "TCPC_VENDOR_ALERT2 0x%x", vendor_status2); ret = max77759_write16(tcpci->regmap, TCPC_VENDOR_ALERT2, vendor_status2); if (ret < 0) - return ret; + goto reschedule; } if (status & TCPC_ALERT_VBUS_DISCNCT) { @@ -1309,6 +1317,8 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, ret = max77759_write8(tcpci->regmap, TCPC_VENDOR_USBSW_CTRL, USBSW_CONNECT); logbuffer_log(chip->log, "Forcing on dp switches %s", ret < 0 ? "fail" : "success"); + if (ret < 0) + goto reschedule; } } @@ -1318,8 +1328,30 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, * contaminant detection. */ mutex_lock(&chip->rc_lock); - if (!chip->contaminant_detection || !tcpm_is_toggling(tcpci->port) || - !process_contaminant_alert(chip->contaminant, false, true)) { + if (chip->contaminant_detection && tcpm_is_toggling(tcpci->port)) { + ret = process_contaminant_alert(chip->contaminant, false, true, + &contaminant_cc_update_handled); + if (ret < 0) + goto reschedule; + /* + * Invoke TCPM when CC update not related to contaminant + * detection. + */ + invoke_tcpm_for_cc_update = !contaminant_cc_update_handled; + /* + * CC status change handled by contaminant algorithm. + * Handle floating cable if detected. + */ + if (contaminant_cc_update_handled) { + logbuffer_log(log, "CC update: Contaminant algorithm responded"); + if (is_floating_cable_or_sink_detected(chip)) + floating_cable_sink_detected_handler_locked(chip); + } + } else { + invoke_tcpm_for_cc_update = true; + } + + if (invoke_tcpm_for_cc_update) { tcpm_cc_change(tcpci->port); max77759_cache_cc(chip); /* TCPM has detected valid CC terminations */ @@ -1330,7 +1362,7 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, * when contaminant detection is enabled. */ if (chip->contaminant_detection_userspace != - CONTAMINANT_DETECT_DISABLE) + CONTAMINANT_DETECT_DISABLE) disable_auto_ultra_low_power_mode(chip, false); } else { /* @@ -1347,10 +1379,6 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, */ floating_cable_sink_detected_handler_locked(chip); } - } else { - logbuffer_log(log, "CC update: Contaminant algorithm responded"); - if (is_floating_cable_or_sink_detected(chip)) - floating_cable_sink_detected_handler_locked(chip); } mutex_unlock(&chip->rc_lock); } @@ -1361,7 +1389,7 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, if (status & TCPC_ALERT_V_ALARM_LO) { ret = max77759_read16(tcpci->regmap, TCPC_VBUS_VOLTAGE_ALARM_LO_CFG, &raw); if (ret < 0) - return ret; + goto reschedule; logbuffer_log(log, "VBUS LOW ALARM triggered: thresh:%umv vbus:%umv", (raw & TCPC_VBUS_VOLTAGE_MASK) * TCPC_VBUS_VOLTAGE_LSB_MV, @@ -1376,7 +1404,7 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, if (status & TCPC_ALERT_V_ALARM_HI) { ret = max77759_read16(tcpci->regmap, TCPC_VBUS_VOLTAGE_ALARM_HI_CFG, &raw); if (ret < 0) - return ret; + goto reschedule; logbuffer_log(log, "VBUS HIGH ALARM triggered: thresh:%umv vbus:%umv", (raw & TCPC_VBUS_VOLTAGE_MASK) * TCPC_VBUS_VOLTAGE_LSB_MV, @@ -1395,7 +1423,7 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, TCPC_VBUS_SINK_DISCONNECT_THRESH, 0); if (ret < 0) - return ret; + goto reschedule; tcpm_pd_hard_reset(tcpci->port); max77759_init_regs(tcpci->regmap, log); @@ -1410,11 +1438,11 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, ret = max77759_write8(tcpci->regmap, TCPC_VENDOR_ALERT_MASK , 0xff); if (ret < 0) - return ret; + goto reschedule; ret = max77759_write8(tcpci->regmap, TCPC_VENDOR_ALERT_MASK2, 0xff); if (ret < 0) - return ret; + goto reschedule; } if (status & TCPC_ALERT_EXTENDED_STATUS) { @@ -1422,7 +1450,7 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, ret = max77759_read8(tcpci->regmap, TCPC_EXTENDED_STATUS, (u8 *)&raw); if (ret < 0) - return ret; + goto reschedule; vsafe0v = raw & TCPC_EXTENDED_STATUS_VSAFE0V; logbuffer_log(log, "VSAFE0V (runtime): %c -> %c", chip->vsafe0v ? 'Y' : 'N', @@ -1455,6 +1483,13 @@ static irqreturn_t _max77759_irq(struct max77759_plat *chip, u16 status, logbuffer_log(log, "TCPC_ALERT status done: %#x", status); return IRQ_HANDLED; +reschedule: + chip->irq_status = status; + logbuffer_log(log, "TCPC_ALERT IO error occurred. status: %#x", status); + kthread_mod_delayed_work(chip->wq, &chip->max77759_io_error_work, + msecs_to_jiffies(IO_ERROR_RETRY_MS)); + pm_wakeup_event(chip->dev, PD_ACTIVITY_TIMEOUT_MS + IO_ERROR_RETRY_MS); + return IRQ_HANDLED; } static irqreturn_t max77759_irq(int irq, void *dev_id) @@ -1471,8 +1506,9 @@ static irqreturn_t max77759_irq(int irq, void *dev_id) ret = max77759_read16(chip->tcpci->regmap, TCPC_ALERT, &status); if (ret < 0) return ret; + mutex_lock(&chip->irq_status_lock); while (status) { - irq_return = _max77759_irq(chip, status, chip->log); + irq_return = _max77759_irq_locked(chip, status, chip->log); /* Do not return if the ALERT is already set. */ logbuffer_log(chip->log, "TCPC_ALERT read alert status"); ret = max77759_read16(chip->tcpci->regmap, TCPC_ALERT, &status); @@ -1481,6 +1517,7 @@ static irqreturn_t max77759_irq(int irq, void *dev_id) logbuffer_log(chip->log, "TCPC_ALERT status pending: %#x", status); } + mutex_unlock(&chip->irq_status_lock); return irq_return; } @@ -1498,6 +1535,18 @@ static irqreturn_t max77759_isr(int irq, void *dev_id) return IRQ_WAKE_THREAD; } +static void max77759_io_error_work(struct kthread_work *work) +{ + struct max77759_plat *chip = + container_of(container_of(work, struct kthread_delayed_work, work), + struct max77759_plat, max77759_io_error_work); + pm_wakeup_event(chip->dev, PD_ACTIVITY_TIMEOUT_MS); + mutex_lock(&chip->irq_status_lock); + logbuffer_log(chip->log, "IO error retry. status: %#x", chip->irq_status); + _max77759_irq_locked(chip, chip->irq_status, chip->log); + mutex_unlock(&chip->irq_status_lock); +} + static int max77759_init_alert(struct max77759_plat *chip, struct i2c_client *client) { @@ -1631,17 +1680,24 @@ unlock: } static void max77759_check_contaminant(void *unused, struct tcpci *tcpci, struct tcpci_data *tdata, - int *ret) + int *restart_toggling) { struct max77759_plat *chip = tdata_to_max77759(tdata); + bool contaminant_cc_status_handled = false; + int ret = 0; logbuffer_log(chip->log, "%s: debounce path", __func__); mutex_lock(&chip->rc_lock); if (chip->contaminant_detection) { - process_contaminant_alert(chip->contaminant, true, false); - *ret = CONTAMINANT_HANDLES_TOGGLING; + /* + * contaminant_cc_status_handled unused as contaminant detection + * is expected to restart toggling as needed unless EIO. + */ + ret = process_contaminant_alert(chip->contaminant, true, false, + &contaminant_cc_status_handled); + *restart_toggling = ret < 0 ? TCPM_RESTART_TOGGLING : CONTAMINANT_HANDLES_TOGGLING; } else { - *ret = TCPM_RESTART_TOGGLING; + *restart_toggling = TCPM_RESTART_TOGGLING; } mutex_unlock(&chip->rc_lock); @@ -2374,6 +2430,7 @@ static int max77759_probe(struct i2c_client *client, mutex_init(&chip->icl_proto_el_lock); mutex_init(&chip->data_path_lock); mutex_init(&chip->rc_lock); + mutex_init(&chip->irq_status_lock); spin_lock_init(&g_caps_lock); chip->first_toggle = true; @@ -2507,6 +2564,7 @@ static int max77759_probe(struct i2c_client *client, kthread_init_delayed_work(&chip->icl_work, icl_work_item); kthread_init_delayed_work(&chip->enable_vbus_work, enable_vbus_work); kthread_init_delayed_work(&chip->vsafe0v_work, vsafe0v_debounce_work); + kthread_init_delayed_work(&chip->max77759_io_error_work, max77759_io_error_work); /* * b/218797880 Some OVP chips are restricted to quick Vin ramp-up time which means that if diff --git a/drivers/usb/typec/tcpm/google/tcpci_max77759.h b/drivers/usb/typec/tcpm/google/tcpci_max77759.h index 05ca1c5e905e..8fc48bdadc7b 100644 --- a/drivers/usb/typec/tcpm/google/tcpci_max77759.h +++ b/drivers/usb/typec/tcpm/google/tcpci_max77759.h @@ -22,6 +22,7 @@ struct gvotable_election; struct logbuffer; struct max77759_contaminant; struct tcpci_data; +struct max77759_io_error; struct max77759_plat { struct tcpci_data data; @@ -140,6 +141,12 @@ struct max77759_plat { /* Reflects whether BC1.2 is still running */ bool bc12_running; + /* To handle io error - Last cached IRQ status*/ + u16 irq_status; + struct kthread_delayed_work max77759_io_error_work; + /* Hold before calling _max77759_irq */ + struct mutex irq_status_lock; + /* EXT_BST_EN exposed as GPIO */ #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; @@ -175,10 +182,10 @@ int __attribute__((weak)) maxq_query_contaminant(u8 cc1_raw, u8 cc2_raw, u8 sbu1 } struct max77759_contaminant *max77759_contaminant_init(struct max77759_plat *plat, bool enable); -bool process_contaminant_alert(struct max77759_contaminant *contaminant, bool debounce_path, - bool tcpm_toggling); +int process_contaminant_alert(struct max77759_contaminant *contaminant, bool debounce_path, + bool tcpm_toggling, bool *cc_status_handled); int enable_contaminant_detection(struct max77759_plat *chip, bool maxq); -void disable_contaminant_detection(struct max77759_plat *chip); +int disable_contaminant_detection(struct max77759_plat *chip); bool is_contaminant_detected(struct max77759_plat *chip); bool is_floating_cable_or_sink_detected(struct max77759_plat *chip); void disable_auto_ultra_low_power_mode(struct max77759_plat *chip, bool disable); |