]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
usb: typec: tcpm/tcpci_maxim: fix non-contaminant CC handling
authorAndré Draszik <andre.draszik@linaro.org>
Tue, 6 Aug 2024 16:25:24 +0000 (17:25 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 13 Aug 2024 08:31:12 +0000 (10:31 +0200)
tcpci_maxim currently never triggers the TCPM state machine when CC
status has not changed due to a contaminant but due to a real
connection event, i.e. a genuine plug event, meaning the system will
stay idle and not notify any subscribers.

The reason is that the initial state of the port is 'toggling', which
causes _max_tcpci_irq() to only drive the contamination part of the
TCPM state machine (via tcpm_port_clean()).

What should happen instead is that if no contamination was detected,
the TCPM should be notified of the CC change in this case.

To fix this, we update ...is_contaminant() to also allow its caller to
determine if more CC processing is required and then call into the TCPM
as required.

While at it, add a kernel-doc for max_contaminant_is_contaminant().

Note: the code has an issue where I2C errors during contaminant
detection also cause the TCPM state machine to not be updated. This
commit doesn't change this behaviour and should be addressed by
follow-up commit(s).

Signed-off-by: André Draszik <andre.draszik@linaro.org>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20240806-max33359-toggling-v2-1-a66ab37baafb@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/tcpm/maxim_contaminant.c
drivers/usb/typec/tcpm/tcpci_maxim.h
drivers/usb/typec/tcpm/tcpci_maxim_core.c

index 7bfec45e8f4f74618a06af10c28b1925e23053b6..22163d8f9eb07ee3ddfb94dfc0770e51a61666cc 100644 (file)
@@ -324,11 +324,14 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
        return 0;
 }
 
-bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce)
+bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce,
+                                   bool *cc_handled)
 {
        u8 cc_status, pwr_cntl;
        int ret;
 
+       *cc_handled = true;
+
        ret = max_tcpci_read8(chip, TCPC_CC_STATUS, &cc_status);
        if (ret < 0)
                return false;
@@ -370,7 +373,6 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect
                                return true;
                        }
                }
-               return false;
        } else if (chip->contaminant_state == DETECTED) {
                if (!(cc_status & TCPC_CC_STATUS_TOGGLING)) {
                        chip->contaminant_state = max_contaminant_detect_contaminant(chip);
@@ -381,6 +383,7 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect
                }
        }
 
+       *cc_handled = false;
        return false;
 }
 
index a41ca9e7ad0822e36de5b922a9b24f4132dbf1ce..76270d5c283880dc49b13cabe7d682f2c2bf15fe 100644 (file)
@@ -81,6 +81,20 @@ static inline int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg
        return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
 }
 
-bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce);
+/**
+ * max_contaminant_is_contaminant - Test if CC was toggled due to contaminant
+ *
+ * @chip: Handle to a struct max_tcpci_chip
+ * @disconnect_while_debounce: Whether the disconnect was detected when CC
+ *                            pins were debouncing
+ * @cc_handled: Returns whether or not update to CC status was handled here
+ *
+ * Determine if a contaminant was detected.
+ *
+ * Returns: true if a contaminant was detected, false otherwise. cc_handled
+ * is updated to reflect whether or not further CC handling is required.
+ */
+bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce,
+                                   bool *cc_handled);
 
 #endif  // TCPCI_MAXIM_H_
index 1584ab2983beecc989d7016bb25ac72fd58cd59e..fd1b80593367641a6f997da2fb97a2b7238f6982 100644 (file)
@@ -357,12 +357,14 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
                tcpm_vbus_change(chip->port);
 
        if (status & TCPC_ALERT_CC_STATUS) {
+               bool cc_handled = false;
+
                if (chip->contaminant_state == DETECTED || tcpm_port_is_toggling(chip->port)) {
-                       if (!max_contaminant_is_contaminant(chip, false))
+                       if (!max_contaminant_is_contaminant(chip, false, &cc_handled))
                                tcpm_port_clean(chip->port);
-               } else {
-                       tcpm_cc_change(chip->port);
                }
+               if (!cc_handled)
+                       tcpm_cc_change(chip->port);
        }
 
        if (status & TCPC_ALERT_POWER_STATUS)
@@ -455,8 +457,9 @@ static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data)
 static void max_tcpci_check_contaminant(struct tcpci *tcpci, struct tcpci_data *tdata)
 {
        struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
+       bool cc_handled;
 
-       if (!max_contaminant_is_contaminant(chip, true))
+       if (!max_contaminant_is_contaminant(chip, true, &cc_handled))
                tcpm_port_clean(chip->port);
 }