usb: typec: tcpci: add attempt_vconn_swap_discovery callback
authorRD Babiera <rdbabiera@google.com>
Mon, 8 Jan 2024 19:16:20 +0000 (19:16 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 28 Jan 2024 01:38:25 +0000 (17:38 -0800)
Add attempt_vconn_swap_discovery callback to determine whether the TCPM
should perform a Vconn swap following Discover Identity on SOP. The tcpci
will return false unless chip level drivers implement the callback.

Maxim based TCPCs will return true unless the last connection resulted in
a Vconn Over Current Fault, which may be the result of the Vconn swap. In
addition to the port resetting, the TCPCI will veto the next Vconn swap
from occurring.

Signed-off-by: RD Babiera <rdbabiera@google.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20240108191620.987785-21-rdbabiera@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/tcpm/tcpci.c
drivers/usb/typec/tcpm/tcpci_maxim.h
drivers/usb/typec/tcpm/tcpci_maxim_core.c
include/linux/usb/tcpci.h
include/linux/usb/tcpm.h

index 8ea4ed159a13448dcce15e62d8dae5aaaff0b0ee..40c7b6224c74b96613222625a31c18cbd4cf51c9 100644 (file)
@@ -594,6 +594,16 @@ static bool tcpci_cable_comm_capable(struct tcpc_dev *tcpc)
        return tcpci->data->cable_comm_capable;
 }
 
+static bool tcpci_attempt_vconn_swap_discovery(struct tcpc_dev *tcpc)
+{
+       struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+
+       if (tcpci->data->attempt_vconn_swap_discovery)
+               return tcpci->data->attempt_vconn_swap_discovery(tcpci, tcpci->data);
+
+       return false;
+}
+
 static int tcpci_init(struct tcpc_dev *tcpc)
 {
        struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -804,6 +814,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
        tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus;
        tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable;
        tcpci->tcpc.cable_comm_capable = tcpci_cable_comm_capable;
+       tcpci->tcpc.attempt_vconn_swap_discovery = tcpci_attempt_vconn_swap_discovery;
 
        if (tcpci->data->check_contaminant)
                tcpci->tcpc.check_contaminant = tcpci_check_contaminant;
index 2c1c4d161b0dcbcdaadb7616b59ef94e281b6ecf..78ff3b73ee7e3c623386ecd35264454ab857c15a 100644 (file)
@@ -62,6 +62,7 @@ struct max_tcpci_chip {
        struct i2c_client *client;
        struct tcpm_port *port;
        enum contamiant_state contaminant_state;
+       bool veto_vconn_swap;
 };
 
 static inline int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val)
index f9f838df43f73d599f792f3a9cdaf60b850de918..eec3bcec119c1a8c207f0e0b5d9d128625758d70 100644 (file)
@@ -323,8 +323,10 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
                if (ret < 0)
                        return ret;
 
-               if (reg_status & TCPC_FAULT_STATUS_VCONN_OC)
+               if (reg_status & TCPC_FAULT_STATUS_VCONN_OC) {
+                       chip->veto_vconn_swap = true;
                        tcpm_port_error_recovery(chip->port);
+               }
        }
 
        if (status & TCPC_ALERT_EXTND) {
@@ -458,6 +460,18 @@ static void max_tcpci_check_contaminant(struct tcpci *tcpci, struct tcpci_data *
                tcpm_port_clean(chip->port);
 }
 
+static bool max_tcpci_attempt_vconn_swap_discovery(struct tcpci *tcpci, struct tcpci_data *tdata)
+{
+       struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
+
+       if (chip->veto_vconn_swap) {
+               chip->veto_vconn_swap = false;
+               return false;
+       }
+
+       return true;
+}
+
 static int max_tcpci_probe(struct i2c_client *client)
 {
        int ret;
@@ -493,6 +507,7 @@ static int max_tcpci_probe(struct i2c_client *client)
        chip->data.set_partner_usb_comm_capable = max_tcpci_set_partner_usb_comm_capable;
        chip->data.check_contaminant = max_tcpci_check_contaminant;
        chip->data.cable_comm_capable = true;
+       chip->data.attempt_vconn_swap_discovery = max_tcpci_attempt_vconn_swap_discovery;
 
        max_tcpci_init_regs(chip);
        chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
index 9ed6d62c9c5ff6b3616fac01ad901104e80658ef..47a86b8a4a507b3c1e5719f27bed37fe3d3b42c2 100644 (file)
@@ -201,6 +201,14 @@ struct tcpci;
  *             toggling state.
  * @cable_comm_capable
  *             optional; Set when TCPC can communicate with cable plugs over SOP'
+ * @attempt_vconn_swap_discovery:
+ *             Optional; The callback is called by the TCPM when the result of
+ *             a Discover Identity request indicates that the port partner is
+ *             a receptacle capable of modal operation. Chip level TCPCI drivers
+ *             can implement their own policy to determine if and when a Vconn
+ *             swap following Discover Identity on SOP' occurs.
+ *             Return true when the TCPM is allowed to request a Vconn swap
+ *             after Discovery Identity on SOP.
  */
 struct tcpci_data {
        struct regmap *regmap;
@@ -219,6 +227,7 @@ struct tcpci_data {
        void (*set_partner_usb_comm_capable)(struct tcpci *tcpci, struct tcpci_data *data,
                                             bool capable);
        void (*check_contaminant)(struct tcpci *tcpci, struct tcpci_data *data);
+       bool (*attempt_vconn_swap_discovery)(struct tcpci *tcpci, struct tcpci_data *data);
 };
 
 struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
index 41d1ac9c8bbfc77e03c2a52ab8ea635489593a76..6671427f7eebb451599443069f71ea803ce6bb9e 100644 (file)
@@ -122,6 +122,14 @@ enum tcpm_transmit_type {
  * @cable_comm_capable
  *             Optional; Returns whether cable communication over SOP' is supported
  *             by the tcpc
+ * @attempt_vconn_swap_discovery:
+ *             Optional; The callback is called by the TCPM when the result of
+ *             a Discover Identity request indicates that the port partner is
+ *             a receptacle capable of modal operation. Chip level TCPCI drivers
+ *             can implement their own policy to determine if and when a Vconn
+ *             swap following Discover Identity on SOP' occurs.
+ *             Return true when the TCPM is allowed to request a Vconn swap
+ *             after Discovery Identity on SOP.
  */
 struct tcpc_dev {
        struct fwnode_handle *fwnode;
@@ -158,6 +166,7 @@ struct tcpc_dev {
        void (*set_partner_usb_comm_capable)(struct tcpc_dev *dev, bool enable);
        void (*check_contaminant)(struct tcpc_dev *dev);
        bool (*cable_comm_capable)(struct tcpc_dev *dev);
+       bool (*attempt_vconn_swap_discovery)(struct tcpc_dev *dev);
 };
 
 struct tcpm_port;