2020-10-08 06:20:10

by Badhri Jagan Sridharan

[permalink] [raw]
Subject: [PATCH v10 00/15] TCPM support for FRS and AutoDischarge Disconnect

Hi,

Made two changes:

1. Added "additionalProperties: false" as suggested by Rob Herring in
https://lore.kernel.org/linux-usb/20201005144618.GA154206@bogus/

2. Removed FRS dts binding constants to address Rob Herring's comment in
https://lore.kernel.org/linux-usb/20201006182940.GA2574941@bogus/

Thanks,
Badhri

Badhri Jagan Sridharan (15):
usb: typec: tcpci: Add a getter method to retrieve tcpm_port reference
usb: typec: tcpci: Add set_vbus tcpci callback
dt-bindings: usb: Maxim type-c controller device tree binding document
usb: typec: tcpci_maxim: Chip level TCPC driver
dt-bindings: connector: Add property to set initial current cap for
FRS
usb: typec: tcpm: Add support for Sink Fast Role SWAP(FRS)
usb: typec: tcpci: Implement callbacks for FRS
usb: typec: tcpci_maxim: Add support for Sink FRS
usb: typec: tcpm: frs sourcing vbus callback
usb: typec: tcpci: frs sourcing vbus callback
usb: typec: tcpci_max77759: Fix vbus stuck on upon diconnecting sink
usb: typec: tcpm: Parse frs type-c current from device tree
usb: typec: tcpm: Implement enabling Auto Discharge disconnect support
usb: typec: tcpci: Implement Auto discharge disconnect callbacks
usb: typec: tcpci_maxim: Enable auto discharge disconnect

.../bindings/connector/usb-connector.yaml | 26 +
.../devicetree/bindings/usb/maxim,tcpci.yaml | 70 +++
drivers/usb/typec/tcpm/Kconfig | 6 +
drivers/usb/typec/tcpm/Makefile | 15 +-
drivers/usb/typec/tcpm/tcpci.c | 102 +++-
drivers/usb/typec/tcpm/tcpci.h | 30 +-
drivers/usb/typec/tcpm/tcpci_maxim.c | 504 ++++++++++++++++++
drivers/usb/typec/tcpm/tcpm.c | 299 ++++++++++-
include/linux/usb/pd.h | 19 +-
include/linux/usb/tcpm.h | 27 +-
include/linux/usb/typec.h | 12 +
11 files changed, 1085 insertions(+), 25 deletions(-)
create mode 100644 Documentation/devicetree/bindings/usb/maxim,tcpci.yaml
create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.c

--
2.28.0.806.g8561365e88-goog


2020-10-08 06:21:17

by Badhri Jagan Sridharan

[permalink] [raw]
Subject: [PATCH v10 15/15] usb: typec: tcpci_maxim: Enable auto discharge disconnect

Enable auto discharge disconnect for Maxim TCPC.

Signed-off-by: Badhri Jagan Sridharan <[email protected]>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.

Changes since v6:
- Rebase on usb-next.

Changes since v7:
- Heikki's suggestion:
Moved the actual write of TCPC_VBUS_SINK_DISCONNECT_THRES
register to tcpci code.

Changes since v8:
- Moved the logic to program the default values of
TCPC_VBUS_SINK_DISCONNECT_THRESH into the tcpci code.

Changes since v9:
- none.
---
drivers/usb/typec/tcpm/tcpci_maxim.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
index 43dcad95e897..55dea33387ec 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
@@ -441,6 +441,7 @@ static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id
chip->data.TX_BUF_BYTE_x_hidden = true;
chip->data.init = tcpci_init;
chip->data.frs_sourcing_vbus = max_tcpci_frs_sourcing_vbus;
+ chip->data.auto_discharge_disconnect = true;

max_tcpci_init_regs(chip);
chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
--
2.28.0.806.g8561365e88-goog

2020-10-08 06:21:30

by Badhri Jagan Sridharan

[permalink] [raw]
Subject: [PATCH v10 13/15] usb: typec: tcpm: Implement enabling Auto Discharge disconnect support

TCPCI spec allows TCPC hardware to autonomously discharge the vbus
capacitance upon disconnect. The expectation is that the TCPM enables
AutoDischargeDisconnect while entering SNK/SRC_ATTACHED states. Hardware
then automously discharges vbus when the vbus falls below a certain
threshold i.e. VBUS_SINK_DISCONNECT_THRESHOLD.

Apart from enabling the vbus discharge circuit, AutoDischargeDisconnect
is also used a flag to move TCPCI based TCPC implementations into
Attached.Snk/Attached.Src state as mentioned in
Figure 4-15. TCPC State Diagram before a Connection of the
USB Type-C Port Controller Interface Specification.
In such TCPC implementations, setting AutoDischargeDisconnect would
prevent TCPC into entering "Connection_Invalid" state as well.

Signed-off-by: Badhri Jagan Sridharan <[email protected]>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.

Changes since v6:
- Fixed incorrect data_role error that I introduced by mistake in
the previous version.

Changes since v7:
- Rebase on usb-next

Changes since v8:
- Removing the call to tcpm_set_auto_vbus_discharge_threshold
in the source path.

- Changes since v9:
- None
---
drivers/usb/typec/tcpm/tcpm.c | 60 ++++++++++++++++++++++++++++++++---
include/linux/usb/tcpm.h | 15 +++++++++
2 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index d5a3e2b3bea2..51a14d282109 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1701,6 +1701,24 @@ static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload,
}
}

+static int tcpm_set_auto_vbus_discharge_threshold(struct tcpm_port *port,
+ enum typec_pwr_opmode mode, bool pps_active,
+ u32 requested_vbus_voltage)
+{
+ int ret;
+
+ if (!port->tcpc->set_auto_vbus_discharge_threshold)
+ return 0;
+
+ ret = port->tcpc->set_auto_vbus_discharge_threshold(port->tcpc, mode, pps_active,
+ requested_vbus_voltage);
+ tcpm_log_force(port,
+ "set_auto_vbus_discharge_threshold mode:%d pps_active:%c vbus:%u ret:%d",
+ mode, pps_active ? 'y' : 'n', requested_vbus_voltage, ret);
+
+ return ret;
+}
+
static void tcpm_pd_data_request(struct tcpm_port *port,
const struct pd_message *msg)
{
@@ -1871,6 +1889,10 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
port->current_limit,
port->supply_voltage);
port->explicit_contract = true;
+ tcpm_set_auto_vbus_discharge_threshold(port,
+ TYPEC_PWR_MODE_PD,
+ port->pps_data.active,
+ port->supply_voltage);
tcpm_set_state(port, SNK_READY, 0);
} else {
/*
@@ -2785,8 +2807,12 @@ static int tcpm_src_attach(struct tcpm_port *port)
if (ret < 0)
return ret;

- ret = tcpm_set_roles(port, true, TYPEC_SOURCE,
- tcpm_data_role_for_source(port));
+ if (port->tcpc->enable_auto_vbus_discharge) {
+ ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true);
+ tcpm_log_force(port, "enable vbus discharge ret:%d", ret);
+ }
+
+ ret = tcpm_set_roles(port, true, TYPEC_SOURCE, tcpm_data_role_for_source(port));
if (ret < 0)
return ret;

@@ -2853,6 +2879,12 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port)

static void tcpm_reset_port(struct tcpm_port *port)
{
+ int ret;
+
+ if (port->tcpc->enable_auto_vbus_discharge) {
+ ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, false);
+ tcpm_log_force(port, "Disable vbus discharge ret:%d", ret);
+ }
tcpm_unregister_altmodes(port);
tcpm_typec_disconnect(port);
port->attached = false;
@@ -2917,8 +2949,13 @@ static int tcpm_snk_attach(struct tcpm_port *port)
if (ret < 0)
return ret;

- ret = tcpm_set_roles(port, true, TYPEC_SINK,
- tcpm_data_role_for_sink(port));
+ if (port->tcpc->enable_auto_vbus_discharge) {
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V);
+ ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true);
+ tcpm_log_force(port, "enable vbus discharge ret:%d", ret);
+ }
+
+ ret = tcpm_set_roles(port, true, TYPEC_SINK, tcpm_data_role_for_sink(port));
if (ret < 0)
return ret;

@@ -3502,6 +3539,8 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON);
break;
case SNK_HARD_RESET_SINK_OFF:
+ /* Do not discharge/disconnect during hard reseet */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0);
memset(&port->pps_data, 0, sizeof(port->pps_data));
tcpm_set_vconn(port, false);
if (port->pd_capable)
@@ -3544,6 +3583,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_charge(port, true);
}
tcpm_set_attached_state(port, true);
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V);
tcpm_set_state(port, SNK_STARTUP, 0);
break;

@@ -3645,6 +3685,10 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, PR_SWAP_SNK_SRC_SINK_OFF, 0);
break;
case PR_SWAP_SRC_SNK_TRANSITION_OFF:
+ /*
+ * Prevent vbus discharge circuit from turning on during PR_SWAP
+ * as this is not a disconnect.
+ */
tcpm_set_vbus(port, false);
port->explicit_contract = false;
/* allow time for Vbus discharge, must be < tSrcSwapStdby */
@@ -3673,9 +3717,17 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state_cond(port, SNK_UNATTACHED, PD_T_PS_SOURCE_ON);
break;
case PR_SWAP_SRC_SNK_SINK_ON:
+ /* Set the vbus disconnect threshold for implicit contract */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V);
tcpm_set_state(port, SNK_STARTUP, 0);
break;
case PR_SWAP_SNK_SRC_SINK_OFF:
+ /*
+ * Prevent vbus discharge circuit from turning on during PR_SWAP
+ * as this is not a disconnect.
+ */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB,
+ port->pps_data.active, 0);
tcpm_set_charge(port, false);
tcpm_set_state(port, hard_reset_state(port),
PD_T_PS_SOURCE_OFF);
diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h
index 7303f518ba49..e68aaa12886f 100644
--- a/include/linux/usb/tcpm.h
+++ b/include/linux/usb/tcpm.h
@@ -86,6 +86,18 @@ enum tcpm_transmit_type {
* @frs_sourcing_vbus:
* Optional; Called to notify that vbus is now being sourced.
* Low level drivers can perform chip specific operations, if any.
+ * @enable_auto_vbus_discharge:
+ * Optional; TCPCI spec based TCPC implementations can optionally
+ * support hardware to autonomously dischrge vbus upon disconnecting
+ * as sink or source. TCPM signals TCPC to enable the mechanism upon
+ * entering connected state and signals disabling upon disconnect.
+ * @set_auto_vbus_discharge_threshold:
+ * Mandatory when enable_auto_vbus_discharge is implemented. TCPM
+ * calls this function to allow lower levels drivers to program the
+ * vbus threshold voltage below which the vbus discharge circuit
+ * will be turned on. requested_vbus_voltage is set to 0 when vbus
+ * is going to disappear knowingly i.e. during PR_SWAP and
+ * HARD_RESET etc.
*/
struct tcpc_dev {
struct fwnode_handle *fwnode;
@@ -113,6 +125,9 @@ struct tcpc_dev {
int (*set_bist_data)(struct tcpc_dev *dev, bool on);
int (*enable_frs)(struct tcpc_dev *dev, bool enable);
void (*frs_sourcing_vbus)(struct tcpc_dev *dev);
+ int (*enable_auto_vbus_discharge)(struct tcpc_dev *dev, bool enable);
+ int (*set_auto_vbus_discharge_threshold)(struct tcpc_dev *dev, enum typec_pwr_opmode mode,
+ bool pps_active, u32 requested_vbus_voltage);
};

struct tcpm_port;
--
2.28.0.806.g8561365e88-goog

2020-10-08 07:18:33

by Badhri Jagan Sridharan

[permalink] [raw]
Subject: [PATCH v10 03/15] dt-bindings: usb: Maxim type-c controller device tree binding document

Add device tree binding document for Maxim TCPCI based Type-C chip driver

Signed-off-by: Badhri Jagan Sridharan <[email protected]>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.

Changes since v6:
- Migrated to yaml format.

Changes since v7:
- Rebase on usb-next

Changes since v8:
- Fix errors from make dt_binding_check as suggested by
Rob Herring.

Changes since v9:
- additionalProperties: false as suggested by Rob Herring.
---
.../devicetree/bindings/usb/maxim,tcpci.yaml | 70 +++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/maxim,tcpci.yaml

diff --git a/Documentation/devicetree/bindings/usb/maxim,tcpci.yaml b/Documentation/devicetree/bindings/usb/maxim,tcpci.yaml
new file mode 100644
index 000000000000..12e0ece1b515
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/maxim,tcpci.yaml
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/usb/maxim,tcpci.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Maxim TCPCI Type-C PD controller DT bindings
+
+maintainers:
+ - Badhri Jagan Sridharan <[email protected]>
+
+description: Maxim TCPCI Type-C PD controller
+
+properties:
+ compatible:
+ enum:
+ - maxim,tcpci
+
+ interrupts:
+ maxItems: 1
+
+ connector:
+ type: object
+ $ref: ../connector/usb-connector.yaml#
+ description:
+ Properties for usb c connector.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/usb/pd.h>
+ i2c0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ maxtcpc@25 {
+ compatible = "maxim,tcpc";
+ reg = <0x25>;
+ interrupt-parent = <&gpa8>;
+ interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+
+ connector {
+ compatible = "usb-c-connector";
+ label = "USB-C";
+ data-role = "dual";
+ power-role = "dual";
+ try-power-role = "sink";
+ self-powered;
+ op-sink-microwatt = <2600000>;
+ source-pdos = <PDO_FIXED(5000, 900,
+ PDO_FIXED_SUSPEND |
+ PDO_FIXED_USB_COMM |
+ PDO_FIXED_DATA_SWAP |
+ PDO_FIXED_DUAL_ROLE)>;
+ sink-pdos = <PDO_FIXED(5000, 3000,
+ PDO_FIXED_USB_COMM |
+ PDO_FIXED_DATA_SWAP |
+ PDO_FIXED_DUAL_ROLE)
+ PDO_FIXED(9000, 2000, 0)>;
+ };
+ };
+ };
+...
--
2.28.0.806.g8561365e88-goog

2020-10-08 07:46:46

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v10 00/15] TCPM support for FRS and AutoDischarge Disconnect

On Wed, Oct 07, 2020 at 11:15:41PM -0700, Badhri Jagan Sridharan wrote:
> Hi,
>
> Made two changes:
>
> 1. Added "additionalProperties: false" as suggested by Rob Herring in
> https://lore.kernel.org/linux-usb/20201005144618.GA154206@bogus/
>
> 2. Removed FRS dts binding constants to address Rob Herring's comment in
> https://lore.kernel.org/linux-usb/20201006182940.GA2574941@bogus/

That worked better. I've applied the patches that Heikki had reviewed
to my usb-testing branch now.

thanks,

greg k-h

2020-10-08 08:25:23

by Badhri Jagan Sridharan

[permalink] [raw]
Subject: [PATCH v10 10/15] usb: typec: tcpci: frs sourcing vbus callback

During FRS hardware autonomously starts to source vbus. Provide
callback to perform chip specific operations.

Signed-off-by: Badhri Jagan Sridharan <[email protected]>
---
v9 is the first version of this patch in the series. Added to fix
occasional bug of vbus turning back on when disconnecting the FRS accessory
after disconnect. No changes since v9.
---
drivers/usb/typec/tcpm/tcpci.c | 9 +++++++++
drivers/usb/typec/tcpm/tcpci.h | 4 ++++
2 files changed, 13 insertions(+)

diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index f9f0af64da5f..f91688e43991 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -284,6 +284,14 @@ static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable)
return ret;
}

+static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(dev);
+
+ if (tcpci->data->frs_sourcing_vbus)
+ tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data);
+}
+
static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -628,6 +636,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
tcpci->tcpc.set_bist_data = tcpci_set_bist_data;
tcpci->tcpc.enable_frs = tcpci_enable_frs;
+ tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus;

err = tcpci_parse_config(tcpci);
if (err < 0)
diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
index 5ef07a56d67a..b418fe11b527 100644
--- a/drivers/usb/typec/tcpm/tcpci.h
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -143,6 +143,9 @@
/*
* @TX_BUF_BYTE_x_hidden
* optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT.
+ * @frs_sourcing_vbus:
+ * Optional; Callback to perform chip specific operations when FRS
+ * is sourcing vbus.
*/
struct tcpci;
struct tcpci_data {
@@ -154,6 +157,7 @@ struct tcpci_data {
int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data,
enum typec_cc_status cc);
int (*set_vbus)(struct tcpci *tcpci, struct tcpci_data *data, bool source, bool sink);
+ void (*frs_sourcing_vbus)(struct tcpci *tcpci, struct tcpci_data *data);
};

struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
--
2.28.0.806.g8561365e88-goog

2020-10-08 08:25:23

by Badhri Jagan Sridharan

[permalink] [raw]
Subject: [PATCH v10 11/15] usb: typec: tcpci_max77759: Fix vbus stuck on upon diconnecting sink

Occasionally, POWER_STATUS.sourcing_vbus takes a while to clear after
writing to MAX_BUCK_BOOST_OP register. This causes vbus to turn back
on while disconnecting the sink. Overcome this issue by writing into
MAX_BUCK_BOOST_OP during frs while sourcing vbu, instead of always
into the register whenever POWER_STATUS.sourcing_vbus is set.

Signed-off-by: Badhri Jagan Sridharan <[email protected]>
---
v9 is the first version of this patch. Added to fix
occasional bug of vbus turning back on when disconnecting the FRS accessory
after disconnect. No changes since v9.
---
drivers/usb/typec/tcpm/tcpci_maxim.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
index 723d7dd38f75..43dcad95e897 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
@@ -238,23 +238,22 @@ static void process_power_status(struct max_tcpci_chip *chip)
if (ret < 0)
return;

- if (pwr_status == 0xff) {
+ if (pwr_status == 0xff)
max_tcpci_init_regs(chip);
- } else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS) {
+ else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS)
tcpm_sourcing_vbus(chip->port);
- /*
- * Alawys re-enable boost here.
- * In normal case, when say an headset is attached, TCPM would
- * have instructed to TCPC to enable boost, so the call is a
- * no-op.
- * But for Fast Role Swap case, Boost turns on autonomously without
- * AP intervention, but, needs AP to enable source mode explicitly
- * for AP to regain control.
- */
- max_tcpci_set_vbus(chip->tcpci, &chip->data, true, false);
- } else {
+ else
tcpm_vbus_change(chip->port);
- }
+}
+
+static void max_tcpci_frs_sourcing_vbus(struct tcpci *tcpci, struct tcpci_data *tdata)
+{
+ /*
+ * For Fast Role Swap case, Boost turns on autonomously without
+ * AP intervention, but, needs AP to enable source mode explicitly
+ * for AP to regain control.
+ */
+ max_tcpci_set_vbus(tcpci, tdata, true, false);
}

static void process_tx(struct max_tcpci_chip *chip, u16 status)
@@ -441,6 +440,7 @@ static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id
chip->data.start_drp_toggling = max_tcpci_start_toggling;
chip->data.TX_BUF_BYTE_x_hidden = true;
chip->data.init = tcpci_init;
+ chip->data.frs_sourcing_vbus = max_tcpci_frs_sourcing_vbus;

max_tcpci_init_regs(chip);
chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
--
2.28.0.806.g8561365e88-goog

2020-10-08 08:25:23

by Badhri Jagan Sridharan

[permalink] [raw]
Subject: [PATCH v10 08/15] usb: typec: tcpci_maxim: Add support for Sink FRS

Upon receiving ALERT_EXTENDED.TCPC_SINK_FAST_ROLE_SWAP signal
tcpm to start Sink fast role swap signal.

Inform when TCPM is sourcing vbus.

Signed-off-by: Badhri Jagan Sridharan <[email protected]>
Reviewed-by: Heikki Krogerus <[email protected]>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.

Changes since v6:
- rebase on usb-next
- Added Reviewed-by: Heikki

Changes since v7:
- Rebase on usb-next

Changes since v8:
- None.

Changes since v9:
- None.
---
drivers/usb/typec/tcpm/tcpci_maxim.c | 50 +++++++++++++++++++++++++---
1 file changed, 46 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
index 91337ddb4962..723d7dd38f75 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
@@ -106,13 +106,22 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
return;
}

+ ret = max_tcpci_write8(chip, TCPC_ALERT_EXTENDED, 0xff);
+ if (ret < 0) {
+ dev_err(chip->dev, "Unable to clear TCPC_ALERT_EXTENDED ret:%d\n", ret);
+ return;
+ }
+
alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_FAILED |
TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_RX_STATUS | TCPC_ALERT_CC_STATUS |
- TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS;
+ TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS |
+ /* Enable Extended alert for detecting Fast Role Swap Signal */
+ TCPC_ALERT_EXTND;

ret = max_tcpci_write16(chip, TCPC_ALERT_MASK, alert_mask);
if (ret < 0) {
- dev_err(chip->dev, "Error writing to TCPC_ALERT_MASK ret:%d\n", ret);
+ dev_err(chip->dev,
+ "Error enabling TCPC_ALERT: TCPC_ALERT_MASK write failed ret:%d\n", ret);
return;
}

@@ -122,6 +131,10 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
dev_err(chip->dev, "Error writing to TCPC_POWER_CTRL ret:%d\n", ret);
return;
}
+
+ ret = max_tcpci_write8(chip, TCPC_ALERT_EXTENDED_MASK, TCPC_SINK_FAST_ROLE_SWAP);
+ if (ret < 0)
+ return;
}

static void process_rx(struct max_tcpci_chip *chip, u16 status)
@@ -225,10 +238,23 @@ static void process_power_status(struct max_tcpci_chip *chip)
if (ret < 0)
return;

- if (pwr_status == 0xff)
+ if (pwr_status == 0xff) {
max_tcpci_init_regs(chip);
- else
+ } else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS) {
+ tcpm_sourcing_vbus(chip->port);
+ /*
+ * Alawys re-enable boost here.
+ * In normal case, when say an headset is attached, TCPM would
+ * have instructed to TCPC to enable boost, so the call is a
+ * no-op.
+ * But for Fast Role Swap case, Boost turns on autonomously without
+ * AP intervention, but, needs AP to enable source mode explicitly
+ * for AP to regain control.
+ */
+ max_tcpci_set_vbus(chip->tcpci, &chip->data, true, false);
+ } else {
tcpm_vbus_change(chip->port);
+ }
}

static void process_tx(struct max_tcpci_chip *chip, u16 status)
@@ -249,6 +275,7 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
{
u16 mask;
int ret;
+ u8 reg_status;

/*
* Clear alert status for everything except RX_STATUS, which shouldn't
@@ -274,6 +301,21 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
}
}

+ if (status & TCPC_ALERT_EXTND) {
+ ret = max_tcpci_read8(chip, TCPC_ALERT_EXTENDED, &reg_status);
+ if (ret < 0)
+ return ret;
+
+ ret = max_tcpci_write8(chip, TCPC_ALERT_EXTENDED, reg_status);
+ if (ret < 0)
+ return ret;
+
+ if (reg_status & TCPC_SINK_FAST_ROLE_SWAP) {
+ dev_info(chip->dev, "FRS Signal");
+ tcpm_sink_frs(chip->port);
+ }
+ }
+
if (status & TCPC_ALERT_RX_STATUS)
process_rx(chip, status);

--
2.28.0.806.g8561365e88-goog

2020-10-08 18:53:11

by Badhri Jagan Sridharan

[permalink] [raw]
Subject: Re: [PATCH v10 00/15] TCPM support for FRS and AutoDischarge Disconnect

Thanks Greg !

On Thu, Oct 8, 2020 at 12:45 AM Greg Kroah-Hartman
<[email protected]> wrote:
>
> On Wed, Oct 07, 2020 at 11:15:41PM -0700, Badhri Jagan Sridharan wrote:
> > Hi,
> >
> > Made two changes:
> >
> > 1. Added "additionalProperties: false" as suggested by Rob Herring in
> > https://lore.kernel.org/linux-usb/20201005144618.GA154206@bogus/
> >
> > 2. Removed FRS dts binding constants to address Rob Herring's comment in
> > https://lore.kernel.org/linux-usb/20201006182940.GA2574941@bogus/
>
> That worked better. I've applied the patches that Heikki had reviewed
> to my usb-testing branch now.
>
> thanks,
>
> greg k-h

2020-10-13 23:52:25

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v10 00/15] TCPM support for FRS and AutoDischarge Disconnect

On Thu, Oct 8, 2020 at 2:45 AM Greg Kroah-Hartman
<[email protected]> wrote:
>
> On Wed, Oct 07, 2020 at 11:15:41PM -0700, Badhri Jagan Sridharan wrote:
> > Hi,
> >
> > Made two changes:
> >
> > 1. Added "additionalProperties: false" as suggested by Rob Herring in
> > https://lore.kernel.org/linux-usb/20201005144618.GA154206@bogus/
> >
> > 2. Removed FRS dts binding constants to address Rob Herring's comment in
> > https://lore.kernel.org/linux-usb/20201006182940.GA2574941@bogus/

And didn't address my other comments...

> That worked better. I've applied the patches that Heikki had reviewed
> to my usb-testing branch now.

Why is the driver being applied without the binding? Bindings come
first. The binding and driver don't even agree on the compatible
string (maxim,tcpci vs. maxim,tcpc), neither of which are right.

The FRS bindings need to be sorted out too as we have multiple folks
proposing bindings for it. I wish someone would review all these TypeC
related bindings because I'm getting a continual stream of piecemeal
additions with no coordination and I don't have knowledge on TypeC nor
bandwidth to review it all.

Rob

2020-10-19 13:52:32

by Heikki Krogerus

[permalink] [raw]
Subject: Re: [PATCH v10 15/15] usb: typec: tcpci_maxim: Enable auto discharge disconnect

On Wed, Oct 07, 2020 at 11:15:56PM -0700, Badhri Jagan Sridharan wrote:
> Enable auto discharge disconnect for Maxim TCPC.
>
> Signed-off-by: Badhri Jagan Sridharan <[email protected]>

Reviewed-by: Heikki Krogerus <[email protected]>

> ---
> Changes since v1:
> - Changing patch version to v6 to fix version number confusion.
>
> Changes since v6:
> - Rebase on usb-next.
>
> Changes since v7:
> - Heikki's suggestion:
> Moved the actual write of TCPC_VBUS_SINK_DISCONNECT_THRES
> register to tcpci code.
>
> Changes since v8:
> - Moved the logic to program the default values of
> TCPC_VBUS_SINK_DISCONNECT_THRESH into the tcpci code.
>
> Changes since v9:
> - none.
> ---
> drivers/usb/typec/tcpm/tcpci_maxim.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
> index 43dcad95e897..55dea33387ec 100644
> --- a/drivers/usb/typec/tcpm/tcpci_maxim.c
> +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
> @@ -441,6 +441,7 @@ static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id
> chip->data.TX_BUF_BYTE_x_hidden = true;
> chip->data.init = tcpci_init;
> chip->data.frs_sourcing_vbus = max_tcpci_frs_sourcing_vbus;
> + chip->data.auto_discharge_disconnect = true;
>
> max_tcpci_init_regs(chip);
> chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
> --
> 2.28.0.806.g8561365e88-goog

--
heikki

2020-10-19 13:58:56

by Heikki Krogerus

[permalink] [raw]
Subject: Re: [PATCH v10 00/15] TCPM support for FRS and AutoDischarge Disconnect

On Wed, Oct 07, 2020 at 11:15:41PM -0700, Badhri Jagan Sridharan wrote:
> Hi,
>
> Made two changes:
>
> 1. Added "additionalProperties: false" as suggested by Rob Herring in
> https://lore.kernel.org/linux-usb/20201005144618.GA154206@bogus/
>
> 2. Removed FRS dts binding constants to address Rob Herring's comment in
> https://lore.kernel.org/linux-usb/20201006182940.GA2574941@bogus/

I skipped 12/15. I thought that we better wait for Rob's approval for
the device property.

> Thanks,
> Badhri
>
> Badhri Jagan Sridharan (15):
> usb: typec: tcpci: Add a getter method to retrieve tcpm_port reference
> usb: typec: tcpci: Add set_vbus tcpci callback
> dt-bindings: usb: Maxim type-c controller device tree binding document
> usb: typec: tcpci_maxim: Chip level TCPC driver
> dt-bindings: connector: Add property to set initial current cap for
> FRS
> usb: typec: tcpm: Add support for Sink Fast Role SWAP(FRS)
> usb: typec: tcpci: Implement callbacks for FRS
> usb: typec: tcpci_maxim: Add support for Sink FRS
> usb: typec: tcpm: frs sourcing vbus callback
> usb: typec: tcpci: frs sourcing vbus callback
> usb: typec: tcpci_max77759: Fix vbus stuck on upon diconnecting sink
> usb: typec: tcpm: Parse frs type-c current from device tree
> usb: typec: tcpm: Implement enabling Auto Discharge disconnect support
> usb: typec: tcpci: Implement Auto discharge disconnect callbacks
> usb: typec: tcpci_maxim: Enable auto discharge disconnect
>
> .../bindings/connector/usb-connector.yaml | 26 +
> .../devicetree/bindings/usb/maxim,tcpci.yaml | 70 +++
> drivers/usb/typec/tcpm/Kconfig | 6 +
> drivers/usb/typec/tcpm/Makefile | 15 +-
> drivers/usb/typec/tcpm/tcpci.c | 102 +++-
> drivers/usb/typec/tcpm/tcpci.h | 30 +-
> drivers/usb/typec/tcpm/tcpci_maxim.c | 504 ++++++++++++++++++
> drivers/usb/typec/tcpm/tcpm.c | 299 ++++++++++-
> include/linux/usb/pd.h | 19 +-
> include/linux/usb/tcpm.h | 27 +-
> include/linux/usb/typec.h | 12 +
> 11 files changed, 1085 insertions(+), 25 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/usb/maxim,tcpci.yaml
> create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.c

thanks,

--
heikki

2020-10-20 01:26:33

by Heikki Krogerus

[permalink] [raw]
Subject: Re: [PATCH v10 10/15] usb: typec: tcpci: frs sourcing vbus callback

On Wed, Oct 07, 2020 at 11:15:51PM -0700, Badhri Jagan Sridharan wrote:
> During FRS hardware autonomously starts to source vbus. Provide
> callback to perform chip specific operations.
>
> Signed-off-by: Badhri Jagan Sridharan <[email protected]>

Reviewed-by: Heikki Krogerus <[email protected]>

> ---
> v9 is the first version of this patch in the series. Added to fix
> occasional bug of vbus turning back on when disconnecting the FRS accessory
> after disconnect. No changes since v9.
> ---
> drivers/usb/typec/tcpm/tcpci.c | 9 +++++++++
> drivers/usb/typec/tcpm/tcpci.h | 4 ++++
> 2 files changed, 13 insertions(+)
>
> diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
> index f9f0af64da5f..f91688e43991 100644
> --- a/drivers/usb/typec/tcpm/tcpci.c
> +++ b/drivers/usb/typec/tcpm/tcpci.c
> @@ -284,6 +284,14 @@ static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable)
> return ret;
> }
>
> +static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev)
> +{
> + struct tcpci *tcpci = tcpc_to_tcpci(dev);
> +
> + if (tcpci->data->frs_sourcing_vbus)
> + tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data);
> +}
> +
> static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable)
> {
> struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
> @@ -628,6 +636,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
> tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
> tcpci->tcpc.set_bist_data = tcpci_set_bist_data;
> tcpci->tcpc.enable_frs = tcpci_enable_frs;
> + tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus;
>
> err = tcpci_parse_config(tcpci);
> if (err < 0)
> diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
> index 5ef07a56d67a..b418fe11b527 100644
> --- a/drivers/usb/typec/tcpm/tcpci.h
> +++ b/drivers/usb/typec/tcpm/tcpci.h
> @@ -143,6 +143,9 @@
> /*
> * @TX_BUF_BYTE_x_hidden
> * optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT.
> + * @frs_sourcing_vbus:
> + * Optional; Callback to perform chip specific operations when FRS
> + * is sourcing vbus.
> */
> struct tcpci;
> struct tcpci_data {
> @@ -154,6 +157,7 @@ struct tcpci_data {
> int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data,
> enum typec_cc_status cc);
> int (*set_vbus)(struct tcpci *tcpci, struct tcpci_data *data, bool source, bool sink);
> + void (*frs_sourcing_vbus)(struct tcpci *tcpci, struct tcpci_data *data);
> };
>
> struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
> --
> 2.28.0.806.g8561365e88-goog

thanks,

--
heikki

2020-10-20 01:27:07

by Heikki Krogerus

[permalink] [raw]
Subject: Re: [PATCH v10 11/15] usb: typec: tcpci_max77759: Fix vbus stuck on upon diconnecting sink

On Wed, Oct 07, 2020 at 11:15:52PM -0700, Badhri Jagan Sridharan wrote:
> Occasionally, POWER_STATUS.sourcing_vbus takes a while to clear after
> writing to MAX_BUCK_BOOST_OP register. This causes vbus to turn back
> on while disconnecting the sink. Overcome this issue by writing into
> MAX_BUCK_BOOST_OP during frs while sourcing vbu, instead of always
> into the register whenever POWER_STATUS.sourcing_vbus is set.
>
> Signed-off-by: Badhri Jagan Sridharan <[email protected]>

Reviewed-by: Heikki Krogerus <[email protected]>

> ---
> v9 is the first version of this patch. Added to fix
> occasional bug of vbus turning back on when disconnecting the FRS accessory
> after disconnect. No changes since v9.
> ---
> drivers/usb/typec/tcpm/tcpci_maxim.c | 28 ++++++++++++++--------------
> 1 file changed, 14 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
> index 723d7dd38f75..43dcad95e897 100644
> --- a/drivers/usb/typec/tcpm/tcpci_maxim.c
> +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
> @@ -238,23 +238,22 @@ static void process_power_status(struct max_tcpci_chip *chip)
> if (ret < 0)
> return;
>
> - if (pwr_status == 0xff) {
> + if (pwr_status == 0xff)
> max_tcpci_init_regs(chip);
> - } else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS) {
> + else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS)
> tcpm_sourcing_vbus(chip->port);
> - /*
> - * Alawys re-enable boost here.
> - * In normal case, when say an headset is attached, TCPM would
> - * have instructed to TCPC to enable boost, so the call is a
> - * no-op.
> - * But for Fast Role Swap case, Boost turns on autonomously without
> - * AP intervention, but, needs AP to enable source mode explicitly
> - * for AP to regain control.
> - */
> - max_tcpci_set_vbus(chip->tcpci, &chip->data, true, false);
> - } else {
> + else
> tcpm_vbus_change(chip->port);
> - }
> +}
> +
> +static void max_tcpci_frs_sourcing_vbus(struct tcpci *tcpci, struct tcpci_data *tdata)
> +{
> + /*
> + * For Fast Role Swap case, Boost turns on autonomously without
> + * AP intervention, but, needs AP to enable source mode explicitly
> + * for AP to regain control.
> + */
> + max_tcpci_set_vbus(tcpci, tdata, true, false);
> }
>
> static void process_tx(struct max_tcpci_chip *chip, u16 status)
> @@ -441,6 +440,7 @@ static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id
> chip->data.start_drp_toggling = max_tcpci_start_toggling;
> chip->data.TX_BUF_BYTE_x_hidden = true;
> chip->data.init = tcpci_init;
> + chip->data.frs_sourcing_vbus = max_tcpci_frs_sourcing_vbus;
>
> max_tcpci_init_regs(chip);
> chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
> --
> 2.28.0.806.g8561365e88-goog

--
heikki

2020-10-20 01:29:25

by Heikki Krogerus

[permalink] [raw]
Subject: Re: [PATCH v10 13/15] usb: typec: tcpm: Implement enabling Auto Discharge disconnect support

On Wed, Oct 07, 2020 at 11:15:54PM -0700, Badhri Jagan Sridharan wrote:
> TCPCI spec allows TCPC hardware to autonomously discharge the vbus
> capacitance upon disconnect. The expectation is that the TCPM enables
> AutoDischargeDisconnect while entering SNK/SRC_ATTACHED states. Hardware
> then automously discharges vbus when the vbus falls below a certain
> threshold i.e. VBUS_SINK_DISCONNECT_THRESHOLD.
>
> Apart from enabling the vbus discharge circuit, AutoDischargeDisconnect
> is also used a flag to move TCPCI based TCPC implementations into
> Attached.Snk/Attached.Src state as mentioned in
> Figure 4-15. TCPC State Diagram before a Connection of the
> USB Type-C Port Controller Interface Specification.
> In such TCPC implementations, setting AutoDischargeDisconnect would
> prevent TCPC into entering "Connection_Invalid" state as well.
>
> Signed-off-by: Badhri Jagan Sridharan <[email protected]>

Reviewed-by: Heikki Krogerus <[email protected]>

> ---
> Changes since v1:
> - Changing patch version to v6 to fix version number confusion.
>
> Changes since v6:
> - Fixed incorrect data_role error that I introduced by mistake in
> the previous version.
>
> Changes since v7:
> - Rebase on usb-next
>
> Changes since v8:
> - Removing the call to tcpm_set_auto_vbus_discharge_threshold
> in the source path.
>
> - Changes since v9:
> - None
> ---
> drivers/usb/typec/tcpm/tcpm.c | 60 ++++++++++++++++++++++++++++++++---
> include/linux/usb/tcpm.h | 15 +++++++++
> 2 files changed, 71 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index d5a3e2b3bea2..51a14d282109 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -1701,6 +1701,24 @@ static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload,
> }
> }
>
> +static int tcpm_set_auto_vbus_discharge_threshold(struct tcpm_port *port,
> + enum typec_pwr_opmode mode, bool pps_active,
> + u32 requested_vbus_voltage)
> +{
> + int ret;
> +
> + if (!port->tcpc->set_auto_vbus_discharge_threshold)
> + return 0;
> +
> + ret = port->tcpc->set_auto_vbus_discharge_threshold(port->tcpc, mode, pps_active,
> + requested_vbus_voltage);
> + tcpm_log_force(port,
> + "set_auto_vbus_discharge_threshold mode:%d pps_active:%c vbus:%u ret:%d",
> + mode, pps_active ? 'y' : 'n', requested_vbus_voltage, ret);
> +
> + return ret;
> +}
> +
> static void tcpm_pd_data_request(struct tcpm_port *port,
> const struct pd_message *msg)
> {
> @@ -1871,6 +1889,10 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
> port->current_limit,
> port->supply_voltage);
> port->explicit_contract = true;
> + tcpm_set_auto_vbus_discharge_threshold(port,
> + TYPEC_PWR_MODE_PD,
> + port->pps_data.active,
> + port->supply_voltage);
> tcpm_set_state(port, SNK_READY, 0);
> } else {
> /*
> @@ -2785,8 +2807,12 @@ static int tcpm_src_attach(struct tcpm_port *port)
> if (ret < 0)
> return ret;
>
> - ret = tcpm_set_roles(port, true, TYPEC_SOURCE,
> - tcpm_data_role_for_source(port));
> + if (port->tcpc->enable_auto_vbus_discharge) {
> + ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true);
> + tcpm_log_force(port, "enable vbus discharge ret:%d", ret);
> + }
> +
> + ret = tcpm_set_roles(port, true, TYPEC_SOURCE, tcpm_data_role_for_source(port));
> if (ret < 0)
> return ret;
>
> @@ -2853,6 +2879,12 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port)
>
> static void tcpm_reset_port(struct tcpm_port *port)
> {
> + int ret;
> +
> + if (port->tcpc->enable_auto_vbus_discharge) {
> + ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, false);
> + tcpm_log_force(port, "Disable vbus discharge ret:%d", ret);
> + }
> tcpm_unregister_altmodes(port);
> tcpm_typec_disconnect(port);
> port->attached = false;
> @@ -2917,8 +2949,13 @@ static int tcpm_snk_attach(struct tcpm_port *port)
> if (ret < 0)
> return ret;
>
> - ret = tcpm_set_roles(port, true, TYPEC_SINK,
> - tcpm_data_role_for_sink(port));
> + if (port->tcpc->enable_auto_vbus_discharge) {
> + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V);
> + ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true);
> + tcpm_log_force(port, "enable vbus discharge ret:%d", ret);
> + }
> +
> + ret = tcpm_set_roles(port, true, TYPEC_SINK, tcpm_data_role_for_sink(port));
> if (ret < 0)
> return ret;
>
> @@ -3502,6 +3539,8 @@ static void run_state_machine(struct tcpm_port *port)
> tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON);
> break;
> case SNK_HARD_RESET_SINK_OFF:
> + /* Do not discharge/disconnect during hard reseet */
> + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0);
> memset(&port->pps_data, 0, sizeof(port->pps_data));
> tcpm_set_vconn(port, false);
> if (port->pd_capable)
> @@ -3544,6 +3583,7 @@ static void run_state_machine(struct tcpm_port *port)
> tcpm_set_charge(port, true);
> }
> tcpm_set_attached_state(port, true);
> + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V);
> tcpm_set_state(port, SNK_STARTUP, 0);
> break;
>
> @@ -3645,6 +3685,10 @@ static void run_state_machine(struct tcpm_port *port)
> tcpm_set_state(port, PR_SWAP_SNK_SRC_SINK_OFF, 0);
> break;
> case PR_SWAP_SRC_SNK_TRANSITION_OFF:
> + /*
> + * Prevent vbus discharge circuit from turning on during PR_SWAP
> + * as this is not a disconnect.
> + */
> tcpm_set_vbus(port, false);
> port->explicit_contract = false;
> /* allow time for Vbus discharge, must be < tSrcSwapStdby */
> @@ -3673,9 +3717,17 @@ static void run_state_machine(struct tcpm_port *port)
> tcpm_set_state_cond(port, SNK_UNATTACHED, PD_T_PS_SOURCE_ON);
> break;
> case PR_SWAP_SRC_SNK_SINK_ON:
> + /* Set the vbus disconnect threshold for implicit contract */
> + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V);
> tcpm_set_state(port, SNK_STARTUP, 0);
> break;
> case PR_SWAP_SNK_SRC_SINK_OFF:
> + /*
> + * Prevent vbus discharge circuit from turning on during PR_SWAP
> + * as this is not a disconnect.
> + */
> + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB,
> + port->pps_data.active, 0);
> tcpm_set_charge(port, false);
> tcpm_set_state(port, hard_reset_state(port),
> PD_T_PS_SOURCE_OFF);
> diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h
> index 7303f518ba49..e68aaa12886f 100644
> --- a/include/linux/usb/tcpm.h
> +++ b/include/linux/usb/tcpm.h
> @@ -86,6 +86,18 @@ enum tcpm_transmit_type {
> * @frs_sourcing_vbus:
> * Optional; Called to notify that vbus is now being sourced.
> * Low level drivers can perform chip specific operations, if any.
> + * @enable_auto_vbus_discharge:
> + * Optional; TCPCI spec based TCPC implementations can optionally
> + * support hardware to autonomously dischrge vbus upon disconnecting
> + * as sink or source. TCPM signals TCPC to enable the mechanism upon
> + * entering connected state and signals disabling upon disconnect.
> + * @set_auto_vbus_discharge_threshold:
> + * Mandatory when enable_auto_vbus_discharge is implemented. TCPM
> + * calls this function to allow lower levels drivers to program the
> + * vbus threshold voltage below which the vbus discharge circuit
> + * will be turned on. requested_vbus_voltage is set to 0 when vbus
> + * is going to disappear knowingly i.e. during PR_SWAP and
> + * HARD_RESET etc.
> */
> struct tcpc_dev {
> struct fwnode_handle *fwnode;
> @@ -113,6 +125,9 @@ struct tcpc_dev {
> int (*set_bist_data)(struct tcpc_dev *dev, bool on);
> int (*enable_frs)(struct tcpc_dev *dev, bool enable);
> void (*frs_sourcing_vbus)(struct tcpc_dev *dev);
> + int (*enable_auto_vbus_discharge)(struct tcpc_dev *dev, bool enable);
> + int (*set_auto_vbus_discharge_threshold)(struct tcpc_dev *dev, enum typec_pwr_opmode mode,
> + bool pps_active, u32 requested_vbus_voltage);
> };
>
> struct tcpm_port;
> --
> 2.28.0.806.g8561365e88-goog

--
heikki