On Thu, Dec 14, 2023 at 11:08:51PM +0000, RD Babiera wrote: > 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@xxxxxxxxxx> Reviewed-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > --- > drivers/usb/typec/tcpm/tcpci.c | 11 +++++++++++ > drivers/usb/typec/tcpm/tcpci_maxim.h | 1 + > drivers/usb/typec/tcpm/tcpci_maxim_core.c | 17 ++++++++++++++++- > include/linux/usb/tcpci.h | 9 +++++++++ > include/linux/usb/tcpm.h | 9 +++++++++ > 5 files changed, 46 insertions(+), 1 deletion(-) > > diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c > index 8ea4ed159a13..40c7b6224c74 100644 > --- a/drivers/usb/typec/tcpm/tcpci.c > +++ b/drivers/usb/typec/tcpm/tcpci.c > @@ -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; > diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h > index 2c1c4d161b0d..78ff3b73ee7e 100644 > --- a/drivers/usb/typec/tcpm/tcpci_maxim.h > +++ b/drivers/usb/typec/tcpm/tcpci_maxim.h > @@ -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) > diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c > index f9f838df43f7..eec3bcec119c 100644 > --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c > +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c > @@ -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); > diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h > index 9ed6d62c9c5f..47a86b8a4a50 100644 > --- a/include/linux/usb/tcpci.h > +++ b/include/linux/usb/tcpci.h > @@ -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); > diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h > index 41d1ac9c8bbf..6671427f7eeb 100644 > --- a/include/linux/usb/tcpm.h > +++ b/include/linux/usb/tcpm.h > @@ -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; > -- > 2.43.0.472.g3155946c3a-goog -- heikki